xref: /freebsd/usr.bin/rctl/rctl.c (revision 0a17b9e03e8402aa912c398e98b48c8b3b4ee232)
16ec54a57SEdward Tomasz Napierala /*-
21de7b4b8SPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
31de7b4b8SPedro F. Giffuni  *
46ec54a57SEdward Tomasz Napierala  * Copyright (c) 2010 The FreeBSD Foundation
56ec54a57SEdward Tomasz Napierala  * All rights reserved.
66ec54a57SEdward Tomasz Napierala  *
76ec54a57SEdward Tomasz Napierala  * This software was developed by Edward Tomasz Napierala under sponsorship
86ec54a57SEdward Tomasz Napierala  * from the FreeBSD Foundation.
96ec54a57SEdward Tomasz Napierala  *
106ec54a57SEdward Tomasz Napierala  * Redistribution and use in source and binary forms, with or without
116ec54a57SEdward Tomasz Napierala  * modification, are permitted provided that the following conditions
126ec54a57SEdward Tomasz Napierala  * are met:
136ec54a57SEdward Tomasz Napierala  * 1. Redistributions of source code must retain the above copyright
146ec54a57SEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer.
156ec54a57SEdward Tomasz Napierala  * 2. Redistributions in binary form must reproduce the above copyright
166ec54a57SEdward Tomasz Napierala  *    notice, this list of conditions and the following disclaimer in the
176ec54a57SEdward Tomasz Napierala  *    documentation and/or other materials provided with the distribution.
186ec54a57SEdward Tomasz Napierala  *
196ec54a57SEdward Tomasz Napierala  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
206ec54a57SEdward Tomasz Napierala  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
216ec54a57SEdward Tomasz Napierala  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
226ec54a57SEdward Tomasz Napierala  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
236ec54a57SEdward Tomasz Napierala  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
246ec54a57SEdward Tomasz Napierala  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
256ec54a57SEdward Tomasz Napierala  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
266ec54a57SEdward Tomasz Napierala  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
276ec54a57SEdward Tomasz Napierala  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
286ec54a57SEdward Tomasz Napierala  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
296ec54a57SEdward Tomasz Napierala  * SUCH DAMAGE.
306ec54a57SEdward Tomasz Napierala  *
316ec54a57SEdward Tomasz Napierala  * $FreeBSD$
326ec54a57SEdward Tomasz Napierala  */
336ec54a57SEdward Tomasz Napierala 
346ec54a57SEdward Tomasz Napierala #include <sys/cdefs.h>
356ec54a57SEdward Tomasz Napierala __FBSDID("$FreeBSD$");
366ec54a57SEdward Tomasz Napierala 
376ec54a57SEdward Tomasz Napierala #include <sys/types.h>
386ec54a57SEdward Tomasz Napierala #include <sys/rctl.h>
39aae2a24bSEdward Tomasz Napierala #include <sys/sysctl.h>
406ec54a57SEdward Tomasz Napierala #include <assert.h>
416ec54a57SEdward Tomasz Napierala #include <ctype.h>
426ec54a57SEdward Tomasz Napierala #include <err.h>
436ec54a57SEdward Tomasz Napierala #include <errno.h>
446ec54a57SEdward Tomasz Napierala #include <getopt.h>
456ec54a57SEdward Tomasz Napierala #include <grp.h>
466ec54a57SEdward Tomasz Napierala #include <libutil.h>
476ec54a57SEdward Tomasz Napierala #include <pwd.h>
480971623eSEdward Tomasz Napierala #include <stdbool.h>
496ec54a57SEdward Tomasz Napierala #include <stdint.h>
506ec54a57SEdward Tomasz Napierala #include <stdio.h>
516ec54a57SEdward Tomasz Napierala #include <stdlib.h>
526ec54a57SEdward Tomasz Napierala #include <string.h>
536ec54a57SEdward Tomasz Napierala 
5418e1f46eSEdward Tomasz Napierala #define	RCTL_DEFAULT_BUFSIZE	128 * 1024
556ec54a57SEdward Tomasz Napierala 
560971623eSEdward Tomasz Napierala static int
575c859bfbSEdward Tomasz Napierala parse_user(const char *s, id_t *uidp, const char *unexpanded_rule)
586ec54a57SEdward Tomasz Napierala {
596ec54a57SEdward Tomasz Napierala 	char *end;
606ec54a57SEdward Tomasz Napierala 	struct passwd *pwd;
616ec54a57SEdward Tomasz Napierala 
626ec54a57SEdward Tomasz Napierala 	pwd = getpwnam(s);
630971623eSEdward Tomasz Napierala 	if (pwd != NULL) {
640971623eSEdward Tomasz Napierala 		*uidp = pwd->pw_uid;
650971623eSEdward Tomasz Napierala 		return (0);
666ec54a57SEdward Tomasz Napierala 	}
676ec54a57SEdward Tomasz Napierala 
680971623eSEdward Tomasz Napierala 	if (!isnumber(s[0])) {
69c281fdb1SEnji Cooper 		warnx("malformed rule '%s': unknown user '%s'",
705c859bfbSEdward Tomasz Napierala 		    unexpanded_rule, s);
710971623eSEdward Tomasz Napierala 		return (1);
720971623eSEdward Tomasz Napierala 	}
730971623eSEdward Tomasz Napierala 
740971623eSEdward Tomasz Napierala 	*uidp = strtod(s, &end);
750971623eSEdward Tomasz Napierala 	if ((size_t)(end - s) != strlen(s)) {
765c859bfbSEdward Tomasz Napierala 		warnx("malformed rule '%s': trailing characters "
775c859bfbSEdward Tomasz Napierala 		    "after numerical id", unexpanded_rule);
780971623eSEdward Tomasz Napierala 		return (1);
790971623eSEdward Tomasz Napierala 	}
800971623eSEdward Tomasz Napierala 
810971623eSEdward Tomasz Napierala 	return (0);
820971623eSEdward Tomasz Napierala }
830971623eSEdward Tomasz Napierala 
840971623eSEdward Tomasz Napierala static int
855c859bfbSEdward Tomasz Napierala parse_group(const char *s, id_t *gidp, const char *unexpanded_rule)
866ec54a57SEdward Tomasz Napierala {
876ec54a57SEdward Tomasz Napierala 	char *end;
886ec54a57SEdward Tomasz Napierala 	struct group *grp;
896ec54a57SEdward Tomasz Napierala 
906ec54a57SEdward Tomasz Napierala 	grp = getgrnam(s);
910971623eSEdward Tomasz Napierala 	if (grp != NULL) {
920971623eSEdward Tomasz Napierala 		*gidp = grp->gr_gid;
930971623eSEdward Tomasz Napierala 		return (0);
940971623eSEdward Tomasz Napierala 	}
956ec54a57SEdward Tomasz Napierala 
960971623eSEdward Tomasz Napierala 	if (!isnumber(s[0])) {
97c281fdb1SEnji Cooper 		warnx("malformed rule '%s': unknown group '%s'",
985c859bfbSEdward Tomasz Napierala 		    unexpanded_rule, s);
990971623eSEdward Tomasz Napierala 		return (1);
1000971623eSEdward Tomasz Napierala 	}
1016ec54a57SEdward Tomasz Napierala 
1020971623eSEdward Tomasz Napierala 	*gidp = strtod(s, &end);
1030971623eSEdward Tomasz Napierala 	if ((size_t)(end - s) != strlen(s)) {
1045c859bfbSEdward Tomasz Napierala 		warnx("malformed rule '%s': trailing characters "
1055c859bfbSEdward Tomasz Napierala 		    "after numerical id", unexpanded_rule);
1060971623eSEdward Tomasz Napierala 		return (1);
1070971623eSEdward Tomasz Napierala 	}
1086ec54a57SEdward Tomasz Napierala 
1090971623eSEdward Tomasz Napierala 	return (0);
1106ec54a57SEdward Tomasz Napierala }
1116ec54a57SEdward Tomasz Napierala 
1126ec54a57SEdward Tomasz Napierala /*
1130971623eSEdward Tomasz Napierala  * Replace human-readable number with its expanded form.
1146ec54a57SEdward Tomasz Napierala  */
1156ec54a57SEdward Tomasz Napierala static char *
116508744b2SEdward Tomasz Napierala expand_amount(const char *rule, const char *unexpanded_rule)
1176ec54a57SEdward Tomasz Napierala {
1186ec54a57SEdward Tomasz Napierala 	uint64_t num;
1196ec54a57SEdward Tomasz Napierala 	const char *subject, *subject_id, *resource, *action, *amount, *per;
120508744b2SEdward Tomasz Napierala 	char *copy, *expanded, *tofree;
121f4e361a7SEdward Tomasz Napierala 	int ret;
1226ec54a57SEdward Tomasz Napierala 
123508744b2SEdward Tomasz Napierala 	tofree = copy = strdup(rule);
124508744b2SEdward Tomasz Napierala 	if (copy == NULL) {
125508744b2SEdward Tomasz Napierala 		warn("strdup");
126508744b2SEdward Tomasz Napierala 		return (NULL);
127508744b2SEdward Tomasz Napierala 	}
1286ec54a57SEdward Tomasz Napierala 
129508744b2SEdward Tomasz Napierala 	subject = strsep(&copy, ":");
130508744b2SEdward Tomasz Napierala 	subject_id = strsep(&copy, ":");
131508744b2SEdward Tomasz Napierala 	resource = strsep(&copy, ":");
132508744b2SEdward Tomasz Napierala 	action = strsep(&copy, "=/");
133508744b2SEdward Tomasz Napierala 	amount = strsep(&copy, "/");
134508744b2SEdward Tomasz Napierala 	per = copy;
135508744b2SEdward Tomasz Napierala 
136508744b2SEdward Tomasz Napierala 	if (amount == NULL || strlen(amount) == 0) {
137508744b2SEdward Tomasz Napierala 		/*
138508744b2SEdward Tomasz Napierala 		 * The "copy" has already been tinkered with by strsep().
139508744b2SEdward Tomasz Napierala 		 */
140508744b2SEdward Tomasz Napierala 		free(tofree);
141508744b2SEdward Tomasz Napierala 		copy = strdup(rule);
142508744b2SEdward Tomasz Napierala 		if (copy == NULL) {
143508744b2SEdward Tomasz Napierala 			warn("strdup");
144508744b2SEdward Tomasz Napierala 			return (NULL);
145508744b2SEdward Tomasz Napierala 		}
146508744b2SEdward Tomasz Napierala 		return (copy);
147508744b2SEdward Tomasz Napierala 	}
1486ec54a57SEdward Tomasz Napierala 
1496ec54a57SEdward Tomasz Napierala 	assert(subject != NULL);
1506ec54a57SEdward Tomasz Napierala 	assert(subject_id != NULL);
1516ec54a57SEdward Tomasz Napierala 	assert(resource != NULL);
1526ec54a57SEdward Tomasz Napierala 	assert(action != NULL);
1536ec54a57SEdward Tomasz Napierala 
1540971623eSEdward Tomasz Napierala 	if (expand_number(amount, &num)) {
1555c859bfbSEdward Tomasz Napierala 		warnx("malformed rule '%s': invalid numeric value '%s'",
1565c859bfbSEdward Tomasz Napierala 		    unexpanded_rule, amount);
157508744b2SEdward Tomasz Napierala 		free(tofree);
1580971623eSEdward Tomasz Napierala 		return (NULL);
1590971623eSEdward Tomasz Napierala 	}
1606ec54a57SEdward Tomasz Napierala 
161f4e361a7SEdward Tomasz Napierala 	if (per == NULL) {
162f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&expanded, "%s:%s:%s:%s=%ju",
163f4e361a7SEdward Tomasz Napierala 		    subject, subject_id, resource, action, (uintmax_t)num);
164f4e361a7SEdward Tomasz Napierala 	} else {
165f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&expanded, "%s:%s:%s:%s=%ju/%s",
166f4e361a7SEdward Tomasz Napierala 		    subject, subject_id, resource, action, (uintmax_t)num, per);
167f4e361a7SEdward Tomasz Napierala 	}
1686ec54a57SEdward Tomasz Napierala 
169f4e361a7SEdward Tomasz Napierala 	if (ret <= 0) {
1700971623eSEdward Tomasz Napierala 		warn("asprintf");
171508744b2SEdward Tomasz Napierala 		free(tofree);
1720971623eSEdward Tomasz Napierala 		return (NULL);
1730971623eSEdward Tomasz Napierala 	}
1746ec54a57SEdward Tomasz Napierala 
175508744b2SEdward Tomasz Napierala 	free(tofree);
176508744b2SEdward Tomasz Napierala 
1776ec54a57SEdward Tomasz Napierala 	return (expanded);
1786ec54a57SEdward Tomasz Napierala }
1796ec54a57SEdward Tomasz Napierala 
1800971623eSEdward Tomasz Napierala static char *
1815c859bfbSEdward Tomasz Napierala expand_rule(const char *rule, bool resolve_ids)
1820971623eSEdward Tomasz Napierala {
1830971623eSEdward Tomasz Napierala 	id_t id;
1840971623eSEdward Tomasz Napierala 	const char *subject, *textid, *rest;
1855c859bfbSEdward Tomasz Napierala 	char *copy, *expanded, *resolved, *tofree;
186f4e361a7SEdward Tomasz Napierala 	int error, ret;
1870971623eSEdward Tomasz Napierala 
1885c859bfbSEdward Tomasz Napierala 	tofree = copy = strdup(rule);
1895c859bfbSEdward Tomasz Napierala 	if (copy == NULL) {
1905c859bfbSEdward Tomasz Napierala 		warn("strdup");
1910971623eSEdward Tomasz Napierala 		return (NULL);
1920971623eSEdward Tomasz Napierala 	}
1935c859bfbSEdward Tomasz Napierala 
1945c859bfbSEdward Tomasz Napierala 	subject = strsep(&copy, ":");
1955c859bfbSEdward Tomasz Napierala 	textid = strsep(&copy, ":");
1965c859bfbSEdward Tomasz Napierala 	if (textid == NULL) {
1975c859bfbSEdward Tomasz Napierala 		warnx("malformed rule '%s': missing subject", rule);
1985c859bfbSEdward Tomasz Napierala 		return (NULL);
1995c859bfbSEdward Tomasz Napierala 	}
2005c859bfbSEdward Tomasz Napierala 	if (copy != NULL)
2015c859bfbSEdward Tomasz Napierala 		rest = copy;
2020971623eSEdward Tomasz Napierala 	else
2030971623eSEdward Tomasz Napierala 		rest = "";
2040971623eSEdward Tomasz Napierala 
2050971623eSEdward Tomasz Napierala 	if (strcasecmp(subject, "u") == 0)
2060971623eSEdward Tomasz Napierala 		subject = "user";
2070971623eSEdward Tomasz Napierala 	else if (strcasecmp(subject, "g") == 0)
2080971623eSEdward Tomasz Napierala 		subject = "group";
2090971623eSEdward Tomasz Napierala 	else if (strcasecmp(subject, "p") == 0)
2100971623eSEdward Tomasz Napierala 		subject = "process";
2110971623eSEdward Tomasz Napierala 	else if (strcasecmp(subject, "l") == 0 ||
2120971623eSEdward Tomasz Napierala 	    strcasecmp(subject, "c") == 0 ||
2130971623eSEdward Tomasz Napierala 	    strcasecmp(subject, "class") == 0)
2140971623eSEdward Tomasz Napierala 		subject = "loginclass";
2150971623eSEdward Tomasz Napierala 	else if (strcasecmp(subject, "j") == 0)
2160971623eSEdward Tomasz Napierala 		subject = "jail";
2170971623eSEdward Tomasz Napierala 
2180971623eSEdward Tomasz Napierala 	if (resolve_ids &&
2190971623eSEdward Tomasz Napierala 	    strcasecmp(subject, "user") == 0 && strlen(textid) > 0) {
2205c859bfbSEdward Tomasz Napierala 		error = parse_user(textid, &id, rule);
2215c859bfbSEdward Tomasz Napierala 		if (error != 0) {
2225c859bfbSEdward Tomasz Napierala 			free(tofree);
2230971623eSEdward Tomasz Napierala 			return (NULL);
2245c859bfbSEdward Tomasz Napierala 		}
225f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
2260971623eSEdward Tomasz Napierala 	} else if (resolve_ids &&
2270971623eSEdward Tomasz Napierala 	    strcasecmp(subject, "group") == 0 && strlen(textid) > 0) {
2285c859bfbSEdward Tomasz Napierala 		error = parse_group(textid, &id, rule);
2295c859bfbSEdward Tomasz Napierala 		if (error != 0) {
2305c859bfbSEdward Tomasz Napierala 			free(tofree);
2310971623eSEdward Tomasz Napierala 			return (NULL);
2325c859bfbSEdward Tomasz Napierala 		}
233f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
2340971623eSEdward Tomasz Napierala 	} else {
235f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&resolved, "%s:%s:%s", subject, textid, rest);
2360971623eSEdward Tomasz Napierala 	}
2370971623eSEdward Tomasz Napierala 
238f4e361a7SEdward Tomasz Napierala 	if (ret <= 0) {
2390971623eSEdward Tomasz Napierala 		warn("asprintf");
2405c859bfbSEdward Tomasz Napierala 		free(tofree);
2410971623eSEdward Tomasz Napierala 		return (NULL);
2420971623eSEdward Tomasz Napierala 	}
2430971623eSEdward Tomasz Napierala 
2445c859bfbSEdward Tomasz Napierala 	free(tofree);
2455c859bfbSEdward Tomasz Napierala 
2465c859bfbSEdward Tomasz Napierala 	expanded = expand_amount(resolved, rule);
2475c859bfbSEdward Tomasz Napierala 	free(resolved);
2485c859bfbSEdward Tomasz Napierala 
2495c859bfbSEdward Tomasz Napierala 	return (expanded);
2500971623eSEdward Tomasz Napierala }
2510971623eSEdward Tomasz Napierala 
2526ec54a57SEdward Tomasz Napierala static char *
2536ec54a57SEdward Tomasz Napierala humanize_ids(char *rule)
2546ec54a57SEdward Tomasz Napierala {
2556ec54a57SEdward Tomasz Napierala 	id_t id;
2566ec54a57SEdward Tomasz Napierala 	struct passwd *pwd;
2576ec54a57SEdward Tomasz Napierala 	struct group *grp;
2586ec54a57SEdward Tomasz Napierala 	const char *subject, *textid, *rest;
259478f7a72SEdward Tomasz Napierala 	char *end, *humanized;
260f4e361a7SEdward Tomasz Napierala 	int ret;
2616ec54a57SEdward Tomasz Napierala 
2626ec54a57SEdward Tomasz Napierala 	subject = strsep(&rule, ":");
2636ec54a57SEdward Tomasz Napierala 	textid = strsep(&rule, ":");
2646ec54a57SEdward Tomasz Napierala 	if (textid == NULL)
2656ec54a57SEdward Tomasz Napierala 		errx(1, "rule passed from the kernel didn't contain subject");
2666ec54a57SEdward Tomasz Napierala 	if (rule != NULL)
2676ec54a57SEdward Tomasz Napierala 		rest = rule;
2686ec54a57SEdward Tomasz Napierala 	else
2696ec54a57SEdward Tomasz Napierala 		rest = "";
2706ec54a57SEdward Tomasz Napierala 
2716ec54a57SEdward Tomasz Napierala 	/* Replace numerical user and group ids with names. */
2726ec54a57SEdward Tomasz Napierala 	if (strcasecmp(subject, "user") == 0) {
273478f7a72SEdward Tomasz Napierala 		id = strtod(textid, &end);
274478f7a72SEdward Tomasz Napierala 		if ((size_t)(end - textid) != strlen(textid))
275478f7a72SEdward Tomasz Napierala 			errx(1, "malformed uid '%s'", textid);
2766ec54a57SEdward Tomasz Napierala 		pwd = getpwuid(id);
2776ec54a57SEdward Tomasz Napierala 		if (pwd != NULL)
2786ec54a57SEdward Tomasz Napierala 			textid = pwd->pw_name;
2796ec54a57SEdward Tomasz Napierala 	} else if (strcasecmp(subject, "group") == 0) {
280478f7a72SEdward Tomasz Napierala 		id = strtod(textid, &end);
281478f7a72SEdward Tomasz Napierala 		if ((size_t)(end - textid) != strlen(textid))
282478f7a72SEdward Tomasz Napierala 			errx(1, "malformed gid '%s'", textid);
2836ec54a57SEdward Tomasz Napierala 		grp = getgrgid(id);
2846ec54a57SEdward Tomasz Napierala 		if (grp != NULL)
2856ec54a57SEdward Tomasz Napierala 			textid = grp->gr_name;
2866ec54a57SEdward Tomasz Napierala 	}
2876ec54a57SEdward Tomasz Napierala 
288f4e361a7SEdward Tomasz Napierala 	ret = asprintf(&humanized, "%s:%s:%s", subject, textid, rest);
289f4e361a7SEdward Tomasz Napierala 	if (ret <= 0)
2906ec54a57SEdward Tomasz Napierala 		err(1, "asprintf");
2916ec54a57SEdward Tomasz Napierala 
2926ec54a57SEdward Tomasz Napierala 	return (humanized);
2936ec54a57SEdward Tomasz Napierala }
2946ec54a57SEdward Tomasz Napierala 
2956ec54a57SEdward Tomasz Napierala static int
2966ec54a57SEdward Tomasz Napierala str2int64(const char *str, int64_t *value)
2976ec54a57SEdward Tomasz Napierala {
2986ec54a57SEdward Tomasz Napierala 	char *end;
2996ec54a57SEdward Tomasz Napierala 
3006ec54a57SEdward Tomasz Napierala 	if (str == NULL)
3016ec54a57SEdward Tomasz Napierala 		return (EINVAL);
3026ec54a57SEdward Tomasz Napierala 
3036ec54a57SEdward Tomasz Napierala 	*value = strtoul(str, &end, 10);
3046ec54a57SEdward Tomasz Napierala 	if ((size_t)(end - str) != strlen(str))
3056ec54a57SEdward Tomasz Napierala 		return (EINVAL);
3066ec54a57SEdward Tomasz Napierala 
3076ec54a57SEdward Tomasz Napierala 	return (0);
3086ec54a57SEdward Tomasz Napierala }
3096ec54a57SEdward Tomasz Napierala 
3106ec54a57SEdward Tomasz Napierala static char *
3116ec54a57SEdward Tomasz Napierala humanize_amount(char *rule)
3126ec54a57SEdward Tomasz Napierala {
3136ec54a57SEdward Tomasz Napierala 	int64_t num;
3146ec54a57SEdward Tomasz Napierala 	const char *subject, *subject_id, *resource, *action, *amount, *per;
3155e7a2555SEdward Tomasz Napierala 	char *copy, *humanized, buf[6], *tofree;
316f4e361a7SEdward Tomasz Napierala 	int ret;
3176ec54a57SEdward Tomasz Napierala 
3185e7a2555SEdward Tomasz Napierala 	tofree = copy = strdup(rule);
3196ec54a57SEdward Tomasz Napierala 	if (copy == NULL)
3206ec54a57SEdward Tomasz Napierala 		err(1, "strdup");
3216ec54a57SEdward Tomasz Napierala 
3226ec54a57SEdward Tomasz Napierala 	subject = strsep(&copy, ":");
3236ec54a57SEdward Tomasz Napierala 	subject_id = strsep(&copy, ":");
3246ec54a57SEdward Tomasz Napierala 	resource = strsep(&copy, ":");
3256ec54a57SEdward Tomasz Napierala 	action = strsep(&copy, "=/");
3266ec54a57SEdward Tomasz Napierala 	amount = strsep(&copy, "/");
3276ec54a57SEdward Tomasz Napierala 	per = copy;
3286ec54a57SEdward Tomasz Napierala 
3296ec54a57SEdward Tomasz Napierala 	if (amount == NULL || strlen(amount) == 0 ||
3306ec54a57SEdward Tomasz Napierala 	    str2int64(amount, &num) != 0) {
3315e7a2555SEdward Tomasz Napierala 		free(tofree);
3326ec54a57SEdward Tomasz Napierala 		return (rule);
3336ec54a57SEdward Tomasz Napierala 	}
3346ec54a57SEdward Tomasz Napierala 
3356ec54a57SEdward Tomasz Napierala 	assert(subject != NULL);
3366ec54a57SEdward Tomasz Napierala 	assert(subject_id != NULL);
3376ec54a57SEdward Tomasz Napierala 	assert(resource != NULL);
3386ec54a57SEdward Tomasz Napierala 	assert(action != NULL);
3396ec54a57SEdward Tomasz Napierala 
3406ec54a57SEdward Tomasz Napierala 	if (humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
3416ec54a57SEdward Tomasz Napierala 	    HN_DECIMAL | HN_NOSPACE) == -1)
3426ec54a57SEdward Tomasz Napierala 		err(1, "humanize_number");
3436ec54a57SEdward Tomasz Napierala 
344f4e361a7SEdward Tomasz Napierala 	if (per == NULL) {
345f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&humanized, "%s:%s:%s:%s=%s",
346f4e361a7SEdward Tomasz Napierala 		    subject, subject_id, resource, action, buf);
347f4e361a7SEdward Tomasz Napierala 	} else {
348f4e361a7SEdward Tomasz Napierala 		ret = asprintf(&humanized, "%s:%s:%s:%s=%s/%s",
349f4e361a7SEdward Tomasz Napierala 		    subject, subject_id, resource, action, buf, per);
350f4e361a7SEdward Tomasz Napierala 	}
3516ec54a57SEdward Tomasz Napierala 
352f4e361a7SEdward Tomasz Napierala 	if (ret <= 0)
3536ec54a57SEdward Tomasz Napierala 		err(1, "asprintf");
3546ec54a57SEdward Tomasz Napierala 
3555e7a2555SEdward Tomasz Napierala 	free(tofree);
3566ec54a57SEdward Tomasz Napierala 	return (humanized);
3576ec54a57SEdward Tomasz Napierala }
3586ec54a57SEdward Tomasz Napierala 
3596ec54a57SEdward Tomasz Napierala /*
3606ec54a57SEdward Tomasz Napierala  * Print rules, one per line.
3616ec54a57SEdward Tomasz Napierala  */
3626ec54a57SEdward Tomasz Napierala static void
3636ec54a57SEdward Tomasz Napierala print_rules(char *rules, int hflag, int nflag)
3646ec54a57SEdward Tomasz Napierala {
3656ec54a57SEdward Tomasz Napierala 	char *rule;
3666ec54a57SEdward Tomasz Napierala 
3676ec54a57SEdward Tomasz Napierala 	while ((rule = strsep(&rules, ",")) != NULL) {
3686ec54a57SEdward Tomasz Napierala 		if (rule[0] == '\0')
3696ec54a57SEdward Tomasz Napierala 			break; /* XXX */
3706ec54a57SEdward Tomasz Napierala 		if (nflag == 0)
3716ec54a57SEdward Tomasz Napierala 			rule = humanize_ids(rule);
3726ec54a57SEdward Tomasz Napierala 		if (hflag)
3736ec54a57SEdward Tomasz Napierala 			rule = humanize_amount(rule);
3746ec54a57SEdward Tomasz Napierala 		printf("%s\n", rule);
3756ec54a57SEdward Tomasz Napierala 	}
3766ec54a57SEdward Tomasz Napierala }
3776ec54a57SEdward Tomasz Napierala 
3786ec54a57SEdward Tomasz Napierala static void
379aae2a24bSEdward Tomasz Napierala enosys(void)
380aae2a24bSEdward Tomasz Napierala {
381aae2a24bSEdward Tomasz Napierala 	size_t racct_enable_len;
382*0a17b9e0SMateusz Guzik 	int error;
383*0a17b9e0SMateusz Guzik 	bool racct_enable;
384aae2a24bSEdward Tomasz Napierala 
385aae2a24bSEdward Tomasz Napierala 	racct_enable_len = sizeof(racct_enable);
386aae2a24bSEdward Tomasz Napierala 	error = sysctlbyname("kern.racct.enable",
387aae2a24bSEdward Tomasz Napierala 	    &racct_enable, &racct_enable_len, NULL, 0);
388aae2a24bSEdward Tomasz Napierala 
389aae2a24bSEdward Tomasz Napierala 	if (error != 0) {
390aae2a24bSEdward Tomasz Napierala 		if (errno == ENOENT)
39182224d7dSEdward Tomasz Napierala 			errx(1, "RACCT/RCTL support not present in kernel; see rctl(8) for details");
392aae2a24bSEdward Tomasz Napierala 
393aae2a24bSEdward Tomasz Napierala 		err(1, "sysctlbyname");
394aae2a24bSEdward Tomasz Napierala 	}
395aae2a24bSEdward Tomasz Napierala 
396*0a17b9e0SMateusz Guzik 	if (!racct_enable)
397aae2a24bSEdward Tomasz Napierala 		errx(1, "RACCT/RCTL present, but disabled; enable using kern.racct.enable=1 tunable");
398aae2a24bSEdward Tomasz Napierala }
399aae2a24bSEdward Tomasz Napierala 
4000971623eSEdward Tomasz Napierala static int
4015c859bfbSEdward Tomasz Napierala add_rule(const char *rule, const char *unexpanded_rule)
4026ec54a57SEdward Tomasz Napierala {
4036ec54a57SEdward Tomasz Napierala 	int error;
4046ec54a57SEdward Tomasz Napierala 
4056ec54a57SEdward Tomasz Napierala 	error = rctl_add_rule(rule, strlen(rule) + 1, NULL, 0);
406aae2a24bSEdward Tomasz Napierala 	if (error != 0) {
407aae2a24bSEdward Tomasz Napierala 		if (errno == ENOSYS)
408aae2a24bSEdward Tomasz Napierala 			enosys();
4095c859bfbSEdward Tomasz Napierala 		warn("failed to add rule '%s'", unexpanded_rule);
4106ec54a57SEdward Tomasz Napierala 	}
4116ec54a57SEdward Tomasz Napierala 
4120971623eSEdward Tomasz Napierala 	return (error);
4130971623eSEdward Tomasz Napierala }
4140971623eSEdward Tomasz Napierala 
4150971623eSEdward Tomasz Napierala static int
4165c859bfbSEdward Tomasz Napierala show_limits(const char *filter, const char *unexpanded_rule,
4175c859bfbSEdward Tomasz Napierala     int hflag, int nflag)
4186ec54a57SEdward Tomasz Napierala {
4196ec54a57SEdward Tomasz Napierala 	int error;
4206ec54a57SEdward Tomasz Napierala 	char *outbuf = NULL;
4216ec54a57SEdward Tomasz Napierala 	size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
4226ec54a57SEdward Tomasz Napierala 
4234cf0d895SEdward Tomasz Napierala 	for (;;) {
4246ec54a57SEdward Tomasz Napierala 		outbuflen *= 4;
4256ec54a57SEdward Tomasz Napierala 		outbuf = realloc(outbuf, outbuflen);
4266ec54a57SEdward Tomasz Napierala 		if (outbuf == NULL)
4276ec54a57SEdward Tomasz Napierala 			err(1, "realloc");
4284cf0d895SEdward Tomasz Napierala 		error = rctl_get_limits(filter, strlen(filter) + 1,
4294cf0d895SEdward Tomasz Napierala 		    outbuf, outbuflen);
4304cf0d895SEdward Tomasz Napierala 		if (error == 0)
4314cf0d895SEdward Tomasz Napierala 			break;
4324cf0d895SEdward Tomasz Napierala 		if (errno == ERANGE)
4334cf0d895SEdward Tomasz Napierala 			continue;
434aae2a24bSEdward Tomasz Napierala 		if (errno == ENOSYS)
435aae2a24bSEdward Tomasz Napierala 			enosys();
4365c859bfbSEdward Tomasz Napierala 		warn("failed to get limits for '%s'", unexpanded_rule);
4374cf0d895SEdward Tomasz Napierala 		free(outbuf);
4384cf0d895SEdward Tomasz Napierala 
4394cf0d895SEdward Tomasz Napierala 		return (error);
440aae2a24bSEdward Tomasz Napierala 	}
4416ec54a57SEdward Tomasz Napierala 
4426ec54a57SEdward Tomasz Napierala 	print_rules(outbuf, hflag, nflag);
4436ec54a57SEdward Tomasz Napierala 	free(outbuf);
4440971623eSEdward Tomasz Napierala 
4450971623eSEdward Tomasz Napierala 	return (error);
4466ec54a57SEdward Tomasz Napierala }
4476ec54a57SEdward Tomasz Napierala 
4480971623eSEdward Tomasz Napierala static int
4495c859bfbSEdward Tomasz Napierala remove_rule(const char *filter, const char *unexpanded_rule)
4506ec54a57SEdward Tomasz Napierala {
4516ec54a57SEdward Tomasz Napierala 	int error;
4526ec54a57SEdward Tomasz Napierala 
4536ec54a57SEdward Tomasz Napierala 	error = rctl_remove_rule(filter, strlen(filter) + 1, NULL, 0);
454aae2a24bSEdward Tomasz Napierala 	if (error != 0) {
455aae2a24bSEdward Tomasz Napierala 		if (errno == ENOSYS)
456aae2a24bSEdward Tomasz Napierala 			enosys();
4575c859bfbSEdward Tomasz Napierala 		warn("failed to remove rule '%s'", unexpanded_rule);
458aae2a24bSEdward Tomasz Napierala 	}
4590971623eSEdward Tomasz Napierala 
4600971623eSEdward Tomasz Napierala 	return (error);
4616ec54a57SEdward Tomasz Napierala }
4626ec54a57SEdward Tomasz Napierala 
4636ec54a57SEdward Tomasz Napierala static char *
4646ec54a57SEdward Tomasz Napierala humanize_usage_amount(char *usage)
4656ec54a57SEdward Tomasz Napierala {
4666ec54a57SEdward Tomasz Napierala 	int64_t num;
4676ec54a57SEdward Tomasz Napierala 	const char *resource, *amount;
4685e7a2555SEdward Tomasz Napierala 	char *copy, *humanized, buf[6], *tofree;
469f4e361a7SEdward Tomasz Napierala 	int ret;
4706ec54a57SEdward Tomasz Napierala 
4715e7a2555SEdward Tomasz Napierala 	tofree = copy = strdup(usage);
4726ec54a57SEdward Tomasz Napierala 	if (copy == NULL)
4736ec54a57SEdward Tomasz Napierala 		err(1, "strdup");
4746ec54a57SEdward Tomasz Napierala 
4756ec54a57SEdward Tomasz Napierala 	resource = strsep(&copy, "=");
4766ec54a57SEdward Tomasz Napierala 	amount = copy;
4776ec54a57SEdward Tomasz Napierala 
4786ec54a57SEdward Tomasz Napierala 	assert(resource != NULL);
4796ec54a57SEdward Tomasz Napierala 	assert(amount != NULL);
4806ec54a57SEdward Tomasz Napierala 
4816ec54a57SEdward Tomasz Napierala 	if (str2int64(amount, &num) != 0 ||
4826ec54a57SEdward Tomasz Napierala 	    humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
4836ec54a57SEdward Tomasz Napierala 	    HN_DECIMAL | HN_NOSPACE) == -1) {
4845e7a2555SEdward Tomasz Napierala 		free(tofree);
4856ec54a57SEdward Tomasz Napierala 		return (usage);
4866ec54a57SEdward Tomasz Napierala 	}
4876ec54a57SEdward Tomasz Napierala 
488f4e361a7SEdward Tomasz Napierala 	ret = asprintf(&humanized, "%s=%s", resource, buf);
489f4e361a7SEdward Tomasz Napierala 	if (ret <= 0)
4906ec54a57SEdward Tomasz Napierala 		err(1, "asprintf");
4916ec54a57SEdward Tomasz Napierala 
4925e7a2555SEdward Tomasz Napierala 	free(tofree);
4936ec54a57SEdward Tomasz Napierala 	return (humanized);
4946ec54a57SEdward Tomasz Napierala }
4956ec54a57SEdward Tomasz Napierala 
4966ec54a57SEdward Tomasz Napierala /*
4976ec54a57SEdward Tomasz Napierala  * Query the kernel about a resource usage and print it out.
4986ec54a57SEdward Tomasz Napierala  */
4990971623eSEdward Tomasz Napierala static int
5005c859bfbSEdward Tomasz Napierala show_usage(const char *filter, const char *unexpanded_rule, int hflag)
5016ec54a57SEdward Tomasz Napierala {
5026ec54a57SEdward Tomasz Napierala 	int error;
5035e7a2555SEdward Tomasz Napierala 	char *copy, *outbuf = NULL, *tmp;
5046ec54a57SEdward Tomasz Napierala 	size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
5056ec54a57SEdward Tomasz Napierala 
5064cf0d895SEdward Tomasz Napierala 	for (;;) {
5076ec54a57SEdward Tomasz Napierala 		outbuflen *= 4;
5086ec54a57SEdward Tomasz Napierala 		outbuf = realloc(outbuf, outbuflen);
5096ec54a57SEdward Tomasz Napierala 		if (outbuf == NULL)
5106ec54a57SEdward Tomasz Napierala 			err(1, "realloc");
5114cf0d895SEdward Tomasz Napierala 		error = rctl_get_racct(filter, strlen(filter) + 1,
5124cf0d895SEdward Tomasz Napierala 		    outbuf, outbuflen);
5134cf0d895SEdward Tomasz Napierala 		if (error == 0)
5144cf0d895SEdward Tomasz Napierala 			break;
5154cf0d895SEdward Tomasz Napierala 		if (errno == ERANGE)
5164cf0d895SEdward Tomasz Napierala 			continue;
517aae2a24bSEdward Tomasz Napierala 		if (errno == ENOSYS)
518aae2a24bSEdward Tomasz Napierala 			enosys();
5195c859bfbSEdward Tomasz Napierala 		warn("failed to show resource consumption for '%s'",
5205c859bfbSEdward Tomasz Napierala 		    unexpanded_rule);
5214cf0d895SEdward Tomasz Napierala 		free(outbuf);
5224cf0d895SEdward Tomasz Napierala 
5234cf0d895SEdward Tomasz Napierala 		return (error);
524aae2a24bSEdward Tomasz Napierala 	}
5256ec54a57SEdward Tomasz Napierala 
5265e7a2555SEdward Tomasz Napierala 	copy = outbuf;
5275e7a2555SEdward Tomasz Napierala 	while ((tmp = strsep(&copy, ",")) != NULL) {
5286ec54a57SEdward Tomasz Napierala 		if (tmp[0] == '\0')
5296ec54a57SEdward Tomasz Napierala 			break; /* XXX */
5306ec54a57SEdward Tomasz Napierala 
5316ec54a57SEdward Tomasz Napierala 		if (hflag)
5326ec54a57SEdward Tomasz Napierala 			tmp = humanize_usage_amount(tmp);
5336ec54a57SEdward Tomasz Napierala 
5346ec54a57SEdward Tomasz Napierala 		printf("%s\n", tmp);
5356ec54a57SEdward Tomasz Napierala 	}
5366ec54a57SEdward Tomasz Napierala 
5376ec54a57SEdward Tomasz Napierala 	free(outbuf);
5380971623eSEdward Tomasz Napierala 
5390971623eSEdward Tomasz Napierala 	return (error);
5406ec54a57SEdward Tomasz Napierala }
5416ec54a57SEdward Tomasz Napierala 
5426ec54a57SEdward Tomasz Napierala /*
5436ec54a57SEdward Tomasz Napierala  * Query the kernel about resource limit rules and print them out.
5446ec54a57SEdward Tomasz Napierala  */
5450971623eSEdward Tomasz Napierala static int
5465c859bfbSEdward Tomasz Napierala show_rules(const char *filter, const char *unexpanded_rule,
5475c859bfbSEdward Tomasz Napierala     int hflag, int nflag)
5486ec54a57SEdward Tomasz Napierala {
5496ec54a57SEdward Tomasz Napierala 	int error;
5506ec54a57SEdward Tomasz Napierala 	char *outbuf = NULL;
5516ec54a57SEdward Tomasz Napierala 	size_t filterlen, outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
5526ec54a57SEdward Tomasz Napierala 
5536ec54a57SEdward Tomasz Napierala 	if (filter != NULL)
5546ec54a57SEdward Tomasz Napierala 		filterlen = strlen(filter) + 1;
5556ec54a57SEdward Tomasz Napierala 	else
5566ec54a57SEdward Tomasz Napierala 		filterlen = 0;
5576ec54a57SEdward Tomasz Napierala 
5584cf0d895SEdward Tomasz Napierala 	for (;;) {
5596ec54a57SEdward Tomasz Napierala 		outbuflen *= 4;
5606ec54a57SEdward Tomasz Napierala 		outbuf = realloc(outbuf, outbuflen);
5616ec54a57SEdward Tomasz Napierala 		if (outbuf == NULL)
5626ec54a57SEdward Tomasz Napierala 			err(1, "realloc");
5636ec54a57SEdward Tomasz Napierala 		error = rctl_get_rules(filter, filterlen, outbuf, outbuflen);
5644cf0d895SEdward Tomasz Napierala 		if (error == 0)
5654cf0d895SEdward Tomasz Napierala 			break;
5664cf0d895SEdward Tomasz Napierala 		if (errno == ERANGE)
5674cf0d895SEdward Tomasz Napierala 			continue;
568aae2a24bSEdward Tomasz Napierala 		if (errno == ENOSYS)
569aae2a24bSEdward Tomasz Napierala 			enosys();
5705c859bfbSEdward Tomasz Napierala 		warn("failed to show rules for '%s'", unexpanded_rule);
5714cf0d895SEdward Tomasz Napierala 		free(outbuf);
5724cf0d895SEdward Tomasz Napierala 
5734cf0d895SEdward Tomasz Napierala 		return (error);
574aae2a24bSEdward Tomasz Napierala 	}
5756ec54a57SEdward Tomasz Napierala 
5766ec54a57SEdward Tomasz Napierala 	print_rules(outbuf, hflag, nflag);
5776ec54a57SEdward Tomasz Napierala 	free(outbuf);
5780971623eSEdward Tomasz Napierala 
5790971623eSEdward Tomasz Napierala 	return (error);
5806ec54a57SEdward Tomasz Napierala }
5816ec54a57SEdward Tomasz Napierala 
5826ec54a57SEdward Tomasz Napierala static void
5836ec54a57SEdward Tomasz Napierala usage(void)
5846ec54a57SEdward Tomasz Napierala {
5856ec54a57SEdward Tomasz Napierala 
5866ec54a57SEdward Tomasz Napierala 	fprintf(stderr, "usage: rctl [ -h ] [-a rule | -l filter | -r filter "
5876ec54a57SEdward Tomasz Napierala 	    "| -u filter | filter]\n");
5886ec54a57SEdward Tomasz Napierala 	exit(1);
5896ec54a57SEdward Tomasz Napierala }
5906ec54a57SEdward Tomasz Napierala 
5916ec54a57SEdward Tomasz Napierala int
592d0ab9cbeSBaptiste Daroussin main(int argc, char **argv)
5936ec54a57SEdward Tomasz Napierala {
5946ec54a57SEdward Tomasz Napierala 	int ch, aflag = 0, hflag = 0, nflag = 0, lflag = 0, rflag = 0,
5956ec54a57SEdward Tomasz Napierala 	    uflag = 0;
5965c859bfbSEdward Tomasz Napierala 	char *rule = NULL, *unexpanded_rule;
5975c859bfbSEdward Tomasz Napierala 	int i, cumulated_error, error;
5986ec54a57SEdward Tomasz Napierala 
5990971623eSEdward Tomasz Napierala 	while ((ch = getopt(argc, argv, "ahlnru")) != -1) {
6006ec54a57SEdward Tomasz Napierala 		switch (ch) {
6016ec54a57SEdward Tomasz Napierala 		case 'a':
6026ec54a57SEdward Tomasz Napierala 			aflag = 1;
6036ec54a57SEdward Tomasz Napierala 			break;
6046ec54a57SEdward Tomasz Napierala 		case 'h':
6056ec54a57SEdward Tomasz Napierala 			hflag = 1;
6066ec54a57SEdward Tomasz Napierala 			break;
6076ec54a57SEdward Tomasz Napierala 		case 'l':
6086ec54a57SEdward Tomasz Napierala 			lflag = 1;
6096ec54a57SEdward Tomasz Napierala 			break;
6106ec54a57SEdward Tomasz Napierala 		case 'n':
6116ec54a57SEdward Tomasz Napierala 			nflag = 1;
6126ec54a57SEdward Tomasz Napierala 			break;
6136ec54a57SEdward Tomasz Napierala 		case 'r':
6146ec54a57SEdward Tomasz Napierala 			rflag = 1;
6156ec54a57SEdward Tomasz Napierala 			break;
6166ec54a57SEdward Tomasz Napierala 		case 'u':
6176ec54a57SEdward Tomasz Napierala 			uflag = 1;
6186ec54a57SEdward Tomasz Napierala 			break;
6196ec54a57SEdward Tomasz Napierala 
6206ec54a57SEdward Tomasz Napierala 		case '?':
6216ec54a57SEdward Tomasz Napierala 		default:
6226ec54a57SEdward Tomasz Napierala 			usage();
6236ec54a57SEdward Tomasz Napierala 		}
6246ec54a57SEdward Tomasz Napierala 	}
6256ec54a57SEdward Tomasz Napierala 
6266ec54a57SEdward Tomasz Napierala 	argc -= optind;
6276ec54a57SEdward Tomasz Napierala 	argv += optind;
6286ec54a57SEdward Tomasz Napierala 
6290971623eSEdward Tomasz Napierala 	if (aflag + lflag + rflag + uflag > 1)
6300971623eSEdward Tomasz Napierala 		errx(1, "at most one of -a, -l, -r, or -u may be specified");
6310971623eSEdward Tomasz Napierala 
6320971623eSEdward Tomasz Napierala 	if (argc == 0) {
6330971623eSEdward Tomasz Napierala 		if (aflag + lflag + rflag + uflag == 0) {
6340971623eSEdward Tomasz Napierala 			rule = strdup("::");
6355c859bfbSEdward Tomasz Napierala 			show_rules(rule, rule, hflag, nflag);
6360971623eSEdward Tomasz Napierala 
6370971623eSEdward Tomasz Napierala 			return (0);
6380971623eSEdward Tomasz Napierala 		}
6390971623eSEdward Tomasz Napierala 
6406ec54a57SEdward Tomasz Napierala 		usage();
6410971623eSEdward Tomasz Napierala 	}
6420971623eSEdward Tomasz Napierala 
6430971623eSEdward Tomasz Napierala 	cumulated_error = 0;
6440971623eSEdward Tomasz Napierala 
6450971623eSEdward Tomasz Napierala 	for (i = 0; i < argc; i++) {
6465c859bfbSEdward Tomasz Napierala 		unexpanded_rule = argv[i];
6470971623eSEdward Tomasz Napierala 
6480971623eSEdward Tomasz Napierala 		/*
6490971623eSEdward Tomasz Napierala 		 * Skip resolving if passed -n _and_ -a.  Ignore -n otherwise,
6500971623eSEdward Tomasz Napierala 		 * so we can still do "rctl -n u:root" and see the rules without
6510971623eSEdward Tomasz Napierala 		 * resolving the UID.
6520971623eSEdward Tomasz Napierala 		 */
6530971623eSEdward Tomasz Napierala 		if (aflag != 0 && nflag != 0)
6545c859bfbSEdward Tomasz Napierala 			rule = expand_rule(unexpanded_rule, false);
6550971623eSEdward Tomasz Napierala 		else
6565c859bfbSEdward Tomasz Napierala 			rule = expand_rule(unexpanded_rule, true);
6576ec54a57SEdward Tomasz Napierala 
6586ec54a57SEdward Tomasz Napierala 		if (rule == NULL) {
6590971623eSEdward Tomasz Napierala 			cumulated_error++;
6600971623eSEdward Tomasz Napierala 			continue;
6616ec54a57SEdward Tomasz Napierala 		}
6626ec54a57SEdward Tomasz Napierala 
6635c859bfbSEdward Tomasz Napierala 		/*
6645c859bfbSEdward Tomasz Napierala 		 * The reason for passing the unexpanded_rule is to make
6655c859bfbSEdward Tomasz Napierala 		 * it easier for the user to search for the problematic
6665c859bfbSEdward Tomasz Napierala 		 * rule in the passed input.
6675c859bfbSEdward Tomasz Napierala 		 */
6686ec54a57SEdward Tomasz Napierala 		if (aflag) {
6695c859bfbSEdward Tomasz Napierala 			error = add_rule(rule, unexpanded_rule);
6700971623eSEdward Tomasz Napierala 		} else if (lflag) {
6715c859bfbSEdward Tomasz Napierala 			error = show_limits(rule, unexpanded_rule,
6725c859bfbSEdward Tomasz Napierala 			    hflag, nflag);
6730971623eSEdward Tomasz Napierala 		} else if (rflag) {
6745c859bfbSEdward Tomasz Napierala 			error = remove_rule(rule, unexpanded_rule);
6750971623eSEdward Tomasz Napierala 		} else if (uflag) {
6765c859bfbSEdward Tomasz Napierala 			error = show_usage(rule, unexpanded_rule, hflag);
6770971623eSEdward Tomasz Napierala 		} else  {
6785c859bfbSEdward Tomasz Napierala 			error = show_rules(rule, unexpanded_rule,
6795c859bfbSEdward Tomasz Napierala 			    hflag, nflag);
6806ec54a57SEdward Tomasz Napierala 		}
6816ec54a57SEdward Tomasz Napierala 
6825c859bfbSEdward Tomasz Napierala 		if (error != 0)
6835c859bfbSEdward Tomasz Napierala 			cumulated_error++;
6845c859bfbSEdward Tomasz Napierala 
6850971623eSEdward Tomasz Napierala 		free(rule);
6866ec54a57SEdward Tomasz Napierala 	}
6876ec54a57SEdward Tomasz Napierala 
6880971623eSEdward Tomasz Napierala 	return (cumulated_error);
6896ec54a57SEdward Tomasz Napierala }
690