]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_resource.c
xnu-792.2.4.tar.gz
[apple/xnu.git] / bsd / kern / kern_resource.c
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /* Copyright (c) 1995, 1997 Apple Computer, Inc. All Rights Reserved */
23 /*-
24 * Copyright (c) 1982, 1986, 1991, 1993
25 * The Regents of the University of California. All rights reserved.
26 * (c) UNIX System Laboratories, Inc.
27 * All or some portions of this file are derived from material licensed
28 * to the University of California by American Telephone and Telegraph
29 * Co. or Unix System Laboratories, Inc. and are reproduced herein with
30 * the permission of UNIX System Laboratories, Inc.
31 *
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
34 * are met:
35 * 1. Redistributions of source code must retain the above copyright
36 * notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 * notice, this list of conditions and the following disclaimer in the
39 * documentation and/or other materials provided with the distribution.
40 * 3. All advertising materials mentioning features or use of this software
41 * must display the following acknowledgement:
42 * This product includes software developed by the University of
43 * California, Berkeley and its contributors.
44 * 4. Neither the name of the University nor the names of its contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 *
60 * @(#)kern_resource.c 8.5 (Berkeley) 1/21/94
61 */
62
63 #include <sys/param.h>
64 #include <sys/systm.h>
65 #include <sys/sysctl.h>
66 #include <sys/kernel.h>
67 #include <sys/file_internal.h>
68 #include <sys/resourcevar.h>
69 #include <sys/malloc.h>
70 #include <sys/proc_internal.h>
71 #include <sys/kauth.h>
72 #include <machine/spl.h>
73
74 #include <sys/mount_internal.h>
75 #include <sys/sysproto.h>
76
77 #include <bsm/audit_kernel.h>
78
79 #include <machine/vmparam.h>
80
81 #include <mach/mach_types.h>
82 #include <mach/time_value.h>
83 #include <mach/task_info.h>
84 #include <mach/vm_map.h>
85
86 #include <vm/vm_map.h>
87
88 int donice(struct proc *curp, struct proc *chgp, int n);
89 int dosetrlimit(struct proc *p, u_int which, struct rlimit *limp);
90
91 rlim_t maxdmap = MAXDSIZ; /* XXX */
92 rlim_t maxsmap = MAXSSIZ; /* XXX */
93
94 /*
95 * Limits on the number of open files per process, and the number
96 * of child processes per process.
97 *
98 * Note: would be in kern/subr_param.c in FreeBSD.
99 */
100 int maxprocperuid = CHILD_MAX; /* max # of procs per user */
101 int maxfilesperproc = OPEN_MAX; /* per-proc open files limit */
102
103 SYSCTL_INT( _kern, KERN_MAXPROCPERUID, maxprocperuid, CTLFLAG_RW,
104 &maxprocperuid, 0, "Maximum processes allowed per userid" );
105
106 SYSCTL_INT( _kern, KERN_MAXFILESPERPROC, maxfilesperproc, CTLFLAG_RW,
107 &maxfilesperproc, 0, "Maximum files allowed open per process" );
108
109
110 /*
111 * Resource controls and accounting.
112 */
113 int
114 getpriority(struct proc *curp, struct getpriority_args *uap, register_t *retval)
115 {
116 register struct proc *p;
117 register int low = PRIO_MAX + 1;
118
119 if (uap->who < 0)
120 return (EINVAL);
121
122 switch (uap->which) {
123
124 case PRIO_PROCESS:
125 if (uap->who == 0)
126 p = curp;
127 else
128 p = pfind(uap->who);
129 if (p == 0)
130 break;
131 low = p->p_nice;
132 break;
133
134 case PRIO_PGRP: {
135 register struct pgrp *pg;
136
137 if (uap->who == 0)
138 pg = curp->p_pgrp;
139 else if ((pg = pgfind(uap->who)) == NULL)
140 break;
141 for (p = pg->pg_members.lh_first; p != 0; p = p->p_pglist.le_next) {
142 if (p->p_nice < low)
143 low = p->p_nice;
144 }
145 break;
146 }
147
148 case PRIO_USER:
149 if (uap->who == 0)
150 uap->who = kauth_cred_getuid(kauth_cred_get());
151 for (p = allproc.lh_first; p != 0; p = p->p_list.le_next)
152 if (kauth_cred_getuid(p->p_ucred) == uap->who &&
153 p->p_nice < low)
154 low = p->p_nice;
155 break;
156
157 default:
158 return (EINVAL);
159 }
160 if (low == PRIO_MAX + 1)
161 return (ESRCH);
162 *retval = low;
163 return (0);
164 }
165
166 /* ARGSUSED */
167 int
168 setpriority(struct proc *curp, struct setpriority_args *uap, __unused register_t *retval)
169 {
170 register struct proc *p;
171 int found = 0, error = 0;
172
173 AUDIT_ARG(cmd, uap->which);
174 AUDIT_ARG(owner, uap->who, 0);
175 AUDIT_ARG(value, uap->prio);
176
177 if (uap->who < 0)
178 return (EINVAL);
179
180 switch (uap->which) {
181
182 case PRIO_PROCESS:
183 if (uap->who == 0)
184 p = curp;
185 else
186 p = pfind(uap->who);
187 if (p == 0)
188 break;
189 error = donice(curp, p, uap->prio);
190 found++;
191 break;
192
193 case PRIO_PGRP: {
194 register struct pgrp *pg;
195
196 if (uap->who == 0)
197 pg = curp->p_pgrp;
198 else if ((pg = pgfind(uap->who)) == NULL)
199 break;
200 for (p = pg->pg_members.lh_first; p != 0;
201 p = p->p_pglist.le_next) {
202 error = donice(curp, p, uap->prio);
203 found++;
204 }
205 break;
206 }
207
208 case PRIO_USER:
209 if (uap->who == 0)
210 uap->who = kauth_cred_getuid(kauth_cred_get());
211 for (p = allproc.lh_first; p != 0; p = p->p_list.le_next)
212 if (kauth_cred_getuid(p->p_ucred) == uap->who) {
213 error = donice(curp, p, uap->prio);
214 found++;
215 }
216 break;
217
218 default:
219 return (EINVAL);
220 }
221 if (found == 0)
222 return (ESRCH);
223 return (error);
224 }
225
226 int
227 donice(curp, chgp, n)
228 register struct proc *curp, *chgp;
229 register int n;
230 {
231 kauth_cred_t ucred = curp->p_ucred;
232
233 if (suser(ucred, NULL) && ucred->cr_ruid &&
234 kauth_cred_getuid(ucred) != kauth_cred_getuid(chgp->p_ucred) &&
235 ucred->cr_ruid != kauth_cred_getuid(chgp->p_ucred))
236 return (EPERM);
237 if (n > PRIO_MAX)
238 n = PRIO_MAX;
239 if (n < PRIO_MIN)
240 n = PRIO_MIN;
241 if (n < chgp->p_nice && suser(ucred, &curp->p_acflag))
242 return (EACCES);
243 chgp->p_nice = n;
244 (void)resetpriority(chgp);
245 return (0);
246 }
247
248
249 /* ARGSUSED */
250 int
251 setrlimit(struct proc *p, register struct setrlimit_args *uap, __unused register_t *retval)
252 {
253 struct rlimit alim;
254 int error;
255
256 if ((error = copyin(uap->rlp, (caddr_t)&alim,
257 sizeof (struct rlimit))))
258 return (error);
259 return (dosetrlimit(p, uap->which, &alim));
260 }
261
262 int
263 dosetrlimit(p, which, limp)
264 struct proc *p;
265 u_int which;
266 struct rlimit *limp;
267 {
268 register struct rlimit *alimp;
269 int error;
270
271 if (which >= RLIM_NLIMITS)
272 return (EINVAL);
273 alimp = &p->p_rlimit[which];
274 if (limp->rlim_cur > alimp->rlim_max ||
275 limp->rlim_max > alimp->rlim_max)
276 if ((error = suser(kauth_cred_get(), &p->p_acflag)))
277 return (error);
278 if (limp->rlim_cur > limp->rlim_max)
279 limp->rlim_cur = limp->rlim_max;
280 if (p->p_limit->p_refcnt > 1 &&
281 (p->p_limit->p_lflags & PL_SHAREMOD) == 0) {
282 p->p_limit->p_refcnt--;
283 p->p_limit = limcopy(p->p_limit);
284 alimp = &p->p_rlimit[which];
285 }
286
287 switch (which) {
288
289 case RLIMIT_DATA:
290 if (limp->rlim_cur > maxdmap)
291 limp->rlim_cur = maxdmap;
292 if (limp->rlim_max > maxdmap)
293 limp->rlim_max = maxdmap;
294 break;
295
296 case RLIMIT_STACK:
297 if (limp->rlim_cur > maxsmap)
298 limp->rlim_cur = maxsmap;
299 if (limp->rlim_max > maxsmap)
300 limp->rlim_max = maxsmap;
301 /*
302 * Stack is allocated to the max at exec time with only
303 * "rlim_cur" bytes accessible. If stack limit is going
304 * up make more accessible, if going down make inaccessible.
305 */
306 if (limp->rlim_cur != alimp->rlim_cur) {
307 user_addr_t addr;
308 user_size_t size;
309
310 if (limp->rlim_cur > alimp->rlim_cur) {
311 /* grow stack */
312 size = round_page_64(limp->rlim_cur);
313 size -= round_page_64(alimp->rlim_cur);
314
315 #if STACK_GROWTH_UP
316 /* go to top of current stack */
317 addr = p->user_stack + alimp->rlim_cur;
318 #else STACK_GROWTH_UP
319 addr = p->user_stack - alimp->rlim_cur;
320 addr -= size;
321 #endif /* STACK_GROWTH_UP */
322 if (mach_vm_allocate(current_map(),
323 &addr, size,
324 VM_FLAGS_FIXED) != KERN_SUCCESS)
325 return(EINVAL);
326 } else {
327 /* shrink stack */
328 }
329 }
330 break;
331
332 case RLIMIT_NOFILE:
333 /*
334 * Only root can set the maxfiles limits, as it is systemwide resource
335 */
336 if ( is_suser() ) {
337 if (limp->rlim_cur > maxfiles)
338 limp->rlim_cur = maxfiles;
339 if (limp->rlim_max > maxfiles)
340 limp->rlim_max = maxfiles;
341 }
342 else {
343 if (limp->rlim_cur > maxfilesperproc)
344 limp->rlim_cur = maxfilesperproc;
345 if (limp->rlim_max > maxfilesperproc)
346 limp->rlim_max = maxfilesperproc;
347 }
348 break;
349
350 case RLIMIT_NPROC:
351 /*
352 * Only root can set to the maxproc limits, as it is
353 * systemwide resource; all others are limited to
354 * maxprocperuid (presumably less than maxproc).
355 */
356 if ( is_suser() ) {
357 if (limp->rlim_cur > maxproc)
358 limp->rlim_cur = maxproc;
359 if (limp->rlim_max > maxproc)
360 limp->rlim_max = maxproc;
361 }
362 else {
363 if (limp->rlim_cur > maxprocperuid)
364 limp->rlim_cur = maxprocperuid;
365 if (limp->rlim_max > maxprocperuid)
366 limp->rlim_max = maxprocperuid;
367 }
368 break;
369
370 } /* switch... */
371 *alimp = *limp;
372 return (0);
373 }
374
375 /* ARGSUSED */
376 int
377 getrlimit(struct proc *p, register struct getrlimit_args *uap, __unused register_t *retval)
378 {
379 if (uap->which >= RLIM_NLIMITS)
380 return (EINVAL);
381 return (copyout((caddr_t)&p->p_rlimit[uap->which],
382 uap->rlp, sizeof (struct rlimit)));
383 }
384
385 /*
386 * Transform the running time and tick information in proc p into user,
387 * system, and interrupt time usage.
388 */
389 void
390 calcru(p, up, sp, ip)
391 register struct proc *p;
392 register struct timeval *up;
393 register struct timeval *sp;
394 register struct timeval *ip;
395 {
396 task_t task;
397
398 timerclear(up);
399 timerclear(sp);
400 if (ip != NULL)
401 timerclear(ip);
402
403 task = p->task;
404 if (task) {
405 task_basic_info_data_t tinfo;
406 task_thread_times_info_data_t ttimesinfo;
407 int task_info_stuff, task_ttimes_stuff;
408 struct timeval ut,st;
409
410 task_info_stuff = TASK_BASIC_INFO_COUNT;
411 task_info(task, TASK_BASIC_INFO,
412 &tinfo, &task_info_stuff);
413 ut.tv_sec = tinfo.user_time.seconds;
414 ut.tv_usec = tinfo.user_time.microseconds;
415 st.tv_sec = tinfo.system_time.seconds;
416 st.tv_usec = tinfo.system_time.microseconds;
417 timeradd(&ut, up, up);
418 timeradd(&st, sp, sp);
419
420 task_ttimes_stuff = TASK_THREAD_TIMES_INFO_COUNT;
421 task_info(task, TASK_THREAD_TIMES_INFO,
422 &ttimesinfo, &task_ttimes_stuff);
423
424 ut.tv_sec = ttimesinfo.user_time.seconds;
425 ut.tv_usec = ttimesinfo.user_time.microseconds;
426 st.tv_sec = ttimesinfo.system_time.seconds;
427 st.tv_usec = ttimesinfo.system_time.microseconds;
428 timeradd(&ut, up, up);
429 timeradd(&st, sp, sp);
430 }
431 }
432
433 __private_extern__ void munge_rusage(struct rusage *a_rusage_p, struct user_rusage *a_user_rusage_p);
434
435 /* ARGSUSED */
436 int
437 getrusage(register struct proc *p, register struct getrusage_args *uap, __unused register_t *retval)
438 {
439 struct rusage *rup, rubuf;
440 struct user_rusage rubuf64;
441 size_t retsize = sizeof(rubuf); /* default: 32 bits */
442 caddr_t retbuf = (caddr_t)&rubuf; /* default: 32 bits */
443
444 switch (uap->who) {
445 case RUSAGE_SELF:
446 rup = &p->p_stats->p_ru;
447 calcru(p, &rup->ru_utime, &rup->ru_stime, NULL);
448 // LP64todo: proc struct should have 64 bit version of struct
449 rubuf = *rup;
450 break;
451
452 case RUSAGE_CHILDREN:
453 rup = &p->p_stats->p_cru;
454 rubuf = *rup;
455 break;
456
457 default:
458 return (EINVAL);
459 }
460 if (IS_64BIT_PROCESS(p)) {
461 retsize = sizeof(rubuf64);
462 retbuf = (caddr_t)&rubuf64;
463 munge_rusage(&rubuf, &rubuf64);
464 }
465 return (copyout(retbuf, uap->rusage, retsize));
466 }
467
468 void
469 ruadd(ru, ru2)
470 register struct rusage *ru, *ru2;
471 {
472 register long *ip, *ip2;
473 register int i;
474
475 timeradd(&ru->ru_utime, &ru2->ru_utime, &ru->ru_utime);
476 timeradd(&ru->ru_stime, &ru2->ru_stime, &ru->ru_stime);
477 if (ru->ru_maxrss < ru2->ru_maxrss)
478 ru->ru_maxrss = ru2->ru_maxrss;
479 ip = &ru->ru_first; ip2 = &ru2->ru_first;
480 for (i = &ru->ru_last - &ru->ru_first; i >= 0; i--)
481 *ip++ += *ip2++;
482 }
483
484 /*
485 * Make a copy of the plimit structure.
486 * We share these structures copy-on-write after fork,
487 * and copy when a limit is changed.
488 */
489 struct plimit *
490 limcopy(lim)
491 struct plimit *lim;
492 {
493 register struct plimit *copy;
494
495 MALLOC_ZONE(copy, struct plimit *,
496 sizeof(struct plimit), M_SUBPROC, M_WAITOK);
497 if (copy == NULL)
498 panic("limcopy");
499 bcopy(lim->pl_rlimit, copy->pl_rlimit,
500 sizeof(struct rlimit) * RLIM_NLIMITS);
501 copy->p_lflags = 0;
502 copy->p_refcnt = 1;
503 return (copy);
504 }