Commit | Line | Data |
---|---|---|
f406ae69 | 1 | /*- |
c34daa85 KB |
2 | * Copyright (c) 1982, 1986, 1991, 1993 |
3 | * The Regents of the University of California. All rights reserved. | |
adb35f79 KB |
4 | * (c) UNIX System Laboratories, Inc. |
5 | * All or some portions of this file are derived from material licensed | |
6 | * to the University of California by American Telephone and Telegraph | |
7 | * Co. or Unix System Laboratories, Inc. and are reproduced herein with | |
8 | * the permission of UNIX System Laboratories, Inc. | |
f406ae69 KB |
9 | * |
10 | * %sccs.include.redist.c% | |
da7c5cc6 | 11 | * |
adb35f79 | 12 | * @(#)kern_resource.c 8.5 (Berkeley) %G% |
da7c5cc6 | 13 | */ |
1b64633a | 14 | |
38a01dbe KB |
15 | #include <sys/param.h> |
16 | #include <sys/kernel.h> | |
e0ed5a07 | 17 | #include <sys/file.h> |
38a01dbe KB |
18 | #include <sys/resourcevar.h> |
19 | #include <sys/malloc.h> | |
20 | #include <sys/proc.h> | |
db1874da | 21 | |
38a01dbe | 22 | #include <vm/vm.h> |
4be63405 | 23 | |
93cc02ac BJ |
24 | /* |
25 | * Resource controls and accounting. | |
26 | */ | |
27 | ||
d5820a3c CT |
28 | struct getpriority_args { |
29 | int which; | |
30 | int who; | |
31 | }; | |
8eea23a6 KM |
32 | getpriority(curp, uap, retval) |
33 | struct proc *curp; | |
d5820a3c | 34 | register struct getpriority_args *uap; |
8eea23a6 KM |
35 | int *retval; |
36 | { | |
db1874da | 37 | register struct proc *p; |
8fe87cbb | 38 | register int low = PRIO_MAX + 1; |
db1874da | 39 | |
db1874da BJ |
40 | switch (uap->which) { |
41 | ||
42 | case PRIO_PROCESS: | |
43 | if (uap->who == 0) | |
8eea23a6 | 44 | p = curp; |
db1874da BJ |
45 | else |
46 | p = pfind(uap->who); | |
47 | if (p == 0) | |
957edfef | 48 | break; |
3fa5efae | 49 | low = p->p_nice; |
db1874da BJ |
50 | break; |
51 | ||
8fe87cbb MT |
52 | case PRIO_PGRP: { |
53 | register struct pgrp *pg; | |
54 | ||
db1874da | 55 | if (uap->who == 0) |
8eea23a6 | 56 | pg = curp->p_pgrp; |
8fe87cbb MT |
57 | else if ((pg = pgfind(uap->who)) == NULL) |
58 | break; | |
59 | for (p = pg->pg_mem; p != NULL; p = p->p_pgrpnxt) { | |
60 | if (p->p_nice < low) | |
3fa5efae | 61 | low = p->p_nice; |
93cc02ac BJ |
62 | } |
63 | break; | |
8fe87cbb | 64 | } |
93cc02ac BJ |
65 | |
66 | case PRIO_USER: | |
67 | if (uap->who == 0) | |
0cee12d3 | 68 | uap->who = curp->p_ucred->cr_uid; |
cf5ef508 | 69 | for (p = (struct proc *)allproc; p != NULL; p = p->p_next) { |
0cee12d3 | 70 | if (p->p_ucred->cr_uid == uap->who && |
3fa5efae MK |
71 | p->p_nice < low) |
72 | low = p->p_nice; | |
93cc02ac | 73 | } |
db1874da BJ |
74 | break; |
75 | ||
76 | default: | |
d9c2f47f | 77 | return (EINVAL); |
db1874da | 78 | } |
8eea23a6 | 79 | if (low == PRIO_MAX + 1) |
d9c2f47f | 80 | return (ESRCH); |
8eea23a6 | 81 | *retval = low; |
d9c2f47f | 82 | return (0); |
db1874da BJ |
83 | } |
84 | ||
d5820a3c CT |
85 | struct setpriority_args { |
86 | int which; | |
87 | int who; | |
88 | int prio; | |
89 | }; | |
8eea23a6 KM |
90 | /* ARGSUSED */ |
91 | setpriority(curp, uap, retval) | |
92 | struct proc *curp; | |
d5820a3c | 93 | register struct setpriority_args *uap; |
8eea23a6 KM |
94 | int *retval; |
95 | { | |
db1874da | 96 | register struct proc *p; |
8eea23a6 | 97 | int found = 0, error = 0; |
db1874da | 98 | |
db1874da BJ |
99 | switch (uap->which) { |
100 | ||
101 | case PRIO_PROCESS: | |
93cc02ac | 102 | if (uap->who == 0) |
8eea23a6 | 103 | p = curp; |
93cc02ac BJ |
104 | else |
105 | p = pfind(uap->who); | |
db1874da | 106 | if (p == 0) |
957edfef | 107 | break; |
8eea23a6 | 108 | error = donice(curp, p, uap->prio); |
3fa5efae | 109 | found++; |
db1874da BJ |
110 | break; |
111 | ||
8fe87cbb MT |
112 | case PRIO_PGRP: { |
113 | register struct pgrp *pg; | |
114 | ||
93cc02ac | 115 | if (uap->who == 0) |
8eea23a6 | 116 | pg = curp->p_pgrp; |
8fe87cbb MT |
117 | else if ((pg = pgfind(uap->who)) == NULL) |
118 | break; | |
119 | for (p = pg->pg_mem; p != NULL; p = p->p_pgrpnxt) { | |
8eea23a6 | 120 | error = donice(curp, p, uap->prio); |
8fe87cbb MT |
121 | found++; |
122 | } | |
db1874da | 123 | break; |
8fe87cbb | 124 | } |
db1874da | 125 | |
93cc02ac BJ |
126 | case PRIO_USER: |
127 | if (uap->who == 0) | |
0cee12d3 | 128 | uap->who = curp->p_ucred->cr_uid; |
cf5ef508 | 129 | for (p = (struct proc *)allproc; p != NULL; p = p->p_next) |
0cee12d3 | 130 | if (p->p_ucred->cr_uid == uap->who) { |
8eea23a6 | 131 | error = donice(curp, p, uap->prio); |
3fa5efae MK |
132 | found++; |
133 | } | |
93cc02ac BJ |
134 | break; |
135 | ||
db1874da | 136 | default: |
d9c2f47f | 137 | return (EINVAL); |
db1874da | 138 | } |
3fa5efae | 139 | if (found == 0) |
d9c2f47f | 140 | return (ESRCH); |
1cd6a11c | 141 | return (error); |
db1874da BJ |
142 | } |
143 | ||
8eea23a6 KM |
144 | donice(curp, chgp, n) |
145 | register struct proc *curp, *chgp; | |
db1874da BJ |
146 | register int n; |
147 | { | |
0cee12d3 | 148 | register struct pcred *pcred = curp->p_cred; |
db1874da | 149 | |
0cee12d3 MK |
150 | if (pcred->pc_ucred->cr_uid && pcred->p_ruid && |
151 | pcred->pc_ucred->cr_uid != chgp->p_ucred->cr_uid && | |
152 | pcred->p_ruid != chgp->p_ucred->cr_uid) | |
8eea23a6 | 153 | return (EPERM); |
3fa5efae MK |
154 | if (n > PRIO_MAX) |
155 | n = PRIO_MAX; | |
156 | if (n < PRIO_MIN) | |
157 | n = PRIO_MIN; | |
0cee12d3 | 158 | if (n < chgp->p_nice && suser(pcred->pc_ucred, &curp->p_acflag)) |
8eea23a6 KM |
159 | return (EACCES); |
160 | chgp->p_nice = n; | |
802236f4 | 161 | (void)resetpriority(chgp); |
8eea23a6 | 162 | return (0); |
db1874da BJ |
163 | } |
164 | ||
0c1cfb60 | 165 | #if defined(COMPAT_43) || defined(COMPAT_SUNOS) |
d5820a3c CT |
166 | struct setrlimit_args { |
167 | u_int which; | |
168 | struct orlimit *lim; | |
169 | }; | |
8eea23a6 | 170 | /* ARGSUSED */ |
51a9a1cf | 171 | osetrlimit(p, uap, retval) |
8eea23a6 | 172 | struct proc *p; |
d5820a3c | 173 | register struct setrlimit_args *uap; |
8eea23a6 KM |
174 | int *retval; |
175 | { | |
eda8fb5d KM |
176 | struct orlimit olim; |
177 | struct rlimit lim; | |
8eea23a6 | 178 | int error; |
db1874da | 179 | |
eda8fb5d KM |
180 | if (error = |
181 | copyin((caddr_t)uap->lim, (caddr_t)&olim, sizeof (struct orlimit))) | |
182 | return (error); | |
183 | lim.rlim_cur = olim.rlim_cur; | |
184 | lim.rlim_max = olim.rlim_max; | |
185 | return (dosetrlimit(p, uap->which, &lim)); | |
186 | } | |
187 | ||
d5820a3c CT |
188 | struct getrlimit_args { |
189 | u_int which; | |
190 | struct orlimit *rlp; | |
191 | }; | |
eda8fb5d | 192 | /* ARGSUSED */ |
51a9a1cf | 193 | ogetrlimit(p, uap, retval) |
eda8fb5d | 194 | struct proc *p; |
d5820a3c | 195 | register struct getrlimit_args *uap; |
eda8fb5d KM |
196 | int *retval; |
197 | { | |
198 | struct orlimit olim; | |
199 | ||
8eea23a6 | 200 | if (uap->which >= RLIM_NLIMITS) |
d9c2f47f | 201 | return (EINVAL); |
eda8fb5d KM |
202 | olim.rlim_cur = p->p_rlimit[uap->which].rlim_cur; |
203 | if (olim.rlim_cur == -1) | |
204 | olim.rlim_cur = 0x7fffffff; | |
205 | olim.rlim_max = p->p_rlimit[uap->which].rlim_max; | |
206 | if (olim.rlim_max == -1) | |
207 | olim.rlim_max = 0x7fffffff; | |
208 | return (copyout((caddr_t)&olim, (caddr_t)uap->rlp, sizeof(olim))); | |
209 | } | |
0c1cfb60 | 210 | #endif /* COMPAT_43 || COMPAT_SUNOS */ |
eda8fb5d | 211 | |
d5820a3c CT |
212 | struct __setrlimit_args { |
213 | u_int which; | |
214 | struct rlimit *lim; | |
215 | }; | |
eda8fb5d | 216 | /* ARGSUSED */ |
4e6fba13 | 217 | setrlimit(p, uap, retval) |
eda8fb5d | 218 | struct proc *p; |
d5820a3c | 219 | register struct __setrlimit_args *uap; |
eda8fb5d KM |
220 | int *retval; |
221 | { | |
222 | struct rlimit alim; | |
223 | int error; | |
224 | ||
8eea23a6 KM |
225 | if (error = |
226 | copyin((caddr_t)uap->lim, (caddr_t)&alim, sizeof (struct rlimit))) | |
d9c2f47f | 227 | return (error); |
eda8fb5d KM |
228 | return (dosetrlimit(p, uap->which, &alim)); |
229 | } | |
230 | ||
231 | dosetrlimit(p, which, limp) | |
232 | struct proc *p; | |
233 | u_int which; | |
234 | struct rlimit *limp; | |
235 | { | |
236 | register struct rlimit *alimp; | |
237 | extern unsigned maxdmap; | |
238 | int error; | |
239 | ||
240 | if (which >= RLIM_NLIMITS) | |
241 | return (EINVAL); | |
242 | alimp = &p->p_rlimit[which]; | |
243 | if (limp->rlim_cur > alimp->rlim_max || | |
244 | limp->rlim_max > alimp->rlim_max) | |
0cee12d3 | 245 | if (error = suser(p->p_ucred, &p->p_acflag)) |
d9c2f47f | 246 | return (error); |
eda8fb5d KM |
247 | if (limp->rlim_cur > limp->rlim_max) |
248 | limp->rlim_cur = limp->rlim_max; | |
0cee12d3 MK |
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); | |
eda8fb5d | 253 | alimp = &p->p_rlimit[which]; |
0cee12d3 MK |
254 | } |
255 | ||
eda8fb5d | 256 | switch (which) { |
db1874da BJ |
257 | |
258 | case RLIMIT_DATA: | |
eda8fb5d KM |
259 | if (limp->rlim_cur > maxdmap) |
260 | limp->rlim_cur = maxdmap; | |
261 | if (limp->rlim_max > maxdmap) | |
262 | limp->rlim_max = maxdmap; | |
db1874da BJ |
263 | break; |
264 | ||
265 | case RLIMIT_STACK: | |
eda8fb5d KM |
266 | if (limp->rlim_cur > maxdmap) |
267 | limp->rlim_cur = maxdmap; | |
268 | if (limp->rlim_max > maxdmap) | |
269 | limp->rlim_max = maxdmap; | |
4be63405 MH |
270 | /* |
271 | * Stack is allocated to the max at exec time with only | |
272 | * "rlim_cur" bytes accessible. If stack limit is going | |
273 | * up make more accessible, if going down make inaccessible. | |
274 | */ | |
eda8fb5d | 275 | if (limp->rlim_cur != alimp->rlim_cur) { |
4be63405 MH |
276 | vm_offset_t addr; |
277 | vm_size_t size; | |
278 | vm_prot_t prot; | |
279 | ||
eda8fb5d | 280 | if (limp->rlim_cur > alimp->rlim_cur) { |
4be63405 | 281 | prot = VM_PROT_ALL; |
eda8fb5d KM |
282 | size = limp->rlim_cur - alimp->rlim_cur; |
283 | addr = USRSTACK - limp->rlim_cur; | |
4be63405 MH |
284 | } else { |
285 | prot = VM_PROT_NONE; | |
eda8fb5d | 286 | size = alimp->rlim_cur - limp->rlim_cur; |
4be63405 MH |
287 | addr = USRSTACK - alimp->rlim_cur; |
288 | } | |
289 | addr = trunc_page(addr); | |
290 | size = round_page(size); | |
291 | (void) vm_map_protect(&p->p_vmspace->vm_map, | |
292 | addr, addr+size, prot, FALSE); | |
293 | } | |
db1874da | 294 | break; |
e0ed5a07 KM |
295 | |
296 | case RLIMIT_NOFILE: | |
297 | if (limp->rlim_cur > maxfiles) | |
298 | limp->rlim_cur = maxfiles; | |
299 | if (limp->rlim_max > maxfiles) | |
300 | limp->rlim_max = maxfiles; | |
301 | break; | |
349af62b KM |
302 | |
303 | case RLIMIT_NPROC: | |
304 | if (limp->rlim_cur > maxproc) | |
305 | limp->rlim_cur = maxproc; | |
306 | if (limp->rlim_max > maxproc) | |
307 | limp->rlim_max = maxproc; | |
308 | break; | |
db1874da | 309 | } |
eda8fb5d | 310 | *alimp = *limp; |
d9c2f47f | 311 | return (0); |
db1874da BJ |
312 | } |
313 | ||
d5820a3c CT |
314 | struct __getrlimit_args { |
315 | u_int which; | |
316 | struct rlimit *rlp; | |
317 | }; | |
8eea23a6 | 318 | /* ARGSUSED */ |
4e6fba13 | 319 | getrlimit(p, uap, retval) |
8eea23a6 | 320 | struct proc *p; |
d5820a3c | 321 | register struct __getrlimit_args *uap; |
8eea23a6 KM |
322 | int *retval; |
323 | { | |
db1874da | 324 | |
8eea23a6 | 325 | if (uap->which >= RLIM_NLIMITS) |
d9c2f47f | 326 | return (EINVAL); |
0cee12d3 | 327 | return (copyout((caddr_t)&p->p_rlimit[uap->which], (caddr_t)uap->rlp, |
8eea23a6 | 328 | sizeof (struct rlimit))); |
db1874da BJ |
329 | } |
330 | ||
421f4b3a CT |
331 | /* |
332 | * Transform the running time and tick information in proc p into user, | |
333 | * system, and interrupt time usage. | |
334 | */ | |
335 | calcru(p, up, sp, ip) | |
336 | register struct proc *p; | |
337 | register struct timeval *up; | |
338 | register struct timeval *sp; | |
339 | register struct timeval *ip; | |
340 | { | |
ee8285b5 CT |
341 | register u_quad_t u, st, ut, it, tot; |
342 | register u_long sec, usec; | |
343 | register int s; | |
344 | struct timeval tv; | |
421f4b3a | 345 | |
421f4b3a CT |
346 | s = splstatclock(); |
347 | st = p->p_sticks; | |
348 | ut = p->p_uticks; | |
349 | it = p->p_iticks; | |
421f4b3a CT |
350 | splx(s); |
351 | ||
352 | tot = st + ut + it; | |
353 | if (tot == 0) { | |
354 | up->tv_sec = up->tv_usec = 0; | |
355 | sp->tv_sec = sp->tv_usec = 0; | |
356 | if (ip != NULL) | |
357 | ip->tv_sec = ip->tv_usec = 0; | |
358 | return; | |
359 | } | |
ee8285b5 CT |
360 | |
361 | sec = p->p_rtime.tv_sec; | |
362 | usec = p->p_rtime.tv_usec; | |
363 | if (p == curproc) { | |
364 | /* | |
365 | * Adjust for the current time slice. This is actually fairly | |
366 | * important since the error here is on the order of a time | |
367 | * quantum, which is much greater than the sampling error. | |
368 | */ | |
369 | microtime(&tv); | |
370 | sec += tv.tv_sec - runtime.tv_sec; | |
371 | usec += tv.tv_usec - runtime.tv_usec; | |
372 | } | |
373 | u = sec * 1000000 + usec; | |
374 | st = (u * st) / tot; | |
421f4b3a CT |
375 | sp->tv_sec = st / 1000000; |
376 | sp->tv_usec = st % 1000000; | |
ee8285b5 | 377 | ut = (u * ut) / tot; |
421f4b3a CT |
378 | up->tv_sec = ut / 1000000; |
379 | up->tv_usec = ut % 1000000; | |
380 | if (ip != NULL) { | |
ee8285b5 | 381 | it = (u * it) / tot; |
421f4b3a CT |
382 | ip->tv_sec = it / 1000000; |
383 | ip->tv_usec = it % 1000000; | |
384 | } | |
385 | } | |
386 | ||
d5820a3c CT |
387 | struct getrusage_args { |
388 | int who; | |
389 | struct rusage *rusage; | |
390 | }; | |
8eea23a6 KM |
391 | /* ARGSUSED */ |
392 | getrusage(p, uap, retval) | |
393 | register struct proc *p; | |
d5820a3c | 394 | register struct getrusage_args *uap; |
8eea23a6 KM |
395 | int *retval; |
396 | { | |
db1874da BJ |
397 | register struct rusage *rup; |
398 | ||
399 | switch (uap->who) { | |
400 | ||
ee8285b5 | 401 | case RUSAGE_SELF: |
0cee12d3 | 402 | rup = &p->p_stats->p_ru; |
ee8285b5 | 403 | calcru(p, &rup->ru_utime, &rup->ru_stime, NULL); |
db1874da BJ |
404 | break; |
405 | ||
406 | case RUSAGE_CHILDREN: | |
0cee12d3 | 407 | rup = &p->p_stats->p_cru; |
db1874da BJ |
408 | break; |
409 | ||
410 | default: | |
d9c2f47f | 411 | return (EINVAL); |
db1874da | 412 | } |
d9c2f47f | 413 | return (copyout((caddr_t)rup, (caddr_t)uap->rusage, |
8eea23a6 | 414 | sizeof (struct rusage))); |
db1874da BJ |
415 | } |
416 | ||
417 | ruadd(ru, ru2) | |
418 | register struct rusage *ru, *ru2; | |
419 | { | |
3fd23f5c | 420 | register long *ip, *ip2; |
db1874da BJ |
421 | register int i; |
422 | ||
423 | timevaladd(&ru->ru_utime, &ru2->ru_utime); | |
424 | timevaladd(&ru->ru_stime, &ru2->ru_stime); | |
425 | if (ru->ru_maxrss < ru2->ru_maxrss) | |
426 | ru->ru_maxrss = ru2->ru_maxrss; | |
427 | ip = &ru->ru_first; ip2 = &ru2->ru_first; | |
3cc4f177 | 428 | for (i = &ru->ru_last - &ru->ru_first; i >= 0; i--) |
db1874da BJ |
429 | *ip++ += *ip2++; |
430 | } | |
0cee12d3 MK |
431 | |
432 | /* | |
433 | * Make a copy of the plimit structure. | |
434 | * We share these structures copy-on-write after fork, | |
435 | * and copy when a limit is changed. | |
436 | */ | |
437 | struct plimit * | |
438 | limcopy(lim) | |
439 | struct plimit *lim; | |
440 | { | |
441 | register struct plimit *copy; | |
442 | ||
443 | MALLOC(copy, struct plimit *, sizeof(struct plimit), | |
444 | M_SUBPROC, M_WAITOK); | |
445 | bcopy(lim->pl_rlimit, copy->pl_rlimit, | |
446 | sizeof(struct rlimit) * RLIM_NLIMITS); | |
447 | copy->p_lflags = 0; | |
448 | copy->p_refcnt = 1; | |
449 | return (copy); | |
450 | } |