1 /* $OpenBSD: kern_resource.c,v 1.37 2011/03/07 07:07:13 guenther 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. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 * 37 * @(#)kern_resource.c 8.5 (Berkeley) 1/21/94 38 */ 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/kernel.h> 43 #include <sys/file.h> 44 #include <sys/resourcevar.h> 45 #include <sys/pool.h> 46 #include <sys/proc.h> 47 #include <sys/sched.h> 48 49 #include <sys/mount.h> 50 #include <sys/syscallargs.h> 51 52 #include <uvm/uvm_extern.h> 53 54 /* 55 * Patchable maximum data and stack limits. 56 */ 57 rlim_t maxdmap = MAXDSIZ; 58 rlim_t maxsmap = MAXSSIZ; 59 60 /* 61 * Resource controls and accounting. 62 */ 63 64 int 65 sys_getpriority(struct proc *curp, void *v, register_t *retval) 66 { 67 struct sys_getpriority_args /* { 68 syscallarg(int) which; 69 syscallarg(id_t) who; 70 } */ *uap = v; 71 struct process *pr; 72 struct proc *p; 73 int low = NZERO + PRIO_MAX + 1; 74 75 switch (SCARG(uap, which)) { 76 77 case PRIO_PROCESS: 78 if (SCARG(uap, who) == 0) 79 pr = curp->p_p; 80 else 81 pr = prfind(SCARG(uap, who)); 82 if (pr == NULL) 83 break; 84 if (pr->ps_nice < low) 85 low = pr->ps_nice; 86 break; 87 88 case PRIO_PGRP: { 89 struct pgrp *pg; 90 91 if (SCARG(uap, who) == 0) 92 pg = curp->p_p->ps_pgrp; 93 else if ((pg = pgfind(SCARG(uap, who))) == NULL) 94 break; 95 LIST_FOREACH(pr, &pg->pg_members, ps_pglist) 96 if (pr->ps_nice < low) 97 low = pr->ps_nice; 98 break; 99 } 100 101 case PRIO_USER: 102 if (SCARG(uap, who) == 0) 103 SCARG(uap, who) = curp->p_ucred->cr_uid; 104 LIST_FOREACH(p, &allproc, p_list) 105 if ((p->p_flag & P_THREAD) == 0 && 106 p->p_ucred->cr_uid == SCARG(uap, who) && 107 p->p_p->ps_nice < low) 108 low = p->p_p->ps_nice; 109 break; 110 111 default: 112 return (EINVAL); 113 } 114 if (low == NZERO + PRIO_MAX + 1) 115 return (ESRCH); 116 *retval = low - NZERO; 117 return (0); 118 } 119 120 /* ARGSUSED */ 121 int 122 sys_setpriority(struct proc *curp, void *v, register_t *retval) 123 { 124 struct sys_setpriority_args /* { 125 syscallarg(int) which; 126 syscallarg(id_t) who; 127 syscallarg(int) prio; 128 } */ *uap = v; 129 struct process *pr; 130 int found = 0, error = 0; 131 132 switch (SCARG(uap, which)) { 133 134 case PRIO_PROCESS: 135 if (SCARG(uap, who) == 0) 136 pr = curp->p_p; 137 else 138 pr = prfind(SCARG(uap, who)); 139 if (pr == NULL) 140 break; 141 error = donice(curp, pr, SCARG(uap, prio)); 142 found++; 143 break; 144 145 case PRIO_PGRP: { 146 struct pgrp *pg; 147 148 if (SCARG(uap, who) == 0) 149 pg = curp->p_p->ps_pgrp; 150 else if ((pg = pgfind(SCARG(uap, who))) == NULL) 151 break; 152 LIST_FOREACH(pr, &pg->pg_members, ps_pglist) { 153 error = donice(curp, pr, SCARG(uap, prio)); 154 found++; 155 } 156 break; 157 } 158 159 case PRIO_USER: { 160 struct proc *p; 161 if (SCARG(uap, who) == 0) 162 SCARG(uap, who) = curp->p_ucred->cr_uid; 163 LIST_FOREACH(p, &allproc, p_list) 164 if ((p->p_flag & P_THREAD) == 0 && 165 p->p_ucred->cr_uid == SCARG(uap, who)) { 166 error = donice(curp, p->p_p, SCARG(uap, prio)); 167 found++; 168 } 169 break; 170 } 171 172 default: 173 return (EINVAL); 174 } 175 if (found == 0) 176 return (ESRCH); 177 return (error); 178 } 179 180 int 181 donice(struct proc *curp, struct process *chgpr, int n) 182 { 183 struct pcred *pcred = curp->p_cred; 184 struct proc *p; 185 int s; 186 187 if (pcred->pc_ucred->cr_uid && pcred->p_ruid && 188 pcred->pc_ucred->cr_uid != chgpr->ps_cred->pc_ucred->cr_uid && 189 pcred->p_ruid != chgpr->ps_cred->pc_ucred->cr_uid) 190 return (EPERM); 191 if (n > PRIO_MAX) 192 n = PRIO_MAX; 193 if (n < PRIO_MIN) 194 n = PRIO_MIN; 195 n += NZERO; 196 if (n < chgpr->ps_nice && suser(curp, 0)) 197 return (EACCES); 198 chgpr->ps_nice = n; 199 SCHED_LOCK(s); 200 TAILQ_FOREACH(p, &chgpr->ps_threads, p_thr_link) 201 (void)resetpriority(p); 202 SCHED_UNLOCK(s); 203 return (0); 204 } 205 206 /* ARGSUSED */ 207 int 208 sys_setrlimit(struct proc *p, void *v, register_t *retval) 209 { 210 struct sys_setrlimit_args /* { 211 syscallarg(int) which; 212 syscallarg(const struct rlimit *) rlp; 213 } */ *uap = v; 214 struct rlimit alim; 215 int error; 216 217 error = copyin((caddr_t)SCARG(uap, rlp), (caddr_t)&alim, 218 sizeof (struct rlimit)); 219 if (error) 220 return (error); 221 return (dosetrlimit(p, SCARG(uap, which), &alim)); 222 } 223 224 int 225 dosetrlimit(struct proc *p, u_int which, struct rlimit *limp) 226 { 227 struct rlimit *alimp; 228 rlim_t maxlim; 229 int error; 230 231 if (which >= RLIM_NLIMITS) 232 return (EINVAL); 233 234 alimp = &p->p_rlimit[which]; 235 if (limp->rlim_cur > alimp->rlim_max || 236 limp->rlim_max > alimp->rlim_max) 237 if ((error = suser(p, 0)) != 0) 238 return (error); 239 if (p->p_p->ps_limit->p_refcnt > 1) { 240 struct plimit *l = p->p_p->ps_limit; 241 242 /* limcopy() can sleep, so copy before decrementing refcnt */ 243 p->p_p->ps_limit = limcopy(l); 244 l->p_refcnt--; 245 alimp = &p->p_rlimit[which]; 246 } 247 248 switch (which) { 249 case RLIMIT_DATA: 250 maxlim = maxdmap; 251 break; 252 case RLIMIT_STACK: 253 maxlim = maxsmap; 254 break; 255 case RLIMIT_NOFILE: 256 maxlim = maxfiles; 257 break; 258 case RLIMIT_NPROC: 259 maxlim = maxproc; 260 break; 261 default: 262 maxlim = RLIM_INFINITY; 263 break; 264 } 265 266 if (limp->rlim_max > maxlim) 267 limp->rlim_max = maxlim; 268 if (limp->rlim_cur > limp->rlim_max) 269 limp->rlim_cur = limp->rlim_max; 270 271 if (which == RLIMIT_STACK) { 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_READ|VM_PROT_WRITE; 284 size = limp->rlim_cur - alimp->rlim_cur; 285 #ifdef MACHINE_STACK_GROWS_UP 286 addr = USRSTACK + alimp->rlim_cur; 287 #else 288 addr = USRSTACK - limp->rlim_cur; 289 #endif 290 } else { 291 prot = VM_PROT_NONE; 292 size = alimp->rlim_cur - limp->rlim_cur; 293 #ifdef MACHINE_STACK_GROWS_UP 294 addr = USRSTACK + limp->rlim_cur; 295 #else 296 addr = USRSTACK - alimp->rlim_cur; 297 #endif 298 } 299 addr = trunc_page(addr); 300 size = round_page(size); 301 (void) uvm_map_protect(&p->p_vmspace->vm_map, 302 addr, addr+size, prot, FALSE); 303 } 304 } 305 306 *alimp = *limp; 307 return (0); 308 } 309 310 /* ARGSUSED */ 311 int 312 sys_getrlimit(struct proc *p, void *v, register_t *retval) 313 { 314 struct sys_getrlimit_args /* { 315 syscallarg(int) which; 316 syscallarg(struct rlimit *) rlp; 317 } */ *uap = v; 318 319 if (SCARG(uap, which) < 0 || SCARG(uap, which) >= RLIM_NLIMITS) 320 return (EINVAL); 321 return (copyout((caddr_t)&p->p_rlimit[SCARG(uap, which)], 322 (caddr_t)SCARG(uap, rlp), sizeof (struct rlimit))); 323 } 324 325 /* 326 * Transform the running time and tick information in proc p into user, 327 * system, and interrupt time usage. 328 */ 329 void 330 calcru(struct proc *p, struct timeval *up, struct timeval *sp, 331 struct timeval *ip) 332 { 333 u_quad_t st, ut, it; 334 int freq; 335 int s; 336 337 s = splstatclock(); 338 st = p->p_sticks; 339 ut = p->p_uticks; 340 it = p->p_iticks; 341 splx(s); 342 343 if (st + ut + it == 0) { 344 timerclear(up); 345 timerclear(sp); 346 if (ip != NULL) 347 timerclear(ip); 348 return; 349 } 350 351 freq = stathz ? stathz : hz; 352 353 st = st * 1000000 / freq; 354 sp->tv_sec = st / 1000000; 355 sp->tv_usec = st % 1000000; 356 ut = ut * 1000000 / freq; 357 up->tv_sec = ut / 1000000; 358 up->tv_usec = ut % 1000000; 359 if (ip != NULL) { 360 it = it * 1000000 / freq; 361 ip->tv_sec = it / 1000000; 362 ip->tv_usec = it % 1000000; 363 } 364 } 365 366 /* ARGSUSED */ 367 int 368 sys_getrusage(struct proc *p, void *v, register_t *retval) 369 { 370 struct sys_getrusage_args /* { 371 syscallarg(int) who; 372 syscallarg(struct rusage *) rusage; 373 } */ *uap = v; 374 struct process *pr = p->p_p; 375 struct rusage ru; 376 struct rusage *rup; 377 378 switch (SCARG(uap, who)) { 379 380 case RUSAGE_SELF: 381 calcru(p, &p->p_stats->p_ru.ru_utime, 382 &p->p_stats->p_ru.ru_stime, NULL); 383 ru = p->p_stats->p_ru; 384 rup = &ru; 385 386 /* XXX add on already dead threads */ 387 388 /* add on other living threads */ 389 { 390 struct proc *q; 391 392 TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) { 393 if (q == p || P_ZOMBIE(q)) 394 continue; 395 /* 396 * XXX this is approximate: no call 397 * to calcru in other running threads 398 */ 399 ruadd(rup, &q->p_stats->p_ru); 400 } 401 } 402 break; 403 404 case RUSAGE_THREAD: 405 rup = &p->p_stats->p_ru; 406 calcru(p, &rup->ru_utime, &rup->ru_stime, NULL); 407 ru = *rup; 408 break; 409 410 case RUSAGE_CHILDREN: 411 rup = &p->p_stats->p_cru; 412 break; 413 414 default: 415 return (EINVAL); 416 } 417 return (copyout((caddr_t)rup, (caddr_t)SCARG(uap, rusage), 418 sizeof (struct rusage))); 419 } 420 421 void 422 ruadd(struct rusage *ru, struct rusage *ru2) 423 { 424 long *ip, *ip2; 425 int i; 426 427 timeradd(&ru->ru_utime, &ru2->ru_utime, &ru->ru_utime); 428 timeradd(&ru->ru_stime, &ru2->ru_stime, &ru->ru_stime); 429 if (ru->ru_maxrss < ru2->ru_maxrss) 430 ru->ru_maxrss = ru2->ru_maxrss; 431 ip = &ru->ru_first; ip2 = &ru2->ru_first; 432 for (i = &ru->ru_last - &ru->ru_first; i >= 0; i--) 433 *ip++ += *ip2++; 434 } 435 436 struct pool plimit_pool; 437 438 /* 439 * Make a copy of the plimit structure. 440 * We share these structures copy-on-write after fork, 441 * and copy when a limit is changed. 442 */ 443 struct plimit * 444 limcopy(struct plimit *lim) 445 { 446 struct plimit *newlim; 447 static int initialized; 448 449 if (!initialized) { 450 pool_init(&plimit_pool, sizeof(struct plimit), 0, 0, 0, 451 "plimitpl", &pool_allocator_nointr); 452 initialized = 1; 453 } 454 455 newlim = pool_get(&plimit_pool, PR_WAITOK); 456 bcopy(lim->pl_rlimit, newlim->pl_rlimit, 457 sizeof(struct rlimit) * RLIM_NLIMITS); 458 newlim->p_refcnt = 1; 459 return (newlim); 460 } 461 462 void 463 limfree(struct plimit *lim) 464 { 465 if (--lim->p_refcnt > 0) 466 return; 467 pool_put(&plimit_pool, lim); 468 } 469