16ec54a57SEdward Tomasz Napierala /*- 21de7b4b8SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 31de7b4b8SPedro F. Giffuni * 46ec54a57SEdward Tomasz Napierala * Copyright (c) 2010 The FreeBSD Foundation 56ec54a57SEdward Tomasz Napierala * All rights reserved. 66ec54a57SEdward Tomasz Napierala * 76ec54a57SEdward Tomasz Napierala * This software was developed by Edward Tomasz Napierala under sponsorship 86ec54a57SEdward Tomasz Napierala * from the FreeBSD Foundation. 96ec54a57SEdward Tomasz Napierala * 106ec54a57SEdward Tomasz Napierala * Redistribution and use in source and binary forms, with or without 116ec54a57SEdward Tomasz Napierala * modification, are permitted provided that the following conditions 126ec54a57SEdward Tomasz Napierala * are met: 136ec54a57SEdward Tomasz Napierala * 1. Redistributions of source code must retain the above copyright 146ec54a57SEdward Tomasz Napierala * notice, this list of conditions and the following disclaimer. 156ec54a57SEdward Tomasz Napierala * 2. Redistributions in binary form must reproduce the above copyright 166ec54a57SEdward Tomasz Napierala * notice, this list of conditions and the following disclaimer in the 176ec54a57SEdward Tomasz Napierala * documentation and/or other materials provided with the distribution. 186ec54a57SEdward Tomasz Napierala * 196ec54a57SEdward Tomasz Napierala * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 206ec54a57SEdward Tomasz Napierala * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 216ec54a57SEdward Tomasz Napierala * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 226ec54a57SEdward Tomasz Napierala * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 236ec54a57SEdward Tomasz Napierala * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 246ec54a57SEdward Tomasz Napierala * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 256ec54a57SEdward Tomasz Napierala * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 266ec54a57SEdward Tomasz Napierala * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 276ec54a57SEdward Tomasz Napierala * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 286ec54a57SEdward Tomasz Napierala * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 296ec54a57SEdward Tomasz Napierala * SUCH DAMAGE. 306ec54a57SEdward Tomasz Napierala * 316ec54a57SEdward Tomasz Napierala * $FreeBSD$ 326ec54a57SEdward Tomasz Napierala */ 336ec54a57SEdward Tomasz Napierala 346ec54a57SEdward Tomasz Napierala #include <sys/cdefs.h> 356ec54a57SEdward Tomasz Napierala __FBSDID("$FreeBSD$"); 366ec54a57SEdward Tomasz Napierala 376ec54a57SEdward Tomasz Napierala #include <sys/types.h> 386ec54a57SEdward Tomasz Napierala #include <sys/rctl.h> 39aae2a24bSEdward Tomasz Napierala #include <sys/sysctl.h> 406ec54a57SEdward Tomasz Napierala #include <assert.h> 416ec54a57SEdward Tomasz Napierala #include <ctype.h> 426ec54a57SEdward Tomasz Napierala #include <err.h> 436ec54a57SEdward Tomasz Napierala #include <errno.h> 446ec54a57SEdward Tomasz Napierala #include <getopt.h> 456ec54a57SEdward Tomasz Napierala #include <grp.h> 466ec54a57SEdward Tomasz Napierala #include <libutil.h> 476ec54a57SEdward Tomasz Napierala #include <pwd.h> 480971623eSEdward Tomasz Napierala #include <stdbool.h> 496ec54a57SEdward Tomasz Napierala #include <stdint.h> 506ec54a57SEdward Tomasz Napierala #include <stdio.h> 516ec54a57SEdward Tomasz Napierala #include <stdlib.h> 526ec54a57SEdward Tomasz Napierala #include <string.h> 536ec54a57SEdward Tomasz Napierala 5418e1f46eSEdward Tomasz Napierala #define RCTL_DEFAULT_BUFSIZE 128 * 1024 556ec54a57SEdward Tomasz Napierala 560971623eSEdward Tomasz Napierala static int 575c859bfbSEdward Tomasz Napierala parse_user(const char *s, id_t *uidp, const char *unexpanded_rule) 586ec54a57SEdward Tomasz Napierala { 596ec54a57SEdward Tomasz Napierala char *end; 606ec54a57SEdward Tomasz Napierala struct passwd *pwd; 616ec54a57SEdward Tomasz Napierala 626ec54a57SEdward Tomasz Napierala pwd = getpwnam(s); 630971623eSEdward Tomasz Napierala if (pwd != NULL) { 640971623eSEdward Tomasz Napierala *uidp = pwd->pw_uid; 650971623eSEdward Tomasz Napierala return (0); 666ec54a57SEdward Tomasz Napierala } 676ec54a57SEdward Tomasz Napierala 680971623eSEdward Tomasz Napierala if (!isnumber(s[0])) { 69c281fdb1SEnji Cooper warnx("malformed rule '%s': unknown user '%s'", 705c859bfbSEdward Tomasz Napierala unexpanded_rule, s); 710971623eSEdward Tomasz Napierala return (1); 720971623eSEdward Tomasz Napierala } 730971623eSEdward Tomasz Napierala 740971623eSEdward Tomasz Napierala *uidp = strtod(s, &end); 750971623eSEdward Tomasz Napierala if ((size_t)(end - s) != strlen(s)) { 765c859bfbSEdward Tomasz Napierala warnx("malformed rule '%s': trailing characters " 775c859bfbSEdward Tomasz Napierala "after numerical id", unexpanded_rule); 780971623eSEdward Tomasz Napierala return (1); 790971623eSEdward Tomasz Napierala } 800971623eSEdward Tomasz Napierala 810971623eSEdward Tomasz Napierala return (0); 820971623eSEdward Tomasz Napierala } 830971623eSEdward Tomasz Napierala 840971623eSEdward Tomasz Napierala static int 855c859bfbSEdward Tomasz Napierala parse_group(const char *s, id_t *gidp, const char *unexpanded_rule) 866ec54a57SEdward Tomasz Napierala { 876ec54a57SEdward Tomasz Napierala char *end; 886ec54a57SEdward Tomasz Napierala struct group *grp; 896ec54a57SEdward Tomasz Napierala 906ec54a57SEdward Tomasz Napierala grp = getgrnam(s); 910971623eSEdward Tomasz Napierala if (grp != NULL) { 920971623eSEdward Tomasz Napierala *gidp = grp->gr_gid; 930971623eSEdward Tomasz Napierala return (0); 940971623eSEdward Tomasz Napierala } 956ec54a57SEdward Tomasz Napierala 960971623eSEdward Tomasz Napierala if (!isnumber(s[0])) { 97c281fdb1SEnji Cooper warnx("malformed rule '%s': unknown group '%s'", 985c859bfbSEdward Tomasz Napierala unexpanded_rule, s); 990971623eSEdward Tomasz Napierala return (1); 1000971623eSEdward Tomasz Napierala } 1016ec54a57SEdward Tomasz Napierala 1020971623eSEdward Tomasz Napierala *gidp = strtod(s, &end); 1030971623eSEdward Tomasz Napierala if ((size_t)(end - s) != strlen(s)) { 1045c859bfbSEdward Tomasz Napierala warnx("malformed rule '%s': trailing characters " 1055c859bfbSEdward Tomasz Napierala "after numerical id", unexpanded_rule); 1060971623eSEdward Tomasz Napierala return (1); 1070971623eSEdward Tomasz Napierala } 1086ec54a57SEdward Tomasz Napierala 1090971623eSEdward Tomasz Napierala return (0); 1106ec54a57SEdward Tomasz Napierala } 1116ec54a57SEdward Tomasz Napierala 1126ec54a57SEdward Tomasz Napierala /* 1130971623eSEdward Tomasz Napierala * Replace human-readable number with its expanded form. 1146ec54a57SEdward Tomasz Napierala */ 1156ec54a57SEdward Tomasz Napierala static char * 116508744b2SEdward Tomasz Napierala expand_amount(const char *rule, const char *unexpanded_rule) 1176ec54a57SEdward Tomasz Napierala { 1186ec54a57SEdward Tomasz Napierala uint64_t num; 1196ec54a57SEdward Tomasz Napierala const char *subject, *subject_id, *resource, *action, *amount, *per; 120508744b2SEdward Tomasz Napierala char *copy, *expanded, *tofree; 121f4e361a7SEdward Tomasz Napierala int ret; 1226ec54a57SEdward Tomasz Napierala 123508744b2SEdward Tomasz Napierala tofree = copy = strdup(rule); 124508744b2SEdward Tomasz Napierala if (copy == NULL) { 125508744b2SEdward Tomasz Napierala warn("strdup"); 126508744b2SEdward Tomasz Napierala return (NULL); 127508744b2SEdward Tomasz Napierala } 1286ec54a57SEdward Tomasz Napierala 129508744b2SEdward Tomasz Napierala subject = strsep(©, ":"); 130508744b2SEdward Tomasz Napierala subject_id = strsep(©, ":"); 131508744b2SEdward Tomasz Napierala resource = strsep(©, ":"); 132508744b2SEdward Tomasz Napierala action = strsep(©, "=/"); 133508744b2SEdward Tomasz Napierala amount = strsep(©, "/"); 134508744b2SEdward Tomasz Napierala per = copy; 135508744b2SEdward Tomasz Napierala 136508744b2SEdward Tomasz Napierala if (amount == NULL || strlen(amount) == 0) { 137508744b2SEdward Tomasz Napierala /* 138508744b2SEdward Tomasz Napierala * The "copy" has already been tinkered with by strsep(). 139508744b2SEdward Tomasz Napierala */ 140508744b2SEdward Tomasz Napierala free(tofree); 141508744b2SEdward Tomasz Napierala copy = strdup(rule); 142508744b2SEdward Tomasz Napierala if (copy == NULL) { 143508744b2SEdward Tomasz Napierala warn("strdup"); 144508744b2SEdward Tomasz Napierala return (NULL); 145508744b2SEdward Tomasz Napierala } 146508744b2SEdward Tomasz Napierala return (copy); 147508744b2SEdward Tomasz Napierala } 1486ec54a57SEdward Tomasz Napierala 1496ec54a57SEdward Tomasz Napierala assert(subject != NULL); 1506ec54a57SEdward Tomasz Napierala assert(subject_id != NULL); 1516ec54a57SEdward Tomasz Napierala assert(resource != NULL); 1526ec54a57SEdward Tomasz Napierala assert(action != NULL); 1536ec54a57SEdward Tomasz Napierala 1540971623eSEdward Tomasz Napierala if (expand_number(amount, &num)) { 1555c859bfbSEdward Tomasz Napierala warnx("malformed rule '%s': invalid numeric value '%s'", 1565c859bfbSEdward Tomasz Napierala unexpanded_rule, amount); 157508744b2SEdward Tomasz Napierala free(tofree); 1580971623eSEdward Tomasz Napierala return (NULL); 1590971623eSEdward Tomasz Napierala } 1606ec54a57SEdward Tomasz Napierala 161f4e361a7SEdward Tomasz Napierala if (per == NULL) { 162f4e361a7SEdward Tomasz Napierala ret = asprintf(&expanded, "%s:%s:%s:%s=%ju", 163f4e361a7SEdward Tomasz Napierala subject, subject_id, resource, action, (uintmax_t)num); 164f4e361a7SEdward Tomasz Napierala } else { 165f4e361a7SEdward Tomasz Napierala ret = asprintf(&expanded, "%s:%s:%s:%s=%ju/%s", 166f4e361a7SEdward Tomasz Napierala subject, subject_id, resource, action, (uintmax_t)num, per); 167f4e361a7SEdward Tomasz Napierala } 1686ec54a57SEdward Tomasz Napierala 169f4e361a7SEdward Tomasz Napierala if (ret <= 0) { 1700971623eSEdward Tomasz Napierala warn("asprintf"); 171508744b2SEdward Tomasz Napierala free(tofree); 1720971623eSEdward Tomasz Napierala return (NULL); 1730971623eSEdward Tomasz Napierala } 1746ec54a57SEdward Tomasz Napierala 175508744b2SEdward Tomasz Napierala free(tofree); 176508744b2SEdward Tomasz Napierala 1776ec54a57SEdward Tomasz Napierala return (expanded); 1786ec54a57SEdward Tomasz Napierala } 1796ec54a57SEdward Tomasz Napierala 1800971623eSEdward Tomasz Napierala static char * 1815c859bfbSEdward Tomasz Napierala expand_rule(const char *rule, bool resolve_ids) 1820971623eSEdward Tomasz Napierala { 1830971623eSEdward Tomasz Napierala id_t id; 1840971623eSEdward Tomasz Napierala const char *subject, *textid, *rest; 1855c859bfbSEdward Tomasz Napierala char *copy, *expanded, *resolved, *tofree; 186f4e361a7SEdward Tomasz Napierala int error, ret; 1870971623eSEdward Tomasz Napierala 1885c859bfbSEdward Tomasz Napierala tofree = copy = strdup(rule); 1895c859bfbSEdward Tomasz Napierala if (copy == NULL) { 1905c859bfbSEdward Tomasz Napierala warn("strdup"); 1910971623eSEdward Tomasz Napierala return (NULL); 1920971623eSEdward Tomasz Napierala } 1935c859bfbSEdward Tomasz Napierala 1945c859bfbSEdward Tomasz Napierala subject = strsep(©, ":"); 1955c859bfbSEdward Tomasz Napierala textid = strsep(©, ":"); 1965c859bfbSEdward Tomasz Napierala if (textid == NULL) { 1975c859bfbSEdward Tomasz Napierala warnx("malformed rule '%s': missing subject", rule); 1985c859bfbSEdward Tomasz Napierala return (NULL); 1995c859bfbSEdward Tomasz Napierala } 2005c859bfbSEdward Tomasz Napierala if (copy != NULL) 2015c859bfbSEdward Tomasz Napierala rest = copy; 2020971623eSEdward Tomasz Napierala else 2030971623eSEdward Tomasz Napierala rest = ""; 2040971623eSEdward Tomasz Napierala 2050971623eSEdward Tomasz Napierala if (strcasecmp(subject, "u") == 0) 2060971623eSEdward Tomasz Napierala subject = "user"; 2070971623eSEdward Tomasz Napierala else if (strcasecmp(subject, "g") == 0) 2080971623eSEdward Tomasz Napierala subject = "group"; 2090971623eSEdward Tomasz Napierala else if (strcasecmp(subject, "p") == 0) 2100971623eSEdward Tomasz Napierala subject = "process"; 2110971623eSEdward Tomasz Napierala else if (strcasecmp(subject, "l") == 0 || 2120971623eSEdward Tomasz Napierala strcasecmp(subject, "c") == 0 || 2130971623eSEdward Tomasz Napierala strcasecmp(subject, "class") == 0) 2140971623eSEdward Tomasz Napierala subject = "loginclass"; 2150971623eSEdward Tomasz Napierala else if (strcasecmp(subject, "j") == 0) 2160971623eSEdward Tomasz Napierala subject = "jail"; 2170971623eSEdward Tomasz Napierala 2180971623eSEdward Tomasz Napierala if (resolve_ids && 2190971623eSEdward Tomasz Napierala strcasecmp(subject, "user") == 0 && strlen(textid) > 0) { 2205c859bfbSEdward Tomasz Napierala error = parse_user(textid, &id, rule); 2215c859bfbSEdward Tomasz Napierala if (error != 0) { 2225c859bfbSEdward Tomasz Napierala free(tofree); 2230971623eSEdward Tomasz Napierala return (NULL); 2245c859bfbSEdward Tomasz Napierala } 225f4e361a7SEdward Tomasz Napierala ret = asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest); 2260971623eSEdward Tomasz Napierala } else if (resolve_ids && 2270971623eSEdward Tomasz Napierala strcasecmp(subject, "group") == 0 && strlen(textid) > 0) { 2285c859bfbSEdward Tomasz Napierala error = parse_group(textid, &id, rule); 2295c859bfbSEdward Tomasz Napierala if (error != 0) { 2305c859bfbSEdward Tomasz Napierala free(tofree); 2310971623eSEdward Tomasz Napierala return (NULL); 2325c859bfbSEdward Tomasz Napierala } 233f4e361a7SEdward Tomasz Napierala ret = asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest); 2340971623eSEdward Tomasz Napierala } else { 235f4e361a7SEdward Tomasz Napierala ret = asprintf(&resolved, "%s:%s:%s", subject, textid, rest); 2360971623eSEdward Tomasz Napierala } 2370971623eSEdward Tomasz Napierala 238f4e361a7SEdward Tomasz Napierala if (ret <= 0) { 2390971623eSEdward Tomasz Napierala warn("asprintf"); 2405c859bfbSEdward Tomasz Napierala free(tofree); 2410971623eSEdward Tomasz Napierala return (NULL); 2420971623eSEdward Tomasz Napierala } 2430971623eSEdward Tomasz Napierala 2445c859bfbSEdward Tomasz Napierala free(tofree); 2455c859bfbSEdward Tomasz Napierala 2465c859bfbSEdward Tomasz Napierala expanded = expand_amount(resolved, rule); 2475c859bfbSEdward Tomasz Napierala free(resolved); 2485c859bfbSEdward Tomasz Napierala 2495c859bfbSEdward Tomasz Napierala return (expanded); 2500971623eSEdward Tomasz Napierala } 2510971623eSEdward Tomasz Napierala 2526ec54a57SEdward Tomasz Napierala static char * 2536ec54a57SEdward Tomasz Napierala humanize_ids(char *rule) 2546ec54a57SEdward Tomasz Napierala { 2556ec54a57SEdward Tomasz Napierala id_t id; 2566ec54a57SEdward Tomasz Napierala struct passwd *pwd; 2576ec54a57SEdward Tomasz Napierala struct group *grp; 2586ec54a57SEdward Tomasz Napierala const char *subject, *textid, *rest; 259478f7a72SEdward Tomasz Napierala char *end, *humanized; 260f4e361a7SEdward Tomasz Napierala int ret; 2616ec54a57SEdward Tomasz Napierala 2626ec54a57SEdward Tomasz Napierala subject = strsep(&rule, ":"); 2636ec54a57SEdward Tomasz Napierala textid = strsep(&rule, ":"); 2646ec54a57SEdward Tomasz Napierala if (textid == NULL) 2656ec54a57SEdward Tomasz Napierala errx(1, "rule passed from the kernel didn't contain subject"); 2666ec54a57SEdward Tomasz Napierala if (rule != NULL) 2676ec54a57SEdward Tomasz Napierala rest = rule; 2686ec54a57SEdward Tomasz Napierala else 2696ec54a57SEdward Tomasz Napierala rest = ""; 2706ec54a57SEdward Tomasz Napierala 2716ec54a57SEdward Tomasz Napierala /* Replace numerical user and group ids with names. */ 2726ec54a57SEdward Tomasz Napierala if (strcasecmp(subject, "user") == 0) { 273478f7a72SEdward Tomasz Napierala id = strtod(textid, &end); 274478f7a72SEdward Tomasz Napierala if ((size_t)(end - textid) != strlen(textid)) 275478f7a72SEdward Tomasz Napierala errx(1, "malformed uid '%s'", textid); 2766ec54a57SEdward Tomasz Napierala pwd = getpwuid(id); 2776ec54a57SEdward Tomasz Napierala if (pwd != NULL) 2786ec54a57SEdward Tomasz Napierala textid = pwd->pw_name; 2796ec54a57SEdward Tomasz Napierala } else if (strcasecmp(subject, "group") == 0) { 280478f7a72SEdward Tomasz Napierala id = strtod(textid, &end); 281478f7a72SEdward Tomasz Napierala if ((size_t)(end - textid) != strlen(textid)) 282478f7a72SEdward Tomasz Napierala errx(1, "malformed gid '%s'", textid); 2836ec54a57SEdward Tomasz Napierala grp = getgrgid(id); 2846ec54a57SEdward Tomasz Napierala if (grp != NULL) 2856ec54a57SEdward Tomasz Napierala textid = grp->gr_name; 2866ec54a57SEdward Tomasz Napierala } 2876ec54a57SEdward Tomasz Napierala 288f4e361a7SEdward Tomasz Napierala ret = asprintf(&humanized, "%s:%s:%s", subject, textid, rest); 289f4e361a7SEdward Tomasz Napierala if (ret <= 0) 2906ec54a57SEdward Tomasz Napierala err(1, "asprintf"); 2916ec54a57SEdward Tomasz Napierala 2926ec54a57SEdward Tomasz Napierala return (humanized); 2936ec54a57SEdward Tomasz Napierala } 2946ec54a57SEdward Tomasz Napierala 2956ec54a57SEdward Tomasz Napierala static int 2966ec54a57SEdward Tomasz Napierala str2int64(const char *str, int64_t *value) 2976ec54a57SEdward Tomasz Napierala { 2986ec54a57SEdward Tomasz Napierala char *end; 2996ec54a57SEdward Tomasz Napierala 3006ec54a57SEdward Tomasz Napierala if (str == NULL) 3016ec54a57SEdward Tomasz Napierala return (EINVAL); 3026ec54a57SEdward Tomasz Napierala 3036ec54a57SEdward Tomasz Napierala *value = strtoul(str, &end, 10); 3046ec54a57SEdward Tomasz Napierala if ((size_t)(end - str) != strlen(str)) 3056ec54a57SEdward Tomasz Napierala return (EINVAL); 3066ec54a57SEdward Tomasz Napierala 3076ec54a57SEdward Tomasz Napierala return (0); 3086ec54a57SEdward Tomasz Napierala } 3096ec54a57SEdward Tomasz Napierala 3106ec54a57SEdward Tomasz Napierala static char * 3116ec54a57SEdward Tomasz Napierala humanize_amount(char *rule) 3126ec54a57SEdward Tomasz Napierala { 3136ec54a57SEdward Tomasz Napierala int64_t num; 3146ec54a57SEdward Tomasz Napierala const char *subject, *subject_id, *resource, *action, *amount, *per; 3155e7a2555SEdward Tomasz Napierala char *copy, *humanized, buf[6], *tofree; 316f4e361a7SEdward Tomasz Napierala int ret; 3176ec54a57SEdward Tomasz Napierala 3185e7a2555SEdward Tomasz Napierala tofree = copy = strdup(rule); 3196ec54a57SEdward Tomasz Napierala if (copy == NULL) 3206ec54a57SEdward Tomasz Napierala err(1, "strdup"); 3216ec54a57SEdward Tomasz Napierala 3226ec54a57SEdward Tomasz Napierala subject = strsep(©, ":"); 3236ec54a57SEdward Tomasz Napierala subject_id = strsep(©, ":"); 3246ec54a57SEdward Tomasz Napierala resource = strsep(©, ":"); 3256ec54a57SEdward Tomasz Napierala action = strsep(©, "=/"); 3266ec54a57SEdward Tomasz Napierala amount = strsep(©, "/"); 3276ec54a57SEdward Tomasz Napierala per = copy; 3286ec54a57SEdward Tomasz Napierala 3296ec54a57SEdward Tomasz Napierala if (amount == NULL || strlen(amount) == 0 || 3306ec54a57SEdward Tomasz Napierala str2int64(amount, &num) != 0) { 3315e7a2555SEdward Tomasz Napierala free(tofree); 3326ec54a57SEdward Tomasz Napierala return (rule); 3336ec54a57SEdward Tomasz Napierala } 3346ec54a57SEdward Tomasz Napierala 3356ec54a57SEdward Tomasz Napierala assert(subject != NULL); 3366ec54a57SEdward Tomasz Napierala assert(subject_id != NULL); 3376ec54a57SEdward Tomasz Napierala assert(resource != NULL); 3386ec54a57SEdward Tomasz Napierala assert(action != NULL); 3396ec54a57SEdward Tomasz Napierala 3406ec54a57SEdward Tomasz Napierala if (humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE, 3416ec54a57SEdward Tomasz Napierala HN_DECIMAL | HN_NOSPACE) == -1) 3426ec54a57SEdward Tomasz Napierala err(1, "humanize_number"); 3436ec54a57SEdward Tomasz Napierala 344f4e361a7SEdward Tomasz Napierala if (per == NULL) { 345f4e361a7SEdward Tomasz Napierala ret = asprintf(&humanized, "%s:%s:%s:%s=%s", 346f4e361a7SEdward Tomasz Napierala subject, subject_id, resource, action, buf); 347f4e361a7SEdward Tomasz Napierala } else { 348f4e361a7SEdward Tomasz Napierala ret = asprintf(&humanized, "%s:%s:%s:%s=%s/%s", 349f4e361a7SEdward Tomasz Napierala subject, subject_id, resource, action, buf, per); 350f4e361a7SEdward Tomasz Napierala } 3516ec54a57SEdward Tomasz Napierala 352f4e361a7SEdward Tomasz Napierala if (ret <= 0) 3536ec54a57SEdward Tomasz Napierala err(1, "asprintf"); 3546ec54a57SEdward Tomasz Napierala 3555e7a2555SEdward Tomasz Napierala free(tofree); 3566ec54a57SEdward Tomasz Napierala return (humanized); 3576ec54a57SEdward Tomasz Napierala } 3586ec54a57SEdward Tomasz Napierala 3596ec54a57SEdward Tomasz Napierala /* 3606ec54a57SEdward Tomasz Napierala * Print rules, one per line. 3616ec54a57SEdward Tomasz Napierala */ 3626ec54a57SEdward Tomasz Napierala static void 3636ec54a57SEdward Tomasz Napierala print_rules(char *rules, int hflag, int nflag) 3646ec54a57SEdward Tomasz Napierala { 3656ec54a57SEdward Tomasz Napierala char *rule; 3666ec54a57SEdward Tomasz Napierala 3676ec54a57SEdward Tomasz Napierala while ((rule = strsep(&rules, ",")) != NULL) { 3686ec54a57SEdward Tomasz Napierala if (rule[0] == '\0') 3696ec54a57SEdward Tomasz Napierala break; /* XXX */ 3706ec54a57SEdward Tomasz Napierala if (nflag == 0) 3716ec54a57SEdward Tomasz Napierala rule = humanize_ids(rule); 3726ec54a57SEdward Tomasz Napierala if (hflag) 3736ec54a57SEdward Tomasz Napierala rule = humanize_amount(rule); 3746ec54a57SEdward Tomasz Napierala printf("%s\n", rule); 3756ec54a57SEdward Tomasz Napierala } 3766ec54a57SEdward Tomasz Napierala } 3776ec54a57SEdward Tomasz Napierala 3786ec54a57SEdward Tomasz Napierala static void 379aae2a24bSEdward Tomasz Napierala enosys(void) 380aae2a24bSEdward Tomasz Napierala { 381aae2a24bSEdward Tomasz Napierala size_t racct_enable_len; 382*0a17b9e0SMateusz Guzik int error; 383*0a17b9e0SMateusz Guzik bool racct_enable; 384aae2a24bSEdward Tomasz Napierala 385aae2a24bSEdward Tomasz Napierala racct_enable_len = sizeof(racct_enable); 386aae2a24bSEdward Tomasz Napierala error = sysctlbyname("kern.racct.enable", 387aae2a24bSEdward Tomasz Napierala &racct_enable, &racct_enable_len, NULL, 0); 388aae2a24bSEdward Tomasz Napierala 389aae2a24bSEdward Tomasz Napierala if (error != 0) { 390aae2a24bSEdward Tomasz Napierala if (errno == ENOENT) 39182224d7dSEdward Tomasz Napierala errx(1, "RACCT/RCTL support not present in kernel; see rctl(8) for details"); 392aae2a24bSEdward Tomasz Napierala 393aae2a24bSEdward Tomasz Napierala err(1, "sysctlbyname"); 394aae2a24bSEdward Tomasz Napierala } 395aae2a24bSEdward Tomasz Napierala 396*0a17b9e0SMateusz Guzik if (!racct_enable) 397aae2a24bSEdward Tomasz Napierala errx(1, "RACCT/RCTL present, but disabled; enable using kern.racct.enable=1 tunable"); 398aae2a24bSEdward Tomasz Napierala } 399aae2a24bSEdward Tomasz Napierala 4000971623eSEdward Tomasz Napierala static int 4015c859bfbSEdward Tomasz Napierala add_rule(const char *rule, const char *unexpanded_rule) 4026ec54a57SEdward Tomasz Napierala { 4036ec54a57SEdward Tomasz Napierala int error; 4046ec54a57SEdward Tomasz Napierala 4056ec54a57SEdward Tomasz Napierala error = rctl_add_rule(rule, strlen(rule) + 1, NULL, 0); 406aae2a24bSEdward Tomasz Napierala if (error != 0) { 407aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 408aae2a24bSEdward Tomasz Napierala enosys(); 4095c859bfbSEdward Tomasz Napierala warn("failed to add rule '%s'", unexpanded_rule); 4106ec54a57SEdward Tomasz Napierala } 4116ec54a57SEdward Tomasz Napierala 4120971623eSEdward Tomasz Napierala return (error); 4130971623eSEdward Tomasz Napierala } 4140971623eSEdward Tomasz Napierala 4150971623eSEdward Tomasz Napierala static int 4165c859bfbSEdward Tomasz Napierala show_limits(const char *filter, const char *unexpanded_rule, 4175c859bfbSEdward Tomasz Napierala int hflag, int nflag) 4186ec54a57SEdward Tomasz Napierala { 4196ec54a57SEdward Tomasz Napierala int error; 4206ec54a57SEdward Tomasz Napierala char *outbuf = NULL; 4216ec54a57SEdward Tomasz Napierala size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4; 4226ec54a57SEdward Tomasz Napierala 4234cf0d895SEdward Tomasz Napierala for (;;) { 4246ec54a57SEdward Tomasz Napierala outbuflen *= 4; 4256ec54a57SEdward Tomasz Napierala outbuf = realloc(outbuf, outbuflen); 4266ec54a57SEdward Tomasz Napierala if (outbuf == NULL) 4276ec54a57SEdward Tomasz Napierala err(1, "realloc"); 4284cf0d895SEdward Tomasz Napierala error = rctl_get_limits(filter, strlen(filter) + 1, 4294cf0d895SEdward Tomasz Napierala outbuf, outbuflen); 4304cf0d895SEdward Tomasz Napierala if (error == 0) 4314cf0d895SEdward Tomasz Napierala break; 4324cf0d895SEdward Tomasz Napierala if (errno == ERANGE) 4334cf0d895SEdward Tomasz Napierala continue; 434aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 435aae2a24bSEdward Tomasz Napierala enosys(); 4365c859bfbSEdward Tomasz Napierala warn("failed to get limits for '%s'", unexpanded_rule); 4374cf0d895SEdward Tomasz Napierala free(outbuf); 4384cf0d895SEdward Tomasz Napierala 4394cf0d895SEdward Tomasz Napierala return (error); 440aae2a24bSEdward Tomasz Napierala } 4416ec54a57SEdward Tomasz Napierala 4426ec54a57SEdward Tomasz Napierala print_rules(outbuf, hflag, nflag); 4436ec54a57SEdward Tomasz Napierala free(outbuf); 4440971623eSEdward Tomasz Napierala 4450971623eSEdward Tomasz Napierala return (error); 4466ec54a57SEdward Tomasz Napierala } 4476ec54a57SEdward Tomasz Napierala 4480971623eSEdward Tomasz Napierala static int 4495c859bfbSEdward Tomasz Napierala remove_rule(const char *filter, const char *unexpanded_rule) 4506ec54a57SEdward Tomasz Napierala { 4516ec54a57SEdward Tomasz Napierala int error; 4526ec54a57SEdward Tomasz Napierala 4536ec54a57SEdward Tomasz Napierala error = rctl_remove_rule(filter, strlen(filter) + 1, NULL, 0); 454aae2a24bSEdward Tomasz Napierala if (error != 0) { 455aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 456aae2a24bSEdward Tomasz Napierala enosys(); 4575c859bfbSEdward Tomasz Napierala warn("failed to remove rule '%s'", unexpanded_rule); 458aae2a24bSEdward Tomasz Napierala } 4590971623eSEdward Tomasz Napierala 4600971623eSEdward Tomasz Napierala return (error); 4616ec54a57SEdward Tomasz Napierala } 4626ec54a57SEdward Tomasz Napierala 4636ec54a57SEdward Tomasz Napierala static char * 4646ec54a57SEdward Tomasz Napierala humanize_usage_amount(char *usage) 4656ec54a57SEdward Tomasz Napierala { 4666ec54a57SEdward Tomasz Napierala int64_t num; 4676ec54a57SEdward Tomasz Napierala const char *resource, *amount; 4685e7a2555SEdward Tomasz Napierala char *copy, *humanized, buf[6], *tofree; 469f4e361a7SEdward Tomasz Napierala int ret; 4706ec54a57SEdward Tomasz Napierala 4715e7a2555SEdward Tomasz Napierala tofree = copy = strdup(usage); 4726ec54a57SEdward Tomasz Napierala if (copy == NULL) 4736ec54a57SEdward Tomasz Napierala err(1, "strdup"); 4746ec54a57SEdward Tomasz Napierala 4756ec54a57SEdward Tomasz Napierala resource = strsep(©, "="); 4766ec54a57SEdward Tomasz Napierala amount = copy; 4776ec54a57SEdward Tomasz Napierala 4786ec54a57SEdward Tomasz Napierala assert(resource != NULL); 4796ec54a57SEdward Tomasz Napierala assert(amount != NULL); 4806ec54a57SEdward Tomasz Napierala 4816ec54a57SEdward Tomasz Napierala if (str2int64(amount, &num) != 0 || 4826ec54a57SEdward Tomasz Napierala humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE, 4836ec54a57SEdward Tomasz Napierala HN_DECIMAL | HN_NOSPACE) == -1) { 4845e7a2555SEdward Tomasz Napierala free(tofree); 4856ec54a57SEdward Tomasz Napierala return (usage); 4866ec54a57SEdward Tomasz Napierala } 4876ec54a57SEdward Tomasz Napierala 488f4e361a7SEdward Tomasz Napierala ret = asprintf(&humanized, "%s=%s", resource, buf); 489f4e361a7SEdward Tomasz Napierala if (ret <= 0) 4906ec54a57SEdward Tomasz Napierala err(1, "asprintf"); 4916ec54a57SEdward Tomasz Napierala 4925e7a2555SEdward Tomasz Napierala free(tofree); 4936ec54a57SEdward Tomasz Napierala return (humanized); 4946ec54a57SEdward Tomasz Napierala } 4956ec54a57SEdward Tomasz Napierala 4966ec54a57SEdward Tomasz Napierala /* 4976ec54a57SEdward Tomasz Napierala * Query the kernel about a resource usage and print it out. 4986ec54a57SEdward Tomasz Napierala */ 4990971623eSEdward Tomasz Napierala static int 5005c859bfbSEdward Tomasz Napierala show_usage(const char *filter, const char *unexpanded_rule, int hflag) 5016ec54a57SEdward Tomasz Napierala { 5026ec54a57SEdward Tomasz Napierala int error; 5035e7a2555SEdward Tomasz Napierala char *copy, *outbuf = NULL, *tmp; 5046ec54a57SEdward Tomasz Napierala size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4; 5056ec54a57SEdward Tomasz Napierala 5064cf0d895SEdward Tomasz Napierala for (;;) { 5076ec54a57SEdward Tomasz Napierala outbuflen *= 4; 5086ec54a57SEdward Tomasz Napierala outbuf = realloc(outbuf, outbuflen); 5096ec54a57SEdward Tomasz Napierala if (outbuf == NULL) 5106ec54a57SEdward Tomasz Napierala err(1, "realloc"); 5114cf0d895SEdward Tomasz Napierala error = rctl_get_racct(filter, strlen(filter) + 1, 5124cf0d895SEdward Tomasz Napierala outbuf, outbuflen); 5134cf0d895SEdward Tomasz Napierala if (error == 0) 5144cf0d895SEdward Tomasz Napierala break; 5154cf0d895SEdward Tomasz Napierala if (errno == ERANGE) 5164cf0d895SEdward Tomasz Napierala continue; 517aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 518aae2a24bSEdward Tomasz Napierala enosys(); 5195c859bfbSEdward Tomasz Napierala warn("failed to show resource consumption for '%s'", 5205c859bfbSEdward Tomasz Napierala unexpanded_rule); 5214cf0d895SEdward Tomasz Napierala free(outbuf); 5224cf0d895SEdward Tomasz Napierala 5234cf0d895SEdward Tomasz Napierala return (error); 524aae2a24bSEdward Tomasz Napierala } 5256ec54a57SEdward Tomasz Napierala 5265e7a2555SEdward Tomasz Napierala copy = outbuf; 5275e7a2555SEdward Tomasz Napierala while ((tmp = strsep(©, ",")) != NULL) { 5286ec54a57SEdward Tomasz Napierala if (tmp[0] == '\0') 5296ec54a57SEdward Tomasz Napierala break; /* XXX */ 5306ec54a57SEdward Tomasz Napierala 5316ec54a57SEdward Tomasz Napierala if (hflag) 5326ec54a57SEdward Tomasz Napierala tmp = humanize_usage_amount(tmp); 5336ec54a57SEdward Tomasz Napierala 5346ec54a57SEdward Tomasz Napierala printf("%s\n", tmp); 5356ec54a57SEdward Tomasz Napierala } 5366ec54a57SEdward Tomasz Napierala 5376ec54a57SEdward Tomasz Napierala free(outbuf); 5380971623eSEdward Tomasz Napierala 5390971623eSEdward Tomasz Napierala return (error); 5406ec54a57SEdward Tomasz Napierala } 5416ec54a57SEdward Tomasz Napierala 5426ec54a57SEdward Tomasz Napierala /* 5436ec54a57SEdward Tomasz Napierala * Query the kernel about resource limit rules and print them out. 5446ec54a57SEdward Tomasz Napierala */ 5450971623eSEdward Tomasz Napierala static int 5465c859bfbSEdward Tomasz Napierala show_rules(const char *filter, const char *unexpanded_rule, 5475c859bfbSEdward Tomasz Napierala int hflag, int nflag) 5486ec54a57SEdward Tomasz Napierala { 5496ec54a57SEdward Tomasz Napierala int error; 5506ec54a57SEdward Tomasz Napierala char *outbuf = NULL; 5516ec54a57SEdward Tomasz Napierala size_t filterlen, outbuflen = RCTL_DEFAULT_BUFSIZE / 4; 5526ec54a57SEdward Tomasz Napierala 5536ec54a57SEdward Tomasz Napierala if (filter != NULL) 5546ec54a57SEdward Tomasz Napierala filterlen = strlen(filter) + 1; 5556ec54a57SEdward Tomasz Napierala else 5566ec54a57SEdward Tomasz Napierala filterlen = 0; 5576ec54a57SEdward Tomasz Napierala 5584cf0d895SEdward Tomasz Napierala for (;;) { 5596ec54a57SEdward Tomasz Napierala outbuflen *= 4; 5606ec54a57SEdward Tomasz Napierala outbuf = realloc(outbuf, outbuflen); 5616ec54a57SEdward Tomasz Napierala if (outbuf == NULL) 5626ec54a57SEdward Tomasz Napierala err(1, "realloc"); 5636ec54a57SEdward Tomasz Napierala error = rctl_get_rules(filter, filterlen, outbuf, outbuflen); 5644cf0d895SEdward Tomasz Napierala if (error == 0) 5654cf0d895SEdward Tomasz Napierala break; 5664cf0d895SEdward Tomasz Napierala if (errno == ERANGE) 5674cf0d895SEdward Tomasz Napierala continue; 568aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 569aae2a24bSEdward Tomasz Napierala enosys(); 5705c859bfbSEdward Tomasz Napierala warn("failed to show rules for '%s'", unexpanded_rule); 5714cf0d895SEdward Tomasz Napierala free(outbuf); 5724cf0d895SEdward Tomasz Napierala 5734cf0d895SEdward Tomasz Napierala return (error); 574aae2a24bSEdward Tomasz Napierala } 5756ec54a57SEdward Tomasz Napierala 5766ec54a57SEdward Tomasz Napierala print_rules(outbuf, hflag, nflag); 5776ec54a57SEdward Tomasz Napierala free(outbuf); 5780971623eSEdward Tomasz Napierala 5790971623eSEdward Tomasz Napierala return (error); 5806ec54a57SEdward Tomasz Napierala } 5816ec54a57SEdward Tomasz Napierala 5826ec54a57SEdward Tomasz Napierala static void 5836ec54a57SEdward Tomasz Napierala usage(void) 5846ec54a57SEdward Tomasz Napierala { 5856ec54a57SEdward Tomasz Napierala 5866ec54a57SEdward Tomasz Napierala fprintf(stderr, "usage: rctl [ -h ] [-a rule | -l filter | -r filter " 5876ec54a57SEdward Tomasz Napierala "| -u filter | filter]\n"); 5886ec54a57SEdward Tomasz Napierala exit(1); 5896ec54a57SEdward Tomasz Napierala } 5906ec54a57SEdward Tomasz Napierala 5916ec54a57SEdward Tomasz Napierala int 592d0ab9cbeSBaptiste Daroussin main(int argc, char **argv) 5936ec54a57SEdward Tomasz Napierala { 5946ec54a57SEdward Tomasz Napierala int ch, aflag = 0, hflag = 0, nflag = 0, lflag = 0, rflag = 0, 5956ec54a57SEdward Tomasz Napierala uflag = 0; 5965c859bfbSEdward Tomasz Napierala char *rule = NULL, *unexpanded_rule; 5975c859bfbSEdward Tomasz Napierala int i, cumulated_error, error; 5986ec54a57SEdward Tomasz Napierala 5990971623eSEdward Tomasz Napierala while ((ch = getopt(argc, argv, "ahlnru")) != -1) { 6006ec54a57SEdward Tomasz Napierala switch (ch) { 6016ec54a57SEdward Tomasz Napierala case 'a': 6026ec54a57SEdward Tomasz Napierala aflag = 1; 6036ec54a57SEdward Tomasz Napierala break; 6046ec54a57SEdward Tomasz Napierala case 'h': 6056ec54a57SEdward Tomasz Napierala hflag = 1; 6066ec54a57SEdward Tomasz Napierala break; 6076ec54a57SEdward Tomasz Napierala case 'l': 6086ec54a57SEdward Tomasz Napierala lflag = 1; 6096ec54a57SEdward Tomasz Napierala break; 6106ec54a57SEdward Tomasz Napierala case 'n': 6116ec54a57SEdward Tomasz Napierala nflag = 1; 6126ec54a57SEdward Tomasz Napierala break; 6136ec54a57SEdward Tomasz Napierala case 'r': 6146ec54a57SEdward Tomasz Napierala rflag = 1; 6156ec54a57SEdward Tomasz Napierala break; 6166ec54a57SEdward Tomasz Napierala case 'u': 6176ec54a57SEdward Tomasz Napierala uflag = 1; 6186ec54a57SEdward Tomasz Napierala break; 6196ec54a57SEdward Tomasz Napierala 6206ec54a57SEdward Tomasz Napierala case '?': 6216ec54a57SEdward Tomasz Napierala default: 6226ec54a57SEdward Tomasz Napierala usage(); 6236ec54a57SEdward Tomasz Napierala } 6246ec54a57SEdward Tomasz Napierala } 6256ec54a57SEdward Tomasz Napierala 6266ec54a57SEdward Tomasz Napierala argc -= optind; 6276ec54a57SEdward Tomasz Napierala argv += optind; 6286ec54a57SEdward Tomasz Napierala 6290971623eSEdward Tomasz Napierala if (aflag + lflag + rflag + uflag > 1) 6300971623eSEdward Tomasz Napierala errx(1, "at most one of -a, -l, -r, or -u may be specified"); 6310971623eSEdward Tomasz Napierala 6320971623eSEdward Tomasz Napierala if (argc == 0) { 6330971623eSEdward Tomasz Napierala if (aflag + lflag + rflag + uflag == 0) { 6340971623eSEdward Tomasz Napierala rule = strdup("::"); 6355c859bfbSEdward Tomasz Napierala show_rules(rule, rule, hflag, nflag); 6360971623eSEdward Tomasz Napierala 6370971623eSEdward Tomasz Napierala return (0); 6380971623eSEdward Tomasz Napierala } 6390971623eSEdward Tomasz Napierala 6406ec54a57SEdward Tomasz Napierala usage(); 6410971623eSEdward Tomasz Napierala } 6420971623eSEdward Tomasz Napierala 6430971623eSEdward Tomasz Napierala cumulated_error = 0; 6440971623eSEdward Tomasz Napierala 6450971623eSEdward Tomasz Napierala for (i = 0; i < argc; i++) { 6465c859bfbSEdward Tomasz Napierala unexpanded_rule = argv[i]; 6470971623eSEdward Tomasz Napierala 6480971623eSEdward Tomasz Napierala /* 6490971623eSEdward Tomasz Napierala * Skip resolving if passed -n _and_ -a. Ignore -n otherwise, 6500971623eSEdward Tomasz Napierala * so we can still do "rctl -n u:root" and see the rules without 6510971623eSEdward Tomasz Napierala * resolving the UID. 6520971623eSEdward Tomasz Napierala */ 6530971623eSEdward Tomasz Napierala if (aflag != 0 && nflag != 0) 6545c859bfbSEdward Tomasz Napierala rule = expand_rule(unexpanded_rule, false); 6550971623eSEdward Tomasz Napierala else 6565c859bfbSEdward Tomasz Napierala rule = expand_rule(unexpanded_rule, true); 6576ec54a57SEdward Tomasz Napierala 6586ec54a57SEdward Tomasz Napierala if (rule == NULL) { 6590971623eSEdward Tomasz Napierala cumulated_error++; 6600971623eSEdward Tomasz Napierala continue; 6616ec54a57SEdward Tomasz Napierala } 6626ec54a57SEdward Tomasz Napierala 6635c859bfbSEdward Tomasz Napierala /* 6645c859bfbSEdward Tomasz Napierala * The reason for passing the unexpanded_rule is to make 6655c859bfbSEdward Tomasz Napierala * it easier for the user to search for the problematic 6665c859bfbSEdward Tomasz Napierala * rule in the passed input. 6675c859bfbSEdward Tomasz Napierala */ 6686ec54a57SEdward Tomasz Napierala if (aflag) { 6695c859bfbSEdward Tomasz Napierala error = add_rule(rule, unexpanded_rule); 6700971623eSEdward Tomasz Napierala } else if (lflag) { 6715c859bfbSEdward Tomasz Napierala error = show_limits(rule, unexpanded_rule, 6725c859bfbSEdward Tomasz Napierala hflag, nflag); 6730971623eSEdward Tomasz Napierala } else if (rflag) { 6745c859bfbSEdward Tomasz Napierala error = remove_rule(rule, unexpanded_rule); 6750971623eSEdward Tomasz Napierala } else if (uflag) { 6765c859bfbSEdward Tomasz Napierala error = show_usage(rule, unexpanded_rule, hflag); 6770971623eSEdward Tomasz Napierala } else { 6785c859bfbSEdward Tomasz Napierala error = show_rules(rule, unexpanded_rule, 6795c859bfbSEdward Tomasz Napierala hflag, nflag); 6806ec54a57SEdward Tomasz Napierala } 6816ec54a57SEdward Tomasz Napierala 6825c859bfbSEdward Tomasz Napierala if (error != 0) 6835c859bfbSEdward Tomasz Napierala cumulated_error++; 6845c859bfbSEdward Tomasz Napierala 6850971623eSEdward Tomasz Napierala free(rule); 6866ec54a57SEdward Tomasz Napierala } 6876ec54a57SEdward Tomasz Napierala 6880971623eSEdward Tomasz Napierala return (cumulated_error); 6896ec54a57SEdward Tomasz Napierala } 690