/usr.bin/rctl/rctl.c
https://bitbucket.org/freebsd/freebsd-head/ · C · 539 lines · 402 code · 91 blank · 46 comment · 155 complexity · f8f8aadca9433a7cb8e9230e0f2bd683 MD5 · raw file
- /*-
- * Copyright (c) 2010 The FreeBSD Foundation
- * All rights reserved.
- *
- * This software was developed by Edward Tomasz Napierala under sponsorship
- * from the FreeBSD Foundation.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $FreeBSD$
- */
- #include <sys/cdefs.h>
- __FBSDID("$FreeBSD$");
- #include <sys/types.h>
- #include <sys/rctl.h>
- #include <assert.h>
- #include <ctype.h>
- #include <err.h>
- #include <errno.h>
- #include <getopt.h>
- #include <grp.h>
- #include <libutil.h>
- #include <pwd.h>
- #include <stdint.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #define RCTL_DEFAULT_BUFSIZE 4096
- static id_t
- parse_user(const char *s)
- {
- id_t id;
- char *end;
- struct passwd *pwd;
- pwd = getpwnam(s);
- if (pwd != NULL)
- return (pwd->pw_uid);
- if (!isnumber(s[0]))
- errx(1, "uknown user '%s'", s);
- id = strtod(s, &end);
- if ((size_t)(end - s) != strlen(s))
- errx(1, "trailing characters after numerical id");
- return (id);
- }
- static id_t
- parse_group(const char *s)
- {
- id_t id;
- char *end;
- struct group *grp;
- grp = getgrnam(s);
- if (grp != NULL)
- return (grp->gr_gid);
- if (!isnumber(s[0]))
- errx(1, "uknown group '%s'", s);
- id = strtod(s, &end);
- if ((size_t)(end - s) != strlen(s))
- errx(1, "trailing characters after numerical id");
- return (id);
- }
- /*
- * This routine replaces user/group name with numeric id.
- */
- static char *
- resolve_ids(char *rule)
- {
- id_t id;
- const char *subject, *textid, *rest;
- char *resolved;
- subject = strsep(&rule, ":");
- textid = strsep(&rule, ":");
- if (textid == NULL)
- errx(1, "error in rule specification -- no subject");
- if (rule != NULL)
- rest = rule;
- else
- rest = "";
- if (strcasecmp(subject, "u") == 0)
- subject = "user";
- else if (strcasecmp(subject, "g") == 0)
- subject = "group";
- else if (strcasecmp(subject, "p") == 0)
- subject = "process";
- else if (strcasecmp(subject, "l") == 0 ||
- strcasecmp(subject, "c") == 0 ||
- strcasecmp(subject, "class") == 0)
- subject = "loginclass";
- else if (strcasecmp(subject, "j") == 0)
- subject = "jail";
- if (strcasecmp(subject, "user") == 0 && strlen(textid) > 0) {
- id = parse_user(textid);
- asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
- } else if (strcasecmp(subject, "group") == 0 && strlen(textid) > 0) {
- id = parse_group(textid);
- asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
- } else
- asprintf(&resolved, "%s:%s:%s", subject, textid, rest);
- if (resolved == NULL)
- err(1, "asprintf");
- return (resolved);
- }
- /*
- * This routine replaces "human-readable" number with its expanded form.
- */
- static char *
- expand_amount(char *rule)
- {
- uint64_t num;
- const char *subject, *subject_id, *resource, *action, *amount, *per;
- char *copy, *expanded;
- copy = strdup(rule);
- if (copy == NULL)
- err(1, "strdup");
- subject = strsep(©, ":");
- subject_id = strsep(©, ":");
- resource = strsep(©, ":");
- action = strsep(©, "=/");
- amount = strsep(©, "/");
- per = copy;
- if (amount == NULL || strlen(amount) == 0) {
- free(copy);
- return (rule);
- }
- assert(subject != NULL);
- assert(subject_id != NULL);
- assert(resource != NULL);
- assert(action != NULL);
- if (expand_number(amount, &num))
- err(1, "expand_number");
- if (per == NULL)
- asprintf(&expanded, "%s:%s:%s:%s=%ju", subject, subject_id,
- resource, action, (uintmax_t)num);
- else
- asprintf(&expanded, "%s:%s:%s:%s=%ju/%s", subject, subject_id,
- resource, action, (uintmax_t)num, per);
- if (expanded == NULL)
- err(1, "asprintf");
- return (expanded);
- }
- static char *
- humanize_ids(char *rule)
- {
- id_t id;
- struct passwd *pwd;
- struct group *grp;
- const char *subject, *textid, *rest;
- char *humanized;
- subject = strsep(&rule, ":");
- textid = strsep(&rule, ":");
- if (textid == NULL)
- errx(1, "rule passed from the kernel didn't contain subject");
- if (rule != NULL)
- rest = rule;
- else
- rest = "";
- /* Replace numerical user and group ids with names. */
- if (strcasecmp(subject, "user") == 0) {
- id = parse_user(textid);
- pwd = getpwuid(id);
- if (pwd != NULL)
- textid = pwd->pw_name;
- } else if (strcasecmp(subject, "group") == 0) {
- id = parse_group(textid);
- grp = getgrgid(id);
- if (grp != NULL)
- textid = grp->gr_name;
- }
- asprintf(&humanized, "%s:%s:%s", subject, textid, rest);
- if (humanized == NULL)
- err(1, "asprintf");
- return (humanized);
- }
- static int
- str2int64(const char *str, int64_t *value)
- {
- char *end;
- if (str == NULL)
- return (EINVAL);
- *value = strtoul(str, &end, 10);
- if ((size_t)(end - str) != strlen(str))
- return (EINVAL);
- return (0);
- }
- static char *
- humanize_amount(char *rule)
- {
- int64_t num;
- const char *subject, *subject_id, *resource, *action, *amount, *per;
- char *copy, *humanized, buf[6];
- copy = strdup(rule);
- if (copy == NULL)
- err(1, "strdup");
- subject = strsep(©, ":");
- subject_id = strsep(©, ":");
- resource = strsep(©, ":");
- action = strsep(©, "=/");
- amount = strsep(©, "/");
- per = copy;
- if (amount == NULL || strlen(amount) == 0 ||
- str2int64(amount, &num) != 0) {
- free(copy);
- return (rule);
- }
- assert(subject != NULL);
- assert(subject_id != NULL);
- assert(resource != NULL);
- assert(action != NULL);
- if (humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
- HN_DECIMAL | HN_NOSPACE) == -1)
- err(1, "humanize_number");
- if (per == NULL)
- asprintf(&humanized, "%s:%s:%s:%s=%s", subject, subject_id,
- resource, action, buf);
- else
- asprintf(&humanized, "%s:%s:%s:%s=%s/%s", subject, subject_id,
- resource, action, buf, per);
- if (humanized == NULL)
- err(1, "asprintf");
- return (humanized);
- }
- /*
- * Print rules, one per line.
- */
- static void
- print_rules(char *rules, int hflag, int nflag)
- {
- char *rule;
- while ((rule = strsep(&rules, ",")) != NULL) {
- if (rule[0] == '\0')
- break; /* XXX */
- if (nflag == 0)
- rule = humanize_ids(rule);
- if (hflag)
- rule = humanize_amount(rule);
- printf("%s\n", rule);
- }
- }
- static void
- add_rule(char *rule)
- {
- int error;
- error = rctl_add_rule(rule, strlen(rule) + 1, NULL, 0);
- if (error != 0)
- err(1, "rctl_add_rule");
- free(rule);
- }
- static void
- show_limits(char *filter, int hflag, int nflag)
- {
- int error;
- char *outbuf = NULL;
- size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
- do {
- outbuflen *= 4;
- outbuf = realloc(outbuf, outbuflen);
- if (outbuf == NULL)
- err(1, "realloc");
- error = rctl_get_limits(filter, strlen(filter) + 1, outbuf,
- outbuflen);
- if (error && errno != ERANGE)
- err(1, "rctl_get_limits");
- } while (error && errno == ERANGE);
- print_rules(outbuf, hflag, nflag);
- free(filter);
- free(outbuf);
- }
- static void
- remove_rule(char *filter)
- {
- int error;
- error = rctl_remove_rule(filter, strlen(filter) + 1, NULL, 0);
- if (error != 0)
- err(1, "rctl_remove_rule");
- free(filter);
- }
- static char *
- humanize_usage_amount(char *usage)
- {
- int64_t num;
- const char *resource, *amount;
- char *copy, *humanized, buf[6];
- copy = strdup(usage);
- if (copy == NULL)
- err(1, "strdup");
- resource = strsep(©, "=");
- amount = copy;
- assert(resource != NULL);
- assert(amount != NULL);
- if (str2int64(amount, &num) != 0 ||
- humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
- HN_DECIMAL | HN_NOSPACE) == -1) {
- free(copy);
- return (usage);
- }
- asprintf(&humanized, "%s=%s", resource, buf);
- if (humanized == NULL)
- err(1, "asprintf");
- return (humanized);
- }
- /*
- * Query the kernel about a resource usage and print it out.
- */
- static void
- show_usage(char *filter, int hflag)
- {
- int error;
- char *outbuf = NULL, *tmp;
- size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
- do {
- outbuflen *= 4;
- outbuf = realloc(outbuf, outbuflen);
- if (outbuf == NULL)
- err(1, "realloc");
- error = rctl_get_racct(filter, strlen(filter) + 1, outbuf,
- outbuflen);
- if (error && errno != ERANGE)
- err(1, "rctl_get_racct");
- } while (error && errno == ERANGE);
- while ((tmp = strsep(&outbuf, ",")) != NULL) {
- if (tmp[0] == '\0')
- break; /* XXX */
- if (hflag)
- tmp = humanize_usage_amount(tmp);
- printf("%s\n", tmp);
- }
- free(filter);
- free(outbuf);
- }
- /*
- * Query the kernel about resource limit rules and print them out.
- */
- static void
- show_rules(char *filter, int hflag, int nflag)
- {
- int error;
- char *outbuf = NULL;
- size_t filterlen, outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
- if (filter != NULL)
- filterlen = strlen(filter) + 1;
- else
- filterlen = 0;
- do {
- outbuflen *= 4;
- outbuf = realloc(outbuf, outbuflen);
- if (outbuf == NULL)
- err(1, "realloc");
- error = rctl_get_rules(filter, filterlen, outbuf, outbuflen);
- if (error && errno != ERANGE)
- err(1, "rctl_get_rules");
- } while (error && errno == ERANGE);
- print_rules(outbuf, hflag, nflag);
- free(outbuf);
- }
- static void
- usage(void)
- {
- fprintf(stderr, "usage: rctl [ -h ] [-a rule | -l filter | -r filter "
- "| -u filter | filter]\n");
- exit(1);
- }
- int
- main(int argc __unused, char **argv __unused)
- {
- int ch, aflag = 0, hflag = 0, nflag = 0, lflag = 0, rflag = 0,
- uflag = 0;
- char *rule = NULL;
- while ((ch = getopt(argc, argv, "a:hl:nr:u:")) != -1) {
- switch (ch) {
- case 'a':
- aflag = 1;
- rule = strdup(optarg);
- break;
- case 'h':
- hflag = 1;
- break;
- case 'l':
- lflag = 1;
- rule = strdup(optarg);
- break;
- case 'n':
- nflag = 1;
- break;
- case 'r':
- rflag = 1;
- rule = strdup(optarg);
- break;
- case 'u':
- uflag = 1;
- rule = strdup(optarg);
- break;
- case '?':
- default:
- usage();
- }
- }
- argc -= optind;
- argv += optind;
- if (argc > 1)
- usage();
- if (rule == NULL) {
- if (argc == 1)
- rule = strdup(argv[0]);
- else
- rule = strdup("::");
- }
- if (aflag + lflag + rflag + uflag + argc > 1)
- errx(1, "only one flag or argument may be specified "
- "at the same time");
- rule = resolve_ids(rule);
- rule = expand_amount(rule);
- if (aflag) {
- add_rule(rule);
- return (0);
- }
- if (lflag) {
- show_limits(rule, hflag, nflag);
- return (0);
- }
- if (rflag) {
- remove_rule(rule);
- return (0);
- }
- if (uflag) {
- show_usage(rule, hflag);
- return (0);
- }
- show_rules(rule, hflag, nflag);
- return (0);
- }