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 quota enabled on this one ? */ 420 if (mntbufp[i].f_flags & MNT_QUOTA) 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 } 776