xref: /freebsd/usr.bin/rctl/rctl.c (revision 5e3934b15a2741b2de6b217e77dc9d798d740804)
16ec54a57SEdward Tomasz Napierala /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
46ec54a57SEdward Tomasz Napierala  * Copyright (c) 2010 The FreeBSD Foundation
56ec54a57SEdward Tomasz Napierala  *
66ec54a57SEdward Tomasz Napierala  * This software was developed by Edward Tomasz Napierala under sponsorship
76ec54a57SEdward Tomasz Napierala  * from the FreeBSD Foundation.
86ec54a57SEdward Tomasz Napierala  *
96ec54a57SEdward Tomasz Napierala  * Redistribution and use in source and binary forms, with or without
106ec54a57SEdward Tomasz Napierala  * modification, are permitted provided that the following conditions
116ec54a57SEdward Tomasz Napierala  * are met:
126ec54a57SEdward Tomasz Napierala  * 1. Redistributions of source code must retain the above copyright
136ec54a57SEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer.
146ec54a57SEdward Tomasz Napierala  * 2. Redistributions in binary form must reproduce the above copyright
156ec54a57SEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer in the
166ec54a57SEdward Tomasz Napierala  *    documentation and/or other materials provided with the distribution.
176ec54a57SEdward Tomasz Napierala  *
186ec54a57SEdward Tomasz Napierala  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
196ec54a57SEdward Tomasz Napierala  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
206ec54a57SEdward Tomasz Napierala  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
216ec54a57SEdward Tomasz Napierala  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
226ec54a57SEdward Tomasz Napierala  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
236ec54a57SEdward Tomasz Napierala  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
246ec54a57SEdward Tomasz Napierala  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
256ec54a57SEdward Tomasz Napierala  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
266ec54a57SEdward Tomasz Napierala  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
276ec54a57SEdward Tomasz Napierala  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
286ec54a57SEdward Tomasz Napierala  * SUCH DAMAGE.
296ec54a57SEdward Tomasz Napierala  */
306ec54a57SEdward Tomasz Napierala 
316ec54a57SEdward Tomasz Napierala #include <sys/types.h>
326ec54a57SEdward Tomasz Napierala #include <sys/rctl.h>
33aae2a24bSEdward Tomasz Napierala #include <sys/sysctl.h>
346ec54a57SEdward Tomasz Napierala #include <assert.h>
356ec54a57SEdward Tomasz Napierala #include <ctype.h>
366ec54a57SEdward Tomasz Napierala #include <err.h>
376ec54a57SEdward Tomasz Napierala #include <errno.h>
386ec54a57SEdward Tomasz Napierala #include <getopt.h>
396ec54a57SEdward Tomasz Napierala #include <grp.h>
406ec54a57SEdward Tomasz Napierala #include <libutil.h>
416ec54a57SEdward Tomasz Napierala #include <pwd.h>
420971623eSEdward Tomasz Napierala #include <stdbool.h>
436ec54a57SEdward Tomasz Napierala #include <stdint.h>
446ec54a57SEdward Tomasz Napierala #include <stdio.h>
456ec54a57SEdward Tomasz Napierala #include <stdlib.h>
466ec54a57SEdward Tomasz Napierala #include <string.h>
476ec54a57SEdward Tomasz Napierala 
4818e1f46eSEdward Tomasz Napierala #define	RCTL_DEFAULT_BUFSIZE	128 * 1024
496ec54a57SEdward Tomasz Napierala 
500971623eSEdward Tomasz Napierala static int
parse_user(const char * s,id_t * uidp,const char * unexpanded_rule)515c859bfbSEdward Tomasz Napierala parse_user(const char *s, id_t *uidp, const char *unexpanded_rule)
526ec54a57SEdward Tomasz Napierala {
536ec54a57SEdward Tomasz Napierala 	char *end;
546ec54a57SEdward Tomasz Napierala 	struct passwd *pwd;
556ec54a57SEdward Tomasz Napierala 
566ec54a57SEdward Tomasz Napierala 	pwd = getpwnam(s);
570971623eSEdward Tomasz Napierala 	if (pwd != NULL) {
580971623eSEdward Tomasz Napierala 		*uidp = pwd->pw_uid;
590971623eSEdward Tomasz Napierala 		return (0);
606ec54a57SEdward Tomasz Napierala 	}
616ec54a57SEdward Tomasz Napierala 
620971623eSEdward Tomasz Napierala 	if (!isnumber(s[0])) {
63c281fdb1SEnji Cooper 		warnx("malformed rule '%s': unknown user '%s'",
645c859bfbSEdward Tomasz Napierala 		    unexpanded_rule, s);
650971623eSEdward Tomasz Napierala 		return (1);
660971623eSEdward Tomasz Napierala 	}
670971623eSEdward Tomasz Napierala 
680971623eSEdward Tomasz Napierala 	*uidp = strtod(s, &end);
690971623eSEdward Tomasz Napierala 	if ((size_t)(end - s) != strlen(s)) {
705c859bfbSEdward Tomasz Napierala 		warnx("malformed rule '%s': trailing characters "
715c859bfbSEdward Tomasz Napierala 		    "after numerical id", unexpanded_rule);
720971623eSEdward Tomasz Napierala 		return (1);
730971623eSEdward Tomasz Napierala 	}
740971623eSEdward Tomasz Napierala 
750971623eSEdward Tomasz Napierala 	return (0);
760971623eSEdward Tomasz Napierala }
770971623eSEdward Tomasz Napierala 
780971623eSEdward Tomasz Napierala static int
parse_group(const char * s,id_t * gidp,const char * unexpanded_rule)795c859bfbSEdward Tomasz Napierala parse_group(const char *s, id_t *gidp, const char *unexpanded_rule)
806ec54a57SEdward Tomasz Napierala {
816ec54a57SEdward Tomasz Napierala 	char *end;
826ec54a57SEdward Tomasz Napierala 	struct group *grp;
836ec54a57SEdward Tomasz Napierala 
846ec54a57SEdward Tomasz Napierala 	grp = getgrnam(s);
850971623eSEdward Tomasz Napierala 	if (grp != NULL) {
860971623eSEdward Tomasz Napierala 		*gidp = grp->gr_gid;
870971623eSEdward Tomasz Napierala 		return (0);
880971623eSEdward Tomasz Napierala 	}
896ec54a57SEdward Tomasz Napierala 
900971623eSEdward Tomasz Napierala 	if (!isnumber(s[0])) {
91c281fdb1SEnji Cooper 		warnx("malformed rule '%s': unknown group '%s'",
925c859bfbSEdward Tomasz Napierala 		    unexpanded_rule, s);
930971623eSEdward Tomasz Napierala 		return (1);
940971623eSEdward Tomasz Napierala 	}
956ec54a57SEdward Tomasz Napierala 
960971623eSEdward Tomasz Napierala 	*gidp = strtod(s, &end);
970971623eSEdward Tomasz Napierala 	if ((size_t)(end - s) != strlen(s)) {
985c859bfbSEdward Tomasz Napierala 		warnx("malformed rule '%s': trailing characters "
995c859bfbSEdward Tomasz Napierala 		    "after numerical id", unexpanded_rule);
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 *
expand_amount(const char * rule,const char * unexpanded_rule)110508744b2SEdward Tomasz Napierala expand_amount(const char *rule, const char *unexpanded_rule)
1116ec54a57SEdward Tomasz Napierala {
1126ec54a57SEdward Tomasz Napierala 	uint64_t num;
1136ec54a57SEdward Tomasz Napierala 	const char *subject, *subject_id, *resource, *action, *amount, *per;
114508744b2SEdward Tomasz Napierala 	char *copy, *expanded, *tofree;
115f4e361a7SEdward Tomasz Napierala 	int ret;
1166ec54a57SEdward Tomasz Napierala 
117508744b2SEdward Tomasz Napierala 	tofree = copy = strdup(rule);
118508744b2SEdward Tomasz Napierala 	if (copy == NULL) {
119508744b2SEdward Tomasz Napierala 		warn("strdup");
120508744b2SEdward Tomasz Napierala 		return (NULL);
121508744b2SEdward Tomasz Napierala 	}
1226ec54a57SEdward Tomasz Napierala 
123508744b2SEdward Tomasz Napierala 	subject = strsep(&copy, ":");
124508744b2SEdward Tomasz Napierala 	subject_id = strsep(&copy, ":");
125508744b2SEdward Tomasz Napierala 	resource = strsep(&copy, ":");
126508744b2SEdward Tomasz Napierala 	action = strsep(&copy, "=/");
127508744b2SEdward Tomasz Napierala 	amount = strsep(&copy, "/");
128508744b2SEdward Tomasz Napierala 	per = copy;
129508744b2SEdward Tomasz Napierala 
130508744b2SEdward Tomasz Napierala 	if (amount == NULL || strlen(amount) == 0) {
131508744b2SEdward Tomasz Napierala 		/*
132508744b2SEdward Tomasz Napierala 		 * The "copy" has already been tinkered with by strsep().
133508744b2SEdward Tomasz Napierala 		 */
134508744b2SEdward Tomasz Napierala 		free(tofree);
135508744b2SEdward Tomasz Napierala 		copy = strdup(rule);
136508744b2SEdward Tomasz Napierala 		if (copy == NULL) {
137508744b2SEdward Tomasz Napierala 			warn("strdup");
138508744b2SEdward Tomasz Napierala 			return (NULL);
139508744b2SEdward Tomasz Napierala 		}
140508744b2SEdward Tomasz Napierala 		return (copy);
141508744b2SEdward Tomasz Napierala 	}
1426ec54a57SEdward Tomasz Napierala 
1436ec54a57SEdward Tomasz Napierala 	assert(subject != NULL);
1446ec54a57SEdward Tomasz Napierala 	assert(subject_id != NULL);
1456ec54a57SEdward Tomasz Napierala 	assert(resource != NULL);
1466ec54a57SEdward Tomasz Napierala 	assert(action != NULL);
1476ec54a57SEdward Tomasz Napierala 
1480971623eSEdward Tomasz Napierala 	if (expand_number(amount, &num)) {
1495c859bfbSEdward Tomasz Napierala 		warnx("malformed rule '%s': invalid numeric value '%s'",
1505c859bfbSEdward Tomasz Napierala 		    unexpanded_rule, amount);
151508744b2SEdward Tomasz Napierala 		free(tofree);
1520971623eSEdward Tomasz Napierala 		return (NULL);
1530971623eSEdward Tomasz Napierala 	}
1546ec54a57SEdward Tomasz Napierala 
155f4e361a7SEdward Tomasz Napierala 	if (per == NULL) {
156f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&expanded, "%s:%s:%s:%s=%ju",
157f4e361a7SEdward Tomasz Napierala 		    subject, subject_id, resource, action, (uintmax_t)num);
158f4e361a7SEdward Tomasz Napierala 	} else {
159f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&expanded, "%s:%s:%s:%s=%ju/%s",
160f4e361a7SEdward Tomasz Napierala 		    subject, subject_id, resource, action, (uintmax_t)num, per);
161f4e361a7SEdward Tomasz Napierala 	}
1626ec54a57SEdward Tomasz Napierala 
163f4e361a7SEdward Tomasz Napierala 	if (ret <= 0) {
1640971623eSEdward Tomasz Napierala 		warn("asprintf");
165508744b2SEdward Tomasz Napierala 		free(tofree);
1660971623eSEdward Tomasz Napierala 		return (NULL);
1670971623eSEdward Tomasz Napierala 	}
1686ec54a57SEdward Tomasz Napierala 
169508744b2SEdward Tomasz Napierala 	free(tofree);
170508744b2SEdward Tomasz Napierala 
1716ec54a57SEdward Tomasz Napierala 	return (expanded);
1726ec54a57SEdward Tomasz Napierala }
1736ec54a57SEdward Tomasz Napierala 
1740971623eSEdward Tomasz Napierala static char *
expand_rule(const char * rule,bool resolve_ids)1755c859bfbSEdward Tomasz Napierala expand_rule(const char *rule, bool resolve_ids)
1760971623eSEdward Tomasz Napierala {
1770971623eSEdward Tomasz Napierala 	id_t id;
1780971623eSEdward Tomasz Napierala 	const char *subject, *textid, *rest;
1795c859bfbSEdward Tomasz Napierala 	char *copy, *expanded, *resolved, *tofree;
180f4e361a7SEdward Tomasz Napierala 	int error, ret;
1810971623eSEdward Tomasz Napierala 
1825c859bfbSEdward Tomasz Napierala 	tofree = copy = strdup(rule);
1835c859bfbSEdward Tomasz Napierala 	if (copy == NULL) {
1845c859bfbSEdward Tomasz Napierala 		warn("strdup");
1850971623eSEdward Tomasz Napierala 		return (NULL);
1860971623eSEdward Tomasz Napierala 	}
1875c859bfbSEdward Tomasz Napierala 
1885c859bfbSEdward Tomasz Napierala 	subject = strsep(&copy, ":");
1895c859bfbSEdward Tomasz Napierala 	textid = strsep(&copy, ":");
1905c859bfbSEdward Tomasz Napierala 	if (textid == NULL) {
1915c859bfbSEdward Tomasz Napierala 		warnx("malformed rule '%s': missing subject", rule);
1925c859bfbSEdward Tomasz Napierala 		return (NULL);
1935c859bfbSEdward Tomasz Napierala 	}
1945c859bfbSEdward Tomasz Napierala 	if (copy != NULL)
1955c859bfbSEdward Tomasz Napierala 		rest = copy;
1960971623eSEdward Tomasz Napierala 	else
1970971623eSEdward Tomasz Napierala 		rest = "";
1980971623eSEdward Tomasz Napierala 
1990971623eSEdward Tomasz Napierala 	if (strcasecmp(subject, "u") == 0)
2000971623eSEdward Tomasz Napierala 		subject = "user";
2010971623eSEdward Tomasz Napierala 	else if (strcasecmp(subject, "g") == 0)
2020971623eSEdward Tomasz Napierala 		subject = "group";
2030971623eSEdward Tomasz Napierala 	else if (strcasecmp(subject, "p") == 0)
2040971623eSEdward Tomasz Napierala 		subject = "process";
2050971623eSEdward Tomasz Napierala 	else if (strcasecmp(subject, "l") == 0 ||
2060971623eSEdward Tomasz Napierala 	    strcasecmp(subject, "c") == 0 ||
2070971623eSEdward Tomasz Napierala 	    strcasecmp(subject, "class") == 0)
2080971623eSEdward Tomasz Napierala 		subject = "loginclass";
2090971623eSEdward Tomasz Napierala 	else if (strcasecmp(subject, "j") == 0)
2100971623eSEdward Tomasz Napierala 		subject = "jail";
2110971623eSEdward Tomasz Napierala 
2120971623eSEdward Tomasz Napierala 	if (resolve_ids &&
2130971623eSEdward Tomasz Napierala 	    strcasecmp(subject, "user") == 0 && strlen(textid) > 0) {
2145c859bfbSEdward Tomasz Napierala 		error = parse_user(textid, &id, rule);
2155c859bfbSEdward Tomasz Napierala 		if (error != 0) {
2165c859bfbSEdward Tomasz Napierala 			free(tofree);
2170971623eSEdward Tomasz Napierala 			return (NULL);
2185c859bfbSEdward Tomasz Napierala 		}
219f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
2200971623eSEdward Tomasz Napierala 	} else if (resolve_ids &&
2210971623eSEdward Tomasz Napierala 	    strcasecmp(subject, "group") == 0 && strlen(textid) > 0) {
2225c859bfbSEdward Tomasz Napierala 		error = parse_group(textid, &id, rule);
2235c859bfbSEdward Tomasz Napierala 		if (error != 0) {
2245c859bfbSEdward Tomasz Napierala 			free(tofree);
2250971623eSEdward Tomasz Napierala 			return (NULL);
2265c859bfbSEdward Tomasz Napierala 		}
227f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
2280971623eSEdward Tomasz Napierala 	} else {
229f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&resolved, "%s:%s:%s", subject, textid, rest);
2300971623eSEdward Tomasz Napierala 	}
2310971623eSEdward Tomasz Napierala 
232f4e361a7SEdward Tomasz Napierala 	if (ret <= 0) {
2330971623eSEdward Tomasz Napierala 		warn("asprintf");
2345c859bfbSEdward Tomasz Napierala 		free(tofree);
2350971623eSEdward Tomasz Napierala 		return (NULL);
2360971623eSEdward Tomasz Napierala 	}
2370971623eSEdward Tomasz Napierala 
2385c859bfbSEdward Tomasz Napierala 	free(tofree);
2395c859bfbSEdward Tomasz Napierala 
2405c859bfbSEdward Tomasz Napierala 	expanded = expand_amount(resolved, rule);
2415c859bfbSEdward Tomasz Napierala 	free(resolved);
2425c859bfbSEdward Tomasz Napierala 
2435c859bfbSEdward Tomasz Napierala 	return (expanded);
2440971623eSEdward Tomasz Napierala }
2450971623eSEdward Tomasz Napierala 
2466ec54a57SEdward Tomasz Napierala static char *
humanize_ids(char * rule)2476ec54a57SEdward Tomasz Napierala humanize_ids(char *rule)
2486ec54a57SEdward Tomasz Napierala {
2496ec54a57SEdward Tomasz Napierala 	id_t id;
2506ec54a57SEdward Tomasz Napierala 	struct passwd *pwd;
2516ec54a57SEdward Tomasz Napierala 	struct group *grp;
2526ec54a57SEdward Tomasz Napierala 	const char *subject, *textid, *rest;
253478f7a72SEdward Tomasz Napierala 	char *end, *humanized;
254f4e361a7SEdward Tomasz Napierala 	int ret;
2556ec54a57SEdward Tomasz Napierala 
2566ec54a57SEdward Tomasz Napierala 	subject = strsep(&rule, ":");
2576ec54a57SEdward Tomasz Napierala 	textid = strsep(&rule, ":");
2586ec54a57SEdward Tomasz Napierala 	if (textid == NULL)
2596ec54a57SEdward Tomasz Napierala 		errx(1, "rule passed from the kernel didn't contain subject");
2606ec54a57SEdward Tomasz Napierala 	if (rule != NULL)
2616ec54a57SEdward Tomasz Napierala 		rest = rule;
2626ec54a57SEdward Tomasz Napierala 	else
2636ec54a57SEdward Tomasz Napierala 		rest = "";
2646ec54a57SEdward Tomasz Napierala 
2656ec54a57SEdward Tomasz Napierala 	/* Replace numerical user and group ids with names. */
2666ec54a57SEdward Tomasz Napierala 	if (strcasecmp(subject, "user") == 0) {
267478f7a72SEdward Tomasz Napierala 		id = strtod(textid, &end);
268478f7a72SEdward Tomasz Napierala 		if ((size_t)(end - textid) != strlen(textid))
269478f7a72SEdward Tomasz Napierala 			errx(1, "malformed uid '%s'", textid);
2706ec54a57SEdward Tomasz Napierala 		pwd = getpwuid(id);
2716ec54a57SEdward Tomasz Napierala 		if (pwd != NULL)
2726ec54a57SEdward Tomasz Napierala 			textid = pwd->pw_name;
2736ec54a57SEdward Tomasz Napierala 	} else if (strcasecmp(subject, "group") == 0) {
274478f7a72SEdward Tomasz Napierala 		id = strtod(textid, &end);
275478f7a72SEdward Tomasz Napierala 		if ((size_t)(end - textid) != strlen(textid))
276478f7a72SEdward Tomasz Napierala 			errx(1, "malformed gid '%s'", textid);
2776ec54a57SEdward Tomasz Napierala 		grp = getgrgid(id);
2786ec54a57SEdward Tomasz Napierala 		if (grp != NULL)
2796ec54a57SEdward Tomasz Napierala 			textid = grp->gr_name;
2806ec54a57SEdward Tomasz Napierala 	}
2816ec54a57SEdward Tomasz Napierala 
282f4e361a7SEdward Tomasz Napierala 	ret = asprintf(&humanized, "%s:%s:%s", subject, textid, rest);
283f4e361a7SEdward Tomasz Napierala 	if (ret <= 0)
2846ec54a57SEdward Tomasz Napierala 		err(1, "asprintf");
2856ec54a57SEdward Tomasz Napierala 
2866ec54a57SEdward Tomasz Napierala 	return (humanized);
2876ec54a57SEdward Tomasz Napierala }
2886ec54a57SEdward Tomasz Napierala 
2896ec54a57SEdward Tomasz Napierala static int
str2int64(const char * str,int64_t * value)2906ec54a57SEdward Tomasz Napierala str2int64(const char *str, int64_t *value)
2916ec54a57SEdward Tomasz Napierala {
2926ec54a57SEdward Tomasz Napierala 	char *end;
2936ec54a57SEdward Tomasz Napierala 
2946ec54a57SEdward Tomasz Napierala 	if (str == NULL)
2956ec54a57SEdward Tomasz Napierala 		return (EINVAL);
2966ec54a57SEdward Tomasz Napierala 
2976ec54a57SEdward Tomasz Napierala 	*value = strtoul(str, &end, 10);
2986ec54a57SEdward Tomasz Napierala 	if ((size_t)(end - str) != strlen(str))
2996ec54a57SEdward Tomasz Napierala 		return (EINVAL);
3006ec54a57SEdward Tomasz Napierala 
3016ec54a57SEdward Tomasz Napierala 	return (0);
3026ec54a57SEdward Tomasz Napierala }
3036ec54a57SEdward Tomasz Napierala 
3046ec54a57SEdward Tomasz Napierala static char *
humanize_amount(char * rule)3056ec54a57SEdward Tomasz Napierala humanize_amount(char *rule)
3066ec54a57SEdward Tomasz Napierala {
3076ec54a57SEdward Tomasz Napierala 	int64_t num;
3086ec54a57SEdward Tomasz Napierala 	const char *subject, *subject_id, *resource, *action, *amount, *per;
3095e7a2555SEdward Tomasz Napierala 	char *copy, *humanized, buf[6], *tofree;
310f4e361a7SEdward Tomasz Napierala 	int ret;
3116ec54a57SEdward Tomasz Napierala 
3125e7a2555SEdward Tomasz Napierala 	tofree = copy = strdup(rule);
3136ec54a57SEdward Tomasz Napierala 	if (copy == NULL)
3146ec54a57SEdward Tomasz Napierala 		err(1, "strdup");
3156ec54a57SEdward Tomasz Napierala 
3166ec54a57SEdward Tomasz Napierala 	subject = strsep(&copy, ":");
3176ec54a57SEdward Tomasz Napierala 	subject_id = strsep(&copy, ":");
3186ec54a57SEdward Tomasz Napierala 	resource = strsep(&copy, ":");
3196ec54a57SEdward Tomasz Napierala 	action = strsep(&copy, "=/");
3206ec54a57SEdward Tomasz Napierala 	amount = strsep(&copy, "/");
3216ec54a57SEdward Tomasz Napierala 	per = copy;
3226ec54a57SEdward Tomasz Napierala 
3236ec54a57SEdward Tomasz Napierala 	if (amount == NULL || strlen(amount) == 0 ||
3246ec54a57SEdward Tomasz Napierala 	    str2int64(amount, &num) != 0) {
3255e7a2555SEdward Tomasz Napierala 		free(tofree);
3266ec54a57SEdward Tomasz Napierala 		return (rule);
3276ec54a57SEdward Tomasz Napierala 	}
3286ec54a57SEdward Tomasz Napierala 
3296ec54a57SEdward Tomasz Napierala 	assert(subject != NULL);
3306ec54a57SEdward Tomasz Napierala 	assert(subject_id != NULL);
3316ec54a57SEdward Tomasz Napierala 	assert(resource != NULL);
3326ec54a57SEdward Tomasz Napierala 	assert(action != NULL);
3336ec54a57SEdward Tomasz Napierala 
3346ec54a57SEdward Tomasz Napierala 	if (humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
3356ec54a57SEdward Tomasz Napierala 	    HN_DECIMAL | HN_NOSPACE) == -1)
3366ec54a57SEdward Tomasz Napierala 		err(1, "humanize_number");
3376ec54a57SEdward Tomasz Napierala 
338f4e361a7SEdward Tomasz Napierala 	if (per == NULL) {
339f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&humanized, "%s:%s:%s:%s=%s",
340f4e361a7SEdward Tomasz Napierala 		    subject, subject_id, resource, action, buf);
341f4e361a7SEdward Tomasz Napierala 	} else {
342f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&humanized, "%s:%s:%s:%s=%s/%s",
343f4e361a7SEdward Tomasz Napierala 		    subject, subject_id, resource, action, buf, per);
344f4e361a7SEdward Tomasz Napierala 	}
3456ec54a57SEdward Tomasz Napierala 
346f4e361a7SEdward Tomasz Napierala 	if (ret <= 0)
3476ec54a57SEdward Tomasz Napierala 		err(1, "asprintf");
3486ec54a57SEdward Tomasz Napierala 
3495e7a2555SEdward Tomasz Napierala 	free(tofree);
3506ec54a57SEdward Tomasz Napierala 	return (humanized);
3516ec54a57SEdward Tomasz Napierala }
3526ec54a57SEdward Tomasz Napierala 
3536ec54a57SEdward Tomasz Napierala /*
3546ec54a57SEdward Tomasz Napierala  * Print rules, one per line.
3556ec54a57SEdward Tomasz Napierala  */
3566ec54a57SEdward Tomasz Napierala static void
print_rules(char * rules,int hflag,int nflag)3576ec54a57SEdward Tomasz Napierala print_rules(char *rules, int hflag, int nflag)
3586ec54a57SEdward Tomasz Napierala {
3596ec54a57SEdward Tomasz Napierala 	char *rule;
3606ec54a57SEdward Tomasz Napierala 
3616ec54a57SEdward Tomasz Napierala 	while ((rule = strsep(&rules, ",")) != NULL) {
3626ec54a57SEdward Tomasz Napierala 		if (rule[0] == '\0')
3636ec54a57SEdward Tomasz Napierala 			break; /* XXX */
3646ec54a57SEdward Tomasz Napierala 		if (nflag == 0)
3656ec54a57SEdward Tomasz Napierala 			rule = humanize_ids(rule);
3666ec54a57SEdward Tomasz Napierala 		if (hflag)
3676ec54a57SEdward Tomasz Napierala 			rule = humanize_amount(rule);
3686ec54a57SEdward Tomasz Napierala 		printf("%s\n", rule);
3696ec54a57SEdward Tomasz Napierala 	}
3706ec54a57SEdward Tomasz Napierala }
3716ec54a57SEdward Tomasz Napierala 
3726ec54a57SEdward Tomasz Napierala static void
enosys(void)373aae2a24bSEdward Tomasz Napierala enosys(void)
374aae2a24bSEdward Tomasz Napierala {
375aae2a24bSEdward Tomasz Napierala 	size_t racct_enable_len;
3760a17b9e0SMateusz Guzik 	int error;
3770a17b9e0SMateusz Guzik 	bool racct_enable;
378aae2a24bSEdward Tomasz Napierala 
379aae2a24bSEdward Tomasz Napierala 	racct_enable_len = sizeof(racct_enable);
380aae2a24bSEdward Tomasz Napierala 	error = sysctlbyname("kern.racct.enable",
381aae2a24bSEdward Tomasz Napierala 	    &racct_enable, &racct_enable_len, NULL, 0);
382aae2a24bSEdward Tomasz Napierala 
383aae2a24bSEdward Tomasz Napierala 	if (error != 0) {
384aae2a24bSEdward Tomasz Napierala 		if (errno == ENOENT)
38582224d7dSEdward Tomasz Napierala 			errx(1, "RACCT/RCTL support not present in kernel; see rctl(8) for details");
386aae2a24bSEdward Tomasz Napierala 
387aae2a24bSEdward Tomasz Napierala 		err(1, "sysctlbyname");
388aae2a24bSEdward Tomasz Napierala 	}
389aae2a24bSEdward Tomasz Napierala 
3900a17b9e0SMateusz Guzik 	if (!racct_enable)
391aae2a24bSEdward Tomasz Napierala 		errx(1, "RACCT/RCTL present, but disabled; enable using kern.racct.enable=1 tunable");
392aae2a24bSEdward Tomasz Napierala }
393aae2a24bSEdward Tomasz Napierala 
3940971623eSEdward Tomasz Napierala static int
add_rule(const char * rule,const char * unexpanded_rule)3955c859bfbSEdward Tomasz Napierala add_rule(const char *rule, const char *unexpanded_rule)
3966ec54a57SEdward Tomasz Napierala {
3976ec54a57SEdward Tomasz Napierala 	int error;
3986ec54a57SEdward Tomasz Napierala 
3996ec54a57SEdward Tomasz Napierala 	error = rctl_add_rule(rule, strlen(rule) + 1, NULL, 0);
400aae2a24bSEdward Tomasz Napierala 	if (error != 0) {
401aae2a24bSEdward Tomasz Napierala 		if (errno == ENOSYS)
402aae2a24bSEdward Tomasz Napierala 			enosys();
4035c859bfbSEdward Tomasz Napierala 		warn("failed to add rule '%s'", unexpanded_rule);
4046ec54a57SEdward Tomasz Napierala 	}
4056ec54a57SEdward Tomasz Napierala 
4060971623eSEdward Tomasz Napierala 	return (error);
4070971623eSEdward Tomasz Napierala }
4080971623eSEdward Tomasz Napierala 
4090971623eSEdward Tomasz Napierala static int
show_limits(const char * filter,const char * unexpanded_rule,int hflag,int nflag)4105c859bfbSEdward Tomasz Napierala show_limits(const char *filter, const char *unexpanded_rule,
4115c859bfbSEdward Tomasz Napierala     int hflag, int nflag)
4126ec54a57SEdward Tomasz Napierala {
4136ec54a57SEdward Tomasz Napierala 	int error;
4146ec54a57SEdward Tomasz Napierala 	char *outbuf = NULL;
4156ec54a57SEdward Tomasz Napierala 	size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
4166ec54a57SEdward Tomasz Napierala 
4174cf0d895SEdward Tomasz Napierala 	for (;;) {
4186ec54a57SEdward Tomasz Napierala 		outbuflen *= 4;
4196ec54a57SEdward Tomasz Napierala 		outbuf = realloc(outbuf, outbuflen);
4206ec54a57SEdward Tomasz Napierala 		if (outbuf == NULL)
4216ec54a57SEdward Tomasz Napierala 			err(1, "realloc");
4224cf0d895SEdward Tomasz Napierala 		error = rctl_get_limits(filter, strlen(filter) + 1,
4234cf0d895SEdward Tomasz Napierala 		    outbuf, outbuflen);
4244cf0d895SEdward Tomasz Napierala 		if (error == 0)
4254cf0d895SEdward Tomasz Napierala 			break;
4264cf0d895SEdward Tomasz Napierala 		if (errno == ERANGE)
4274cf0d895SEdward Tomasz Napierala 			continue;
428aae2a24bSEdward Tomasz Napierala 		if (errno == ENOSYS)
429aae2a24bSEdward Tomasz Napierala 			enosys();
4305c859bfbSEdward Tomasz Napierala 		warn("failed to get limits for '%s'", unexpanded_rule);
4314cf0d895SEdward Tomasz Napierala 		free(outbuf);
4324cf0d895SEdward Tomasz Napierala 
4334cf0d895SEdward Tomasz Napierala 		return (error);
434aae2a24bSEdward Tomasz Napierala 	}
4356ec54a57SEdward Tomasz Napierala 
4366ec54a57SEdward Tomasz Napierala 	print_rules(outbuf, hflag, nflag);
4376ec54a57SEdward Tomasz Napierala 	free(outbuf);
4380971623eSEdward Tomasz Napierala 
4390971623eSEdward Tomasz Napierala 	return (error);
4406ec54a57SEdward Tomasz Napierala }
4416ec54a57SEdward Tomasz Napierala 
4420971623eSEdward Tomasz Napierala static int
remove_rule(const char * filter,const char * unexpanded_rule)4435c859bfbSEdward Tomasz Napierala remove_rule(const char *filter, const char *unexpanded_rule)
4446ec54a57SEdward Tomasz Napierala {
4456ec54a57SEdward Tomasz Napierala 	int error;
4466ec54a57SEdward Tomasz Napierala 
4476ec54a57SEdward Tomasz Napierala 	error = rctl_remove_rule(filter, strlen(filter) + 1, NULL, 0);
448aae2a24bSEdward Tomasz Napierala 	if (error != 0) {
449aae2a24bSEdward Tomasz Napierala 		if (errno == ENOSYS)
450aae2a24bSEdward Tomasz Napierala 			enosys();
4515c859bfbSEdward Tomasz Napierala 		warn("failed to remove rule '%s'", unexpanded_rule);
452aae2a24bSEdward Tomasz Napierala 	}
4530971623eSEdward Tomasz Napierala 
4540971623eSEdward Tomasz Napierala 	return (error);
4556ec54a57SEdward Tomasz Napierala }
4566ec54a57SEdward Tomasz Napierala 
4576ec54a57SEdward Tomasz Napierala static char *
humanize_usage_amount(char * usage)4586ec54a57SEdward Tomasz Napierala humanize_usage_amount(char *usage)
4596ec54a57SEdward Tomasz Napierala {
4606ec54a57SEdward Tomasz Napierala 	int64_t num;
4616ec54a57SEdward Tomasz Napierala 	const char *resource, *amount;
4625e7a2555SEdward Tomasz Napierala 	char *copy, *humanized, buf[6], *tofree;
463f4e361a7SEdward Tomasz Napierala 	int ret;
4646ec54a57SEdward Tomasz Napierala 
4655e7a2555SEdward Tomasz Napierala 	tofree = copy = strdup(usage);
4666ec54a57SEdward Tomasz Napierala 	if (copy == NULL)
4676ec54a57SEdward Tomasz Napierala 		err(1, "strdup");
4686ec54a57SEdward Tomasz Napierala 
4696ec54a57SEdward Tomasz Napierala 	resource = strsep(&copy, "=");
4706ec54a57SEdward Tomasz Napierala 	amount = copy;
4716ec54a57SEdward Tomasz Napierala 
4726ec54a57SEdward Tomasz Napierala 	assert(resource != NULL);
4736ec54a57SEdward Tomasz Napierala 	assert(amount != NULL);
4746ec54a57SEdward Tomasz Napierala 
4756ec54a57SEdward Tomasz Napierala 	if (str2int64(amount, &num) != 0 ||
4766ec54a57SEdward Tomasz Napierala 	    humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
4776ec54a57SEdward Tomasz Napierala 	    HN_DECIMAL | HN_NOSPACE) == -1) {
4785e7a2555SEdward Tomasz Napierala 		free(tofree);
4796ec54a57SEdward Tomasz Napierala 		return (usage);
4806ec54a57SEdward Tomasz Napierala 	}
4816ec54a57SEdward Tomasz Napierala 
482f4e361a7SEdward Tomasz Napierala 	ret = asprintf(&humanized, "%s=%s", resource, buf);
483f4e361a7SEdward Tomasz Napierala 	if (ret <= 0)
4846ec54a57SEdward Tomasz Napierala 		err(1, "asprintf");
4856ec54a57SEdward Tomasz Napierala 
4865e7a2555SEdward Tomasz Napierala 	free(tofree);
4876ec54a57SEdward Tomasz Napierala 	return (humanized);
4886ec54a57SEdward Tomasz Napierala }
4896ec54a57SEdward Tomasz Napierala 
4906ec54a57SEdward Tomasz Napierala /*
4916ec54a57SEdward Tomasz Napierala  * Query the kernel about a resource usage and print it out.
4926ec54a57SEdward Tomasz Napierala  */
4930971623eSEdward Tomasz Napierala static int
show_usage(const char * filter,const char * unexpanded_rule,int hflag)4945c859bfbSEdward Tomasz Napierala show_usage(const char *filter, const char *unexpanded_rule, int hflag)
4956ec54a57SEdward Tomasz Napierala {
4966ec54a57SEdward Tomasz Napierala 	int error;
4975e7a2555SEdward Tomasz Napierala 	char *copy, *outbuf = NULL, *tmp;
4986ec54a57SEdward Tomasz Napierala 	size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
4996ec54a57SEdward Tomasz Napierala 
5004cf0d895SEdward Tomasz Napierala 	for (;;) {
5016ec54a57SEdward Tomasz Napierala 		outbuflen *= 4;
5026ec54a57SEdward Tomasz Napierala 		outbuf = realloc(outbuf, outbuflen);
5036ec54a57SEdward Tomasz Napierala 		if (outbuf == NULL)
5046ec54a57SEdward Tomasz Napierala 			err(1, "realloc");
5054cf0d895SEdward Tomasz Napierala 		error = rctl_get_racct(filter, strlen(filter) + 1,
5064cf0d895SEdward Tomasz Napierala 		    outbuf, outbuflen);
5074cf0d895SEdward Tomasz Napierala 		if (error == 0)
5084cf0d895SEdward Tomasz Napierala 			break;
5094cf0d895SEdward Tomasz Napierala 		if (errno == ERANGE)
5104cf0d895SEdward Tomasz Napierala 			continue;
511aae2a24bSEdward Tomasz Napierala 		if (errno == ENOSYS)
512aae2a24bSEdward Tomasz Napierala 			enosys();
5135c859bfbSEdward Tomasz Napierala 		warn("failed to show resource consumption for '%s'",
5145c859bfbSEdward Tomasz Napierala 		    unexpanded_rule);
5154cf0d895SEdward Tomasz Napierala 		free(outbuf);
5164cf0d895SEdward Tomasz Napierala 
5174cf0d895SEdward Tomasz Napierala 		return (error);
518aae2a24bSEdward Tomasz Napierala 	}
5196ec54a57SEdward Tomasz Napierala 
5205e7a2555SEdward Tomasz Napierala 	copy = outbuf;
5215e7a2555SEdward Tomasz Napierala 	while ((tmp = strsep(&copy, ",")) != NULL) {
5226ec54a57SEdward Tomasz Napierala 		if (tmp[0] == '\0')
5236ec54a57SEdward Tomasz Napierala 			break; /* XXX */
5246ec54a57SEdward Tomasz Napierala 
5256ec54a57SEdward Tomasz Napierala 		if (hflag)
5266ec54a57SEdward Tomasz Napierala 			tmp = humanize_usage_amount(tmp);
5276ec54a57SEdward Tomasz Napierala 
5286ec54a57SEdward Tomasz Napierala 		printf("%s\n", tmp);
5296ec54a57SEdward Tomasz Napierala 	}
5306ec54a57SEdward Tomasz Napierala 
5316ec54a57SEdward Tomasz Napierala 	free(outbuf);
5320971623eSEdward Tomasz Napierala 
5330971623eSEdward Tomasz Napierala 	return (error);
5346ec54a57SEdward Tomasz Napierala }
5356ec54a57SEdward Tomasz Napierala 
5366ec54a57SEdward Tomasz Napierala /*
5376ec54a57SEdward Tomasz Napierala  * Query the kernel about resource limit rules and print them out.
5386ec54a57SEdward Tomasz Napierala  */
5390971623eSEdward Tomasz Napierala static int
show_rules(const char * filter,const char * unexpanded_rule,int hflag,int nflag)5405c859bfbSEdward Tomasz Napierala show_rules(const char *filter, const char *unexpanded_rule,
5415c859bfbSEdward Tomasz Napierala     int hflag, int nflag)
5426ec54a57SEdward Tomasz Napierala {
5436ec54a57SEdward Tomasz Napierala 	int error;
5446ec54a57SEdward Tomasz Napierala 	char *outbuf = NULL;
5456ec54a57SEdward Tomasz Napierala 	size_t filterlen, outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
5466ec54a57SEdward Tomasz Napierala 
5476ec54a57SEdward Tomasz Napierala 	if (filter != NULL)
5486ec54a57SEdward Tomasz Napierala 		filterlen = strlen(filter) + 1;
5496ec54a57SEdward Tomasz Napierala 	else
5506ec54a57SEdward Tomasz Napierala 		filterlen = 0;
5516ec54a57SEdward Tomasz Napierala 
5524cf0d895SEdward Tomasz Napierala 	for (;;) {
5536ec54a57SEdward Tomasz Napierala 		outbuflen *= 4;
5546ec54a57SEdward Tomasz Napierala 		outbuf = realloc(outbuf, outbuflen);
5556ec54a57SEdward Tomasz Napierala 		if (outbuf == NULL)
5566ec54a57SEdward Tomasz Napierala 			err(1, "realloc");
5576ec54a57SEdward Tomasz Napierala 		error = rctl_get_rules(filter, filterlen, outbuf, outbuflen);
5584cf0d895SEdward Tomasz Napierala 		if (error == 0)
5594cf0d895SEdward Tomasz Napierala 			break;
5604cf0d895SEdward Tomasz Napierala 		if (errno == ERANGE)
5614cf0d895SEdward Tomasz Napierala 			continue;
562aae2a24bSEdward Tomasz Napierala 		if (errno == ENOSYS)
563aae2a24bSEdward Tomasz Napierala 			enosys();
5645c859bfbSEdward Tomasz Napierala 		warn("failed to show rules for '%s'", unexpanded_rule);
5654cf0d895SEdward Tomasz Napierala 		free(outbuf);
5664cf0d895SEdward Tomasz Napierala 
5674cf0d895SEdward Tomasz Napierala 		return (error);
568aae2a24bSEdward Tomasz Napierala 	}
5696ec54a57SEdward Tomasz Napierala 
5706ec54a57SEdward Tomasz Napierala 	print_rules(outbuf, hflag, nflag);
5716ec54a57SEdward Tomasz Napierala 	free(outbuf);
5720971623eSEdward Tomasz Napierala 
5730971623eSEdward Tomasz Napierala 	return (error);
5746ec54a57SEdward Tomasz Napierala }
5756ec54a57SEdward Tomasz Napierala 
5766ec54a57SEdward Tomasz Napierala static void
usage(void)5776ec54a57SEdward Tomasz Napierala usage(void)
5786ec54a57SEdward Tomasz Napierala {
5796ec54a57SEdward Tomasz Napierala 
5806ec54a57SEdward Tomasz Napierala 	fprintf(stderr, "usage: rctl [ -h ] [-a rule | -l filter | -r filter "
5816ec54a57SEdward Tomasz Napierala 	    "| -u filter | filter]\n");
5826ec54a57SEdward Tomasz Napierala 	exit(1);
5836ec54a57SEdward Tomasz Napierala }
5846ec54a57SEdward Tomasz Napierala 
5856ec54a57SEdward Tomasz Napierala int
main(int argc,char ** argv)586d0ab9cbeSBaptiste Daroussin main(int argc, char **argv)
5876ec54a57SEdward Tomasz Napierala {
5886ec54a57SEdward Tomasz Napierala 	int ch, aflag = 0, hflag = 0, nflag = 0, lflag = 0, rflag = 0,
5896ec54a57SEdward Tomasz Napierala 	    uflag = 0;
5905c859bfbSEdward Tomasz Napierala 	char *rule = NULL, *unexpanded_rule;
5915c859bfbSEdward Tomasz Napierala 	int i, cumulated_error, error;
5926ec54a57SEdward Tomasz Napierala 
5930971623eSEdward Tomasz Napierala 	while ((ch = getopt(argc, argv, "ahlnru")) != -1) {
5946ec54a57SEdward Tomasz Napierala 		switch (ch) {
5956ec54a57SEdward Tomasz Napierala 		case 'a':
5966ec54a57SEdward Tomasz Napierala 			aflag = 1;
5976ec54a57SEdward Tomasz Napierala 			break;
5986ec54a57SEdward Tomasz Napierala 		case 'h':
5996ec54a57SEdward Tomasz Napierala 			hflag = 1;
6006ec54a57SEdward Tomasz Napierala 			break;
6016ec54a57SEdward Tomasz Napierala 		case 'l':
6026ec54a57SEdward Tomasz Napierala 			lflag = 1;
6036ec54a57SEdward Tomasz Napierala 			break;
6046ec54a57SEdward Tomasz Napierala 		case 'n':
6056ec54a57SEdward Tomasz Napierala 			nflag = 1;
6066ec54a57SEdward Tomasz Napierala 			break;
6076ec54a57SEdward Tomasz Napierala 		case 'r':
6086ec54a57SEdward Tomasz Napierala 			rflag = 1;
6096ec54a57SEdward Tomasz Napierala 			break;
6106ec54a57SEdward Tomasz Napierala 		case 'u':
6116ec54a57SEdward Tomasz Napierala 			uflag = 1;
6126ec54a57SEdward Tomasz Napierala 			break;
6136ec54a57SEdward Tomasz Napierala 
6146ec54a57SEdward Tomasz Napierala 		case '?':
6156ec54a57SEdward Tomasz Napierala 		default:
6166ec54a57SEdward Tomasz Napierala 			usage();
6176ec54a57SEdward Tomasz Napierala 		}
6186ec54a57SEdward Tomasz Napierala 	}
6196ec54a57SEdward Tomasz Napierala 
6206ec54a57SEdward Tomasz Napierala 	argc -= optind;
6216ec54a57SEdward Tomasz Napierala 	argv += optind;
6226ec54a57SEdward Tomasz Napierala 
6230971623eSEdward Tomasz Napierala 	if (aflag + lflag + rflag + uflag > 1)
6240971623eSEdward Tomasz Napierala 		errx(1, "at most one of -a, -l, -r, or -u may be specified");
6250971623eSEdward Tomasz Napierala 
6260971623eSEdward Tomasz Napierala 	if (argc == 0) {
6270971623eSEdward Tomasz Napierala 		if (aflag + lflag + rflag + uflag == 0) {
6280971623eSEdward Tomasz Napierala 			rule = strdup("::");
6295c859bfbSEdward Tomasz Napierala 			show_rules(rule, rule, hflag, nflag);
6300971623eSEdward Tomasz Napierala 
6310971623eSEdward Tomasz Napierala 			return (0);
6320971623eSEdward Tomasz Napierala 		}
6330971623eSEdward Tomasz Napierala 
6346ec54a57SEdward Tomasz Napierala 		usage();
6350971623eSEdward Tomasz Napierala 	}
6360971623eSEdward Tomasz Napierala 
6370971623eSEdward Tomasz Napierala 	cumulated_error = 0;
6380971623eSEdward Tomasz Napierala 
6390971623eSEdward Tomasz Napierala 	for (i = 0; i < argc; i++) {
6405c859bfbSEdward Tomasz Napierala 		unexpanded_rule = argv[i];
6410971623eSEdward Tomasz Napierala 
6420971623eSEdward Tomasz Napierala 		/*
6430971623eSEdward Tomasz Napierala 		 * Skip resolving if passed -n _and_ -a.  Ignore -n otherwise,
6440971623eSEdward Tomasz Napierala 		 * so we can still do "rctl -n u:root" and see the rules without
6450971623eSEdward Tomasz Napierala 		 * resolving the UID.
6460971623eSEdward Tomasz Napierala 		 */
6470971623eSEdward Tomasz Napierala 		if (aflag != 0 && nflag != 0)
6485c859bfbSEdward Tomasz Napierala 			rule = expand_rule(unexpanded_rule, false);
6490971623eSEdward Tomasz Napierala 		else
6505c859bfbSEdward Tomasz Napierala 			rule = expand_rule(unexpanded_rule, true);
6516ec54a57SEdward Tomasz Napierala 
6526ec54a57SEdward Tomasz Napierala 		if (rule == NULL) {
6530971623eSEdward Tomasz Napierala 			cumulated_error++;
6540971623eSEdward Tomasz Napierala 			continue;
6556ec54a57SEdward Tomasz Napierala 		}
6566ec54a57SEdward Tomasz Napierala 
6575c859bfbSEdward Tomasz Napierala 		/*
6585c859bfbSEdward Tomasz Napierala 		 * The reason for passing the unexpanded_rule is to make
6595c859bfbSEdward Tomasz Napierala 		 * it easier for the user to search for the problematic
6605c859bfbSEdward Tomasz Napierala 		 * rule in the passed input.
6615c859bfbSEdward Tomasz Napierala 		 */
6626ec54a57SEdward Tomasz Napierala 		if (aflag) {
6635c859bfbSEdward Tomasz Napierala 			error = add_rule(rule, unexpanded_rule);
6640971623eSEdward Tomasz Napierala 		} else if (lflag) {
6655c859bfbSEdward Tomasz Napierala 			error = show_limits(rule, unexpanded_rule,
6665c859bfbSEdward Tomasz Napierala 			    hflag, nflag);
6670971623eSEdward Tomasz Napierala 		} else if (rflag) {
6685c859bfbSEdward Tomasz Napierala 			error = remove_rule(rule, unexpanded_rule);
6690971623eSEdward Tomasz Napierala 		} else if (uflag) {
6705c859bfbSEdward Tomasz Napierala 			error = show_usage(rule, unexpanded_rule, hflag);
6710971623eSEdward Tomasz Napierala 		} else  {
6725c859bfbSEdward Tomasz Napierala 			error = show_rules(rule, unexpanded_rule,
6735c859bfbSEdward Tomasz Napierala 			    hflag, nflag);
6746ec54a57SEdward Tomasz Napierala 		}
6756ec54a57SEdward Tomasz Napierala 
6765c859bfbSEdward Tomasz Napierala 		if (error != 0)
6775c859bfbSEdward Tomasz Napierala 			cumulated_error++;
6785c859bfbSEdward Tomasz Napierala 
6790971623eSEdward Tomasz Napierala 		free(rule);
6806ec54a57SEdward Tomasz Napierala 	}
6816ec54a57SEdward Tomasz Napierala 
6820971623eSEdward Tomasz Napierala 	return (cumulated_error);
6836ec54a57SEdward Tomasz Napierala }
684