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; 114*5e7a2555SEdward Tomasz Napierala char *copy, *expanded, *tofree; 115f4e361a7SEdward Tomasz Napierala int ret; 1166ec54a57SEdward Tomasz Napierala 117*5e7a2555SEdward 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) { 131*5e7a2555SEdward 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); 142*5e7a2555SEdward 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"); 156*5e7a2555SEdward Tomasz Napierala free(tofree); 1570971623eSEdward Tomasz Napierala return (NULL); 1580971623eSEdward Tomasz Napierala } 1596ec54a57SEdward Tomasz Napierala 160*5e7a2555SEdward 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; 284*5e7a2555SEdward Tomasz Napierala char *copy, *humanized, buf[6], *tofree; 285f4e361a7SEdward Tomasz Napierala int ret; 2866ec54a57SEdward Tomasz Napierala 287*5e7a2555SEdward 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) { 300*5e7a2555SEdward 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 324*5e7a2555SEdward 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 3906ec54a57SEdward Tomasz Napierala do { 3916ec54a57SEdward Tomasz Napierala outbuflen *= 4; 3926ec54a57SEdward Tomasz Napierala outbuf = realloc(outbuf, outbuflen); 3936ec54a57SEdward Tomasz Napierala if (outbuf == NULL) 3946ec54a57SEdward Tomasz Napierala err(1, "realloc"); 3956ec54a57SEdward Tomasz Napierala 3966ec54a57SEdward Tomasz Napierala error = rctl_get_limits(filter, strlen(filter) + 1, outbuf, 3976ec54a57SEdward Tomasz Napierala outbuflen); 398aae2a24bSEdward Tomasz Napierala if (error && errno != ERANGE) { 399aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 400aae2a24bSEdward Tomasz Napierala enosys(); 4010971623eSEdward Tomasz Napierala warn("rctl_get_limits"); 402aae2a24bSEdward Tomasz Napierala } 4036ec54a57SEdward Tomasz Napierala } while (error && errno == ERANGE); 4046ec54a57SEdward Tomasz Napierala 4056ec54a57SEdward Tomasz Napierala print_rules(outbuf, hflag, nflag); 4066ec54a57SEdward Tomasz Napierala free(outbuf); 4070971623eSEdward Tomasz Napierala 4080971623eSEdward Tomasz Napierala return (error); 4096ec54a57SEdward Tomasz Napierala } 4106ec54a57SEdward Tomasz Napierala 4110971623eSEdward Tomasz Napierala static int 4120971623eSEdward Tomasz Napierala remove_rule(const char *filter) 4136ec54a57SEdward Tomasz Napierala { 4146ec54a57SEdward Tomasz Napierala int error; 4156ec54a57SEdward Tomasz Napierala 4166ec54a57SEdward Tomasz Napierala error = rctl_remove_rule(filter, strlen(filter) + 1, NULL, 0); 417aae2a24bSEdward Tomasz Napierala if (error != 0) { 418aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 419aae2a24bSEdward Tomasz Napierala enosys(); 4200971623eSEdward Tomasz Napierala warn("rctl_remove_rule"); 421aae2a24bSEdward Tomasz Napierala } 4220971623eSEdward Tomasz Napierala 4230971623eSEdward Tomasz Napierala return (error); 4246ec54a57SEdward Tomasz Napierala } 4256ec54a57SEdward Tomasz Napierala 4266ec54a57SEdward Tomasz Napierala static char * 4276ec54a57SEdward Tomasz Napierala humanize_usage_amount(char *usage) 4286ec54a57SEdward Tomasz Napierala { 4296ec54a57SEdward Tomasz Napierala int64_t num; 4306ec54a57SEdward Tomasz Napierala const char *resource, *amount; 431*5e7a2555SEdward Tomasz Napierala char *copy, *humanized, buf[6], *tofree; 432f4e361a7SEdward Tomasz Napierala int ret; 4336ec54a57SEdward Tomasz Napierala 434*5e7a2555SEdward Tomasz Napierala tofree = copy = strdup(usage); 4356ec54a57SEdward Tomasz Napierala if (copy == NULL) 4366ec54a57SEdward Tomasz Napierala err(1, "strdup"); 4376ec54a57SEdward Tomasz Napierala 4386ec54a57SEdward Tomasz Napierala resource = strsep(©, "="); 4396ec54a57SEdward Tomasz Napierala amount = copy; 4406ec54a57SEdward Tomasz Napierala 4416ec54a57SEdward Tomasz Napierala assert(resource != NULL); 4426ec54a57SEdward Tomasz Napierala assert(amount != NULL); 4436ec54a57SEdward Tomasz Napierala 4446ec54a57SEdward Tomasz Napierala if (str2int64(amount, &num) != 0 || 4456ec54a57SEdward Tomasz Napierala humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE, 4466ec54a57SEdward Tomasz Napierala HN_DECIMAL | HN_NOSPACE) == -1) { 447*5e7a2555SEdward Tomasz Napierala free(tofree); 4486ec54a57SEdward Tomasz Napierala return (usage); 4496ec54a57SEdward Tomasz Napierala } 4506ec54a57SEdward Tomasz Napierala 451f4e361a7SEdward Tomasz Napierala ret = asprintf(&humanized, "%s=%s", resource, buf); 452f4e361a7SEdward Tomasz Napierala if (ret <= 0) 4536ec54a57SEdward Tomasz Napierala err(1, "asprintf"); 4546ec54a57SEdward Tomasz Napierala 455*5e7a2555SEdward Tomasz Napierala free(tofree); 4566ec54a57SEdward Tomasz Napierala return (humanized); 4576ec54a57SEdward Tomasz Napierala } 4586ec54a57SEdward Tomasz Napierala 4596ec54a57SEdward Tomasz Napierala /* 4606ec54a57SEdward Tomasz Napierala * Query the kernel about a resource usage and print it out. 4616ec54a57SEdward Tomasz Napierala */ 4620971623eSEdward Tomasz Napierala static int 4630971623eSEdward Tomasz Napierala show_usage(const char *filter, int hflag) 4646ec54a57SEdward Tomasz Napierala { 4656ec54a57SEdward Tomasz Napierala int error; 466*5e7a2555SEdward Tomasz Napierala char *copy, *outbuf = NULL, *tmp; 4676ec54a57SEdward Tomasz Napierala size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4; 4686ec54a57SEdward Tomasz Napierala 4696ec54a57SEdward Tomasz Napierala do { 4706ec54a57SEdward Tomasz Napierala outbuflen *= 4; 4716ec54a57SEdward Tomasz Napierala outbuf = realloc(outbuf, outbuflen); 4726ec54a57SEdward Tomasz Napierala if (outbuf == NULL) 4736ec54a57SEdward Tomasz Napierala err(1, "realloc"); 4746ec54a57SEdward Tomasz Napierala 4756ec54a57SEdward Tomasz Napierala error = rctl_get_racct(filter, strlen(filter) + 1, outbuf, 4766ec54a57SEdward Tomasz Napierala outbuflen); 477aae2a24bSEdward Tomasz Napierala if (error && errno != ERANGE) { 478aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 479aae2a24bSEdward Tomasz Napierala enosys(); 4800971623eSEdward Tomasz Napierala warn("rctl_get_racct"); 481aae2a24bSEdward Tomasz Napierala } 4826ec54a57SEdward Tomasz Napierala } while (error && errno == ERANGE); 4836ec54a57SEdward Tomasz Napierala 484*5e7a2555SEdward Tomasz Napierala copy = outbuf; 485*5e7a2555SEdward Tomasz Napierala while ((tmp = strsep(©, ",")) != NULL) { 4866ec54a57SEdward Tomasz Napierala if (tmp[0] == '\0') 4876ec54a57SEdward Tomasz Napierala break; /* XXX */ 4886ec54a57SEdward Tomasz Napierala 4896ec54a57SEdward Tomasz Napierala if (hflag) 4906ec54a57SEdward Tomasz Napierala tmp = humanize_usage_amount(tmp); 4916ec54a57SEdward Tomasz Napierala 4926ec54a57SEdward Tomasz Napierala printf("%s\n", tmp); 4936ec54a57SEdward Tomasz Napierala } 4946ec54a57SEdward Tomasz Napierala 4956ec54a57SEdward Tomasz Napierala free(outbuf); 4960971623eSEdward Tomasz Napierala 4970971623eSEdward Tomasz Napierala return (error); 4986ec54a57SEdward Tomasz Napierala } 4996ec54a57SEdward Tomasz Napierala 5006ec54a57SEdward Tomasz Napierala /* 5016ec54a57SEdward Tomasz Napierala * Query the kernel about resource limit rules and print them out. 5026ec54a57SEdward Tomasz Napierala */ 5030971623eSEdward Tomasz Napierala static int 5040971623eSEdward Tomasz Napierala show_rules(const char *filter, int hflag, int nflag) 5056ec54a57SEdward Tomasz Napierala { 5066ec54a57SEdward Tomasz Napierala int error; 5076ec54a57SEdward Tomasz Napierala char *outbuf = NULL; 5086ec54a57SEdward Tomasz Napierala size_t filterlen, outbuflen = RCTL_DEFAULT_BUFSIZE / 4; 5096ec54a57SEdward Tomasz Napierala 5106ec54a57SEdward Tomasz Napierala if (filter != NULL) 5116ec54a57SEdward Tomasz Napierala filterlen = strlen(filter) + 1; 5126ec54a57SEdward Tomasz Napierala else 5136ec54a57SEdward Tomasz Napierala filterlen = 0; 5146ec54a57SEdward Tomasz Napierala 5156ec54a57SEdward Tomasz Napierala do { 5166ec54a57SEdward Tomasz Napierala outbuflen *= 4; 5176ec54a57SEdward Tomasz Napierala outbuf = realloc(outbuf, outbuflen); 5186ec54a57SEdward Tomasz Napierala if (outbuf == NULL) 5196ec54a57SEdward Tomasz Napierala err(1, "realloc"); 5206ec54a57SEdward Tomasz Napierala 5216ec54a57SEdward Tomasz Napierala error = rctl_get_rules(filter, filterlen, outbuf, outbuflen); 522aae2a24bSEdward Tomasz Napierala if (error && errno != ERANGE) { 523aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 524aae2a24bSEdward Tomasz Napierala enosys(); 5250971623eSEdward Tomasz Napierala warn("rctl_get_rules"); 526aae2a24bSEdward Tomasz Napierala } 5276ec54a57SEdward Tomasz Napierala } while (error && errno == ERANGE); 5286ec54a57SEdward Tomasz Napierala 5296ec54a57SEdward Tomasz Napierala print_rules(outbuf, hflag, nflag); 5306ec54a57SEdward Tomasz Napierala free(outbuf); 5310971623eSEdward Tomasz Napierala 5320971623eSEdward Tomasz Napierala return (error); 5336ec54a57SEdward Tomasz Napierala } 5346ec54a57SEdward Tomasz Napierala 5356ec54a57SEdward Tomasz Napierala static void 5366ec54a57SEdward Tomasz Napierala usage(void) 5376ec54a57SEdward Tomasz Napierala { 5386ec54a57SEdward Tomasz Napierala 5396ec54a57SEdward Tomasz Napierala fprintf(stderr, "usage: rctl [ -h ] [-a rule | -l filter | -r filter " 5406ec54a57SEdward Tomasz Napierala "| -u filter | filter]\n"); 5416ec54a57SEdward Tomasz Napierala exit(1); 5426ec54a57SEdward Tomasz Napierala } 5436ec54a57SEdward Tomasz Napierala 5446ec54a57SEdward Tomasz Napierala int 5456ec54a57SEdward Tomasz Napierala main(int argc __unused, char **argv __unused) 5466ec54a57SEdward Tomasz Napierala { 5476ec54a57SEdward Tomasz Napierala int ch, aflag = 0, hflag = 0, nflag = 0, lflag = 0, rflag = 0, 5486ec54a57SEdward Tomasz Napierala uflag = 0; 5496ec54a57SEdward Tomasz Napierala char *rule = NULL; 5500971623eSEdward Tomasz Napierala int i, cumulated_error; 5516ec54a57SEdward Tomasz Napierala 5520971623eSEdward Tomasz Napierala while ((ch = getopt(argc, argv, "ahlnru")) != -1) { 5536ec54a57SEdward Tomasz Napierala switch (ch) { 5546ec54a57SEdward Tomasz Napierala case 'a': 5556ec54a57SEdward Tomasz Napierala aflag = 1; 5566ec54a57SEdward Tomasz Napierala break; 5576ec54a57SEdward Tomasz Napierala case 'h': 5586ec54a57SEdward Tomasz Napierala hflag = 1; 5596ec54a57SEdward Tomasz Napierala break; 5606ec54a57SEdward Tomasz Napierala case 'l': 5616ec54a57SEdward Tomasz Napierala lflag = 1; 5626ec54a57SEdward Tomasz Napierala break; 5636ec54a57SEdward Tomasz Napierala case 'n': 5646ec54a57SEdward Tomasz Napierala nflag = 1; 5656ec54a57SEdward Tomasz Napierala break; 5666ec54a57SEdward Tomasz Napierala case 'r': 5676ec54a57SEdward Tomasz Napierala rflag = 1; 5686ec54a57SEdward Tomasz Napierala break; 5696ec54a57SEdward Tomasz Napierala case 'u': 5706ec54a57SEdward Tomasz Napierala uflag = 1; 5716ec54a57SEdward Tomasz Napierala break; 5726ec54a57SEdward Tomasz Napierala 5736ec54a57SEdward Tomasz Napierala case '?': 5746ec54a57SEdward Tomasz Napierala default: 5756ec54a57SEdward Tomasz Napierala usage(); 5766ec54a57SEdward Tomasz Napierala } 5776ec54a57SEdward Tomasz Napierala } 5786ec54a57SEdward Tomasz Napierala 5796ec54a57SEdward Tomasz Napierala argc -= optind; 5806ec54a57SEdward Tomasz Napierala argv += optind; 5816ec54a57SEdward Tomasz Napierala 5820971623eSEdward Tomasz Napierala if (aflag + lflag + rflag + uflag > 1) 5830971623eSEdward Tomasz Napierala errx(1, "at most one of -a, -l, -r, or -u may be specified"); 5840971623eSEdward Tomasz Napierala 5850971623eSEdward Tomasz Napierala if (argc == 0) { 5860971623eSEdward Tomasz Napierala if (aflag + lflag + rflag + uflag == 0) { 5870971623eSEdward Tomasz Napierala rule = strdup("::"); 5880971623eSEdward Tomasz Napierala show_rules(rule, hflag, nflag); 5890971623eSEdward Tomasz Napierala 5900971623eSEdward Tomasz Napierala return (0); 5910971623eSEdward Tomasz Napierala } 5920971623eSEdward Tomasz Napierala 5936ec54a57SEdward Tomasz Napierala usage(); 5940971623eSEdward Tomasz Napierala } 5950971623eSEdward Tomasz Napierala 5960971623eSEdward Tomasz Napierala cumulated_error = 0; 5970971623eSEdward Tomasz Napierala 5980971623eSEdward Tomasz Napierala for (i = 0; i < argc; i++) { 5990971623eSEdward Tomasz Napierala rule = argv[i]; 6000971623eSEdward Tomasz Napierala 6010971623eSEdward Tomasz Napierala /* 6020971623eSEdward Tomasz Napierala * Skip resolving if passed -n _and_ -a. Ignore -n otherwise, 6030971623eSEdward Tomasz Napierala * so we can still do "rctl -n u:root" and see the rules without 6040971623eSEdward Tomasz Napierala * resolving the UID. 6050971623eSEdward Tomasz Napierala */ 6060971623eSEdward Tomasz Napierala if (aflag != 0 && nflag != 0) 6070971623eSEdward Tomasz Napierala rule = expand_rule(rule, false); 6080971623eSEdward Tomasz Napierala else 6090971623eSEdward Tomasz Napierala rule = expand_rule(rule, true); 6106ec54a57SEdward Tomasz Napierala 6116ec54a57SEdward Tomasz Napierala if (rule == NULL) { 6120971623eSEdward Tomasz Napierala cumulated_error++; 6130971623eSEdward Tomasz Napierala continue; 6146ec54a57SEdward Tomasz Napierala } 6156ec54a57SEdward Tomasz Napierala 6166ec54a57SEdward Tomasz Napierala if (aflag) { 6170971623eSEdward Tomasz Napierala cumulated_error += add_rule(rule); 6180971623eSEdward Tomasz Napierala } else if (lflag) { 6190971623eSEdward Tomasz Napierala cumulated_error += show_limits(rule, hflag, nflag); 6200971623eSEdward Tomasz Napierala } else if (rflag) { 6210971623eSEdward Tomasz Napierala cumulated_error += remove_rule(rule); 6220971623eSEdward Tomasz Napierala } else if (uflag) { 6230971623eSEdward Tomasz Napierala cumulated_error += show_usage(rule, hflag); 6240971623eSEdward Tomasz Napierala } else { 6250971623eSEdward Tomasz Napierala cumulated_error += show_rules(rule, hflag, nflag); 6266ec54a57SEdward Tomasz Napierala } 6276ec54a57SEdward Tomasz Napierala 6280971623eSEdward Tomasz Napierala free(rule); 6296ec54a57SEdward Tomasz Napierala } 6306ec54a57SEdward Tomasz Napierala 6310971623eSEdward Tomasz Napierala return (cumulated_error); 6326ec54a57SEdward Tomasz Napierala } 633