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> 46*0971623eSEdward 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 54*0971623eSEdward Tomasz Napierala static int 55*0971623eSEdward 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); 61*0971623eSEdward Tomasz Napierala if (pwd != NULL) { 62*0971623eSEdward Tomasz Napierala *uidp = pwd->pw_uid; 63*0971623eSEdward Tomasz Napierala return (0); 646ec54a57SEdward Tomasz Napierala } 656ec54a57SEdward Tomasz Napierala 66*0971623eSEdward Tomasz Napierala if (!isnumber(s[0])) { 67*0971623eSEdward Tomasz Napierala warnx("uknown user '%s'", s); 68*0971623eSEdward Tomasz Napierala return (1); 69*0971623eSEdward Tomasz Napierala } 70*0971623eSEdward Tomasz Napierala 71*0971623eSEdward Tomasz Napierala *uidp = strtod(s, &end); 72*0971623eSEdward Tomasz Napierala if ((size_t)(end - s) != strlen(s)) { 73*0971623eSEdward Tomasz Napierala warnx("trailing characters after numerical id"); 74*0971623eSEdward Tomasz Napierala return (1); 75*0971623eSEdward Tomasz Napierala } 76*0971623eSEdward Tomasz Napierala 77*0971623eSEdward Tomasz Napierala return (0); 78*0971623eSEdward Tomasz Napierala } 79*0971623eSEdward Tomasz Napierala 80*0971623eSEdward Tomasz Napierala static int 81*0971623eSEdward 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); 87*0971623eSEdward Tomasz Napierala if (grp != NULL) { 88*0971623eSEdward Tomasz Napierala *gidp = grp->gr_gid; 89*0971623eSEdward Tomasz Napierala return (0); 90*0971623eSEdward Tomasz Napierala } 916ec54a57SEdward Tomasz Napierala 92*0971623eSEdward Tomasz Napierala if (!isnumber(s[0])) { 93*0971623eSEdward Tomasz Napierala warnx("uknown group '%s'", s); 94*0971623eSEdward Tomasz Napierala return (1); 95*0971623eSEdward Tomasz Napierala } 966ec54a57SEdward Tomasz Napierala 97*0971623eSEdward Tomasz Napierala *gidp = strtod(s, &end); 98*0971623eSEdward Tomasz Napierala if ((size_t)(end - s) != strlen(s)) { 99*0971623eSEdward Tomasz Napierala warnx("trailing characters after numerical id"); 100*0971623eSEdward Tomasz Napierala return (1); 101*0971623eSEdward Tomasz Napierala } 1026ec54a57SEdward Tomasz Napierala 103*0971623eSEdward Tomasz Napierala return (0); 1046ec54a57SEdward Tomasz Napierala } 1056ec54a57SEdward Tomasz Napierala 1066ec54a57SEdward Tomasz Napierala /* 107*0971623eSEdward 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; 1146ec54a57SEdward Tomasz Napierala char *copy, *expanded; 1156ec54a57SEdward Tomasz Napierala 1166ec54a57SEdward Tomasz Napierala copy = strdup(rule); 117*0971623eSEdward Tomasz Napierala if (copy == NULL) { 118*0971623eSEdward Tomasz Napierala warn("strdup"); 119*0971623eSEdward Tomasz Napierala return (NULL); 120*0971623eSEdward Tomasz Napierala } 1216ec54a57SEdward Tomasz Napierala 1226ec54a57SEdward Tomasz Napierala subject = strsep(©, ":"); 1236ec54a57SEdward Tomasz Napierala subject_id = strsep(©, ":"); 1246ec54a57SEdward Tomasz Napierala resource = strsep(©, ":"); 1256ec54a57SEdward Tomasz Napierala action = strsep(©, "=/"); 1266ec54a57SEdward Tomasz Napierala amount = strsep(©, "/"); 1276ec54a57SEdward Tomasz Napierala per = copy; 1286ec54a57SEdward Tomasz Napierala 1296ec54a57SEdward Tomasz Napierala if (amount == NULL || strlen(amount) == 0) { 1306ec54a57SEdward Tomasz Napierala free(copy); 1316ec54a57SEdward Tomasz Napierala return (rule); 1326ec54a57SEdward Tomasz Napierala } 1336ec54a57SEdward Tomasz Napierala 1346ec54a57SEdward Tomasz Napierala assert(subject != NULL); 1356ec54a57SEdward Tomasz Napierala assert(subject_id != NULL); 1366ec54a57SEdward Tomasz Napierala assert(resource != NULL); 1376ec54a57SEdward Tomasz Napierala assert(action != NULL); 1386ec54a57SEdward Tomasz Napierala 139*0971623eSEdward Tomasz Napierala if (expand_number(amount, &num)) { 140*0971623eSEdward Tomasz Napierala warnx("invalid numeric value '%s'", amount); 141*0971623eSEdward Tomasz Napierala free(copy); 142*0971623eSEdward Tomasz Napierala return (NULL); 143*0971623eSEdward Tomasz Napierala } 1446ec54a57SEdward Tomasz Napierala 1456ec54a57SEdward Tomasz Napierala if (per == NULL) 1466ec54a57SEdward Tomasz Napierala asprintf(&expanded, "%s:%s:%s:%s=%ju", subject, subject_id, 1476ec54a57SEdward Tomasz Napierala resource, action, (uintmax_t)num); 1486ec54a57SEdward Tomasz Napierala else 1496ec54a57SEdward Tomasz Napierala asprintf(&expanded, "%s:%s:%s:%s=%ju/%s", subject, subject_id, 1506ec54a57SEdward Tomasz Napierala resource, action, (uintmax_t)num, per); 1516ec54a57SEdward Tomasz Napierala 152*0971623eSEdward Tomasz Napierala if (expanded == NULL) { 153*0971623eSEdward Tomasz Napierala warn("asprintf"); 154*0971623eSEdward Tomasz Napierala free(copy); 155*0971623eSEdward Tomasz Napierala return (NULL); 156*0971623eSEdward Tomasz Napierala } 1576ec54a57SEdward Tomasz Napierala 1586ec54a57SEdward Tomasz Napierala return (expanded); 1596ec54a57SEdward Tomasz Napierala } 1606ec54a57SEdward Tomasz Napierala 161*0971623eSEdward Tomasz Napierala 162*0971623eSEdward Tomasz Napierala static char * 163*0971623eSEdward Tomasz Napierala expand_rule(char *rule, bool resolve_ids) 164*0971623eSEdward Tomasz Napierala { 165*0971623eSEdward Tomasz Napierala id_t id; 166*0971623eSEdward Tomasz Napierala const char *subject, *textid, *rest; 167*0971623eSEdward Tomasz Napierala char *resolved; 168*0971623eSEdward Tomasz Napierala int error; 169*0971623eSEdward Tomasz Napierala 170*0971623eSEdward Tomasz Napierala subject = strsep(&rule, ":"); 171*0971623eSEdward Tomasz Napierala textid = strsep(&rule, ":"); 172*0971623eSEdward Tomasz Napierala if (textid == NULL) { 173*0971623eSEdward Tomasz Napierala warnx("error in rule specification -- no subject"); 174*0971623eSEdward Tomasz Napierala return (NULL); 175*0971623eSEdward Tomasz Napierala } 176*0971623eSEdward Tomasz Napierala if (rule != NULL) 177*0971623eSEdward Tomasz Napierala rest = rule; 178*0971623eSEdward Tomasz Napierala else 179*0971623eSEdward Tomasz Napierala rest = ""; 180*0971623eSEdward Tomasz Napierala 181*0971623eSEdward Tomasz Napierala if (strcasecmp(subject, "u") == 0) 182*0971623eSEdward Tomasz Napierala subject = "user"; 183*0971623eSEdward Tomasz Napierala else if (strcasecmp(subject, "g") == 0) 184*0971623eSEdward Tomasz Napierala subject = "group"; 185*0971623eSEdward Tomasz Napierala else if (strcasecmp(subject, "p") == 0) 186*0971623eSEdward Tomasz Napierala subject = "process"; 187*0971623eSEdward Tomasz Napierala else if (strcasecmp(subject, "l") == 0 || 188*0971623eSEdward Tomasz Napierala strcasecmp(subject, "c") == 0 || 189*0971623eSEdward Tomasz Napierala strcasecmp(subject, "class") == 0) 190*0971623eSEdward Tomasz Napierala subject = "loginclass"; 191*0971623eSEdward Tomasz Napierala else if (strcasecmp(subject, "j") == 0) 192*0971623eSEdward Tomasz Napierala subject = "jail"; 193*0971623eSEdward Tomasz Napierala 194*0971623eSEdward Tomasz Napierala if (resolve_ids && 195*0971623eSEdward Tomasz Napierala strcasecmp(subject, "user") == 0 && strlen(textid) > 0) { 196*0971623eSEdward Tomasz Napierala error = parse_user(textid, &id); 197*0971623eSEdward Tomasz Napierala if (error != 0) 198*0971623eSEdward Tomasz Napierala return (NULL); 199*0971623eSEdward Tomasz Napierala asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest); 200*0971623eSEdward Tomasz Napierala } else if (resolve_ids && 201*0971623eSEdward Tomasz Napierala strcasecmp(subject, "group") == 0 && strlen(textid) > 0) { 202*0971623eSEdward Tomasz Napierala error = parse_group(textid, &id); 203*0971623eSEdward Tomasz Napierala if (error != 0) 204*0971623eSEdward Tomasz Napierala return (NULL); 205*0971623eSEdward Tomasz Napierala asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest); 206*0971623eSEdward Tomasz Napierala } else { 207*0971623eSEdward Tomasz Napierala asprintf(&resolved, "%s:%s:%s", subject, textid, rest); 208*0971623eSEdward Tomasz Napierala } 209*0971623eSEdward Tomasz Napierala 210*0971623eSEdward Tomasz Napierala if (resolved == NULL) { 211*0971623eSEdward Tomasz Napierala warn("asprintf"); 212*0971623eSEdward Tomasz Napierala return (NULL); 213*0971623eSEdward Tomasz Napierala } 214*0971623eSEdward Tomasz Napierala 215*0971623eSEdward Tomasz Napierala return (expand_amount(resolved)); 216*0971623eSEdward Tomasz Napierala } 217*0971623eSEdward Tomasz Napierala 2186ec54a57SEdward Tomasz Napierala static char * 2196ec54a57SEdward Tomasz Napierala humanize_ids(char *rule) 2206ec54a57SEdward Tomasz Napierala { 2216ec54a57SEdward Tomasz Napierala id_t id; 2226ec54a57SEdward Tomasz Napierala struct passwd *pwd; 2236ec54a57SEdward Tomasz Napierala struct group *grp; 2246ec54a57SEdward Tomasz Napierala const char *subject, *textid, *rest; 225478f7a72SEdward Tomasz Napierala char *end, *humanized; 2266ec54a57SEdward Tomasz Napierala 2276ec54a57SEdward Tomasz Napierala subject = strsep(&rule, ":"); 2286ec54a57SEdward Tomasz Napierala textid = strsep(&rule, ":"); 2296ec54a57SEdward Tomasz Napierala if (textid == NULL) 2306ec54a57SEdward Tomasz Napierala errx(1, "rule passed from the kernel didn't contain subject"); 2316ec54a57SEdward Tomasz Napierala if (rule != NULL) 2326ec54a57SEdward Tomasz Napierala rest = rule; 2336ec54a57SEdward Tomasz Napierala else 2346ec54a57SEdward Tomasz Napierala rest = ""; 2356ec54a57SEdward Tomasz Napierala 2366ec54a57SEdward Tomasz Napierala /* Replace numerical user and group ids with names. */ 2376ec54a57SEdward Tomasz Napierala if (strcasecmp(subject, "user") == 0) { 238478f7a72SEdward Tomasz Napierala id = strtod(textid, &end); 239478f7a72SEdward Tomasz Napierala if ((size_t)(end - textid) != strlen(textid)) 240478f7a72SEdward Tomasz Napierala errx(1, "malformed uid '%s'", textid); 2416ec54a57SEdward Tomasz Napierala pwd = getpwuid(id); 2426ec54a57SEdward Tomasz Napierala if (pwd != NULL) 2436ec54a57SEdward Tomasz Napierala textid = pwd->pw_name; 2446ec54a57SEdward Tomasz Napierala } else if (strcasecmp(subject, "group") == 0) { 245478f7a72SEdward Tomasz Napierala id = strtod(textid, &end); 246478f7a72SEdward Tomasz Napierala if ((size_t)(end - textid) != strlen(textid)) 247478f7a72SEdward Tomasz Napierala errx(1, "malformed gid '%s'", textid); 2486ec54a57SEdward Tomasz Napierala grp = getgrgid(id); 2496ec54a57SEdward Tomasz Napierala if (grp != NULL) 2506ec54a57SEdward Tomasz Napierala textid = grp->gr_name; 2516ec54a57SEdward Tomasz Napierala } 2526ec54a57SEdward Tomasz Napierala 2536ec54a57SEdward Tomasz Napierala asprintf(&humanized, "%s:%s:%s", subject, textid, rest); 2546ec54a57SEdward Tomasz Napierala 2556ec54a57SEdward Tomasz Napierala if (humanized == NULL) 2566ec54a57SEdward Tomasz Napierala err(1, "asprintf"); 2576ec54a57SEdward Tomasz Napierala 2586ec54a57SEdward Tomasz Napierala return (humanized); 2596ec54a57SEdward Tomasz Napierala } 2606ec54a57SEdward Tomasz Napierala 2616ec54a57SEdward Tomasz Napierala static int 2626ec54a57SEdward Tomasz Napierala str2int64(const char *str, int64_t *value) 2636ec54a57SEdward Tomasz Napierala { 2646ec54a57SEdward Tomasz Napierala char *end; 2656ec54a57SEdward Tomasz Napierala 2666ec54a57SEdward Tomasz Napierala if (str == NULL) 2676ec54a57SEdward Tomasz Napierala return (EINVAL); 2686ec54a57SEdward Tomasz Napierala 2696ec54a57SEdward Tomasz Napierala *value = strtoul(str, &end, 10); 2706ec54a57SEdward Tomasz Napierala if ((size_t)(end - str) != strlen(str)) 2716ec54a57SEdward Tomasz Napierala return (EINVAL); 2726ec54a57SEdward Tomasz Napierala 2736ec54a57SEdward Tomasz Napierala return (0); 2746ec54a57SEdward Tomasz Napierala } 2756ec54a57SEdward Tomasz Napierala 2766ec54a57SEdward Tomasz Napierala static char * 2776ec54a57SEdward Tomasz Napierala humanize_amount(char *rule) 2786ec54a57SEdward Tomasz Napierala { 2796ec54a57SEdward Tomasz Napierala int64_t num; 2806ec54a57SEdward Tomasz Napierala const char *subject, *subject_id, *resource, *action, *amount, *per; 2816ec54a57SEdward Tomasz Napierala char *copy, *humanized, buf[6]; 2826ec54a57SEdward Tomasz Napierala 2836ec54a57SEdward Tomasz Napierala copy = strdup(rule); 2846ec54a57SEdward Tomasz Napierala if (copy == NULL) 2856ec54a57SEdward Tomasz Napierala err(1, "strdup"); 2866ec54a57SEdward Tomasz Napierala 2876ec54a57SEdward Tomasz Napierala subject = strsep(©, ":"); 2886ec54a57SEdward Tomasz Napierala subject_id = strsep(©, ":"); 2896ec54a57SEdward Tomasz Napierala resource = strsep(©, ":"); 2906ec54a57SEdward Tomasz Napierala action = strsep(©, "=/"); 2916ec54a57SEdward Tomasz Napierala amount = strsep(©, "/"); 2926ec54a57SEdward Tomasz Napierala per = copy; 2936ec54a57SEdward Tomasz Napierala 2946ec54a57SEdward Tomasz Napierala if (amount == NULL || strlen(amount) == 0 || 2956ec54a57SEdward Tomasz Napierala str2int64(amount, &num) != 0) { 2966ec54a57SEdward Tomasz Napierala free(copy); 2976ec54a57SEdward Tomasz Napierala return (rule); 2986ec54a57SEdward Tomasz Napierala } 2996ec54a57SEdward Tomasz Napierala 3006ec54a57SEdward Tomasz Napierala assert(subject != NULL); 3016ec54a57SEdward Tomasz Napierala assert(subject_id != NULL); 3026ec54a57SEdward Tomasz Napierala assert(resource != NULL); 3036ec54a57SEdward Tomasz Napierala assert(action != NULL); 3046ec54a57SEdward Tomasz Napierala 3056ec54a57SEdward Tomasz Napierala if (humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE, 3066ec54a57SEdward Tomasz Napierala HN_DECIMAL | HN_NOSPACE) == -1) 3076ec54a57SEdward Tomasz Napierala err(1, "humanize_number"); 3086ec54a57SEdward Tomasz Napierala 3096ec54a57SEdward Tomasz Napierala if (per == NULL) 3106ec54a57SEdward Tomasz Napierala asprintf(&humanized, "%s:%s:%s:%s=%s", subject, subject_id, 3116ec54a57SEdward Tomasz Napierala resource, action, buf); 3126ec54a57SEdward Tomasz Napierala else 3136ec54a57SEdward Tomasz Napierala asprintf(&humanized, "%s:%s:%s:%s=%s/%s", subject, subject_id, 3146ec54a57SEdward Tomasz Napierala resource, action, buf, per); 3156ec54a57SEdward Tomasz Napierala 3166ec54a57SEdward Tomasz Napierala if (humanized == NULL) 3176ec54a57SEdward Tomasz Napierala err(1, "asprintf"); 3186ec54a57SEdward Tomasz Napierala 3196ec54a57SEdward Tomasz Napierala return (humanized); 3206ec54a57SEdward Tomasz Napierala } 3216ec54a57SEdward Tomasz Napierala 3226ec54a57SEdward Tomasz Napierala /* 3236ec54a57SEdward Tomasz Napierala * Print rules, one per line. 3246ec54a57SEdward Tomasz Napierala */ 3256ec54a57SEdward Tomasz Napierala static void 3266ec54a57SEdward Tomasz Napierala print_rules(char *rules, int hflag, int nflag) 3276ec54a57SEdward Tomasz Napierala { 3286ec54a57SEdward Tomasz Napierala char *rule; 3296ec54a57SEdward Tomasz Napierala 3306ec54a57SEdward Tomasz Napierala while ((rule = strsep(&rules, ",")) != NULL) { 3316ec54a57SEdward Tomasz Napierala if (rule[0] == '\0') 3326ec54a57SEdward Tomasz Napierala break; /* XXX */ 3336ec54a57SEdward Tomasz Napierala if (nflag == 0) 3346ec54a57SEdward Tomasz Napierala rule = humanize_ids(rule); 3356ec54a57SEdward Tomasz Napierala if (hflag) 3366ec54a57SEdward Tomasz Napierala rule = humanize_amount(rule); 3376ec54a57SEdward Tomasz Napierala printf("%s\n", rule); 3386ec54a57SEdward Tomasz Napierala } 3396ec54a57SEdward Tomasz Napierala } 3406ec54a57SEdward Tomasz Napierala 3416ec54a57SEdward Tomasz Napierala static void 342aae2a24bSEdward Tomasz Napierala enosys(void) 343aae2a24bSEdward Tomasz Napierala { 344aae2a24bSEdward Tomasz Napierala int error, racct_enable; 345aae2a24bSEdward Tomasz Napierala size_t racct_enable_len; 346aae2a24bSEdward Tomasz Napierala 347aae2a24bSEdward Tomasz Napierala racct_enable_len = sizeof(racct_enable); 348aae2a24bSEdward Tomasz Napierala error = sysctlbyname("kern.racct.enable", 349aae2a24bSEdward Tomasz Napierala &racct_enable, &racct_enable_len, NULL, 0); 350aae2a24bSEdward Tomasz Napierala 351aae2a24bSEdward Tomasz Napierala if (error != 0) { 352aae2a24bSEdward Tomasz Napierala if (errno == ENOENT) 35382224d7dSEdward Tomasz Napierala errx(1, "RACCT/RCTL support not present in kernel; see rctl(8) for details"); 354aae2a24bSEdward Tomasz Napierala 355aae2a24bSEdward Tomasz Napierala err(1, "sysctlbyname"); 356aae2a24bSEdward Tomasz Napierala } 357aae2a24bSEdward Tomasz Napierala 358aae2a24bSEdward Tomasz Napierala if (racct_enable == 0) 359aae2a24bSEdward Tomasz Napierala errx(1, "RACCT/RCTL present, but disabled; enable using kern.racct.enable=1 tunable"); 360aae2a24bSEdward Tomasz Napierala } 361aae2a24bSEdward Tomasz Napierala 362*0971623eSEdward Tomasz Napierala static int 363*0971623eSEdward Tomasz Napierala add_rule(const char *rule) 3646ec54a57SEdward Tomasz Napierala { 3656ec54a57SEdward Tomasz Napierala int error; 3666ec54a57SEdward Tomasz Napierala 3676ec54a57SEdward Tomasz Napierala error = rctl_add_rule(rule, strlen(rule) + 1, NULL, 0); 368aae2a24bSEdward Tomasz Napierala if (error != 0) { 369aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 370aae2a24bSEdward Tomasz Napierala enosys(); 371*0971623eSEdward Tomasz Napierala warn("rctl_add_rule"); 3726ec54a57SEdward Tomasz Napierala } 3736ec54a57SEdward Tomasz Napierala 374*0971623eSEdward Tomasz Napierala return (error); 375*0971623eSEdward Tomasz Napierala } 376*0971623eSEdward Tomasz Napierala 377*0971623eSEdward Tomasz Napierala static int 378*0971623eSEdward Tomasz Napierala show_limits(const char *filter, int hflag, int nflag) 3796ec54a57SEdward Tomasz Napierala { 3806ec54a57SEdward Tomasz Napierala int error; 3816ec54a57SEdward Tomasz Napierala char *outbuf = NULL; 3826ec54a57SEdward Tomasz Napierala size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4; 3836ec54a57SEdward Tomasz Napierala 3846ec54a57SEdward Tomasz Napierala do { 3856ec54a57SEdward Tomasz Napierala outbuflen *= 4; 3866ec54a57SEdward Tomasz Napierala outbuf = realloc(outbuf, outbuflen); 3876ec54a57SEdward Tomasz Napierala if (outbuf == NULL) 3886ec54a57SEdward Tomasz Napierala err(1, "realloc"); 3896ec54a57SEdward Tomasz Napierala 3906ec54a57SEdward Tomasz Napierala error = rctl_get_limits(filter, strlen(filter) + 1, outbuf, 3916ec54a57SEdward Tomasz Napierala outbuflen); 392aae2a24bSEdward Tomasz Napierala if (error && errno != ERANGE) { 393aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 394aae2a24bSEdward Tomasz Napierala enosys(); 395*0971623eSEdward Tomasz Napierala warn("rctl_get_limits"); 396aae2a24bSEdward Tomasz Napierala } 3976ec54a57SEdward Tomasz Napierala } while (error && errno == ERANGE); 3986ec54a57SEdward Tomasz Napierala 3996ec54a57SEdward Tomasz Napierala print_rules(outbuf, hflag, nflag); 4006ec54a57SEdward Tomasz Napierala free(outbuf); 401*0971623eSEdward Tomasz Napierala 402*0971623eSEdward Tomasz Napierala return (error); 4036ec54a57SEdward Tomasz Napierala } 4046ec54a57SEdward Tomasz Napierala 405*0971623eSEdward Tomasz Napierala static int 406*0971623eSEdward Tomasz Napierala remove_rule(const char *filter) 4076ec54a57SEdward Tomasz Napierala { 4086ec54a57SEdward Tomasz Napierala int error; 4096ec54a57SEdward Tomasz Napierala 4106ec54a57SEdward Tomasz Napierala error = rctl_remove_rule(filter, strlen(filter) + 1, NULL, 0); 411aae2a24bSEdward Tomasz Napierala if (error != 0) { 412aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 413aae2a24bSEdward Tomasz Napierala enosys(); 414*0971623eSEdward Tomasz Napierala warn("rctl_remove_rule"); 415aae2a24bSEdward Tomasz Napierala } 416*0971623eSEdward Tomasz Napierala 417*0971623eSEdward Tomasz Napierala return (error); 4186ec54a57SEdward Tomasz Napierala } 4196ec54a57SEdward Tomasz Napierala 4206ec54a57SEdward Tomasz Napierala static char * 4216ec54a57SEdward Tomasz Napierala humanize_usage_amount(char *usage) 4226ec54a57SEdward Tomasz Napierala { 4236ec54a57SEdward Tomasz Napierala int64_t num; 4246ec54a57SEdward Tomasz Napierala const char *resource, *amount; 4256ec54a57SEdward Tomasz Napierala char *copy, *humanized, buf[6]; 4266ec54a57SEdward Tomasz Napierala 4276ec54a57SEdward Tomasz Napierala copy = strdup(usage); 4286ec54a57SEdward Tomasz Napierala if (copy == NULL) 4296ec54a57SEdward Tomasz Napierala err(1, "strdup"); 4306ec54a57SEdward Tomasz Napierala 4316ec54a57SEdward Tomasz Napierala resource = strsep(©, "="); 4326ec54a57SEdward Tomasz Napierala amount = copy; 4336ec54a57SEdward Tomasz Napierala 4346ec54a57SEdward Tomasz Napierala assert(resource != NULL); 4356ec54a57SEdward Tomasz Napierala assert(amount != NULL); 4366ec54a57SEdward Tomasz Napierala 4376ec54a57SEdward Tomasz Napierala if (str2int64(amount, &num) != 0 || 4386ec54a57SEdward Tomasz Napierala humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE, 4396ec54a57SEdward Tomasz Napierala HN_DECIMAL | HN_NOSPACE) == -1) { 4406ec54a57SEdward Tomasz Napierala free(copy); 4416ec54a57SEdward Tomasz Napierala return (usage); 4426ec54a57SEdward Tomasz Napierala } 4436ec54a57SEdward Tomasz Napierala 4446ec54a57SEdward Tomasz Napierala asprintf(&humanized, "%s=%s", resource, buf); 4456ec54a57SEdward Tomasz Napierala if (humanized == NULL) 4466ec54a57SEdward Tomasz Napierala err(1, "asprintf"); 4476ec54a57SEdward Tomasz Napierala 4486ec54a57SEdward Tomasz Napierala return (humanized); 4496ec54a57SEdward Tomasz Napierala } 4506ec54a57SEdward Tomasz Napierala 4516ec54a57SEdward Tomasz Napierala /* 4526ec54a57SEdward Tomasz Napierala * Query the kernel about a resource usage and print it out. 4536ec54a57SEdward Tomasz Napierala */ 454*0971623eSEdward Tomasz Napierala static int 455*0971623eSEdward Tomasz Napierala show_usage(const char *filter, int hflag) 4566ec54a57SEdward Tomasz Napierala { 4576ec54a57SEdward Tomasz Napierala int error; 4586ec54a57SEdward Tomasz Napierala char *outbuf = NULL, *tmp; 4596ec54a57SEdward Tomasz Napierala size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4; 4606ec54a57SEdward Tomasz Napierala 4616ec54a57SEdward Tomasz Napierala do { 4626ec54a57SEdward Tomasz Napierala outbuflen *= 4; 4636ec54a57SEdward Tomasz Napierala outbuf = realloc(outbuf, outbuflen); 4646ec54a57SEdward Tomasz Napierala if (outbuf == NULL) 4656ec54a57SEdward Tomasz Napierala err(1, "realloc"); 4666ec54a57SEdward Tomasz Napierala 4676ec54a57SEdward Tomasz Napierala error = rctl_get_racct(filter, strlen(filter) + 1, outbuf, 4686ec54a57SEdward Tomasz Napierala outbuflen); 469aae2a24bSEdward Tomasz Napierala if (error && errno != ERANGE) { 470aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 471aae2a24bSEdward Tomasz Napierala enosys(); 472*0971623eSEdward Tomasz Napierala warn("rctl_get_racct"); 473aae2a24bSEdward Tomasz Napierala } 4746ec54a57SEdward Tomasz Napierala } while (error && errno == ERANGE); 4756ec54a57SEdward Tomasz Napierala 4766ec54a57SEdward Tomasz Napierala while ((tmp = strsep(&outbuf, ",")) != NULL) { 4776ec54a57SEdward Tomasz Napierala if (tmp[0] == '\0') 4786ec54a57SEdward Tomasz Napierala break; /* XXX */ 4796ec54a57SEdward Tomasz Napierala 4806ec54a57SEdward Tomasz Napierala if (hflag) 4816ec54a57SEdward Tomasz Napierala tmp = humanize_usage_amount(tmp); 4826ec54a57SEdward Tomasz Napierala 4836ec54a57SEdward Tomasz Napierala printf("%s\n", tmp); 4846ec54a57SEdward Tomasz Napierala } 4856ec54a57SEdward Tomasz Napierala 4866ec54a57SEdward Tomasz Napierala free(outbuf); 487*0971623eSEdward Tomasz Napierala 488*0971623eSEdward Tomasz Napierala return (error); 4896ec54a57SEdward Tomasz Napierala } 4906ec54a57SEdward Tomasz Napierala 4916ec54a57SEdward Tomasz Napierala /* 4926ec54a57SEdward Tomasz Napierala * Query the kernel about resource limit rules and print them out. 4936ec54a57SEdward Tomasz Napierala */ 494*0971623eSEdward Tomasz Napierala static int 495*0971623eSEdward Tomasz Napierala show_rules(const char *filter, int hflag, int nflag) 4966ec54a57SEdward Tomasz Napierala { 4976ec54a57SEdward Tomasz Napierala int error; 4986ec54a57SEdward Tomasz Napierala char *outbuf = NULL; 4996ec54a57SEdward Tomasz Napierala size_t filterlen, outbuflen = RCTL_DEFAULT_BUFSIZE / 4; 5006ec54a57SEdward Tomasz Napierala 5016ec54a57SEdward Tomasz Napierala if (filter != NULL) 5026ec54a57SEdward Tomasz Napierala filterlen = strlen(filter) + 1; 5036ec54a57SEdward Tomasz Napierala else 5046ec54a57SEdward Tomasz Napierala filterlen = 0; 5056ec54a57SEdward Tomasz Napierala 5066ec54a57SEdward Tomasz Napierala do { 5076ec54a57SEdward Tomasz Napierala outbuflen *= 4; 5086ec54a57SEdward Tomasz Napierala outbuf = realloc(outbuf, outbuflen); 5096ec54a57SEdward Tomasz Napierala if (outbuf == NULL) 5106ec54a57SEdward Tomasz Napierala err(1, "realloc"); 5116ec54a57SEdward Tomasz Napierala 5126ec54a57SEdward Tomasz Napierala error = rctl_get_rules(filter, filterlen, outbuf, outbuflen); 513aae2a24bSEdward Tomasz Napierala if (error && errno != ERANGE) { 514aae2a24bSEdward Tomasz Napierala if (errno == ENOSYS) 515aae2a24bSEdward Tomasz Napierala enosys(); 516*0971623eSEdward Tomasz Napierala warn("rctl_get_rules"); 517aae2a24bSEdward Tomasz Napierala } 5186ec54a57SEdward Tomasz Napierala } while (error && errno == ERANGE); 5196ec54a57SEdward Tomasz Napierala 5206ec54a57SEdward Tomasz Napierala print_rules(outbuf, hflag, nflag); 5216ec54a57SEdward Tomasz Napierala free(outbuf); 522*0971623eSEdward Tomasz Napierala 523*0971623eSEdward Tomasz Napierala return (error); 5246ec54a57SEdward Tomasz Napierala } 5256ec54a57SEdward Tomasz Napierala 5266ec54a57SEdward Tomasz Napierala static void 5276ec54a57SEdward Tomasz Napierala usage(void) 5286ec54a57SEdward Tomasz Napierala { 5296ec54a57SEdward Tomasz Napierala 5306ec54a57SEdward Tomasz Napierala fprintf(stderr, "usage: rctl [ -h ] [-a rule | -l filter | -r filter " 5316ec54a57SEdward Tomasz Napierala "| -u filter | filter]\n"); 5326ec54a57SEdward Tomasz Napierala exit(1); 5336ec54a57SEdward Tomasz Napierala } 5346ec54a57SEdward Tomasz Napierala 5356ec54a57SEdward Tomasz Napierala int 5366ec54a57SEdward Tomasz Napierala main(int argc __unused, char **argv __unused) 5376ec54a57SEdward Tomasz Napierala { 5386ec54a57SEdward Tomasz Napierala int ch, aflag = 0, hflag = 0, nflag = 0, lflag = 0, rflag = 0, 5396ec54a57SEdward Tomasz Napierala uflag = 0; 5406ec54a57SEdward Tomasz Napierala char *rule = NULL; 541*0971623eSEdward Tomasz Napierala int i, cumulated_error; 5426ec54a57SEdward Tomasz Napierala 543*0971623eSEdward Tomasz Napierala while ((ch = getopt(argc, argv, "ahlnru")) != -1) { 5446ec54a57SEdward Tomasz Napierala switch (ch) { 5456ec54a57SEdward Tomasz Napierala case 'a': 5466ec54a57SEdward Tomasz Napierala aflag = 1; 5476ec54a57SEdward Tomasz Napierala break; 5486ec54a57SEdward Tomasz Napierala case 'h': 5496ec54a57SEdward Tomasz Napierala hflag = 1; 5506ec54a57SEdward Tomasz Napierala break; 5516ec54a57SEdward Tomasz Napierala case 'l': 5526ec54a57SEdward Tomasz Napierala lflag = 1; 5536ec54a57SEdward Tomasz Napierala break; 5546ec54a57SEdward Tomasz Napierala case 'n': 5556ec54a57SEdward Tomasz Napierala nflag = 1; 5566ec54a57SEdward Tomasz Napierala break; 5576ec54a57SEdward Tomasz Napierala case 'r': 5586ec54a57SEdward Tomasz Napierala rflag = 1; 5596ec54a57SEdward Tomasz Napierala break; 5606ec54a57SEdward Tomasz Napierala case 'u': 5616ec54a57SEdward Tomasz Napierala uflag = 1; 5626ec54a57SEdward Tomasz Napierala break; 5636ec54a57SEdward Tomasz Napierala 5646ec54a57SEdward Tomasz Napierala case '?': 5656ec54a57SEdward Tomasz Napierala default: 5666ec54a57SEdward Tomasz Napierala usage(); 5676ec54a57SEdward Tomasz Napierala } 5686ec54a57SEdward Tomasz Napierala } 5696ec54a57SEdward Tomasz Napierala 5706ec54a57SEdward Tomasz Napierala argc -= optind; 5716ec54a57SEdward Tomasz Napierala argv += optind; 5726ec54a57SEdward Tomasz Napierala 573*0971623eSEdward Tomasz Napierala if (aflag + lflag + rflag + uflag > 1) 574*0971623eSEdward Tomasz Napierala errx(1, "at most one of -a, -l, -r, or -u may be specified"); 575*0971623eSEdward Tomasz Napierala 576*0971623eSEdward Tomasz Napierala if (argc == 0) { 577*0971623eSEdward Tomasz Napierala if (aflag + lflag + rflag + uflag == 0) { 578*0971623eSEdward Tomasz Napierala rule = strdup("::"); 579*0971623eSEdward Tomasz Napierala show_rules(rule, hflag, nflag); 580*0971623eSEdward Tomasz Napierala 581*0971623eSEdward Tomasz Napierala return (0); 582*0971623eSEdward Tomasz Napierala } 583*0971623eSEdward Tomasz Napierala 5846ec54a57SEdward Tomasz Napierala usage(); 585*0971623eSEdward Tomasz Napierala } 586*0971623eSEdward Tomasz Napierala 587*0971623eSEdward Tomasz Napierala cumulated_error = 0; 588*0971623eSEdward Tomasz Napierala 589*0971623eSEdward Tomasz Napierala for (i = 0; i < argc; i++) { 590*0971623eSEdward Tomasz Napierala rule = argv[i]; 591*0971623eSEdward Tomasz Napierala 592*0971623eSEdward Tomasz Napierala /* 593*0971623eSEdward Tomasz Napierala * Skip resolving if passed -n _and_ -a. Ignore -n otherwise, 594*0971623eSEdward Tomasz Napierala * so we can still do "rctl -n u:root" and see the rules without 595*0971623eSEdward Tomasz Napierala * resolving the UID. 596*0971623eSEdward Tomasz Napierala */ 597*0971623eSEdward Tomasz Napierala if (aflag != 0 && nflag != 0) 598*0971623eSEdward Tomasz Napierala rule = expand_rule(rule, false); 599*0971623eSEdward Tomasz Napierala else 600*0971623eSEdward Tomasz Napierala rule = expand_rule(rule, true); 6016ec54a57SEdward Tomasz Napierala 6026ec54a57SEdward Tomasz Napierala if (rule == NULL) { 603*0971623eSEdward Tomasz Napierala cumulated_error++; 604*0971623eSEdward Tomasz Napierala continue; 6056ec54a57SEdward Tomasz Napierala } 6066ec54a57SEdward Tomasz Napierala 6076ec54a57SEdward Tomasz Napierala if (aflag) { 608*0971623eSEdward Tomasz Napierala cumulated_error += add_rule(rule); 609*0971623eSEdward Tomasz Napierala } else if (lflag) { 610*0971623eSEdward Tomasz Napierala cumulated_error += show_limits(rule, hflag, nflag); 611*0971623eSEdward Tomasz Napierala } else if (rflag) { 612*0971623eSEdward Tomasz Napierala cumulated_error += remove_rule(rule); 613*0971623eSEdward Tomasz Napierala } else if (uflag) { 614*0971623eSEdward Tomasz Napierala cumulated_error += show_usage(rule, hflag); 615*0971623eSEdward Tomasz Napierala } else { 616*0971623eSEdward Tomasz Napierala cumulated_error += show_rules(rule, hflag, nflag); 6176ec54a57SEdward Tomasz Napierala } 6186ec54a57SEdward Tomasz Napierala 619*0971623eSEdward Tomasz Napierala free(rule); 6206ec54a57SEdward Tomasz Napierala } 6216ec54a57SEdward Tomasz Napierala 622*0971623eSEdward Tomasz Napierala return (cumulated_error); 6236ec54a57SEdward Tomasz Napierala } 624