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>
37 #include <sys/sysctl.h>
51 #define RCTL_DEFAULT_BUFSIZE 128 * 1024
54 parse_user(const char *s)
65 errx(1, "uknown user '%s'", s);
68 if ((size_t)(end - s) != strlen(s))
69 errx(1, "trailing characters after numerical id");
75 parse_group(const char *s)
86 errx(1, "uknown group '%s'", s);
89 if ((size_t)(end - s) != strlen(s))
90 errx(1, "trailing characters after numerical id");
96 * This routine replaces user/group name with numeric id.
99 resolve_ids(char *rule)
102 const char *subject, *textid, *rest;
105 subject = strsep(&rule, ":");
106 textid = strsep(&rule, ":");
108 errx(1, "error in rule specification -- no subject");
114 if (strcasecmp(subject, "u") == 0)
116 else if (strcasecmp(subject, "g") == 0)
118 else if (strcasecmp(subject, "p") == 0)
120 else if (strcasecmp(subject, "l") == 0 ||
121 strcasecmp(subject, "c") == 0 ||
122 strcasecmp(subject, "class") == 0)
123 subject = "loginclass";
124 else if (strcasecmp(subject, "j") == 0)
127 if (strcasecmp(subject, "user") == 0 && strlen(textid) > 0) {
128 id = parse_user(textid);
129 asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
130 } else if (strcasecmp(subject, "group") == 0 && strlen(textid) > 0) {
131 id = parse_group(textid);
132 asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
134 asprintf(&resolved, "%s:%s:%s", subject, textid, rest);
136 if (resolved == NULL)
143 * This routine replaces "human-readable" number with its expanded form.
146 expand_amount(char *rule)
149 const char *subject, *subject_id, *resource, *action, *amount, *per;
150 char *copy, *expanded;
156 subject = strsep(©, ":");
157 subject_id = strsep(©, ":");
158 resource = strsep(©, ":");
159 action = strsep(©, "=/");
160 amount = strsep(©, "/");
163 if (amount == NULL || strlen(amount) == 0) {
168 assert(subject != NULL);
169 assert(subject_id != NULL);
170 assert(resource != NULL);
171 assert(action != NULL);
173 if (expand_number(amount, &num))
174 err(1, "expand_number");
177 asprintf(&expanded, "%s:%s:%s:%s=%ju", subject, subject_id,
178 resource, action, (uintmax_t)num);
180 asprintf(&expanded, "%s:%s:%s:%s=%ju/%s", subject, subject_id,
181 resource, action, (uintmax_t)num, per);
183 if (expanded == NULL)
190 humanize_ids(char *rule)
195 const char *subject, *textid, *rest;
198 subject = strsep(&rule, ":");
199 textid = strsep(&rule, ":");
201 errx(1, "rule passed from the kernel didn't contain subject");
207 /* Replace numerical user and group ids with names. */
208 if (strcasecmp(subject, "user") == 0) {
209 id = parse_user(textid);
212 textid = pwd->pw_name;
213 } else if (strcasecmp(subject, "group") == 0) {
214 id = parse_group(textid);
217 textid = grp->gr_name;
220 asprintf(&humanized, "%s:%s:%s", subject, textid, rest);
222 if (humanized == NULL)
229 str2int64(const char *str, int64_t *value)
236 *value = strtoul(str, &end, 10);
237 if ((size_t)(end - str) != strlen(str))
244 humanize_amount(char *rule)
247 const char *subject, *subject_id, *resource, *action, *amount, *per;
248 char *copy, *humanized, buf[6];
254 subject = strsep(©, ":");
255 subject_id = strsep(©, ":");
256 resource = strsep(©, ":");
257 action = strsep(©, "=/");
258 amount = strsep(©, "/");
261 if (amount == NULL || strlen(amount) == 0 ||
262 str2int64(amount, &num) != 0) {
267 assert(subject != NULL);
268 assert(subject_id != NULL);
269 assert(resource != NULL);
270 assert(action != NULL);
272 if (humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
273 HN_DECIMAL | HN_NOSPACE) == -1)
274 err(1, "humanize_number");
277 asprintf(&humanized, "%s:%s:%s:%s=%s", subject, subject_id,
278 resource, action, buf);
280 asprintf(&humanized, "%s:%s:%s:%s=%s/%s", subject, subject_id,
281 resource, action, buf, per);
283 if (humanized == NULL)
290 * Print rules, one per line.
293 print_rules(char *rules, int hflag, int nflag)
297 while ((rule = strsep(&rules, ",")) != NULL) {
301 rule = humanize_ids(rule);
303 rule = humanize_amount(rule);
304 printf("%s\n", rule);
311 int error, racct_enable;
312 size_t racct_enable_len;
314 racct_enable_len = sizeof(racct_enable);
315 error = sysctlbyname("kern.racct.enable",
316 &racct_enable, &racct_enable_len, NULL, 0);
320 errx(1, "RACCT/RCTL support not present in kernel; see rctl(8) for details");
322 err(1, "sysctlbyname");
325 if (racct_enable == 0)
326 errx(1, "RACCT/RCTL present, but disabled; enable using kern.racct.enable=1 tunable");
334 error = rctl_add_rule(rule, strlen(rule) + 1, NULL, 0);
338 err(1, "rctl_add_rule");
344 show_limits(char *filter, int hflag, int nflag)
348 size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
352 outbuf = realloc(outbuf, outbuflen);
356 error = rctl_get_limits(filter, strlen(filter) + 1, outbuf,
358 if (error && errno != ERANGE) {
361 err(1, "rctl_get_limits");
363 } while (error && errno == ERANGE);
365 print_rules(outbuf, hflag, nflag);
371 remove_rule(char *filter)
375 error = rctl_remove_rule(filter, strlen(filter) + 1, NULL, 0);
379 err(1, "rctl_remove_rule");
385 humanize_usage_amount(char *usage)
388 const char *resource, *amount;
389 char *copy, *humanized, buf[6];
391 copy = strdup(usage);
395 resource = strsep(©, "=");
398 assert(resource != NULL);
399 assert(amount != NULL);
401 if (str2int64(amount, &num) != 0 ||
402 humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
403 HN_DECIMAL | HN_NOSPACE) == -1) {
408 asprintf(&humanized, "%s=%s", resource, buf);
409 if (humanized == NULL)
416 * Query the kernel about a resource usage and print it out.
419 show_usage(char *filter, int hflag)
422 char *outbuf = NULL, *tmp;
423 size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
427 outbuf = realloc(outbuf, outbuflen);
431 error = rctl_get_racct(filter, strlen(filter) + 1, outbuf,
433 if (error && errno != ERANGE) {
436 err(1, "rctl_get_racct");
438 } while (error && errno == ERANGE);
440 while ((tmp = strsep(&outbuf, ",")) != NULL) {
445 tmp = humanize_usage_amount(tmp);
455 * Query the kernel about resource limit rules and print them out.
458 show_rules(char *filter, int hflag, int nflag)
462 size_t filterlen, outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
465 filterlen = strlen(filter) + 1;
471 outbuf = realloc(outbuf, outbuflen);
475 error = rctl_get_rules(filter, filterlen, outbuf, outbuflen);
476 if (error && errno != ERANGE) {
479 err(1, "rctl_get_rules");
481 } while (error && errno == ERANGE);
483 print_rules(outbuf, hflag, nflag);
491 fprintf(stderr, "usage: rctl [ -h ] [-a rule | -l filter | -r filter "
492 "| -u filter | filter]\n");
497 main(int argc __unused, char **argv __unused)
499 int ch, aflag = 0, hflag = 0, nflag = 0, lflag = 0, rflag = 0,
503 while ((ch = getopt(argc, argv, "a:hl:nr:u:")) != -1) {
507 rule = strdup(optarg);
514 rule = strdup(optarg);
521 rule = strdup(optarg);
525 rule = strdup(optarg);
542 rule = strdup(argv[0]);
547 if (aflag + lflag + rflag + uflag + argc > 1)
548 errx(1, "only one flag or argument may be specified "
551 rule = resolve_ids(rule);
552 rule = expand_amount(rule);
560 show_limits(rule, hflag, nflag);
570 show_usage(rule, hflag);
574 show_rules(rule, hflag, nflag);