2 * Copyright (c) 2010 The FreeBSD Foundation
5 * This software was developed by Edward Tomasz Napierala under sponsorship
6 * from the FreeBSD Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
35 #include <sys/types.h>
50 #define RCTL_DEFAULT_BUFSIZE 4096
53 parse_user(const char *s)
64 errx(1, "uknown user '%s'", s);
67 if ((size_t)(end - s) != strlen(s))
68 errx(1, "trailing characters after numerical id");
74 parse_group(const char *s)
85 errx(1, "uknown group '%s'", s);
88 if ((size_t)(end - s) != strlen(s))
89 errx(1, "trailing characters after numerical id");
95 * This routine replaces user/group name with numeric id.
98 resolve_ids(char *rule)
101 const char *subject, *textid, *rest;
104 subject = strsep(&rule, ":");
105 textid = strsep(&rule, ":");
107 errx(1, "error in rule specification -- no subject");
113 if (strcasecmp(subject, "u") == 0)
115 else if (strcasecmp(subject, "g") == 0)
117 else if (strcasecmp(subject, "p") == 0)
119 else if (strcasecmp(subject, "l") == 0 ||
120 strcasecmp(subject, "c") == 0 ||
121 strcasecmp(subject, "class") == 0)
122 subject = "loginclass";
123 else if (strcasecmp(subject, "j") == 0)
126 if (strcasecmp(subject, "user") == 0 && strlen(textid) > 0) {
127 id = parse_user(textid);
128 asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
129 } else if (strcasecmp(subject, "group") == 0 && strlen(textid) > 0) {
130 id = parse_group(textid);
131 asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
133 asprintf(&resolved, "%s:%s:%s", subject, textid, rest);
135 if (resolved == NULL)
142 * This routine replaces "human-readable" number with its expanded form.
145 expand_amount(char *rule)
148 const char *subject, *subject_id, *resource, *action, *amount, *per;
149 char *copy, *expanded;
155 subject = strsep(©, ":");
156 subject_id = strsep(©, ":");
157 resource = strsep(©, ":");
158 action = strsep(©, "=/");
159 amount = strsep(©, "/");
162 if (amount == NULL || strlen(amount) == 0) {
167 assert(subject != NULL);
168 assert(subject_id != NULL);
169 assert(resource != NULL);
170 assert(action != NULL);
172 if (expand_number(amount, &num))
173 err(1, "expand_number");
176 asprintf(&expanded, "%s:%s:%s:%s=%ju", subject, subject_id,
177 resource, action, (uintmax_t)num);
179 asprintf(&expanded, "%s:%s:%s:%s=%ju/%s", subject, subject_id,
180 resource, action, (uintmax_t)num, per);
182 if (expanded == NULL)
189 humanize_ids(char *rule)
194 const char *subject, *textid, *rest;
197 subject = strsep(&rule, ":");
198 textid = strsep(&rule, ":");
200 errx(1, "rule passed from the kernel didn't contain subject");
206 /* Replace numerical user and group ids with names. */
207 if (strcasecmp(subject, "user") == 0) {
208 id = parse_user(textid);
211 textid = pwd->pw_name;
212 } else if (strcasecmp(subject, "group") == 0) {
213 id = parse_group(textid);
216 textid = grp->gr_name;
219 asprintf(&humanized, "%s:%s:%s", subject, textid, rest);
221 if (humanized == NULL)
228 str2int64(const char *str, int64_t *value)
235 *value = strtoul(str, &end, 10);
236 if ((size_t)(end - str) != strlen(str))
243 humanize_amount(char *rule)
246 const char *subject, *subject_id, *resource, *action, *amount, *per;
247 char *copy, *humanized, buf[6];
253 subject = strsep(©, ":");
254 subject_id = strsep(©, ":");
255 resource = strsep(©, ":");
256 action = strsep(©, "=/");
257 amount = strsep(©, "/");
260 if (amount == NULL || strlen(amount) == 0 ||
261 str2int64(amount, &num) != 0) {
266 assert(subject != NULL);
267 assert(subject_id != NULL);
268 assert(resource != NULL);
269 assert(action != NULL);
271 if (humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
272 HN_DECIMAL | HN_NOSPACE) == -1)
273 err(1, "humanize_number");
276 asprintf(&humanized, "%s:%s:%s:%s=%s", subject, subject_id,
277 resource, action, buf);
279 asprintf(&humanized, "%s:%s:%s:%s=%s/%s", subject, subject_id,
280 resource, action, buf, per);
282 if (humanized == NULL)
289 * Print rules, one per line.
292 print_rules(char *rules, int hflag, int nflag)
296 while ((rule = strsep(&rules, ",")) != NULL) {
300 rule = humanize_ids(rule);
302 rule = humanize_amount(rule);
303 printf("%s\n", rule);
312 error = rctl_add_rule(rule, strlen(rule) + 1, NULL, 0);
314 err(1, "rctl_add_rule");
319 show_limits(char *filter, int hflag, int nflag)
323 size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
327 outbuf = realloc(outbuf, outbuflen);
331 error = rctl_get_limits(filter, strlen(filter) + 1, outbuf,
333 if (error && errno != ERANGE)
334 err(1, "rctl_get_limits");
335 } while (error && errno == ERANGE);
337 print_rules(outbuf, hflag, nflag);
343 remove_rule(char *filter)
347 error = rctl_remove_rule(filter, strlen(filter) + 1, NULL, 0);
349 err(1, "rctl_remove_rule");
354 humanize_usage_amount(char *usage)
357 const char *resource, *amount;
358 char *copy, *humanized, buf[6];
360 copy = strdup(usage);
364 resource = strsep(©, "=");
367 assert(resource != NULL);
368 assert(amount != NULL);
370 if (str2int64(amount, &num) != 0 ||
371 humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
372 HN_DECIMAL | HN_NOSPACE) == -1) {
377 asprintf(&humanized, "%s=%s", resource, buf);
378 if (humanized == NULL)
385 * Query the kernel about a resource usage and print it out.
388 show_usage(char *filter, int hflag)
391 char *outbuf = NULL, *tmp;
392 size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
396 outbuf = realloc(outbuf, outbuflen);
400 error = rctl_get_racct(filter, strlen(filter) + 1, outbuf,
402 if (error && errno != ERANGE)
403 err(1, "rctl_get_racct");
404 } while (error && errno == ERANGE);
406 while ((tmp = strsep(&outbuf, ",")) != NULL) {
411 tmp = humanize_usage_amount(tmp);
421 * Query the kernel about resource limit rules and print them out.
424 show_rules(char *filter, int hflag, int nflag)
428 size_t filterlen, outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
431 filterlen = strlen(filter) + 1;
437 outbuf = realloc(outbuf, outbuflen);
441 error = rctl_get_rules(filter, filterlen, outbuf, outbuflen);
442 if (error && errno != ERANGE)
443 err(1, "rctl_get_rules");
444 } while (error && errno == ERANGE);
446 print_rules(outbuf, hflag, nflag);
454 fprintf(stderr, "usage: rctl [ -h ] [-a rule | -l filter | -r filter "
455 "| -u filter | filter]\n");
460 main(int argc __unused, char **argv __unused)
462 int ch, aflag = 0, hflag = 0, nflag = 0, lflag = 0, rflag = 0,
466 while ((ch = getopt(argc, argv, "a:hl:nr:u:")) != -1) {
470 rule = strdup(optarg);
477 rule = strdup(optarg);
484 rule = strdup(optarg);
488 rule = strdup(optarg);
505 rule = strdup(argv[0]);
510 if (aflag + lflag + rflag + uflag + argc > 1)
511 errx(1, "only one flag or argument may be specified "
514 rule = resolve_ids(rule);
515 rule = expand_amount(rule);
523 show_limits(rule, hflag, nflag);
533 show_usage(rule, hflag);
537 show_rules(rule, hflag, nflag);