kern_resource.c revision 1.13
1/*	$OpenBSD: kern_resource.c,v 1.13 2001/05/26 04:10:26 art 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/malloc.h>
50#include <sys/proc.h>
51
52#include <sys/mount.h>
53#include <sys/syscallargs.h>
54
55#include <vm/vm.h>
56
57#if defined(UVM)
58#include <uvm/uvm_extern.h>
59#endif
60
61/*
62 * Resource controls and accounting.
63 */
64
65int
66sys_getpriority(curp, v, retval)
67	struct proc *curp;
68	void *v;
69	register_t *retval;
70{
71	register struct sys_getpriority_args /* {
72		syscallarg(int) which;
73		syscallarg(int) who;
74	} */ *uap = v;
75	register struct proc *p;
76	register int low = NZERO + PRIO_MAX + 1;
77
78	switch (SCARG(uap, which)) {
79
80	case PRIO_PROCESS:
81		if (SCARG(uap, who) == 0)
82			p = curp;
83		else
84			p = pfind(SCARG(uap, who));
85		if (p == 0)
86			break;
87		low = p->p_nice;
88		break;
89
90	case PRIO_PGRP: {
91		register struct pgrp *pg;
92
93		if (SCARG(uap, who) == 0)
94			pg = curp->p_pgrp;
95		else if ((pg = pgfind(SCARG(uap, who))) == NULL)
96			break;
97		for (p = pg->pg_members.lh_first; p != 0; p = p->p_pglist.le_next) {
98			if (p->p_nice < low)
99				low = p->p_nice;
100		}
101		break;
102	}
103
104	case PRIO_USER:
105		if (SCARG(uap, who) == 0)
106			SCARG(uap, who) = curp->p_ucred->cr_uid;
107		for (p = LIST_FIRST(&allproc); p; p = LIST_NEXT(p, p_list))
108			if (p->p_ucred->cr_uid == SCARG(uap, who) &&
109			    p->p_nice < low)
110				low = p->p_nice;
111		break;
112
113	default:
114		return (EINVAL);
115	}
116	if (low == NZERO + PRIO_MAX + 1)
117		return (ESRCH);
118	*retval = low - NZERO;
119	return (0);
120}
121
122/* ARGSUSED */
123int
124sys_setpriority(curp, v, retval)
125	struct proc *curp;
126	void *v;
127	register_t *retval;
128{
129	register struct sys_setpriority_args /* {
130		syscallarg(int) which;
131		syscallarg(int) who;
132		syscallarg(int) prio;
133	} */ *uap = v;
134	register struct proc *p;
135	int found = 0, error = 0;
136
137	switch (SCARG(uap, which)) {
138
139	case PRIO_PROCESS:
140		if (SCARG(uap, who) == 0)
141			p = curp;
142		else
143			p = pfind(SCARG(uap, who));
144		if (p == 0)
145			break;
146		error = donice(curp, p, SCARG(uap, prio));
147		found++;
148		break;
149
150	case PRIO_PGRP: {
151		register struct pgrp *pg;
152
153		if (SCARG(uap, who) == 0)
154			pg = curp->p_pgrp;
155		else if ((pg = pgfind(SCARG(uap, who))) == NULL)
156			break;
157		for (p = pg->pg_members.lh_first; p != 0;
158		    p = p->p_pglist.le_next) {
159			error = donice(curp, p, SCARG(uap, prio));
160			found++;
161		}
162		break;
163	}
164
165	case PRIO_USER:
166		if (SCARG(uap, who) == 0)
167			SCARG(uap, who) = curp->p_ucred->cr_uid;
168		for (p = LIST_FIRST(&allproc); p; p = LIST_NEXT(p, p_list))
169			if (p->p_ucred->cr_uid == SCARG(uap, who)) {
170				error = donice(curp, p, SCARG(uap, prio));
171				found++;
172			}
173		break;
174
175	default:
176		return (EINVAL);
177	}
178	if (found == 0)
179		return (ESRCH);
180	return (error);
181}
182
183int
184donice(curp, chgp, n)
185	register struct proc *curp, *chgp;
186	register int n;
187{
188	register struct pcred *pcred = curp->p_cred;
189
190	if (pcred->pc_ucred->cr_uid && pcred->p_ruid &&
191	    pcred->pc_ucred->cr_uid != chgp->p_ucred->cr_uid &&
192	    pcred->p_ruid != chgp->p_ucred->cr_uid)
193		return (EPERM);
194	if (n > PRIO_MAX)
195		n = PRIO_MAX;
196	if (n < PRIO_MIN)
197		n = PRIO_MIN;
198	n += NZERO;
199	if (n < chgp->p_nice && suser(pcred->pc_ucred, &curp->p_acflag))
200		return (EACCES);
201	chgp->p_nice = n;
202	(void)resetpriority(chgp);
203	return (0);
204}
205
206/* ARGSUSED */
207int
208sys_setrlimit(p, v, retval)
209	struct proc *p;
210	void *v;
211	register_t *retval;
212{
213	register struct sys_setrlimit_args /* {
214		syscallarg(u_int) which;
215		syscallarg(struct rlimit *) rlp;
216	} */ *uap = v;
217	struct rlimit alim;
218	int error;
219
220	error = copyin((caddr_t)SCARG(uap, rlp), (caddr_t)&alim,
221		       sizeof (struct rlimit));
222	if (error)
223		return (error);
224	return (dosetrlimit(p, SCARG(uap, which), &alim));
225}
226
227int
228dosetrlimit(p, which, limp)
229	struct proc *p;
230	u_int which;
231	struct rlimit *limp;
232{
233	struct rlimit *alimp;
234	extern unsigned maxdmap, maxsmap;
235	rlim_t maxlim;
236	int error;
237
238	if (which >= RLIM_NLIMITS)
239		return (EINVAL);
240
241	if (limp->rlim_cur < 0 || limp->rlim_max < 0)
242		return (EINVAL);
243
244	alimp = &p->p_rlimit[which];
245	if (limp->rlim_cur > alimp->rlim_max ||
246	    limp->rlim_max > alimp->rlim_max)
247		if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
248			return (error);
249	if (p->p_limit->p_refcnt > 1 &&
250	    (p->p_limit->p_lflags & PL_SHAREMOD) == 0) {
251		p->p_limit->p_refcnt--;
252		p->p_limit = limcopy(p->p_limit);
253		alimp = &p->p_rlimit[which];
254	}
255
256	switch (which) {
257	case RLIMIT_DATA:
258		maxlim = maxdmap;
259		break;
260	case RLIMIT_STACK:
261		maxlim = maxsmap;
262		break;
263	case RLIMIT_NOFILE:
264		maxlim = maxfiles;
265		break;
266	case RLIMIT_NPROC:
267		maxlim = maxproc;
268		break;
269	default:
270		maxlim = RLIM_INFINITY;
271		break;
272	}
273
274	if (limp->rlim_max > maxlim)
275		limp->rlim_max = maxlim;
276	if (limp->rlim_cur > limp->rlim_max)
277		limp->rlim_cur = limp->rlim_max;
278
279	if (which == RLIMIT_STACK) {
280		/*
281		 * Stack is allocated to the max at exec time with only
282		 * "rlim_cur" bytes accessible.  If stack limit is going
283		 * up make more accessible, if going down make inaccessible.
284		 */
285		if (limp->rlim_cur != alimp->rlim_cur) {
286			vaddr_t addr;
287			vsize_t size;
288			vm_prot_t prot;
289
290			if (limp->rlim_cur > alimp->rlim_cur) {
291				prot = VM_PROT_ALL;
292				size = limp->rlim_cur - alimp->rlim_cur;
293#ifdef MACHINE_STACK_GROWS_UP
294				addr = USRSTACK + alimp->rlim_cur;
295#else
296				addr = USRSTACK - limp->rlim_cur;
297#endif
298			} else {
299				prot = VM_PROT_NONE;
300				size = alimp->rlim_cur - limp->rlim_cur;
301#ifdef MACHINE_STACK_GROWS_UP
302				addr = USRSTACK + limp->rlim_cur;
303#else
304				addr = USRSTACK - alimp->rlim_cur;
305#endif
306			}
307			addr = trunc_page(addr);
308			size = round_page(size);
309#if defined(UVM)
310			(void) uvm_map_protect(&p->p_vmspace->vm_map,
311					      addr, addr+size, prot, FALSE);
312#else
313			(void) vm_map_protect(&p->p_vmspace->vm_map,
314					      addr, addr+size, prot, FALSE);
315#endif
316		}
317	}
318
319	*alimp = *limp;
320	return (0);
321}
322
323/* ARGSUSED */
324int
325sys_getrlimit(p, v, retval)
326	struct proc *p;
327	void *v;
328	register_t *retval;
329{
330	register struct sys_getrlimit_args /* {
331		syscallarg(u_int) which;
332		syscallarg(struct rlimit *) rlp;
333	} */ *uap = v;
334
335	if (SCARG(uap, which) >= RLIM_NLIMITS)
336		return (EINVAL);
337	return (copyout((caddr_t)&p->p_rlimit[SCARG(uap, which)],
338	    (caddr_t)SCARG(uap, rlp), sizeof (struct rlimit)));
339}
340
341/*
342 * Transform the running time and tick information in proc p into user,
343 * system, and interrupt time usage.
344 */
345void
346calcru(p, up, sp, ip)
347	register struct proc *p;
348	register struct timeval *up;
349	register struct timeval *sp;
350	register struct timeval *ip;
351{
352	register u_quad_t u, st, ut, it, tot;
353	register long sec, usec;
354	register int s;
355	struct timeval tv;
356
357	s = splstatclock();
358	st = p->p_sticks;
359	ut = p->p_uticks;
360	it = p->p_iticks;
361	splx(s);
362
363	tot = st + ut + it;
364	if (tot == 0) {
365		up->tv_sec = up->tv_usec = 0;
366		sp->tv_sec = sp->tv_usec = 0;
367		if (ip != NULL)
368			ip->tv_sec = ip->tv_usec = 0;
369		return;
370	}
371
372	sec = p->p_rtime.tv_sec;
373	usec = p->p_rtime.tv_usec;
374	if (p == curproc) {
375		/*
376		 * Adjust for the current time slice.  This is actually fairly
377		 * important since the error here is on the order of a time
378		 * quantum, which is much greater than the sampling error.
379		 */
380		microtime(&tv);
381		sec += tv.tv_sec - runtime.tv_sec;
382		usec += tv.tv_usec - runtime.tv_usec;
383	}
384	u = (u_quad_t) sec * 1000000 + usec;
385	st = (u * st) / tot;
386	sp->tv_sec = st / 1000000;
387	sp->tv_usec = st % 1000000;
388	ut = (u * ut) / tot;
389	up->tv_sec = ut / 1000000;
390	up->tv_usec = ut % 1000000;
391	if (ip != NULL) {
392		it = (u * it) / tot;
393		ip->tv_sec = it / 1000000;
394		ip->tv_usec = it % 1000000;
395	}
396}
397
398/* ARGSUSED */
399int
400sys_getrusage(p, v, retval)
401	register struct proc *p;
402	void *v;
403	register_t *retval;
404{
405	register struct sys_getrusage_args /* {
406		syscallarg(int) who;
407		syscallarg(struct rusage *) rusage;
408	} */ *uap = v;
409	register struct rusage *rup;
410
411	switch (SCARG(uap, who)) {
412
413	case RUSAGE_SELF:
414		rup = &p->p_stats->p_ru;
415		calcru(p, &rup->ru_utime, &rup->ru_stime, NULL);
416		break;
417
418	case RUSAGE_CHILDREN:
419		rup = &p->p_stats->p_cru;
420		break;
421
422	default:
423		return (EINVAL);
424	}
425	return (copyout((caddr_t)rup, (caddr_t)SCARG(uap, rusage),
426	    sizeof (struct rusage)));
427}
428
429void
430ruadd(ru, ru2)
431	register struct rusage *ru, *ru2;
432{
433	register long *ip, *ip2;
434	register int i;
435
436	timeradd(&ru->ru_utime, &ru2->ru_utime, &ru->ru_utime);
437	timeradd(&ru->ru_stime, &ru2->ru_stime, &ru->ru_stime);
438	if (ru->ru_maxrss < ru2->ru_maxrss)
439		ru->ru_maxrss = ru2->ru_maxrss;
440	ip = &ru->ru_first; ip2 = &ru2->ru_first;
441	for (i = &ru->ru_last - &ru->ru_first; i >= 0; i--)
442		*ip++ += *ip2++;
443}
444
445/*
446 * Make a copy of the plimit structure.
447 * We share these structures copy-on-write after fork,
448 * and copy when a limit is changed.
449 */
450struct plimit *
451limcopy(lim)
452	struct plimit *lim;
453{
454	register struct plimit *newlim;
455
456	MALLOC(newlim, struct plimit *, sizeof(struct plimit),
457	    M_SUBPROC, M_WAITOK);
458	bcopy(lim->pl_rlimit, newlim->pl_rlimit,
459	    sizeof(struct rlimit) * RLIM_NLIMITS);
460	newlim->p_lflags = 0;
461	newlim->p_refcnt = 1;
462	return (newlim);
463}
464
465void
466limfree(lim)
467	struct plimit *lim;
468{
469
470	if (--lim->p_refcnt > 0)
471		return;
472	FREE(lim, M_SUBPROC);
473}
474