kern_rctl.c revision 234380
1220163Strasz/*-
2220163Strasz * Copyright (c) 2010 The FreeBSD Foundation
3220163Strasz * All rights reserved.
4220163Strasz *
5220163Strasz * This software was developed by Edward Tomasz Napierala under sponsorship
6220163Strasz * from the FreeBSD Foundation.
7220163Strasz *
8220163Strasz * Redistribution and use in source and binary forms, with or without
9220163Strasz * modification, are permitted provided that the following conditions
10220163Strasz * are met:
11220163Strasz * 1. Redistributions of source code must retain the above copyright
12220163Strasz *    notice, this list of conditions and the following disclaimer.
13220163Strasz * 2. Redistributions in binary form must reproduce the above copyright
14220163Strasz *    notice, this list of conditions and the following disclaimer in the
15220163Strasz *    documentation and/or other materials provided with the distribution.
16220163Strasz *
17220163Strasz * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18220163Strasz * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19220163Strasz * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20220163Strasz * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21220163Strasz * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22220163Strasz * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23220163Strasz * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24220163Strasz * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25220163Strasz * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26220163Strasz * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27220163Strasz * SUCH DAMAGE.
28220163Strasz *
29220163Strasz * $FreeBSD: head/sys/kern/kern_rctl.c 234380 2012-04-17 13:28:14Z trasz $
30220163Strasz */
31220163Strasz
32220163Strasz#include <sys/cdefs.h>
33220163Strasz__FBSDID("$FreeBSD: head/sys/kern/kern_rctl.c 234380 2012-04-17 13:28:14Z trasz $");
34220163Strasz
35220163Strasz#include <sys/param.h>
36220163Strasz#include <sys/bus.h>
37220163Strasz#include <sys/malloc.h>
38220163Strasz#include <sys/queue.h>
39220163Strasz#include <sys/refcount.h>
40220163Strasz#include <sys/jail.h>
41220163Strasz#include <sys/kernel.h>
42220163Strasz#include <sys/limits.h>
43220163Strasz#include <sys/loginclass.h>
44220163Strasz#include <sys/priv.h>
45220163Strasz#include <sys/proc.h>
46220163Strasz#include <sys/racct.h>
47220163Strasz#include <sys/rctl.h>
48220163Strasz#include <sys/resourcevar.h>
49220163Strasz#include <sys/sx.h>
50220163Strasz#include <sys/sysent.h>
51220163Strasz#include <sys/sysproto.h>
52220163Strasz#include <sys/systm.h>
53220163Strasz#include <sys/types.h>
54220163Strasz#include <sys/eventhandler.h>
55220163Strasz#include <sys/lock.h>
56220163Strasz#include <sys/mutex.h>
57220163Strasz#include <sys/rwlock.h>
58220163Strasz#include <sys/sbuf.h>
59220163Strasz#include <sys/taskqueue.h>
60220163Strasz#include <sys/tree.h>
61220163Strasz#include <vm/uma.h>
62220163Strasz
63220163Strasz#ifdef RCTL
64220163Strasz#ifndef RACCT
65220163Strasz#error "The RCTL option requires the RACCT option"
66220163Strasz#endif
67220163Strasz
68220163StraszFEATURE(rctl, "Resource Limits");
69220163Strasz
70220163Strasz#define	HRF_DEFAULT		0
71220163Strasz#define	HRF_DONT_INHERIT	1
72220163Strasz#define	HRF_DONT_ACCUMULATE	2
73220163Strasz
74220163Strasz/* Default buffer size for rctl_get_rules(2). */
75220163Strasz#define	RCTL_DEFAULT_BUFSIZE	4096
76234380Strasz#define	RCTL_MAX_INBUFLEN	4096
77220163Strasz#define	RCTL_LOG_BUFSIZE	128
78220163Strasz
79220163Strasz/*
80220163Strasz * 'rctl_rule_link' connects a rule with every racct it's related to.
81220163Strasz * For example, rule 'user:X:openfiles:deny=N/process' is linked
82220163Strasz * with uidinfo for user X, and to each process of that user.
83220163Strasz */
84220163Straszstruct rctl_rule_link {
85220163Strasz	LIST_ENTRY(rctl_rule_link)	rrl_next;
86220163Strasz	struct rctl_rule		*rrl_rule;
87220163Strasz	int				rrl_exceeded;
88220163Strasz};
89220163Strasz
90220163Straszstruct dict {
91220163Strasz	const char	*d_name;
92220163Strasz	int		d_value;
93220163Strasz};
94220163Strasz
95220163Straszstatic struct dict subjectnames[] = {
96220163Strasz	{ "process", RCTL_SUBJECT_TYPE_PROCESS },
97220163Strasz	{ "user", RCTL_SUBJECT_TYPE_USER },
98220163Strasz	{ "loginclass", RCTL_SUBJECT_TYPE_LOGINCLASS },
99220163Strasz	{ "jail", RCTL_SUBJECT_TYPE_JAIL },
100220163Strasz	{ NULL, -1 }};
101220163Strasz
102220163Straszstatic struct dict resourcenames[] = {
103224036Strasz	{ "cputime", RACCT_CPU },
104224036Strasz	{ "datasize", RACCT_DATA },
105224036Strasz	{ "stacksize", RACCT_STACK },
106224036Strasz	{ "coredumpsize", RACCT_CORE },
107224036Strasz	{ "memoryuse", RACCT_RSS },
108224036Strasz	{ "memorylocked", RACCT_MEMLOCK },
109224036Strasz	{ "maxproc", RACCT_NPROC },
110224036Strasz	{ "openfiles", RACCT_NOFILE },
111224036Strasz	{ "vmemoryuse", RACCT_VMEM },
112224036Strasz	{ "pseudoterminals", RACCT_NPTS },
113224036Strasz	{ "swapuse", RACCT_SWAP },
114220163Strasz	{ "nthr", RACCT_NTHR },
115220163Strasz	{ "msgqqueued", RACCT_MSGQQUEUED },
116220163Strasz	{ "msgqsize", RACCT_MSGQSIZE },
117220163Strasz	{ "nmsgq", RACCT_NMSGQ },
118220163Strasz	{ "nsem", RACCT_NSEM },
119220163Strasz	{ "nsemop", RACCT_NSEMOP },
120220163Strasz	{ "nshm", RACCT_NSHM },
121220163Strasz	{ "shmsize", RACCT_SHMSIZE },
122220163Strasz	{ "wallclock", RACCT_WALLCLOCK },
123220163Strasz	{ NULL, -1 }};
124220163Strasz
125220163Straszstatic struct dict actionnames[] = {
126220163Strasz	{ "sighup", RCTL_ACTION_SIGHUP },
127220163Strasz	{ "sigint", RCTL_ACTION_SIGINT },
128220163Strasz	{ "sigquit", RCTL_ACTION_SIGQUIT },
129220163Strasz	{ "sigill", RCTL_ACTION_SIGILL },
130220163Strasz	{ "sigtrap", RCTL_ACTION_SIGTRAP },
131220163Strasz	{ "sigabrt", RCTL_ACTION_SIGABRT },
132220163Strasz	{ "sigemt", RCTL_ACTION_SIGEMT },
133220163Strasz	{ "sigfpe", RCTL_ACTION_SIGFPE },
134220163Strasz	{ "sigkill", RCTL_ACTION_SIGKILL },
135220163Strasz	{ "sigbus", RCTL_ACTION_SIGBUS },
136220163Strasz	{ "sigsegv", RCTL_ACTION_SIGSEGV },
137220163Strasz	{ "sigsys", RCTL_ACTION_SIGSYS },
138220163Strasz	{ "sigpipe", RCTL_ACTION_SIGPIPE },
139220163Strasz	{ "sigalrm", RCTL_ACTION_SIGALRM },
140220163Strasz	{ "sigterm", RCTL_ACTION_SIGTERM },
141220163Strasz	{ "sigurg", RCTL_ACTION_SIGURG },
142220163Strasz	{ "sigstop", RCTL_ACTION_SIGSTOP },
143220163Strasz	{ "sigtstp", RCTL_ACTION_SIGTSTP },
144220163Strasz	{ "sigchld", RCTL_ACTION_SIGCHLD },
145220163Strasz	{ "sigttin", RCTL_ACTION_SIGTTIN },
146220163Strasz	{ "sigttou", RCTL_ACTION_SIGTTOU },
147220163Strasz	{ "sigio", RCTL_ACTION_SIGIO },
148220163Strasz	{ "sigxcpu", RCTL_ACTION_SIGXCPU },
149220163Strasz	{ "sigxfsz", RCTL_ACTION_SIGXFSZ },
150220163Strasz	{ "sigvtalrm", RCTL_ACTION_SIGVTALRM },
151220163Strasz	{ "sigprof", RCTL_ACTION_SIGPROF },
152220163Strasz	{ "sigwinch", RCTL_ACTION_SIGWINCH },
153220163Strasz	{ "siginfo", RCTL_ACTION_SIGINFO },
154220163Strasz	{ "sigusr1", RCTL_ACTION_SIGUSR1 },
155220163Strasz	{ "sigusr2", RCTL_ACTION_SIGUSR2 },
156220163Strasz	{ "sigthr", RCTL_ACTION_SIGTHR },
157220163Strasz	{ "deny", RCTL_ACTION_DENY },
158220163Strasz	{ "log", RCTL_ACTION_LOG },
159220163Strasz	{ "devctl", RCTL_ACTION_DEVCTL },
160220163Strasz	{ NULL, -1 }};
161220163Strasz
162220163Straszstatic void rctl_init(void);
163220163StraszSYSINIT(rctl, SI_SUB_RACCT, SI_ORDER_FIRST, rctl_init, NULL);
164220163Strasz
165220163Straszstatic uma_zone_t rctl_rule_link_zone;
166220163Straszstatic uma_zone_t rctl_rule_zone;
167220163Straszstatic struct rwlock rctl_lock;
168220163StraszRW_SYSINIT(rctl_lock, &rctl_lock, "RCTL lock");
169220163Strasz
170220163Straszstatic int rctl_rule_fully_specified(const struct rctl_rule *rule);
171220163Straszstatic void rctl_rule_to_sbuf(struct sbuf *sb, const struct rctl_rule *rule);
172220163Strasz
173227293Sedstatic MALLOC_DEFINE(M_RCTL, "rctl", "Resource Limits");
174220163Strasz
175220163Straszstatic const char *
176220163Straszrctl_subject_type_name(int subject)
177220163Strasz{
178220163Strasz	int i;
179220163Strasz
180220163Strasz	for (i = 0; subjectnames[i].d_name != NULL; i++) {
181220163Strasz		if (subjectnames[i].d_value == subject)
182220163Strasz			return (subjectnames[i].d_name);
183220163Strasz	}
184220163Strasz
185220163Strasz	panic("rctl_subject_type_name: unknown subject type %d", subject);
186220163Strasz}
187220163Strasz
188220163Straszstatic const char *
189220163Straszrctl_action_name(int action)
190220163Strasz{
191220163Strasz	int i;
192220163Strasz
193220163Strasz	for (i = 0; actionnames[i].d_name != NULL; i++) {
194220163Strasz		if (actionnames[i].d_value == action)
195220163Strasz			return (actionnames[i].d_name);
196220163Strasz	}
197220163Strasz
198220163Strasz	panic("rctl_action_name: unknown action %d", action);
199220163Strasz}
200220163Strasz
201220163Straszconst char *
202220163Straszrctl_resource_name(int resource)
203220163Strasz{
204220163Strasz	int i;
205220163Strasz
206220163Strasz	for (i = 0; resourcenames[i].d_name != NULL; i++) {
207220163Strasz		if (resourcenames[i].d_value == resource)
208220163Strasz			return (resourcenames[i].d_name);
209220163Strasz	}
210220163Strasz
211220163Strasz	panic("rctl_resource_name: unknown resource %d", resource);
212220163Strasz}
213220163Strasz
214220163Strasz/*
215220163Strasz * Return the amount of resource that can be allocated by 'p' before
216220163Strasz * hitting 'rule'.
217220163Strasz */
218220163Straszstatic int64_t
219220163Straszrctl_available_resource(const struct proc *p, const struct rctl_rule *rule)
220220163Strasz{
221220163Strasz	int resource;
222220163Strasz	int64_t available = INT64_MAX;
223220163Strasz	struct ucred *cred = p->p_ucred;
224220163Strasz
225220163Strasz	rw_assert(&rctl_lock, RA_LOCKED);
226220163Strasz
227220163Strasz	resource = rule->rr_resource;
228220163Strasz	switch (rule->rr_per) {
229220163Strasz	case RCTL_SUBJECT_TYPE_PROCESS:
230220163Strasz		available = rule->rr_amount -
231220163Strasz		    p->p_racct->r_resources[resource];
232220163Strasz		break;
233220163Strasz	case RCTL_SUBJECT_TYPE_USER:
234220163Strasz		available = rule->rr_amount -
235220163Strasz		    cred->cr_ruidinfo->ui_racct->r_resources[resource];
236220163Strasz		break;
237220163Strasz	case RCTL_SUBJECT_TYPE_LOGINCLASS:
238220163Strasz		available = rule->rr_amount -
239220163Strasz		    cred->cr_loginclass->lc_racct->r_resources[resource];
240220163Strasz		break;
241220163Strasz	case RCTL_SUBJECT_TYPE_JAIL:
242220163Strasz		available = rule->rr_amount -
243221362Strasz		    cred->cr_prison->pr_prison_racct->prr_racct->
244221362Strasz		        r_resources[resource];
245220163Strasz		break;
246220163Strasz	default:
247220163Strasz		panic("rctl_compute_available: unknown per %d",
248220163Strasz		    rule->rr_per);
249220163Strasz	}
250220163Strasz
251220163Strasz	return (available);
252220163Strasz}
253220163Strasz
254220163Strasz/*
255220163Strasz * Return non-zero if allocating 'amount' by proc 'p' would exceed
256220163Strasz * resource limit specified by 'rule'.
257220163Strasz */
258220163Straszstatic int
259220163Straszrctl_would_exceed(const struct proc *p, const struct rctl_rule *rule,
260220163Strasz    int64_t amount)
261220163Strasz{
262220163Strasz	int64_t available;
263220163Strasz
264220163Strasz	rw_assert(&rctl_lock, RA_LOCKED);
265220163Strasz
266220163Strasz	available = rctl_available_resource(p, rule);
267220163Strasz	if (available >= amount)
268220163Strasz		return (0);
269220163Strasz
270220163Strasz	return (1);
271220163Strasz}
272220163Strasz
273220163Strasz/*
274220163Strasz * Check whether the proc 'p' can allocate 'amount' of 'resource' in addition
275220163Strasz * to what it keeps allocated now.  Returns non-zero if the allocation should
276220163Strasz * be denied, 0 otherwise.
277220163Strasz */
278220163Straszint
279220163Straszrctl_enforce(struct proc *p, int resource, uint64_t amount)
280220163Strasz{
281220163Strasz	struct rctl_rule *rule;
282220163Strasz	struct rctl_rule_link *link;
283220163Strasz	struct sbuf sb;
284220163Strasz	int should_deny = 0;
285220163Strasz	char *buf;
286220163Strasz	static int curtime = 0;
287220163Strasz	static struct timeval lasttime;
288220163Strasz
289220163Strasz	rw_rlock(&rctl_lock);
290220163Strasz
291220163Strasz	/*
292220163Strasz	 * There may be more than one matching rule; go through all of them.
293220163Strasz	 * Denial should be done last, after logging and sending signals.
294220163Strasz	 */
295220163Strasz	LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) {
296220163Strasz		rule = link->rrl_rule;
297220163Strasz		if (rule->rr_resource != resource)
298220163Strasz			continue;
299220163Strasz		if (!rctl_would_exceed(p, rule, amount)) {
300220163Strasz			link->rrl_exceeded = 0;
301220163Strasz			continue;
302220163Strasz		}
303220163Strasz
304220163Strasz		switch (rule->rr_action) {
305220163Strasz		case RCTL_ACTION_DENY:
306220163Strasz			should_deny = 1;
307220163Strasz			continue;
308220163Strasz		case RCTL_ACTION_LOG:
309220163Strasz			/*
310220163Strasz			 * If rrl_exceeded != 0, it means we've already
311220163Strasz			 * logged a warning for this process.
312220163Strasz			 */
313220163Strasz			if (link->rrl_exceeded != 0)
314220163Strasz				continue;
315220163Strasz
316225940Strasz			/*
317225940Strasz			 * If the process state is not fully initialized yet,
318225940Strasz			 * we can't access most of the required fields, e.g.
319225940Strasz			 * p->p_comm.  This happens when called from fork1().
320225940Strasz			 * Ignore this rule for now; it will be processed just
321225940Strasz			 * after fork, when called from racct_proc_fork_done().
322225940Strasz			 */
323225940Strasz			if (p->p_state != PRS_NORMAL)
324225940Strasz				continue;
325225940Strasz
326220163Strasz			if (!ppsratecheck(&lasttime, &curtime, 10))
327220163Strasz				continue;
328220163Strasz
329220163Strasz			buf = malloc(RCTL_LOG_BUFSIZE, M_RCTL, M_NOWAIT);
330220163Strasz			if (buf == NULL) {
331220163Strasz				printf("rctl_enforce: out of memory\n");
332220163Strasz				continue;
333220163Strasz			}
334220163Strasz			sbuf_new(&sb, buf, RCTL_LOG_BUFSIZE, SBUF_FIXEDLEN);
335220163Strasz			rctl_rule_to_sbuf(&sb, rule);
336220163Strasz			sbuf_finish(&sb);
337220163Strasz			printf("rctl: rule \"%s\" matched by pid %d "
338220163Strasz			    "(%s), uid %d, jail %s\n", sbuf_data(&sb),
339220163Strasz			    p->p_pid, p->p_comm, p->p_ucred->cr_uid,
340221362Strasz			    p->p_ucred->cr_prison->pr_prison_racct->prr_name);
341220163Strasz			sbuf_delete(&sb);
342220163Strasz			free(buf, M_RCTL);
343220163Strasz			link->rrl_exceeded = 1;
344220163Strasz			continue;
345220163Strasz		case RCTL_ACTION_DEVCTL:
346220163Strasz			if (link->rrl_exceeded != 0)
347220163Strasz				continue;
348220163Strasz
349225940Strasz			if (p->p_state != PRS_NORMAL)
350225940Strasz				continue;
351225940Strasz
352220163Strasz			buf = malloc(RCTL_LOG_BUFSIZE, M_RCTL, M_NOWAIT);
353220163Strasz			if (buf == NULL) {
354220163Strasz				printf("rctl_enforce: out of memory\n");
355220163Strasz				continue;
356220163Strasz			}
357220163Strasz			sbuf_new(&sb, buf, RCTL_LOG_BUFSIZE, SBUF_FIXEDLEN);
358220163Strasz			sbuf_printf(&sb, "rule=");
359220163Strasz			rctl_rule_to_sbuf(&sb, rule);
360220163Strasz			sbuf_printf(&sb, " pid=%d ruid=%d jail=%s",
361220163Strasz			    p->p_pid, p->p_ucred->cr_ruid,
362221362Strasz			    p->p_ucred->cr_prison->pr_prison_racct->prr_name);
363220163Strasz			sbuf_finish(&sb);
364220163Strasz			devctl_notify_f("RCTL", "rule", "matched",
365220163Strasz			    sbuf_data(&sb), M_NOWAIT);
366220163Strasz			sbuf_delete(&sb);
367220163Strasz			free(buf, M_RCTL);
368220163Strasz			link->rrl_exceeded = 1;
369220163Strasz			continue;
370220163Strasz		default:
371220163Strasz			if (link->rrl_exceeded != 0)
372220163Strasz				continue;
373220163Strasz
374225940Strasz			if (p->p_state != PRS_NORMAL)
375225940Strasz				continue;
376225940Strasz
377220163Strasz			KASSERT(rule->rr_action > 0 &&
378220163Strasz			    rule->rr_action <= RCTL_ACTION_SIGNAL_MAX,
379220163Strasz			    ("rctl_enforce: unknown action %d",
380220163Strasz			     rule->rr_action));
381220163Strasz
382220163Strasz			/*
383220163Strasz			 * We're using the fact that RCTL_ACTION_SIG* values
384220163Strasz			 * are equal to their counterparts from sys/signal.h.
385220163Strasz			 */
386225617Skmacy			kern_psignal(p, rule->rr_action);
387220163Strasz			link->rrl_exceeded = 1;
388220163Strasz			continue;
389220163Strasz		}
390220163Strasz	}
391220163Strasz
392220163Strasz	rw_runlock(&rctl_lock);
393220163Strasz
394220163Strasz	if (should_deny) {
395220163Strasz		/*
396220163Strasz		 * Return fake error code; the caller should change it
397220163Strasz		 * into one proper for the situation - EFSIZ, ENOMEM etc.
398220163Strasz		 */
399220163Strasz		return (EDOOFUS);
400220163Strasz	}
401220163Strasz
402220163Strasz	return (0);
403220163Strasz}
404220163Strasz
405220163Straszuint64_t
406220163Straszrctl_get_limit(struct proc *p, int resource)
407220163Strasz{
408220163Strasz	struct rctl_rule *rule;
409220163Strasz	struct rctl_rule_link *link;
410220163Strasz	uint64_t amount = UINT64_MAX;
411220163Strasz
412220163Strasz	rw_rlock(&rctl_lock);
413220163Strasz
414220163Strasz	/*
415220163Strasz	 * There may be more than one matching rule; go through all of them.
416220163Strasz	 * Denial should be done last, after logging and sending signals.
417220163Strasz	 */
418220163Strasz	LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) {
419220163Strasz		rule = link->rrl_rule;
420220163Strasz		if (rule->rr_resource != resource)
421220163Strasz			continue;
422220163Strasz		if (rule->rr_action != RCTL_ACTION_DENY)
423220163Strasz			continue;
424220163Strasz		if (rule->rr_amount < amount)
425220163Strasz			amount = rule->rr_amount;
426220163Strasz	}
427220163Strasz
428220163Strasz	rw_runlock(&rctl_lock);
429220163Strasz
430220163Strasz	return (amount);
431220163Strasz}
432220163Strasz
433220163Straszuint64_t
434220163Straszrctl_get_available(struct proc *p, int resource)
435220163Strasz{
436220163Strasz	struct rctl_rule *rule;
437220163Strasz	struct rctl_rule_link *link;
438220163Strasz	int64_t available, minavailable, allocated;
439220163Strasz
440220163Strasz	minavailable = INT64_MAX;
441220163Strasz
442220163Strasz	rw_rlock(&rctl_lock);
443220163Strasz
444220163Strasz	/*
445220163Strasz	 * There may be more than one matching rule; go through all of them.
446220163Strasz	 * Denial should be done last, after logging and sending signals.
447220163Strasz	 */
448220163Strasz	LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) {
449220163Strasz		rule = link->rrl_rule;
450220163Strasz		if (rule->rr_resource != resource)
451220163Strasz			continue;
452220163Strasz		if (rule->rr_action != RCTL_ACTION_DENY)
453220163Strasz			continue;
454220163Strasz		available = rctl_available_resource(p, rule);
455220163Strasz		if (available < minavailable)
456220163Strasz			minavailable = available;
457220163Strasz	}
458220163Strasz
459220163Strasz	rw_runlock(&rctl_lock);
460220163Strasz
461220163Strasz	/*
462220163Strasz	 * XXX: Think about this _hard_.
463220163Strasz	 */
464220163Strasz	allocated = p->p_racct->r_resources[resource];
465220163Strasz	if (minavailable < INT64_MAX - allocated)
466220163Strasz		minavailable += allocated;
467220163Strasz	if (minavailable < 0)
468220163Strasz		minavailable = 0;
469220163Strasz	return (minavailable);
470220163Strasz}
471220163Strasz
472220163Straszstatic int
473220163Straszrctl_rule_matches(const struct rctl_rule *rule, const struct rctl_rule *filter)
474220163Strasz{
475220163Strasz
476220163Strasz	if (filter->rr_subject_type != RCTL_SUBJECT_TYPE_UNDEFINED) {
477220163Strasz		if (rule->rr_subject_type != filter->rr_subject_type)
478220163Strasz			return (0);
479220163Strasz
480220163Strasz		switch (filter->rr_subject_type) {
481220163Strasz		case RCTL_SUBJECT_TYPE_PROCESS:
482220163Strasz			if (filter->rr_subject.rs_proc != NULL &&
483220163Strasz			    rule->rr_subject.rs_proc !=
484220163Strasz			    filter->rr_subject.rs_proc)
485220163Strasz				return (0);
486220163Strasz			break;
487220163Strasz		case RCTL_SUBJECT_TYPE_USER:
488220163Strasz			if (filter->rr_subject.rs_uip != NULL &&
489220163Strasz			    rule->rr_subject.rs_uip !=
490220163Strasz			    filter->rr_subject.rs_uip)
491220163Strasz				return (0);
492220163Strasz			break;
493220163Strasz		case RCTL_SUBJECT_TYPE_LOGINCLASS:
494220527Strasz			if (filter->rr_subject.rs_loginclass != NULL &&
495220527Strasz			    rule->rr_subject.rs_loginclass !=
496220527Strasz			    filter->rr_subject.rs_loginclass)
497220163Strasz				return (0);
498220163Strasz			break;
499220163Strasz		case RCTL_SUBJECT_TYPE_JAIL:
500221362Strasz			if (filter->rr_subject.rs_prison_racct != NULL &&
501221362Strasz			    rule->rr_subject.rs_prison_racct !=
502221362Strasz			    filter->rr_subject.rs_prison_racct)
503220163Strasz				return (0);
504220163Strasz			break;
505220163Strasz		default:
506220163Strasz			panic("rctl_rule_matches: unknown subject type %d",
507220163Strasz			    filter->rr_subject_type);
508220163Strasz		}
509220163Strasz	}
510220163Strasz
511220163Strasz	if (filter->rr_resource != RACCT_UNDEFINED) {
512220163Strasz		if (rule->rr_resource != filter->rr_resource)
513220163Strasz			return (0);
514220163Strasz	}
515220163Strasz
516220163Strasz	if (filter->rr_action != RCTL_ACTION_UNDEFINED) {
517220163Strasz		if (rule->rr_action != filter->rr_action)
518220163Strasz			return (0);
519220163Strasz	}
520220163Strasz
521220163Strasz	if (filter->rr_amount != RCTL_AMOUNT_UNDEFINED) {
522220163Strasz		if (rule->rr_amount != filter->rr_amount)
523220163Strasz			return (0);
524220163Strasz	}
525220163Strasz
526220163Strasz	if (filter->rr_per != RCTL_SUBJECT_TYPE_UNDEFINED) {
527220163Strasz		if (rule->rr_per != filter->rr_per)
528220163Strasz			return (0);
529220163Strasz	}
530220163Strasz
531220163Strasz	return (1);
532220163Strasz}
533220163Strasz
534220163Straszstatic int
535220163Straszstr2value(const char *str, int *value, struct dict *table)
536220163Strasz{
537220163Strasz	int i;
538220163Strasz
539220163Strasz	if (value == NULL)
540220163Strasz		return (EINVAL);
541220163Strasz
542220163Strasz	for (i = 0; table[i].d_name != NULL; i++) {
543220163Strasz		if (strcasecmp(table[i].d_name, str) == 0) {
544220163Strasz			*value =  table[i].d_value;
545220163Strasz			return (0);
546220163Strasz		}
547220163Strasz	}
548220163Strasz
549220163Strasz	return (EINVAL);
550220163Strasz}
551220163Strasz
552220163Straszstatic int
553220163Straszstr2id(const char *str, id_t *value)
554220163Strasz{
555220163Strasz	char *end;
556220163Strasz
557220163Strasz	if (str == NULL)
558220163Strasz		return (EINVAL);
559220163Strasz
560220163Strasz	*value = strtoul(str, &end, 10);
561220163Strasz	if ((size_t)(end - str) != strlen(str))
562220163Strasz		return (EINVAL);
563220163Strasz
564220163Strasz	return (0);
565220163Strasz}
566220163Strasz
567220163Straszstatic int
568220163Straszstr2int64(const char *str, int64_t *value)
569220163Strasz{
570220163Strasz	char *end;
571220163Strasz
572220163Strasz	if (str == NULL)
573220163Strasz		return (EINVAL);
574220163Strasz
575220163Strasz	*value = strtoul(str, &end, 10);
576220163Strasz	if ((size_t)(end - str) != strlen(str))
577220163Strasz		return (EINVAL);
578220163Strasz
579220163Strasz	return (0);
580220163Strasz}
581220163Strasz
582220163Strasz/*
583220163Strasz * Connect the rule to the racct, increasing refcount for the rule.
584220163Strasz */
585220163Straszstatic void
586220163Straszrctl_racct_add_rule(struct racct *racct, struct rctl_rule *rule)
587220163Strasz{
588220163Strasz	struct rctl_rule_link *link;
589220163Strasz
590220163Strasz	KASSERT(rctl_rule_fully_specified(rule), ("rule not fully specified"));
591220163Strasz
592220163Strasz	rctl_rule_acquire(rule);
593220163Strasz	link = uma_zalloc(rctl_rule_link_zone, M_WAITOK);
594220163Strasz	link->rrl_rule = rule;
595220163Strasz	link->rrl_exceeded = 0;
596220163Strasz
597220163Strasz	rw_wlock(&rctl_lock);
598220163Strasz	LIST_INSERT_HEAD(&racct->r_rule_links, link, rrl_next);
599220163Strasz	rw_wunlock(&rctl_lock);
600220163Strasz}
601220163Strasz
602220163Straszstatic int
603220163Straszrctl_racct_add_rule_locked(struct racct *racct, struct rctl_rule *rule)
604220163Strasz{
605220163Strasz	struct rctl_rule_link *link;
606220163Strasz
607220163Strasz	KASSERT(rctl_rule_fully_specified(rule), ("rule not fully specified"));
608220163Strasz	rw_assert(&rctl_lock, RA_WLOCKED);
609220163Strasz
610220163Strasz	link = uma_zalloc(rctl_rule_link_zone, M_NOWAIT);
611220163Strasz	if (link == NULL)
612220163Strasz		return (ENOMEM);
613220163Strasz	rctl_rule_acquire(rule);
614220163Strasz	link->rrl_rule = rule;
615220163Strasz	link->rrl_exceeded = 0;
616220163Strasz
617220163Strasz	LIST_INSERT_HEAD(&racct->r_rule_links, link, rrl_next);
618220163Strasz	return (0);
619220163Strasz}
620220163Strasz
621220163Strasz/*
622220163Strasz * Remove limits for a rules matching the filter and release
623220163Strasz * the refcounts for the rules, possibly freeing them.  Returns
624220163Strasz * the number of limit structures removed.
625220163Strasz */
626220163Straszstatic int
627220163Straszrctl_racct_remove_rules(struct racct *racct,
628220163Strasz    const struct rctl_rule *filter)
629220163Strasz{
630220163Strasz	int removed = 0;
631220163Strasz	struct rctl_rule_link *link, *linktmp;
632220163Strasz
633220163Strasz	rw_assert(&rctl_lock, RA_WLOCKED);
634220163Strasz
635220163Strasz	LIST_FOREACH_SAFE(link, &racct->r_rule_links, rrl_next, linktmp) {
636220163Strasz		if (!rctl_rule_matches(link->rrl_rule, filter))
637220163Strasz			continue;
638220163Strasz
639220163Strasz		LIST_REMOVE(link, rrl_next);
640220163Strasz		rctl_rule_release(link->rrl_rule);
641220163Strasz		uma_zfree(rctl_rule_link_zone, link);
642220163Strasz		removed++;
643220163Strasz	}
644220163Strasz	return (removed);
645220163Strasz}
646220163Strasz
647220163Straszstatic void
648220163Straszrctl_rule_acquire_subject(struct rctl_rule *rule)
649220163Strasz{
650220163Strasz
651220163Strasz	switch (rule->rr_subject_type) {
652220163Strasz	case RCTL_SUBJECT_TYPE_UNDEFINED:
653220163Strasz	case RCTL_SUBJECT_TYPE_PROCESS:
654221362Strasz		break;
655220163Strasz	case RCTL_SUBJECT_TYPE_JAIL:
656221362Strasz		if (rule->rr_subject.rs_prison_racct != NULL)
657221362Strasz			prison_racct_hold(rule->rr_subject.rs_prison_racct);
658220163Strasz		break;
659220163Strasz	case RCTL_SUBJECT_TYPE_USER:
660220163Strasz		if (rule->rr_subject.rs_uip != NULL)
661220163Strasz			uihold(rule->rr_subject.rs_uip);
662220163Strasz		break;
663220163Strasz	case RCTL_SUBJECT_TYPE_LOGINCLASS:
664220527Strasz		if (rule->rr_subject.rs_loginclass != NULL)
665220527Strasz			loginclass_hold(rule->rr_subject.rs_loginclass);
666220163Strasz		break;
667220163Strasz	default:
668220163Strasz		panic("rctl_rule_acquire_subject: unknown subject type %d",
669220163Strasz		    rule->rr_subject_type);
670220163Strasz	}
671220163Strasz}
672220163Strasz
673220163Straszstatic void
674220163Straszrctl_rule_release_subject(struct rctl_rule *rule)
675220163Strasz{
676220163Strasz
677220163Strasz	switch (rule->rr_subject_type) {
678220163Strasz	case RCTL_SUBJECT_TYPE_UNDEFINED:
679220163Strasz	case RCTL_SUBJECT_TYPE_PROCESS:
680221362Strasz		break;
681220163Strasz	case RCTL_SUBJECT_TYPE_JAIL:
682221362Strasz		if (rule->rr_subject.rs_prison_racct != NULL)
683221362Strasz			prison_racct_free(rule->rr_subject.rs_prison_racct);
684220163Strasz		break;
685220163Strasz	case RCTL_SUBJECT_TYPE_USER:
686220163Strasz		if (rule->rr_subject.rs_uip != NULL)
687220163Strasz			uifree(rule->rr_subject.rs_uip);
688220163Strasz		break;
689220163Strasz	case RCTL_SUBJECT_TYPE_LOGINCLASS:
690220527Strasz		if (rule->rr_subject.rs_loginclass != NULL)
691220527Strasz			loginclass_free(rule->rr_subject.rs_loginclass);
692220163Strasz		break;
693220163Strasz	default:
694220163Strasz		panic("rctl_rule_release_subject: unknown subject type %d",
695220163Strasz		    rule->rr_subject_type);
696220163Strasz	}
697220163Strasz}
698220163Strasz
699220163Straszstruct rctl_rule *
700220163Straszrctl_rule_alloc(int flags)
701220163Strasz{
702220163Strasz	struct rctl_rule *rule;
703220163Strasz
704220163Strasz	rule = uma_zalloc(rctl_rule_zone, flags);
705220163Strasz	if (rule == NULL)
706220163Strasz		return (NULL);
707220163Strasz	rule->rr_subject_type = RCTL_SUBJECT_TYPE_UNDEFINED;
708220163Strasz	rule->rr_subject.rs_proc = NULL;
709220163Strasz	rule->rr_subject.rs_uip = NULL;
710220527Strasz	rule->rr_subject.rs_loginclass = NULL;
711221362Strasz	rule->rr_subject.rs_prison_racct = NULL;
712220163Strasz	rule->rr_per = RCTL_SUBJECT_TYPE_UNDEFINED;
713220163Strasz	rule->rr_resource = RACCT_UNDEFINED;
714220163Strasz	rule->rr_action = RCTL_ACTION_UNDEFINED;
715220163Strasz	rule->rr_amount = RCTL_AMOUNT_UNDEFINED;
716220163Strasz	refcount_init(&rule->rr_refcount, 1);
717220163Strasz
718220163Strasz	return (rule);
719220163Strasz}
720220163Strasz
721220163Straszstruct rctl_rule *
722220163Straszrctl_rule_duplicate(const struct rctl_rule *rule, int flags)
723220163Strasz{
724220163Strasz	struct rctl_rule *copy;
725220163Strasz
726220163Strasz	copy = uma_zalloc(rctl_rule_zone, flags);
727220163Strasz	if (copy == NULL)
728220163Strasz		return (NULL);
729220163Strasz	copy->rr_subject_type = rule->rr_subject_type;
730220163Strasz	copy->rr_subject.rs_proc = rule->rr_subject.rs_proc;
731220163Strasz	copy->rr_subject.rs_uip = rule->rr_subject.rs_uip;
732220527Strasz	copy->rr_subject.rs_loginclass = rule->rr_subject.rs_loginclass;
733221362Strasz	copy->rr_subject.rs_prison_racct = rule->rr_subject.rs_prison_racct;
734220163Strasz	copy->rr_per = rule->rr_per;
735220163Strasz	copy->rr_resource = rule->rr_resource;
736220163Strasz	copy->rr_action = rule->rr_action;
737220163Strasz	copy->rr_amount = rule->rr_amount;
738220163Strasz	refcount_init(&copy->rr_refcount, 1);
739220163Strasz	rctl_rule_acquire_subject(copy);
740220163Strasz
741220163Strasz	return (copy);
742220163Strasz}
743220163Strasz
744220163Straszvoid
745220163Straszrctl_rule_acquire(struct rctl_rule *rule)
746220163Strasz{
747220163Strasz
748220163Strasz	KASSERT(rule->rr_refcount > 0, ("rule->rr_refcount <= 0"));
749220163Strasz
750220163Strasz	refcount_acquire(&rule->rr_refcount);
751220163Strasz}
752220163Strasz
753220163Straszstatic void
754220163Straszrctl_rule_free(void *context, int pending)
755220163Strasz{
756220163Strasz	struct rctl_rule *rule;
757220163Strasz
758220163Strasz	rule = (struct rctl_rule *)context;
759220163Strasz
760220163Strasz	KASSERT(rule->rr_refcount == 0, ("rule->rr_refcount != 0"));
761220163Strasz
762220163Strasz	/*
763220163Strasz	 * We don't need locking here; rule is guaranteed to be inaccessible.
764220163Strasz	 */
765220163Strasz
766220163Strasz	rctl_rule_release_subject(rule);
767220163Strasz	uma_zfree(rctl_rule_zone, rule);
768220163Strasz}
769220163Strasz
770220163Straszvoid
771220163Straszrctl_rule_release(struct rctl_rule *rule)
772220163Strasz{
773220163Strasz
774220163Strasz	KASSERT(rule->rr_refcount > 0, ("rule->rr_refcount <= 0"));
775220163Strasz
776220163Strasz	if (refcount_release(&rule->rr_refcount)) {
777220163Strasz		/*
778220163Strasz		 * rctl_rule_release() is often called when iterating
779220163Strasz		 * over all the uidinfo structures in the system,
780220163Strasz		 * holding uihashtbl_lock.  Since rctl_rule_free()
781220163Strasz		 * might end up calling uifree(), this would lead
782220163Strasz		 * to lock recursion.  Use taskqueue to avoid this.
783220163Strasz		 */
784220163Strasz		TASK_INIT(&rule->rr_task, 0, rctl_rule_free, rule);
785220163Strasz		taskqueue_enqueue(taskqueue_thread, &rule->rr_task);
786220163Strasz	}
787220163Strasz}
788220163Strasz
789220163Straszstatic int
790220163Straszrctl_rule_fully_specified(const struct rctl_rule *rule)
791220163Strasz{
792220163Strasz
793220163Strasz	switch (rule->rr_subject_type) {
794220163Strasz	case RCTL_SUBJECT_TYPE_UNDEFINED:
795220163Strasz		return (0);
796220163Strasz	case RCTL_SUBJECT_TYPE_PROCESS:
797220163Strasz		if (rule->rr_subject.rs_proc == NULL)
798220163Strasz			return (0);
799220163Strasz		break;
800220163Strasz	case RCTL_SUBJECT_TYPE_USER:
801220163Strasz		if (rule->rr_subject.rs_uip == NULL)
802220163Strasz			return (0);
803220163Strasz		break;
804220163Strasz	case RCTL_SUBJECT_TYPE_LOGINCLASS:
805220527Strasz		if (rule->rr_subject.rs_loginclass == NULL)
806220163Strasz			return (0);
807220163Strasz		break;
808220163Strasz	case RCTL_SUBJECT_TYPE_JAIL:
809221362Strasz		if (rule->rr_subject.rs_prison_racct == NULL)
810220163Strasz			return (0);
811220163Strasz		break;
812220163Strasz	default:
813220163Strasz		panic("rctl_rule_fully_specified: unknown subject type %d",
814220163Strasz		    rule->rr_subject_type);
815220163Strasz	}
816220163Strasz	if (rule->rr_resource == RACCT_UNDEFINED)
817220163Strasz		return (0);
818220163Strasz	if (rule->rr_action == RCTL_ACTION_UNDEFINED)
819220163Strasz		return (0);
820220163Strasz	if (rule->rr_amount == RCTL_AMOUNT_UNDEFINED)
821220163Strasz		return (0);
822220163Strasz	if (rule->rr_per == RCTL_SUBJECT_TYPE_UNDEFINED)
823220163Strasz		return (0);
824220163Strasz
825220163Strasz	return (1);
826220163Strasz}
827220163Strasz
828220163Straszstatic int
829220163Straszrctl_string_to_rule(char *rulestr, struct rctl_rule **rulep)
830220163Strasz{
831220163Strasz	int error = 0;
832220163Strasz	char *subjectstr, *subject_idstr, *resourcestr, *actionstr,
833220163Strasz	     *amountstr, *perstr;
834220163Strasz	struct rctl_rule *rule;
835220163Strasz	id_t id;
836220163Strasz
837220163Strasz	rule = rctl_rule_alloc(M_WAITOK);
838220163Strasz
839220163Strasz	subjectstr = strsep(&rulestr, ":");
840220163Strasz	subject_idstr = strsep(&rulestr, ":");
841220163Strasz	resourcestr = strsep(&rulestr, ":");
842220163Strasz	actionstr = strsep(&rulestr, "=/");
843220163Strasz	amountstr = strsep(&rulestr, "/");
844220163Strasz	perstr = rulestr;
845220163Strasz
846220163Strasz	if (subjectstr == NULL || subjectstr[0] == '\0')
847220163Strasz		rule->rr_subject_type = RCTL_SUBJECT_TYPE_UNDEFINED;
848220163Strasz	else {
849220163Strasz		error = str2value(subjectstr, &rule->rr_subject_type, subjectnames);
850220163Strasz		if (error != 0)
851220163Strasz			goto out;
852220163Strasz	}
853220163Strasz
854220163Strasz	if (subject_idstr == NULL || subject_idstr[0] == '\0') {
855220163Strasz		rule->rr_subject.rs_proc = NULL;
856220163Strasz		rule->rr_subject.rs_uip = NULL;
857220527Strasz		rule->rr_subject.rs_loginclass = NULL;
858221362Strasz		rule->rr_subject.rs_prison_racct = NULL;
859220163Strasz	} else {
860220163Strasz		switch (rule->rr_subject_type) {
861220163Strasz		case RCTL_SUBJECT_TYPE_UNDEFINED:
862220163Strasz			error = EINVAL;
863220163Strasz			goto out;
864220163Strasz		case RCTL_SUBJECT_TYPE_PROCESS:
865220163Strasz			error = str2id(subject_idstr, &id);
866220163Strasz			if (error != 0)
867220163Strasz				goto out;
868220163Strasz			sx_assert(&allproc_lock, SA_LOCKED);
869220163Strasz			rule->rr_subject.rs_proc = pfind(id);
870220163Strasz			if (rule->rr_subject.rs_proc == NULL) {
871220163Strasz				error = ESRCH;
872220163Strasz				goto out;
873220163Strasz			}
874220163Strasz			PROC_UNLOCK(rule->rr_subject.rs_proc);
875220163Strasz			break;
876220163Strasz		case RCTL_SUBJECT_TYPE_USER:
877220163Strasz			error = str2id(subject_idstr, &id);
878220163Strasz			if (error != 0)
879220163Strasz				goto out;
880220163Strasz			rule->rr_subject.rs_uip = uifind(id);
881220163Strasz			break;
882220163Strasz		case RCTL_SUBJECT_TYPE_LOGINCLASS:
883220527Strasz			rule->rr_subject.rs_loginclass =
884220163Strasz			    loginclass_find(subject_idstr);
885220527Strasz			if (rule->rr_subject.rs_loginclass == NULL) {
886220163Strasz				error = ENAMETOOLONG;
887220163Strasz				goto out;
888220163Strasz			}
889220163Strasz			break;
890220163Strasz		case RCTL_SUBJECT_TYPE_JAIL:
891221362Strasz			rule->rr_subject.rs_prison_racct =
892221362Strasz			    prison_racct_find(subject_idstr);
893221362Strasz			if (rule->rr_subject.rs_prison_racct == NULL) {
894221362Strasz				error = ENAMETOOLONG;
895221362Strasz				goto out;
896220163Strasz			}
897220163Strasz			break;
898220163Strasz               default:
899220163Strasz                       panic("rctl_string_to_rule: unknown subject type %d",
900220163Strasz                           rule->rr_subject_type);
901220163Strasz               }
902220163Strasz	}
903220163Strasz
904220163Strasz	if (resourcestr == NULL || resourcestr[0] == '\0')
905220163Strasz		rule->rr_resource = RACCT_UNDEFINED;
906220163Strasz	else {
907220163Strasz		error = str2value(resourcestr, &rule->rr_resource,
908220163Strasz		    resourcenames);
909220163Strasz		if (error != 0)
910220163Strasz			goto out;
911220163Strasz	}
912220163Strasz
913220163Strasz	if (actionstr == NULL || actionstr[0] == '\0')
914220163Strasz		rule->rr_action = RCTL_ACTION_UNDEFINED;
915220163Strasz	else {
916220163Strasz		error = str2value(actionstr, &rule->rr_action, actionnames);
917220163Strasz		if (error != 0)
918220163Strasz			goto out;
919220163Strasz	}
920220163Strasz
921220163Strasz	if (amountstr == NULL || amountstr[0] == '\0')
922220163Strasz		rule->rr_amount = RCTL_AMOUNT_UNDEFINED;
923220163Strasz	else {
924220163Strasz		error = str2int64(amountstr, &rule->rr_amount);
925220163Strasz		if (error != 0)
926220163Strasz			goto out;
927224036Strasz		if (RACCT_IS_IN_MILLIONS(rule->rr_resource))
928225371Strasz			rule->rr_amount *= 1000000;
929220163Strasz	}
930220163Strasz
931220163Strasz	if (perstr == NULL || perstr[0] == '\0')
932220163Strasz		rule->rr_per = RCTL_SUBJECT_TYPE_UNDEFINED;
933220163Strasz	else {
934220163Strasz		error = str2value(perstr, &rule->rr_per, subjectnames);
935220163Strasz		if (error != 0)
936220163Strasz			goto out;
937220163Strasz	}
938220163Strasz
939220163Straszout:
940220163Strasz	if (error == 0)
941220163Strasz		*rulep = rule;
942220163Strasz	else
943220163Strasz		rctl_rule_release(rule);
944220163Strasz
945220163Strasz	return (error);
946220163Strasz}
947220163Strasz
948220163Strasz/*
949220163Strasz * Link a rule with all the subjects it applies to.
950220163Strasz */
951220163Straszint
952220163Straszrctl_rule_add(struct rctl_rule *rule)
953220163Strasz{
954220163Strasz	struct proc *p;
955220163Strasz	struct ucred *cred;
956220163Strasz	struct uidinfo *uip;
957220163Strasz	struct prison *pr;
958221362Strasz	struct prison_racct *prr;
959220163Strasz	struct loginclass *lc;
960220163Strasz	struct rctl_rule *rule2;
961220163Strasz	int match;
962220163Strasz
963220163Strasz	KASSERT(rctl_rule_fully_specified(rule), ("rule not fully specified"));
964220163Strasz
965220163Strasz	/*
966220163Strasz	 * Some rules just don't make sense.  Note that the one below
967223844Strasz	 * cannot be rewritten using RACCT_IS_DENIABLE(); the RACCT_PCTCPU,
968220163Strasz	 * for example, is not deniable in the racct sense, but the
969220163Strasz	 * limit is enforced in a different way, so "deny" rules for %CPU
970220163Strasz	 * do make sense.
971220163Strasz	 */
972220163Strasz	if (rule->rr_action == RCTL_ACTION_DENY &&
973220163Strasz	    (rule->rr_resource == RACCT_CPU ||
974220163Strasz	    rule->rr_resource == RACCT_WALLCLOCK))
975220163Strasz		return (EOPNOTSUPP);
976220163Strasz
977220163Strasz	if (rule->rr_per == RCTL_SUBJECT_TYPE_PROCESS &&
978223844Strasz	    RACCT_IS_SLOPPY(rule->rr_resource))
979220163Strasz		return (EOPNOTSUPP);
980220163Strasz
981220163Strasz	/*
982220163Strasz	 * Make sure there are no duplicated rules.  Also, for the "deny"
983220163Strasz	 * rules, remove ones differing only by "amount".
984220163Strasz	 */
985220163Strasz	if (rule->rr_action == RCTL_ACTION_DENY) {
986220163Strasz		rule2 = rctl_rule_duplicate(rule, M_WAITOK);
987220163Strasz		rule2->rr_amount = RCTL_AMOUNT_UNDEFINED;
988220163Strasz		rctl_rule_remove(rule2);
989220163Strasz		rctl_rule_release(rule2);
990220163Strasz	} else
991220163Strasz		rctl_rule_remove(rule);
992220163Strasz
993220163Strasz	switch (rule->rr_subject_type) {
994220163Strasz	case RCTL_SUBJECT_TYPE_PROCESS:
995220163Strasz		p = rule->rr_subject.rs_proc;
996220163Strasz		KASSERT(p != NULL, ("rctl_rule_add: NULL proc"));
997220163Strasz		/*
998220163Strasz		 * No resource limits for system processes.
999220163Strasz		 */
1000220163Strasz		if (p->p_flag & P_SYSTEM)
1001220163Strasz			return (EPERM);
1002220163Strasz
1003220163Strasz		rctl_racct_add_rule(p->p_racct, rule);
1004220163Strasz		/*
1005220163Strasz		 * In case of per-process rule, we don't have anything more
1006220163Strasz		 * to do.
1007220163Strasz		 */
1008220163Strasz		return (0);
1009220163Strasz
1010220163Strasz	case RCTL_SUBJECT_TYPE_USER:
1011220163Strasz		uip = rule->rr_subject.rs_uip;
1012220163Strasz		KASSERT(uip != NULL, ("rctl_rule_add: NULL uip"));
1013220163Strasz		rctl_racct_add_rule(uip->ui_racct, rule);
1014220163Strasz		break;
1015220163Strasz
1016220163Strasz	case RCTL_SUBJECT_TYPE_LOGINCLASS:
1017220527Strasz		lc = rule->rr_subject.rs_loginclass;
1018220163Strasz		KASSERT(lc != NULL, ("rctl_rule_add: NULL loginclass"));
1019220163Strasz		rctl_racct_add_rule(lc->lc_racct, rule);
1020220163Strasz		break;
1021220163Strasz
1022220163Strasz	case RCTL_SUBJECT_TYPE_JAIL:
1023221362Strasz		prr = rule->rr_subject.rs_prison_racct;
1024221362Strasz		KASSERT(prr != NULL, ("rctl_rule_add: NULL pr"));
1025221362Strasz		rctl_racct_add_rule(prr->prr_racct, rule);
1026220163Strasz		break;
1027220163Strasz
1028220163Strasz	default:
1029220163Strasz		panic("rctl_rule_add: unknown subject type %d",
1030220163Strasz		    rule->rr_subject_type);
1031220163Strasz	}
1032220163Strasz
1033220163Strasz	/*
1034220163Strasz	 * Now go through all the processes and add the new rule to the ones
1035220163Strasz	 * it applies to.
1036220163Strasz	 */
1037220163Strasz	sx_assert(&allproc_lock, SA_LOCKED);
1038220163Strasz	FOREACH_PROC_IN_SYSTEM(p) {
1039220163Strasz		if (p->p_flag & P_SYSTEM)
1040220163Strasz			continue;
1041220163Strasz		cred = p->p_ucred;
1042220163Strasz		switch (rule->rr_subject_type) {
1043220163Strasz		case RCTL_SUBJECT_TYPE_USER:
1044220163Strasz			if (cred->cr_uidinfo == rule->rr_subject.rs_uip ||
1045220163Strasz			    cred->cr_ruidinfo == rule->rr_subject.rs_uip)
1046220163Strasz				break;
1047220163Strasz			continue;
1048220163Strasz		case RCTL_SUBJECT_TYPE_LOGINCLASS:
1049220527Strasz			if (cred->cr_loginclass == rule->rr_subject.rs_loginclass)
1050220163Strasz				break;
1051220163Strasz			continue;
1052220163Strasz		case RCTL_SUBJECT_TYPE_JAIL:
1053220163Strasz			match = 0;
1054220163Strasz			for (pr = cred->cr_prison; pr != NULL; pr = pr->pr_parent) {
1055221362Strasz				if (pr->pr_prison_racct == rule->rr_subject.rs_prison_racct) {
1056220163Strasz					match = 1;
1057220163Strasz					break;
1058220163Strasz				}
1059220163Strasz			}
1060220163Strasz			if (match)
1061220163Strasz				break;
1062220163Strasz			continue;
1063220163Strasz		default:
1064220163Strasz			panic("rctl_rule_add: unknown subject type %d",
1065220163Strasz			    rule->rr_subject_type);
1066220163Strasz		}
1067220163Strasz
1068220163Strasz		rctl_racct_add_rule(p->p_racct, rule);
1069220163Strasz	}
1070220163Strasz
1071220163Strasz	return (0);
1072220163Strasz}
1073220163Strasz
1074220163Straszstatic void
1075220163Straszrctl_rule_remove_callback(struct racct *racct, void *arg2, void *arg3)
1076220163Strasz{
1077220163Strasz	struct rctl_rule *filter = (struct rctl_rule *)arg2;
1078220163Strasz	int found = 0;
1079220163Strasz
1080220163Strasz	rw_wlock(&rctl_lock);
1081220163Strasz	found += rctl_racct_remove_rules(racct, filter);
1082220163Strasz	rw_wunlock(&rctl_lock);
1083220163Strasz
1084220163Strasz	*((int *)arg3) += found;
1085220163Strasz}
1086220163Strasz
1087220163Strasz/*
1088220163Strasz * Remove all rules that match the filter.
1089220163Strasz */
1090220163Straszint
1091220163Straszrctl_rule_remove(struct rctl_rule *filter)
1092220163Strasz{
1093220163Strasz	int found = 0;
1094220163Strasz	struct proc *p;
1095220163Strasz
1096220163Strasz	if (filter->rr_subject_type == RCTL_SUBJECT_TYPE_PROCESS &&
1097220163Strasz	    filter->rr_subject.rs_proc != NULL) {
1098220163Strasz		p = filter->rr_subject.rs_proc;
1099220163Strasz		rw_wlock(&rctl_lock);
1100220163Strasz		found = rctl_racct_remove_rules(p->p_racct, filter);
1101220163Strasz		rw_wunlock(&rctl_lock);
1102220163Strasz		if (found)
1103220163Strasz			return (0);
1104220163Strasz		return (ESRCH);
1105220163Strasz	}
1106220163Strasz
1107220163Strasz	loginclass_racct_foreach(rctl_rule_remove_callback, filter,
1108220163Strasz	    (void *)&found);
1109220163Strasz	ui_racct_foreach(rctl_rule_remove_callback, filter,
1110220163Strasz	    (void *)&found);
1111220163Strasz	prison_racct_foreach(rctl_rule_remove_callback, filter,
1112220163Strasz	    (void *)&found);
1113220163Strasz
1114220163Strasz	sx_assert(&allproc_lock, SA_LOCKED);
1115220163Strasz	rw_wlock(&rctl_lock);
1116220163Strasz	FOREACH_PROC_IN_SYSTEM(p) {
1117220163Strasz		found += rctl_racct_remove_rules(p->p_racct, filter);
1118220163Strasz	}
1119220163Strasz	rw_wunlock(&rctl_lock);
1120220163Strasz
1121220163Strasz	if (found)
1122220163Strasz		return (0);
1123220163Strasz	return (ESRCH);
1124220163Strasz}
1125220163Strasz
1126220163Strasz/*
1127220163Strasz * Appends a rule to the sbuf.
1128220163Strasz */
1129220163Straszstatic void
1130220163Straszrctl_rule_to_sbuf(struct sbuf *sb, const struct rctl_rule *rule)
1131220163Strasz{
1132220163Strasz	int64_t amount;
1133220163Strasz
1134220163Strasz	sbuf_printf(sb, "%s:", rctl_subject_type_name(rule->rr_subject_type));
1135220163Strasz
1136220163Strasz	switch (rule->rr_subject_type) {
1137220163Strasz	case RCTL_SUBJECT_TYPE_PROCESS:
1138220163Strasz		if (rule->rr_subject.rs_proc == NULL)
1139220163Strasz			sbuf_printf(sb, ":");
1140220163Strasz		else
1141220163Strasz			sbuf_printf(sb, "%d:",
1142220163Strasz			    rule->rr_subject.rs_proc->p_pid);
1143220163Strasz		break;
1144220163Strasz	case RCTL_SUBJECT_TYPE_USER:
1145220163Strasz		if (rule->rr_subject.rs_uip == NULL)
1146220163Strasz			sbuf_printf(sb, ":");
1147220163Strasz		else
1148220163Strasz			sbuf_printf(sb, "%d:",
1149220163Strasz			    rule->rr_subject.rs_uip->ui_uid);
1150220163Strasz		break;
1151220163Strasz	case RCTL_SUBJECT_TYPE_LOGINCLASS:
1152220527Strasz		if (rule->rr_subject.rs_loginclass == NULL)
1153220163Strasz			sbuf_printf(sb, ":");
1154220163Strasz		else
1155220163Strasz			sbuf_printf(sb, "%s:",
1156220527Strasz			    rule->rr_subject.rs_loginclass->lc_name);
1157220163Strasz		break;
1158220163Strasz	case RCTL_SUBJECT_TYPE_JAIL:
1159221362Strasz		if (rule->rr_subject.rs_prison_racct == NULL)
1160220163Strasz			sbuf_printf(sb, ":");
1161220163Strasz		else
1162220163Strasz			sbuf_printf(sb, "%s:",
1163221362Strasz			    rule->rr_subject.rs_prison_racct->prr_name);
1164220163Strasz		break;
1165220163Strasz	default:
1166220163Strasz		panic("rctl_rule_to_sbuf: unknown subject type %d",
1167220163Strasz		    rule->rr_subject_type);
1168220163Strasz	}
1169220163Strasz
1170220163Strasz	amount = rule->rr_amount;
1171220163Strasz	if (amount != RCTL_AMOUNT_UNDEFINED &&
1172224036Strasz	    RACCT_IS_IN_MILLIONS(rule->rr_resource))
1173224036Strasz		amount /= 1000000;
1174220163Strasz
1175220163Strasz	sbuf_printf(sb, "%s:%s=%jd",
1176220163Strasz	    rctl_resource_name(rule->rr_resource),
1177220163Strasz	    rctl_action_name(rule->rr_action),
1178220163Strasz	    amount);
1179220163Strasz
1180220163Strasz	if (rule->rr_per != rule->rr_subject_type)
1181220163Strasz		sbuf_printf(sb, "/%s", rctl_subject_type_name(rule->rr_per));
1182220163Strasz}
1183220163Strasz
1184220163Strasz/*
1185220163Strasz * Routine used by RCTL syscalls to read in input string.
1186220163Strasz */
1187220163Straszstatic int
1188220163Straszrctl_read_inbuf(char **inputstr, const char *inbufp, size_t inbuflen)
1189220163Strasz{
1190220163Strasz	int error;
1191220163Strasz	char *str;
1192220163Strasz
1193220163Strasz	if (inbuflen <= 0)
1194220163Strasz		return (EINVAL);
1195234380Strasz	if (inbuflen > RCTL_MAX_INBUFLEN)
1196234380Strasz		return (E2BIG);
1197220163Strasz
1198220163Strasz	str = malloc(inbuflen + 1, M_RCTL, M_WAITOK);
1199220163Strasz	error = copyinstr(inbufp, str, inbuflen, NULL);
1200220163Strasz	if (error != 0) {
1201220163Strasz		free(str, M_RCTL);
1202220163Strasz		return (error);
1203220163Strasz	}
1204220163Strasz
1205220163Strasz	*inputstr = str;
1206220163Strasz
1207220163Strasz	return (0);
1208220163Strasz}
1209220163Strasz
1210220163Strasz/*
1211220163Strasz * Routine used by RCTL syscalls to write out output string.
1212220163Strasz */
1213220163Straszstatic int
1214220163Straszrctl_write_outbuf(struct sbuf *outputsbuf, char *outbufp, size_t outbuflen)
1215220163Strasz{
1216220163Strasz	int error;
1217220163Strasz
1218220163Strasz	if (outputsbuf == NULL)
1219220163Strasz		return (0);
1220220163Strasz
1221220163Strasz	sbuf_finish(outputsbuf);
1222220163Strasz	if (outbuflen < sbuf_len(outputsbuf) + 1) {
1223220163Strasz		sbuf_delete(outputsbuf);
1224220163Strasz		return (ERANGE);
1225220163Strasz	}
1226220163Strasz	error = copyout(sbuf_data(outputsbuf), outbufp,
1227220163Strasz	    sbuf_len(outputsbuf) + 1);
1228220163Strasz	sbuf_delete(outputsbuf);
1229220163Strasz	return (error);
1230220163Strasz}
1231220163Strasz
1232220163Straszstatic struct sbuf *
1233220163Straszrctl_racct_to_sbuf(struct racct *racct, int sloppy)
1234220163Strasz{
1235220163Strasz	int i;
1236220163Strasz	int64_t amount;
1237220163Strasz	struct sbuf *sb;
1238220163Strasz
1239220163Strasz	sb = sbuf_new_auto();
1240220163Strasz	for (i = 0; i <= RACCT_MAX; i++) {
1241223844Strasz		if (sloppy == 0 && RACCT_IS_SLOPPY(i))
1242220163Strasz			continue;
1243220163Strasz		amount = racct->r_resources[i];
1244224036Strasz		if (RACCT_IS_IN_MILLIONS(i))
1245225371Strasz			amount /= 1000000;
1246220163Strasz		sbuf_printf(sb, "%s=%jd,", rctl_resource_name(i), amount);
1247220163Strasz	}
1248220163Strasz	sbuf_setpos(sb, sbuf_len(sb) - 1);
1249220163Strasz	return (sb);
1250220163Strasz}
1251220163Strasz
1252220163Straszint
1253225617Skmacysys_rctl_get_racct(struct thread *td, struct rctl_get_racct_args *uap)
1254220163Strasz{
1255220163Strasz	int error;
1256220163Strasz	char *inputstr;
1257220163Strasz	struct rctl_rule *filter;
1258220163Strasz	struct sbuf *outputsbuf = NULL;
1259220163Strasz	struct proc *p;
1260220163Strasz	struct uidinfo *uip;
1261220163Strasz	struct loginclass *lc;
1262221362Strasz	struct prison_racct *prr;
1263220163Strasz
1264220527Strasz	error = priv_check(td, PRIV_RCTL_GET_RACCT);
1265220163Strasz	if (error != 0)
1266220163Strasz		return (error);
1267220163Strasz
1268220163Strasz	error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen);
1269220163Strasz	if (error != 0)
1270220163Strasz		return (error);
1271220163Strasz
1272220163Strasz	sx_slock(&allproc_lock);
1273220163Strasz	error = rctl_string_to_rule(inputstr, &filter);
1274220163Strasz	free(inputstr, M_RCTL);
1275220163Strasz	if (error != 0) {
1276220163Strasz		sx_sunlock(&allproc_lock);
1277220163Strasz		return (error);
1278220163Strasz	}
1279220163Strasz
1280220163Strasz	switch (filter->rr_subject_type) {
1281220163Strasz	case RCTL_SUBJECT_TYPE_PROCESS:
1282220163Strasz		p = filter->rr_subject.rs_proc;
1283220163Strasz		if (p == NULL) {
1284220163Strasz			error = EINVAL;
1285220163Strasz			goto out;
1286220163Strasz		}
1287220163Strasz		if (p->p_flag & P_SYSTEM) {
1288220163Strasz			error = EINVAL;
1289220163Strasz			goto out;
1290220163Strasz		}
1291220163Strasz		outputsbuf = rctl_racct_to_sbuf(p->p_racct, 0);
1292220163Strasz		break;
1293220163Strasz	case RCTL_SUBJECT_TYPE_USER:
1294220163Strasz		uip = filter->rr_subject.rs_uip;
1295220163Strasz		if (uip == NULL) {
1296220163Strasz			error = EINVAL;
1297220163Strasz			goto out;
1298220163Strasz		}
1299220163Strasz		outputsbuf = rctl_racct_to_sbuf(uip->ui_racct, 1);
1300220163Strasz		break;
1301220163Strasz	case RCTL_SUBJECT_TYPE_LOGINCLASS:
1302220527Strasz		lc = filter->rr_subject.rs_loginclass;
1303220163Strasz		if (lc == NULL) {
1304220163Strasz			error = EINVAL;
1305220163Strasz			goto out;
1306220163Strasz		}
1307220163Strasz		outputsbuf = rctl_racct_to_sbuf(lc->lc_racct, 1);
1308220163Strasz		break;
1309220163Strasz	case RCTL_SUBJECT_TYPE_JAIL:
1310221362Strasz		prr = filter->rr_subject.rs_prison_racct;
1311221362Strasz		if (prr == NULL) {
1312220163Strasz			error = EINVAL;
1313220163Strasz			goto out;
1314220163Strasz		}
1315221362Strasz		outputsbuf = rctl_racct_to_sbuf(prr->prr_racct, 1);
1316220163Strasz		break;
1317220163Strasz	default:
1318220163Strasz		error = EINVAL;
1319220163Strasz	}
1320220163Straszout:
1321220163Strasz	rctl_rule_release(filter);
1322220163Strasz	sx_sunlock(&allproc_lock);
1323220163Strasz	if (error != 0)
1324220163Strasz		return (error);
1325220163Strasz
1326220163Strasz	error = rctl_write_outbuf(outputsbuf, uap->outbufp, uap->outbuflen);
1327220163Strasz
1328220163Strasz	return (error);
1329220163Strasz}
1330220163Strasz
1331220163Straszstatic void
1332220163Straszrctl_get_rules_callback(struct racct *racct, void *arg2, void *arg3)
1333220163Strasz{
1334220163Strasz	struct rctl_rule *filter = (struct rctl_rule *)arg2;
1335220163Strasz	struct rctl_rule_link *link;
1336220163Strasz	struct sbuf *sb = (struct sbuf *)arg3;
1337220163Strasz
1338220163Strasz	rw_rlock(&rctl_lock);
1339220163Strasz	LIST_FOREACH(link, &racct->r_rule_links, rrl_next) {
1340220163Strasz		if (!rctl_rule_matches(link->rrl_rule, filter))
1341220163Strasz			continue;
1342220163Strasz		rctl_rule_to_sbuf(sb, link->rrl_rule);
1343220163Strasz		sbuf_printf(sb, ",");
1344220163Strasz	}
1345220163Strasz	rw_runlock(&rctl_lock);
1346220163Strasz}
1347220163Strasz
1348220163Straszint
1349225617Skmacysys_rctl_get_rules(struct thread *td, struct rctl_get_rules_args *uap)
1350220163Strasz{
1351220163Strasz	int error;
1352220163Strasz	size_t bufsize = RCTL_DEFAULT_BUFSIZE;
1353220163Strasz	char *inputstr, *buf;
1354220163Strasz	struct sbuf *sb;
1355220163Strasz	struct rctl_rule *filter;
1356220163Strasz	struct rctl_rule_link *link;
1357220163Strasz	struct proc *p;
1358220163Strasz
1359220163Strasz	error = priv_check(td, PRIV_RCTL_GET_RULES);
1360220163Strasz	if (error != 0)
1361220163Strasz		return (error);
1362220163Strasz
1363220163Strasz	error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen);
1364220163Strasz	if (error != 0)
1365220163Strasz		return (error);
1366220163Strasz
1367220163Strasz	sx_slock(&allproc_lock);
1368220163Strasz	error = rctl_string_to_rule(inputstr, &filter);
1369220163Strasz	free(inputstr, M_RCTL);
1370220163Strasz	if (error != 0) {
1371220163Strasz		sx_sunlock(&allproc_lock);
1372220163Strasz		return (error);
1373220163Strasz	}
1374220163Strasz
1375220163Straszagain:
1376220163Strasz	buf = malloc(bufsize, M_RCTL, M_WAITOK);
1377220163Strasz	sb = sbuf_new(NULL, buf, bufsize, SBUF_FIXEDLEN);
1378220163Strasz	KASSERT(sb != NULL, ("sbuf_new failed"));
1379220163Strasz
1380220163Strasz	sx_assert(&allproc_lock, SA_LOCKED);
1381220163Strasz	FOREACH_PROC_IN_SYSTEM(p) {
1382220163Strasz		rw_rlock(&rctl_lock);
1383220163Strasz		LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) {
1384220163Strasz			/*
1385220163Strasz			 * Non-process rules will be added to the buffer later.
1386220163Strasz			 * Adding them here would result in duplicated output.
1387220163Strasz			 */
1388220163Strasz			if (link->rrl_rule->rr_subject_type !=
1389220163Strasz			    RCTL_SUBJECT_TYPE_PROCESS)
1390220163Strasz				continue;
1391220163Strasz			if (!rctl_rule_matches(link->rrl_rule, filter))
1392220163Strasz				continue;
1393220163Strasz			rctl_rule_to_sbuf(sb, link->rrl_rule);
1394220163Strasz			sbuf_printf(sb, ",");
1395220163Strasz		}
1396220163Strasz		rw_runlock(&rctl_lock);
1397220163Strasz	}
1398220163Strasz
1399220163Strasz	loginclass_racct_foreach(rctl_get_rules_callback, filter, sb);
1400220163Strasz	ui_racct_foreach(rctl_get_rules_callback, filter, sb);
1401220163Strasz	prison_racct_foreach(rctl_get_rules_callback, filter, sb);
1402220163Strasz	if (sbuf_error(sb) == ENOMEM) {
1403220163Strasz		sbuf_delete(sb);
1404220163Strasz		free(buf, M_RCTL);
1405220163Strasz		bufsize *= 4;
1406220163Strasz		goto again;
1407220163Strasz	}
1408220163Strasz
1409220163Strasz	/*
1410220163Strasz	 * Remove trailing ",".
1411220163Strasz	 */
1412220163Strasz	if (sbuf_len(sb) > 0)
1413220163Strasz		sbuf_setpos(sb, sbuf_len(sb) - 1);
1414220163Strasz
1415220163Strasz	error = rctl_write_outbuf(sb, uap->outbufp, uap->outbuflen);
1416220163Strasz
1417220163Strasz	rctl_rule_release(filter);
1418220163Strasz	sx_sunlock(&allproc_lock);
1419220163Strasz	free(buf, M_RCTL);
1420220163Strasz	return (error);
1421220163Strasz}
1422220163Strasz
1423220163Straszint
1424225617Skmacysys_rctl_get_limits(struct thread *td, struct rctl_get_limits_args *uap)
1425220163Strasz{
1426220163Strasz	int error;
1427220163Strasz	size_t bufsize = RCTL_DEFAULT_BUFSIZE;
1428220163Strasz	char *inputstr, *buf;
1429220163Strasz	struct sbuf *sb;
1430220163Strasz	struct rctl_rule *filter;
1431220163Strasz	struct rctl_rule_link *link;
1432220163Strasz
1433220163Strasz	error = priv_check(td, PRIV_RCTL_GET_LIMITS);
1434220163Strasz	if (error != 0)
1435220163Strasz		return (error);
1436220163Strasz
1437220163Strasz	error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen);
1438220163Strasz	if (error != 0)
1439220163Strasz		return (error);
1440220163Strasz
1441220163Strasz	sx_slock(&allproc_lock);
1442220163Strasz	error = rctl_string_to_rule(inputstr, &filter);
1443220163Strasz	free(inputstr, M_RCTL);
1444220163Strasz	if (error != 0) {
1445220163Strasz		sx_sunlock(&allproc_lock);
1446220163Strasz		return (error);
1447220163Strasz	}
1448220163Strasz
1449220163Strasz	if (filter->rr_subject_type == RCTL_SUBJECT_TYPE_UNDEFINED) {
1450220163Strasz		rctl_rule_release(filter);
1451220163Strasz		sx_sunlock(&allproc_lock);
1452220163Strasz		return (EINVAL);
1453220163Strasz	}
1454220163Strasz	if (filter->rr_subject_type != RCTL_SUBJECT_TYPE_PROCESS) {
1455220163Strasz		rctl_rule_release(filter);
1456220163Strasz		sx_sunlock(&allproc_lock);
1457220163Strasz		return (EOPNOTSUPP);
1458220163Strasz	}
1459220163Strasz	if (filter->rr_subject.rs_proc == NULL) {
1460220163Strasz		rctl_rule_release(filter);
1461220163Strasz		sx_sunlock(&allproc_lock);
1462220163Strasz		return (EINVAL);
1463220163Strasz	}
1464220163Strasz
1465220163Straszagain:
1466220163Strasz	buf = malloc(bufsize, M_RCTL, M_WAITOK);
1467220163Strasz	sb = sbuf_new(NULL, buf, bufsize, SBUF_FIXEDLEN);
1468220163Strasz	KASSERT(sb != NULL, ("sbuf_new failed"));
1469220163Strasz
1470220163Strasz	rw_rlock(&rctl_lock);
1471220163Strasz	LIST_FOREACH(link, &filter->rr_subject.rs_proc->p_racct->r_rule_links,
1472220163Strasz	    rrl_next) {
1473220163Strasz		rctl_rule_to_sbuf(sb, link->rrl_rule);
1474220163Strasz		sbuf_printf(sb, ",");
1475220163Strasz	}
1476220163Strasz	rw_runlock(&rctl_lock);
1477220163Strasz	if (sbuf_error(sb) == ENOMEM) {
1478220163Strasz		sbuf_delete(sb);
1479220163Strasz		free(buf, M_RCTL);
1480220163Strasz		bufsize *= 4;
1481220163Strasz		goto again;
1482220163Strasz	}
1483220163Strasz
1484220163Strasz	/*
1485220163Strasz	 * Remove trailing ",".
1486220163Strasz	 */
1487220163Strasz	if (sbuf_len(sb) > 0)
1488220163Strasz		sbuf_setpos(sb, sbuf_len(sb) - 1);
1489220163Strasz
1490220163Strasz	error = rctl_write_outbuf(sb, uap->outbufp, uap->outbuflen);
1491220163Strasz	rctl_rule_release(filter);
1492220163Strasz	sx_sunlock(&allproc_lock);
1493220163Strasz	free(buf, M_RCTL);
1494220163Strasz	return (error);
1495220163Strasz}
1496220163Strasz
1497220163Straszint
1498225617Skmacysys_rctl_add_rule(struct thread *td, struct rctl_add_rule_args *uap)
1499220163Strasz{
1500220163Strasz	int error;
1501220163Strasz	struct rctl_rule *rule;
1502220163Strasz	char *inputstr;
1503220163Strasz
1504220163Strasz	error = priv_check(td, PRIV_RCTL_ADD_RULE);
1505220163Strasz	if (error != 0)
1506220163Strasz		return (error);
1507220163Strasz
1508220163Strasz	error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen);
1509220163Strasz	if (error != 0)
1510220163Strasz		return (error);
1511220163Strasz
1512220163Strasz	sx_slock(&allproc_lock);
1513220163Strasz	error = rctl_string_to_rule(inputstr, &rule);
1514220163Strasz	free(inputstr, M_RCTL);
1515220163Strasz	if (error != 0) {
1516220163Strasz		sx_sunlock(&allproc_lock);
1517220163Strasz		return (error);
1518220163Strasz	}
1519220163Strasz	/*
1520220163Strasz	 * The 'per' part of a rule is optional.
1521220163Strasz	 */
1522220163Strasz	if (rule->rr_per == RCTL_SUBJECT_TYPE_UNDEFINED &&
1523220163Strasz	    rule->rr_subject_type != RCTL_SUBJECT_TYPE_UNDEFINED)
1524220163Strasz		rule->rr_per = rule->rr_subject_type;
1525220163Strasz
1526220163Strasz	if (!rctl_rule_fully_specified(rule)) {
1527220163Strasz		error = EINVAL;
1528220163Strasz		goto out;
1529220163Strasz	}
1530220163Strasz
1531220163Strasz	error = rctl_rule_add(rule);
1532220163Strasz
1533220163Straszout:
1534220163Strasz	rctl_rule_release(rule);
1535220163Strasz	sx_sunlock(&allproc_lock);
1536220163Strasz	return (error);
1537220163Strasz}
1538220163Strasz
1539220163Straszint
1540225617Skmacysys_rctl_remove_rule(struct thread *td, struct rctl_remove_rule_args *uap)
1541220163Strasz{
1542220163Strasz	int error;
1543220163Strasz	struct rctl_rule *filter;
1544220163Strasz	char *inputstr;
1545220163Strasz
1546220163Strasz	error = priv_check(td, PRIV_RCTL_REMOVE_RULE);
1547220163Strasz	if (error != 0)
1548220163Strasz		return (error);
1549220163Strasz
1550220163Strasz	error = rctl_read_inbuf(&inputstr, uap->inbufp, uap->inbuflen);
1551220163Strasz	if (error != 0)
1552220163Strasz		return (error);
1553220163Strasz
1554220163Strasz	sx_slock(&allproc_lock);
1555220163Strasz	error = rctl_string_to_rule(inputstr, &filter);
1556220163Strasz	free(inputstr, M_RCTL);
1557220163Strasz	if (error != 0) {
1558220163Strasz		sx_sunlock(&allproc_lock);
1559220163Strasz		return (error);
1560220163Strasz	}
1561220163Strasz
1562220163Strasz	error = rctl_rule_remove(filter);
1563220163Strasz	rctl_rule_release(filter);
1564220163Strasz	sx_sunlock(&allproc_lock);
1565220163Strasz
1566220163Strasz	return (error);
1567220163Strasz}
1568220163Strasz
1569220163Strasz/*
1570220163Strasz * Update RCTL rule list after credential change.
1571220163Strasz */
1572220163Straszvoid
1573220163Straszrctl_proc_ucred_changed(struct proc *p, struct ucred *newcred)
1574220163Strasz{
1575220163Strasz	int rulecnt, i;
1576220163Strasz	struct rctl_rule_link *link, *newlink;
1577220163Strasz	struct uidinfo *newuip;
1578220163Strasz	struct loginclass *newlc;
1579221362Strasz	struct prison_racct *newprr;
1580220163Strasz	LIST_HEAD(, rctl_rule_link) newrules;
1581220163Strasz
1582220163Strasz	newuip = newcred->cr_ruidinfo;
1583220163Strasz	newlc = newcred->cr_loginclass;
1584221362Strasz	newprr = newcred->cr_prison->pr_prison_racct;
1585220163Strasz
1586220163Strasz	LIST_INIT(&newrules);
1587220163Strasz
1588220163Straszagain:
1589220163Strasz	/*
1590220163Strasz	 * First, count the rules that apply to the process with new
1591220163Strasz	 * credentials.
1592220163Strasz	 */
1593220163Strasz	rulecnt = 0;
1594220163Strasz	rw_rlock(&rctl_lock);
1595220163Strasz	LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) {
1596220163Strasz		if (link->rrl_rule->rr_subject_type ==
1597220163Strasz		    RCTL_SUBJECT_TYPE_PROCESS)
1598220163Strasz			rulecnt++;
1599220163Strasz	}
1600220163Strasz	LIST_FOREACH(link, &newuip->ui_racct->r_rule_links, rrl_next)
1601220163Strasz		rulecnt++;
1602220163Strasz	LIST_FOREACH(link, &newlc->lc_racct->r_rule_links, rrl_next)
1603220163Strasz		rulecnt++;
1604221362Strasz	LIST_FOREACH(link, &newprr->prr_racct->r_rule_links, rrl_next)
1605220163Strasz		rulecnt++;
1606220163Strasz	rw_runlock(&rctl_lock);
1607220163Strasz
1608220163Strasz	/*
1609220163Strasz	 * Create temporary list.  We've dropped the rctl_lock in order
1610220163Strasz	 * to use M_WAITOK.
1611220163Strasz	 */
1612220163Strasz	for (i = 0; i < rulecnt; i++) {
1613220163Strasz		newlink = uma_zalloc(rctl_rule_link_zone, M_WAITOK);
1614220163Strasz		newlink->rrl_rule = NULL;
1615220163Strasz		LIST_INSERT_HEAD(&newrules, newlink, rrl_next);
1616220163Strasz	}
1617220163Strasz
1618220163Strasz	newlink = LIST_FIRST(&newrules);
1619220163Strasz
1620220163Strasz	/*
1621220163Strasz	 * Assign rules to the newly allocated list entries.
1622220163Strasz	 */
1623220163Strasz	rw_wlock(&rctl_lock);
1624220163Strasz	LIST_FOREACH(link, &p->p_racct->r_rule_links, rrl_next) {
1625220163Strasz		if (link->rrl_rule->rr_subject_type ==
1626220163Strasz		    RCTL_SUBJECT_TYPE_PROCESS) {
1627220163Strasz			if (newlink == NULL)
1628220163Strasz				goto goaround;
1629220163Strasz			rctl_rule_acquire(link->rrl_rule);
1630220163Strasz			newlink->rrl_rule = link->rrl_rule;
1631220163Strasz			newlink = LIST_NEXT(newlink, rrl_next);
1632220163Strasz			rulecnt--;
1633220163Strasz		}
1634220163Strasz	}
1635220163Strasz
1636220163Strasz	LIST_FOREACH(link, &newuip->ui_racct->r_rule_links, rrl_next) {
1637220163Strasz		if (newlink == NULL)
1638220163Strasz			goto goaround;
1639220163Strasz		rctl_rule_acquire(link->rrl_rule);
1640220163Strasz		newlink->rrl_rule = link->rrl_rule;
1641220163Strasz		newlink = LIST_NEXT(newlink, rrl_next);
1642220163Strasz		rulecnt--;
1643220163Strasz	}
1644220163Strasz
1645220163Strasz	LIST_FOREACH(link, &newlc->lc_racct->r_rule_links, rrl_next) {
1646220163Strasz		if (newlink == NULL)
1647220163Strasz			goto goaround;
1648220163Strasz		rctl_rule_acquire(link->rrl_rule);
1649220163Strasz		newlink->rrl_rule = link->rrl_rule;
1650220163Strasz		newlink = LIST_NEXT(newlink, rrl_next);
1651220163Strasz		rulecnt--;
1652220163Strasz	}
1653220163Strasz
1654221362Strasz	LIST_FOREACH(link, &newprr->prr_racct->r_rule_links, rrl_next) {
1655220163Strasz		if (newlink == NULL)
1656220163Strasz			goto goaround;
1657220163Strasz		rctl_rule_acquire(link->rrl_rule);
1658220163Strasz		newlink->rrl_rule = link->rrl_rule;
1659220163Strasz		newlink = LIST_NEXT(newlink, rrl_next);
1660220163Strasz		rulecnt--;
1661220163Strasz	}
1662220163Strasz
1663220163Strasz	if (rulecnt == 0) {
1664220163Strasz		/*
1665220163Strasz		 * Free the old rule list.
1666220163Strasz		 */
1667220163Strasz		while (!LIST_EMPTY(&p->p_racct->r_rule_links)) {
1668220163Strasz			link = LIST_FIRST(&p->p_racct->r_rule_links);
1669220163Strasz			LIST_REMOVE(link, rrl_next);
1670220163Strasz			rctl_rule_release(link->rrl_rule);
1671220163Strasz			uma_zfree(rctl_rule_link_zone, link);
1672220163Strasz		}
1673220163Strasz
1674220163Strasz		/*
1675220163Strasz		 * Replace lists and we're done.
1676220163Strasz		 *
1677220163Strasz		 * XXX: Is there any way to switch list heads instead
1678220163Strasz		 *      of iterating here?
1679220163Strasz		 */
1680220163Strasz		while (!LIST_EMPTY(&newrules)) {
1681220163Strasz			newlink = LIST_FIRST(&newrules);
1682220163Strasz			LIST_REMOVE(newlink, rrl_next);
1683220163Strasz			LIST_INSERT_HEAD(&p->p_racct->r_rule_links,
1684220163Strasz			    newlink, rrl_next);
1685220163Strasz		}
1686220163Strasz
1687220163Strasz		rw_wunlock(&rctl_lock);
1688220163Strasz
1689220163Strasz		return;
1690220163Strasz	}
1691220163Strasz
1692220163Straszgoaround:
1693220163Strasz	rw_wunlock(&rctl_lock);
1694220163Strasz
1695220163Strasz	/*
1696220163Strasz	 * Rule list changed while we were not holding the rctl_lock.
1697220163Strasz	 * Free the new list and try again.
1698220163Strasz	 */
1699220163Strasz	while (!LIST_EMPTY(&newrules)) {
1700220163Strasz		newlink = LIST_FIRST(&newrules);
1701220163Strasz		LIST_REMOVE(newlink, rrl_next);
1702220163Strasz		if (newlink->rrl_rule != NULL)
1703220163Strasz			rctl_rule_release(newlink->rrl_rule);
1704220163Strasz		uma_zfree(rctl_rule_link_zone, newlink);
1705220163Strasz	}
1706220163Strasz
1707220163Strasz	goto again;
1708220163Strasz}
1709220163Strasz
1710220163Strasz/*
1711220163Strasz * Assign RCTL rules to the newly created process.
1712220163Strasz */
1713220163Straszint
1714220163Straszrctl_proc_fork(struct proc *parent, struct proc *child)
1715220163Strasz{
1716220163Strasz	int error;
1717220163Strasz	struct rctl_rule_link *link;
1718220163Strasz	struct rctl_rule *rule;
1719220163Strasz
1720220163Strasz	LIST_INIT(&child->p_racct->r_rule_links);
1721220163Strasz
1722220163Strasz	/*
1723220163Strasz	 * No limits for kernel processes.
1724220163Strasz	 */
1725220163Strasz	if (child->p_flag & P_SYSTEM)
1726220163Strasz		return (0);
1727220163Strasz
1728220163Strasz	/*
1729220163Strasz	 * Nothing to inherit from P_SYSTEM parents.
1730220163Strasz	 */
1731220163Strasz	if (parent->p_racct == NULL) {
1732220163Strasz		KASSERT(parent->p_flag & P_SYSTEM,
1733220163Strasz		    ("non-system process without racct; p = %p", parent));
1734220163Strasz		return (0);
1735220163Strasz	}
1736220163Strasz
1737220163Strasz	rw_wlock(&rctl_lock);
1738220163Strasz
1739220163Strasz	/*
1740220163Strasz	 * Go through limits applicable to the parent and assign them
1741220163Strasz	 * to the child.  Rules with 'process' subject have to be duplicated
1742220163Strasz	 * in order to make their rr_subject point to the new process.
1743220163Strasz	 */
1744220163Strasz	LIST_FOREACH(link, &parent->p_racct->r_rule_links, rrl_next) {
1745220163Strasz		if (link->rrl_rule->rr_subject_type ==
1746220163Strasz		    RCTL_SUBJECT_TYPE_PROCESS) {
1747220163Strasz			rule = rctl_rule_duplicate(link->rrl_rule, M_NOWAIT);
1748220163Strasz			if (rule == NULL)
1749220163Strasz				goto fail;
1750220163Strasz			KASSERT(rule->rr_subject.rs_proc == parent,
1751220163Strasz			    ("rule->rr_subject.rs_proc != parent"));
1752220163Strasz			rule->rr_subject.rs_proc = child;
1753220163Strasz			error = rctl_racct_add_rule_locked(child->p_racct,
1754220163Strasz			    rule);
1755220163Strasz			rctl_rule_release(rule);
1756220163Strasz			if (error != 0)
1757220163Strasz				goto fail;
1758220163Strasz		} else {
1759220163Strasz			error = rctl_racct_add_rule_locked(child->p_racct,
1760220163Strasz			    link->rrl_rule);
1761220163Strasz			if (error != 0)
1762220163Strasz				goto fail;
1763220163Strasz		}
1764220163Strasz	}
1765220163Strasz
1766220163Strasz	rw_wunlock(&rctl_lock);
1767220163Strasz	return (0);
1768220163Strasz
1769220163Straszfail:
1770220163Strasz	while (!LIST_EMPTY(&child->p_racct->r_rule_links)) {
1771220163Strasz		link = LIST_FIRST(&child->p_racct->r_rule_links);
1772220163Strasz		LIST_REMOVE(link, rrl_next);
1773220163Strasz		rctl_rule_release(link->rrl_rule);
1774220163Strasz		uma_zfree(rctl_rule_link_zone, link);
1775220163Strasz	}
1776220163Strasz	rw_wunlock(&rctl_lock);
1777220163Strasz	return (EAGAIN);
1778220163Strasz}
1779220163Strasz
1780220163Strasz/*
1781220163Strasz * Release rules attached to the racct.
1782220163Strasz */
1783220163Straszvoid
1784220163Straszrctl_racct_release(struct racct *racct)
1785220163Strasz{
1786220163Strasz	struct rctl_rule_link *link;
1787220163Strasz
1788220163Strasz	rw_wlock(&rctl_lock);
1789220163Strasz	while (!LIST_EMPTY(&racct->r_rule_links)) {
1790220163Strasz		link = LIST_FIRST(&racct->r_rule_links);
1791220163Strasz		LIST_REMOVE(link, rrl_next);
1792220163Strasz		rctl_rule_release(link->rrl_rule);
1793220163Strasz		uma_zfree(rctl_rule_link_zone, link);
1794220163Strasz	}
1795220163Strasz	rw_wunlock(&rctl_lock);
1796220163Strasz}
1797220163Strasz
1798220163Straszstatic void
1799220163Straszrctl_init(void)
1800220163Strasz{
1801220163Strasz
1802220163Strasz	rctl_rule_link_zone = uma_zcreate("rctl_rule_link",
1803220163Strasz	    sizeof(struct rctl_rule_link), NULL, NULL, NULL, NULL,
1804220163Strasz	    UMA_ALIGN_PTR, UMA_ZONE_NOFREE);
1805220163Strasz	rctl_rule_zone = uma_zcreate("rctl_rule", sizeof(struct rctl_rule),
1806220163Strasz	    NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE);
1807220163Strasz}
1808220163Strasz
1809220163Strasz#else /* !RCTL */
1810220163Strasz
1811220163Straszint
1812225617Skmacysys_rctl_get_racct(struct thread *td, struct rctl_get_racct_args *uap)
1813220163Strasz{
1814220163Strasz
1815220163Strasz	return (ENOSYS);
1816220163Strasz}
1817220163Strasz
1818220163Straszint
1819225617Skmacysys_rctl_get_rules(struct thread *td, struct rctl_get_rules_args *uap)
1820220163Strasz{
1821220163Strasz
1822220163Strasz	return (ENOSYS);
1823220163Strasz}
1824220163Strasz
1825220163Straszint
1826225617Skmacysys_rctl_get_limits(struct thread *td, struct rctl_get_limits_args *uap)
1827220163Strasz{
1828220163Strasz
1829220163Strasz	return (ENOSYS);
1830220163Strasz}
1831220163Strasz
1832220163Straszint
1833225617Skmacysys_rctl_add_rule(struct thread *td, struct rctl_add_rule_args *uap)
1834220163Strasz{
1835220163Strasz
1836220163Strasz	return (ENOSYS);
1837220163Strasz}
1838220163Strasz
1839220163Straszint
1840225617Skmacysys_rctl_remove_rule(struct thread *td, struct rctl_remove_rule_args *uap)
1841220163Strasz{
1842220163Strasz
1843220163Strasz	return (ENOSYS);
1844220163Strasz}
1845220163Strasz
1846220163Strasz#endif /* !RCTL */
1847