xref: /freebsd/usr.bin/rctl/rctl.c (revision 5e7a25553c4e4d585e09220da2b28f9d4eca6b3a)
16ec54a57SEdward Tomasz Napierala /*-
26ec54a57SEdward Tomasz Napierala  * Copyright (c) 2010 The FreeBSD Foundation
36ec54a57SEdward Tomasz Napierala  * All rights reserved.
46ec54a57SEdward Tomasz Napierala  *
56ec54a57SEdward Tomasz Napierala  * This software was developed by Edward Tomasz Napierala under sponsorship
66ec54a57SEdward Tomasz Napierala  * from the FreeBSD Foundation.
76ec54a57SEdward Tomasz Napierala  *
86ec54a57SEdward Tomasz Napierala  * Redistribution and use in source and binary forms, with or without
96ec54a57SEdward Tomasz Napierala  * modification, are permitted provided that the following conditions
106ec54a57SEdward Tomasz Napierala  * are met:
116ec54a57SEdward Tomasz Napierala  * 1. Redistributions of source code must retain the above copyright
126ec54a57SEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer.
136ec54a57SEdward Tomasz Napierala  * 2. Redistributions in binary form must reproduce the above copyright
146ec54a57SEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer in the
156ec54a57SEdward Tomasz Napierala  *    documentation and/or other materials provided with the distribution.
166ec54a57SEdward Tomasz Napierala  *
176ec54a57SEdward Tomasz Napierala  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
186ec54a57SEdward Tomasz Napierala  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
196ec54a57SEdward Tomasz Napierala  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
206ec54a57SEdward Tomasz Napierala  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
216ec54a57SEdward Tomasz Napierala  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
226ec54a57SEdward Tomasz Napierala  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
236ec54a57SEdward Tomasz Napierala  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
246ec54a57SEdward Tomasz Napierala  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
256ec54a57SEdward Tomasz Napierala  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
266ec54a57SEdward Tomasz Napierala  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
276ec54a57SEdward Tomasz Napierala  * SUCH DAMAGE.
286ec54a57SEdward Tomasz Napierala  *
296ec54a57SEdward Tomasz Napierala  * $FreeBSD$
306ec54a57SEdward Tomasz Napierala  */
316ec54a57SEdward Tomasz Napierala 
326ec54a57SEdward Tomasz Napierala #include <sys/cdefs.h>
336ec54a57SEdward Tomasz Napierala __FBSDID("$FreeBSD$");
346ec54a57SEdward Tomasz Napierala 
356ec54a57SEdward Tomasz Napierala #include <sys/types.h>
366ec54a57SEdward Tomasz Napierala #include <sys/rctl.h>
37aae2a24bSEdward Tomasz Napierala #include <sys/sysctl.h>
386ec54a57SEdward Tomasz Napierala #include <assert.h>
396ec54a57SEdward Tomasz Napierala #include <ctype.h>
406ec54a57SEdward Tomasz Napierala #include <err.h>
416ec54a57SEdward Tomasz Napierala #include <errno.h>
426ec54a57SEdward Tomasz Napierala #include <getopt.h>
436ec54a57SEdward Tomasz Napierala #include <grp.h>
446ec54a57SEdward Tomasz Napierala #include <libutil.h>
456ec54a57SEdward Tomasz Napierala #include <pwd.h>
460971623eSEdward Tomasz Napierala #include <stdbool.h>
476ec54a57SEdward Tomasz Napierala #include <stdint.h>
486ec54a57SEdward Tomasz Napierala #include <stdio.h>
496ec54a57SEdward Tomasz Napierala #include <stdlib.h>
506ec54a57SEdward Tomasz Napierala #include <string.h>
516ec54a57SEdward Tomasz Napierala 
5218e1f46eSEdward Tomasz Napierala #define	RCTL_DEFAULT_BUFSIZE	128 * 1024
536ec54a57SEdward Tomasz Napierala 
540971623eSEdward Tomasz Napierala static int
550971623eSEdward Tomasz Napierala parse_user(const char *s, id_t *uidp)
566ec54a57SEdward Tomasz Napierala {
576ec54a57SEdward Tomasz Napierala 	char *end;
586ec54a57SEdward Tomasz Napierala 	struct passwd *pwd;
596ec54a57SEdward Tomasz Napierala 
606ec54a57SEdward Tomasz Napierala 	pwd = getpwnam(s);
610971623eSEdward Tomasz Napierala 	if (pwd != NULL) {
620971623eSEdward Tomasz Napierala 		*uidp = pwd->pw_uid;
630971623eSEdward Tomasz Napierala 		return (0);
646ec54a57SEdward Tomasz Napierala 	}
656ec54a57SEdward Tomasz Napierala 
660971623eSEdward Tomasz Napierala 	if (!isnumber(s[0])) {
670971623eSEdward Tomasz Napierala 		warnx("uknown user '%s'", s);
680971623eSEdward Tomasz Napierala 		return (1);
690971623eSEdward Tomasz Napierala 	}
700971623eSEdward Tomasz Napierala 
710971623eSEdward Tomasz Napierala 	*uidp = strtod(s, &end);
720971623eSEdward Tomasz Napierala 	if ((size_t)(end - s) != strlen(s)) {
730971623eSEdward Tomasz Napierala 		warnx("trailing characters after numerical id");
740971623eSEdward Tomasz Napierala 		return (1);
750971623eSEdward Tomasz Napierala 	}
760971623eSEdward Tomasz Napierala 
770971623eSEdward Tomasz Napierala 	return (0);
780971623eSEdward Tomasz Napierala }
790971623eSEdward Tomasz Napierala 
800971623eSEdward Tomasz Napierala static int
810971623eSEdward Tomasz Napierala parse_group(const char *s, id_t *gidp)
826ec54a57SEdward Tomasz Napierala {
836ec54a57SEdward Tomasz Napierala 	char *end;
846ec54a57SEdward Tomasz Napierala 	struct group *grp;
856ec54a57SEdward Tomasz Napierala 
866ec54a57SEdward Tomasz Napierala 	grp = getgrnam(s);
870971623eSEdward Tomasz Napierala 	if (grp != NULL) {
880971623eSEdward Tomasz Napierala 		*gidp = grp->gr_gid;
890971623eSEdward Tomasz Napierala 		return (0);
900971623eSEdward Tomasz Napierala 	}
916ec54a57SEdward Tomasz Napierala 
920971623eSEdward Tomasz Napierala 	if (!isnumber(s[0])) {
930971623eSEdward Tomasz Napierala 		warnx("uknown group '%s'", s);
940971623eSEdward Tomasz Napierala 		return (1);
950971623eSEdward Tomasz Napierala 	}
966ec54a57SEdward Tomasz Napierala 
970971623eSEdward Tomasz Napierala 	*gidp = strtod(s, &end);
980971623eSEdward Tomasz Napierala 	if ((size_t)(end - s) != strlen(s)) {
990971623eSEdward Tomasz Napierala 		warnx("trailing characters after numerical id");
1000971623eSEdward Tomasz Napierala 		return (1);
1010971623eSEdward Tomasz Napierala 	}
1026ec54a57SEdward Tomasz Napierala 
1030971623eSEdward Tomasz Napierala 	return (0);
1046ec54a57SEdward Tomasz Napierala }
1056ec54a57SEdward Tomasz Napierala 
1066ec54a57SEdward Tomasz Napierala /*
1070971623eSEdward Tomasz Napierala  * Replace human-readable number with its expanded form.
1086ec54a57SEdward Tomasz Napierala  */
1096ec54a57SEdward Tomasz Napierala static char *
1106ec54a57SEdward Tomasz Napierala expand_amount(char *rule)
1116ec54a57SEdward Tomasz Napierala {
1126ec54a57SEdward Tomasz Napierala 	uint64_t num;
1136ec54a57SEdward Tomasz Napierala 	const char *subject, *subject_id, *resource, *action, *amount, *per;
114*5e7a2555SEdward Tomasz Napierala 	char *copy, *expanded, *tofree;
115f4e361a7SEdward Tomasz Napierala 	int ret;
1166ec54a57SEdward Tomasz Napierala 
117*5e7a2555SEdward Tomasz Napierala 	tofree = copy = strdup(rule);
1180971623eSEdward Tomasz Napierala 	if (copy == NULL) {
1190971623eSEdward Tomasz Napierala 		warn("strdup");
1200971623eSEdward Tomasz Napierala 		return (NULL);
1210971623eSEdward Tomasz Napierala 	}
1226ec54a57SEdward Tomasz Napierala 
1236ec54a57SEdward Tomasz Napierala 	subject = strsep(&copy, ":");
1246ec54a57SEdward Tomasz Napierala 	subject_id = strsep(&copy, ":");
1256ec54a57SEdward Tomasz Napierala 	resource = strsep(&copy, ":");
1266ec54a57SEdward Tomasz Napierala 	action = strsep(&copy, "=/");
1276ec54a57SEdward Tomasz Napierala 	amount = strsep(&copy, "/");
1286ec54a57SEdward Tomasz Napierala 	per = copy;
1296ec54a57SEdward Tomasz Napierala 
1306ec54a57SEdward Tomasz Napierala 	if (amount == NULL || strlen(amount) == 0) {
131*5e7a2555SEdward Tomasz Napierala 		free(tofree);
1326ec54a57SEdward Tomasz Napierala 		return (rule);
1336ec54a57SEdward Tomasz Napierala 	}
1346ec54a57SEdward Tomasz Napierala 
1356ec54a57SEdward Tomasz Napierala 	assert(subject != NULL);
1366ec54a57SEdward Tomasz Napierala 	assert(subject_id != NULL);
1376ec54a57SEdward Tomasz Napierala 	assert(resource != NULL);
1386ec54a57SEdward Tomasz Napierala 	assert(action != NULL);
1396ec54a57SEdward Tomasz Napierala 
1400971623eSEdward Tomasz Napierala 	if (expand_number(amount, &num)) {
1410971623eSEdward Tomasz Napierala 		warnx("invalid numeric value '%s'", amount);
142*5e7a2555SEdward Tomasz Napierala 		free(tofree);
1430971623eSEdward Tomasz Napierala 		return (NULL);
1440971623eSEdward Tomasz Napierala 	}
1456ec54a57SEdward Tomasz Napierala 
146f4e361a7SEdward Tomasz Napierala 	if (per == NULL) {
147f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&expanded, "%s:%s:%s:%s=%ju",
148f4e361a7SEdward Tomasz Napierala 		    subject, subject_id, resource, action, (uintmax_t)num);
149f4e361a7SEdward Tomasz Napierala 	} else {
150f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&expanded, "%s:%s:%s:%s=%ju/%s",
151f4e361a7SEdward Tomasz Napierala 		    subject, subject_id, resource, action, (uintmax_t)num, per);
152f4e361a7SEdward Tomasz Napierala 	}
1536ec54a57SEdward Tomasz Napierala 
154f4e361a7SEdward Tomasz Napierala 	if (ret <= 0) {
1550971623eSEdward Tomasz Napierala 		warn("asprintf");
156*5e7a2555SEdward Tomasz Napierala 		free(tofree);
1570971623eSEdward Tomasz Napierala 		return (NULL);
1580971623eSEdward Tomasz Napierala 	}
1596ec54a57SEdward Tomasz Napierala 
160*5e7a2555SEdward Tomasz Napierala 	free(tofree);
1616ec54a57SEdward Tomasz Napierala 	return (expanded);
1626ec54a57SEdward Tomasz Napierala }
1636ec54a57SEdward Tomasz Napierala 
1640971623eSEdward Tomasz Napierala 
1650971623eSEdward Tomasz Napierala static char *
1660971623eSEdward Tomasz Napierala expand_rule(char *rule, bool resolve_ids)
1670971623eSEdward Tomasz Napierala {
1680971623eSEdward Tomasz Napierala 	id_t id;
1690971623eSEdward Tomasz Napierala 	const char *subject, *textid, *rest;
1700971623eSEdward Tomasz Napierala 	char *resolved;
171f4e361a7SEdward Tomasz Napierala 	int error, ret;
1720971623eSEdward Tomasz Napierala 
1730971623eSEdward Tomasz Napierala 	subject = strsep(&rule, ":");
1740971623eSEdward Tomasz Napierala 	textid = strsep(&rule, ":");
1750971623eSEdward Tomasz Napierala 	if (textid == NULL) {
1760971623eSEdward Tomasz Napierala 		warnx("error in rule specification -- no subject");
1770971623eSEdward Tomasz Napierala 		return (NULL);
1780971623eSEdward Tomasz Napierala 	}
1790971623eSEdward Tomasz Napierala 	if (rule != NULL)
1800971623eSEdward Tomasz Napierala 		rest = rule;
1810971623eSEdward Tomasz Napierala 	else
1820971623eSEdward Tomasz Napierala 		rest = "";
1830971623eSEdward Tomasz Napierala 
1840971623eSEdward Tomasz Napierala 	if (strcasecmp(subject, "u") == 0)
1850971623eSEdward Tomasz Napierala 		subject = "user";
1860971623eSEdward Tomasz Napierala 	else if (strcasecmp(subject, "g") == 0)
1870971623eSEdward Tomasz Napierala 		subject = "group";
1880971623eSEdward Tomasz Napierala 	else if (strcasecmp(subject, "p") == 0)
1890971623eSEdward Tomasz Napierala 		subject = "process";
1900971623eSEdward Tomasz Napierala 	else if (strcasecmp(subject, "l") == 0 ||
1910971623eSEdward Tomasz Napierala 	    strcasecmp(subject, "c") == 0 ||
1920971623eSEdward Tomasz Napierala 	    strcasecmp(subject, "class") == 0)
1930971623eSEdward Tomasz Napierala 		subject = "loginclass";
1940971623eSEdward Tomasz Napierala 	else if (strcasecmp(subject, "j") == 0)
1950971623eSEdward Tomasz Napierala 		subject = "jail";
1960971623eSEdward Tomasz Napierala 
1970971623eSEdward Tomasz Napierala 	if (resolve_ids &&
1980971623eSEdward Tomasz Napierala 	    strcasecmp(subject, "user") == 0 && strlen(textid) > 0) {
1990971623eSEdward Tomasz Napierala 		error = parse_user(textid, &id);
2000971623eSEdward Tomasz Napierala 		if (error != 0)
2010971623eSEdward Tomasz Napierala 			return (NULL);
202f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
2030971623eSEdward Tomasz Napierala 	} else if (resolve_ids &&
2040971623eSEdward Tomasz Napierala 	    strcasecmp(subject, "group") == 0 && strlen(textid) > 0) {
2050971623eSEdward Tomasz Napierala 		error = parse_group(textid, &id);
2060971623eSEdward Tomasz Napierala 		if (error != 0)
2070971623eSEdward Tomasz Napierala 			return (NULL);
208f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
2090971623eSEdward Tomasz Napierala 	} else {
210f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&resolved, "%s:%s:%s", subject, textid, rest);
2110971623eSEdward Tomasz Napierala 	}
2120971623eSEdward Tomasz Napierala 
213f4e361a7SEdward Tomasz Napierala 	if (ret <= 0) {
2140971623eSEdward Tomasz Napierala 		warn("asprintf");
2150971623eSEdward Tomasz Napierala 		return (NULL);
2160971623eSEdward Tomasz Napierala 	}
2170971623eSEdward Tomasz Napierala 
2180971623eSEdward Tomasz Napierala 	return (expand_amount(resolved));
2190971623eSEdward Tomasz Napierala }
2200971623eSEdward Tomasz Napierala 
2216ec54a57SEdward Tomasz Napierala static char *
2226ec54a57SEdward Tomasz Napierala humanize_ids(char *rule)
2236ec54a57SEdward Tomasz Napierala {
2246ec54a57SEdward Tomasz Napierala 	id_t id;
2256ec54a57SEdward Tomasz Napierala 	struct passwd *pwd;
2266ec54a57SEdward Tomasz Napierala 	struct group *grp;
2276ec54a57SEdward Tomasz Napierala 	const char *subject, *textid, *rest;
228478f7a72SEdward Tomasz Napierala 	char *end, *humanized;
229f4e361a7SEdward Tomasz Napierala 	int ret;
2306ec54a57SEdward Tomasz Napierala 
2316ec54a57SEdward Tomasz Napierala 	subject = strsep(&rule, ":");
2326ec54a57SEdward Tomasz Napierala 	textid = strsep(&rule, ":");
2336ec54a57SEdward Tomasz Napierala 	if (textid == NULL)
2346ec54a57SEdward Tomasz Napierala 		errx(1, "rule passed from the kernel didn't contain subject");
2356ec54a57SEdward Tomasz Napierala 	if (rule != NULL)
2366ec54a57SEdward Tomasz Napierala 		rest = rule;
2376ec54a57SEdward Tomasz Napierala 	else
2386ec54a57SEdward Tomasz Napierala 		rest = "";
2396ec54a57SEdward Tomasz Napierala 
2406ec54a57SEdward Tomasz Napierala 	/* Replace numerical user and group ids with names. */
2416ec54a57SEdward Tomasz Napierala 	if (strcasecmp(subject, "user") == 0) {
242478f7a72SEdward Tomasz Napierala 		id = strtod(textid, &end);
243478f7a72SEdward Tomasz Napierala 		if ((size_t)(end - textid) != strlen(textid))
244478f7a72SEdward Tomasz Napierala 			errx(1, "malformed uid '%s'", textid);
2456ec54a57SEdward Tomasz Napierala 		pwd = getpwuid(id);
2466ec54a57SEdward Tomasz Napierala 		if (pwd != NULL)
2476ec54a57SEdward Tomasz Napierala 			textid = pwd->pw_name;
2486ec54a57SEdward Tomasz Napierala 	} else if (strcasecmp(subject, "group") == 0) {
249478f7a72SEdward Tomasz Napierala 		id = strtod(textid, &end);
250478f7a72SEdward Tomasz Napierala 		if ((size_t)(end - textid) != strlen(textid))
251478f7a72SEdward Tomasz Napierala 			errx(1, "malformed gid '%s'", textid);
2526ec54a57SEdward Tomasz Napierala 		grp = getgrgid(id);
2536ec54a57SEdward Tomasz Napierala 		if (grp != NULL)
2546ec54a57SEdward Tomasz Napierala 			textid = grp->gr_name;
2556ec54a57SEdward Tomasz Napierala 	}
2566ec54a57SEdward Tomasz Napierala 
257f4e361a7SEdward Tomasz Napierala 	ret = asprintf(&humanized, "%s:%s:%s", subject, textid, rest);
258f4e361a7SEdward Tomasz Napierala 	if (ret <= 0)
2596ec54a57SEdward Tomasz Napierala 		err(1, "asprintf");
2606ec54a57SEdward Tomasz Napierala 
2616ec54a57SEdward Tomasz Napierala 	return (humanized);
2626ec54a57SEdward Tomasz Napierala }
2636ec54a57SEdward Tomasz Napierala 
2646ec54a57SEdward Tomasz Napierala static int
2656ec54a57SEdward Tomasz Napierala str2int64(const char *str, int64_t *value)
2666ec54a57SEdward Tomasz Napierala {
2676ec54a57SEdward Tomasz Napierala 	char *end;
2686ec54a57SEdward Tomasz Napierala 
2696ec54a57SEdward Tomasz Napierala 	if (str == NULL)
2706ec54a57SEdward Tomasz Napierala 		return (EINVAL);
2716ec54a57SEdward Tomasz Napierala 
2726ec54a57SEdward Tomasz Napierala 	*value = strtoul(str, &end, 10);
2736ec54a57SEdward Tomasz Napierala 	if ((size_t)(end - str) != strlen(str))
2746ec54a57SEdward Tomasz Napierala 		return (EINVAL);
2756ec54a57SEdward Tomasz Napierala 
2766ec54a57SEdward Tomasz Napierala 	return (0);
2776ec54a57SEdward Tomasz Napierala }
2786ec54a57SEdward Tomasz Napierala 
2796ec54a57SEdward Tomasz Napierala static char *
2806ec54a57SEdward Tomasz Napierala humanize_amount(char *rule)
2816ec54a57SEdward Tomasz Napierala {
2826ec54a57SEdward Tomasz Napierala 	int64_t num;
2836ec54a57SEdward Tomasz Napierala 	const char *subject, *subject_id, *resource, *action, *amount, *per;
284*5e7a2555SEdward Tomasz Napierala 	char *copy, *humanized, buf[6], *tofree;
285f4e361a7SEdward Tomasz Napierala 	int ret;
2866ec54a57SEdward Tomasz Napierala 
287*5e7a2555SEdward Tomasz Napierala 	tofree = copy = strdup(rule);
2886ec54a57SEdward Tomasz Napierala 	if (copy == NULL)
2896ec54a57SEdward Tomasz Napierala 		err(1, "strdup");
2906ec54a57SEdward Tomasz Napierala 
2916ec54a57SEdward Tomasz Napierala 	subject = strsep(&copy, ":");
2926ec54a57SEdward Tomasz Napierala 	subject_id = strsep(&copy, ":");
2936ec54a57SEdward Tomasz Napierala 	resource = strsep(&copy, ":");
2946ec54a57SEdward Tomasz Napierala 	action = strsep(&copy, "=/");
2956ec54a57SEdward Tomasz Napierala 	amount = strsep(&copy, "/");
2966ec54a57SEdward Tomasz Napierala 	per = copy;
2976ec54a57SEdward Tomasz Napierala 
2986ec54a57SEdward Tomasz Napierala 	if (amount == NULL || strlen(amount) == 0 ||
2996ec54a57SEdward Tomasz Napierala 	    str2int64(amount, &num) != 0) {
300*5e7a2555SEdward Tomasz Napierala 		free(tofree);
3016ec54a57SEdward Tomasz Napierala 		return (rule);
3026ec54a57SEdward Tomasz Napierala 	}
3036ec54a57SEdward Tomasz Napierala 
3046ec54a57SEdward Tomasz Napierala 	assert(subject != NULL);
3056ec54a57SEdward Tomasz Napierala 	assert(subject_id != NULL);
3066ec54a57SEdward Tomasz Napierala 	assert(resource != NULL);
3076ec54a57SEdward Tomasz Napierala 	assert(action != NULL);
3086ec54a57SEdward Tomasz Napierala 
3096ec54a57SEdward Tomasz Napierala 	if (humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
3106ec54a57SEdward Tomasz Napierala 	    HN_DECIMAL | HN_NOSPACE) == -1)
3116ec54a57SEdward Tomasz Napierala 		err(1, "humanize_number");
3126ec54a57SEdward Tomasz Napierala 
313f4e361a7SEdward Tomasz Napierala 	if (per == NULL) {
314f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&humanized, "%s:%s:%s:%s=%s",
315f4e361a7SEdward Tomasz Napierala 		    subject, subject_id, resource, action, buf);
316f4e361a7SEdward Tomasz Napierala 	} else {
317f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&humanized, "%s:%s:%s:%s=%s/%s",
318f4e361a7SEdward Tomasz Napierala 		    subject, subject_id, resource, action, buf, per);
319f4e361a7SEdward Tomasz Napierala 	}
3206ec54a57SEdward Tomasz Napierala 
321f4e361a7SEdward Tomasz Napierala 	if (ret <= 0)
3226ec54a57SEdward Tomasz Napierala 		err(1, "asprintf");
3236ec54a57SEdward Tomasz Napierala 
324*5e7a2555SEdward Tomasz Napierala 	free(tofree);
3256ec54a57SEdward Tomasz Napierala 	return (humanized);
3266ec54a57SEdward Tomasz Napierala }
3276ec54a57SEdward Tomasz Napierala 
3286ec54a57SEdward Tomasz Napierala /*
3296ec54a57SEdward Tomasz Napierala  * Print rules, one per line.
3306ec54a57SEdward Tomasz Napierala  */
3316ec54a57SEdward Tomasz Napierala static void
3326ec54a57SEdward Tomasz Napierala print_rules(char *rules, int hflag, int nflag)
3336ec54a57SEdward Tomasz Napierala {
3346ec54a57SEdward Tomasz Napierala 	char *rule;
3356ec54a57SEdward Tomasz Napierala 
3366ec54a57SEdward Tomasz Napierala 	while ((rule = strsep(&rules, ",")) != NULL) {
3376ec54a57SEdward Tomasz Napierala 		if (rule[0] == '\0')
3386ec54a57SEdward Tomasz Napierala 			break; /* XXX */
3396ec54a57SEdward Tomasz Napierala 		if (nflag == 0)
3406ec54a57SEdward Tomasz Napierala 			rule = humanize_ids(rule);
3416ec54a57SEdward Tomasz Napierala 		if (hflag)
3426ec54a57SEdward Tomasz Napierala 			rule = humanize_amount(rule);
3436ec54a57SEdward Tomasz Napierala 		printf("%s\n", rule);
3446ec54a57SEdward Tomasz Napierala 	}
3456ec54a57SEdward Tomasz Napierala }
3466ec54a57SEdward Tomasz Napierala 
3476ec54a57SEdward Tomasz Napierala static void
348aae2a24bSEdward Tomasz Napierala enosys(void)
349aae2a24bSEdward Tomasz Napierala {
350aae2a24bSEdward Tomasz Napierala 	int error, racct_enable;
351aae2a24bSEdward Tomasz Napierala 	size_t racct_enable_len;
352aae2a24bSEdward Tomasz Napierala 
353aae2a24bSEdward Tomasz Napierala 	racct_enable_len = sizeof(racct_enable);
354aae2a24bSEdward Tomasz Napierala 	error = sysctlbyname("kern.racct.enable",
355aae2a24bSEdward Tomasz Napierala 	    &racct_enable, &racct_enable_len, NULL, 0);
356aae2a24bSEdward Tomasz Napierala 
357aae2a24bSEdward Tomasz Napierala 	if (error != 0) {
358aae2a24bSEdward Tomasz Napierala 		if (errno == ENOENT)
35982224d7dSEdward Tomasz Napierala 			errx(1, "RACCT/RCTL support not present in kernel; see rctl(8) for details");
360aae2a24bSEdward Tomasz Napierala 
361aae2a24bSEdward Tomasz Napierala 		err(1, "sysctlbyname");
362aae2a24bSEdward Tomasz Napierala 	}
363aae2a24bSEdward Tomasz Napierala 
364aae2a24bSEdward Tomasz Napierala 	if (racct_enable == 0)
365aae2a24bSEdward Tomasz Napierala 		errx(1, "RACCT/RCTL present, but disabled; enable using kern.racct.enable=1 tunable");
366aae2a24bSEdward Tomasz Napierala }
367aae2a24bSEdward Tomasz Napierala 
3680971623eSEdward Tomasz Napierala static int
3690971623eSEdward Tomasz Napierala add_rule(const char *rule)
3706ec54a57SEdward Tomasz Napierala {
3716ec54a57SEdward Tomasz Napierala 	int error;
3726ec54a57SEdward Tomasz Napierala 
3736ec54a57SEdward Tomasz Napierala 	error = rctl_add_rule(rule, strlen(rule) + 1, NULL, 0);
374aae2a24bSEdward Tomasz Napierala 	if (error != 0) {
375aae2a24bSEdward Tomasz Napierala 		if (errno == ENOSYS)
376aae2a24bSEdward Tomasz Napierala 			enosys();
3770971623eSEdward Tomasz Napierala 		warn("rctl_add_rule");
3786ec54a57SEdward Tomasz Napierala 	}
3796ec54a57SEdward Tomasz Napierala 
3800971623eSEdward Tomasz Napierala 	return (error);
3810971623eSEdward Tomasz Napierala }
3820971623eSEdward Tomasz Napierala 
3830971623eSEdward Tomasz Napierala static int
3840971623eSEdward Tomasz Napierala show_limits(const char *filter, int hflag, int nflag)
3856ec54a57SEdward Tomasz Napierala {
3866ec54a57SEdward Tomasz Napierala 	int error;
3876ec54a57SEdward Tomasz Napierala 	char *outbuf = NULL;
3886ec54a57SEdward Tomasz Napierala 	size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
3896ec54a57SEdward Tomasz Napierala 
3906ec54a57SEdward Tomasz Napierala 	do {
3916ec54a57SEdward Tomasz Napierala 		outbuflen *= 4;
3926ec54a57SEdward Tomasz Napierala 		outbuf = realloc(outbuf, outbuflen);
3936ec54a57SEdward Tomasz Napierala 		if (outbuf == NULL)
3946ec54a57SEdward Tomasz Napierala 			err(1, "realloc");
3956ec54a57SEdward Tomasz Napierala 
3966ec54a57SEdward Tomasz Napierala 		error = rctl_get_limits(filter, strlen(filter) + 1, outbuf,
3976ec54a57SEdward Tomasz Napierala 		    outbuflen);
398aae2a24bSEdward Tomasz Napierala 		if (error && errno != ERANGE) {
399aae2a24bSEdward Tomasz Napierala 			if (errno == ENOSYS)
400aae2a24bSEdward Tomasz Napierala 				enosys();
4010971623eSEdward Tomasz Napierala 			warn("rctl_get_limits");
402aae2a24bSEdward Tomasz Napierala 		}
4036ec54a57SEdward Tomasz Napierala 	} while (error && errno == ERANGE);
4046ec54a57SEdward Tomasz Napierala 
4056ec54a57SEdward Tomasz Napierala 	print_rules(outbuf, hflag, nflag);
4066ec54a57SEdward Tomasz Napierala 	free(outbuf);
4070971623eSEdward Tomasz Napierala 
4080971623eSEdward Tomasz Napierala 	return (error);
4096ec54a57SEdward Tomasz Napierala }
4106ec54a57SEdward Tomasz Napierala 
4110971623eSEdward Tomasz Napierala static int
4120971623eSEdward Tomasz Napierala remove_rule(const char *filter)
4136ec54a57SEdward Tomasz Napierala {
4146ec54a57SEdward Tomasz Napierala 	int error;
4156ec54a57SEdward Tomasz Napierala 
4166ec54a57SEdward Tomasz Napierala 	error = rctl_remove_rule(filter, strlen(filter) + 1, NULL, 0);
417aae2a24bSEdward Tomasz Napierala 	if (error != 0) {
418aae2a24bSEdward Tomasz Napierala 		if (errno == ENOSYS)
419aae2a24bSEdward Tomasz Napierala 			enosys();
4200971623eSEdward Tomasz Napierala 		warn("rctl_remove_rule");
421aae2a24bSEdward Tomasz Napierala 	}
4220971623eSEdward Tomasz Napierala 
4230971623eSEdward Tomasz Napierala 	return (error);
4246ec54a57SEdward Tomasz Napierala }
4256ec54a57SEdward Tomasz Napierala 
4266ec54a57SEdward Tomasz Napierala static char *
4276ec54a57SEdward Tomasz Napierala humanize_usage_amount(char *usage)
4286ec54a57SEdward Tomasz Napierala {
4296ec54a57SEdward Tomasz Napierala 	int64_t num;
4306ec54a57SEdward Tomasz Napierala 	const char *resource, *amount;
431*5e7a2555SEdward Tomasz Napierala 	char *copy, *humanized, buf[6], *tofree;
432f4e361a7SEdward Tomasz Napierala 	int ret;
4336ec54a57SEdward Tomasz Napierala 
434*5e7a2555SEdward Tomasz Napierala 	tofree = copy = strdup(usage);
4356ec54a57SEdward Tomasz Napierala 	if (copy == NULL)
4366ec54a57SEdward Tomasz Napierala 		err(1, "strdup");
4376ec54a57SEdward Tomasz Napierala 
4386ec54a57SEdward Tomasz Napierala 	resource = strsep(&copy, "=");
4396ec54a57SEdward Tomasz Napierala 	amount = copy;
4406ec54a57SEdward Tomasz Napierala 
4416ec54a57SEdward Tomasz Napierala 	assert(resource != NULL);
4426ec54a57SEdward Tomasz Napierala 	assert(amount != NULL);
4436ec54a57SEdward Tomasz Napierala 
4446ec54a57SEdward Tomasz Napierala 	if (str2int64(amount, &num) != 0 ||
4456ec54a57SEdward Tomasz Napierala 	    humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
4466ec54a57SEdward Tomasz Napierala 	    HN_DECIMAL | HN_NOSPACE) == -1) {
447*5e7a2555SEdward Tomasz Napierala 		free(tofree);
4486ec54a57SEdward Tomasz Napierala 		return (usage);
4496ec54a57SEdward Tomasz Napierala 	}
4506ec54a57SEdward Tomasz Napierala 
451f4e361a7SEdward Tomasz Napierala 	ret = asprintf(&humanized, "%s=%s", resource, buf);
452f4e361a7SEdward Tomasz Napierala 	if (ret <= 0)
4536ec54a57SEdward Tomasz Napierala 		err(1, "asprintf");
4546ec54a57SEdward Tomasz Napierala 
455*5e7a2555SEdward Tomasz Napierala 	free(tofree);
4566ec54a57SEdward Tomasz Napierala 	return (humanized);
4576ec54a57SEdward Tomasz Napierala }
4586ec54a57SEdward Tomasz Napierala 
4596ec54a57SEdward Tomasz Napierala /*
4606ec54a57SEdward Tomasz Napierala  * Query the kernel about a resource usage and print it out.
4616ec54a57SEdward Tomasz Napierala  */
4620971623eSEdward Tomasz Napierala static int
4630971623eSEdward Tomasz Napierala show_usage(const char *filter, int hflag)
4646ec54a57SEdward Tomasz Napierala {
4656ec54a57SEdward Tomasz Napierala 	int error;
466*5e7a2555SEdward Tomasz Napierala 	char *copy, *outbuf = NULL, *tmp;
4676ec54a57SEdward Tomasz Napierala 	size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
4686ec54a57SEdward Tomasz Napierala 
4696ec54a57SEdward Tomasz Napierala 	do {
4706ec54a57SEdward Tomasz Napierala 		outbuflen *= 4;
4716ec54a57SEdward Tomasz Napierala 		outbuf = realloc(outbuf, outbuflen);
4726ec54a57SEdward Tomasz Napierala 		if (outbuf == NULL)
4736ec54a57SEdward Tomasz Napierala 			err(1, "realloc");
4746ec54a57SEdward Tomasz Napierala 
4756ec54a57SEdward Tomasz Napierala 		error = rctl_get_racct(filter, strlen(filter) + 1, outbuf,
4766ec54a57SEdward Tomasz Napierala 		    outbuflen);
477aae2a24bSEdward Tomasz Napierala 		if (error && errno != ERANGE) {
478aae2a24bSEdward Tomasz Napierala 			if (errno == ENOSYS)
479aae2a24bSEdward Tomasz Napierala 				enosys();
4800971623eSEdward Tomasz Napierala 			warn("rctl_get_racct");
481aae2a24bSEdward Tomasz Napierala 		}
4826ec54a57SEdward Tomasz Napierala 	} while (error && errno == ERANGE);
4836ec54a57SEdward Tomasz Napierala 
484*5e7a2555SEdward Tomasz Napierala 	copy = outbuf;
485*5e7a2555SEdward Tomasz Napierala 	while ((tmp = strsep(&copy, ",")) != NULL) {
4866ec54a57SEdward Tomasz Napierala 		if (tmp[0] == '\0')
4876ec54a57SEdward Tomasz Napierala 			break; /* XXX */
4886ec54a57SEdward Tomasz Napierala 
4896ec54a57SEdward Tomasz Napierala 		if (hflag)
4906ec54a57SEdward Tomasz Napierala 			tmp = humanize_usage_amount(tmp);
4916ec54a57SEdward Tomasz Napierala 
4926ec54a57SEdward Tomasz Napierala 		printf("%s\n", tmp);
4936ec54a57SEdward Tomasz Napierala 	}
4946ec54a57SEdward Tomasz Napierala 
4956ec54a57SEdward Tomasz Napierala 	free(outbuf);
4960971623eSEdward Tomasz Napierala 
4970971623eSEdward Tomasz Napierala 	return (error);
4986ec54a57SEdward Tomasz Napierala }
4996ec54a57SEdward Tomasz Napierala 
5006ec54a57SEdward Tomasz Napierala /*
5016ec54a57SEdward Tomasz Napierala  * Query the kernel about resource limit rules and print them out.
5026ec54a57SEdward Tomasz Napierala  */
5030971623eSEdward Tomasz Napierala static int
5040971623eSEdward Tomasz Napierala show_rules(const char *filter, int hflag, int nflag)
5056ec54a57SEdward Tomasz Napierala {
5066ec54a57SEdward Tomasz Napierala 	int error;
5076ec54a57SEdward Tomasz Napierala 	char *outbuf = NULL;
5086ec54a57SEdward Tomasz Napierala 	size_t filterlen, outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
5096ec54a57SEdward Tomasz Napierala 
5106ec54a57SEdward Tomasz Napierala 	if (filter != NULL)
5116ec54a57SEdward Tomasz Napierala 		filterlen = strlen(filter) + 1;
5126ec54a57SEdward Tomasz Napierala 	else
5136ec54a57SEdward Tomasz Napierala 		filterlen = 0;
5146ec54a57SEdward Tomasz Napierala 
5156ec54a57SEdward Tomasz Napierala 	do {
5166ec54a57SEdward Tomasz Napierala 		outbuflen *= 4;
5176ec54a57SEdward Tomasz Napierala 		outbuf = realloc(outbuf, outbuflen);
5186ec54a57SEdward Tomasz Napierala 		if (outbuf == NULL)
5196ec54a57SEdward Tomasz Napierala 			err(1, "realloc");
5206ec54a57SEdward Tomasz Napierala 
5216ec54a57SEdward Tomasz Napierala 		error = rctl_get_rules(filter, filterlen, outbuf, outbuflen);
522aae2a24bSEdward Tomasz Napierala 		if (error && errno != ERANGE) {
523aae2a24bSEdward Tomasz Napierala 			if (errno == ENOSYS)
524aae2a24bSEdward Tomasz Napierala 				enosys();
5250971623eSEdward Tomasz Napierala 			warn("rctl_get_rules");
526aae2a24bSEdward Tomasz Napierala 		}
5276ec54a57SEdward Tomasz Napierala 	} while (error && errno == ERANGE);
5286ec54a57SEdward Tomasz Napierala 
5296ec54a57SEdward Tomasz Napierala 	print_rules(outbuf, hflag, nflag);
5306ec54a57SEdward Tomasz Napierala 	free(outbuf);
5310971623eSEdward Tomasz Napierala 
5320971623eSEdward Tomasz Napierala 	return (error);
5336ec54a57SEdward Tomasz Napierala }
5346ec54a57SEdward Tomasz Napierala 
5356ec54a57SEdward Tomasz Napierala static void
5366ec54a57SEdward Tomasz Napierala usage(void)
5376ec54a57SEdward Tomasz Napierala {
5386ec54a57SEdward Tomasz Napierala 
5396ec54a57SEdward Tomasz Napierala 	fprintf(stderr, "usage: rctl [ -h ] [-a rule | -l filter | -r filter "
5406ec54a57SEdward Tomasz Napierala 	    "| -u filter | filter]\n");
5416ec54a57SEdward Tomasz Napierala 	exit(1);
5426ec54a57SEdward Tomasz Napierala }
5436ec54a57SEdward Tomasz Napierala 
5446ec54a57SEdward Tomasz Napierala int
5456ec54a57SEdward Tomasz Napierala main(int argc __unused, char **argv __unused)
5466ec54a57SEdward Tomasz Napierala {
5476ec54a57SEdward Tomasz Napierala 	int ch, aflag = 0, hflag = 0, nflag = 0, lflag = 0, rflag = 0,
5486ec54a57SEdward Tomasz Napierala 	    uflag = 0;
5496ec54a57SEdward Tomasz Napierala 	char *rule = NULL;
5500971623eSEdward Tomasz Napierala 	int i, cumulated_error;
5516ec54a57SEdward Tomasz Napierala 
5520971623eSEdward Tomasz Napierala 	while ((ch = getopt(argc, argv, "ahlnru")) != -1) {
5536ec54a57SEdward Tomasz Napierala 		switch (ch) {
5546ec54a57SEdward Tomasz Napierala 		case 'a':
5556ec54a57SEdward Tomasz Napierala 			aflag = 1;
5566ec54a57SEdward Tomasz Napierala 			break;
5576ec54a57SEdward Tomasz Napierala 		case 'h':
5586ec54a57SEdward Tomasz Napierala 			hflag = 1;
5596ec54a57SEdward Tomasz Napierala 			break;
5606ec54a57SEdward Tomasz Napierala 		case 'l':
5616ec54a57SEdward Tomasz Napierala 			lflag = 1;
5626ec54a57SEdward Tomasz Napierala 			break;
5636ec54a57SEdward Tomasz Napierala 		case 'n':
5646ec54a57SEdward Tomasz Napierala 			nflag = 1;
5656ec54a57SEdward Tomasz Napierala 			break;
5666ec54a57SEdward Tomasz Napierala 		case 'r':
5676ec54a57SEdward Tomasz Napierala 			rflag = 1;
5686ec54a57SEdward Tomasz Napierala 			break;
5696ec54a57SEdward Tomasz Napierala 		case 'u':
5706ec54a57SEdward Tomasz Napierala 			uflag = 1;
5716ec54a57SEdward Tomasz Napierala 			break;
5726ec54a57SEdward Tomasz Napierala 
5736ec54a57SEdward Tomasz Napierala 		case '?':
5746ec54a57SEdward Tomasz Napierala 		default:
5756ec54a57SEdward Tomasz Napierala 			usage();
5766ec54a57SEdward Tomasz Napierala 		}
5776ec54a57SEdward Tomasz Napierala 	}
5786ec54a57SEdward Tomasz Napierala 
5796ec54a57SEdward Tomasz Napierala 	argc -= optind;
5806ec54a57SEdward Tomasz Napierala 	argv += optind;
5816ec54a57SEdward Tomasz Napierala 
5820971623eSEdward Tomasz Napierala 	if (aflag + lflag + rflag + uflag > 1)
5830971623eSEdward Tomasz Napierala 		errx(1, "at most one of -a, -l, -r, or -u may be specified");
5840971623eSEdward Tomasz Napierala 
5850971623eSEdward Tomasz Napierala 	if (argc == 0) {
5860971623eSEdward Tomasz Napierala 		if (aflag + lflag + rflag + uflag == 0) {
5870971623eSEdward Tomasz Napierala 			rule = strdup("::");
5880971623eSEdward Tomasz Napierala 			show_rules(rule, hflag, nflag);
5890971623eSEdward Tomasz Napierala 
5900971623eSEdward Tomasz Napierala 			return (0);
5910971623eSEdward Tomasz Napierala 		}
5920971623eSEdward Tomasz Napierala 
5936ec54a57SEdward Tomasz Napierala 		usage();
5940971623eSEdward Tomasz Napierala 	}
5950971623eSEdward Tomasz Napierala 
5960971623eSEdward Tomasz Napierala 	cumulated_error = 0;
5970971623eSEdward Tomasz Napierala 
5980971623eSEdward Tomasz Napierala 	for (i = 0; i < argc; i++) {
5990971623eSEdward Tomasz Napierala 		rule = argv[i];
6000971623eSEdward Tomasz Napierala 
6010971623eSEdward Tomasz Napierala 		/*
6020971623eSEdward Tomasz Napierala 		 * Skip resolving if passed -n _and_ -a.  Ignore -n otherwise,
6030971623eSEdward Tomasz Napierala 		 * so we can still do "rctl -n u:root" and see the rules without
6040971623eSEdward Tomasz Napierala 		 * resolving the UID.
6050971623eSEdward Tomasz Napierala 		 */
6060971623eSEdward Tomasz Napierala 		if (aflag != 0 && nflag != 0)
6070971623eSEdward Tomasz Napierala 			rule = expand_rule(rule, false);
6080971623eSEdward Tomasz Napierala 		else
6090971623eSEdward Tomasz Napierala 			rule = expand_rule(rule, true);
6106ec54a57SEdward Tomasz Napierala 
6116ec54a57SEdward Tomasz Napierala 		if (rule == NULL) {
6120971623eSEdward Tomasz Napierala 			cumulated_error++;
6130971623eSEdward Tomasz Napierala 			continue;
6146ec54a57SEdward Tomasz Napierala 		}
6156ec54a57SEdward Tomasz Napierala 
6166ec54a57SEdward Tomasz Napierala 		if (aflag) {
6170971623eSEdward Tomasz Napierala 			cumulated_error += add_rule(rule);
6180971623eSEdward Tomasz Napierala 		} else if (lflag) {
6190971623eSEdward Tomasz Napierala 			cumulated_error += show_limits(rule, hflag, nflag);
6200971623eSEdward Tomasz Napierala 		} else if (rflag) {
6210971623eSEdward Tomasz Napierala 			cumulated_error += remove_rule(rule);
6220971623eSEdward Tomasz Napierala 		} else if (uflag) {
6230971623eSEdward Tomasz Napierala 			cumulated_error += show_usage(rule, hflag);
6240971623eSEdward Tomasz Napierala 		} else  {
6250971623eSEdward Tomasz Napierala 			cumulated_error += show_rules(rule, hflag, nflag);
6266ec54a57SEdward Tomasz Napierala 		}
6276ec54a57SEdward Tomasz Napierala 
6280971623eSEdward Tomasz Napierala 		free(rule);
6296ec54a57SEdward Tomasz Napierala 	}
6306ec54a57SEdward Tomasz Napierala 
6310971623eSEdward Tomasz Napierala 	return (cumulated_error);
6326ec54a57SEdward Tomasz Napierala }
633