16ec54a57SEdward Tomasz Napierala /*- 26ec54a57SEdward Tomasz Napierala * Copyright (c) 2010 The FreeBSD Foundation 36ec54a57SEdward Tomasz Napierala * All rights reserved. 46ec54a57SEdward Tomasz Napierala * 56ec54a57SEdward Tomasz Napierala * This software was developed by Edward Tomasz Napierala under sponsorship 66ec54a57SEdward Tomasz Napierala * from the FreeBSD Foundation. 76ec54a57SEdward Tomasz Napierala * 86ec54a57SEdward Tomasz Napierala * Redistribution and use in source and binary forms, with or without 96ec54a57SEdward Tomasz Napierala * modification, are permitted provided that the following conditions 106ec54a57SEdward Tomasz Napierala * are met: 116ec54a57SEdward Tomasz Napierala * 1. Redistributions of source code must retain the above copyright 126ec54a57SEdward Tomasz Napierala * notice, this list of conditions and the following disclaimer. 136ec54a57SEdward Tomasz Napierala * 2. Redistributions in binary form must reproduce the above copyright 146ec54a57SEdward Tomasz Napierala * notice, this list of conditions and the following disclaimer in the 156ec54a57SEdward Tomasz Napierala * documentation and/or other materials provided with the distribution. 166ec54a57SEdward Tomasz Napierala * 176ec54a57SEdward Tomasz Napierala * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 186ec54a57SEdward Tomasz Napierala * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 196ec54a57SEdward Tomasz Napierala * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 206ec54a57SEdward Tomasz Napierala * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 216ec54a57SEdward Tomasz Napierala * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 226ec54a57SEdward Tomasz Napierala * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 236ec54a57SEdward Tomasz Napierala * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 246ec54a57SEdward Tomasz Napierala * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 256ec54a57SEdward Tomasz Napierala * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 266ec54a57SEdward Tomasz Napierala * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 276ec54a57SEdward Tomasz Napierala * SUCH DAMAGE. 286ec54a57SEdward Tomasz Napierala * 296ec54a57SEdward Tomasz Napierala * $FreeBSD$ 306ec54a57SEdward Tomasz Napierala */ 316ec54a57SEdward Tomasz Napierala 326ec54a57SEdward Tomasz Napierala #include <sys/cdefs.h> 336ec54a57SEdward Tomasz Napierala __FBSDID("$FreeBSD$"); 346ec54a57SEdward Tomasz Napierala 356ec54a57SEdward Tomasz Napierala #include <sys/types.h> 366ec54a57SEdward Tomasz Napierala #include <sys/rctl.h> 37aae2a24bSEdward Tomasz Napierala #include <sys/sysctl.h> 386ec54a57SEdward Tomasz Napierala #include <assert.h> 396ec54a57SEdward Tomasz Napierala #include <ctype.h> 406ec54a57SEdward Tomasz Napierala #include <err.h> 416ec54a57SEdward Tomasz Napierala #include <errno.h> 426ec54a57SEdward Tomasz Napierala #include <getopt.h> 436ec54a57SEdward Tomasz Napierala #include <grp.h> 446ec54a57SEdward Tomasz Napierala #include <libutil.h> 456ec54a57SEdward Tomasz Napierala #include <pwd.h> 460971623eSEdward Tomasz Napierala #include <stdbool.h> 476ec54a57SEdward Tomasz Napierala #include <stdint.h> 486ec54a57SEdward Tomasz Napierala #include <stdio.h> 496ec54a57SEdward Tomasz Napierala #include <stdlib.h> 506ec54a57SEdward Tomasz Napierala #include <string.h> 516ec54a57SEdward Tomasz Napierala 5218e1f46eSEdward Tomasz Napierala #define RCTL_DEFAULT_BUFSIZE 128 * 1024 536ec54a57SEdward Tomasz Napierala 540971623eSEdward Tomasz Napierala static int 550971623eSEdward Tomasz Napierala parse_user(const char *s, id_t *uidp) 566ec54a57SEdward Tomasz Napierala { 576ec54a57SEdward Tomasz Napierala char *end; 586ec54a57SEdward Tomasz Napierala struct passwd *pwd; 596ec54a57SEdward Tomasz Napierala 606ec54a57SEdward Tomasz Napierala pwd = getpwnam(s); 610971623eSEdward Tomasz Napierala if (pwd != NULL) { 620971623eSEdward Tomasz Napierala *uidp = pwd->pw_uid; 630971623eSEdward Tomasz Napierala return (0); 646ec54a57SEdward Tomasz Napierala } 656ec54a57SEdward Tomasz Napierala 660971623eSEdward Tomasz Napierala if (!isnumber(s[0])) { 670971623eSEdward Tomasz Napierala warnx("uknown user '%s'", s); 680971623eSEdward Tomasz Napierala return (1); 690971623eSEdward Tomasz Napierala } 700971623eSEdward Tomasz Napierala 710971623eSEdward Tomasz Napierala *uidp = strtod(s, &end); 720971623eSEdward Tomasz Napierala if ((size_t)(end - s) != strlen(s)) { 730971623eSEdward Tomasz Napierala warnx("trailing characters after numerical id"); 740971623eSEdward Tomasz Napierala return (1); 750971623eSEdward Tomasz Napierala } 760971623eSEdward Tomasz Napierala 770971623eSEdward Tomasz Napierala return (0); 780971623eSEdward Tomasz Napierala } 790971623eSEdward Tomasz Napierala 800971623eSEdward Tomasz Napierala static int 810971623eSEdward Tomasz Napierala parse_group(const char *s, id_t *gidp) 826ec54a57SEdward Tomasz Napierala { 836ec54a57SEdward Tomasz Napierala char *end; 846ec54a57SEdward Tomasz Napierala struct group *grp; 856ec54a57SEdward Tomasz Napierala 866ec54a57SEdward Tomasz Napierala grp = getgrnam(s); 870971623eSEdward Tomasz Napierala if (grp != NULL) { 880971623eSEdward Tomasz Napierala *gidp = grp->gr_gid; 890971623eSEdward Tomasz Napierala return (0); 900971623eSEdward Tomasz Napierala } 916ec54a57SEdward Tomasz Napierala 920971623eSEdward Tomasz Napierala if (!isnumber(s[0])) { 930971623eSEdward Tomasz Napierala warnx("uknown group '%s'", s); 940971623eSEdward Tomasz Napierala return (1); 950971623eSEdward Tomasz Napierala } 966ec54a57SEdward Tomasz Napierala 970971623eSEdward Tomasz Napierala *gidp = strtod(s, &end); 980971623eSEdward Tomasz Napierala if ((size_t)(end - s) != strlen(s)) { 990971623eSEdward Tomasz Napierala warnx("trailing characters after numerical id"); 1000971623eSEdward Tomasz Napierala return (1); 1010971623eSEdward Tomasz Napierala } 1026ec54a57SEdward Tomasz Napierala 1030971623eSEdward Tomasz Napierala return (0); 1046ec54a57SEdward Tomasz Napierala } 1056ec54a57SEdward Tomasz Napierala 1066ec54a57SEdward Tomasz Napierala /* 1070971623eSEdward Tomasz Napierala * Replace human-readable number with its expanded form. 1086ec54a57SEdward Tomasz Napierala */ 1096ec54a57SEdward Tomasz Napierala static char * 1106ec54a57SEdward Tomasz Napierala expand_amount(char *rule) 1116ec54a57SEdward Tomasz Napierala { 1126ec54a57SEdward Tomasz Napierala uint64_t num; 1136ec54a57SEdward Tomasz Napierala const char *subject, *subject_id, *resource, *action, *amount, *per; 1145e7a2555SEdward Tomasz Napierala char *copy, *expanded, *tofree; 115f4e361a7SEdward Tomasz Napierala int ret; 1166ec54a57SEdward Tomasz Napierala 1175e7a2555SEdward Tomasz Napierala tofree = copy = strdup(rule); 1180971623eSEdward Tomasz Napierala if (copy == NULL) { 1190971623eSEdward Tomasz Napierala warn("strdup"); 1200971623eSEdward Tomasz Napierala return (NULL); 1210971623eSEdward Tomasz Napierala } 1226ec54a57SEdward Tomasz Napierala 1236ec54a57SEdward Tomasz Napierala subject = strsep(©, ":"); 1246ec54a57SEdward Tomasz Napierala subject_id = strsep(©, ":"); 1256ec54a57SEdward Tomasz Napierala resource = strsep(©, ":"); 1266ec54a57SEdward Tomasz Napierala action = strsep(©, "=/"); 1276ec54a57SEdward Tomasz Napierala amount = strsep(©, "/"); 1286ec54a57SEdward Tomasz Napierala per = copy; 1296ec54a57SEdward Tomasz Napierala 1306ec54a57SEdward Tomasz Napierala if (amount == NULL || strlen(amount) == 0) { 1315e7a2555SEdward Tomasz Napierala free(tofree); 1326ec54a57SEdward Tomasz Napierala return (rule); 1336ec54a57SEdward Tomasz Napierala } 1346ec54a57SEdward Tomasz Napierala 1356ec54a57SEdward Tomasz Napierala assert(subject != NULL); 1366ec54a57SEdward Tomasz Napierala assert(subject_id != NULL); 1376ec54a57SEdward Tomasz Napierala assert(resource != NULL); 1386ec54a57SEdward Tomasz Napierala assert(action != NULL); 1396ec54a57SEdward Tomasz Napierala 1400971623eSEdward Tomasz Napierala if (expand_number(amount, &num)) { 1410971623eSEdward Tomasz Napierala warnx("invalid numeric value '%s'", amount); 1425e7a2555SEdward Tomasz Napierala free(tofree); 1430971623eSEdward Tomasz Napierala return (NULL); 1440971623eSEdward Tomasz Napierala } 1456ec54a57SEdward Tomasz Napierala 146f4e361a7SEdward Tomasz Napierala if (per == NULL) { 147f4e361a7SEdward Tomasz Napierala ret = asprintf(&expanded, "%s:%s:%s:%s=%ju", 148f4e361a7SEdward Tomasz Napierala subject, subject_id, resource, action, (uintmax_t)num); 149f4e361a7SEdward Tomasz Napierala } else { 150f4e361a7SEdward Tomasz Napierala ret = asprintf(&expanded, "%s:%s:%s:%s=%ju/%s", 151f4e361a7SEdward Tomasz Napierala subject, subject_id, resource, action, (uintmax_t)num, per); 152f4e361a7SEdward Tomasz Napierala } 1536ec54a57SEdward Tomasz Napierala 154f4e361a7SEdward Tomasz Napierala if (ret <= 0) { 1550971623eSEdward Tomasz Napierala warn("asprintf"); 1565e7a2555SEdward Tomasz Napierala free(tofree); 1570971623eSEdward Tomasz Napierala return (NULL); 1580971623eSEdward Tomasz Napierala } 1596ec54a57SEdward Tomasz Napierala 1605e7a2555SEdward Tomasz Napierala free(tofree); 1616ec54a57SEdward Tomasz Napierala return (expanded); 1626ec54a57SEdward Tomasz Napierala } 1636ec54a57SEdward Tomasz Napierala 1640971623eSEdward Tomasz Napierala 1650971623eSEdward Tomasz Napierala static char * 1660971623eSEdward Tomasz Napierala expand_rule(char *rule, bool resolve_ids) 1670971623eSEdward Tomasz Napierala { 1680971623eSEdward Tomasz Napierala id_t id; 1690971623eSEdward Tomasz Napierala const char *subject, *textid, *rest; 1700971623eSEdward Tomasz Napierala char *resolved; 171f4e361a7SEdward Tomasz Napierala int error, ret; 1720971623eSEdward Tomasz Napierala 1730971623eSEdward Tomasz Napierala subject = strsep(&rule, ":"); 1740971623eSEdward Tomasz Napierala textid = strsep(&rule, ":"); 1750971623eSEdward Tomasz Napierala if (textid == NULL) { 1760971623eSEdward Tomasz Napierala warnx("error in rule specification -- no subject"); 1770971623eSEdward Tomasz Napierala return (NULL); 1780971623eSEdward Tomasz Napierala } 1790971623eSEdward Tomasz Napierala if (rule != NULL) 1800971623eSEdward Tomasz Napierala rest = rule; 1810971623eSEdward Tomasz Napierala else 1820971623eSEdward Tomasz Napierala rest = ""; 1830971623eSEdward Tomasz Napierala 1840971623eSEdward Tomasz Napierala if (strcasecmp(subject, "u") == 0) 1850971623eSEdward Tomasz Napierala subject = "user"; 1860971623eSEdward Tomasz Napierala else if (strcasecmp(subject, "g") == 0) 1870971623eSEdward Tomasz Napierala subject = "group"; 1880971623eSEdward Tomasz Napierala else if (strcasecmp(subject, "p") == 0) 1890971623eSEdward Tomasz Napierala subject = "process"; 1900971623eSEdward Tomasz Napierala else if (strcasecmp(subject, "l") == 0 || 1910971623eSEdward Tomasz Napierala strcasecmp(subject, "c") == 0 || 1920971623eSEdward Tomasz Napierala strcasecmp(subject, "class") == 0) 1930971623eSEdward Tomasz Napierala subject = "loginclass"; 1940971623eSEdward Tomasz Napierala else if (strcasecmp(subject, "j") == 0) 1950971623eSEdward Tomasz Napierala subject = "jail"; 1960971623eSEdward Tomasz Napierala 1970971623eSEdward Tomasz Napierala if (resolve_ids && 1980971623eSEdward Tomasz Napierala strcasecmp(subject, "user") == 0 && strlen(textid) > 0) { 1990971623eSEdward Tomasz Napierala error = parse_user(textid, &id); 2000971623eSEdward Tomasz Napierala if (error != 0) 2010971623eSEdward Tomasz Napierala return (NULL); 202f4e361a7SEdward Tomasz Napierala ret = asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest); 2030971623eSEdward Tomasz Napierala } else if (resolve_ids && 2040971623eSEdward Tomasz Napierala strcasecmp(subject, "group") == 0 && strlen(textid) > 0) { 2050971623eSEdward Tomasz Napierala error = parse_group(textid, &id); 2060971623eSEdward Tomasz Napierala if (error != 0) 2070971623eSEdward Tomasz Napierala return (NULL); 208f4e361a7SEdward Tomasz Napierala ret = asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest); 2090971623eSEdward Tomasz Napierala } else { 210f4e361a7SEdward Tomasz Napierala ret = asprintf(&resolved, "%s:%s:%s", subject, textid, rest); 2110971623eSEdward Tomasz Napierala } 2120971623eSEdward Tomasz Napierala 213f4e361a7SEdward Tomasz Napierala if (ret <= 0) { 2140971623eSEdward Tomasz Napierala warn("asprintf"); 2150971623eSEdward Tomasz Napierala return (NULL); 2160971623eSEdward Tomasz Napierala } 2170971623eSEdward Tomasz Napierala 2180971623eSEdward Tomasz Napierala return (expand_amount(resolved)); 2190971623eSEdward Tomasz Napierala } 2200971623eSEdward Tomasz Napierala 2216ec54a57SEdward Tomasz Napierala static char * 2226ec54a57SEdward Tomasz Napierala humanize_ids(char *rule) 2236ec54a57SEdward Tomasz Napierala { 2246ec54a57SEdward Tomasz Napierala id_t id; 2256ec54a57SEdward Tomasz Napierala struct passwd *pwd; 2266ec54a57SEdward Tomasz Napierala struct group *grp; 2276ec54a57SEdward Tomasz Napierala const char *subject, *textid, *rest; 228478f7a72SEdward Tomasz Napierala char *end, *humanized; 229f4e361a7SEdward Tomasz Napierala int ret; 2306ec54a57SEdward Tomasz Napierala 2316ec54a57SEdward Tomasz Napierala subject = strsep(&rule, ":"); 2326ec54a57SEdward Tomasz Napierala textid = strsep(&rule, ":"); 2336ec54a57SEdward Tomasz Napierala if (textid == NULL) 2346ec54a57SEdward Tomasz Napierala errx(1, "rule passed from the kernel didn't contain subject"); 2356ec54a57SEdward Tomasz Napierala if (rule != NULL) 2366ec54a57SEdward Tomasz Napierala rest = rule; 2376ec54a57SEdward Tomasz Napierala else 2386ec54a57SEdward Tomasz Napierala rest = ""; 2396ec54a57SEdward Tomasz Napierala 2406ec54a57SEdward Tomasz Napierala /* Replace numerical user and group ids with names. */ 2416ec54a57SEdward Tomasz Napierala if (strcasecmp(subject, "user") == 0) { 242478f7a72SEdward Tomasz Napierala id = strtod(textid, &end); 243478f7a72SEdward Tomasz Napierala if ((size_t)(end - textid) != strlen(textid)) 244478f7a72SEdward Tomasz Napierala errx(1, "malformed uid '%s'", textid); 2456ec54a57SEdward Tomasz Napierala pwd = getpwuid(id); 2466ec54a57SEdward Tomasz Napierala if (pwd != NULL) 2476ec54a57SEdward Tomasz Napierala textid = pwd->pw_name; 2486ec54a57SEdward Tomasz Napierala } else if (strcasecmp(subject, "group") == 0) { 249478f7a72SEdward Tomasz Napierala id = strtod(textid, &end); 250478f7a72SEdward Tomasz Napierala if ((size_t)(end - textid) != strlen(textid)) 251478f7a72SEdward Tomasz Napierala errx(1, "malformed gid '%s'", textid); 2526ec54a57SEdward Tomasz Napierala grp = getgrgid(id); 2536ec54a57SEdward Tomasz Napierala if (grp != NULL) 2546ec54a57SEdward Tomasz Napierala textid = grp->gr_name; 2556ec54a57SEdward Tomasz Napierala } 2566ec54a57SEdward Tomasz Napierala 257f4e361a7SEdward Tomasz Napierala ret = asprintf(&humanized, "%s:%s:%s", subject, textid, rest); 258f4e361a7SEdward Tomasz Napierala if (ret <= 0) 2596ec54a57SEdward Tomasz Napierala err(1, "asprintf"); 2606ec54a57SEdward Tomasz Napierala 2616ec54a57SEdward Tomasz Napierala return (humanized); 2626ec54a57SEdward Tomasz Napierala } 2636ec54a57SEdward Tomasz Napierala 2646ec54a57SEdward Tomasz Napierala static int 2656ec54a57SEdward Tomasz Napierala str2int64(const char *str, int64_t *value) 2666ec54a57SEdward Tomasz Napierala { 2676ec54a57SEdward Tomasz Napierala char *end; 2686ec54a57SEdward Tomasz Napierala 2696ec54a57SEdward Tomasz Napierala if (str == NULL) 2706ec54a57SEdward Tomasz Napierala return (EINVAL); 2716ec54a57SEdward Tomasz Napierala 2726ec54a57SEdward Tomasz Napierala *value = strtoul(str, &end, 10); 2736ec54a57SEdward Tomasz Napierala if ((size_t)(end - str) != strlen(str)) 2746ec54a57SEdward Tomasz Napierala return (EINVAL); 2756ec54a57SEdward Tomasz Napierala 2766ec54a57SEdward Tomasz Napierala return (0); 2776ec54a57SEdward Tomasz Napierala } 2786ec54a57SEdward Tomasz Napierala 2796ec54a57SEdward Tomasz Napierala static char * 2806ec54a57SEdward Tomasz Napierala humanize_amount(char *rule) 2816ec54a57SEdward Tomasz Napierala { 2826ec54a57SEdward Tomasz Napierala int64_t num; 2836ec54a57SEdward Tomasz Napierala const char *subject, *subject_id, *resource, *action, *amount, *per; 2845e7a2555SEdward Tomasz Napierala char *copy, *humanized, buf[6], *tofree; 285f4e361a7SEdward Tomasz Napierala int ret; 2866ec54a57SEdward Tomasz Napierala 2875e7a2555SEdward Tomasz Napierala tofree = copy = strdup(rule); 2886ec54a57SEdward Tomasz Napierala if (copy == NULL) 2896ec54a57SEdward Tomasz Napierala err(1, "strdup"); 2906ec54a57SEdward Tomasz Napierala 2916ec54a57SEdward Tomasz Napierala subject = strsep(©, ":"); 2926ec54a57SEdward Tomasz Napierala subject_id = strsep(©, ":"); 2936ec54a57SEdward Tomasz Napierala resource = strsep(©, ":"); 2946ec54a57SEdward Tomasz Napierala action = strsep(©, "=/"); 2956ec54a57SEdward Tomasz Napierala amount = strsep(©, "/"); 2966ec54a57SEdward Tomasz Napierala per = copy; 2976ec54a57SEdward Tomasz Napierala 2986ec54a57SEdward Tomasz Napierala if (amount == NULL || strlen(amount) == 0 || 2996ec54a57SEdward Tomasz Napierala str2int64(amount, &num) != 0) { 3005e7a2555SEdward Tomasz Napierala free(tofree); 3016ec54a57SEdward Tomasz Napierala return (rule); 3026ec54a57SEdward Tomasz Napierala } 3036ec54a57SEdward Tomasz Napierala 3046ec54a57SEdward Tomasz Napierala assert(subject != NULL); 3056ec54a57SEdward Tomasz Napierala assert(subject_id != NULL); 3066ec54a57SEdward Tomasz Napierala assert(resource != NULL); 3076ec54a57SEdward Tomasz Napierala assert(action != NULL); 3086ec54a57SEdward Tomasz Napierala 3096ec54a57SEdward Tomasz Napierala if (humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE, 3106ec54a57SEdward Tomasz Napierala HN_DECIMAL | HN_NOSPACE) == -1) 3116ec54a57SEdward Tomasz Napierala err(1, "humanize_number"); 3126ec54a57SEdward Tomasz Napierala 313f4e361a7SEdward Tomasz Napierala if (per == NULL) { 314f4e361a7SEdward Tomasz Napierala ret = asprintf(&humanized, "%s:%s:%s:%s=%s", 315f4e361a7SEdward Tomasz Napierala subject, subject_id, resource, action, buf); 316f4e361a7SEdward Tomasz Napierala } else { 317f4e361a7SEdward Tomasz Napierala ret = asprintf(&humanized, "%s:%s:%s:%s=%s/%s", 318f4e361a7SEdward Tomasz Napierala subject, subject_id, resource, action, buf, per); 319f4e361a7SEdward Tomasz Napierala } 3206ec54a57SEdward Tomasz Napierala 321f4e361a7SEdward Tomasz Napierala if (ret <= 0) 3226ec54a57SEdward Tomasz Napierala err(1, "asprintf"); 3236ec54a57SEdward Tomasz Napierala 3245e7a2555SEdward Tomasz Napierala free(tofree); 3256ec54a57SEdward Tomasz Napierala return (humanized); 3266ec54a57SEdward Tomasz Napierala } 3276ec54a57SEdward Tomasz Napierala 3286ec54a57SEdward Tomasz Napierala /* 3296ec54a57SEdward Tomasz Napierala * Print rules, one per line. 3306ec54a57SEdward Tomasz Napierala */ 3316ec54a57SEdward Tomasz Napierala static void 3326ec54a57SEdward Tomasz Napierala print_rules(char *rules, int hflag, int nflag) 3336ec54a57SEdward Tomasz Napierala { 3346ec54a57SEdward Tomasz Napierala char *rule; 3356ec54a57SEdward Tomasz Napierala 3366ec54a57SEdward Tomasz Napierala while ((rule = strsep(&rules, ",")) != NULL) { 3376ec54a57SEdward Tomasz Napierala if (rule[0] == '\0') 3386ec54a57SEdward Tomasz Napierala break; /* XXX */ 3396ec54a57SEdward Tomasz Napierala if (nflag == 0) 3406ec54a57SEdward Tomasz Napierala rule = humanize_ids(rule); 3416ec54a57SEdward Tomasz Napierala if (hflag) 3426ec54a57SEdward Tomasz Napierala rule = humanize_amount(rule); 3436ec54a57SEdward Tomasz Napierala printf("%s\n", rule); 3446ec54a57SEdward Tomasz Napierala } 3456ec54a57SEdward Tomasz Napierala } 3466ec54a57SEdward Tomasz Napierala 3476ec54a57SEdward Tomasz Napierala static void 348aae2a24bSEdward Tomasz Napierala enosys(void) 349aae2a24bSEdward Tomasz Napierala { 350aae2a24bSEdward Tomasz Napierala int error, racct_enable; 351aae2a24bSEdward Tomasz Napierala size_t racct_enable_len; 352aae2a24bSEdward Tomasz Napierala 353aae2a24bSEdward Tomasz Napierala racct_enable_len = sizeof(racct_enable); 354aae2a24bSEdward Tomasz Napierala error = sysctlbyname("kern.racct.enable", 355aae2a24bSEdward Tomasz Napierala &racct_enable, &racct_enable_len, NULL, 0); 356aae2a24bSEdward Tomasz Napierala 357aae2a24bSEdward Tomasz Napierala if (error != 0) { 358aae2a24bSEdward Tomasz Napierala if (errno == ENOENT) 35982224d7dSEdward Tomasz Napierala errx(1, "RACCT/RCTL support not present in kernel; see rctl(8) for details"); 360aae2a24bSEdward Tomasz Napierala 361aae2a24bSEdward Tomasz Napierala err(1, "sysctlbyname"); 362aae2a24bSEdward Tomasz Napierala } 363aae2a24bSEdward Tomasz Napierala 364aae2a24bSEdward Tomasz Napierala if (racct_enable == 0) 365aae2a24bSEdward Tomasz Napierala errx(1, "RACCT/RCTL present, but disabled; enable using kern.racct.enable=1 tunable"); 366aae2a24bSEdward Tomasz Napierala } 367aae2a24bSEdward Tomasz Napierala 3680971623eSEdward Tomasz Napierala static int 3690971623eSEdward Tomasz Napierala add_rule(const char *rule) 3706ec54a57SEdward Tomasz Napierala { 3716ec54a57SEdward Tomasz Napierala int error; 3726ec54a57SEdward Tomasz Napierala 3736ec54a57SEdward Tomasz Napierala error = rctl_add_rule(rule, strlen(rule) + 1, NULL, 0); 374aae2a24bSEdward Tomasz Napierala if (error != 0) { 375aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 376aae2a24bSEdward Tomasz Napierala enosys(); 3770971623eSEdward Tomasz Napierala warn("rctl_add_rule"); 3786ec54a57SEdward Tomasz Napierala } 3796ec54a57SEdward Tomasz Napierala 3800971623eSEdward Tomasz Napierala return (error); 3810971623eSEdward Tomasz Napierala } 3820971623eSEdward Tomasz Napierala 3830971623eSEdward Tomasz Napierala static int 3840971623eSEdward Tomasz Napierala show_limits(const char *filter, int hflag, int nflag) 3856ec54a57SEdward Tomasz Napierala { 3866ec54a57SEdward Tomasz Napierala int error; 3876ec54a57SEdward Tomasz Napierala char *outbuf = NULL; 3886ec54a57SEdward Tomasz Napierala size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4; 3896ec54a57SEdward Tomasz Napierala 390*4cf0d895SEdward Tomasz Napierala for (;;) { 3916ec54a57SEdward Tomasz Napierala outbuflen *= 4; 3926ec54a57SEdward Tomasz Napierala outbuf = realloc(outbuf, outbuflen); 3936ec54a57SEdward Tomasz Napierala if (outbuf == NULL) 3946ec54a57SEdward Tomasz Napierala err(1, "realloc"); 395*4cf0d895SEdward Tomasz Napierala error = rctl_get_limits(filter, strlen(filter) + 1, 396*4cf0d895SEdward Tomasz Napierala outbuf, outbuflen); 397*4cf0d895SEdward Tomasz Napierala if (error == 0) 398*4cf0d895SEdward Tomasz Napierala break; 399*4cf0d895SEdward Tomasz Napierala if (errno == ERANGE) 400*4cf0d895SEdward Tomasz Napierala continue; 401aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 402aae2a24bSEdward Tomasz Napierala enosys(); 4030971623eSEdward Tomasz Napierala warn("rctl_get_limits"); 404*4cf0d895SEdward Tomasz Napierala free(outbuf); 405*4cf0d895SEdward Tomasz Napierala 406*4cf0d895SEdward Tomasz Napierala return (error); 407aae2a24bSEdward Tomasz Napierala } 4086ec54a57SEdward Tomasz Napierala 4096ec54a57SEdward Tomasz Napierala print_rules(outbuf, hflag, nflag); 4106ec54a57SEdward Tomasz Napierala free(outbuf); 4110971623eSEdward Tomasz Napierala 4120971623eSEdward Tomasz Napierala return (error); 4136ec54a57SEdward Tomasz Napierala } 4146ec54a57SEdward Tomasz Napierala 4150971623eSEdward Tomasz Napierala static int 4160971623eSEdward Tomasz Napierala remove_rule(const char *filter) 4176ec54a57SEdward Tomasz Napierala { 4186ec54a57SEdward Tomasz Napierala int error; 4196ec54a57SEdward Tomasz Napierala 4206ec54a57SEdward Tomasz Napierala error = rctl_remove_rule(filter, strlen(filter) + 1, NULL, 0); 421aae2a24bSEdward Tomasz Napierala if (error != 0) { 422aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 423aae2a24bSEdward Tomasz Napierala enosys(); 4240971623eSEdward Tomasz Napierala warn("rctl_remove_rule"); 425aae2a24bSEdward Tomasz Napierala } 4260971623eSEdward Tomasz Napierala 4270971623eSEdward Tomasz Napierala return (error); 4286ec54a57SEdward Tomasz Napierala } 4296ec54a57SEdward Tomasz Napierala 4306ec54a57SEdward Tomasz Napierala static char * 4316ec54a57SEdward Tomasz Napierala humanize_usage_amount(char *usage) 4326ec54a57SEdward Tomasz Napierala { 4336ec54a57SEdward Tomasz Napierala int64_t num; 4346ec54a57SEdward Tomasz Napierala const char *resource, *amount; 4355e7a2555SEdward Tomasz Napierala char *copy, *humanized, buf[6], *tofree; 436f4e361a7SEdward Tomasz Napierala int ret; 4376ec54a57SEdward Tomasz Napierala 4385e7a2555SEdward Tomasz Napierala tofree = copy = strdup(usage); 4396ec54a57SEdward Tomasz Napierala if (copy == NULL) 4406ec54a57SEdward Tomasz Napierala err(1, "strdup"); 4416ec54a57SEdward Tomasz Napierala 4426ec54a57SEdward Tomasz Napierala resource = strsep(©, "="); 4436ec54a57SEdward Tomasz Napierala amount = copy; 4446ec54a57SEdward Tomasz Napierala 4456ec54a57SEdward Tomasz Napierala assert(resource != NULL); 4466ec54a57SEdward Tomasz Napierala assert(amount != NULL); 4476ec54a57SEdward Tomasz Napierala 4486ec54a57SEdward Tomasz Napierala if (str2int64(amount, &num) != 0 || 4496ec54a57SEdward Tomasz Napierala humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE, 4506ec54a57SEdward Tomasz Napierala HN_DECIMAL | HN_NOSPACE) == -1) { 4515e7a2555SEdward Tomasz Napierala free(tofree); 4526ec54a57SEdward Tomasz Napierala return (usage); 4536ec54a57SEdward Tomasz Napierala } 4546ec54a57SEdward Tomasz Napierala 455f4e361a7SEdward Tomasz Napierala ret = asprintf(&humanized, "%s=%s", resource, buf); 456f4e361a7SEdward Tomasz Napierala if (ret <= 0) 4576ec54a57SEdward Tomasz Napierala err(1, "asprintf"); 4586ec54a57SEdward Tomasz Napierala 4595e7a2555SEdward Tomasz Napierala free(tofree); 4606ec54a57SEdward Tomasz Napierala return (humanized); 4616ec54a57SEdward Tomasz Napierala } 4626ec54a57SEdward Tomasz Napierala 4636ec54a57SEdward Tomasz Napierala /* 4646ec54a57SEdward Tomasz Napierala * Query the kernel about a resource usage and print it out. 4656ec54a57SEdward Tomasz Napierala */ 4660971623eSEdward Tomasz Napierala static int 4670971623eSEdward Tomasz Napierala show_usage(const char *filter, int hflag) 4686ec54a57SEdward Tomasz Napierala { 4696ec54a57SEdward Tomasz Napierala int error; 4705e7a2555SEdward Tomasz Napierala char *copy, *outbuf = NULL, *tmp; 4716ec54a57SEdward Tomasz Napierala size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4; 4726ec54a57SEdward Tomasz Napierala 473*4cf0d895SEdward Tomasz Napierala for (;;) { 4746ec54a57SEdward Tomasz Napierala outbuflen *= 4; 4756ec54a57SEdward Tomasz Napierala outbuf = realloc(outbuf, outbuflen); 4766ec54a57SEdward Tomasz Napierala if (outbuf == NULL) 4776ec54a57SEdward Tomasz Napierala err(1, "realloc"); 478*4cf0d895SEdward Tomasz Napierala error = rctl_get_racct(filter, strlen(filter) + 1, 479*4cf0d895SEdward Tomasz Napierala outbuf, outbuflen); 480*4cf0d895SEdward Tomasz Napierala if (error == 0) 481*4cf0d895SEdward Tomasz Napierala break; 482*4cf0d895SEdward Tomasz Napierala if (errno == ERANGE) 483*4cf0d895SEdward Tomasz Napierala continue; 484aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 485aae2a24bSEdward Tomasz Napierala enosys(); 4860971623eSEdward Tomasz Napierala warn("rctl_get_racct"); 487*4cf0d895SEdward Tomasz Napierala free(outbuf); 488*4cf0d895SEdward Tomasz Napierala 489*4cf0d895SEdward Tomasz Napierala return (error); 490aae2a24bSEdward Tomasz Napierala } 4916ec54a57SEdward Tomasz Napierala 4925e7a2555SEdward Tomasz Napierala copy = outbuf; 4935e7a2555SEdward Tomasz Napierala while ((tmp = strsep(©, ",")) != NULL) { 4946ec54a57SEdward Tomasz Napierala if (tmp[0] == '\0') 4956ec54a57SEdward Tomasz Napierala break; /* XXX */ 4966ec54a57SEdward Tomasz Napierala 4976ec54a57SEdward Tomasz Napierala if (hflag) 4986ec54a57SEdward Tomasz Napierala tmp = humanize_usage_amount(tmp); 4996ec54a57SEdward Tomasz Napierala 5006ec54a57SEdward Tomasz Napierala printf("%s\n", tmp); 5016ec54a57SEdward Tomasz Napierala } 5026ec54a57SEdward Tomasz Napierala 5036ec54a57SEdward Tomasz Napierala free(outbuf); 5040971623eSEdward Tomasz Napierala 5050971623eSEdward Tomasz Napierala return (error); 5066ec54a57SEdward Tomasz Napierala } 5076ec54a57SEdward Tomasz Napierala 5086ec54a57SEdward Tomasz Napierala /* 5096ec54a57SEdward Tomasz Napierala * Query the kernel about resource limit rules and print them out. 5106ec54a57SEdward Tomasz Napierala */ 5110971623eSEdward Tomasz Napierala static int 5120971623eSEdward Tomasz Napierala show_rules(const char *filter, int hflag, int nflag) 5136ec54a57SEdward Tomasz Napierala { 5146ec54a57SEdward Tomasz Napierala int error; 5156ec54a57SEdward Tomasz Napierala char *outbuf = NULL; 5166ec54a57SEdward Tomasz Napierala size_t filterlen, outbuflen = RCTL_DEFAULT_BUFSIZE / 4; 5176ec54a57SEdward Tomasz Napierala 5186ec54a57SEdward Tomasz Napierala if (filter != NULL) 5196ec54a57SEdward Tomasz Napierala filterlen = strlen(filter) + 1; 5206ec54a57SEdward Tomasz Napierala else 5216ec54a57SEdward Tomasz Napierala filterlen = 0; 5226ec54a57SEdward Tomasz Napierala 523*4cf0d895SEdward Tomasz Napierala for (;;) { 5246ec54a57SEdward Tomasz Napierala outbuflen *= 4; 5256ec54a57SEdward Tomasz Napierala outbuf = realloc(outbuf, outbuflen); 5266ec54a57SEdward Tomasz Napierala if (outbuf == NULL) 5276ec54a57SEdward Tomasz Napierala err(1, "realloc"); 5286ec54a57SEdward Tomasz Napierala error = rctl_get_rules(filter, filterlen, outbuf, outbuflen); 529*4cf0d895SEdward Tomasz Napierala if (error == 0) 530*4cf0d895SEdward Tomasz Napierala break; 531*4cf0d895SEdward Tomasz Napierala if (errno == ERANGE) 532*4cf0d895SEdward Tomasz Napierala continue; 533aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 534aae2a24bSEdward Tomasz Napierala enosys(); 5350971623eSEdward Tomasz Napierala warn("rctl_get_rules"); 536*4cf0d895SEdward Tomasz Napierala free(outbuf); 537*4cf0d895SEdward Tomasz Napierala 538*4cf0d895SEdward Tomasz Napierala return (error); 539aae2a24bSEdward Tomasz Napierala } 5406ec54a57SEdward Tomasz Napierala 5416ec54a57SEdward Tomasz Napierala print_rules(outbuf, hflag, nflag); 5426ec54a57SEdward Tomasz Napierala free(outbuf); 5430971623eSEdward Tomasz Napierala 5440971623eSEdward Tomasz Napierala return (error); 5456ec54a57SEdward Tomasz Napierala } 5466ec54a57SEdward Tomasz Napierala 5476ec54a57SEdward Tomasz Napierala static void 5486ec54a57SEdward Tomasz Napierala usage(void) 5496ec54a57SEdward Tomasz Napierala { 5506ec54a57SEdward Tomasz Napierala 5516ec54a57SEdward Tomasz Napierala fprintf(stderr, "usage: rctl [ -h ] [-a rule | -l filter | -r filter " 5526ec54a57SEdward Tomasz Napierala "| -u filter | filter]\n"); 5536ec54a57SEdward Tomasz Napierala exit(1); 5546ec54a57SEdward Tomasz Napierala } 5556ec54a57SEdward Tomasz Napierala 5566ec54a57SEdward Tomasz Napierala int 5576ec54a57SEdward Tomasz Napierala main(int argc __unused, char **argv __unused) 5586ec54a57SEdward Tomasz Napierala { 5596ec54a57SEdward Tomasz Napierala int ch, aflag = 0, hflag = 0, nflag = 0, lflag = 0, rflag = 0, 5606ec54a57SEdward Tomasz Napierala uflag = 0; 5616ec54a57SEdward Tomasz Napierala char *rule = NULL; 5620971623eSEdward Tomasz Napierala int i, cumulated_error; 5636ec54a57SEdward Tomasz Napierala 5640971623eSEdward Tomasz Napierala while ((ch = getopt(argc, argv, "ahlnru")) != -1) { 5656ec54a57SEdward Tomasz Napierala switch (ch) { 5666ec54a57SEdward Tomasz Napierala case 'a': 5676ec54a57SEdward Tomasz Napierala aflag = 1; 5686ec54a57SEdward Tomasz Napierala break; 5696ec54a57SEdward Tomasz Napierala case 'h': 5706ec54a57SEdward Tomasz Napierala hflag = 1; 5716ec54a57SEdward Tomasz Napierala break; 5726ec54a57SEdward Tomasz Napierala case 'l': 5736ec54a57SEdward Tomasz Napierala lflag = 1; 5746ec54a57SEdward Tomasz Napierala break; 5756ec54a57SEdward Tomasz Napierala case 'n': 5766ec54a57SEdward Tomasz Napierala nflag = 1; 5776ec54a57SEdward Tomasz Napierala break; 5786ec54a57SEdward Tomasz Napierala case 'r': 5796ec54a57SEdward Tomasz Napierala rflag = 1; 5806ec54a57SEdward Tomasz Napierala break; 5816ec54a57SEdward Tomasz Napierala case 'u': 5826ec54a57SEdward Tomasz Napierala uflag = 1; 5836ec54a57SEdward Tomasz Napierala break; 5846ec54a57SEdward Tomasz Napierala 5856ec54a57SEdward Tomasz Napierala case '?': 5866ec54a57SEdward Tomasz Napierala default: 5876ec54a57SEdward Tomasz Napierala usage(); 5886ec54a57SEdward Tomasz Napierala } 5896ec54a57SEdward Tomasz Napierala } 5906ec54a57SEdward Tomasz Napierala 5916ec54a57SEdward Tomasz Napierala argc -= optind; 5926ec54a57SEdward Tomasz Napierala argv += optind; 5936ec54a57SEdward Tomasz Napierala 5940971623eSEdward Tomasz Napierala if (aflag + lflag + rflag + uflag > 1) 5950971623eSEdward Tomasz Napierala errx(1, "at most one of -a, -l, -r, or -u may be specified"); 5960971623eSEdward Tomasz Napierala 5970971623eSEdward Tomasz Napierala if (argc == 0) { 5980971623eSEdward Tomasz Napierala if (aflag + lflag + rflag + uflag == 0) { 5990971623eSEdward Tomasz Napierala rule = strdup("::"); 6000971623eSEdward Tomasz Napierala show_rules(rule, hflag, nflag); 6010971623eSEdward Tomasz Napierala 6020971623eSEdward Tomasz Napierala return (0); 6030971623eSEdward Tomasz Napierala } 6040971623eSEdward Tomasz Napierala 6056ec54a57SEdward Tomasz Napierala usage(); 6060971623eSEdward Tomasz Napierala } 6070971623eSEdward Tomasz Napierala 6080971623eSEdward Tomasz Napierala cumulated_error = 0; 6090971623eSEdward Tomasz Napierala 6100971623eSEdward Tomasz Napierala for (i = 0; i < argc; i++) { 6110971623eSEdward Tomasz Napierala rule = argv[i]; 6120971623eSEdward Tomasz Napierala 6130971623eSEdward Tomasz Napierala /* 6140971623eSEdward Tomasz Napierala * Skip resolving if passed -n _and_ -a. Ignore -n otherwise, 6150971623eSEdward Tomasz Napierala * so we can still do "rctl -n u:root" and see the rules without 6160971623eSEdward Tomasz Napierala * resolving the UID. 6170971623eSEdward Tomasz Napierala */ 6180971623eSEdward Tomasz Napierala if (aflag != 0 && nflag != 0) 6190971623eSEdward Tomasz Napierala rule = expand_rule(rule, false); 6200971623eSEdward Tomasz Napierala else 6210971623eSEdward Tomasz Napierala rule = expand_rule(rule, true); 6226ec54a57SEdward Tomasz Napierala 6236ec54a57SEdward Tomasz Napierala if (rule == NULL) { 6240971623eSEdward Tomasz Napierala cumulated_error++; 6250971623eSEdward Tomasz Napierala continue; 6266ec54a57SEdward Tomasz Napierala } 6276ec54a57SEdward Tomasz Napierala 6286ec54a57SEdward Tomasz Napierala if (aflag) { 6290971623eSEdward Tomasz Napierala cumulated_error += add_rule(rule); 6300971623eSEdward Tomasz Napierala } else if (lflag) { 6310971623eSEdward Tomasz Napierala cumulated_error += show_limits(rule, hflag, nflag); 6320971623eSEdward Tomasz Napierala } else if (rflag) { 6330971623eSEdward Tomasz Napierala cumulated_error += remove_rule(rule); 6340971623eSEdward Tomasz Napierala } else if (uflag) { 6350971623eSEdward Tomasz Napierala cumulated_error += show_usage(rule, hflag); 6360971623eSEdward Tomasz Napierala } else { 6370971623eSEdward Tomasz Napierala cumulated_error += show_rules(rule, hflag, nflag); 6386ec54a57SEdward Tomasz Napierala } 6396ec54a57SEdward Tomasz Napierala 6400971623eSEdward Tomasz Napierala free(rule); 6416ec54a57SEdward Tomasz Napierala } 6426ec54a57SEdward Tomasz Napierala 6430971623eSEdward Tomasz Napierala return (cumulated_error); 6446ec54a57SEdward Tomasz Napierala } 645