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 555c859bfbSEdward Tomasz Napierala parse_user(const char *s, id_t *uidp, const char *unexpanded_rule) 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])) { 67*c281fdb1SEnji Cooper warnx("malformed rule '%s': unknown user '%s'", 685c859bfbSEdward Tomasz Napierala unexpanded_rule, s); 690971623eSEdward Tomasz Napierala return (1); 700971623eSEdward Tomasz Napierala } 710971623eSEdward Tomasz Napierala 720971623eSEdward Tomasz Napierala *uidp = strtod(s, &end); 730971623eSEdward Tomasz Napierala if ((size_t)(end - s) != strlen(s)) { 745c859bfbSEdward Tomasz Napierala warnx("malformed rule '%s': trailing characters " 755c859bfbSEdward Tomasz Napierala "after numerical id", unexpanded_rule); 760971623eSEdward Tomasz Napierala return (1); 770971623eSEdward Tomasz Napierala } 780971623eSEdward Tomasz Napierala 790971623eSEdward Tomasz Napierala return (0); 800971623eSEdward Tomasz Napierala } 810971623eSEdward Tomasz Napierala 820971623eSEdward Tomasz Napierala static int 835c859bfbSEdward Tomasz Napierala parse_group(const char *s, id_t *gidp, const char *unexpanded_rule) 846ec54a57SEdward Tomasz Napierala { 856ec54a57SEdward Tomasz Napierala char *end; 866ec54a57SEdward Tomasz Napierala struct group *grp; 876ec54a57SEdward Tomasz Napierala 886ec54a57SEdward Tomasz Napierala grp = getgrnam(s); 890971623eSEdward Tomasz Napierala if (grp != NULL) { 900971623eSEdward Tomasz Napierala *gidp = grp->gr_gid; 910971623eSEdward Tomasz Napierala return (0); 920971623eSEdward Tomasz Napierala } 936ec54a57SEdward Tomasz Napierala 940971623eSEdward Tomasz Napierala if (!isnumber(s[0])) { 95*c281fdb1SEnji Cooper warnx("malformed rule '%s': unknown group '%s'", 965c859bfbSEdward Tomasz Napierala unexpanded_rule, s); 970971623eSEdward Tomasz Napierala return (1); 980971623eSEdward Tomasz Napierala } 996ec54a57SEdward Tomasz Napierala 1000971623eSEdward Tomasz Napierala *gidp = strtod(s, &end); 1010971623eSEdward Tomasz Napierala if ((size_t)(end - s) != strlen(s)) { 1025c859bfbSEdward Tomasz Napierala warnx("malformed rule '%s': trailing characters " 1035c859bfbSEdward Tomasz Napierala "after numerical id", unexpanded_rule); 1040971623eSEdward Tomasz Napierala return (1); 1050971623eSEdward Tomasz Napierala } 1066ec54a57SEdward Tomasz Napierala 1070971623eSEdward Tomasz Napierala return (0); 1086ec54a57SEdward Tomasz Napierala } 1096ec54a57SEdward Tomasz Napierala 1106ec54a57SEdward Tomasz Napierala /* 1110971623eSEdward Tomasz Napierala * Replace human-readable number with its expanded form. 1126ec54a57SEdward Tomasz Napierala */ 1136ec54a57SEdward Tomasz Napierala static char * 114508744b2SEdward Tomasz Napierala expand_amount(const char *rule, const char *unexpanded_rule) 1156ec54a57SEdward Tomasz Napierala { 1166ec54a57SEdward Tomasz Napierala uint64_t num; 1176ec54a57SEdward Tomasz Napierala const char *subject, *subject_id, *resource, *action, *amount, *per; 118508744b2SEdward Tomasz Napierala char *copy, *expanded, *tofree; 119f4e361a7SEdward Tomasz Napierala int ret; 1206ec54a57SEdward Tomasz Napierala 121508744b2SEdward Tomasz Napierala tofree = copy = strdup(rule); 122508744b2SEdward Tomasz Napierala if (copy == NULL) { 123508744b2SEdward Tomasz Napierala warn("strdup"); 124508744b2SEdward Tomasz Napierala return (NULL); 125508744b2SEdward Tomasz Napierala } 1266ec54a57SEdward Tomasz Napierala 127508744b2SEdward Tomasz Napierala subject = strsep(©, ":"); 128508744b2SEdward Tomasz Napierala subject_id = strsep(©, ":"); 129508744b2SEdward Tomasz Napierala resource = strsep(©, ":"); 130508744b2SEdward Tomasz Napierala action = strsep(©, "=/"); 131508744b2SEdward Tomasz Napierala amount = strsep(©, "/"); 132508744b2SEdward Tomasz Napierala per = copy; 133508744b2SEdward Tomasz Napierala 134508744b2SEdward Tomasz Napierala if (amount == NULL || strlen(amount) == 0) { 135508744b2SEdward Tomasz Napierala /* 136508744b2SEdward Tomasz Napierala * The "copy" has already been tinkered with by strsep(). 137508744b2SEdward Tomasz Napierala */ 138508744b2SEdward Tomasz Napierala free(tofree); 139508744b2SEdward Tomasz Napierala copy = strdup(rule); 140508744b2SEdward Tomasz Napierala if (copy == NULL) { 141508744b2SEdward Tomasz Napierala warn("strdup"); 142508744b2SEdward Tomasz Napierala return (NULL); 143508744b2SEdward Tomasz Napierala } 144508744b2SEdward Tomasz Napierala return (copy); 145508744b2SEdward Tomasz Napierala } 1466ec54a57SEdward Tomasz Napierala 1476ec54a57SEdward Tomasz Napierala assert(subject != NULL); 1486ec54a57SEdward Tomasz Napierala assert(subject_id != NULL); 1496ec54a57SEdward Tomasz Napierala assert(resource != NULL); 1506ec54a57SEdward Tomasz Napierala assert(action != NULL); 1516ec54a57SEdward Tomasz Napierala 1520971623eSEdward Tomasz Napierala if (expand_number(amount, &num)) { 1535c859bfbSEdward Tomasz Napierala warnx("malformed rule '%s': invalid numeric value '%s'", 1545c859bfbSEdward Tomasz Napierala unexpanded_rule, amount); 155508744b2SEdward Tomasz Napierala free(tofree); 1560971623eSEdward Tomasz Napierala return (NULL); 1570971623eSEdward Tomasz Napierala } 1586ec54a57SEdward Tomasz Napierala 159f4e361a7SEdward Tomasz Napierala if (per == NULL) { 160f4e361a7SEdward Tomasz Napierala ret = asprintf(&expanded, "%s:%s:%s:%s=%ju", 161f4e361a7SEdward Tomasz Napierala subject, subject_id, resource, action, (uintmax_t)num); 162f4e361a7SEdward Tomasz Napierala } else { 163f4e361a7SEdward Tomasz Napierala ret = asprintf(&expanded, "%s:%s:%s:%s=%ju/%s", 164f4e361a7SEdward Tomasz Napierala subject, subject_id, resource, action, (uintmax_t)num, per); 165f4e361a7SEdward Tomasz Napierala } 1666ec54a57SEdward Tomasz Napierala 167f4e361a7SEdward Tomasz Napierala if (ret <= 0) { 1680971623eSEdward Tomasz Napierala warn("asprintf"); 169508744b2SEdward Tomasz Napierala free(tofree); 1700971623eSEdward Tomasz Napierala return (NULL); 1710971623eSEdward Tomasz Napierala } 1726ec54a57SEdward Tomasz Napierala 173508744b2SEdward Tomasz Napierala free(tofree); 174508744b2SEdward Tomasz Napierala 1756ec54a57SEdward Tomasz Napierala return (expanded); 1766ec54a57SEdward Tomasz Napierala } 1776ec54a57SEdward Tomasz Napierala 1780971623eSEdward Tomasz Napierala static char * 1795c859bfbSEdward Tomasz Napierala expand_rule(const char *rule, bool resolve_ids) 1800971623eSEdward Tomasz Napierala { 1810971623eSEdward Tomasz Napierala id_t id; 1820971623eSEdward Tomasz Napierala const char *subject, *textid, *rest; 1835c859bfbSEdward Tomasz Napierala char *copy, *expanded, *resolved, *tofree; 184f4e361a7SEdward Tomasz Napierala int error, ret; 1850971623eSEdward Tomasz Napierala 1865c859bfbSEdward Tomasz Napierala tofree = copy = strdup(rule); 1875c859bfbSEdward Tomasz Napierala if (copy == NULL) { 1885c859bfbSEdward Tomasz Napierala warn("strdup"); 1890971623eSEdward Tomasz Napierala return (NULL); 1900971623eSEdward Tomasz Napierala } 1915c859bfbSEdward Tomasz Napierala 1925c859bfbSEdward Tomasz Napierala subject = strsep(©, ":"); 1935c859bfbSEdward Tomasz Napierala textid = strsep(©, ":"); 1945c859bfbSEdward Tomasz Napierala if (textid == NULL) { 1955c859bfbSEdward Tomasz Napierala warnx("malformed rule '%s': missing subject", rule); 1965c859bfbSEdward Tomasz Napierala return (NULL); 1975c859bfbSEdward Tomasz Napierala } 1985c859bfbSEdward Tomasz Napierala if (copy != NULL) 1995c859bfbSEdward Tomasz Napierala rest = copy; 2000971623eSEdward Tomasz Napierala else 2010971623eSEdward Tomasz Napierala rest = ""; 2020971623eSEdward Tomasz Napierala 2030971623eSEdward Tomasz Napierala if (strcasecmp(subject, "u") == 0) 2040971623eSEdward Tomasz Napierala subject = "user"; 2050971623eSEdward Tomasz Napierala else if (strcasecmp(subject, "g") == 0) 2060971623eSEdward Tomasz Napierala subject = "group"; 2070971623eSEdward Tomasz Napierala else if (strcasecmp(subject, "p") == 0) 2080971623eSEdward Tomasz Napierala subject = "process"; 2090971623eSEdward Tomasz Napierala else if (strcasecmp(subject, "l") == 0 || 2100971623eSEdward Tomasz Napierala strcasecmp(subject, "c") == 0 || 2110971623eSEdward Tomasz Napierala strcasecmp(subject, "class") == 0) 2120971623eSEdward Tomasz Napierala subject = "loginclass"; 2130971623eSEdward Tomasz Napierala else if (strcasecmp(subject, "j") == 0) 2140971623eSEdward Tomasz Napierala subject = "jail"; 2150971623eSEdward Tomasz Napierala 2160971623eSEdward Tomasz Napierala if (resolve_ids && 2170971623eSEdward Tomasz Napierala strcasecmp(subject, "user") == 0 && strlen(textid) > 0) { 2185c859bfbSEdward Tomasz Napierala error = parse_user(textid, &id, rule); 2195c859bfbSEdward Tomasz Napierala if (error != 0) { 2205c859bfbSEdward Tomasz Napierala free(tofree); 2210971623eSEdward Tomasz Napierala return (NULL); 2225c859bfbSEdward Tomasz Napierala } 223f4e361a7SEdward Tomasz Napierala ret = asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest); 2240971623eSEdward Tomasz Napierala } else if (resolve_ids && 2250971623eSEdward Tomasz Napierala strcasecmp(subject, "group") == 0 && strlen(textid) > 0) { 2265c859bfbSEdward Tomasz Napierala error = parse_group(textid, &id, rule); 2275c859bfbSEdward Tomasz Napierala if (error != 0) { 2285c859bfbSEdward Tomasz Napierala free(tofree); 2290971623eSEdward Tomasz Napierala return (NULL); 2305c859bfbSEdward Tomasz Napierala } 231f4e361a7SEdward Tomasz Napierala ret = asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest); 2320971623eSEdward Tomasz Napierala } else { 233f4e361a7SEdward Tomasz Napierala ret = asprintf(&resolved, "%s:%s:%s", subject, textid, rest); 2340971623eSEdward Tomasz Napierala } 2350971623eSEdward Tomasz Napierala 236f4e361a7SEdward Tomasz Napierala if (ret <= 0) { 2370971623eSEdward Tomasz Napierala warn("asprintf"); 2385c859bfbSEdward Tomasz Napierala free(tofree); 2390971623eSEdward Tomasz Napierala return (NULL); 2400971623eSEdward Tomasz Napierala } 2410971623eSEdward Tomasz Napierala 2425c859bfbSEdward Tomasz Napierala free(tofree); 2435c859bfbSEdward Tomasz Napierala 2445c859bfbSEdward Tomasz Napierala expanded = expand_amount(resolved, rule); 2455c859bfbSEdward Tomasz Napierala free(resolved); 2465c859bfbSEdward Tomasz Napierala 2475c859bfbSEdward Tomasz Napierala return (expanded); 2480971623eSEdward Tomasz Napierala } 2490971623eSEdward Tomasz Napierala 2506ec54a57SEdward Tomasz Napierala static char * 2516ec54a57SEdward Tomasz Napierala humanize_ids(char *rule) 2526ec54a57SEdward Tomasz Napierala { 2536ec54a57SEdward Tomasz Napierala id_t id; 2546ec54a57SEdward Tomasz Napierala struct passwd *pwd; 2556ec54a57SEdward Tomasz Napierala struct group *grp; 2566ec54a57SEdward Tomasz Napierala const char *subject, *textid, *rest; 257478f7a72SEdward Tomasz Napierala char *end, *humanized; 258f4e361a7SEdward Tomasz Napierala int ret; 2596ec54a57SEdward Tomasz Napierala 2606ec54a57SEdward Tomasz Napierala subject = strsep(&rule, ":"); 2616ec54a57SEdward Tomasz Napierala textid = strsep(&rule, ":"); 2626ec54a57SEdward Tomasz Napierala if (textid == NULL) 2636ec54a57SEdward Tomasz Napierala errx(1, "rule passed from the kernel didn't contain subject"); 2646ec54a57SEdward Tomasz Napierala if (rule != NULL) 2656ec54a57SEdward Tomasz Napierala rest = rule; 2666ec54a57SEdward Tomasz Napierala else 2676ec54a57SEdward Tomasz Napierala rest = ""; 2686ec54a57SEdward Tomasz Napierala 2696ec54a57SEdward Tomasz Napierala /* Replace numerical user and group ids with names. */ 2706ec54a57SEdward Tomasz Napierala if (strcasecmp(subject, "user") == 0) { 271478f7a72SEdward Tomasz Napierala id = strtod(textid, &end); 272478f7a72SEdward Tomasz Napierala if ((size_t)(end - textid) != strlen(textid)) 273478f7a72SEdward Tomasz Napierala errx(1, "malformed uid '%s'", textid); 2746ec54a57SEdward Tomasz Napierala pwd = getpwuid(id); 2756ec54a57SEdward Tomasz Napierala if (pwd != NULL) 2766ec54a57SEdward Tomasz Napierala textid = pwd->pw_name; 2776ec54a57SEdward Tomasz Napierala } else if (strcasecmp(subject, "group") == 0) { 278478f7a72SEdward Tomasz Napierala id = strtod(textid, &end); 279478f7a72SEdward Tomasz Napierala if ((size_t)(end - textid) != strlen(textid)) 280478f7a72SEdward Tomasz Napierala errx(1, "malformed gid '%s'", textid); 2816ec54a57SEdward Tomasz Napierala grp = getgrgid(id); 2826ec54a57SEdward Tomasz Napierala if (grp != NULL) 2836ec54a57SEdward Tomasz Napierala textid = grp->gr_name; 2846ec54a57SEdward Tomasz Napierala } 2856ec54a57SEdward Tomasz Napierala 286f4e361a7SEdward Tomasz Napierala ret = asprintf(&humanized, "%s:%s:%s", subject, textid, rest); 287f4e361a7SEdward Tomasz Napierala if (ret <= 0) 2886ec54a57SEdward Tomasz Napierala err(1, "asprintf"); 2896ec54a57SEdward Tomasz Napierala 2906ec54a57SEdward Tomasz Napierala return (humanized); 2916ec54a57SEdward Tomasz Napierala } 2926ec54a57SEdward Tomasz Napierala 2936ec54a57SEdward Tomasz Napierala static int 2946ec54a57SEdward Tomasz Napierala str2int64(const char *str, int64_t *value) 2956ec54a57SEdward Tomasz Napierala { 2966ec54a57SEdward Tomasz Napierala char *end; 2976ec54a57SEdward Tomasz Napierala 2986ec54a57SEdward Tomasz Napierala if (str == NULL) 2996ec54a57SEdward Tomasz Napierala return (EINVAL); 3006ec54a57SEdward Tomasz Napierala 3016ec54a57SEdward Tomasz Napierala *value = strtoul(str, &end, 10); 3026ec54a57SEdward Tomasz Napierala if ((size_t)(end - str) != strlen(str)) 3036ec54a57SEdward Tomasz Napierala return (EINVAL); 3046ec54a57SEdward Tomasz Napierala 3056ec54a57SEdward Tomasz Napierala return (0); 3066ec54a57SEdward Tomasz Napierala } 3076ec54a57SEdward Tomasz Napierala 3086ec54a57SEdward Tomasz Napierala static char * 3096ec54a57SEdward Tomasz Napierala humanize_amount(char *rule) 3106ec54a57SEdward Tomasz Napierala { 3116ec54a57SEdward Tomasz Napierala int64_t num; 3126ec54a57SEdward Tomasz Napierala const char *subject, *subject_id, *resource, *action, *amount, *per; 3135e7a2555SEdward Tomasz Napierala char *copy, *humanized, buf[6], *tofree; 314f4e361a7SEdward Tomasz Napierala int ret; 3156ec54a57SEdward Tomasz Napierala 3165e7a2555SEdward Tomasz Napierala tofree = copy = strdup(rule); 3176ec54a57SEdward Tomasz Napierala if (copy == NULL) 3186ec54a57SEdward Tomasz Napierala err(1, "strdup"); 3196ec54a57SEdward Tomasz Napierala 3206ec54a57SEdward Tomasz Napierala subject = strsep(©, ":"); 3216ec54a57SEdward Tomasz Napierala subject_id = strsep(©, ":"); 3226ec54a57SEdward Tomasz Napierala resource = strsep(©, ":"); 3236ec54a57SEdward Tomasz Napierala action = strsep(©, "=/"); 3246ec54a57SEdward Tomasz Napierala amount = strsep(©, "/"); 3256ec54a57SEdward Tomasz Napierala per = copy; 3266ec54a57SEdward Tomasz Napierala 3276ec54a57SEdward Tomasz Napierala if (amount == NULL || strlen(amount) == 0 || 3286ec54a57SEdward Tomasz Napierala str2int64(amount, &num) != 0) { 3295e7a2555SEdward Tomasz Napierala free(tofree); 3306ec54a57SEdward Tomasz Napierala return (rule); 3316ec54a57SEdward Tomasz Napierala } 3326ec54a57SEdward Tomasz Napierala 3336ec54a57SEdward Tomasz Napierala assert(subject != NULL); 3346ec54a57SEdward Tomasz Napierala assert(subject_id != NULL); 3356ec54a57SEdward Tomasz Napierala assert(resource != NULL); 3366ec54a57SEdward Tomasz Napierala assert(action != NULL); 3376ec54a57SEdward Tomasz Napierala 3386ec54a57SEdward Tomasz Napierala if (humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE, 3396ec54a57SEdward Tomasz Napierala HN_DECIMAL | HN_NOSPACE) == -1) 3406ec54a57SEdward Tomasz Napierala err(1, "humanize_number"); 3416ec54a57SEdward Tomasz Napierala 342f4e361a7SEdward Tomasz Napierala if (per == NULL) { 343f4e361a7SEdward Tomasz Napierala ret = asprintf(&humanized, "%s:%s:%s:%s=%s", 344f4e361a7SEdward Tomasz Napierala subject, subject_id, resource, action, buf); 345f4e361a7SEdward Tomasz Napierala } else { 346f4e361a7SEdward Tomasz Napierala ret = asprintf(&humanized, "%s:%s:%s:%s=%s/%s", 347f4e361a7SEdward Tomasz Napierala subject, subject_id, resource, action, buf, per); 348f4e361a7SEdward Tomasz Napierala } 3496ec54a57SEdward Tomasz Napierala 350f4e361a7SEdward Tomasz Napierala if (ret <= 0) 3516ec54a57SEdward Tomasz Napierala err(1, "asprintf"); 3526ec54a57SEdward Tomasz Napierala 3535e7a2555SEdward Tomasz Napierala free(tofree); 3546ec54a57SEdward Tomasz Napierala return (humanized); 3556ec54a57SEdward Tomasz Napierala } 3566ec54a57SEdward Tomasz Napierala 3576ec54a57SEdward Tomasz Napierala /* 3586ec54a57SEdward Tomasz Napierala * Print rules, one per line. 3596ec54a57SEdward Tomasz Napierala */ 3606ec54a57SEdward Tomasz Napierala static void 3616ec54a57SEdward Tomasz Napierala print_rules(char *rules, int hflag, int nflag) 3626ec54a57SEdward Tomasz Napierala { 3636ec54a57SEdward Tomasz Napierala char *rule; 3646ec54a57SEdward Tomasz Napierala 3656ec54a57SEdward Tomasz Napierala while ((rule = strsep(&rules, ",")) != NULL) { 3666ec54a57SEdward Tomasz Napierala if (rule[0] == '\0') 3676ec54a57SEdward Tomasz Napierala break; /* XXX */ 3686ec54a57SEdward Tomasz Napierala if (nflag == 0) 3696ec54a57SEdward Tomasz Napierala rule = humanize_ids(rule); 3706ec54a57SEdward Tomasz Napierala if (hflag) 3716ec54a57SEdward Tomasz Napierala rule = humanize_amount(rule); 3726ec54a57SEdward Tomasz Napierala printf("%s\n", rule); 3736ec54a57SEdward Tomasz Napierala } 3746ec54a57SEdward Tomasz Napierala } 3756ec54a57SEdward Tomasz Napierala 3766ec54a57SEdward Tomasz Napierala static void 377aae2a24bSEdward Tomasz Napierala enosys(void) 378aae2a24bSEdward Tomasz Napierala { 379aae2a24bSEdward Tomasz Napierala int error, racct_enable; 380aae2a24bSEdward Tomasz Napierala size_t racct_enable_len; 381aae2a24bSEdward Tomasz Napierala 382aae2a24bSEdward Tomasz Napierala racct_enable_len = sizeof(racct_enable); 383aae2a24bSEdward Tomasz Napierala error = sysctlbyname("kern.racct.enable", 384aae2a24bSEdward Tomasz Napierala &racct_enable, &racct_enable_len, NULL, 0); 385aae2a24bSEdward Tomasz Napierala 386aae2a24bSEdward Tomasz Napierala if (error != 0) { 387aae2a24bSEdward Tomasz Napierala if (errno == ENOENT) 38882224d7dSEdward Tomasz Napierala errx(1, "RACCT/RCTL support not present in kernel; see rctl(8) for details"); 389aae2a24bSEdward Tomasz Napierala 390aae2a24bSEdward Tomasz Napierala err(1, "sysctlbyname"); 391aae2a24bSEdward Tomasz Napierala } 392aae2a24bSEdward Tomasz Napierala 393aae2a24bSEdward Tomasz Napierala if (racct_enable == 0) 394aae2a24bSEdward Tomasz Napierala errx(1, "RACCT/RCTL present, but disabled; enable using kern.racct.enable=1 tunable"); 395aae2a24bSEdward Tomasz Napierala } 396aae2a24bSEdward Tomasz Napierala 3970971623eSEdward Tomasz Napierala static int 3985c859bfbSEdward Tomasz Napierala add_rule(const char *rule, const char *unexpanded_rule) 3996ec54a57SEdward Tomasz Napierala { 4006ec54a57SEdward Tomasz Napierala int error; 4016ec54a57SEdward Tomasz Napierala 4026ec54a57SEdward Tomasz Napierala error = rctl_add_rule(rule, strlen(rule) + 1, NULL, 0); 403aae2a24bSEdward Tomasz Napierala if (error != 0) { 404aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 405aae2a24bSEdward Tomasz Napierala enosys(); 4065c859bfbSEdward Tomasz Napierala warn("failed to add rule '%s'", unexpanded_rule); 4076ec54a57SEdward Tomasz Napierala } 4086ec54a57SEdward Tomasz Napierala 4090971623eSEdward Tomasz Napierala return (error); 4100971623eSEdward Tomasz Napierala } 4110971623eSEdward Tomasz Napierala 4120971623eSEdward Tomasz Napierala static int 4135c859bfbSEdward Tomasz Napierala show_limits(const char *filter, const char *unexpanded_rule, 4145c859bfbSEdward Tomasz Napierala int hflag, int nflag) 4156ec54a57SEdward Tomasz Napierala { 4166ec54a57SEdward Tomasz Napierala int error; 4176ec54a57SEdward Tomasz Napierala char *outbuf = NULL; 4186ec54a57SEdward Tomasz Napierala size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4; 4196ec54a57SEdward Tomasz Napierala 4204cf0d895SEdward Tomasz Napierala for (;;) { 4216ec54a57SEdward Tomasz Napierala outbuflen *= 4; 4226ec54a57SEdward Tomasz Napierala outbuf = realloc(outbuf, outbuflen); 4236ec54a57SEdward Tomasz Napierala if (outbuf == NULL) 4246ec54a57SEdward Tomasz Napierala err(1, "realloc"); 4254cf0d895SEdward Tomasz Napierala error = rctl_get_limits(filter, strlen(filter) + 1, 4264cf0d895SEdward Tomasz Napierala outbuf, outbuflen); 4274cf0d895SEdward Tomasz Napierala if (error == 0) 4284cf0d895SEdward Tomasz Napierala break; 4294cf0d895SEdward Tomasz Napierala if (errno == ERANGE) 4304cf0d895SEdward Tomasz Napierala continue; 431aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 432aae2a24bSEdward Tomasz Napierala enosys(); 4335c859bfbSEdward Tomasz Napierala warn("failed to get limits for '%s'", unexpanded_rule); 4344cf0d895SEdward Tomasz Napierala free(outbuf); 4354cf0d895SEdward Tomasz Napierala 4364cf0d895SEdward Tomasz Napierala return (error); 437aae2a24bSEdward Tomasz Napierala } 4386ec54a57SEdward Tomasz Napierala 4396ec54a57SEdward Tomasz Napierala print_rules(outbuf, hflag, nflag); 4406ec54a57SEdward Tomasz Napierala free(outbuf); 4410971623eSEdward Tomasz Napierala 4420971623eSEdward Tomasz Napierala return (error); 4436ec54a57SEdward Tomasz Napierala } 4446ec54a57SEdward Tomasz Napierala 4450971623eSEdward Tomasz Napierala static int 4465c859bfbSEdward Tomasz Napierala remove_rule(const char *filter, const char *unexpanded_rule) 4476ec54a57SEdward Tomasz Napierala { 4486ec54a57SEdward Tomasz Napierala int error; 4496ec54a57SEdward Tomasz Napierala 4506ec54a57SEdward Tomasz Napierala error = rctl_remove_rule(filter, strlen(filter) + 1, NULL, 0); 451aae2a24bSEdward Tomasz Napierala if (error != 0) { 452aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 453aae2a24bSEdward Tomasz Napierala enosys(); 4545c859bfbSEdward Tomasz Napierala warn("failed to remove rule '%s'", unexpanded_rule); 455aae2a24bSEdward Tomasz Napierala } 4560971623eSEdward Tomasz Napierala 4570971623eSEdward Tomasz Napierala return (error); 4586ec54a57SEdward Tomasz Napierala } 4596ec54a57SEdward Tomasz Napierala 4606ec54a57SEdward Tomasz Napierala static char * 4616ec54a57SEdward Tomasz Napierala humanize_usage_amount(char *usage) 4626ec54a57SEdward Tomasz Napierala { 4636ec54a57SEdward Tomasz Napierala int64_t num; 4646ec54a57SEdward Tomasz Napierala const char *resource, *amount; 4655e7a2555SEdward Tomasz Napierala char *copy, *humanized, buf[6], *tofree; 466f4e361a7SEdward Tomasz Napierala int ret; 4676ec54a57SEdward Tomasz Napierala 4685e7a2555SEdward Tomasz Napierala tofree = copy = strdup(usage); 4696ec54a57SEdward Tomasz Napierala if (copy == NULL) 4706ec54a57SEdward Tomasz Napierala err(1, "strdup"); 4716ec54a57SEdward Tomasz Napierala 4726ec54a57SEdward Tomasz Napierala resource = strsep(©, "="); 4736ec54a57SEdward Tomasz Napierala amount = copy; 4746ec54a57SEdward Tomasz Napierala 4756ec54a57SEdward Tomasz Napierala assert(resource != NULL); 4766ec54a57SEdward Tomasz Napierala assert(amount != NULL); 4776ec54a57SEdward Tomasz Napierala 4786ec54a57SEdward Tomasz Napierala if (str2int64(amount, &num) != 0 || 4796ec54a57SEdward Tomasz Napierala humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE, 4806ec54a57SEdward Tomasz Napierala HN_DECIMAL | HN_NOSPACE) == -1) { 4815e7a2555SEdward Tomasz Napierala free(tofree); 4826ec54a57SEdward Tomasz Napierala return (usage); 4836ec54a57SEdward Tomasz Napierala } 4846ec54a57SEdward Tomasz Napierala 485f4e361a7SEdward Tomasz Napierala ret = asprintf(&humanized, "%s=%s", resource, buf); 486f4e361a7SEdward Tomasz Napierala if (ret <= 0) 4876ec54a57SEdward Tomasz Napierala err(1, "asprintf"); 4886ec54a57SEdward Tomasz Napierala 4895e7a2555SEdward Tomasz Napierala free(tofree); 4906ec54a57SEdward Tomasz Napierala return (humanized); 4916ec54a57SEdward Tomasz Napierala } 4926ec54a57SEdward Tomasz Napierala 4936ec54a57SEdward Tomasz Napierala /* 4946ec54a57SEdward Tomasz Napierala * Query the kernel about a resource usage and print it out. 4956ec54a57SEdward Tomasz Napierala */ 4960971623eSEdward Tomasz Napierala static int 4975c859bfbSEdward Tomasz Napierala show_usage(const char *filter, const char *unexpanded_rule, int hflag) 4986ec54a57SEdward Tomasz Napierala { 4996ec54a57SEdward Tomasz Napierala int error; 5005e7a2555SEdward Tomasz Napierala char *copy, *outbuf = NULL, *tmp; 5016ec54a57SEdward Tomasz Napierala size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4; 5026ec54a57SEdward Tomasz Napierala 5034cf0d895SEdward Tomasz Napierala for (;;) { 5046ec54a57SEdward Tomasz Napierala outbuflen *= 4; 5056ec54a57SEdward Tomasz Napierala outbuf = realloc(outbuf, outbuflen); 5066ec54a57SEdward Tomasz Napierala if (outbuf == NULL) 5076ec54a57SEdward Tomasz Napierala err(1, "realloc"); 5084cf0d895SEdward Tomasz Napierala error = rctl_get_racct(filter, strlen(filter) + 1, 5094cf0d895SEdward Tomasz Napierala outbuf, outbuflen); 5104cf0d895SEdward Tomasz Napierala if (error == 0) 5114cf0d895SEdward Tomasz Napierala break; 5124cf0d895SEdward Tomasz Napierala if (errno == ERANGE) 5134cf0d895SEdward Tomasz Napierala continue; 514aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 515aae2a24bSEdward Tomasz Napierala enosys(); 5165c859bfbSEdward Tomasz Napierala warn("failed to show resource consumption for '%s'", 5175c859bfbSEdward Tomasz Napierala unexpanded_rule); 5184cf0d895SEdward Tomasz Napierala free(outbuf); 5194cf0d895SEdward Tomasz Napierala 5204cf0d895SEdward Tomasz Napierala return (error); 521aae2a24bSEdward Tomasz Napierala } 5226ec54a57SEdward Tomasz Napierala 5235e7a2555SEdward Tomasz Napierala copy = outbuf; 5245e7a2555SEdward Tomasz Napierala while ((tmp = strsep(©, ",")) != NULL) { 5256ec54a57SEdward Tomasz Napierala if (tmp[0] == '\0') 5266ec54a57SEdward Tomasz Napierala break; /* XXX */ 5276ec54a57SEdward Tomasz Napierala 5286ec54a57SEdward Tomasz Napierala if (hflag) 5296ec54a57SEdward Tomasz Napierala tmp = humanize_usage_amount(tmp); 5306ec54a57SEdward Tomasz Napierala 5316ec54a57SEdward Tomasz Napierala printf("%s\n", tmp); 5326ec54a57SEdward Tomasz Napierala } 5336ec54a57SEdward Tomasz Napierala 5346ec54a57SEdward Tomasz Napierala free(outbuf); 5350971623eSEdward Tomasz Napierala 5360971623eSEdward Tomasz Napierala return (error); 5376ec54a57SEdward Tomasz Napierala } 5386ec54a57SEdward Tomasz Napierala 5396ec54a57SEdward Tomasz Napierala /* 5406ec54a57SEdward Tomasz Napierala * Query the kernel about resource limit rules and print them out. 5416ec54a57SEdward Tomasz Napierala */ 5420971623eSEdward Tomasz Napierala static int 5435c859bfbSEdward Tomasz Napierala show_rules(const char *filter, const char *unexpanded_rule, 5445c859bfbSEdward Tomasz Napierala int hflag, int nflag) 5456ec54a57SEdward Tomasz Napierala { 5466ec54a57SEdward Tomasz Napierala int error; 5476ec54a57SEdward Tomasz Napierala char *outbuf = NULL; 5486ec54a57SEdward Tomasz Napierala size_t filterlen, outbuflen = RCTL_DEFAULT_BUFSIZE / 4; 5496ec54a57SEdward Tomasz Napierala 5506ec54a57SEdward Tomasz Napierala if (filter != NULL) 5516ec54a57SEdward Tomasz Napierala filterlen = strlen(filter) + 1; 5526ec54a57SEdward Tomasz Napierala else 5536ec54a57SEdward Tomasz Napierala filterlen = 0; 5546ec54a57SEdward Tomasz Napierala 5554cf0d895SEdward Tomasz Napierala for (;;) { 5566ec54a57SEdward Tomasz Napierala outbuflen *= 4; 5576ec54a57SEdward Tomasz Napierala outbuf = realloc(outbuf, outbuflen); 5586ec54a57SEdward Tomasz Napierala if (outbuf == NULL) 5596ec54a57SEdward Tomasz Napierala err(1, "realloc"); 5606ec54a57SEdward Tomasz Napierala error = rctl_get_rules(filter, filterlen, outbuf, outbuflen); 5614cf0d895SEdward Tomasz Napierala if (error == 0) 5624cf0d895SEdward Tomasz Napierala break; 5634cf0d895SEdward Tomasz Napierala if (errno == ERANGE) 5644cf0d895SEdward Tomasz Napierala continue; 565aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 566aae2a24bSEdward Tomasz Napierala enosys(); 5675c859bfbSEdward Tomasz Napierala warn("failed to show rules for '%s'", unexpanded_rule); 5684cf0d895SEdward Tomasz Napierala free(outbuf); 5694cf0d895SEdward Tomasz Napierala 5704cf0d895SEdward Tomasz Napierala return (error); 571aae2a24bSEdward Tomasz Napierala } 5726ec54a57SEdward Tomasz Napierala 5736ec54a57SEdward Tomasz Napierala print_rules(outbuf, hflag, nflag); 5746ec54a57SEdward Tomasz Napierala free(outbuf); 5750971623eSEdward Tomasz Napierala 5760971623eSEdward Tomasz Napierala return (error); 5776ec54a57SEdward Tomasz Napierala } 5786ec54a57SEdward Tomasz Napierala 5796ec54a57SEdward Tomasz Napierala static void 5806ec54a57SEdward Tomasz Napierala usage(void) 5816ec54a57SEdward Tomasz Napierala { 5826ec54a57SEdward Tomasz Napierala 5836ec54a57SEdward Tomasz Napierala fprintf(stderr, "usage: rctl [ -h ] [-a rule | -l filter | -r filter " 5846ec54a57SEdward Tomasz Napierala "| -u filter | filter]\n"); 5856ec54a57SEdward Tomasz Napierala exit(1); 5866ec54a57SEdward Tomasz Napierala } 5876ec54a57SEdward Tomasz Napierala 5886ec54a57SEdward Tomasz Napierala int 5896ec54a57SEdward Tomasz Napierala main(int argc __unused, char **argv __unused) 5906ec54a57SEdward Tomasz Napierala { 5916ec54a57SEdward Tomasz Napierala int ch, aflag = 0, hflag = 0, nflag = 0, lflag = 0, rflag = 0, 5926ec54a57SEdward Tomasz Napierala uflag = 0; 5935c859bfbSEdward Tomasz Napierala char *rule = NULL, *unexpanded_rule; 5945c859bfbSEdward Tomasz Napierala int i, cumulated_error, error; 5956ec54a57SEdward Tomasz Napierala 5960971623eSEdward Tomasz Napierala while ((ch = getopt(argc, argv, "ahlnru")) != -1) { 5976ec54a57SEdward Tomasz Napierala switch (ch) { 5986ec54a57SEdward Tomasz Napierala case 'a': 5996ec54a57SEdward Tomasz Napierala aflag = 1; 6006ec54a57SEdward Tomasz Napierala break; 6016ec54a57SEdward Tomasz Napierala case 'h': 6026ec54a57SEdward Tomasz Napierala hflag = 1; 6036ec54a57SEdward Tomasz Napierala break; 6046ec54a57SEdward Tomasz Napierala case 'l': 6056ec54a57SEdward Tomasz Napierala lflag = 1; 6066ec54a57SEdward Tomasz Napierala break; 6076ec54a57SEdward Tomasz Napierala case 'n': 6086ec54a57SEdward Tomasz Napierala nflag = 1; 6096ec54a57SEdward Tomasz Napierala break; 6106ec54a57SEdward Tomasz Napierala case 'r': 6116ec54a57SEdward Tomasz Napierala rflag = 1; 6126ec54a57SEdward Tomasz Napierala break; 6136ec54a57SEdward Tomasz Napierala case 'u': 6146ec54a57SEdward Tomasz Napierala uflag = 1; 6156ec54a57SEdward Tomasz Napierala break; 6166ec54a57SEdward Tomasz Napierala 6176ec54a57SEdward Tomasz Napierala case '?': 6186ec54a57SEdward Tomasz Napierala default: 6196ec54a57SEdward Tomasz Napierala usage(); 6206ec54a57SEdward Tomasz Napierala } 6216ec54a57SEdward Tomasz Napierala } 6226ec54a57SEdward Tomasz Napierala 6236ec54a57SEdward Tomasz Napierala argc -= optind; 6246ec54a57SEdward Tomasz Napierala argv += optind; 6256ec54a57SEdward Tomasz Napierala 6260971623eSEdward Tomasz Napierala if (aflag + lflag + rflag + uflag > 1) 6270971623eSEdward Tomasz Napierala errx(1, "at most one of -a, -l, -r, or -u may be specified"); 6280971623eSEdward Tomasz Napierala 6290971623eSEdward Tomasz Napierala if (argc == 0) { 6300971623eSEdward Tomasz Napierala if (aflag + lflag + rflag + uflag == 0) { 6310971623eSEdward Tomasz Napierala rule = strdup("::"); 6325c859bfbSEdward Tomasz Napierala show_rules(rule, rule, hflag, nflag); 6330971623eSEdward Tomasz Napierala 6340971623eSEdward Tomasz Napierala return (0); 6350971623eSEdward Tomasz Napierala } 6360971623eSEdward Tomasz Napierala 6376ec54a57SEdward Tomasz Napierala usage(); 6380971623eSEdward Tomasz Napierala } 6390971623eSEdward Tomasz Napierala 6400971623eSEdward Tomasz Napierala cumulated_error = 0; 6410971623eSEdward Tomasz Napierala 6420971623eSEdward Tomasz Napierala for (i = 0; i < argc; i++) { 6435c859bfbSEdward Tomasz Napierala unexpanded_rule = argv[i]; 6440971623eSEdward Tomasz Napierala 6450971623eSEdward Tomasz Napierala /* 6460971623eSEdward Tomasz Napierala * Skip resolving if passed -n _and_ -a. Ignore -n otherwise, 6470971623eSEdward Tomasz Napierala * so we can still do "rctl -n u:root" and see the rules without 6480971623eSEdward Tomasz Napierala * resolving the UID. 6490971623eSEdward Tomasz Napierala */ 6500971623eSEdward Tomasz Napierala if (aflag != 0 && nflag != 0) 6515c859bfbSEdward Tomasz Napierala rule = expand_rule(unexpanded_rule, false); 6520971623eSEdward Tomasz Napierala else 6535c859bfbSEdward Tomasz Napierala rule = expand_rule(unexpanded_rule, true); 6546ec54a57SEdward Tomasz Napierala 6556ec54a57SEdward Tomasz Napierala if (rule == NULL) { 6560971623eSEdward Tomasz Napierala cumulated_error++; 6570971623eSEdward Tomasz Napierala continue; 6586ec54a57SEdward Tomasz Napierala } 6596ec54a57SEdward Tomasz Napierala 6605c859bfbSEdward Tomasz Napierala /* 6615c859bfbSEdward Tomasz Napierala * The reason for passing the unexpanded_rule is to make 6625c859bfbSEdward Tomasz Napierala * it easier for the user to search for the problematic 6635c859bfbSEdward Tomasz Napierala * rule in the passed input. 6645c859bfbSEdward Tomasz Napierala */ 6656ec54a57SEdward Tomasz Napierala if (aflag) { 6665c859bfbSEdward Tomasz Napierala error = add_rule(rule, unexpanded_rule); 6670971623eSEdward Tomasz Napierala } else if (lflag) { 6685c859bfbSEdward Tomasz Napierala error = show_limits(rule, unexpanded_rule, 6695c859bfbSEdward Tomasz Napierala hflag, nflag); 6700971623eSEdward Tomasz Napierala } else if (rflag) { 6715c859bfbSEdward Tomasz Napierala error = remove_rule(rule, unexpanded_rule); 6720971623eSEdward Tomasz Napierala } else if (uflag) { 6735c859bfbSEdward Tomasz Napierala error = show_usage(rule, unexpanded_rule, hflag); 6740971623eSEdward Tomasz Napierala } else { 6755c859bfbSEdward Tomasz Napierala error = show_rules(rule, unexpanded_rule, 6765c859bfbSEdward Tomasz Napierala hflag, nflag); 6776ec54a57SEdward Tomasz Napierala } 6786ec54a57SEdward Tomasz Napierala 6795c859bfbSEdward Tomasz Napierala if (error != 0) 6805c859bfbSEdward Tomasz Napierala cumulated_error++; 6815c859bfbSEdward Tomasz Napierala 6820971623eSEdward Tomasz Napierala free(rule); 6836ec54a57SEdward Tomasz Napierala } 6846ec54a57SEdward Tomasz Napierala 6850971623eSEdward Tomasz Napierala return (cumulated_error); 6866ec54a57SEdward Tomasz Napierala } 687