ec4d39459dcddfb8b2e1abba174df22810155282
[dragonfly.git] / sbin / vquota / vquota.c
1 /*
2  * Copyright (c) 2011, 2012 François Tigeot <ftigeot@wolfpond.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in
13  *    the documentation and/or other materials provided with the
14  *    distribution.
15  * 3. Neither the name of The DragonFly Project nor the names of its
16  *    contributors may be used to endorse or promote products derived
17  *    from this software without specific, prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
23  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33 #include <sys/stat.h>
34 #include <sys/mount.h>
35 #include <sys/vfs_quota.h>
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <err.h>
40 #include <string.h>
41 #include <fts.h>
42 #include <libprop/proplib.h>
43 #include <unistd.h>
44 #include <sys/tree.h>
45 #include <errno.h>
46 #include <inttypes.h>
47 #include <sys/types.h>
48 #include <libutil.h>
49 #include <pwd.h>
50 #include <grp.h>
51
52 static bool flag_debug = 0;
53 static bool flag_humanize = 0;
54 static bool flag_resolve_ids = 1;
55
56 static void usage(int);
57 static int get_dirsize(char *);
58 static int get_fslist(void);
59
60 static void
61 usage(int retcode)
62 {
63         fprintf(stderr, "usage: vquota [-Dhn] check directory\n");
64         fprintf(stderr, "       vquota [-Dhn] lsfs\n");
65         fprintf(stderr, "       vquota [-Dhn] limit mount_point size\n");
66         fprintf(stderr, "       vquota [-Dhn] ulim  mount_point user  size\n");
67         fprintf(stderr, "       vquota [-Dhn] glim  mount_point group size\n");
68         fprintf(stderr, "       vquota [-Dhn] show mount_point\n");
69         fprintf(stderr, "       vquota [-Dhn] sync mount_point\n");
70         exit(retcode);
71 }
72
73 /*
74  * Inode numbers with more than one hard link often come in groups;
75  * use linear arrays of 1024 ones as the basic unit of allocation.
76  * We only need to check if the inodes have been previously processed,
77  * bit arrays are perfect for that purpose.
78  */
79 #define HL_CHUNK_BITS           10
80 #define HL_CHUNK_ENTRIES        (1<<HL_CHUNK_BITS)
81 #define HL_CHUNK_MASK           (HL_CHUNK_ENTRIES - 1)
82 #define BA_UINT64_BITS          6
83 #define BA_UINT64_ENTRIES       (1<<BA_UINT64_BITS)
84 #define BA_UINT64_MASK          (BA_UINT64_ENTRIES - 1)
85
86 struct hl_node {
87         RB_ENTRY(hl_node)       rb_entry;
88         ino_t                   ino_left_bits;
89         uint64_t                hl_chunk[HL_CHUNK_ENTRIES/64];
90 };
91
92 RB_HEAD(hl_tree,hl_node)        hl_root;
93
94 RB_PROTOTYPE(hl_tree, hl_node, rb_entry, rb_hl_node_cmp);
95
96 static int
97 rb_hl_node_cmp(struct hl_node *a, struct hl_node *b);
98
99 RB_GENERATE(hl_tree, hl_node, rb_entry, rb_hl_node_cmp);
100
101 struct hl_node* hl_node_insert(ino_t);
102
103
104 static int
105 rb_hl_node_cmp(struct hl_node *a, struct hl_node *b)
106 {
107         if (a->ino_left_bits < b->ino_left_bits)
108                 return(-1);
109         else if (a->ino_left_bits > b->ino_left_bits)
110                 return(1);
111         return(0);
112 }
113
114 struct hl_node* hl_node_insert(ino_t inode)
115 {
116         struct hl_node *hlp, *res;
117
118         hlp = malloc(sizeof(struct hl_node));
119         if (hlp == NULL) {
120                 /* shouldn't happen */
121                 printf("hl_node_insert(): malloc failed\n");
122                 exit(ENOMEM);
123         }
124         bzero(hlp, sizeof(struct hl_node));
125
126         hlp->ino_left_bits = (inode >> HL_CHUNK_BITS);
127         res = RB_INSERT(hl_tree, &hl_root, hlp);
128
129         if (res != NULL)        /* shouldn't happen */
130                 printf("hl_node_insert(): RB_INSERT didn't return NULL\n");
131
132         return hlp;
133 }
134
135 /*
136  * hl_register: register an inode number in a rb-tree of bit arrays
137  * returns:
138  * - true if the inode was already processed
139  * - false otherwise
140  */
141 static bool
142 hl_register(ino_t inode)
143 {
144         struct hl_node hl_find, *hlp;
145         uint64_t ino_right_bits, ba_index, ba_offset;
146         uint64_t bitmask, bitval;
147         bool retval = false;
148
149         /* calculate the different addresses of the wanted bit */
150         hl_find.ino_left_bits = (inode >> HL_CHUNK_BITS);
151
152         ino_right_bits = inode & HL_CHUNK_MASK;
153         ba_index  = ino_right_bits >> BA_UINT64_BITS;
154         ba_offset = ino_right_bits & BA_UINT64_MASK;
155
156         /* no existing node? create and initialize it */
157         if ((hlp = RB_FIND(hl_tree, &hl_root, &hl_find)) == NULL) {
158                 hlp = hl_node_insert(inode);
159         }
160
161         /* node was found, check the bit value */
162         bitmask = 1 << ba_offset;
163         bitval = hlp->hl_chunk[ba_index] & bitmask;
164         if (bitval != 0) {
165                 retval = true;
166         }
167
168         /* set the bit */
169         hlp->hl_chunk[ba_index] |= bitmask;
170
171         return retval;
172 }
173
174 /* global variable used by get_dir_size() */
175 uint64_t global_size;
176
177 /* storage for collected id numbers */
178 /* FIXME: same data structures used in kernel, should find a way to
179  * deduplicate this code */
180
181 static int
182 rb_ac_unode_cmp(struct ac_unode*, struct ac_unode*);
183 static int
184 rb_ac_gnode_cmp(struct ac_gnode*, struct ac_gnode*);
185
186 RB_HEAD(ac_utree,ac_unode) ac_uroot;
187 RB_HEAD(ac_gtree,ac_gnode) ac_groot;
188 RB_PROTOTYPE(ac_utree, ac_unode, rb_entry, rb_ac_unode_cmp);
189 RB_PROTOTYPE(ac_gtree, ac_gnode, rb_entry, rb_ac_gnode_cmp);
190 RB_GENERATE(ac_utree, ac_unode, rb_entry, rb_ac_unode_cmp);
191 RB_GENERATE(ac_gtree, ac_gnode, rb_entry, rb_ac_gnode_cmp);
192
193 static int
194 rb_ac_unode_cmp(struct ac_unode *a, struct ac_unode *b)
195 {
196         if (a->left_bits < b->left_bits)
197                 return(-1);
198         else if (a->left_bits > b->left_bits)
199                 return(1);
200         return(0);
201 }
202
203 static int
204 rb_ac_gnode_cmp(struct ac_gnode *a, struct ac_gnode *b)
205 {
206         if (a->left_bits < b->left_bits)
207                 return(-1);
208         else if (a->left_bits > b->left_bits)
209                 return(1);
210         return(0);
211 }
212
213 static struct ac_unode*
214 unode_insert(uid_t uid)
215 {
216         struct ac_unode *unp, *res;
217
218         unp = malloc(sizeof(struct ac_unode));
219         if (unp == NULL) {
220                 printf("unode_insert(): malloc failed\n");
221                 exit(ENOMEM);
222         }
223         bzero(unp, sizeof(struct ac_unode));
224
225         unp->left_bits = (uid >> ACCT_CHUNK_BITS);
226         res = RB_INSERT(ac_utree, &ac_uroot, unp);
227
228         if (res != NULL)        /* shouldn't happen */
229                 printf("unode_insert(): RB_INSERT didn't return NULL\n");
230
231         return unp;
232 }
233
234 static struct ac_gnode*
235 gnode_insert(gid_t gid)
236 {
237         struct ac_gnode *gnp, *res;
238
239         gnp = malloc(sizeof(struct ac_gnode));
240         if (gnp == NULL) {
241                 printf("gnode_insert(): malloc failed\n");
242                 exit(ENOMEM);
243         }
244         bzero(gnp, sizeof(struct ac_gnode));
245
246         gnp->left_bits = (gid >> ACCT_CHUNK_BITS);
247         res = RB_INSERT(ac_gtree, &ac_groot, gnp);
248
249         if (res != NULL)        /* shouldn't happen */
250                 printf("gnode_insert(): RB_INSERT didn't return NULL\n");
251
252         return gnp;
253 }
254
255 /*
256  * get_dirsize(): walks a directory tree in the same filesystem
257  * output:
258  * - global rb-trees ac_uroot and ac_groot
259  * - global variable global_size
260  */
261 static int
262 get_dirsize(char* dirname)
263 {
264         FTS             *fts;
265         FTSENT          *p;
266         char*           fts_args[2];
267         int             retval = 0;
268
269         /* what we need */
270         ino_t           file_inode;
271         off_t           file_size;
272         uid_t           file_uid;
273         gid_t           file_gid;
274
275         struct ac_unode *unp, ufind;
276         struct ac_gnode *gnp, gfind;
277
278         /* TODO: check directory name sanity */
279         fts_args[0] = dirname;
280         fts_args[1] = NULL;
281
282         if ((fts = fts_open(fts_args, FTS_PHYSICAL|FTS_XDEV, NULL)) == NULL)
283                 err(1, "fts_open() failed");
284
285         while ((p = fts_read(fts)) != NULL) {
286                 switch (p->fts_info) {
287                 /* directories, ignore them */
288                 case FTS_D:
289                 case FTS_DC:
290                 case FTS_DP:
291                         break;
292                 /* read errors, warn, continue and flag */
293                 case FTS_DNR:
294                 case FTS_ERR:
295                 case FTS_NS:
296                         warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
297                         retval = 1;
298                         break;
299                 default:
300                         file_inode = p->fts_statp->st_ino;
301                         file_size = p->fts_statp->st_size;
302                         file_uid = p->fts_statp->st_uid;
303                         file_gid = p->fts_statp->st_gid;
304
305                         /* files with more than one hard link: */
306                         /* process them only once */
307                         if (p->fts_statp->st_nlink > 1)
308                                 if (hl_register(file_inode))
309                                         break;
310
311                         global_size += file_size;
312                         ufind.left_bits = (file_uid >> ACCT_CHUNK_BITS);
313                         gfind.left_bits = (file_gid >> ACCT_CHUNK_BITS);
314                         if ((unp = RB_FIND(ac_utree, &ac_uroot, &ufind)) == NULL)
315                                 unp = unode_insert(file_uid);
316                         if ((gnp = RB_FIND(ac_gtree, &ac_groot, &gfind)) == NULL)
317                                 gnp = gnode_insert(file_gid);
318                         unp->uid_chunk[(file_uid & ACCT_CHUNK_MASK)].space += file_size;
319                         gnp->gid_chunk[(file_gid & ACCT_CHUNK_MASK)].space += file_size;
320                 }
321         }
322         fts_close(fts);
323
324         return retval;
325 }
326
327 static void
328 print_user(uid_t uid)
329 {
330         struct passwd *pw;
331
332         if (flag_resolve_ids && ((pw = getpwuid(uid)) != NULL)) {
333                 printf("user %s:", pw->pw_name);
334         } else {
335                 printf("uid %u:", uid);
336         }
337 }
338
339 static void
340 print_group(gid_t gid)
341 {
342         struct group *gr;
343
344         if (flag_resolve_ids && ((gr = getgrgid(gid)) != NULL)) {
345                 printf("group %s:", gr->gr_name);
346         } else {
347                 printf("gid %u:", gid);
348         }
349 }
350
351 static int
352 cmd_check(char* dirname)
353 {
354         int32_t uid, gid;
355         char    hbuf[5];
356         struct  ac_unode *unp;
357         struct  ac_gnode *gnp;
358         int     rv, i;
359
360         rv = get_dirsize(dirname);
361
362         if (flag_humanize) {
363                 humanize_number(hbuf, sizeof(hbuf), global_size, "",
364                     HN_AUTOSCALE, HN_NOSPACE);
365                 printf("total: %s\n", hbuf);
366         } else {
367                 printf("total: %"PRIu64"\n", global_size);
368         }
369         RB_FOREACH(unp, ac_utree, &ac_uroot) {
370             for (i=0; i<ACCT_CHUNK_NIDS; i++) {
371                 if (unp->uid_chunk[i].space != 0) {
372                     uid = (unp->left_bits << ACCT_CHUNK_BITS) + i;
373                     print_user(uid);
374                     if (flag_humanize) {
375                         humanize_number(hbuf, sizeof(hbuf),
376                         unp->uid_chunk[i].space, "", HN_AUTOSCALE, HN_NOSPACE);
377                         printf(" %s\n", hbuf);
378                     } else {
379                         printf(" %" PRIu64 "\n", unp->uid_chunk[i].space);
380                     }
381                 }
382             }
383         }
384         RB_FOREACH(gnp, ac_gtree, &ac_groot) {
385             for (i=0; i<ACCT_CHUNK_NIDS; i++) {
386                 if (gnp->gid_chunk[i].space != 0) {
387                     gid = (gnp->left_bits << ACCT_CHUNK_BITS) + i;
388                     print_group(gid);
389                     if (flag_humanize) {
390                         humanize_number(hbuf, sizeof(hbuf),
391                         gnp->gid_chunk[i].space, "", HN_AUTOSCALE, HN_NOSPACE);
392                         printf(" %s\n", hbuf);
393                     } else {
394                         printf(" %" PRIu64 "\n", gnp->gid_chunk[i].space);
395                     }
396                 }
397             }
398         }
399
400         return rv;
401 }
402
403 /* print a list of filesystems with accounting enabled */
404 static int
405 get_fslist(void)
406 {
407         struct statfs *mntbufp;
408         int nloc, i;
409
410         /* read mount table from kernel */
411         nloc = getmntinfo(&mntbufp, MNT_NOWAIT|MNT_LOCAL);
412         if (nloc <= 0) {
413                 perror("getmntinfo");
414                 exit(1);
415         }
416
417         /* iterate mounted filesystems */
418         for (i=0; i<nloc; i++) {
419             /* vfs accounting enabled on this one ? */
420             if (mntbufp[i].f_flags & MNT_ACCOUNTING)
421                 printf("%s on %s\n", mntbufp[i].f_mntfromname,
422                     mntbufp[i].f_mntonname);
423         }
424
425         return 0;
426 }
427
428 static bool
429 send_command(const char *path, const char *cmd,
430                 prop_object_t args, prop_dictionary_t *res)
431 {
432         prop_dictionary_t dict;
433         struct plistref pref;
434
435         bool rv;
436         int error;
437
438         dict = prop_dictionary_create();
439
440         if (dict == NULL) {
441                 printf("send_command(): couldn't create dictionary\n");
442                 return false;
443         }
444
445         rv = prop_dictionary_set_cstring(dict, "command", cmd);
446         if (rv== false) {
447                 printf("send_command(): couldn't initialize dictionary\n");
448                 return false;
449         }
450
451         rv = prop_dictionary_set(dict, "arguments", args);
452         if (rv == false) {
453                 printf("prop_dictionary_set() failed\n");
454                 return false;
455         }
456
457         error = prop_dictionary_send_syscall(dict, &pref);
458         if (error != 0) {
459                 printf("prop_dictionary_send_syscall() failed\n");
460                 prop_object_release(dict);
461                 return false;
462         }
463
464         if (flag_debug)
465                 printf("Message to kernel:\n%s\n",
466                     prop_dictionary_externalize(dict));
467
468         error = vquotactl(path, &pref);
469         if (error != 0) {
470                 printf("send_command: vquotactl = %d\n", error);
471                 return false;
472         }
473
474         error = prop_dictionary_recv_syscall(&pref, res);
475         if (error != 0) {
476                 printf("prop_dictionary_recv_syscall() failed\n");
477         }
478
479         if (flag_debug)
480                 printf("Message from kernel:\n%s\n",
481                     prop_dictionary_externalize(*res));
482
483         return true;
484 }
485
486 /* show collected statistics on mount point */
487 static int
488 show_mp(char *path)
489 {
490         prop_dictionary_t args, res;
491         prop_array_t reslist;
492         bool rv;
493         prop_object_iterator_t  iter;
494         prop_dictionary_t item;
495         uint32_t id;
496         uint64_t space, limit=0;
497         char hbuf[5];
498
499         args = prop_dictionary_create();
500         res  = prop_dictionary_create();
501         if (args == NULL)
502                 printf("show_mp(): couldn't create args dictionary\n");
503         res  = prop_dictionary_create();
504         if (res == NULL)
505                 printf("show_mp(): couldn't create res dictionary\n");
506
507         rv = send_command(path, "get usage all", args, &res);
508         if (rv == false) {
509                 printf("show-mp(): failed to send message to kernel\n");
510                 goto end;
511         }
512
513         reslist = prop_dictionary_get(res, "returned data");
514         if (reslist == NULL) {
515                 printf("show_mp(): failed to get array of results");
516                 rv = false;
517                 goto end;
518         }
519
520         iter = prop_array_iterator(reslist);
521         if (iter == NULL) {
522                 printf("show_mp(): failed to create iterator\n");
523                 rv = false;
524                 goto end;
525         }
526
527         while ((item = prop_object_iterator_next(iter)) != NULL) {
528                 rv = prop_dictionary_get_uint64(item, "space used", &space);
529                 rv = prop_dictionary_get_uint64(item, "limit", &limit);
530                 if (prop_dictionary_get_uint32(item, "uid", &id))
531                         print_user(id);
532                 else if (prop_dictionary_get_uint32(item, "gid", &id))
533                         print_group(id);
534                 else
535                         printf("total:");
536                 if (flag_humanize) {
537                         humanize_number(hbuf, sizeof(hbuf), space, "", HN_AUTOSCALE, HN_NOSPACE);
538                         printf(" %s", hbuf);
539                 } else {
540                         printf(" %"PRIu64, space);
541                 }
542                 if (limit == 0) {
543                         printf("\n");
544                         continue;
545                 }
546                 if (flag_humanize) {
547                         humanize_number(hbuf, sizeof(hbuf), limit, "", HN_AUTOSCALE, HN_NOSPACE);
548                         printf(", limit = %s\n", hbuf);
549                 } else {
550                         printf(", limit = %"PRIu64"\n", limit);
551                 }
552         }
553         prop_object_iterator_release(iter);
554
555 end:
556         prop_object_release(args);
557         prop_object_release(res);
558         return (rv == true);
559 }
560
561 /* sync the in-kernel counters to the actual file system usage */
562 static int cmd_sync(char *dirname)
563 {
564         prop_dictionary_t res, item;
565         prop_array_t args;
566         struct ac_unode *unp;
567         struct ac_gnode *gnp;
568         int rv = 0, i;
569
570         args = prop_array_create();
571         if (args == NULL)
572                 printf("cmd_sync(): couldn't create args dictionary\n");
573         res  = prop_dictionary_create();
574         if (res == NULL)
575                 printf("cmd_sync(): couldn't create res dictionary\n");
576
577         rv = get_dirsize(dirname);
578
579         item = prop_dictionary_create();
580         if (item == NULL)
581                 printf("cmd_sync(): couldn't create item dictionary\n");
582         (void) prop_dictionary_set_uint64(item, "space used", global_size);
583         prop_array_add_and_rel(args, item);
584
585         RB_FOREACH(unp, ac_utree, &ac_uroot) {
586             for (i=0; i<ACCT_CHUNK_NIDS; i++) {
587                 if (unp->uid_chunk[i].space != 0) {
588                     item = prop_dictionary_create();
589                     (void) prop_dictionary_set_uint32(item, "uid",
590                                 (unp->left_bits << ACCT_CHUNK_BITS) + i);
591                     (void) prop_dictionary_set_uint64(item, "space used",
592                                 unp->uid_chunk[i].space);
593                     prop_array_add_and_rel(args, item);
594                 }
595             }
596         }
597         RB_FOREACH(gnp, ac_gtree, &ac_groot) {
598             for (i=0; i<ACCT_CHUNK_NIDS; i++) {
599                 if (gnp->gid_chunk[i].space != 0) {
600                     item = prop_dictionary_create();
601                     (void) prop_dictionary_set_uint32(item, "gid",
602                                 (gnp->left_bits << ACCT_CHUNK_BITS) + i);
603                     (void) prop_dictionary_set_uint64(item, "space used",
604                                 gnp->gid_chunk[i].space);
605                     prop_array_add_and_rel(args, item);
606                 }
607             }
608         }
609
610         if (send_command(dirname, "set usage all", args, &res) == false) {
611                 printf("Failed to send message to kernel\n");
612                 rv = 1;
613         }
614
615         prop_object_release(args);
616         prop_object_release(res);
617
618         return rv;
619 }
620
621 static int
622 cmd_limit(char *dirname, uint64_t limit)
623 {
624         prop_dictionary_t res, args;
625         int rv = 0;
626
627         args = prop_dictionary_create();
628         if (args == NULL)
629                 printf("cmd_limit(): couldn't create args dictionary\n");
630         res  = prop_dictionary_create();
631         if (res == NULL)
632                 printf("cmd_limit(): couldn't create res dictionary\n");
633
634         (void) prop_dictionary_set_uint64(args, "limit", limit);
635
636         if (send_command(dirname, "set limit", args, &res) == false) {
637                 printf("Failed to send message to kernel\n");
638                 rv = 1;
639         }
640
641         prop_object_release(args);
642         prop_object_release(res);
643
644         return rv;
645 }
646
647 static int
648 cmd_limit_uid(char *dirname, uid_t uid, uint64_t limit)
649 {
650         prop_dictionary_t res, args;
651         int rv = 0;
652
653         args = prop_dictionary_create();
654         if (args == NULL)
655                 printf("cmd_limit_uid(): couldn't create args dictionary\n");
656         res  = prop_dictionary_create();
657         if (res == NULL)
658                 printf("cmd_limit_uid(): couldn't create res dictionary\n");
659
660         (void) prop_dictionary_set_uint32(args, "uid", uid);
661         (void) prop_dictionary_set_uint64(args, "limit", limit);
662
663         if (send_command(dirname, "set limit uid", args, &res) == false) {
664                 printf("Failed to send message to kernel\n");
665                 rv = 1;
666         }
667
668         prop_object_release(args);
669         prop_object_release(res);
670
671         return rv;
672 }
673
674 static int
675 cmd_limit_gid(char *dirname, gid_t gid, uint64_t limit)
676 {
677         prop_dictionary_t res, args;
678         int rv = 0;
679
680         args = prop_dictionary_create();
681         if (args == NULL)
682                 printf("cmd_limit_gid(): couldn't create args dictionary\n");
683         res  = prop_dictionary_create();
684         if (res == NULL)
685                 printf("cmd_limit_gid(): couldn't create res dictionary\n");
686
687         (void) prop_dictionary_set_uint32(args, "gid", gid);
688         (void) prop_dictionary_set_uint64(args, "limit", limit);
689
690         if (send_command(dirname, "set limit gid", args, &res) == false) {
691                 printf("Failed to send message to kernel\n");
692                 rv = 1;
693         }
694
695         prop_object_release(args);
696         prop_object_release(res);
697
698         return rv;
699 }
700
701 int
702 main(int argc, char **argv)
703 {
704         int ch;
705         uint64_t limit;
706
707         while ((ch = getopt(argc, argv, "Dhn")) != -1) {
708                 switch(ch) {
709                 case 'D':
710                         flag_debug = 1;
711                         break;
712                 case 'h':
713                         flag_humanize = 1;
714                         break;
715                 case 'n':
716                         flag_resolve_ids = 0;
717                         break;
718                 }
719         }
720         argc -= optind;
721         argv += optind;
722         if (argc < 1)
723                 usage(1);
724         
725         if (strcmp(argv[0], "check") == 0) {
726                 if (argc != 2)
727                         usage(1);
728                 return cmd_check(argv[1]);
729         }
730         if (strcmp(argv[0], "lsfs") == 0) {
731                 return get_fslist();
732         }
733         if (strcmp(argv[0], "limit") == 0) {
734                 if (argc != 3)
735                         usage(1);
736                 if (dehumanize_number(argv[2], &limit) < 0)
737                         err(1, "bad number for option: %s", argv[2]);
738
739                 return cmd_limit(argv[1], limit);
740         }
741         if (strcmp(argv[0], "show") == 0) {
742                 if (argc != 2)
743                         usage(1);
744                 return show_mp(argv[1]);
745         }
746         if (strcmp(argv[0], "sync") == 0) {
747                 if (argc != 2)
748                         usage(1);
749                 return cmd_sync(argv[1]);
750         }
751         if (strcmp(argv[0], "ulim") == 0) {
752                 struct passwd *pwd;
753                 if (argc != 4)
754                         usage(1);
755                 if ((pwd = getpwnam(argv[2])) == NULL)
756                         errx(1, "%s: no such user", argv[2]);
757                 if (dehumanize_number(argv[3], &limit) < 0)
758                         err(1, "bad number for option: %s", argv[2]);
759
760                 return cmd_limit_uid(argv[1], pwd->pw_uid, limit);
761         }
762         if (strcmp(argv[0], "glim") == 0) {
763                 struct group *grp;
764                 if (argc != 4)
765                         usage(1);
766                 if ((grp = getgrnam(argv[2])) == NULL)
767                         errx(1, "%s: no such group", argv[2]);
768                 if (dehumanize_number(argv[3], &limit) < 0)
769                         err(1, "bad number for option: %s", argv[2]);
770
771                 return cmd_limit_gid(argv[1], grp->gr_gid, limit);
772         }
773
774         usage(0);
775 }