1220166Strasz/*-
2220166Strasz * Copyright (c) 2010 The FreeBSD Foundation
3220166Strasz * All rights reserved.
4220166Strasz *
5220166Strasz * This software was developed by Edward Tomasz Napierala under sponsorship
6220166Strasz * from the FreeBSD Foundation.
7220166Strasz *
8220166Strasz * Redistribution and use in source and binary forms, with or without
9220166Strasz * modification, are permitted provided that the following conditions
10220166Strasz * are met:
11220166Strasz * 1. Redistributions of source code must retain the above copyright
12220166Strasz *    notice, this list of conditions and the following disclaimer.
13220166Strasz * 2. Redistributions in binary form must reproduce the above copyright
14220166Strasz *    notice, this list of conditions and the following disclaimer in the
15220166Strasz *    documentation and/or other materials provided with the distribution.
16220166Strasz *
17220166Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18220166Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19220166Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20220166Strasz * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21220166Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22220166Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23220166Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24220166Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25220166Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26220166Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27220166Strasz * SUCH DAMAGE.
28220166Strasz *
29220166Strasz * $FreeBSD: stable/10/usr.bin/rctl/rctl.c 325847 2017-11-15 12:21:06Z bapt $
30220166Strasz */
31220166Strasz
32220166Strasz#include <sys/cdefs.h>
33220166Strasz__FBSDID("$FreeBSD: stable/10/usr.bin/rctl/rctl.c 325847 2017-11-15 12:21:06Z bapt $");
34220166Strasz
35220166Strasz#include <sys/types.h>
36220166Strasz#include <sys/rctl.h>
37284667Strasz#include <sys/sysctl.h>
38220166Strasz#include <assert.h>
39220166Strasz#include <ctype.h>
40220166Strasz#include <err.h>
41220166Strasz#include <errno.h>
42220166Strasz#include <getopt.h>
43220166Strasz#include <grp.h>
44220166Strasz#include <libutil.h>
45220166Strasz#include <pwd.h>
46220166Strasz#include <stdint.h>
47220166Strasz#include <stdio.h>
48220166Strasz#include <stdlib.h>
49220166Strasz#include <string.h>
50220166Strasz
51293686Strasz#define	RCTL_DEFAULT_BUFSIZE	128 * 1024
52220166Strasz
53220166Straszstatic id_t
54220166Straszparse_user(const char *s)
55220166Strasz{
56220166Strasz	id_t id;
57220166Strasz	char *end;
58220166Strasz	struct passwd *pwd;
59220166Strasz
60220166Strasz	pwd = getpwnam(s);
61220166Strasz	if (pwd != NULL)
62220166Strasz		return (pwd->pw_uid);
63220166Strasz
64220166Strasz	if (!isnumber(s[0]))
65220166Strasz		errx(1, "uknown user '%s'", s);
66220166Strasz
67220166Strasz	id = strtod(s, &end);
68220166Strasz	if ((size_t)(end - s) != strlen(s))
69220166Strasz		errx(1, "trailing characters after numerical id");
70220166Strasz
71220166Strasz	return (id);
72220166Strasz}
73220166Strasz
74220166Straszstatic id_t
75220166Straszparse_group(const char *s)
76220166Strasz{
77220166Strasz	id_t id;
78220166Strasz	char *end;
79220166Strasz	struct group *grp;
80220166Strasz
81220166Strasz	grp = getgrnam(s);
82220166Strasz	if (grp != NULL)
83220166Strasz		return (grp->gr_gid);
84220166Strasz
85220166Strasz	if (!isnumber(s[0]))
86220166Strasz		errx(1, "uknown group '%s'", s);
87220166Strasz
88220166Strasz	id = strtod(s, &end);
89220166Strasz	if ((size_t)(end - s) != strlen(s))
90220166Strasz		errx(1, "trailing characters after numerical id");
91220166Strasz
92220166Strasz	return (id);
93220166Strasz}
94220166Strasz
95220166Strasz/*
96220166Strasz * This routine replaces user/group name with numeric id.
97220166Strasz */
98220166Straszstatic char *
99220166Straszresolve_ids(char *rule)
100220166Strasz{
101220166Strasz	id_t id;
102220166Strasz	const char *subject, *textid, *rest;
103220166Strasz	char *resolved;
104220166Strasz
105220166Strasz	subject = strsep(&rule, ":");
106220166Strasz	textid = strsep(&rule, ":");
107220166Strasz	if (textid == NULL)
108220166Strasz		errx(1, "error in rule specification -- no subject");
109220166Strasz	if (rule != NULL)
110220166Strasz		rest = rule;
111220166Strasz	else
112220166Strasz		rest = "";
113220166Strasz
114220166Strasz	if (strcasecmp(subject, "u") == 0)
115220166Strasz		subject = "user";
116220166Strasz	else if (strcasecmp(subject, "g") == 0)
117220166Strasz		subject = "group";
118220166Strasz	else if (strcasecmp(subject, "p") == 0)
119220166Strasz		subject = "process";
120220166Strasz	else if (strcasecmp(subject, "l") == 0 ||
121220166Strasz	    strcasecmp(subject, "c") == 0 ||
122220166Strasz	    strcasecmp(subject, "class") == 0)
123220166Strasz		subject = "loginclass";
124220166Strasz	else if (strcasecmp(subject, "j") == 0)
125220166Strasz		subject = "jail";
126220166Strasz
127220166Strasz	if (strcasecmp(subject, "user") == 0 && strlen(textid) > 0) {
128220166Strasz		id = parse_user(textid);
129220166Strasz		asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
130220166Strasz	} else if (strcasecmp(subject, "group") == 0 && strlen(textid) > 0) {
131220166Strasz		id = parse_group(textid);
132220166Strasz		asprintf(&resolved, "%s:%d:%s", subject, (int)id, rest);
133220166Strasz	} else
134220166Strasz		asprintf(&resolved, "%s:%s:%s", subject, textid, rest);
135220166Strasz
136220166Strasz	if (resolved == NULL)
137220166Strasz		err(1, "asprintf");
138220166Strasz
139220166Strasz	return (resolved);
140220166Strasz}
141220166Strasz
142220166Strasz/*
143220166Strasz * This routine replaces "human-readable" number with its expanded form.
144220166Strasz */
145220166Straszstatic char *
146220166Straszexpand_amount(char *rule)
147220166Strasz{
148220166Strasz	uint64_t num;
149220166Strasz	const char *subject, *subject_id, *resource, *action, *amount, *per;
150220166Strasz	char *copy, *expanded;
151220166Strasz
152220166Strasz	copy = strdup(rule);
153220166Strasz	if (copy == NULL)
154220166Strasz		err(1, "strdup");
155220166Strasz
156220166Strasz	subject = strsep(&copy, ":");
157220166Strasz	subject_id = strsep(&copy, ":");
158220166Strasz	resource = strsep(&copy, ":");
159220166Strasz	action = strsep(&copy, "=/");
160220166Strasz	amount = strsep(&copy, "/");
161220166Strasz	per = copy;
162220166Strasz
163220166Strasz	if (amount == NULL || strlen(amount) == 0) {
164220166Strasz		free(copy);
165220166Strasz		return (rule);
166220166Strasz	}
167220166Strasz
168220166Strasz	assert(subject != NULL);
169220166Strasz	assert(subject_id != NULL);
170220166Strasz	assert(resource != NULL);
171220166Strasz	assert(action != NULL);
172220166Strasz
173220166Strasz	if (expand_number(amount, &num))
174220166Strasz		err(1, "expand_number");
175220166Strasz
176220166Strasz	if (per == NULL)
177220166Strasz		asprintf(&expanded, "%s:%s:%s:%s=%ju", subject, subject_id,
178220166Strasz		    resource, action, (uintmax_t)num);
179220166Strasz	else
180220166Strasz		asprintf(&expanded, "%s:%s:%s:%s=%ju/%s", subject, subject_id,
181220166Strasz		    resource, action, (uintmax_t)num, per);
182220166Strasz
183220166Strasz	if (expanded == NULL)
184220166Strasz		err(1, "asprintf");
185220166Strasz
186220166Strasz	return (expanded);
187220166Strasz}
188220166Strasz
189220166Straszstatic char *
190220166Straszhumanize_ids(char *rule)
191220166Strasz{
192220166Strasz	id_t id;
193220166Strasz	struct passwd *pwd;
194220166Strasz	struct group *grp;
195220166Strasz	const char *subject, *textid, *rest;
196220166Strasz	char *humanized;
197220166Strasz
198220166Strasz	subject = strsep(&rule, ":");
199220166Strasz	textid = strsep(&rule, ":");
200220166Strasz	if (textid == NULL)
201220166Strasz		errx(1, "rule passed from the kernel didn't contain subject");
202220166Strasz	if (rule != NULL)
203220166Strasz		rest = rule;
204220166Strasz	else
205220166Strasz		rest = "";
206220166Strasz
207220166Strasz	/* Replace numerical user and group ids with names. */
208220166Strasz	if (strcasecmp(subject, "user") == 0) {
209220166Strasz		id = parse_user(textid);
210220166Strasz		pwd = getpwuid(id);
211220166Strasz		if (pwd != NULL)
212220166Strasz			textid = pwd->pw_name;
213220166Strasz	} else if (strcasecmp(subject, "group") == 0) {
214220166Strasz		id = parse_group(textid);
215220166Strasz		grp = getgrgid(id);
216220166Strasz		if (grp != NULL)
217220166Strasz			textid = grp->gr_name;
218220166Strasz	}
219220166Strasz
220220166Strasz	asprintf(&humanized, "%s:%s:%s", subject, textid, rest);
221220166Strasz
222220166Strasz	if (humanized == NULL)
223220166Strasz		err(1, "asprintf");
224220166Strasz
225220166Strasz	return (humanized);
226220166Strasz}
227220166Strasz
228220166Straszstatic int
229220166Straszstr2int64(const char *str, int64_t *value)
230220166Strasz{
231220166Strasz	char *end;
232220166Strasz
233220166Strasz	if (str == NULL)
234220166Strasz		return (EINVAL);
235220166Strasz
236220166Strasz	*value = strtoul(str, &end, 10);
237220166Strasz	if ((size_t)(end - str) != strlen(str))
238220166Strasz		return (EINVAL);
239220166Strasz
240220166Strasz	return (0);
241220166Strasz}
242220166Strasz
243220166Straszstatic char *
244220166Straszhumanize_amount(char *rule)
245220166Strasz{
246220166Strasz	int64_t num;
247220166Strasz	const char *subject, *subject_id, *resource, *action, *amount, *per;
248220166Strasz	char *copy, *humanized, buf[6];
249220166Strasz
250220166Strasz	copy = strdup(rule);
251220166Strasz	if (copy == NULL)
252220166Strasz		err(1, "strdup");
253220166Strasz
254220166Strasz	subject = strsep(&copy, ":");
255220166Strasz	subject_id = strsep(&copy, ":");
256220166Strasz	resource = strsep(&copy, ":");
257220166Strasz	action = strsep(&copy, "=/");
258220166Strasz	amount = strsep(&copy, "/");
259220166Strasz	per = copy;
260220166Strasz
261220166Strasz	if (amount == NULL || strlen(amount) == 0 ||
262220166Strasz	    str2int64(amount, &num) != 0) {
263220166Strasz		free(copy);
264220166Strasz		return (rule);
265220166Strasz	}
266220166Strasz
267220166Strasz	assert(subject != NULL);
268220166Strasz	assert(subject_id != NULL);
269220166Strasz	assert(resource != NULL);
270220166Strasz	assert(action != NULL);
271220166Strasz
272220166Strasz	if (humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
273220166Strasz	    HN_DECIMAL | HN_NOSPACE) == -1)
274220166Strasz		err(1, "humanize_number");
275220166Strasz
276220166Strasz	if (per == NULL)
277220166Strasz		asprintf(&humanized, "%s:%s:%s:%s=%s", subject, subject_id,
278220166Strasz		    resource, action, buf);
279220166Strasz	else
280220166Strasz		asprintf(&humanized, "%s:%s:%s:%s=%s/%s", subject, subject_id,
281220166Strasz		    resource, action, buf, per);
282220166Strasz
283220166Strasz	if (humanized == NULL)
284220166Strasz		err(1, "asprintf");
285220166Strasz
286220166Strasz	return (humanized);
287220166Strasz}
288220166Strasz
289220166Strasz/*
290220166Strasz * Print rules, one per line.
291220166Strasz */
292220166Straszstatic void
293220166Straszprint_rules(char *rules, int hflag, int nflag)
294220166Strasz{
295220166Strasz	char *rule;
296220166Strasz
297220166Strasz	while ((rule = strsep(&rules, ",")) != NULL) {
298220166Strasz		if (rule[0] == '\0')
299220166Strasz			break; /* XXX */
300220166Strasz		if (nflag == 0)
301220166Strasz			rule = humanize_ids(rule);
302220166Strasz		if (hflag)
303220166Strasz			rule = humanize_amount(rule);
304220166Strasz		printf("%s\n", rule);
305220166Strasz	}
306220166Strasz}
307220166Strasz
308220166Straszstatic void
309284667Straszenosys(void)
310284667Strasz{
311284667Strasz	int error, racct_enable;
312284667Strasz	size_t racct_enable_len;
313284667Strasz
314284667Strasz	racct_enable_len = sizeof(racct_enable);
315284667Strasz	error = sysctlbyname("kern.racct.enable",
316284667Strasz	    &racct_enable, &racct_enable_len, NULL, 0);
317284667Strasz
318284667Strasz	if (error != 0) {
319284667Strasz		if (errno == ENOENT)
320284668Strasz			errx(1, "RACCT/RCTL support not present in kernel; see rctl(8) for details");
321284667Strasz
322284667Strasz		err(1, "sysctlbyname");
323284667Strasz	}
324284667Strasz
325284667Strasz	if (racct_enable == 0)
326284667Strasz		errx(1, "RACCT/RCTL present, but disabled; enable using kern.racct.enable=1 tunable");
327284667Strasz}
328284667Strasz
329284667Straszstatic void
330220166Straszadd_rule(char *rule)
331220166Strasz{
332220166Strasz	int error;
333220166Strasz
334220166Strasz	error = rctl_add_rule(rule, strlen(rule) + 1, NULL, 0);
335284667Strasz	if (error != 0) {
336284667Strasz		if (errno == ENOSYS)
337284667Strasz			enosys();
338220166Strasz		err(1, "rctl_add_rule");
339284667Strasz	}
340220166Strasz	free(rule);
341220166Strasz}
342220166Strasz
343220166Straszstatic void
344220166Straszshow_limits(char *filter, int hflag, int nflag)
345220166Strasz{
346220166Strasz	int error;
347220166Strasz	char *outbuf = NULL;
348220166Strasz	size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
349220166Strasz
350220166Strasz	do {
351220166Strasz		outbuflen *= 4;
352220166Strasz		outbuf = realloc(outbuf, outbuflen);
353220166Strasz		if (outbuf == NULL)
354220166Strasz			err(1, "realloc");
355220166Strasz
356220166Strasz		error = rctl_get_limits(filter, strlen(filter) + 1, outbuf,
357220166Strasz		    outbuflen);
358284667Strasz		if (error && errno != ERANGE) {
359284667Strasz			if (errno == ENOSYS)
360284667Strasz				enosys();
361220166Strasz			err(1, "rctl_get_limits");
362284667Strasz		}
363220166Strasz	} while (error && errno == ERANGE);
364220166Strasz
365220166Strasz	print_rules(outbuf, hflag, nflag);
366220166Strasz	free(filter);
367220166Strasz	free(outbuf);
368220166Strasz}
369220166Strasz
370220166Straszstatic void
371220166Straszremove_rule(char *filter)
372220166Strasz{
373220166Strasz	int error;
374220166Strasz
375220166Strasz	error = rctl_remove_rule(filter, strlen(filter) + 1, NULL, 0);
376284667Strasz	if (error != 0) {
377284667Strasz		if (errno == ENOSYS)
378284667Strasz			enosys();
379220166Strasz		err(1, "rctl_remove_rule");
380284667Strasz	}
381220166Strasz	free(filter);
382220166Strasz}
383220166Strasz
384220166Straszstatic char *
385220166Straszhumanize_usage_amount(char *usage)
386220166Strasz{
387220166Strasz	int64_t num;
388220166Strasz	const char *resource, *amount;
389220166Strasz	char *copy, *humanized, buf[6];
390220166Strasz
391220166Strasz	copy = strdup(usage);
392220166Strasz	if (copy == NULL)
393220166Strasz		err(1, "strdup");
394220166Strasz
395220166Strasz	resource = strsep(&copy, "=");
396220166Strasz	amount = copy;
397220166Strasz
398220166Strasz	assert(resource != NULL);
399220166Strasz	assert(amount != NULL);
400220166Strasz
401220166Strasz	if (str2int64(amount, &num) != 0 ||
402220166Strasz	    humanize_number(buf, sizeof(buf), num, "", HN_AUTOSCALE,
403220166Strasz	    HN_DECIMAL | HN_NOSPACE) == -1) {
404220166Strasz		free(copy);
405220166Strasz		return (usage);
406220166Strasz	}
407220166Strasz
408220166Strasz	asprintf(&humanized, "%s=%s", resource, buf);
409220166Strasz	if (humanized == NULL)
410220166Strasz		err(1, "asprintf");
411220166Strasz
412220166Strasz	return (humanized);
413220166Strasz}
414220166Strasz
415220166Strasz/*
416220166Strasz * Query the kernel about a resource usage and print it out.
417220166Strasz */
418220166Straszstatic void
419220166Straszshow_usage(char *filter, int hflag)
420220166Strasz{
421220166Strasz	int error;
422220166Strasz	char *outbuf = NULL, *tmp;
423220166Strasz	size_t outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
424220166Strasz
425220166Strasz	do {
426220166Strasz		outbuflen *= 4;
427220166Strasz		outbuf = realloc(outbuf, outbuflen);
428220166Strasz		if (outbuf == NULL)
429220166Strasz			err(1, "realloc");
430220166Strasz
431220166Strasz		error = rctl_get_racct(filter, strlen(filter) + 1, outbuf,
432220166Strasz		    outbuflen);
433284667Strasz		if (error && errno != ERANGE) {
434284667Strasz			if (errno == ENOSYS)
435284667Strasz				enosys();
436220166Strasz			err(1, "rctl_get_racct");
437284667Strasz		}
438220166Strasz	} while (error && errno == ERANGE);
439220166Strasz
440220166Strasz	while ((tmp = strsep(&outbuf, ",")) != NULL) {
441220166Strasz		if (tmp[0] == '\0')
442220166Strasz			break; /* XXX */
443220166Strasz
444220166Strasz		if (hflag)
445220166Strasz			tmp = humanize_usage_amount(tmp);
446220166Strasz
447220166Strasz		printf("%s\n", tmp);
448220166Strasz	}
449220166Strasz
450220166Strasz	free(filter);
451220166Strasz	free(outbuf);
452220166Strasz}
453220166Strasz
454220166Strasz/*
455220166Strasz * Query the kernel about resource limit rules and print them out.
456220166Strasz */
457220166Straszstatic void
458220166Straszshow_rules(char *filter, int hflag, int nflag)
459220166Strasz{
460220166Strasz	int error;
461220166Strasz	char *outbuf = NULL;
462220166Strasz	size_t filterlen, outbuflen = RCTL_DEFAULT_BUFSIZE / 4;
463220166Strasz
464220166Strasz	if (filter != NULL)
465220166Strasz		filterlen = strlen(filter) + 1;
466220166Strasz	else
467220166Strasz		filterlen = 0;
468220166Strasz
469220166Strasz	do {
470220166Strasz		outbuflen *= 4;
471220166Strasz		outbuf = realloc(outbuf, outbuflen);
472220166Strasz		if (outbuf == NULL)
473220166Strasz			err(1, "realloc");
474220166Strasz
475220166Strasz		error = rctl_get_rules(filter, filterlen, outbuf, outbuflen);
476284667Strasz		if (error && errno != ERANGE) {
477284667Strasz			if (errno == ENOSYS)
478284667Strasz				enosys();
479220166Strasz			err(1, "rctl_get_rules");
480284667Strasz		}
481220166Strasz	} while (error && errno == ERANGE);
482220166Strasz
483220166Strasz	print_rules(outbuf, hflag, nflag);
484220166Strasz	free(outbuf);
485220166Strasz}
486220166Strasz
487220166Straszstatic void
488220166Straszusage(void)
489220166Strasz{
490220166Strasz
491220166Strasz	fprintf(stderr, "usage: rctl [ -h ] [-a rule | -l filter | -r filter "
492220166Strasz	    "| -u filter | filter]\n");
493220166Strasz	exit(1);
494220166Strasz}
495220166Strasz
496220166Straszint
497325847Sbaptmain(int argc, char **argv)
498220166Strasz{
499220166Strasz	int ch, aflag = 0, hflag = 0, nflag = 0, lflag = 0, rflag = 0,
500220166Strasz	    uflag = 0;
501220166Strasz	char *rule = NULL;
502220166Strasz
503220166Strasz	while ((ch = getopt(argc, argv, "a:hl:nr:u:")) != -1) {
504220166Strasz		switch (ch) {
505220166Strasz		case 'a':
506220166Strasz			aflag = 1;
507220166Strasz			rule = strdup(optarg);
508220166Strasz			break;
509220166Strasz		case 'h':
510220166Strasz			hflag = 1;
511220166Strasz			break;
512220166Strasz		case 'l':
513220166Strasz			lflag = 1;
514220166Strasz			rule = strdup(optarg);
515220166Strasz			break;
516220166Strasz		case 'n':
517220166Strasz			nflag = 1;
518220166Strasz			break;
519220166Strasz		case 'r':
520220166Strasz			rflag = 1;
521220166Strasz			rule = strdup(optarg);
522220166Strasz			break;
523220166Strasz		case 'u':
524220166Strasz			uflag = 1;
525220166Strasz			rule = strdup(optarg);
526220166Strasz			break;
527220166Strasz
528220166Strasz		case '?':
529220166Strasz		default:
530220166Strasz			usage();
531220166Strasz		}
532220166Strasz	}
533220166Strasz
534220166Strasz	argc -= optind;
535220166Strasz	argv += optind;
536220166Strasz
537220166Strasz	if (argc > 1)
538220166Strasz		usage();
539220166Strasz
540220166Strasz	if (rule == NULL) {
541220166Strasz		if (argc == 1)
542220166Strasz			rule = strdup(argv[0]);
543220166Strasz		else
544220166Strasz			rule = strdup("::");
545220166Strasz	}
546220166Strasz
547220166Strasz	if (aflag + lflag + rflag + uflag + argc > 1)
548220166Strasz		errx(1, "only one flag or argument may be specified "
549220166Strasz		    "at the same time");
550220166Strasz
551220166Strasz	rule = resolve_ids(rule);
552220166Strasz	rule = expand_amount(rule);
553220166Strasz
554220166Strasz	if (aflag) {
555220166Strasz		add_rule(rule);
556220166Strasz		return (0);
557220166Strasz	}
558220166Strasz
559220166Strasz	if (lflag) {
560220166Strasz		show_limits(rule, hflag, nflag);
561220166Strasz		return (0);
562220166Strasz	}
563220166Strasz
564220166Strasz	if (rflag) {
565220166Strasz		remove_rule(rule);
566220166Strasz		return (0);
567220166Strasz	}
568220166Strasz
569220166Strasz	if (uflag) {
570220166Strasz		show_usage(rule, hflag);
571220166Strasz		return (0);
572220166Strasz	}
573220166Strasz
574220166Strasz	show_rules(rule, hflag, nflag);
575220166Strasz	return (0);
576220166Strasz}
577