Whamcloud - gitweb
LU-1146 build: batch update copyright messages
[fs/lustre-release.git] / lustre / utils / lfs.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * GPL HEADER START
5  *
6  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 only,
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License version 2 for more details (a copy is included
16  * in the LICENSE file that accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License
19  * version 2 along with this program; If not, see
20  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
21  *
22  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
23  * CA 95054 USA or visit www.sun.com if you need additional information or
24  * have any questions.
25  *
26  * GPL HEADER END
27  */
28 /*
29  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
30  * Use is subject to license terms.
31  *
32  * Copyright (c) 2011, 2012, Whamcloud, Inc.
33  */
34 /*
35  * This file is part of Lustre, http://www.lustre.org/
36  * Lustre is a trademark of Sun Microsystems, Inc.
37  *
38  * lustre/utils/lfs.c
39  *
40  * Author: Peter J. Braam <braam@clusterfs.com>
41  * Author: Phil Schwan <phil@clusterfs.com>
42  * Author: Robert Read <rread@clusterfs.com>
43  */
44
45 /* for O_DIRECTORY */
46 #ifndef _GNU_SOURCE
47 #define _GNU_SOURCE
48 #endif
49
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <getopt.h>
53 #include <string.h>
54 #include <mntent.h>
55 #include <errno.h>
56 #include <pwd.h>
57 #include <grp.h>
58 #include <sys/types.h>
59 #include <sys/stat.h>
60 #include <fcntl.h>
61 #include <dirent.h>
62 #include <time.h>
63 #include <ctype.h>
64 #ifdef HAVE_SYS_QUOTA_H
65 # include <sys/quota.h>
66 #endif
67
68 /* For dirname() */
69 #include <libgen.h>
70
71 #include <lnet/api-support.h>
72 #include <lnet/lnetctl.h>
73
74 #include <liblustre.h>
75 #include <lustre/lustre_idl.h>
76 #include <lustre/liblustreapi.h>
77 #include <lustre/lustre_user.h>
78 #include <lustre_quota.h>
79
80 #include <libcfs/libcfsutil.h>
81 #include "obdctl.h"
82
83 unsigned int libcfs_subsystem_debug = 0;
84
85 /* all functions */
86 static int lfs_setstripe(int argc, char **argv);
87 static int lfs_find(int argc, char **argv);
88 static int lfs_getstripe(int argc, char **argv);
89 static int lfs_osts(int argc, char **argv);
90 static int lfs_mdts(int argc, char **argv);
91 static int lfs_df(int argc, char **argv);
92 static int lfs_getname(int argc, char **argv);
93 static int lfs_check(int argc, char **argv);
94 static int lfs_catinfo(int argc, char **argv);
95 #ifdef HAVE_SYS_QUOTA_H
96 static int lfs_quotachown(int argc, char **argv);
97 static int lfs_quotacheck(int argc, char **argv);
98 static int lfs_quotaon(int argc, char **argv);
99 static int lfs_quotaoff(int argc, char **argv);
100 static int lfs_setquota(int argc, char **argv);
101 static int lfs_quota(int argc, char **argv);
102 static int lfs_quotainv(int argc, char **argv);
103 #endif
104 static int lfs_flushctx(int argc, char **argv);
105 static int lfs_join(int argc, char **argv);
106 static int lfs_lsetfacl(int argc, char **argv);
107 static int lfs_lgetfacl(int argc, char **argv);
108 static int lfs_rsetfacl(int argc, char **argv);
109 static int lfs_rgetfacl(int argc, char **argv);
110 static int lfs_cp(int argc, char **argv);
111 static int lfs_ls(int argc, char **argv);
112 static int lfs_poollist(int argc, char **argv);
113 static int lfs_changelog(int argc, char **argv);
114 static int lfs_changelog_clear(int argc, char **argv);
115 static int lfs_fid2path(int argc, char **argv);
116 static int lfs_path2fid(int argc, char **argv);
117
118 /* all avaialable commands */
119 command_t cmdlist[] = {
120         {"setstripe", lfs_setstripe, 0,
121          "Create a new file with a specific striping pattern or\n"
122          "set the default striping pattern on an existing directory or\n"
123          "delete the default striping pattern from an existing directory\n"
124          "usage: setstripe [--size|-s stripe_size] [--count|-c stripe_count]\n"
125          "                 [--index|-i|--offset|-o start_ost_index]\n"
126          "                 [--pool|-p <pool>] <directory|filename>\n"
127          "       or \n"
128          "       setstripe -d <directory>   (to delete default striping)\n"
129          "\tstripe_size:  Number of bytes on each OST (0 filesystem default)\n"
130          "\t              Can be specified with k, m or g (in KB, MB and GB\n"
131          "\t              respectively)\n"
132          "\tstart_ost_index: OST index of first stripe (-1 default)\n"
133          "\tstripe_count: Number of OSTs to stripe over (0 default, -1 all)\n"
134          "\tpool:         Name of OST pool to use (default none)"},
135         {"getstripe", lfs_getstripe, 0,
136          "To list the striping info for a given file or files in a\n"
137          "directory or recursively for all files in a directory tree.\n"
138          "usage: getstripe [--obd|-O <uuid>] [--quiet | -q] [--verbose | -v]\n"
139          "                 [--count | -c ] [--index | -i | --offset | -o]\n"
140          "                 [--size | -s ] [--pool | -p ] [--directory | -d]\n"
141          "                 [--mdt | -M] [--recursive | -r] [--raw | -R]\n"
142          "                 <directory|filename> ..."},
143         {"pool_list", lfs_poollist, 0,
144          "List pools or pool OSTs\n"
145          "usage: pool_list <fsname>[.<pool>] | <pathname>\n"},
146         {"find", lfs_find, 0,
147          "To find files that match given parameters recursively in a directory tree.\n"
148          "usage: find <directory|filename> ...\n"
149          "     [[!] --atime|-A [+-]N] [[!] --mtime|-M [+-]N] [[!] --ctime|-C [+-]N]\n"
150          "     [--maxdepth|-D N] [[!] --name|-n <pattern>] [--print0|-P]\n"
151          "     [--print|-p] [[!] --obd|-O <uuid[s]>] [[!] --mdt|-m <uuid[s]]\n"
152          "     [[!] --size|-s [+-]N[bkMGTP]] [[!] --type|-t <filetype>]\n"
153          "     [[!] --gid|-g|--group|-G <gid>|<gname>]\n"
154          "     [[!] --uid|-u|--user|-U <uid>|<uname>]\n"
155          "     [[!] --pool <pool>]\n"
156          "\t !: used before an option indicates 'NOT' the requested attribute\n"
157          "\t -: used before an value indicates 'AT MOST' the requested value\n"
158          "\t +: used before an option indicates 'AT LEAST' the requested value\n"},
159         {"check", lfs_check, 0,
160          "Display the status of MDS or OSTs (as specified in the command)\n"
161          "or all the servers (MDS and OSTs).\n"
162          "usage: check <osts|mds|servers>"},
163         {"catinfo", lfs_catinfo, 0,
164          "Show information of specified type logs.\n"
165          "usage: catinfo {keyword} [node name]\n"
166          "\tkeywords are one of followings: config, deletions.\n"
167          "\tnode name must be provided when use keyword config."},
168         {"join", lfs_join, 0,
169          "join two lustre files into one.\n"
170          "obsolete, HEAD does not support it anymore.\n"},
171         {"osts", lfs_osts, 0, "list OSTs connected to client "
172          "[for specified path only]\n" "usage: osts [path]"},
173         {"mdts", lfs_mdts, 0, "list MDTs connected to client "
174          "[for specified path only]\n" "usage: mdts [path]"},
175         {"df", lfs_df, 0,
176          "report filesystem disk space usage or inodes usage"
177          "of each MDS and all OSDs or a batch belonging to a specific pool .\n"
178          "Usage: df [-i] [-h] [--pool|-p <fsname>[.<pool>] [path]"},
179         {"getname", lfs_getname, 0, "list instances and specified mount points "
180          "[for specified path only]\n"
181          "Usage: getname [-h]|[path ...] "},
182 #ifdef HAVE_SYS_QUOTA_H
183         {"quotachown",lfs_quotachown, 0,
184          "Change files' owner or group on the specified filesystem.\n"
185          "usage: quotachown [-i] <filesystem>\n"
186          "\t-i: ignore error if file is not exist\n"},
187         {"quotacheck", lfs_quotacheck, 0,
188          "Scan the specified filesystem for disk usage, and create,\n"
189          "or update quota files.\n"
190          "usage: quotacheck [ -ug ] <filesystem>"},
191         {"quotaon", lfs_quotaon, 0, "Turn filesystem quotas on.\n"
192          "usage: quotaon [ -ugf ] <filesystem>"},
193         {"quotaoff", lfs_quotaoff, 0, "Turn filesystem quotas off.\n"
194          "usage: quotaoff [ -ug ] <filesystem>"},
195         {"setquota", lfs_setquota, 0, "Set filesystem quotas.\n"
196          "usage: setquota <-u|-g> <uname>|<uid>|<gname>|<gid>\n"
197          "                -b <block-softlimit> -B <block-hardlimit>\n"
198          "                -i <inode-softlimit> -I <inode-hardlimit> <filesystem>\n"
199          "       setquota -t <-u|-g> <block-grace> <inode-grace> <filesystem>\n"
200          "       setquota <-u|--user|-g|--group> <uname>|<uid>|<gname>|<gid>\n"
201          "                [--block-softlimit <block-softlimit>]\n"
202          "                [--block-hardlimit <block-hardlimit>]\n"
203          "                [--inode-softlimit <inode-softlimit>]\n"
204          "                [--inode-hardlimit <inode-hardlimit>] <filesystem>\n"
205          "       setquota [-t] <-u|--user|-g|--group>\n"
206          "                [--block-grace <block-grace>]\n"
207          "                [--inode-grace <inode-grace>] <filesystem>\n"
208          "       -b can be used instead of --block-softlimit/--block-grace\n"
209          "       -B can be used instead of --block-hardlimit\n"
210          "       -i can be used instead of --inode-softlimit/--inode-grace\n"
211          "       -I can be used instead of --inode-hardlimit"},
212         {"quota", lfs_quota, 0, "Display disk usage and limits.\n"
213          "usage: quota [-q] [-v] [-o <obd_uuid>|-i <mdt_idx>|-I <ost_idx>]\n"
214          "             [<-u|-g> <uname>|<uid>|<gname>|<gid>] <filesystem>\n"
215          "       quota [-o <obd_uuid>|-i <mdt_idx>|-I <ost_idx>] -t <-u|-g> <filesystem>"},
216         {"quotainv", lfs_quotainv, 0, "Invalidate quota data.\n"
217          "usage: quotainv [-u|-g] <filesystem>"},
218 #endif
219         {"flushctx", lfs_flushctx, 0, "Flush security context for current user.\n"
220          "usage: flushctx [-k] [mountpoint...]"},
221         {"lsetfacl", lfs_lsetfacl, 0,
222          "Remote user setfacl for user/group on the same remote client.\n"
223          "usage: lsetfacl [-bkndRLPvh] [{-m|-x} acl_spec] [{-M|-X} acl_file] file ..."},
224         {"lgetfacl", lfs_lgetfacl, 0,
225          "Remote user getfacl for user/group on the same remote client.\n"
226          "usage: lgetfacl [-dRLPvh] file ..."},
227         {"rsetfacl", lfs_rsetfacl, 0,
228          "Remote user setfacl for user/group on other clients.\n"
229          "usage: rsetfacl [-bkndRLPvh] [{-m|-x} acl_spec] [{-M|-X} acl_file] file ..."},
230         {"rgetfacl", lfs_rgetfacl, 0,
231          "Remote user getfacl for user/group on other clients.\n"
232          "usage: rgetfacl [-dRLPvh] file ..."},
233         {"cp", lfs_cp, 0,
234          "Remote user copy files and directories.\n"
235          "usage: cp [OPTION]... [-T] SOURCE DEST\n\tcp [OPTION]... SOURCE... DIRECTORY\n\tcp [OPTION]... -t DIRECTORY SOURCE..."},
236         {"ls", lfs_ls, 0,
237          "Remote user list directory contents.\n"
238          "usage: ls [OPTION]... [FILE]..."},
239         {"changelog", lfs_changelog, 0,
240          "Show the metadata changes on an MDT."
241          "\nusage: changelog <mdtname> [startrec [endrec]]"},
242         {"changelog_clear", lfs_changelog_clear, 0,
243          "Indicate that old changelog records up to <endrec> are no longer of "
244          "interest to consumer <id>, allowing the system to free up space.\n"
245          "An <endrec> of 0 means all records.\n"
246          "usage: changelog_clear <mdtname> <id> <endrec>"},
247         {"fid2path", lfs_fid2path, 0,
248          "Resolve the full path to a given FID. For a specific hardlink "
249          "specify link number <linkno>.\n"
250          /* "For a historical name, specify changelog record <recno>.\n" */
251          "usage: fid2path <fsname|rootpath> <fid> [--link <linkno>]"
252                 /*[--rec <recno>]*/},
253         {"path2fid", lfs_path2fid, 0, "Display the fid for a given path.\n"
254          "usage: path2fid <path>"},
255         {"help", Parser_help, 0, "help"},
256         {"exit", Parser_quit, 0, "quit"},
257         {"quit", Parser_quit, 0, "quit"},
258         { 0, 0, 0, NULL }
259 };
260
261 static int isnumber(const char *str)
262 {
263         const char *ptr;
264
265         if (str[0] != '-' && !isdigit(str[0]))
266                 return 0;
267
268         for (ptr = str + 1; *ptr != '\0'; ptr++) {
269                 if (!isdigit(*ptr))
270                         return 0;
271         }
272
273         return 1;
274 }
275
276 /* functions */
277 static int lfs_setstripe(int argc, char **argv)
278 {
279         char *fname;
280         int result;
281         unsigned long long st_size;
282         int  st_offset, st_count;
283         char *end;
284         int c;
285         int delete = 0;
286         char *stripe_size_arg = NULL;
287         char *stripe_off_arg = NULL;
288         char *stripe_count_arg = NULL;
289         char *pool_name_arg = NULL;
290         unsigned long long size_units = 1;
291
292         struct option long_opts[] = {
293                 {"count",       required_argument, 0, 'c'},
294                 {"delete",      no_argument,       0, 'd'},
295                 {"index",       required_argument, 0, 'i'},
296                 {"offset",      required_argument, 0, 'o'},
297                 {"pool",        required_argument, 0, 'p'},
298                 {"size",        required_argument, 0, 's'},
299                 {0, 0, 0, 0}
300         };
301
302         st_size = 0;
303         st_offset = -1;
304         st_count = 0;
305
306 #if LUSTRE_VERSION < OBD_OCD_VERSION(2,4,50,0)
307         if (argc == 5 && argv[1][0] != '-' &&
308             isnumber(argv[2]) && isnumber(argv[3]) && isnumber(argv[4])) {
309                 fprintf(stderr, "error: obsolete usage of setstripe "
310                         "positional parameters.  Use -c, -i, -s instead.\n");
311                 return CMD_HELP;
312         } else
313 #else
314 #warning "remove obsolete positional parameter code"
315 #endif
316         {
317                 optind = 0;
318                 while ((c = getopt_long(argc, argv, "c:di:o:p:s:",
319                                         long_opts, NULL)) >= 0) {
320                         switch (c) {
321                         case 0:
322                                 /* Long options. */
323                                 break;
324                         case 'c':
325                                 stripe_count_arg = optarg;
326                                 break;
327                         case 'd':
328                                 /* delete the default striping pattern */
329                                 delete = 1;
330                                 break;
331                         case 'i':
332                         case 'o':
333                                 stripe_off_arg = optarg;
334                                 break;
335                         case 's':
336                                 stripe_size_arg = optarg;
337                                 break;
338                         case 'p':
339                                 pool_name_arg = optarg;
340                                 break;
341                         case '?':
342                                 return CMD_HELP;
343                         default:
344                                 fprintf(stderr, "error: %s: option '%s' "
345                                                 "unrecognized\n",
346                                                 argv[0], argv[optind - 1]);
347                                 return CMD_HELP;
348                         }
349                 }
350
351                 fname = argv[optind];
352
353                 if (delete &&
354                     (stripe_size_arg != NULL || stripe_off_arg != NULL ||
355                      stripe_count_arg != NULL || pool_name_arg != NULL)) {
356                         fprintf(stderr, "error: %s: cannot specify -d with "
357                                         "-s, -c -o or -p options\n",
358                                         argv[0]);
359                         return CMD_HELP;
360                 }
361         }
362
363         if (optind == argc) {
364                 fprintf(stderr, "error: %s: missing filename|dirname\n",
365                         argv[0]);
366                 return CMD_HELP;
367         }
368
369         /* get the stripe size */
370         if (stripe_size_arg != NULL) {
371                 result = parse_size(stripe_size_arg, &st_size, &size_units, 0);
372                 if (result) {
373                         fprintf(stderr, "error: %s: bad size '%s'\n",
374                                 argv[0], stripe_size_arg);
375                         return result;
376                 }
377         }
378         /* get the stripe offset */
379         if (stripe_off_arg != NULL) {
380                 st_offset = strtol(stripe_off_arg, &end, 0);
381                 if (*end != '\0') {
382                         fprintf(stderr, "error: %s: bad stripe offset '%s'\n",
383                                 argv[0], stripe_off_arg);
384                         return CMD_HELP;
385                 }
386         }
387         /* get the stripe count */
388         if (stripe_count_arg != NULL) {
389                 st_count = strtoul(stripe_count_arg, &end, 0);
390                 if (*end != '\0') {
391                         fprintf(stderr, "error: %s: bad stripe count '%s'\n",
392                                 argv[0], stripe_count_arg);
393                         return CMD_HELP;
394                 }
395         }
396
397         do {
398                 result = llapi_file_create_pool(fname, st_size, st_offset,
399                                                 st_count, 0, pool_name_arg);
400                 if (result) {
401                         fprintf(stderr,"error: %s: create stripe file '%s' "
402                                 "failed\n", argv[0], fname);
403                         break;
404                 }
405                 fname = argv[++optind];
406         } while (fname != NULL);
407
408         return result;
409 }
410
411 static int lfs_poollist(int argc, char **argv)
412 {
413         if (argc != 2)
414                 return CMD_HELP;
415
416         return llapi_poollist(argv[1]);
417 }
418
419 static int set_time(time_t *time, time_t *set, char *str)
420 {
421         time_t t;
422         int res = 0;
423
424         if (str[0] == '+')
425                 res = 1;
426         else if (str[0] == '-')
427                 res = -1;
428
429         if (res)
430                 str++;
431
432         t = strtol(str, NULL, 0);
433         if (*time < t * 24 * 60 * 60) {
434                 if (res)
435                         str--;
436                 fprintf(stderr, "Wrong time '%s' is specified.\n", str);
437                 return INT_MAX;
438         }
439
440         *set = *time - t * 24 * 60 * 60;
441         return res;
442 }
443
444 #define USER 0
445 #define GROUP 1
446
447 static int name2id(unsigned int *id, char *name, int type)
448 {
449         if (type == USER) {
450                 struct passwd *entry;
451
452                 if (!(entry = getpwnam(name))) {
453                         if (!errno)
454                                 errno = ENOENT;
455                         return -1;
456                 }
457
458                 *id = entry->pw_uid;
459         } else {
460                 struct group *entry;
461
462                 if (!(entry = getgrnam(name))) {
463                         if (!errno)
464                                 errno = ENOENT;
465                         return -1;
466                 }
467
468                 *id = entry->gr_gid;
469         }
470
471         return 0;
472 }
473
474 static int id2name(char **name, unsigned int id, int type)
475 {
476         if (type == USER) {
477                 struct passwd *entry;
478
479                 if (!(entry = getpwuid(id))) {
480                         if (!errno)
481                                 errno = ENOENT;
482                         return -1;
483                 }
484
485                 *name = entry->pw_name;
486         } else {
487                 struct group *entry;
488
489                 if (!(entry = getgrgid(id))) {
490                         if (!errno)
491                                 errno = ENOENT;
492                         return -1;
493                 }
494
495                 *name = entry->gr_name;
496         }
497
498         return 0;
499 }
500
501 #define FIND_POOL_OPT 3
502 static int lfs_find(int argc, char **argv)
503 {
504         int new_fashion = 1;
505         int c, ret;
506         time_t t;
507         struct find_param param = { .maxdepth = -1, .size_units = 0 };
508         struct option long_opts[] = {
509                 {"atime",     required_argument, 0, 'A'},
510                 {"ctime",     required_argument, 0, 'C'},
511                 {"maxdepth",  required_argument, 0, 'D'},
512                 {"gid",       required_argument, 0, 'g'},
513                 {"group",     required_argument, 0, 'G'},
514                 {"mtime",     required_argument, 0, 'M'},
515                 {"mdt",       required_argument, 0, 'm'},
516                 {"name",      required_argument, 0, 'n'},
517                 /* --obd is considered as a new option. */
518                 {"obd",       required_argument, 0, 'O'},
519                 {"ost",       required_argument, 0, 'O'},
520                 /* no short option for pool, p/P already used */
521                 {"pool",      required_argument, 0, FIND_POOL_OPT},
522                 {"print0",    no_argument,       0, 'p'},
523                 {"print",     no_argument,       0, 'P'},
524                 {"quiet",     no_argument,       0, 'q'},
525                 {"recursive", no_argument,       0, 'r'},
526                 {"size",      required_argument, 0, 's'},
527                 {"type",      required_argument, 0, 't'},
528                 {"uid",       required_argument, 0, 'u'},
529                 {"user",      required_argument, 0, 'U'},
530                 {"verbose",   no_argument,       0, 'v'},
531                 {0, 0, 0, 0}
532         };
533         int pathstart = -1;
534         int pathend = -1;
535         int neg_opt = 0;
536         time_t *xtime;
537         int *xsign;
538         int isoption;
539         char *endptr;
540
541         time(&t);
542
543         optind = 0;
544         /* when getopt_long_only() hits '!' it returns 1, puts "!" in optarg */
545         while ((c = getopt_long_only(argc, argv, "-A:C:D:g:G:M:m:n:O:"
546                                      "Ppqrs:t:u:U:v", long_opts, NULL)) >= 0) {
547                 xtime = NULL;
548                 xsign = NULL;
549                 if (neg_opt)
550                         --neg_opt;
551                 /* '!' is part of option */
552                 /* when getopt_long_only() finds a string which is not
553                  * an option nor a known option argument it returns 1
554                  * in that case if we already have found pathstart and pathend
555                  * (i.e. we have the list of pathnames),
556                  * the only supported value is "!"
557                  */
558                 isoption = (c != 1) || (strcmp(optarg, "!") == 0);
559                 if (!isoption && pathend != -1) {
560                         fprintf(stderr, "err: %s: filename|dirname must either "
561                                         "precede options or follow options\n",
562                                         argv[0]);
563                         ret = CMD_HELP;
564                         goto err;
565                 }
566                 if (!isoption && pathstart == -1)
567                         pathstart = optind - 1;
568                 if (isoption && pathstart != -1 && pathend == -1) {
569                         pathend = optind - 2;
570                         if ((c == 1 && strcmp(optarg, "!") == 0) ||
571                             c == 'P' || c == 'p' || c == 'O' ||
572                             c == 'q' || c == 'r' || c == 'v')
573                                 pathend = optind - 1;
574                 }
575                 switch (c) {
576                 case 0:
577                         /* Long options. */
578                         break;
579                 case 1:
580                         /* unknown; opt is "!" or path component,
581                          * checking done above.
582                          */
583                         if (strcmp(optarg, "!") == 0)
584                                 neg_opt = 2;
585                         break;
586                 case 'A':
587                         xtime = &param.atime;
588                         xsign = &param.asign;
589                         param.exclude_atime = !!neg_opt;
590                 case 'C':
591                         if (c == 'C') {
592                                 xtime = &param.ctime;
593                                 xsign = &param.csign;
594                                 param.exclude_ctime = !!neg_opt;
595                         }
596                 case 'M':
597                         if (c == 'M') {
598                                 xtime = &param.mtime;
599                                 xsign = &param.msign;
600                                 param.exclude_mtime = !!neg_opt;
601                         }
602                         new_fashion = 1;
603                         ret = set_time(&t, xtime, optarg);
604                         if (ret == INT_MAX) {
605                                 ret = -1;
606                                 goto err;
607                         }
608                         if (ret)
609                                 *xsign = ret;
610                         break;
611                 case 'D':
612                         new_fashion = 1;
613                         param.maxdepth = strtol(optarg, 0, 0);
614                         break;
615                 case 'g':
616                 case 'G':
617                         new_fashion = 1;
618                         ret = name2id(&param.gid, optarg, GROUP);
619                         if (ret) {
620                                 param.gid = strtoul(optarg, &endptr, 10);
621                                 if (*endptr != '\0') {
622                                         fprintf(stderr, "Group/GID: %s cannot "
623                                                 "be found.\n", optarg);
624                                         ret = -1;
625                                         goto err;
626                                 }
627                         }
628                         param.exclude_gid = !!neg_opt;
629                         param.check_gid = 1;
630                         break;
631                 case 'u':
632                 case 'U':
633                         new_fashion = 1;
634                         ret = name2id(&param.uid, optarg, USER);
635                         if (ret) {
636                                 param.uid = strtoul(optarg, &endptr, 10);
637                                 if (*endptr != '\0') {
638                                         fprintf(stderr, "User/UID: %s cannot "
639                                                 "be found.\n", optarg);
640                                         ret = -1;
641                                         goto err;
642                                 }
643                         }
644                         param.exclude_uid = !!neg_opt;
645                         param.check_uid = 1;
646                         break;
647                 case FIND_POOL_OPT:
648                         new_fashion = 1;
649                         if (strlen(optarg) > LOV_MAXPOOLNAME) {
650                                 fprintf(stderr,
651                                         "Pool name %s is too long"
652                                         " (max is %d)\n", optarg,
653                                         LOV_MAXPOOLNAME);
654                                 ret = -1;
655                                 goto err;
656                         }
657                         /* we do check for empty pool because empty pool
658                          * is used to find V1 lov attributes */
659                         strncpy(param.poolname, optarg, LOV_MAXPOOLNAME);
660                         param.poolname[LOV_MAXPOOLNAME] = '\0';
661                         param.exclude_pool = !!neg_opt;
662                         param.check_pool = 1;
663                         break;
664                 case 'n':
665                         new_fashion = 1;
666                         param.pattern = (char *)optarg;
667                         param.exclude_pattern = !!neg_opt;
668                         break;
669                 case 'm':
670                 case 'O': {
671                         char *buf, *token, *next, *p;
672                         int len = 1;
673                         void *tmp;
674
675                         buf = strdup(optarg);
676                         if (buf == NULL) {
677                                 ret = -ENOMEM;
678                                 goto err;
679                         }
680
681                         param.exclude_obd = !!neg_opt;
682
683                         token = buf;
684                         while (token && *token) {
685                                 token = strchr(token, ',');
686                                 if (token) {
687                                         len++;
688                                         token++;
689                                 }
690                         }
691                         if (c == 'O') {
692                                 param.exclude_obd = !!neg_opt;
693                                 param.num_alloc_obds += len;
694                                 tmp = realloc(param.obduuid,
695                                               param.num_alloc_obds *
696                                               sizeof(*param.obduuid));
697                                 if (tmp == NULL)
698                                         GOTO(err_free, ret = -ENOMEM);
699                                 param.obduuid = tmp;
700                         } else {
701                                 param.exclude_mdt = !!neg_opt;
702                                 param.num_alloc_mdts += len;
703                                 tmp = realloc(param.mdtuuid,
704                                               param.num_alloc_mdts *
705                                               sizeof(*param.mdtuuid));
706                                 if (tmp == NULL)
707                                         GOTO(err_free, ret = -ENOMEM);
708                                 param.mdtuuid = tmp;
709                         }
710                         for (token = buf; token && *token; token = next) {
711                                 char *uuid;
712                                 if (c == 'O')
713                                         uuid =
714                                           param.obduuid[param.num_obds++].uuid;
715                                 else
716                                         uuid =
717                                           param.mdtuuid[param.num_mdts++].uuid;
718                                 p = strchr(token, ',');
719                                 next = 0;
720                                 if (p) {
721                                         *p = 0;
722                                         next = p+1;
723                                 }
724                                 strcpy((char *)uuid, token);
725                         }
726 err_free:
727                         if (buf)
728                                 free(buf);
729                         break;
730                 }
731                 case 'p':
732                         new_fashion = 1;
733                         param.zeroend = 1;
734                         break;
735                 case 'P':
736                         break;
737                 case 'q':
738                         new_fashion = 0;
739                         param.quiet++;
740                         param.verbose = 0;
741                         break;
742                 case 'r':
743                         new_fashion = 0;
744                         param.recursive = 1;
745                         break;
746                 case 't':
747                         param.exclude_type = !!neg_opt;
748                         switch(optarg[0]) {
749                         case 'b': param.type = S_IFBLK; break;
750                         case 'c': param.type = S_IFCHR; break;
751                         case 'd': param.type = S_IFDIR; break;
752                         case 'f': param.type = S_IFREG; break;
753                         case 'l': param.type = S_IFLNK; break;
754                         case 'p': param.type = S_IFIFO; break;
755                         case 's': param.type = S_IFSOCK; break;
756 #ifdef S_IFDOOR /* Solaris only */
757                         case 'D': param.type = S_IFDOOR; break;
758 #endif
759                         default: fprintf(stderr, "error: %s: bad type '%s'\n",
760                                          argv[0], optarg);
761                                  ret = CMD_HELP;
762                                  goto err;
763                         };
764                         break;
765                 case 's':
766                         if (optarg[0] == '+')
767                                 param.size_sign = -1;
768                         else if (optarg[0] == '-')
769                                 param.size_sign = +1;
770
771                         if (param.size_sign)
772                                 optarg++;
773                         ret = parse_size(optarg, &param.size,
774                                          &param.size_units, 0);
775                         if (ret) {
776                                 fprintf(stderr,"error: bad size '%s'\n",
777                                         optarg);
778                                 goto err;
779                         }
780                         param.check_size = 1;
781                         param.exclude_size = !!neg_opt;
782                         break;
783                 case 'v':
784                         new_fashion = 0;
785                         param.verbose++;
786                         param.quiet = 0;
787                         break;
788                 case '?':
789                         ret = CMD_HELP;
790                         goto err;
791                 default:
792                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
793                                 argv[0], argv[optind - 1]);
794                         ret = CMD_HELP;
795                         goto err;
796                 };
797         }
798
799         if (pathstart == -1) {
800                 fprintf(stderr, "error: %s: no filename|pathname\n",
801                         argv[0]);
802                 ret = CMD_HELP;
803                 goto err;
804         } else if (pathend == -1) {
805                 /* no options */
806                 pathend = argc;
807         }
808
809         if (new_fashion) {
810                 param.quiet = 1;
811         } else {
812                 static int deprecated_warning;
813                 if (!deprecated_warning) {
814                         fprintf(stderr, "lfs find: -q, -r, -v options "
815                                 "deprecated.  Use 'lfs getstripe' instead.\n");
816                         deprecated_warning = 1;
817                 }
818                 if (!param.recursive && param.maxdepth == -1)
819                         param.maxdepth = 1;
820         }
821
822         do {
823                 if (new_fashion)
824                         ret = llapi_find(argv[pathstart], &param);
825                 else
826                         ret = llapi_getstripe(argv[pathstart], &param);
827         } while (++pathstart < pathend && !ret);
828
829         if (ret)
830                 fprintf(stderr, "error: %s failed for %s.\n",
831                         argv[0], argv[optind - 1]);
832 err:
833         if (param.obduuid && param.num_alloc_obds)
834                 free(param.obduuid);
835
836         if (param.mdtuuid && param.num_alloc_mdts)
837                 free(param.mdtuuid);
838
839         return ret;
840 }
841
842 static int lfs_getstripe(int argc, char **argv)
843 {
844         struct option long_opts[] = {
845                 {"count", 0, 0, 'c'},
846                 {"directory", 0, 0, 'd'},
847                 {"generation", 0, 0, 'g'},
848                 {"index", 0, 0, 'i'},
849                 {"mdt", 0, 0, 'M'},
850                 {"offset", 0, 0, 'o'},
851                 {"obd", 1, 0, 'O'},
852                 {"pool", 0, 0, 'p'},
853                 {"quiet", 0, 0, 'q'},
854                 {"recursive", 0, 0, 'r'},
855                 {"raw", 0, 0, 'R'},
856                 {"size", 0, 0, 's'},
857                 {"verbose", 0, 0, 'v'},
858                 {0, 0, 0, 0}
859         };
860         int c, rc;
861         struct find_param param = { 0 };
862
863         param.maxdepth = 1;
864         optind = 0;
865         while ((c = getopt_long(argc, argv, "cdghiMoO:pqrRsv",
866                                 long_opts, NULL)) != -1) {
867                 switch (c) {
868                 case 'O':
869                         if (param.obduuid) {
870                                 fprintf(stderr,
871                                         "error: %s: only one obduuid allowed",
872                                         argv[0]);
873                                 return CMD_HELP;
874                         }
875                         param.obduuid = (struct obd_uuid *)optarg;
876                         break;
877                 case 'q':
878                         param.quiet++;
879                         break;
880                 case 'd':
881                         param.maxdepth = 0;
882                         break;
883                 case 'r':
884                         param.recursive = 1;
885                         break;
886                 case 'v':
887                         param.verbose = VERBOSE_ALL | VERBOSE_DETAIL;
888                         break;
889                 case 'c':
890                         if (!(param.verbose & VERBOSE_DETAIL)) {
891                                 param.verbose |= VERBOSE_COUNT;
892                                 param.maxdepth = 0;
893                         }
894                         break;
895                 case 's':
896                         if (!(param.verbose & VERBOSE_DETAIL)) {
897                                 param.verbose |= VERBOSE_SIZE;
898                                 param.maxdepth = 0;
899                         }
900                         break;
901                 case 'i':
902                 case 'o':
903                         if (!(param.verbose & VERBOSE_DETAIL)) {
904                                 param.verbose |= VERBOSE_OFFSET;
905                                 param.maxdepth = 0;
906                         }
907                         break;
908                 case 'p':
909                         if (!(param.verbose & VERBOSE_DETAIL)) {
910                                 param.verbose |= VERBOSE_POOL;
911                                 param.maxdepth = 0;
912                         }
913                         break;
914                 case 'g':
915                         if (!(param.verbose & VERBOSE_DETAIL)) {
916                                 param.verbose |= VERBOSE_GENERATION;
917                                 param.maxdepth = 0;
918                         }
919                         break;
920                 case 'M':
921                         if (!(param.verbose & VERBOSE_DETAIL))
922                                 param.maxdepth = 0;
923                         param.get_mdt_index = 1;
924                         break;
925                 case 'R':
926                         param.raw = 1;
927                         break;
928                 case '?':
929                         return CMD_HELP;
930                 default:
931                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
932                                 argv[0], argv[optind - 1]);
933                         return CMD_HELP;
934                 }
935         }
936
937         if (optind >= argc)
938                 return CMD_HELP;
939
940         if (param.recursive)
941                 param.maxdepth = -1;
942
943         if (!param.verbose)
944                 param.verbose = VERBOSE_ALL;
945         if (param.quiet)
946                 param.verbose = VERBOSE_OBJID;
947
948         do {
949                 rc = llapi_getstripe(argv[optind], &param);
950         } while (++optind < argc && !rc);
951
952         if (rc)
953                 fprintf(stderr, "error: %s failed for %s.\n",
954                         argv[0], argv[optind - 1]);
955         return rc;
956 }
957
958 static int lfs_tgts(int argc, char **argv)
959 {
960         char mntdir[PATH_MAX] = {'\0'}, path[PATH_MAX] = {'\0'};
961         struct find_param param;
962         int index = 0, rc=0;
963
964         if (argc > 2)
965                 return CMD_HELP;
966
967         if (argc == 2 && !realpath(argv[1], path)) {
968                 rc = -errno;
969                 fprintf(stderr, "error: invalid path '%s': %s\n",
970                         argv[1], strerror(-rc));
971                 return rc;
972         }
973
974         while (!llapi_search_mounts(path, index++, mntdir, NULL)) {
975                 /* Check if we have a mount point */
976                 if (mntdir[0] == '\0')
977                         continue;
978
979                 memset(&param, 0, sizeof(param));
980                 if (!strcmp(argv[0], "mdts"))
981                         param.get_lmv = 1;
982
983                 rc = llapi_ostlist(mntdir, &param);
984                 if (rc) {
985                         fprintf(stderr, "error: %s: failed on %s\n",
986                                 argv[0], mntdir);
987                 }
988                 if (path[0] != '\0')
989                         break;
990                 memset(mntdir, 0, PATH_MAX);
991         }
992
993         return rc;
994 }
995
996 static int lfs_osts(int argc, char **argv)
997 {
998         return lfs_tgts(argc, argv);
999 }
1000
1001 static int lfs_mdts(int argc, char **argv)
1002 {
1003         return lfs_tgts(argc, argv);
1004 }
1005
1006 #define COOK(value)                                                     \
1007 ({                                                                      \
1008         int radix = 0;                                                  \
1009         while (value > 1024) {                                          \
1010                 value /= 1024;                                          \
1011                 radix++;                                                \
1012         }                                                               \
1013         radix;                                                          \
1014 })
1015 #define UUF     "%-20s"
1016 #define CSF     "%11s"
1017 #define CDF     "%11llu"
1018 #define HDF     "%8.1f%c"
1019 #define RSF     "%4s"
1020 #define RDF     "%3d%%"
1021
1022 static int showdf(char *mntdir, struct obd_statfs *stat,
1023                   char *uuid, int ishow, int cooked,
1024                   char *type, int index, int rc)
1025 {
1026         long long avail, used, total;
1027         double ratio = 0;
1028         char *suffix = "KMGTPEZY";
1029         /* Note if we have >2^64 bytes/fs these buffers will need to be grown */
1030         char tbuf[20], ubuf[20], abuf[20], rbuf[20];
1031
1032         if (!uuid || !stat)
1033                 return -EINVAL;
1034
1035         switch (rc) {
1036         case 0:
1037                 if (ishow) {
1038                         avail = stat->os_ffree;
1039                         used = stat->os_files - stat->os_ffree;
1040                         total = stat->os_files;
1041                 } else {
1042                         int shift = cooked ? 0 : 10;
1043
1044                         avail = (stat->os_bavail * stat->os_bsize) >> shift;
1045                         used  = ((stat->os_blocks - stat->os_bfree) *
1046                                  stat->os_bsize) >> shift;
1047                         total = (stat->os_blocks * stat->os_bsize) >> shift;
1048                 }
1049
1050                 if ((used + avail) > 0)
1051                         ratio = (double)used / (double)(used + avail);
1052
1053                 if (cooked) {
1054                         int i;
1055                         double cook_val;
1056
1057                         cook_val = (double)total;
1058                         i = COOK(cook_val);
1059                         if (i > 0)
1060                                 sprintf(tbuf, HDF, cook_val, suffix[i - 1]);
1061                         else
1062                                 sprintf(tbuf, CDF, total);
1063
1064                         cook_val = (double)used;
1065                         i = COOK(cook_val);
1066                         if (i > 0)
1067                                 sprintf(ubuf, HDF, cook_val, suffix[i - 1]);
1068                         else
1069                                 sprintf(ubuf, CDF, used);
1070
1071                         cook_val = (double)avail;
1072                         i = COOK(cook_val);
1073                         if (i > 0)
1074                                 sprintf(abuf, HDF, cook_val, suffix[i - 1]);
1075                         else
1076                                 sprintf(abuf, CDF, avail);
1077                 } else {
1078                         sprintf(tbuf, CDF, total);
1079                         sprintf(ubuf, CDF, used);
1080                         sprintf(abuf, CDF, avail);
1081                 }
1082
1083                 sprintf(rbuf, RDF, (int)(ratio * 100 + 0.5));
1084                 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s",
1085                        uuid, tbuf, ubuf, abuf, rbuf, mntdir);
1086                 if (type)
1087                         printf("[%s:%d]\n", type, index);
1088                 else
1089                         printf("\n");
1090
1091                 break;
1092         case -ENODATA:
1093                 printf(UUF": inactive device\n", uuid);
1094                 break;
1095         default:
1096                 printf(UUF": %s\n", uuid, strerror(-rc));
1097                 break;
1098         }
1099
1100         return 0;
1101 }
1102
1103 struct ll_stat_type {
1104         int   st_op;
1105         char *st_name;
1106 };
1107
1108 static int mntdf(char *mntdir, char *fsname, char *pool, int ishow, int cooked)
1109 {
1110         struct obd_statfs stat_buf, sum = { .os_bsize = 1 };
1111         struct obd_uuid uuid_buf;
1112         char *poolname = NULL;
1113         struct ll_stat_type types[] = { { LL_STATFS_LMV, "MDT" },
1114                                         { LL_STATFS_LOV, "OST" },
1115                                         { 0, NULL } };
1116         struct ll_stat_type *tp;
1117         __u32 index;
1118         int rc;
1119
1120         if (pool) {
1121                 poolname = strchr(pool, '.');
1122                 if (poolname != NULL) {
1123                         if (strncmp(fsname, pool, strlen(fsname))) {
1124                                 fprintf(stderr, "filesystem name incorrect\n");
1125                                 return -ENODEV;
1126                         }
1127                         poolname++;
1128                 } else
1129                         poolname = pool;
1130         }
1131
1132         if (ishow)
1133                 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s\n",
1134                        "UUID", "Inodes", "IUsed", "IFree",
1135                        "IUse%", "Mounted on");
1136         else
1137                 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s\n",
1138                        "UUID", cooked ? "bytes" : "1K-blocks",
1139                        "Used", "Available", "Use%", "Mounted on");
1140
1141         for (tp = types; tp->st_name != NULL; tp++) {
1142                 for (index = 0; ; index++) {
1143                         memset(&stat_buf, 0, sizeof(struct obd_statfs));
1144                         memset(&uuid_buf, 0, sizeof(struct obd_uuid));
1145                         rc = llapi_obd_statfs(mntdir, tp->st_op, index,
1146                                               &stat_buf, &uuid_buf);
1147                         if (rc == -ENODEV)
1148                                 break;
1149
1150                         if (poolname && tp->st_op == LL_STATFS_LOV &&
1151                             llapi_search_ost(fsname, poolname,
1152                                              obd_uuid2str(&uuid_buf)) != 1)
1153                                 continue;
1154
1155                         /* the llapi_obd_statfs() call may have returned with
1156                          * an error, but if it filled in uuid_buf we will at
1157                          * lease use that to print out a message for that OBD.
1158                          * If we didn't get anything in the uuid_buf, then fill
1159                          * it in so that we can print an error message. */
1160                         if (uuid_buf.uuid[0] == '\0')
1161                                 sprintf(uuid_buf.uuid, "%s%04x",
1162                                         tp->st_name, index);
1163                         showdf(mntdir,&stat_buf,obd_uuid2str(&uuid_buf),
1164                                ishow, cooked, tp->st_name, index, rc);
1165
1166                         if (rc == 0) {
1167                                 if (tp->st_op == LL_STATFS_LMV) {
1168                                         sum.os_ffree += stat_buf.os_ffree;
1169                                         sum.os_files += stat_buf.os_files;
1170                                 } else /* if (tp->st_op == LL_STATFS_LOV) */ {
1171                                         sum.os_blocks += stat_buf.os_blocks *
1172                                                 stat_buf.os_bsize;
1173                                         sum.os_bfree  += stat_buf.os_bfree *
1174                                                 stat_buf.os_bsize;
1175                                         sum.os_bavail += stat_buf.os_bavail *
1176                                                 stat_buf.os_bsize;
1177                                 }
1178                         } else if (rc == -EINVAL || rc == -EFAULT) {
1179                                 break;
1180                         }
1181                 }
1182         }
1183
1184         printf("\n");
1185         showdf(mntdir, &sum, "filesystem summary:", ishow, cooked, NULL, 0,0);
1186         printf("\n");
1187         return 0;
1188 }
1189
1190 static int lfs_df(int argc, char **argv)
1191 {
1192         char mntdir[PATH_MAX] = {'\0'}, path[PATH_MAX] = {'\0'};
1193         int ishow = 0, cooked = 0;
1194         int c, rc = 0, index = 0;
1195         char fsname[PATH_MAX] = "", *pool_name = NULL;
1196         struct option long_opts[] = {
1197                 {"pool", required_argument, 0, 'p'},
1198                 {0, 0, 0, 0}
1199         };
1200
1201         optind = 0;
1202         while ((c = getopt_long(argc, argv, "hip:", long_opts, NULL)) != -1) {
1203                 switch (c) {
1204                 case 'i':
1205                         ishow = 1;
1206                         break;
1207                 case 'h':
1208                         cooked = 1;
1209                         break;
1210                 case 'p':
1211                         pool_name = optarg;
1212                         break;
1213                 default:
1214                         return CMD_HELP;
1215                 }
1216         }
1217         if (optind < argc && !realpath(argv[optind], path)) {
1218                 rc = -errno;
1219                 fprintf(stderr, "error: invalid path '%s': %s\n",
1220                         argv[optind], strerror(-rc));
1221                 return rc;
1222         }
1223
1224         while (!llapi_search_mounts(path, index++, mntdir, fsname)) {
1225                 /* Check if we have a mount point */
1226                 if (mntdir[0] == '\0')
1227                         continue;
1228
1229                 rc = mntdf(mntdir, fsname, pool_name, ishow, cooked);
1230                 if (rc || path[0] != '\0')
1231                         break;
1232                 fsname[0] = '\0'; /* avoid matching in next loop */
1233                 mntdir[0] = '\0'; /* avoid matching in next loop */
1234         }
1235
1236         return rc;
1237 }
1238
1239 static int lfs_getname(int argc, char **argv)
1240 {
1241         char mntdir[PATH_MAX] = "", path[PATH_MAX] = "", fsname[PATH_MAX] = "";
1242         int rc = 0, index = 0, c;
1243         char buf[sizeof(struct obd_uuid)];
1244
1245         optind = 0;
1246         while ((c = getopt(argc, argv, "h")) != -1)
1247                 return CMD_HELP;
1248
1249         if (optind == argc) { /* no paths specified, get all paths. */
1250                 while (!llapi_search_mounts(path, index++, mntdir, fsname)) {
1251                         rc = llapi_getname(mntdir, buf, sizeof(buf));
1252                         if (rc < 0) {
1253                                 fprintf(stderr,
1254                                         "cannot get name for `%s': %s\n",
1255                                         mntdir, strerror(-rc));
1256                                 break;
1257                         }
1258
1259                         printf("%s %s\n", buf, mntdir);
1260
1261                         path[0] = fsname[0] = mntdir[0] = 0;
1262                 }
1263         } else { /* paths specified, only attempt to search these. */
1264                 for (; optind < argc; optind++) {
1265                         rc = llapi_getname(argv[optind], buf, sizeof(buf));
1266                         if (rc < 0) {
1267                                 fprintf(stderr,
1268                                         "cannot get name for `%s': %s\n",
1269                                         argv[optind], strerror(-rc));
1270                                 break;
1271                         }
1272
1273                         printf("%s %s\n", buf, argv[optind]);
1274                 }
1275         }
1276         return rc;
1277 }
1278
1279 static int lfs_check(int argc, char **argv)
1280 {
1281         int rc;
1282         char mntdir[PATH_MAX] = {'\0'};
1283         int num_types = 1;
1284         char *obd_types[2];
1285         char obd_type1[4];
1286         char obd_type2[4];
1287
1288         if (argc != 2)
1289                 return CMD_HELP;
1290
1291         obd_types[0] = obd_type1;
1292         obd_types[1] = obd_type2;
1293
1294         if (strcmp(argv[1], "osts") == 0) {
1295                 strcpy(obd_types[0], "osc");
1296         } else if (strcmp(argv[1], "mds") == 0) {
1297                 strcpy(obd_types[0], "mdc");
1298         } else if (strcmp(argv[1], "servers") == 0) {
1299                 num_types = 2;
1300                 strcpy(obd_types[0], "osc");
1301                 strcpy(obd_types[1], "mdc");
1302         } else {
1303                 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
1304                                 argv[0], argv[1]);
1305                         return CMD_HELP;
1306         }
1307
1308         rc = llapi_search_mounts(NULL, 0, mntdir, NULL);
1309         if (rc < 0 || mntdir[0] == '\0') {
1310                 fprintf(stderr, "No suitable Lustre mount found\n");
1311                 return rc;
1312         }
1313
1314         rc = llapi_target_iterate(num_types, obd_types,
1315                                   mntdir, llapi_ping_target);
1316
1317         if (rc)
1318                 fprintf(stderr, "error: %s: %s status failed\n",
1319                                 argv[0],argv[1]);
1320
1321         return rc;
1322
1323 }
1324
1325 static int lfs_catinfo(int argc, char **argv)
1326 {
1327         char mntdir[PATH_MAX] = {'\0'};
1328         int rc;
1329
1330         if (argc < 2 || (!strcmp(argv[1],"config") && argc < 3))
1331                 return CMD_HELP;
1332
1333         if (strcmp(argv[1], "config") && strcmp(argv[1], "deletions"))
1334                 return CMD_HELP;
1335
1336         rc = llapi_search_mounts(NULL, 0, mntdir, NULL);
1337         if (rc == 0 && mntdir[0] != '\0') {
1338                 if (argc == 3)
1339                         rc = llapi_catinfo(mntdir, argv[1], argv[2]);
1340                 else
1341                         rc = llapi_catinfo(mntdir, argv[1], NULL);
1342         } else {
1343                 fprintf(stderr, "no lustre_lite mounted.\n");
1344                 rc = -1;
1345         }
1346
1347         return rc;
1348 }
1349
1350 static int lfs_join(int argc, char **argv)
1351 {
1352         fprintf(stderr, "join two lustre files into one.\n"
1353                         "obsolete, HEAD does not support it anymore.\n");
1354         return 0;
1355 }
1356
1357 #ifdef HAVE_SYS_QUOTA_H
1358 static int lfs_quotachown(int argc, char **argv)
1359 {
1360
1361         int c,rc;
1362         int flag = 0;
1363
1364         optind = 0;
1365         while ((c = getopt(argc, argv, "i")) != -1) {
1366                 switch (c) {
1367                 case 'i':
1368                         flag++;
1369                         break;
1370                 default:
1371                         fprintf(stderr, "error: %s: option '-%c' "
1372                                         "unrecognized\n", argv[0], c);
1373                         return CMD_HELP;
1374                 }
1375         }
1376         if (optind == argc)
1377                 return CMD_HELP;
1378         rc = llapi_quotachown(argv[optind], flag);
1379         if(rc)
1380                 fprintf(stderr,"error: change file owner/group failed.\n");
1381         return rc;
1382 }
1383
1384 static int lfs_quotacheck(int argc, char **argv)
1385 {
1386         int c, check_type = 0;
1387         char *mnt;
1388         struct if_quotacheck qchk;
1389         struct if_quotactl qctl;
1390         char *obd_type = (char *)qchk.obd_type;
1391         int rc;
1392
1393         memset(&qchk, 0, sizeof(qchk));
1394
1395         optind = 0;
1396         while ((c = getopt(argc, argv, "gu")) != -1) {
1397                 switch (c) {
1398                 case 'u':
1399                         check_type |= 0x01;
1400                         break;
1401                 case 'g':
1402                         check_type |= 0x02;
1403                         break;
1404                 default:
1405                         fprintf(stderr, "error: %s: option '-%c' "
1406                                         "unrecognized\n", argv[0], c);
1407                         return CMD_HELP;
1408                 }
1409         }
1410
1411         if (check_type)
1412                 check_type--;
1413         else    /* do quotacheck for both user & group quota by default */
1414                 check_type = 0x02;
1415
1416         if (argc == optind)
1417                 return CMD_HELP;
1418
1419         mnt = argv[optind];
1420
1421         rc = llapi_quotacheck(mnt, check_type);
1422         if (rc) {
1423                 fprintf(stderr, "quotacheck failed: %s\n", strerror(-rc));
1424                 return rc;
1425         }
1426
1427         rc = llapi_poll_quotacheck(mnt, &qchk);
1428         if (rc) {
1429                 if (*obd_type)
1430                         fprintf(stderr, "%s %s ", obd_type,
1431                                 obd_uuid2str(&qchk.obd_uuid));
1432                 fprintf(stderr, "quota check failed: %s\n", strerror(-rc));
1433                 return rc;
1434         }
1435
1436         memset(&qctl, 0, sizeof(qctl));
1437         qctl.qc_cmd = LUSTRE_Q_QUOTAON;
1438         qctl.qc_type = check_type;
1439         rc = llapi_quotactl(mnt, &qctl);
1440         if (rc && rc != -EALREADY) {
1441                 if (*obd_type)
1442                         fprintf(stderr, "%s %s ", (char *)qctl.obd_type,
1443                                 obd_uuid2str(&qctl.obd_uuid));
1444                 fprintf(stderr, "%s turn on quota failed: %s\n",
1445                         argv[0], strerror(-rc));
1446                 return rc;
1447         }
1448
1449         return 0;
1450 }
1451
1452 static int lfs_quotaon(int argc, char **argv)
1453 {
1454         int c;
1455         char *mnt;
1456         struct if_quotactl qctl;
1457         char *obd_type = (char *)qctl.obd_type;
1458         int rc;
1459
1460         memset(&qctl, 0, sizeof(qctl));
1461         qctl.qc_cmd = LUSTRE_Q_QUOTAON;
1462
1463         optind = 0;
1464         while ((c = getopt(argc, argv, "fgu")) != -1) {
1465                 switch (c) {
1466                 case 'u':
1467                         qctl.qc_type |= 0x01;
1468                         break;
1469                 case 'g':
1470                         qctl.qc_type |= 0x02;
1471                         break;
1472                 case 'f':
1473                         qctl.qc_cmd = LUSTRE_Q_QUOTAOFF;
1474                         break;
1475                 default:
1476                         fprintf(stderr, "error: %s: option '-%c' "
1477                                         "unrecognized\n", argv[0], c);
1478                         return CMD_HELP;
1479                 }
1480         }
1481
1482         if (qctl.qc_type)
1483                 qctl.qc_type--;
1484         else /* by default, enable quota for both user & group */
1485                 qctl.qc_type = 0x02;
1486
1487         if (argc == optind)
1488                 return CMD_HELP;
1489
1490         mnt = argv[optind];
1491
1492         rc = llapi_quotactl(mnt, &qctl);
1493         if (rc) {
1494                 if (rc == -EALREADY) {
1495                         rc = 0;
1496                 } else if (rc == -ENOENT) {
1497                         fprintf(stderr, "error: cannot find quota database, "
1498                                         "make sure you have run quotacheck\n");
1499                 } else {
1500                         if (*obd_type)
1501                                 fprintf(stderr, "%s %s ", obd_type,
1502                                         obd_uuid2str(&qctl.obd_uuid));
1503                         fprintf(stderr, "%s failed: %s\n", argv[0],
1504                                 strerror(-rc));
1505                 }
1506         }
1507
1508         return rc;
1509 }
1510
1511 static int lfs_quotaoff(int argc, char **argv)
1512 {
1513         int c;
1514         char *mnt;
1515         struct if_quotactl qctl;
1516         char *obd_type = (char *)qctl.obd_type;
1517         int rc;
1518
1519         memset(&qctl, 0, sizeof(qctl));
1520         qctl.qc_cmd = LUSTRE_Q_QUOTAOFF;
1521
1522         optind = 0;
1523         while ((c = getopt(argc, argv, "gu")) != -1) {
1524                 switch (c) {
1525                 case 'u':
1526                         qctl.qc_type |= 0x01;
1527                         break;
1528                 case 'g':
1529                         qctl.qc_type |= 0x02;
1530                         break;
1531                 default:
1532                         fprintf(stderr, "error: %s: option '-%c' "
1533                                         "unrecognized\n", argv[0], c);
1534                         return CMD_HELP;
1535                 }
1536         }
1537
1538         if (qctl.qc_type)
1539                 qctl.qc_type--;
1540         else /* by default, disable quota for both user & group */
1541                 qctl.qc_type = 0x02;
1542
1543         if (argc == optind)
1544                 return CMD_HELP;
1545
1546         mnt = argv[optind];
1547
1548         rc = llapi_quotactl(mnt, &qctl);
1549         if (rc) {
1550                 if (rc == -EALREADY) {
1551                         rc = 0;
1552                 } else {
1553                         if (*obd_type)
1554                                 fprintf(stderr, "%s %s ", obd_type,
1555                                         obd_uuid2str(&qctl.obd_uuid));
1556                         fprintf(stderr, "quotaoff failed: %s\n",
1557                                 strerror(-rc));
1558                 }
1559         }
1560
1561         return rc;
1562 }
1563
1564 static int lfs_quotainv(int argc, char **argv)
1565 {
1566         int c;
1567         char *mnt;
1568         struct if_quotactl qctl;
1569         int rc;
1570
1571         memset(&qctl, 0, sizeof(qctl));
1572         qctl.qc_cmd = LUSTRE_Q_INVALIDATE;
1573
1574         optind = 0;
1575         while ((c = getopt(argc, argv, "fgu")) != -1) {
1576                 switch (c) {
1577                 case 'u':
1578                         qctl.qc_type |= 0x01;
1579                         break;
1580                 case 'g':
1581                         qctl.qc_type |= 0x02;
1582                         break;
1583                 case 'f':
1584                         qctl.qc_cmd = LUSTRE_Q_FINVALIDATE;
1585                         break;
1586                 default:
1587                         fprintf(stderr, "error: %s: option '-%c' "
1588                                         "unrecognized\n", argv[0], c);
1589                         return CMD_HELP;
1590                 }
1591         }
1592
1593         if (qctl.qc_type)
1594                 qctl.qc_type--;
1595         else /* by default, invalidate quota for both user & group */
1596                 qctl.qc_type = 0x02;
1597
1598         if (argc == optind)
1599                 return CMD_HELP;
1600
1601         mnt = argv[optind];
1602
1603         rc = llapi_quotactl(mnt, &qctl);
1604         if (rc) {
1605                 fprintf(stderr, "quotainv failed: %s\n", strerror(-rc));
1606                 return rc;
1607         }
1608
1609         return 0;
1610 }
1611
1612 #define ARG2INT(nr, str, msg)                                           \
1613 do {                                                                    \
1614         char *endp;                                                     \
1615         nr = strtol(str, &endp, 0);                                     \
1616         if (*endp) {                                                    \
1617                 fprintf(stderr, "error: bad %s: %s\n", msg, str);       \
1618                 return CMD_HELP;                                        \
1619         }                                                               \
1620 } while (0)
1621
1622 #define ADD_OVERFLOW(a,b) ((a + b) < a) ? (a = ULONG_MAX) : (a = a + b)
1623
1624 /* Convert format time string "XXwXXdXXhXXmXXs" into seconds value
1625  * returns the value or ULONG_MAX on integer overflow or incorrect format
1626  * Notes:
1627  *        1. the order of specifiers is arbitrary (may be: 5w3s or 3s5w)
1628  *        2. specifiers may be encountered multiple times (2s3s is 5 seconds)
1629  *        3. empty integer value is interpreted as 0
1630  */
1631 static unsigned long str2sec(const char* timestr)
1632 {
1633         const char spec[] = "smhdw";
1634         const unsigned long mult[] = {1, 60, 60*60, 24*60*60, 7*24*60*60};
1635         unsigned long val = 0;
1636         char *tail;
1637
1638         if (strpbrk(timestr, spec) == NULL) {
1639                 /* no specifiers inside the time string,
1640                    should treat it as an integer value */
1641                 val = strtoul(timestr, &tail, 10);
1642                 return *tail ? ULONG_MAX : val;
1643         }
1644
1645         /* format string is XXwXXdXXhXXmXXs */
1646         while (*timestr) {
1647                 unsigned long v;
1648                 int ind;
1649                 char* ptr;
1650
1651                 v = strtoul(timestr, &tail, 10);
1652                 if (v == ULONG_MAX || *tail == '\0')
1653                         /* value too large (ULONG_MAX or more)
1654                            or missing specifier */
1655                         goto error;
1656
1657                 ptr = strchr(spec, *tail);
1658                 if (ptr == NULL)
1659                         /* unknown specifier */
1660                         goto error;
1661
1662                 ind = ptr - spec;
1663
1664                 /* check if product will overflow the type */
1665                 if (!(v < ULONG_MAX / mult[ind]))
1666                         goto error;
1667
1668                 ADD_OVERFLOW(val, mult[ind] * v);
1669                 if (val == ULONG_MAX)
1670                         goto error;
1671
1672                 timestr = tail + 1;
1673         }
1674
1675         return val;
1676
1677 error:
1678         return ULONG_MAX;
1679 }
1680
1681 #define ARG2ULL(nr, str, def_units)                                     \
1682 do {                                                                    \
1683         unsigned long long limit, units = def_units;                    \
1684         int rc;                                                         \
1685                                                                         \
1686         rc = parse_size(str, &limit, &units, 1);                        \
1687         if (rc < 0) {                                                   \
1688                 fprintf(stderr, "error: bad limit value %s\n", str);    \
1689                 return CMD_HELP;                                        \
1690         }                                                               \
1691         nr = limit;                                                     \
1692 } while (0)
1693
1694 static inline int has_times_option(int argc, char **argv)
1695 {
1696         int i;
1697
1698         for (i = 1; i < argc; i++)
1699                 if (!strcmp(argv[i], "-t"))
1700                         return 1;
1701
1702         return 0;
1703 }
1704
1705 int lfs_setquota_times(int argc, char **argv)
1706 {
1707         int c, rc;
1708         struct if_quotactl qctl;
1709         char *mnt, *obd_type = (char *)qctl.obd_type;
1710         struct obd_dqblk *dqb = &qctl.qc_dqblk;
1711         struct obd_dqinfo *dqi = &qctl.qc_dqinfo;
1712         struct option long_opts[] = {
1713                 {"block-grace",     required_argument, 0, 'b'},
1714                 {"group",           no_argument,       0, 'g'},
1715                 {"inode-grace",     required_argument, 0, 'i'},
1716                 {"times",           no_argument,       0, 't'},
1717                 {"user",            no_argument,       0, 'u'},
1718                 {0, 0, 0, 0}
1719         };
1720
1721         memset(&qctl, 0, sizeof(qctl));
1722         qctl.qc_cmd  = LUSTRE_Q_SETINFO;
1723         qctl.qc_type = UGQUOTA;
1724
1725         optind = 0;
1726         while ((c = getopt_long(argc, argv, "b:gi:tu", long_opts, NULL)) != -1) {
1727                 switch (c) {
1728                 case 'u':
1729                 case 'g':
1730                         if (qctl.qc_type != UGQUOTA) {
1731                                 fprintf(stderr, "error: -u and -g can't be used "
1732                                                 "more than once\n");
1733                                 return CMD_HELP;
1734                         }
1735                         qctl.qc_type = (c == 'u') ? USRQUOTA : GRPQUOTA;
1736                         break;
1737                 case 'b':
1738                         if ((dqi->dqi_bgrace = str2sec(optarg)) == ULONG_MAX) {
1739                                 fprintf(stderr, "error: bad block-grace: %s\n",
1740                                         optarg);
1741                                 return CMD_HELP;
1742                         }
1743                         dqb->dqb_valid |= QIF_BTIME;
1744                         break;
1745                 case 'i':
1746                         if ((dqi->dqi_igrace = str2sec(optarg)) == ULONG_MAX) {
1747                                 fprintf(stderr, "error: bad inode-grace: %s\n",
1748                                         optarg);
1749                                 return CMD_HELP;
1750                         }
1751                         dqb->dqb_valid |= QIF_ITIME;
1752                         break;
1753                 case 't': /* Yes, of course! */
1754                         break;
1755                 default: /* getopt prints error message for us when opterr != 0 */
1756                         return CMD_HELP;
1757                 }
1758         }
1759
1760         if (qctl.qc_type == UGQUOTA) {
1761                 fprintf(stderr, "error: neither -u nor -g specified\n");
1762                 return CMD_HELP;
1763         }
1764
1765         if (optind != argc - 1) {
1766                 fprintf(stderr, "error: unexpected parameters encountered\n");
1767                 return CMD_HELP;
1768         }
1769
1770         mnt = argv[optind];
1771         rc = llapi_quotactl(mnt, &qctl);
1772         if (rc) {
1773                 if (*obd_type)
1774                         fprintf(stderr, "%s %s ", obd_type,
1775                                 obd_uuid2str(&qctl.obd_uuid));
1776                 fprintf(stderr, "setquota failed: %s\n", strerror(-rc));
1777                 return rc;
1778         }
1779
1780         return 0;
1781 }
1782
1783 #define BSLIMIT (1 << 0)
1784 #define BHLIMIT (1 << 1)
1785 #define ISLIMIT (1 << 2)
1786 #define IHLIMIT (1 << 3)
1787
1788 int lfs_setquota(int argc, char **argv)
1789 {
1790         int c, rc;
1791         struct if_quotactl qctl;
1792         char *mnt, *obd_type = (char *)qctl.obd_type;
1793         struct obd_dqblk *dqb = &qctl.qc_dqblk;
1794         struct option long_opts[] = {
1795                 {"block-softlimit", required_argument, 0, 'b'},
1796                 {"block-hardlimit", required_argument, 0, 'B'},
1797                 {"group",           required_argument, 0, 'g'},
1798                 {"inode-softlimit", required_argument, 0, 'i'},
1799                 {"inode-hardlimit", required_argument, 0, 'I'},
1800                 {"user",            required_argument, 0, 'u'},
1801                 {0, 0, 0, 0}
1802         };
1803         unsigned limit_mask = 0;
1804         char *endptr;
1805
1806         if (has_times_option(argc, argv))
1807                 return lfs_setquota_times(argc, argv);
1808
1809         memset(&qctl, 0, sizeof(qctl));
1810         qctl.qc_cmd  = LUSTRE_Q_SETQUOTA;
1811         qctl.qc_type = UGQUOTA; /* UGQUOTA makes no sense for setquota,
1812                                  * so it can be used as a marker that qc_type
1813                                  * isn't reinitialized from command line */
1814
1815         optind = 0;
1816         while ((c = getopt_long(argc, argv, "b:B:g:i:I:u:", long_opts, NULL)) != -1) {
1817                 switch (c) {
1818                 case 'u':
1819                 case 'g':
1820                         if (qctl.qc_type != UGQUOTA) {
1821                                 fprintf(stderr, "error: -u and -g can't be used"
1822                                                 " more than once\n");
1823                                 return CMD_HELP;
1824                         }
1825                         qctl.qc_type = (c == 'u') ? USRQUOTA : GRPQUOTA;
1826                         rc = name2id(&qctl.qc_id, optarg,
1827                                      (qctl.qc_type == USRQUOTA) ? USER : GROUP);
1828                         if (rc) {
1829                                 qctl.qc_id = strtoul(optarg, &endptr, 10);
1830                                 if (*endptr != '\0') {
1831                                         fprintf(stderr, "error: can't find id "
1832                                                 "for name %s\n", optarg);
1833                                         return CMD_HELP;
1834                                 }
1835                         }
1836                         break;
1837                 case 'b':
1838                         ARG2ULL(dqb->dqb_bsoftlimit, optarg, 1024);
1839                         dqb->dqb_bsoftlimit >>= 10;
1840                         limit_mask |= BSLIMIT;
1841                         break;
1842                 case 'B':
1843                         ARG2ULL(dqb->dqb_bhardlimit, optarg, 1024);
1844                         dqb->dqb_bhardlimit >>= 10;
1845                         limit_mask |= BHLIMIT;
1846                         break;
1847                 case 'i':
1848                         ARG2ULL(dqb->dqb_isoftlimit, optarg, 1);
1849                         limit_mask |= ISLIMIT;
1850                         break;
1851                 case 'I':
1852                         ARG2ULL(dqb->dqb_ihardlimit, optarg, 1);
1853                         limit_mask |= IHLIMIT;
1854                         break;
1855                 default: /* getopt prints error message for us when opterr != 0 */
1856                         return CMD_HELP;
1857                 }
1858         }
1859
1860         if (qctl.qc_type == UGQUOTA) {
1861                 fprintf(stderr, "error: neither -u nor -g was specified\n");
1862                 return CMD_HELP;
1863         }
1864
1865         if (limit_mask == 0) {
1866                 fprintf(stderr, "error: at least one limit must be specified\n");
1867                 return CMD_HELP;
1868         }
1869
1870         if (optind != argc - 1) {
1871                 fprintf(stderr, "error: unexpected parameters encountered\n");
1872                 return CMD_HELP;
1873         }
1874
1875         mnt = argv[optind];
1876
1877         if ((!(limit_mask & BHLIMIT) ^ !(limit_mask & BSLIMIT)) ||
1878             (!(limit_mask & IHLIMIT) ^ !(limit_mask & ISLIMIT))) {
1879                 /* sigh, we can't just set blimits/ilimits */
1880                 struct if_quotactl tmp_qctl = {.qc_cmd  = LUSTRE_Q_GETQUOTA,
1881                                                .qc_type = qctl.qc_type,
1882                                                .qc_id   = qctl.qc_id};
1883
1884                 rc = llapi_quotactl(mnt, &tmp_qctl);
1885                 if (rc < 0) {
1886                         fprintf(stderr, "error: setquota failed while retrieving"
1887                                         " current quota settings (%s)\n",
1888                                         strerror(-rc));
1889                         return rc;
1890                 }
1891
1892                 if (!(limit_mask & BHLIMIT))
1893                         dqb->dqb_bhardlimit = tmp_qctl.qc_dqblk.dqb_bhardlimit;
1894                 if (!(limit_mask & BSLIMIT))
1895                         dqb->dqb_bsoftlimit = tmp_qctl.qc_dqblk.dqb_bsoftlimit;
1896                 if (!(limit_mask & IHLIMIT))
1897                         dqb->dqb_ihardlimit = tmp_qctl.qc_dqblk.dqb_ihardlimit;
1898                 if (!(limit_mask & ISLIMIT))
1899                         dqb->dqb_isoftlimit = tmp_qctl.qc_dqblk.dqb_isoftlimit;
1900
1901                 /* Keep grace times if we have got no softlimit arguments */
1902                 if ((limit_mask & BHLIMIT) && !(limit_mask & BSLIMIT)) {
1903                         dqb->dqb_valid |= QIF_BTIME;
1904                         dqb->dqb_btime = tmp_qctl.qc_dqblk.dqb_btime;
1905                 }
1906
1907                 if ((limit_mask & IHLIMIT) && !(limit_mask & ISLIMIT)) {
1908                         dqb->dqb_valid |= QIF_ITIME;
1909                         dqb->dqb_itime = tmp_qctl.qc_dqblk.dqb_itime;
1910                 }
1911         }
1912
1913         dqb->dqb_valid |= (limit_mask & (BHLIMIT | BSLIMIT)) ? QIF_BLIMITS : 0;
1914         dqb->dqb_valid |= (limit_mask & (IHLIMIT | ISLIMIT)) ? QIF_ILIMITS : 0;
1915
1916         rc = llapi_quotactl(mnt, &qctl);
1917         if (rc) {
1918                 if (*obd_type)
1919                         fprintf(stderr, "%s %s ", obd_type,
1920                                 obd_uuid2str(&qctl.obd_uuid));
1921                 fprintf(stderr, "setquota failed: %s\n", strerror(-rc));
1922                 return rc;
1923         }
1924
1925         return 0;
1926 }
1927
1928 static inline char *type2name(int check_type)
1929 {
1930         if (check_type == USRQUOTA)
1931                 return "user";
1932         else if (check_type == GRPQUOTA)
1933                 return "group";
1934         else
1935                 return "unknown";
1936 }
1937
1938 /* Converts seconds value into format string
1939  * result is returned in buf
1940  * Notes:
1941  *        1. result is in descenting order: 1w2d3h4m5s
1942  *        2. zero fields are not filled (except for p. 3): 5d1s
1943  *        3. zero seconds value is presented as "0s"
1944  */
1945 static char * __sec2str(time_t seconds, char *buf)
1946 {
1947         const char spec[] = "smhdw";
1948         const unsigned long mult[] = {1, 60, 60*60, 24*60*60, 7*24*60*60};
1949         unsigned long c;
1950         char *tail = buf;
1951         int i;
1952
1953         for (i = sizeof(mult) / sizeof(mult[0]) - 1 ; i >= 0; i--) {
1954                 c = seconds / mult[i];
1955
1956                 if (c > 0 || (i == 0 && buf == tail))
1957                         tail += snprintf(tail, 40-(tail-buf), "%lu%c", c, spec[i]);
1958
1959                 seconds %= mult[i];
1960         }
1961
1962         return tail;
1963 }
1964
1965 static void sec2str(time_t seconds, char *buf, int rc)
1966 {
1967         char *tail = buf;
1968
1969         if (rc)
1970                 *tail++ = '[';
1971
1972         tail = __sec2str(seconds, tail);
1973
1974         if (rc && tail - buf < 39) {
1975                 *tail++ = ']';
1976                 *tail++ = 0;
1977         }
1978 }
1979
1980 static void diff2str(time_t seconds, char *buf, time_t now)
1981 {
1982
1983         buf[0] = 0;
1984         if (!seconds)
1985                 return;
1986         if (seconds <= now) {
1987                 strcpy(buf, "none");
1988                 return;
1989         }
1990         __sec2str(seconds - now, buf);
1991 }
1992
1993 static void print_quota_title(char *name, struct if_quotactl *qctl)
1994 {
1995         printf("Disk quotas for %s %s (%cid %u):\n",
1996                type2name(qctl->qc_type), name,
1997                *type2name(qctl->qc_type), qctl->qc_id);
1998         printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n",
1999                "Filesystem",
2000                "kbytes", "quota", "limit", "grace",
2001                "files", "quota", "limit", "grace");
2002 }
2003
2004 static void print_quota(char *mnt, struct if_quotactl *qctl, int type, int rc)
2005 {
2006         time_t now;
2007
2008         time(&now);
2009
2010         if (qctl->qc_cmd == LUSTRE_Q_GETQUOTA || qctl->qc_cmd == Q_GETOQUOTA) {
2011                 int bover = 0, iover = 0;
2012                 struct obd_dqblk *dqb = &qctl->qc_dqblk;
2013
2014                 if (dqb->dqb_bhardlimit &&
2015                     toqb(dqb->dqb_curspace) >= dqb->dqb_bhardlimit) {
2016                         bover = 1;
2017                 } else if (dqb->dqb_bsoftlimit && dqb->dqb_btime) {
2018                         if (dqb->dqb_btime > now) {
2019                                 bover = 2;
2020                         } else {
2021                                 bover = 3;
2022                         }
2023                 }
2024
2025                 if (dqb->dqb_ihardlimit &&
2026                     dqb->dqb_curinodes >= dqb->dqb_ihardlimit) {
2027                         iover = 1;
2028                 } else if (dqb->dqb_isoftlimit && dqb->dqb_itime) {
2029                         if (dqb->dqb_btime > now) {
2030                                 iover = 2;
2031                         } else {
2032                                 iover = 3;
2033                         }
2034                 }
2035
2036 #if 0           /* XXX: always print quotas even when no usages */
2037                 if (dqb->dqb_curspace || dqb->dqb_curinodes)
2038 #endif
2039                 {
2040                         char numbuf[3][32];
2041                         char timebuf[40];
2042
2043                         if (strlen(mnt) > 15)
2044                                 printf("%s\n%15s", mnt, "");
2045                         else
2046                                 printf("%15s", mnt);
2047
2048                         if (bover)
2049                                 diff2str(dqb->dqb_btime, timebuf, now);
2050                         if (rc == -EREMOTEIO)
2051                                 sprintf(numbuf[0], LPU64"*",
2052                                         toqb(dqb->dqb_curspace));
2053                         else
2054                                 sprintf(numbuf[0],
2055                                         (dqb->dqb_valid & QIF_SPACE) ?
2056                                         LPU64 : "["LPU64"]",
2057                                         toqb(dqb->dqb_curspace));
2058                         if (type == QC_GENERAL)
2059                                 sprintf(numbuf[1], (dqb->dqb_valid & QIF_BLIMITS)
2060                                         ? LPU64 : "["LPU64"]",
2061                                         dqb->dqb_bsoftlimit);
2062                         else
2063                                 sprintf(numbuf[1], "%s", "-");
2064                         sprintf(numbuf[2], (dqb->dqb_valid & QIF_BLIMITS)
2065                                 ? LPU64 : "["LPU64"]", dqb->dqb_bhardlimit);
2066                         printf(" %7s%c %6s %7s %7s",
2067                                numbuf[0], bover ? '*' : ' ', numbuf[1],
2068                                numbuf[2], bover > 1 ? timebuf : "-");
2069
2070                         if (iover)
2071                                 diff2str(dqb->dqb_itime, timebuf, now);
2072
2073                         sprintf(numbuf[0], (dqb->dqb_valid & QIF_INODES) ?
2074                                 LPU64 : "["LPU64"]", dqb->dqb_curinodes);
2075                        if (type == QC_GENERAL)
2076                                 sprintf(numbuf[1], (dqb->dqb_valid & QIF_ILIMITS)
2077                                         ? LPU64 : "["LPU64"]",
2078                                         dqb->dqb_isoftlimit);
2079                         else
2080                                 sprintf(numbuf[1], "%s", "-");
2081                         sprintf(numbuf[2], (dqb->dqb_valid & QIF_ILIMITS) ?
2082                                 LPU64 : "["LPU64"]", dqb->dqb_ihardlimit);
2083                         if (type != QC_OSTIDX)
2084                                 printf(" %7s%c %6s %7s %7s",
2085                                        numbuf[0], iover ? '*' : ' ', numbuf[1],
2086                                        numbuf[2], iover > 1 ? timebuf : "-");
2087                         else
2088                                 printf(" %7s %7s %7s %7s", "-", "-", "-", "-");
2089                         printf("\n");
2090                 }
2091         } else if (qctl->qc_cmd == LUSTRE_Q_GETINFO ||
2092                    qctl->qc_cmd == Q_GETOINFO) {
2093                 char bgtimebuf[40];
2094                 char igtimebuf[40];
2095
2096                 sec2str(qctl->qc_dqinfo.dqi_bgrace, bgtimebuf, rc);
2097                 sec2str(qctl->qc_dqinfo.dqi_igrace, igtimebuf, rc);
2098                 printf("Block grace time: %s; Inode grace time: %s\n",
2099                        bgtimebuf, igtimebuf);
2100         }
2101 }
2102
2103 static int print_obd_quota(char *mnt, struct if_quotactl *qctl, int is_mdt)
2104 {
2105         int rc = 0, rc1 = 0, count = 0;
2106         __u32 valid = qctl->qc_valid;
2107
2108         rc = llapi_get_obd_count(mnt, &count, is_mdt);
2109         if (rc) {
2110                 fprintf(stderr, "can not get %s count: %s\n",
2111                         is_mdt ? "mdt": "ost", strerror(-rc));
2112                 return rc;
2113         }
2114
2115         for (qctl->qc_idx = 0; qctl->qc_idx < count; qctl->qc_idx++) {
2116                 qctl->qc_valid = is_mdt ? QC_MDTIDX : QC_OSTIDX;
2117                 rc = llapi_quotactl(mnt, qctl);
2118                 if (rc) {
2119                         /* It is remote client case. */
2120                         if (-rc == EOPNOTSUPP) {
2121                                 rc = 0;
2122                                 goto out;
2123                         }
2124
2125                         if (!rc1)
2126                                 rc1 = rc;
2127                         fprintf(stderr, "quotactl %s%d failed.\n",
2128                                 is_mdt ? "mdt": "ost", qctl->qc_idx);
2129                         continue;
2130                 }
2131
2132                 print_quota(obd_uuid2str(&qctl->obd_uuid), qctl, qctl->qc_valid, 0);
2133         }
2134
2135 out:
2136         qctl->qc_valid = valid;
2137         return rc ? : rc1;
2138 }
2139
2140 static int lfs_quota(int argc, char **argv)
2141 {
2142         int c;
2143         char *mnt, *name = NULL;
2144         struct if_quotactl qctl = { .qc_cmd = LUSTRE_Q_GETQUOTA,
2145                                     .qc_type = UGQUOTA };
2146         char *obd_type = (char *)qctl.obd_type;
2147         char *obd_uuid = (char *)qctl.obd_uuid.uuid;
2148         int rc, rc1 = 0, rc2 = 0, rc3 = 0,
2149             verbose = 0, pass = 0, quiet = 0, inacc;
2150         char *endptr;
2151         __u32 valid = QC_GENERAL, idx = 0;
2152
2153         optind = 0;
2154         while ((c = getopt(argc, argv, "gi:I:o:qtuv")) != -1) {
2155                 switch (c) {
2156                 case 'u':
2157                         if (qctl.qc_type != UGQUOTA) {
2158                                 fprintf(stderr, "error: use either -u or -g\n");
2159                                 return CMD_HELP;
2160                         }
2161                         qctl.qc_type = USRQUOTA;
2162                         break;
2163                 case 'g':
2164                         if (qctl.qc_type != UGQUOTA) {
2165                                 fprintf(stderr, "error: use either -u or -g\n");
2166                                 return CMD_HELP;
2167                         }
2168                         qctl.qc_type = GRPQUOTA;
2169                         break;
2170                 case 't':
2171                         qctl.qc_cmd = LUSTRE_Q_GETINFO;
2172                         break;
2173                 case 'o':
2174                         valid = qctl.qc_valid = QC_UUID;
2175                         strncpy(obd_uuid, optarg, sizeof(qctl.obd_uuid));
2176                         break;
2177                 case 'i':
2178                         valid = qctl.qc_valid = QC_MDTIDX;
2179                         idx = qctl.qc_idx = atoi(optarg);
2180                         break;
2181                 case 'I':
2182                         valid = qctl.qc_valid = QC_OSTIDX;
2183                         idx = qctl.qc_idx = atoi(optarg);
2184                         break;
2185                 case 'v':
2186                         verbose = 1;
2187                         break;
2188                 case 'q':
2189                         quiet = 1;
2190                         break;
2191                 default:
2192                         fprintf(stderr, "error: %s: option '-%c' "
2193                                         "unrecognized\n", argv[0], c);
2194                         return CMD_HELP;
2195                 }
2196         }
2197
2198         /* current uid/gid info for "lfs quota /path/to/lustre/mount" */
2199         if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA && qctl.qc_type == UGQUOTA &&
2200             optind == argc - 1) {
2201 ug_output:
2202                 memset(&qctl, 0, sizeof(qctl)); /* spoiled by print_*_quota */
2203                 qctl.qc_cmd = LUSTRE_Q_GETQUOTA;
2204                 qctl.qc_valid = valid;
2205                 qctl.qc_idx = idx;
2206                 if (pass++ == 0) {
2207                         qctl.qc_type = USRQUOTA;
2208                         qctl.qc_id = geteuid();
2209                 } else {
2210                         qctl.qc_type = GRPQUOTA;
2211                         qctl.qc_id = getegid();
2212                 }
2213                 rc = id2name(&name, qctl.qc_id,
2214                              (qctl.qc_type == USRQUOTA) ? USER : GROUP);
2215                 if (rc)
2216                         name = "<unknown>";
2217         /* lfs quota -u username /path/to/lustre/mount */
2218         } else if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA) {
2219                 /* options should be followed by u/g-name and mntpoint */
2220                 if (optind + 2 != argc || qctl.qc_type == UGQUOTA) {
2221                         fprintf(stderr, "error: missing quota argument(s)\n");
2222                         return CMD_HELP;
2223                 }
2224
2225                 name = argv[optind++];
2226                 rc = name2id(&qctl.qc_id, name,
2227                              (qctl.qc_type == USRQUOTA) ? USER : GROUP);
2228                 if (rc) {
2229                         qctl.qc_id = strtoul(name, &endptr, 10);
2230                         if (*endptr != '\0') {
2231                                 fprintf(stderr, "error: can't find id for name "
2232                                         "%s\n", name);
2233                                 return CMD_HELP;
2234                         }
2235                 }
2236         } else if (optind + 1 != argc || qctl.qc_type == UGQUOTA) {
2237                 fprintf(stderr, "error: missing quota info argument(s)\n");
2238                 return CMD_HELP;
2239         }
2240
2241         mnt = argv[optind];
2242
2243         rc1 = llapi_quotactl(mnt, &qctl);
2244         if (rc1 < 0) {
2245                 switch (rc1) {
2246                 case -ESRCH:
2247                         fprintf(stderr, "%s quotas are not enabled.\n",
2248                                 qctl.qc_type == USRQUOTA ? "user" : "group");
2249                         goto out;
2250                 case -EPERM:
2251                         fprintf(stderr, "Permission denied.\n");
2252                 case -ENOENT:
2253                         /* We already got a "No such file..." message. */
2254                         goto out;
2255                 default:
2256                         fprintf(stderr, "Unexpected quotactl error: %s\n",
2257                                 strerror(-rc1));
2258                 }
2259         }
2260
2261         if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA && !quiet)
2262                 print_quota_title(name, &qctl);
2263
2264         if (rc1 && *obd_type)
2265                 fprintf(stderr, "%s %s ", obd_type, obd_uuid);
2266
2267         if (qctl.qc_valid != QC_GENERAL)
2268                 mnt = "";
2269
2270         inacc = (qctl.qc_cmd == LUSTRE_Q_GETQUOTA) &&
2271                 ((qctl.qc_dqblk.dqb_valid&(QIF_LIMITS|QIF_USAGE))!=(QIF_LIMITS|QIF_USAGE));
2272
2273         print_quota(mnt, &qctl, QC_GENERAL, rc1);
2274
2275         if (qctl.qc_valid == QC_GENERAL && qctl.qc_cmd != LUSTRE_Q_GETINFO && verbose) {
2276                 rc2 = print_obd_quota(mnt, &qctl, 1);
2277                 rc3 = print_obd_quota(mnt, &qctl, 0);
2278         }
2279
2280         if (rc1 || rc2 || rc3 || inacc)
2281                 printf("Some errors happened when getting quota info. "
2282                        "Some devices may be not working or deactivated. "
2283                        "The data in \"[]\" is inaccurate.\n");
2284
2285 out:
2286         if (pass == 1)
2287                 goto ug_output;
2288
2289         return rc1;
2290 }
2291 #endif /* HAVE_SYS_QUOTA_H! */
2292
2293 static int flushctx_ioctl(char *mp)
2294 {
2295         int fd, rc;
2296
2297         fd = open(mp, O_RDONLY);
2298         if (fd == -1) {
2299                 fprintf(stderr, "flushctx: error open %s: %s\n",
2300                         mp, strerror(errno));
2301                 return -1;
2302         }
2303
2304         rc = ioctl(fd, LL_IOC_FLUSHCTX);
2305         if (rc == -1)
2306                 fprintf(stderr, "flushctx: error ioctl %s: %s\n",
2307                         mp, strerror(errno));
2308
2309         close(fd);
2310         return rc;
2311 }
2312
2313 static int lfs_flushctx(int argc, char **argv)
2314 {
2315         int     kdestroy = 0, c;
2316         FILE   *proc;
2317         char    procline[PATH_MAX], *line;
2318         int     rc = 0;
2319
2320         optind = 0;
2321         while ((c = getopt(argc, argv, "k")) != -1) {
2322                 switch (c) {
2323                 case 'k':
2324                         kdestroy = 1;
2325                         break;
2326                 default:
2327                         fprintf(stderr, "error: %s: option '-%c' "
2328                                         "unrecognized\n", argv[0], c);
2329                         return CMD_HELP;
2330                 }
2331         }
2332
2333         if (kdestroy) {
2334             int rc;
2335             if ((rc = system("kdestroy > /dev/null")) != 0) {
2336                 rc = WEXITSTATUS(rc);
2337                 fprintf(stderr, "error destroying tickets: %d, continuing\n", rc);
2338             }
2339         }
2340
2341         if (optind >= argc) {
2342                 /* flush for all mounted lustre fs. */
2343                 proc = fopen("/proc/mounts", "r");
2344                 if (!proc) {
2345                         fprintf(stderr, "error: %s: can't open /proc/mounts\n",
2346                                 argv[0]);
2347                         return -1;
2348                 }
2349
2350                 while ((line = fgets(procline, PATH_MAX, proc)) != NULL) {
2351                         char dev[PATH_MAX];
2352                         char mp[PATH_MAX];
2353                         char fs[PATH_MAX];
2354
2355                         if (sscanf(line, "%s %s %s", dev, mp, fs) != 3) {
2356                                 fprintf(stderr, "%s: unexpected format in "
2357                                                 "/proc/mounts\n",
2358                                         argv[0]);
2359                                 return -1;
2360                         }
2361
2362                         if (strcmp(fs, "lustre") != 0)
2363                                 continue;
2364                         /* we use '@' to determine it's a client. are there
2365                          * any other better way?
2366                          */
2367                         if (strchr(dev, '@') == NULL)
2368                                 continue;
2369
2370                         if (flushctx_ioctl(mp))
2371                                 rc = -1;
2372                 }
2373         } else {
2374                 /* flush fs as specified */
2375                 while (optind < argc) {
2376                         if (flushctx_ioctl(argv[optind++]))
2377                                 rc = -1;
2378                 }
2379         }
2380
2381         return rc;
2382 }
2383
2384 static int lfs_lsetfacl(int argc, char **argv)
2385 {
2386         argv[0]++;
2387         return(llapi_lsetfacl(argc, argv));
2388 }
2389
2390 static int lfs_lgetfacl(int argc, char **argv)
2391 {
2392         argv[0]++;
2393         return(llapi_lgetfacl(argc, argv));
2394 }
2395
2396 static int lfs_rsetfacl(int argc, char **argv)
2397 {
2398         argv[0]++;
2399         return(llapi_rsetfacl(argc, argv));
2400 }
2401
2402 static int lfs_rgetfacl(int argc, char **argv)
2403 {
2404         argv[0]++;
2405         return(llapi_rgetfacl(argc, argv));
2406 }
2407
2408 static int lfs_cp(int argc, char **argv)
2409 {
2410         return(llapi_cp(argc, argv));
2411 }
2412
2413 static int lfs_ls(int argc, char **argv)
2414 {
2415         return(llapi_ls(argc, argv));
2416 }
2417
2418 static int lfs_changelog(int argc, char **argv)
2419 {
2420         void *changelog_priv;
2421         struct changelog_rec *rec;
2422         long long startrec = 0, endrec = 0;
2423         char *mdd;
2424         struct option long_opts[] = {
2425                 {"follow", no_argument, 0, 'f'},
2426                 {0, 0, 0, 0}
2427         };
2428         char short_opts[] = "f";
2429         int rc, follow = 0;
2430
2431         optind = 0;
2432         while ((rc = getopt_long(argc, argv, short_opts,
2433                                 long_opts, NULL)) != -1) {
2434                 switch (rc) {
2435                 case 'f':
2436                         follow++;
2437                         break;
2438                 case '?':
2439                         return CMD_HELP;
2440                 default:
2441                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
2442                                 argv[0], argv[optind - 1]);
2443                         return CMD_HELP;
2444                 }
2445         }
2446         if (optind >= argc)
2447                 return CMD_HELP;
2448
2449         mdd = argv[optind++];
2450         if (argc > optind)
2451                 startrec = strtoll(argv[optind++], NULL, 10);
2452         if (argc > optind)
2453                 endrec = strtoll(argv[optind++], NULL, 10);
2454
2455         rc = llapi_changelog_start(&changelog_priv,
2456                                    CHANGELOG_FLAG_BLOCK |
2457                                    (follow ? CHANGELOG_FLAG_FOLLOW : 0),
2458                                    mdd, startrec);
2459         if (rc < 0) {
2460                 fprintf(stderr, "Can't start changelog: %s\n",
2461                         strerror(errno = -rc));
2462                 return rc;
2463         }
2464
2465         while ((rc = llapi_changelog_recv(changelog_priv, &rec)) == 0) {
2466                 time_t secs;
2467                 struct tm ts;
2468
2469                 if (endrec && rec->cr_index > endrec) {
2470                         llapi_changelog_free(&rec);
2471                         break;
2472                 }
2473                 if (rec->cr_index < startrec) {
2474                         llapi_changelog_free(&rec);
2475                         continue;
2476                 }
2477
2478                 secs = rec->cr_time >> 30;
2479                 gmtime_r(&secs, &ts);
2480                 printf(LPU64" %02d%-5s %02d:%02d:%02d.%06d %04d.%02d.%02d "
2481                        "0x%x t="DFID, rec->cr_index, rec->cr_type,
2482                        changelog_type2str(rec->cr_type),
2483                        ts.tm_hour, ts.tm_min, ts.tm_sec,
2484                        (int)(rec->cr_time & ((1<<30) - 1)),
2485                        ts.tm_year+1900, ts.tm_mon+1, ts.tm_mday,
2486                        rec->cr_flags & CLF_FLAGMASK, PFID(&rec->cr_tfid));
2487                 if (rec->cr_namelen)
2488                         /* namespace rec includes parent and filename */
2489                         printf(" p="DFID" %.*s\n", PFID(&rec->cr_pfid),
2490                                rec->cr_namelen, rec->cr_name);
2491                 else
2492                         printf("\n");
2493
2494                 llapi_changelog_free(&rec);
2495         }
2496
2497         llapi_changelog_fini(&changelog_priv);
2498
2499         if (rc < 0)
2500                 fprintf(stderr, "Changelog: %s\n", strerror(errno = -rc));
2501
2502         return (rc == 1 ? 0 : rc);
2503 }
2504
2505 static int lfs_changelog_clear(int argc, char **argv)
2506 {
2507         long long endrec;
2508         int rc;
2509
2510         if (argc != 4)
2511                 return CMD_HELP;
2512
2513         endrec = strtoll(argv[3], NULL, 10);
2514
2515         rc = llapi_changelog_clear(argv[1], argv[2], endrec);
2516         if (rc)
2517                 fprintf(stderr, "%s error: %s\n", argv[0],
2518                         strerror(errno = -rc));
2519         return rc;
2520 }
2521
2522 static int lfs_fid2path(int argc, char **argv)
2523 {
2524         struct option long_opts[] = {
2525                 {"cur", no_argument, 0, 'c'},
2526                 {"link", required_argument, 0, 'l'},
2527                 {"rec", required_argument, 0, 'r'},
2528                 {0, 0, 0, 0}
2529         };
2530         char  short_opts[] = "cl:r:";
2531         char *device, *fid, *path;
2532         long long recno = -1;
2533         int linkno = -1;
2534         int lnktmp;
2535         int printcur = 0;
2536         int rc;
2537
2538         optind = 0;
2539
2540         while ((rc = getopt_long(argc, argv, short_opts,
2541                                 long_opts, NULL)) != -1) {
2542                 switch (rc) {
2543                 case 'c':
2544                         printcur++;
2545                         break;
2546                 case 'l':
2547                         linkno = strtol(optarg, NULL, 10);
2548                         break;
2549                 case 'r':
2550                         recno = strtoll(optarg, NULL, 10);
2551                         break;
2552                 case '?':
2553                         return CMD_HELP;
2554                 default:
2555                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
2556                                 argv[0], argv[optind - 1]);
2557                         return CMD_HELP;
2558                 }
2559         }
2560         device = argv[optind++];
2561         fid = argv[optind++];
2562         if (optind != argc)
2563                 return CMD_HELP;
2564
2565         path = calloc(1, PATH_MAX);
2566
2567         lnktmp = (linkno >= 0) ? linkno : 0;
2568         while (1) {
2569                 int oldtmp = lnktmp;
2570                 long long rectmp = recno;
2571                 rc = llapi_fid2path(device, fid, path, PATH_MAX, &rectmp,
2572                                     &lnktmp);
2573                 if (rc < 0) {
2574                         fprintf(stderr, "%s error: %s\n", argv[0],
2575                                 strerror(errno = -rc));
2576                         break;
2577                 }
2578
2579                 if (printcur)
2580                         fprintf(stdout, "%lld ", rectmp);
2581                 if (device[0] == '/') {
2582                         fprintf(stdout, "%s", device);
2583                         if (device[strlen(device) - 1] != '/')
2584                                 fprintf(stdout, "/");
2585                 } else if (path[0] == '\0') {
2586                         fprintf(stdout, "/");
2587                 }
2588                 fprintf(stdout, "%s\n", path);
2589
2590                 if (linkno >= 0)
2591                         /* specified linkno */
2592                         break;
2593                 if (oldtmp == lnktmp)
2594                         /* no more links */
2595                         break;
2596         }
2597
2598         free(path);
2599         return rc;
2600 }
2601
2602 static int lfs_path2fid(int argc, char **argv)
2603 {
2604         char *path;
2605         lustre_fid fid;
2606         int rc;
2607
2608         if (argc != 2)
2609                 return CMD_HELP;
2610
2611         path = argv[1];
2612         rc = llapi_path2fid(path, &fid);
2613         if (rc) {
2614                 fprintf(stderr, "can't get fid for %s: %s\n", path,
2615                         strerror(errno = -rc));
2616                 return rc;
2617         }
2618
2619         printf(DFID"\n", PFID(&fid));
2620
2621         return 0;
2622 }
2623
2624 int main(int argc, char **argv)
2625 {
2626         int rc;
2627
2628         setlinebuf(stdout);
2629
2630         ptl_initialize(argc, argv);
2631         if (obd_initialize(argc, argv) < 0)
2632                 exit(2);
2633         if (dbg_initialize(argc, argv) < 0)
2634                 exit(3);
2635
2636         Parser_init("lfs > ", cmdlist);
2637
2638         if (argc > 1) {
2639                 rc = Parser_execarg(argc - 1, argv + 1, cmdlist);
2640         } else {
2641                 rc = Parser_commands();
2642         }
2643
2644         obd_finalize(argc, argv);
2645         return rc < 0 ? -rc : rc;
2646 }
2647