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