xref: /freebsd/usr.bin/rctl/rctl.c (revision 0971623e6193cdf5722cb22f2cd57457b5644140)
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(&copy, ":");
1236ec54a57SEdward Tomasz Napierala 	subject_id = strsep(&copy, ":");
1246ec54a57SEdward Tomasz Napierala 	resource = strsep(&copy, ":");
1256ec54a57SEdward Tomasz Napierala 	action = strsep(&copy, "=/");
1266ec54a57SEdward Tomasz Napierala 	amount = strsep(&copy, "/");
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(&copy, ":");
2886ec54a57SEdward Tomasz Napierala 	subject_id = strsep(&copy, ":");
2896ec54a57SEdward Tomasz Napierala 	resource = strsep(&copy, ":");
2906ec54a57SEdward Tomasz Napierala 	action = strsep(&copy, "=/");
2916ec54a57SEdward Tomasz Napierala 	amount = strsep(&copy, "/");
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(&copy, "=");
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