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