xref: /freebsd/usr.bin/rctl/rctl.c (revision aae2a24b49e13a98e90d61f33cfe8fb908196cd0)
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>
37*aae2a24bSEdward 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>
466ec54a57SEdward Tomasz Napierala #include <stdint.h>
476ec54a57SEdward Tomasz Napierala #include <stdio.h>
486ec54a57SEdward Tomasz Napierala #include <stdlib.h>
496ec54a57SEdward Tomasz Napierala #include <string.h>
506ec54a57SEdward Tomasz Napierala 
516ec54a57SEdward Tomasz Napierala #define	RCTL_DEFAULT_BUFSIZE	4096
526ec54a57SEdward Tomasz Napierala 
536ec54a57SEdward Tomasz Napierala static id_t
546ec54a57SEdward Tomasz Napierala parse_user(const char *s)
556ec54a57SEdward Tomasz Napierala {
566ec54a57SEdward Tomasz Napierala 	id_t id;
576ec54a57SEdward Tomasz Napierala 	char *end;
586ec54a57SEdward Tomasz Napierala 	struct passwd *pwd;
596ec54a57SEdward Tomasz Napierala 
606ec54a57SEdward Tomasz Napierala 	pwd = getpwnam(s);
616ec54a57SEdward Tomasz Napierala 	if (pwd != NULL)
626ec54a57SEdward Tomasz Napierala 		return (pwd->pw_uid);
636ec54a57SEdward Tomasz Napierala 
646ec54a57SEdward Tomasz Napierala 	if (!isnumber(s[0]))
656ec54a57SEdward Tomasz Napierala 		errx(1, "uknown user '%s'", s);
666ec54a57SEdward Tomasz Napierala 
676ec54a57SEdward Tomasz Napierala 	id = strtod(s, &end);
686ec54a57SEdward Tomasz Napierala 	if ((size_t)(end - s) != strlen(s))
696ec54a57SEdward Tomasz Napierala 		errx(1, "trailing characters after numerical id");
706ec54a57SEdward Tomasz Napierala 
716ec54a57SEdward Tomasz Napierala 	return (id);
726ec54a57SEdward Tomasz Napierala }
736ec54a57SEdward Tomasz Napierala 
746ec54a57SEdward Tomasz Napierala static id_t
756ec54a57SEdward Tomasz Napierala parse_group(const char *s)
766ec54a57SEdward Tomasz Napierala {
776ec54a57SEdward Tomasz Napierala 	id_t id;
786ec54a57SEdward Tomasz Napierala 	char *end;
796ec54a57SEdward Tomasz Napierala 	struct group *grp;
806ec54a57SEdward Tomasz Napierala 
816ec54a57SEdward Tomasz Napierala 	grp = getgrnam(s);
826ec54a57SEdward Tomasz Napierala 	if (grp != NULL)
836ec54a57SEdward Tomasz Napierala 		return (grp->gr_gid);
846ec54a57SEdward Tomasz Napierala 
856ec54a57SEdward Tomasz Napierala 	if (!isnumber(s[0]))
866ec54a57SEdward Tomasz Napierala 		errx(1, "uknown group '%s'", s);
876ec54a57SEdward Tomasz Napierala 
886ec54a57SEdward Tomasz Napierala 	id = strtod(s, &end);
896ec54a57SEdward Tomasz Napierala 	if ((size_t)(end - s) != strlen(s))
906ec54a57SEdward Tomasz Napierala 		errx(1, "trailing characters after numerical id");
916ec54a57SEdward Tomasz Napierala 
926ec54a57SEdward Tomasz Napierala 	return (id);
936ec54a57SEdward Tomasz Napierala }
946ec54a57SEdward Tomasz Napierala 
956ec54a57SEdward Tomasz Napierala /*
966ec54a57SEdward Tomasz Napierala  * This routine replaces user/group name with numeric id.
976ec54a57SEdward Tomasz Napierala  */
986ec54a57SEdward Tomasz Napierala static char *
996ec54a57SEdward Tomasz Napierala resolve_ids(char *rule)
1006ec54a57SEdward Tomasz Napierala {
1016ec54a57SEdward Tomasz Napierala 	id_t id;
1026ec54a57SEdward Tomasz Napierala 	const char *subject, *textid, *rest;
1036ec54a57SEdward Tomasz Napierala 	char *resolved;
1046ec54a57SEdward Tomasz Napierala 
1056ec54a57SEdward Tomasz Napierala 	subject = strsep(&rule, ":");
1066ec54a57SEdward Tomasz Napierala 	textid = strsep(&rule, ":");
1076ec54a57SEdward Tomasz Napierala 	if (textid == NULL)
1086ec54a57SEdward Tomasz Napierala 		errx(1, "error in rule specification -- no subject");
1096ec54a57SEdward Tomasz Napierala 	if (rule != NULL)
1106ec54a57SEdward Tomasz Napierala 		rest = rule;
1116ec54a57SEdward Tomasz Napierala 	else
1126ec54a57SEdward Tomasz Napierala 		rest = "";
1136ec54a57SEdward Tomasz Napierala 
1146ec54a57SEdward Tomasz Napierala 	if (strcasecmp(subject, "u") == 0)
1156ec54a57SEdward Tomasz Napierala 		subject = "user";
1166ec54a57SEdward Tomasz Napierala 	else if (strcasecmp(subject, "g") == 0)
1176ec54a57SEdward Tomasz Napierala 		subject = "group";
1186ec54a57SEdward Tomasz Napierala 	else if (strcasecmp(subject, "p") == 0)
1196ec54a57SEdward Tomasz Napierala 		subject = "process";
1206ec54a57SEdward Tomasz Napierala 	else if (strcasecmp(subject, "l") == 0 ||
1216ec54a57SEdward Tomasz Napierala 	    strcasecmp(subject, "c") == 0 ||
1226ec54a57SEdward Tomasz Napierala 	    strcasecmp(subject, "class") == 0)
1236ec54a57SEdward Tomasz Napierala 		subject = "loginclass";
1246ec54a57SEdward Tomasz Napierala 	else if (strcasecmp(subject, "j") == 0)
1256ec54a57SEdward Tomasz Napierala 		subject = "jail";
1266ec54a57SEdward Tomasz Napierala 
1276ec54a57SEdward Tomasz Napierala 	if (strcasecmp(subject, "user") == 0 && strlen(textid) > 0) {
1286ec54a57SEdward Tomasz Napierala 		id = parse_user(textid);
1296ec54a57SEdward Tomasz Napierala 		asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
1306ec54a57SEdward Tomasz Napierala 	} else if (strcasecmp(subject, "group") == 0 && strlen(textid) > 0) {
1316ec54a57SEdward Tomasz Napierala 		id = parse_group(textid);
1326ec54a57SEdward Tomasz Napierala 		asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
1336ec54a57SEdward Tomasz Napierala 	} else
1346ec54a57SEdward Tomasz Napierala 		asprintf(&resolved, "%s:%s:%s", subject, textid, rest);
1356ec54a57SEdward Tomasz Napierala 
1366ec54a57SEdward Tomasz Napierala 	if (resolved == NULL)
1376ec54a57SEdward Tomasz Napierala 		err(1, "asprintf");
1386ec54a57SEdward Tomasz Napierala 
1396ec54a57SEdward Tomasz Napierala 	return (resolved);
1406ec54a57SEdward Tomasz Napierala }
1416ec54a57SEdward Tomasz Napierala 
1426ec54a57SEdward Tomasz Napierala /*
1436ec54a57SEdward Tomasz Napierala  * This routine replaces "human-readable" number with its expanded form.
1446ec54a57SEdward Tomasz Napierala  */
1456ec54a57SEdward Tomasz Napierala static char *
1466ec54a57SEdward Tomasz Napierala expand_amount(char *rule)
1476ec54a57SEdward Tomasz Napierala {
1486ec54a57SEdward Tomasz Napierala 	uint64_t num;
1496ec54a57SEdward Tomasz Napierala 	const char *subject, *subject_id, *resource, *action, *amount, *per;
1506ec54a57SEdward Tomasz Napierala 	char *copy, *expanded;
1516ec54a57SEdward Tomasz Napierala 
1526ec54a57SEdward Tomasz Napierala 	copy = strdup(rule);
1536ec54a57SEdward Tomasz Napierala 	if (copy == NULL)
1546ec54a57SEdward Tomasz Napierala 		err(1, "strdup");
1556ec54a57SEdward Tomasz Napierala 
1566ec54a57SEdward Tomasz Napierala 	subject = strsep(&copy, ":");
1576ec54a57SEdward Tomasz Napierala 	subject_id = strsep(&copy, ":");
1586ec54a57SEdward Tomasz Napierala 	resource = strsep(&copy, ":");
1596ec54a57SEdward Tomasz Napierala 	action = strsep(&copy, "=/");
1606ec54a57SEdward Tomasz Napierala 	amount = strsep(&copy, "/");
1616ec54a57SEdward Tomasz Napierala 	per = copy;
1626ec54a57SEdward Tomasz Napierala 
1636ec54a57SEdward Tomasz Napierala 	if (amount == NULL || strlen(amount) == 0) {
1646ec54a57SEdward Tomasz Napierala 		free(copy);
1656ec54a57SEdward Tomasz Napierala 		return (rule);
1666ec54a57SEdward Tomasz Napierala 	}
1676ec54a57SEdward Tomasz Napierala 
1686ec54a57SEdward Tomasz Napierala 	assert(subject != NULL);
1696ec54a57SEdward Tomasz Napierala 	assert(subject_id != NULL);
1706ec54a57SEdward Tomasz Napierala 	assert(resource != NULL);
1716ec54a57SEdward Tomasz Napierala 	assert(action != NULL);
1726ec54a57SEdward Tomasz Napierala 
1736ec54a57SEdward Tomasz Napierala 	if (expand_number(amount, &num))
1746ec54a57SEdward Tomasz Napierala 		err(1, "expand_number");
1756ec54a57SEdward Tomasz Napierala 
1766ec54a57SEdward Tomasz Napierala 	if (per == NULL)
1776ec54a57SEdward Tomasz Napierala 		asprintf(&expanded, "%s:%s:%s:%s=%ju", subject, subject_id,
1786ec54a57SEdward Tomasz Napierala 		    resource, action, (uintmax_t)num);
1796ec54a57SEdward Tomasz Napierala 	else
1806ec54a57SEdward Tomasz Napierala 		asprintf(&expanded, "%s:%s:%s:%s=%ju/%s", subject, subject_id,
1816ec54a57SEdward Tomasz Napierala 		    resource, action, (uintmax_t)num, per);
1826ec54a57SEdward Tomasz Napierala 
1836ec54a57SEdward Tomasz Napierala 	if (expanded == NULL)
1846ec54a57SEdward Tomasz Napierala 		err(1, "asprintf");
1856ec54a57SEdward Tomasz Napierala 
1866ec54a57SEdward Tomasz Napierala 	return (expanded);
1876ec54a57SEdward Tomasz Napierala }
1886ec54a57SEdward Tomasz Napierala 
1896ec54a57SEdward Tomasz Napierala static char *
1906ec54a57SEdward Tomasz Napierala humanize_ids(char *rule)
1916ec54a57SEdward Tomasz Napierala {
1926ec54a57SEdward Tomasz Napierala 	id_t id;
1936ec54a57SEdward Tomasz Napierala 	struct passwd *pwd;
1946ec54a57SEdward Tomasz Napierala 	struct group *grp;
1956ec54a57SEdward Tomasz Napierala 	const char *subject, *textid, *rest;
1966ec54a57SEdward Tomasz Napierala 	char *humanized;
1976ec54a57SEdward Tomasz Napierala 
1986ec54a57SEdward Tomasz Napierala 	subject = strsep(&rule, ":");
1996ec54a57SEdward Tomasz Napierala 	textid = strsep(&rule, ":");
2006ec54a57SEdward Tomasz Napierala 	if (textid == NULL)
2016ec54a57SEdward Tomasz Napierala 		errx(1, "rule passed from the kernel didn't contain subject");
2026ec54a57SEdward Tomasz Napierala 	if (rule != NULL)
2036ec54a57SEdward Tomasz Napierala 		rest = rule;
2046ec54a57SEdward Tomasz Napierala 	else
2056ec54a57SEdward Tomasz Napierala 		rest = "";
2066ec54a57SEdward Tomasz Napierala 
2076ec54a57SEdward Tomasz Napierala 	/* Replace numerical user and group ids with names. */
2086ec54a57SEdward Tomasz Napierala 	if (strcasecmp(subject, "user") == 0) {
2096ec54a57SEdward Tomasz Napierala 		id = parse_user(textid);
2106ec54a57SEdward Tomasz Napierala 		pwd = getpwuid(id);
2116ec54a57SEdward Tomasz Napierala 		if (pwd != NULL)
2126ec54a57SEdward Tomasz Napierala 			textid = pwd->pw_name;
2136ec54a57SEdward Tomasz Napierala 	} else if (strcasecmp(subject, "group") == 0) {
2146ec54a57SEdward Tomasz Napierala 		id = parse_group(textid);
2156ec54a57SEdward Tomasz Napierala 		grp = getgrgid(id);
2166ec54a57SEdward Tomasz Napierala 		if (grp != NULL)
2176ec54a57SEdward Tomasz Napierala 			textid = grp->gr_name;
2186ec54a57SEdward Tomasz Napierala 	}
2196ec54a57SEdward Tomasz Napierala 
2206ec54a57SEdward Tomasz Napierala 	asprintf(&humanized, "%s:%s:%s", subject, textid, rest);
2216ec54a57SEdward Tomasz Napierala 
2226ec54a57SEdward Tomasz Napierala 	if (humanized == NULL)
2236ec54a57SEdward Tomasz Napierala 		err(1, "asprintf");
2246ec54a57SEdward Tomasz Napierala 
2256ec54a57SEdward Tomasz Napierala 	return (humanized);
2266ec54a57SEdward Tomasz Napierala }
2276ec54a57SEdward Tomasz Napierala 
2286ec54a57SEdward Tomasz Napierala static int
2296ec54a57SEdward Tomasz Napierala str2int64(const char *str, int64_t *value)
2306ec54a57SEdward Tomasz Napierala {
2316ec54a57SEdward Tomasz Napierala 	char *end;
2326ec54a57SEdward Tomasz Napierala 
2336ec54a57SEdward Tomasz Napierala 	if (str == NULL)
2346ec54a57SEdward Tomasz Napierala 		return (EINVAL);
2356ec54a57SEdward Tomasz Napierala 
2366ec54a57SEdward Tomasz Napierala 	*value = strtoul(str, &end, 10);
2376ec54a57SEdward Tomasz Napierala 	if ((size_t)(end - str) != strlen(str))
2386ec54a57SEdward Tomasz Napierala 		return (EINVAL);
2396ec54a57SEdward Tomasz Napierala 
2406ec54a57SEdward Tomasz Napierala 	return (0);
2416ec54a57SEdward Tomasz Napierala }
2426ec54a57SEdward Tomasz Napierala 
2436ec54a57SEdward Tomasz Napierala static char *
2446ec54a57SEdward Tomasz Napierala humanize_amount(char *rule)
2456ec54a57SEdward Tomasz Napierala {
2466ec54a57SEdward Tomasz Napierala 	int64_t num;
2476ec54a57SEdward Tomasz Napierala 	const char *subject, *subject_id, *resource, *action, *amount, *per;
2486ec54a57SEdward Tomasz Napierala 	char *copy, *humanized, buf[6];
2496ec54a57SEdward Tomasz Napierala 
2506ec54a57SEdward Tomasz Napierala 	copy = strdup(rule);
2516ec54a57SEdward Tomasz Napierala 	if (copy == NULL)
2526ec54a57SEdward Tomasz Napierala 		err(1, "strdup");
2536ec54a57SEdward Tomasz Napierala 
2546ec54a57SEdward Tomasz Napierala 	subject = strsep(&copy, ":");
2556ec54a57SEdward Tomasz Napierala 	subject_id = strsep(&copy, ":");
2566ec54a57SEdward Tomasz Napierala 	resource = strsep(&copy, ":");
2576ec54a57SEdward Tomasz Napierala 	action = strsep(&copy, "=/");
2586ec54a57SEdward Tomasz Napierala 	amount = strsep(&copy, "/");
2596ec54a57SEdward Tomasz Napierala 	per = copy;
2606ec54a57SEdward Tomasz Napierala 
2616ec54a57SEdward Tomasz Napierala 	if (amount == NULL || strlen(amount) == 0 ||
2626ec54a57SEdward Tomasz Napierala 	    str2int64(amount, &num) != 0) {
2636ec54a57SEdward Tomasz Napierala 		free(copy);
2646ec54a57SEdward Tomasz Napierala 		return (rule);
2656ec54a57SEdward Tomasz Napierala 	}
2666ec54a57SEdward Tomasz Napierala 
2676ec54a57SEdward Tomasz Napierala 	assert(subject != NULL);
2686ec54a57SEdward Tomasz Napierala 	assert(subject_id != NULL);
2696ec54a57SEdward Tomasz Napierala 	assert(resource != NULL);
2706ec54a57SEdward Tomasz Napierala 	assert(action != NULL);
2716ec54a57SEdward Tomasz Napierala 
2726ec54a57SEdward Tomasz Napierala 	if (humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
2736ec54a57SEdward Tomasz Napierala 	    HN_DECIMAL | HN_NOSPACE) == -1)
2746ec54a57SEdward Tomasz Napierala 		err(1, "humanize_number");
2756ec54a57SEdward Tomasz Napierala 
2766ec54a57SEdward Tomasz Napierala 	if (per == NULL)
2776ec54a57SEdward Tomasz Napierala 		asprintf(&humanized, "%s:%s:%s:%s=%s", subject, subject_id,
2786ec54a57SEdward Tomasz Napierala 		    resource, action, buf);
2796ec54a57SEdward Tomasz Napierala 	else
2806ec54a57SEdward Tomasz Napierala 		asprintf(&humanized, "%s:%s:%s:%s=%s/%s", subject, subject_id,
2816ec54a57SEdward Tomasz Napierala 		    resource, action, buf, per);
2826ec54a57SEdward Tomasz Napierala 
2836ec54a57SEdward Tomasz Napierala 	if (humanized == NULL)
2846ec54a57SEdward Tomasz Napierala 		err(1, "asprintf");
2856ec54a57SEdward Tomasz Napierala 
2866ec54a57SEdward Tomasz Napierala 	return (humanized);
2876ec54a57SEdward Tomasz Napierala }
2886ec54a57SEdward Tomasz Napierala 
2896ec54a57SEdward Tomasz Napierala /*
2906ec54a57SEdward Tomasz Napierala  * Print rules, one per line.
2916ec54a57SEdward Tomasz Napierala  */
2926ec54a57SEdward Tomasz Napierala static void
2936ec54a57SEdward Tomasz Napierala print_rules(char *rules, int hflag, int nflag)
2946ec54a57SEdward Tomasz Napierala {
2956ec54a57SEdward Tomasz Napierala 	char *rule;
2966ec54a57SEdward Tomasz Napierala 
2976ec54a57SEdward Tomasz Napierala 	while ((rule = strsep(&rules, ",")) != NULL) {
2986ec54a57SEdward Tomasz Napierala 		if (rule[0] == '\0')
2996ec54a57SEdward Tomasz Napierala 			break; /* XXX */
3006ec54a57SEdward Tomasz Napierala 		if (nflag == 0)
3016ec54a57SEdward Tomasz Napierala 			rule = humanize_ids(rule);
3026ec54a57SEdward Tomasz Napierala 		if (hflag)
3036ec54a57SEdward Tomasz Napierala 			rule = humanize_amount(rule);
3046ec54a57SEdward Tomasz Napierala 		printf("%s\n", rule);
3056ec54a57SEdward Tomasz Napierala 	}
3066ec54a57SEdward Tomasz Napierala }
3076ec54a57SEdward Tomasz Napierala 
3086ec54a57SEdward Tomasz Napierala static void
309*aae2a24bSEdward Tomasz Napierala enosys(void)
310*aae2a24bSEdward Tomasz Napierala {
311*aae2a24bSEdward Tomasz Napierala 	int error, racct_enable;
312*aae2a24bSEdward Tomasz Napierala 	size_t racct_enable_len;
313*aae2a24bSEdward Tomasz Napierala 
314*aae2a24bSEdward Tomasz Napierala 	racct_enable_len = sizeof(racct_enable);
315*aae2a24bSEdward Tomasz Napierala 	error = sysctlbyname("kern.racct.enable",
316*aae2a24bSEdward Tomasz Napierala 	    &racct_enable, &racct_enable_len, NULL, 0);
317*aae2a24bSEdward Tomasz Napierala 
318*aae2a24bSEdward Tomasz Napierala 	if (error != 0) {
319*aae2a24bSEdward Tomasz Napierala 		if (errno == ENOENT)
320*aae2a24bSEdward Tomasz Napierala 			errx(1, "RACCT/RCTL support not present in kernel; see rctl(8) for details.");
321*aae2a24bSEdward Tomasz Napierala 
322*aae2a24bSEdward Tomasz Napierala 		err(1, "sysctlbyname");
323*aae2a24bSEdward Tomasz Napierala 	}
324*aae2a24bSEdward Tomasz Napierala 
325*aae2a24bSEdward Tomasz Napierala 	if (racct_enable == 0)
326*aae2a24bSEdward Tomasz Napierala 		errx(1, "RACCT/RCTL present, but disabled; enable using kern.racct.enable=1 tunable");
327*aae2a24bSEdward Tomasz Napierala }
328*aae2a24bSEdward Tomasz Napierala 
329*aae2a24bSEdward Tomasz Napierala static void
3306ec54a57SEdward Tomasz Napierala add_rule(char *rule)
3316ec54a57SEdward Tomasz Napierala {
3326ec54a57SEdward Tomasz Napierala 	int error;
3336ec54a57SEdward Tomasz Napierala 
3346ec54a57SEdward Tomasz Napierala 	error = rctl_add_rule(rule, strlen(rule) + 1, NULL, 0);
335*aae2a24bSEdward Tomasz Napierala 	if (error != 0) {
336*aae2a24bSEdward Tomasz Napierala 		if (errno == ENOSYS)
337*aae2a24bSEdward Tomasz Napierala 			enosys();
3386ec54a57SEdward Tomasz Napierala 		err(1, "rctl_add_rule");
339*aae2a24bSEdward Tomasz Napierala 	}
3406ec54a57SEdward Tomasz Napierala 	free(rule);
3416ec54a57SEdward Tomasz Napierala }
3426ec54a57SEdward Tomasz Napierala 
3436ec54a57SEdward Tomasz Napierala static void
3446ec54a57SEdward Tomasz Napierala show_limits(char *filter, int hflag, int nflag)
3456ec54a57SEdward Tomasz Napierala {
3466ec54a57SEdward Tomasz Napierala 	int error;
3476ec54a57SEdward Tomasz Napierala 	char *outbuf = NULL;
3486ec54a57SEdward Tomasz Napierala 	size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
3496ec54a57SEdward Tomasz Napierala 
3506ec54a57SEdward Tomasz Napierala 	do {
3516ec54a57SEdward Tomasz Napierala 		outbuflen *= 4;
3526ec54a57SEdward Tomasz Napierala 		outbuf = realloc(outbuf, outbuflen);
3536ec54a57SEdward Tomasz Napierala 		if (outbuf == NULL)
3546ec54a57SEdward Tomasz Napierala 			err(1, "realloc");
3556ec54a57SEdward Tomasz Napierala 
3566ec54a57SEdward Tomasz Napierala 		error = rctl_get_limits(filter, strlen(filter) + 1, outbuf,
3576ec54a57SEdward Tomasz Napierala 		    outbuflen);
358*aae2a24bSEdward Tomasz Napierala 		if (error && errno != ERANGE) {
359*aae2a24bSEdward Tomasz Napierala 			if (errno == ENOSYS)
360*aae2a24bSEdward Tomasz Napierala 				enosys();
3616ec54a57SEdward Tomasz Napierala 			err(1, "rctl_get_limits");
362*aae2a24bSEdward Tomasz Napierala 		}
3636ec54a57SEdward Tomasz Napierala 	} while (error && errno == ERANGE);
3646ec54a57SEdward Tomasz Napierala 
3656ec54a57SEdward Tomasz Napierala 	print_rules(outbuf, hflag, nflag);
3666ec54a57SEdward Tomasz Napierala 	free(filter);
3676ec54a57SEdward Tomasz Napierala 	free(outbuf);
3686ec54a57SEdward Tomasz Napierala }
3696ec54a57SEdward Tomasz Napierala 
3706ec54a57SEdward Tomasz Napierala static void
3716ec54a57SEdward Tomasz Napierala remove_rule(char *filter)
3726ec54a57SEdward Tomasz Napierala {
3736ec54a57SEdward Tomasz Napierala 	int error;
3746ec54a57SEdward Tomasz Napierala 
3756ec54a57SEdward Tomasz Napierala 	error = rctl_remove_rule(filter, strlen(filter) + 1, NULL, 0);
376*aae2a24bSEdward Tomasz Napierala 	if (error != 0) {
377*aae2a24bSEdward Tomasz Napierala 		if (errno == ENOSYS)
378*aae2a24bSEdward Tomasz Napierala 			enosys();
3796ec54a57SEdward Tomasz Napierala 		err(1, "rctl_remove_rule");
380*aae2a24bSEdward Tomasz Napierala 	}
3816ec54a57SEdward Tomasz Napierala 	free(filter);
3826ec54a57SEdward Tomasz Napierala }
3836ec54a57SEdward Tomasz Napierala 
3846ec54a57SEdward Tomasz Napierala static char *
3856ec54a57SEdward Tomasz Napierala humanize_usage_amount(char *usage)
3866ec54a57SEdward Tomasz Napierala {
3876ec54a57SEdward Tomasz Napierala 	int64_t num;
3886ec54a57SEdward Tomasz Napierala 	const char *resource, *amount;
3896ec54a57SEdward Tomasz Napierala 	char *copy, *humanized, buf[6];
3906ec54a57SEdward Tomasz Napierala 
3916ec54a57SEdward Tomasz Napierala 	copy = strdup(usage);
3926ec54a57SEdward Tomasz Napierala 	if (copy == NULL)
3936ec54a57SEdward Tomasz Napierala 		err(1, "strdup");
3946ec54a57SEdward Tomasz Napierala 
3956ec54a57SEdward Tomasz Napierala 	resource = strsep(&copy, "=");
3966ec54a57SEdward Tomasz Napierala 	amount = copy;
3976ec54a57SEdward Tomasz Napierala 
3986ec54a57SEdward Tomasz Napierala 	assert(resource != NULL);
3996ec54a57SEdward Tomasz Napierala 	assert(amount != NULL);
4006ec54a57SEdward Tomasz Napierala 
4016ec54a57SEdward Tomasz Napierala 	if (str2int64(amount, &num) != 0 ||
4026ec54a57SEdward Tomasz Napierala 	    humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
4036ec54a57SEdward Tomasz Napierala 	    HN_DECIMAL | HN_NOSPACE) == -1) {
4046ec54a57SEdward Tomasz Napierala 		free(copy);
4056ec54a57SEdward Tomasz Napierala 		return (usage);
4066ec54a57SEdward Tomasz Napierala 	}
4076ec54a57SEdward Tomasz Napierala 
4086ec54a57SEdward Tomasz Napierala 	asprintf(&humanized, "%s=%s", resource, buf);
4096ec54a57SEdward Tomasz Napierala 	if (humanized == NULL)
4106ec54a57SEdward Tomasz Napierala 		err(1, "asprintf");
4116ec54a57SEdward Tomasz Napierala 
4126ec54a57SEdward Tomasz Napierala 	return (humanized);
4136ec54a57SEdward Tomasz Napierala }
4146ec54a57SEdward Tomasz Napierala 
4156ec54a57SEdward Tomasz Napierala /*
4166ec54a57SEdward Tomasz Napierala  * Query the kernel about a resource usage and print it out.
4176ec54a57SEdward Tomasz Napierala  */
4186ec54a57SEdward Tomasz Napierala static void
4196ec54a57SEdward Tomasz Napierala show_usage(char *filter, int hflag)
4206ec54a57SEdward Tomasz Napierala {
4216ec54a57SEdward Tomasz Napierala 	int error;
4226ec54a57SEdward Tomasz Napierala 	char *outbuf = NULL, *tmp;
4236ec54a57SEdward Tomasz Napierala 	size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
4246ec54a57SEdward Tomasz Napierala 
4256ec54a57SEdward Tomasz Napierala 	do {
4266ec54a57SEdward Tomasz Napierala 		outbuflen *= 4;
4276ec54a57SEdward Tomasz Napierala 		outbuf = realloc(outbuf, outbuflen);
4286ec54a57SEdward Tomasz Napierala 		if (outbuf == NULL)
4296ec54a57SEdward Tomasz Napierala 			err(1, "realloc");
4306ec54a57SEdward Tomasz Napierala 
4316ec54a57SEdward Tomasz Napierala 		error = rctl_get_racct(filter, strlen(filter) + 1, outbuf,
4326ec54a57SEdward Tomasz Napierala 		    outbuflen);
433*aae2a24bSEdward Tomasz Napierala 		if (error && errno != ERANGE) {
434*aae2a24bSEdward Tomasz Napierala 			if (errno == ENOSYS)
435*aae2a24bSEdward Tomasz Napierala 				enosys();
4366ec54a57SEdward Tomasz Napierala 			err(1, "rctl_get_racct");
437*aae2a24bSEdward Tomasz Napierala 		}
4386ec54a57SEdward Tomasz Napierala 	} while (error && errno == ERANGE);
4396ec54a57SEdward Tomasz Napierala 
4406ec54a57SEdward Tomasz Napierala 	while ((tmp = strsep(&outbuf, ",")) != NULL) {
4416ec54a57SEdward Tomasz Napierala 		if (tmp[0] == '\0')
4426ec54a57SEdward Tomasz Napierala 			break; /* XXX */
4436ec54a57SEdward Tomasz Napierala 
4446ec54a57SEdward Tomasz Napierala 		if (hflag)
4456ec54a57SEdward Tomasz Napierala 			tmp = humanize_usage_amount(tmp);
4466ec54a57SEdward Tomasz Napierala 
4476ec54a57SEdward Tomasz Napierala 		printf("%s\n", tmp);
4486ec54a57SEdward Tomasz Napierala 	}
4496ec54a57SEdward Tomasz Napierala 
4506ec54a57SEdward Tomasz Napierala 	free(filter);
4516ec54a57SEdward Tomasz Napierala 	free(outbuf);
4526ec54a57SEdward Tomasz Napierala }
4536ec54a57SEdward Tomasz Napierala 
4546ec54a57SEdward Tomasz Napierala /*
4556ec54a57SEdward Tomasz Napierala  * Query the kernel about resource limit rules and print them out.
4566ec54a57SEdward Tomasz Napierala  */
4576ec54a57SEdward Tomasz Napierala static void
4586ec54a57SEdward Tomasz Napierala show_rules(char *filter, int hflag, int nflag)
4596ec54a57SEdward Tomasz Napierala {
4606ec54a57SEdward Tomasz Napierala 	int error;
4616ec54a57SEdward Tomasz Napierala 	char *outbuf = NULL;
4626ec54a57SEdward Tomasz Napierala 	size_t filterlen, outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
4636ec54a57SEdward Tomasz Napierala 
4646ec54a57SEdward Tomasz Napierala 	if (filter != NULL)
4656ec54a57SEdward Tomasz Napierala 		filterlen = strlen(filter) + 1;
4666ec54a57SEdward Tomasz Napierala 	else
4676ec54a57SEdward Tomasz Napierala 		filterlen = 0;
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_rules(filter, filterlen, outbuf, outbuflen);
476*aae2a24bSEdward Tomasz Napierala 		if (error && errno != ERANGE) {
477*aae2a24bSEdward Tomasz Napierala 			if (errno == ENOSYS)
478*aae2a24bSEdward Tomasz Napierala 				enosys();
4796ec54a57SEdward Tomasz Napierala 			err(1, "rctl_get_rules");
480*aae2a24bSEdward Tomasz Napierala 		}
4816ec54a57SEdward Tomasz Napierala 	} while (error && errno == ERANGE);
4826ec54a57SEdward Tomasz Napierala 
4836ec54a57SEdward Tomasz Napierala 	print_rules(outbuf, hflag, nflag);
4846ec54a57SEdward Tomasz Napierala 	free(outbuf);
4856ec54a57SEdward Tomasz Napierala }
4866ec54a57SEdward Tomasz Napierala 
4876ec54a57SEdward Tomasz Napierala static void
4886ec54a57SEdward Tomasz Napierala usage(void)
4896ec54a57SEdward Tomasz Napierala {
4906ec54a57SEdward Tomasz Napierala 
4916ec54a57SEdward Tomasz Napierala 	fprintf(stderr, "usage: rctl [ -h ] [-a rule | -l filter | -r filter "
4926ec54a57SEdward Tomasz Napierala 	    "| -u filter | filter]\n");
4936ec54a57SEdward Tomasz Napierala 	exit(1);
4946ec54a57SEdward Tomasz Napierala }
4956ec54a57SEdward Tomasz Napierala 
4966ec54a57SEdward Tomasz Napierala int
4976ec54a57SEdward Tomasz Napierala main(int argc __unused, char **argv __unused)
4986ec54a57SEdward Tomasz Napierala {
4996ec54a57SEdward Tomasz Napierala 	int ch, aflag = 0, hflag = 0, nflag = 0, lflag = 0, rflag = 0,
5006ec54a57SEdward Tomasz Napierala 	    uflag = 0;
5016ec54a57SEdward Tomasz Napierala 	char *rule = NULL;
5026ec54a57SEdward Tomasz Napierala 
5036ec54a57SEdward Tomasz Napierala 	while ((ch = getopt(argc, argv, "a:hl:nr:u:")) != -1) {
5046ec54a57SEdward Tomasz Napierala 		switch (ch) {
5056ec54a57SEdward Tomasz Napierala 		case 'a':
5066ec54a57SEdward Tomasz Napierala 			aflag = 1;
5076ec54a57SEdward Tomasz Napierala 			rule = strdup(optarg);
5086ec54a57SEdward Tomasz Napierala 			break;
5096ec54a57SEdward Tomasz Napierala 		case 'h':
5106ec54a57SEdward Tomasz Napierala 			hflag = 1;
5116ec54a57SEdward Tomasz Napierala 			break;
5126ec54a57SEdward Tomasz Napierala 		case 'l':
5136ec54a57SEdward Tomasz Napierala 			lflag = 1;
5146ec54a57SEdward Tomasz Napierala 			rule = strdup(optarg);
5156ec54a57SEdward Tomasz Napierala 			break;
5166ec54a57SEdward Tomasz Napierala 		case 'n':
5176ec54a57SEdward Tomasz Napierala 			nflag = 1;
5186ec54a57SEdward Tomasz Napierala 			break;
5196ec54a57SEdward Tomasz Napierala 		case 'r':
5206ec54a57SEdward Tomasz Napierala 			rflag = 1;
5216ec54a57SEdward Tomasz Napierala 			rule = strdup(optarg);
5226ec54a57SEdward Tomasz Napierala 			break;
5236ec54a57SEdward Tomasz Napierala 		case 'u':
5246ec54a57SEdward Tomasz Napierala 			uflag = 1;
5256ec54a57SEdward Tomasz Napierala 			rule = strdup(optarg);
5266ec54a57SEdward Tomasz Napierala 			break;
5276ec54a57SEdward Tomasz Napierala 
5286ec54a57SEdward Tomasz Napierala 		case '?':
5296ec54a57SEdward Tomasz Napierala 		default:
5306ec54a57SEdward Tomasz Napierala 			usage();
5316ec54a57SEdward Tomasz Napierala 		}
5326ec54a57SEdward Tomasz Napierala 	}
5336ec54a57SEdward Tomasz Napierala 
5346ec54a57SEdward Tomasz Napierala 	argc -= optind;
5356ec54a57SEdward Tomasz Napierala 	argv += optind;
5366ec54a57SEdward Tomasz Napierala 
5376ec54a57SEdward Tomasz Napierala 	if (argc > 1)
5386ec54a57SEdward Tomasz Napierala 		usage();
5396ec54a57SEdward Tomasz Napierala 
5406ec54a57SEdward Tomasz Napierala 	if (rule == NULL) {
5416ec54a57SEdward Tomasz Napierala 		if (argc == 1)
5426ec54a57SEdward Tomasz Napierala 			rule = strdup(argv[0]);
5436ec54a57SEdward Tomasz Napierala 		else
5446ec54a57SEdward Tomasz Napierala 			rule = strdup("::");
5456ec54a57SEdward Tomasz Napierala 	}
5466ec54a57SEdward Tomasz Napierala 
5476ec54a57SEdward Tomasz Napierala 	if (aflag + lflag + rflag + uflag + argc > 1)
5486ec54a57SEdward Tomasz Napierala 		errx(1, "only one flag or argument may be specified "
5496ec54a57SEdward Tomasz Napierala 		    "at the same time");
5506ec54a57SEdward Tomasz Napierala 
5516ec54a57SEdward Tomasz Napierala 	rule = resolve_ids(rule);
5526ec54a57SEdward Tomasz Napierala 	rule = expand_amount(rule);
5536ec54a57SEdward Tomasz Napierala 
5546ec54a57SEdward Tomasz Napierala 	if (aflag) {
5556ec54a57SEdward Tomasz Napierala 		add_rule(rule);
5566ec54a57SEdward Tomasz Napierala 		return (0);
5576ec54a57SEdward Tomasz Napierala 	}
5586ec54a57SEdward Tomasz Napierala 
5596ec54a57SEdward Tomasz Napierala 	if (lflag) {
5606ec54a57SEdward Tomasz Napierala 		show_limits(rule, hflag, nflag);
5616ec54a57SEdward Tomasz Napierala 		return (0);
5626ec54a57SEdward Tomasz Napierala 	}
5636ec54a57SEdward Tomasz Napierala 
5646ec54a57SEdward Tomasz Napierala 	if (rflag) {
5656ec54a57SEdward Tomasz Napierala 		remove_rule(rule);
5666ec54a57SEdward Tomasz Napierala 		return (0);
5676ec54a57SEdward Tomasz Napierala 	}
5686ec54a57SEdward Tomasz Napierala 
5696ec54a57SEdward Tomasz Napierala 	if (uflag) {
5706ec54a57SEdward Tomasz Napierala 		show_usage(rule, hflag);
5716ec54a57SEdward Tomasz Napierala 		return (0);
5726ec54a57SEdward Tomasz Napierala 	}
5736ec54a57SEdward Tomasz Napierala 
5746ec54a57SEdward Tomasz Napierala 	show_rules(rule, hflag, nflag);
5756ec54a57SEdward Tomasz Napierala 	return (0);
5766ec54a57SEdward Tomasz Napierala }
577