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