VFS quota: add a command to set a group quota
[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");
c6e8a1bd 66 fprintf(stderr, " vquota [-Dhn] ulim mount_point user size\n");
3663c79b 67 fprintf(stderr, " vquota [-Dhn] glim mount_point group size\n");
bc844d36
FT
68 fprintf(stderr, " vquota [-Dhn] show mount_point\n");
69 fprintf(stderr, " vquota [-Dhn] sync mount_point\n");
6a4c3e18
FT
70 exit(retcode);
71}
72
49e10979
FT
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
86struct 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
92RB_HEAD(hl_tree,hl_node) hl_root;
93
94RB_PROTOTYPE(hl_tree, hl_node, rb_entry, rb_hl_node_cmp);
95
96static int
97rb_hl_node_cmp(struct hl_node *a, struct hl_node *b);
98
99RB_GENERATE(hl_tree, hl_node, rb_entry, rb_hl_node_cmp);
100
101struct hl_node* hl_node_insert(ino_t);
102
103
104static int
105rb_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
114struct 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 */
141static bool
142hl_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
88c2e66c
FT
174/* global variable used by get_dir_size() */
175uint64_t global_size;
176
177/* storage for collected id numbers */
f236a458
FT
178/* FIXME: same data structures used in kernel, should find a way to
179 * deduplicate this code */
180
181static int
182rb_ac_unode_cmp(struct ac_unode*, struct ac_unode*);
183static int
184rb_ac_gnode_cmp(struct ac_gnode*, struct ac_gnode*);
185
186RB_HEAD(ac_utree,ac_unode) ac_uroot;
187RB_HEAD(ac_gtree,ac_gnode) ac_groot;
188RB_PROTOTYPE(ac_utree, ac_unode, rb_entry, rb_ac_unode_cmp);
189RB_PROTOTYPE(ac_gtree, ac_gnode, rb_entry, rb_ac_gnode_cmp);
190RB_GENERATE(ac_utree, ac_unode, rb_entry, rb_ac_unode_cmp);
191RB_GENERATE(ac_gtree, ac_gnode, rb_entry, rb_ac_gnode_cmp);
192
193static int
194rb_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
203static int
204rb_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
213static struct ac_unode*
214unode_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");
c89ff6a6 221 exit(ENOMEM);
f236a458
FT
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
234static struct ac_gnode*
235gnode_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");
c89ff6a6 242 exit(ENOMEM);
f236a458
FT
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
88c2e66c
FT
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 */
cefc2cb0
SW
261static int
262get_dirsize(char* dirname)
263{
264 FTS *fts;
6a4c3e18
FT
265 FTSENT *p;
266 char* fts_args[2];
cefc2cb0 267 int retval = 0;
6a4c3e18 268
49e10979
FT
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
f236a458
FT
275 struct ac_unode *unp, ufind;
276 struct ac_gnode *gnp, gfind;
f236a458 277
6a4c3e18
FT
278 /* TODO: check directory name sanity */
279 fts_args[0] = dirname;
280 fts_args[1] = NULL;
281
49e10979 282 if ((fts = fts_open(fts_args, FTS_PHYSICAL|FTS_XDEV, NULL)) == NULL)
6a4c3e18
FT
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:
49e10979
FT
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
f236a458
FT
305 /* files with more than one hard link: */
306 /* process them only once */
307 if (p->fts_statp->st_nlink > 1)
85e2fc83 308 if (hl_register(file_inode))
f236a458
FT
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);
3e8ec7e9
FT
318 unp->uid_chunk[(file_uid & ACCT_CHUNK_MASK)].space += file_size;
319 gnp->gid_chunk[(file_gid & ACCT_CHUNK_MASK)].space += file_size;
6a4c3e18
FT
320 }
321 }
a89ecd20 322 fts_close(fts);
6a4c3e18 323
88c2e66c
FT
324 return retval;
325}
326
bc844d36
FT
327static void
328print_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
339static void
340print_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
88c2e66c
FT
351static int
352cmd_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
c4d75bef
FT
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 }
f236a458 369 RB_FOREACH(unp, ac_utree, &ac_uroot) {
c4d75bef 370 for (i=0; i<ACCT_CHUNK_NIDS; i++) {
3e8ec7e9 371 if (unp->uid_chunk[i].space != 0) {
c4d75bef 372 uid = (unp->left_bits << ACCT_CHUNK_BITS) + i;
bc844d36 373 print_user(uid);
c4d75bef
FT
374 if (flag_humanize) {
375 humanize_number(hbuf, sizeof(hbuf),
3e8ec7e9 376 unp->uid_chunk[i].space, "", HN_AUTOSCALE, HN_NOSPACE);
bc844d36 377 printf(" %s\n", hbuf);
c4d75bef 378 } else {
3e8ec7e9 379 printf(" %" PRIu64 "\n", unp->uid_chunk[i].space);
c4d75bef 380 }
f236a458 381 }
c4d75bef 382 }
f236a458
FT
383 }
384 RB_FOREACH(gnp, ac_gtree, &ac_groot) {
c4d75bef 385 for (i=0; i<ACCT_CHUNK_NIDS; i++) {
3e8ec7e9 386 if (gnp->gid_chunk[i].space != 0) {
c4d75bef 387 gid = (gnp->left_bits << ACCT_CHUNK_BITS) + i;
bc844d36 388 print_group(gid);
c4d75bef
FT
389 if (flag_humanize) {
390 humanize_number(hbuf, sizeof(hbuf),
3e8ec7e9 391 gnp->gid_chunk[i].space, "", HN_AUTOSCALE, HN_NOSPACE);
bc844d36 392 printf(" %s\n", hbuf);
c4d75bef 393 } else {
3e8ec7e9 394 printf(" %" PRIu64 "\n", gnp->gid_chunk[i].space);
c4d75bef 395 }
f236a458 396 }
c4d75bef 397 }
f236a458
FT
398 }
399
88c2e66c 400 return rv;
6a4c3e18
FT
401}
402
a3dce641 403/* print a list of filesystems with accounting enabled */
c89ff6a6
SW
404static int
405get_fslist(void)
406{
a3dce641
FT
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,
c89ff6a6 422 mntbufp[i].f_mntonname);
a3dce641
FT
423 }
424
425 return 0;
426}
427
b4d6d8bb
FT
428static bool
429send_command(const char *path, const char *cmd,
88c2e66c 430 prop_object_t args, prop_dictionary_t *res)
c89ff6a6 431{
b4d6d8bb
FT
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)
c89ff6a6
SW
465 printf("Message to kernel:\n%s\n",
466 prop_dictionary_externalize(dict));
b4d6d8bb
FT
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)
c89ff6a6
SW
480 printf("Message from kernel:\n%s\n",
481 prop_dictionary_externalize(*res));
b4d6d8bb
FT
482
483 return true;
484}
485
486/* show collected statistics on mount point */
c89ff6a6
SW
487static int
488show_mp(char *path)
489{
b4d6d8bb
FT
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;
8d91721d 496 uint64_t space, limit=0;
c4d75bef 497 char hbuf[5];
b4d6d8bb
FT
498
499 args = prop_dictionary_create();
500 res = prop_dictionary_create();
501 if (args == NULL)
88c2e66c
FT
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");
b4d6d8bb
FT
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
88c2e66c 513 reslist = prop_dictionary_get(res, "returned data");
b4d6d8bb
FT
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);
8d91721d 529 rv = prop_dictionary_get_uint64(item, "limit", &limit);
83928fe9 530 if (prop_dictionary_get_uint32(item, "uid", &id))
bc844d36 531 print_user(id);
83928fe9 532 else if (prop_dictionary_get_uint32(item, "gid", &id))
bc844d36 533 print_group(id);
b4d6d8bb 534 else
88c2e66c 535 printf("total:");
c4d75bef
FT
536 if (flag_humanize) {
537 humanize_number(hbuf, sizeof(hbuf), space, "", HN_AUTOSCALE, HN_NOSPACE);
8d91721d
FT
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);
c4d75bef 549 } else {
8d91721d 550 printf(", limit = %"PRIu64"\n", limit);
c4d75bef 551 }
b4d6d8bb
FT
552 }
553 prop_object_iterator_release(iter);
554
555end:
556 prop_object_release(args);
557 prop_object_release(res);
558 return (rv == true);
559}
560
88c2e66c
FT
561/* sync the in-kernel counters to the actual file system usage */
562static 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++) {
3e8ec7e9 587 if (unp->uid_chunk[i].space != 0) {
88c2e66c
FT
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",
3e8ec7e9 592 unp->uid_chunk[i].space);
88c2e66c
FT
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++) {
3e8ec7e9 599 if (gnp->gid_chunk[i].space != 0) {
88c2e66c
FT
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",
3e8ec7e9 604 gnp->gid_chunk[i].space);
88c2e66c
FT
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
c115b876
FT
621static int
622cmd_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
c6e8a1bd
FT
647static int
648cmd_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
3663c79b
FT
674static int
675cmd_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
cefc2cb0 701int
c89ff6a6
SW
702main(int argc, char **argv)
703{
b4d6d8bb 704 int ch;
c6e8a1bd 705 uint64_t limit;
b4d6d8bb 706
bc844d36 707 while ((ch = getopt(argc, argv, "Dhn")) != -1) {
b4d6d8bb
FT
708 switch(ch) {
709 case 'D':
710 flag_debug = 1;
711 break;
c4d75bef
FT
712 case 'h':
713 flag_humanize = 1;
714 break;
bc844d36
FT
715 case 'n':
716 flag_resolve_ids = 0;
717 break;
b4d6d8bb
FT
718 }
719 }
720 argc -= optind;
721 argv += optind;
722 if (argc < 1)
6a4c3e18
FT
723 usage(1);
724
b4d6d8bb
FT
725 if (strcmp(argv[0], "check") == 0) {
726 if (argc != 2)
a3dce641 727 usage(1);
88c2e66c 728 return cmd_check(argv[1]);
a3dce641 729 }
b4d6d8bb 730 if (strcmp(argv[0], "lsfs") == 0) {
a3dce641 731 return get_fslist();
6a4c3e18 732 }
c115b876 733 if (strcmp(argv[0], "limit") == 0) {
c115b876
FT
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 }
b4d6d8bb
FT
741 if (strcmp(argv[0], "show") == 0) {
742 if (argc != 2)
743 usage(1);
744 return show_mp(argv[1]);
745 }
88c2e66c
FT
746 if (strcmp(argv[0], "sync") == 0) {
747 if (argc != 2)
748 usage(1);
749 return cmd_sync(argv[1]);
750 }
c6e8a1bd
FT
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 }
3663c79b
FT
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 }
6a4c3e18 773
b4d6d8bb 774 usage(0);
6a4c3e18 775}