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