kern_resource.c revision 1.20
1/*	$OpenBSD: kern_resource.c,v 1.20 2002/10/02 21:56:30 nordin Exp $	*/
2/*	$NetBSD: kern_resource.c,v 1.38 1996/10/23 07:19:38 matthias Exp $	*/
3
4/*-
5 * Copyright (c) 1982, 1986, 1991, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 * (c) UNIX System Laboratories, Inc.
8 * All or some portions of this file are derived from material licensed
9 * to the University of California by American Telephone and Telegraph
10 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
11 * the permission of UNIX System Laboratories, Inc.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 *    notice, this list of conditions and the following disclaimer in the
20 *    documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 *    must display the following acknowledgement:
23 *	This product includes software developed by the University of
24 *	California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 *    may be used to endorse or promote products derived from this software
27 *    without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
40 *
41 *	@(#)kern_resource.c	8.5 (Berkeley) 1/21/94
42 */
43
44#include <sys/param.h>
45#include <sys/systm.h>
46#include <sys/kernel.h>
47#include <sys/file.h>
48#include <sys/resourcevar.h>
49#include <sys/pool.h>
50#include <sys/proc.h>
51
52#include <sys/mount.h>
53#include <sys/syscallargs.h>
54
55#include <uvm/uvm_extern.h>
56
57/*
58 * Patchable maximum data and stack limits.
59 */
60rlim_t maxdmap = MAXDSIZ;
61rlim_t maxsmap = MAXSSIZ;
62
63/*
64 * Resource controls and accounting.
65 */
66
67int
68sys_getpriority(curp, v, retval)
69	struct proc *curp;
70	void *v;
71	register_t *retval;
72{
73	register struct sys_getpriority_args /* {
74		syscallarg(int) which;
75		syscallarg(int) who;
76	} */ *uap = v;
77	register struct proc *p;
78	register int low = NZERO + PRIO_MAX + 1;
79
80	switch (SCARG(uap, which)) {
81
82	case PRIO_PROCESS:
83		if (SCARG(uap, who) == 0)
84			p = curp;
85		else
86			p = pfind(SCARG(uap, who));
87		if (p == 0)
88			break;
89		low = p->p_nice;
90		break;
91
92	case PRIO_PGRP: {
93		register struct pgrp *pg;
94
95		if (SCARG(uap, who) == 0)
96			pg = curp->p_pgrp;
97		else if ((pg = pgfind(SCARG(uap, who))) == NULL)
98			break;
99		for (p = pg->pg_members.lh_first; p != 0; p = p->p_pglist.le_next) {
100			if (p->p_nice < low)
101				low = p->p_nice;
102		}
103		break;
104	}
105
106	case PRIO_USER:
107		if (SCARG(uap, who) == 0)
108			SCARG(uap, who) = curp->p_ucred->cr_uid;
109		for (p = LIST_FIRST(&allproc); p; p = LIST_NEXT(p, p_list))
110			if (p->p_ucred->cr_uid == SCARG(uap, who) &&
111			    p->p_nice < low)
112				low = p->p_nice;
113		break;
114
115	default:
116		return (EINVAL);
117	}
118	if (low == NZERO + PRIO_MAX + 1)
119		return (ESRCH);
120	*retval = low - NZERO;
121	return (0);
122}
123
124/* ARGSUSED */
125int
126sys_setpriority(curp, v, retval)
127	struct proc *curp;
128	void *v;
129	register_t *retval;
130{
131	register struct sys_setpriority_args /* {
132		syscallarg(int) which;
133		syscallarg(int) who;
134		syscallarg(int) prio;
135	} */ *uap = v;
136	register struct proc *p;
137	int found = 0, error = 0;
138
139	switch (SCARG(uap, which)) {
140
141	case PRIO_PROCESS:
142		if (SCARG(uap, who) == 0)
143			p = curp;
144		else
145			p = pfind(SCARG(uap, who));
146		if (p == 0)
147			break;
148		error = donice(curp, p, SCARG(uap, prio));
149		found++;
150		break;
151
152	case PRIO_PGRP: {
153		register struct pgrp *pg;
154
155		if (SCARG(uap, who) == 0)
156			pg = curp->p_pgrp;
157		else if ((pg = pgfind(SCARG(uap, who))) == NULL)
158			break;
159		for (p = pg->pg_members.lh_first; p != 0;
160		    p = p->p_pglist.le_next) {
161			error = donice(curp, p, SCARG(uap, prio));
162			found++;
163		}
164		break;
165	}
166
167	case PRIO_USER:
168		if (SCARG(uap, who) == 0)
169			SCARG(uap, who) = curp->p_ucred->cr_uid;
170		for (p = LIST_FIRST(&allproc); p; p = LIST_NEXT(p, p_list))
171			if (p->p_ucred->cr_uid == SCARG(uap, who)) {
172				error = donice(curp, p, SCARG(uap, prio));
173				found++;
174			}
175		break;
176
177	default:
178		return (EINVAL);
179	}
180	if (found == 0)
181		return (ESRCH);
182	return (error);
183}
184
185int
186donice(curp, chgp, n)
187	register struct proc *curp, *chgp;
188	register int n;
189{
190	register struct pcred *pcred = curp->p_cred;
191
192	if (pcred->pc_ucred->cr_uid && pcred->p_ruid &&
193	    pcred->pc_ucred->cr_uid != chgp->p_ucred->cr_uid &&
194	    pcred->p_ruid != chgp->p_ucred->cr_uid)
195		return (EPERM);
196	if (n > PRIO_MAX)
197		n = PRIO_MAX;
198	if (n < PRIO_MIN)
199		n = PRIO_MIN;
200	n += NZERO;
201	if (n < chgp->p_nice && suser(pcred->pc_ucred, &curp->p_acflag))
202		return (EACCES);
203	chgp->p_nice = n;
204	(void)resetpriority(chgp);
205	return (0);
206}
207
208/* ARGSUSED */
209int
210sys_setrlimit(p, v, retval)
211	struct proc *p;
212	void *v;
213	register_t *retval;
214{
215	register struct sys_setrlimit_args /* {
216		syscallarg(u_int) which;
217		syscallarg(struct rlimit *) rlp;
218	} */ *uap = v;
219	struct rlimit alim;
220	int error;
221
222	error = copyin((caddr_t)SCARG(uap, rlp), (caddr_t)&alim,
223		       sizeof (struct rlimit));
224	if (error)
225		return (error);
226	return (dosetrlimit(p, SCARG(uap, which), &alim));
227}
228
229int
230dosetrlimit(p, which, limp)
231	struct proc *p;
232	u_int which;
233	struct rlimit *limp;
234{
235	struct rlimit *alimp;
236	rlim_t maxlim;
237	int error;
238
239	if (which >= RLIM_NLIMITS)
240		return (EINVAL);
241
242	if (limp->rlim_cur < 0 || limp->rlim_max < 0)
243		return (EINVAL);
244
245	alimp = &p->p_rlimit[which];
246	if (limp->rlim_cur > alimp->rlim_max ||
247	    limp->rlim_max > alimp->rlim_max)
248		if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
249			return (error);
250	if (p->p_limit->p_refcnt > 1 &&
251	    (p->p_limit->p_lflags & PL_SHAREMOD) == 0) {
252		p->p_limit->p_refcnt--;
253		p->p_limit = limcopy(p->p_limit);
254		alimp = &p->p_rlimit[which];
255	}
256
257	switch (which) {
258	case RLIMIT_DATA:
259		maxlim = maxdmap;
260		break;
261	case RLIMIT_STACK:
262		maxlim = maxsmap;
263		break;
264	case RLIMIT_NOFILE:
265		maxlim = maxfiles;
266		break;
267	case RLIMIT_NPROC:
268		maxlim = maxproc;
269		break;
270	default:
271		maxlim = RLIM_INFINITY;
272		break;
273	}
274
275	if (limp->rlim_max > maxlim)
276		limp->rlim_max = maxlim;
277	if (limp->rlim_cur > limp->rlim_max)
278		limp->rlim_cur = limp->rlim_max;
279
280	if (which == RLIMIT_STACK) {
281		/*
282		 * Stack is allocated to the max at exec time with only
283		 * "rlim_cur" bytes accessible.  If stack limit is going
284		 * up make more accessible, if going down make inaccessible.
285		 */
286		if (limp->rlim_cur != alimp->rlim_cur) {
287			vaddr_t addr;
288			vsize_t size;
289			vm_prot_t prot;
290
291			if (limp->rlim_cur > alimp->rlim_cur) {
292				prot = VM_PROT_READ|VM_PROT_WRITE;
293				size = limp->rlim_cur - alimp->rlim_cur;
294#ifdef MACHINE_STACK_GROWS_UP
295				addr = USRSTACK + alimp->rlim_cur;
296#else
297				addr = USRSTACK - limp->rlim_cur;
298#endif
299			} else {
300				prot = VM_PROT_NONE;
301				size = alimp->rlim_cur - limp->rlim_cur;
302#ifdef MACHINE_STACK_GROWS_UP
303				addr = USRSTACK + limp->rlim_cur;
304#else
305				addr = USRSTACK - alimp->rlim_cur;
306#endif
307			}
308			addr = trunc_page(addr);
309			size = round_page(size);
310			(void) uvm_map_protect(&p->p_vmspace->vm_map,
311					      addr, addr+size, prot, FALSE);
312		}
313	}
314
315	*alimp = *limp;
316	return (0);
317}
318
319/* ARGSUSED */
320int
321sys_getrlimit(p, v, retval)
322	struct proc *p;
323	void *v;
324	register_t *retval;
325{
326	register struct sys_getrlimit_args /* {
327		syscallarg(int) which;
328		syscallarg(struct rlimit *) rlp;
329	} */ *uap = v;
330
331	if (SCARG(uap, which) < 0 || SCARG(uap, which) >= RLIM_NLIMITS)
332		return (EINVAL);
333	return (copyout((caddr_t)&p->p_rlimit[SCARG(uap, which)],
334	    (caddr_t)SCARG(uap, rlp), sizeof (struct rlimit)));
335}
336
337/*
338 * Transform the running time and tick information in proc p into user,
339 * system, and interrupt time usage.
340 */
341void
342calcru(p, up, sp, ip)
343	struct proc *p;
344	struct timeval *up;
345	struct timeval *sp;
346	struct timeval *ip;
347{
348	u_quad_t st, ut, it;
349	int freq;
350	int s;
351
352	s = splstatclock();
353	st = p->p_sticks;
354	ut = p->p_uticks;
355	it = p->p_iticks;
356	splx(s);
357
358	if (st + ut + it == 0) {
359		timerclear(up);
360		timerclear(sp);
361		if (ip != NULL)
362			timerclear(ip);
363		return;
364	}
365
366	freq = stathz ? stathz : hz;
367
368	st = st * 1000000 / freq;
369	sp->tv_sec = st / 1000000;
370	sp->tv_usec = st % 1000000;
371	ut = ut * 1000000 / freq;
372	up->tv_sec = ut / 1000000;
373	up->tv_usec = ut % 1000000;
374	if (ip != NULL) {
375		it = it * 1000000 / freq;
376		ip->tv_sec = it / 1000000;
377		ip->tv_usec = it % 1000000;
378	}
379}
380
381/* ARGSUSED */
382int
383sys_getrusage(p, v, retval)
384	register struct proc *p;
385	void *v;
386	register_t *retval;
387{
388	register struct sys_getrusage_args /* {
389		syscallarg(int) who;
390		syscallarg(struct rusage *) rusage;
391	} */ *uap = v;
392	register struct rusage *rup;
393
394	switch (SCARG(uap, who)) {
395
396	case RUSAGE_SELF:
397		rup = &p->p_stats->p_ru;
398		calcru(p, &rup->ru_utime, &rup->ru_stime, NULL);
399		break;
400
401	case RUSAGE_CHILDREN:
402		rup = &p->p_stats->p_cru;
403		break;
404
405	default:
406		return (EINVAL);
407	}
408	return (copyout((caddr_t)rup, (caddr_t)SCARG(uap, rusage),
409	    sizeof (struct rusage)));
410}
411
412void
413ruadd(ru, ru2)
414	register struct rusage *ru, *ru2;
415{
416	register long *ip, *ip2;
417	register int i;
418
419	timeradd(&ru->ru_utime, &ru2->ru_utime, &ru->ru_utime);
420	timeradd(&ru->ru_stime, &ru2->ru_stime, &ru->ru_stime);
421	if (ru->ru_maxrss < ru2->ru_maxrss)
422		ru->ru_maxrss = ru2->ru_maxrss;
423	ip = &ru->ru_first; ip2 = &ru2->ru_first;
424	for (i = &ru->ru_last - &ru->ru_first; i >= 0; i--)
425		*ip++ += *ip2++;
426}
427
428struct pool plimit_pool;
429
430/*
431 * Make a copy of the plimit structure.
432 * We share these structures copy-on-write after fork,
433 * and copy when a limit is changed.
434 */
435struct plimit *
436limcopy(struct plimit *lim)
437{
438	struct plimit *newlim;
439	static int initialized;
440
441	if (!initialized) {
442		pool_init(&plimit_pool, sizeof(struct plimit), 0, 0, 0,
443		    "plimitpl", &pool_allocator_nointr);
444		initialized = 1;
445	}
446
447	newlim = pool_get(&plimit_pool, PR_WAITOK);
448	bcopy(lim->pl_rlimit, newlim->pl_rlimit,
449	    sizeof(struct rlimit) * RLIM_NLIMITS);
450	newlim->p_lflags = 0;
451	newlim->p_refcnt = 1;
452	return (newlim);
453}
454
455void
456limfree(struct plimit *lim)
457{
458	if (--lim->p_refcnt > 0)
459		return;
460	pool_put(&plimit_pool, lim);
461}
462