1 /* $OpenBSD: kern_resource.c,v 1.36 2010/07/26 01:56:27 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 TAILQ_FOREACH(p, &pr->ps_threads, p_thr_link) { 85 if (p->p_nice < low) 86 low = p->p_nice; 87 } 88 break; 89 90 case PRIO_PGRP: { 91 struct pgrp *pg; 92 93 if (SCARG(uap, who) == 0) 94 pg = curp->p_p->ps_pgrp; 95 else if ((pg = pgfind(SCARG(uap, who))) == NULL) 96 break; 97 LIST_FOREACH(pr, &pg->pg_members, ps_pglist) 98 TAILQ_FOREACH(p, &pr->ps_threads, p_thr_link) { 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 LIST_FOREACH(p, &allproc, p_list) 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 */ 124 int 125 sys_setpriority(struct proc *curp, void *v, register_t *retval) 126 { 127 struct sys_setpriority_args /* { 128 syscallarg(int) which; 129 syscallarg(id_t) who; 130 syscallarg(int) prio; 131 } */ *uap = v; 132 struct process *pr; 133 int found = 0, error = 0; 134 135 switch (SCARG(uap, which)) { 136 137 case PRIO_PROCESS: 138 if (SCARG(uap, who) == 0) 139 pr = curp->p_p; 140 else 141 pr = prfind(SCARG(uap, who)); 142 if (pr == NULL) 143 break; 144 error = donice(curp, pr, SCARG(uap, prio)); 145 found++; 146 break; 147 148 case PRIO_PGRP: { 149 struct pgrp *pg; 150 151 if (SCARG(uap, who) == 0) 152 pg = curp->p_p->ps_pgrp; 153 else if ((pg = pgfind(SCARG(uap, who))) == NULL) 154 break; 155 LIST_FOREACH(pr, &pg->pg_members, ps_pglist) { 156 error = donice(curp, pr, SCARG(uap, prio)); 157 found++; 158 } 159 break; 160 } 161 162 case PRIO_USER: { 163 struct proc *p; 164 if (SCARG(uap, who) == 0) 165 SCARG(uap, who) = curp->p_ucred->cr_uid; 166 LIST_FOREACH(p, &allproc, p_list) 167 if ((p->p_flag & P_THREAD) == 0 && 168 p->p_ucred->cr_uid == SCARG(uap, who)) { 169 error = donice(curp, p->p_p, SCARG(uap, prio)); 170 found++; 171 } 172 break; 173 } 174 175 default: 176 return (EINVAL); 177 } 178 if (found == 0) 179 return (ESRCH); 180 return (error); 181 } 182 183 int 184 donice(struct proc *curp, struct process *chgpr, int n) 185 { 186 struct pcred *pcred = curp->p_cred; 187 struct proc *p; 188 int s; 189 190 if (pcred->pc_ucred->cr_uid && pcred->p_ruid && 191 pcred->pc_ucred->cr_uid != chgpr->ps_cred->pc_ucred->cr_uid && 192 pcred->p_ruid != chgpr->ps_cred->pc_ucred->cr_uid) 193 return (EPERM); 194 if (n > PRIO_MAX) 195 n = PRIO_MAX; 196 if (n < PRIO_MIN) 197 n = PRIO_MIN; 198 n += NZERO; 199 /* XXX wrong: p_nice should be in process */ 200 if (n < chgpr->ps_mainproc->p_nice && suser(curp, 0)) 201 return (EACCES); 202 chgpr->ps_mainproc->p_nice = n; 203 SCHED_LOCK(s); 204 TAILQ_FOREACH(p, &chgpr->ps_threads, p_thr_link) 205 (void)resetpriority(p); 206 SCHED_UNLOCK(s); 207 return (0); 208 } 209 210 /* ARGSUSED */ 211 int 212 sys_setrlimit(struct proc *p, void *v, register_t *retval) 213 { 214 struct sys_setrlimit_args /* { 215 syscallarg(int) which; 216 syscallarg(const 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 228 int 229 dosetrlimit(struct proc *p, u_int which, struct rlimit *limp) 230 { 231 struct rlimit *alimp; 232 rlim_t maxlim; 233 int error; 234 235 if (which >= RLIM_NLIMITS) 236 return (EINVAL); 237 238 alimp = &p->p_rlimit[which]; 239 if (limp->rlim_cur > alimp->rlim_max || 240 limp->rlim_max > alimp->rlim_max) 241 if ((error = suser(p, 0)) != 0) 242 return (error); 243 if (p->p_p->ps_limit->p_refcnt > 1) { 244 struct plimit *l = p->p_p->ps_limit; 245 246 /* limcopy() can sleep, so copy before decrementing refcnt */ 247 p->p_p->ps_limit = limcopy(l); 248 l->p_refcnt--; 249 alimp = &p->p_rlimit[which]; 250 } 251 252 switch (which) { 253 case RLIMIT_DATA: 254 maxlim = maxdmap; 255 break; 256 case RLIMIT_STACK: 257 maxlim = maxsmap; 258 break; 259 case RLIMIT_NOFILE: 260 maxlim = maxfiles; 261 break; 262 case RLIMIT_NPROC: 263 maxlim = maxproc; 264 break; 265 default: 266 maxlim = RLIM_INFINITY; 267 break; 268 } 269 270 if (limp->rlim_max > maxlim) 271 limp->rlim_max = maxlim; 272 if (limp->rlim_cur > limp->rlim_max) 273 limp->rlim_cur = limp->rlim_max; 274 275 if (which == RLIMIT_STACK) { 276 /* 277 * Stack is allocated to the max at exec time with only 278 * "rlim_cur" bytes accessible. If stack limit is going 279 * up make more accessible, if going down make inaccessible. 280 */ 281 if (limp->rlim_cur != alimp->rlim_cur) { 282 vaddr_t addr; 283 vsize_t size; 284 vm_prot_t prot; 285 286 if (limp->rlim_cur > alimp->rlim_cur) { 287 prot = VM_PROT_READ|VM_PROT_WRITE; 288 size = limp->rlim_cur - alimp->rlim_cur; 289 #ifdef MACHINE_STACK_GROWS_UP 290 addr = USRSTACK + alimp->rlim_cur; 291 #else 292 addr = USRSTACK - limp->rlim_cur; 293 #endif 294 } else { 295 prot = VM_PROT_NONE; 296 size = alimp->rlim_cur - limp->rlim_cur; 297 #ifdef MACHINE_STACK_GROWS_UP 298 addr = USRSTACK + limp->rlim_cur; 299 #else 300 addr = USRSTACK - alimp->rlim_cur; 301 #endif 302 } 303 addr = trunc_page(addr); 304 size = round_page(size); 305 (void) uvm_map_protect(&p->p_vmspace->vm_map, 306 addr, addr+size, prot, FALSE); 307 } 308 } 309 310 *alimp = *limp; 311 return (0); 312 } 313 314 /* ARGSUSED */ 315 int 316 sys_getrlimit(struct proc *p, void *v, register_t *retval) 317 { 318 struct sys_getrlimit_args /* { 319 syscallarg(int) which; 320 syscallarg(struct rlimit *) rlp; 321 } */ *uap = v; 322 323 if (SCARG(uap, which) < 0 || SCARG(uap, which) >= RLIM_NLIMITS) 324 return (EINVAL); 325 return (copyout((caddr_t)&p->p_rlimit[SCARG(uap, which)], 326 (caddr_t)SCARG(uap, rlp), sizeof (struct rlimit))); 327 } 328 329 /* 330 * Transform the running time and tick information in proc p into user, 331 * system, and interrupt time usage. 332 */ 333 void 334 calcru(struct proc *p, struct timeval *up, struct timeval *sp, 335 struct timeval *ip) 336 { 337 u_quad_t st, ut, it; 338 int freq; 339 int s; 340 341 s = splstatclock(); 342 st = p->p_sticks; 343 ut = p->p_uticks; 344 it = p->p_iticks; 345 splx(s); 346 347 if (st + ut + it == 0) { 348 timerclear(up); 349 timerclear(sp); 350 if (ip != NULL) 351 timerclear(ip); 352 return; 353 } 354 355 freq = stathz ? stathz : hz; 356 357 st = st * 1000000 / freq; 358 sp->tv_sec = st / 1000000; 359 sp->tv_usec = st % 1000000; 360 ut = ut * 1000000 / freq; 361 up->tv_sec = ut / 1000000; 362 up->tv_usec = ut % 1000000; 363 if (ip != NULL) { 364 it = it * 1000000 / freq; 365 ip->tv_sec = it / 1000000; 366 ip->tv_usec = it % 1000000; 367 } 368 } 369 370 /* ARGSUSED */ 371 int 372 sys_getrusage(struct proc *p, void *v, register_t *retval) 373 { 374 struct sys_getrusage_args /* { 375 syscallarg(int) who; 376 syscallarg(struct rusage *) rusage; 377 } */ *uap = v; 378 struct process *pr = p->p_p; 379 struct rusage ru; 380 struct rusage *rup; 381 382 switch (SCARG(uap, who)) { 383 384 case RUSAGE_SELF: 385 calcru(p, &p->p_stats->p_ru.ru_utime, 386 &p->p_stats->p_ru.ru_stime, NULL); 387 ru = p->p_stats->p_ru; 388 rup = &ru; 389 390 /* XXX add on already dead threads */ 391 392 /* add on other living threads */ 393 { 394 struct proc *q; 395 396 TAILQ_FOREACH(q, &pr->ps_threads, p_thr_link) { 397 if (q == p || P_ZOMBIE(q)) 398 continue; 399 /* 400 * XXX this is approximate: no call 401 * to calcru in other running threads 402 */ 403 ruadd(rup, &q->p_stats->p_ru); 404 } 405 } 406 break; 407 408 case RUSAGE_THREAD: 409 rup = &p->p_stats->p_ru; 410 calcru(p, &rup->ru_utime, &rup->ru_stime, NULL); 411 ru = *rup; 412 break; 413 414 case RUSAGE_CHILDREN: 415 rup = &p->p_stats->p_cru; 416 break; 417 418 default: 419 return (EINVAL); 420 } 421 return (copyout((caddr_t)rup, (caddr_t)SCARG(uap, rusage), 422 sizeof (struct rusage))); 423 } 424 425 void 426 ruadd(struct rusage *ru, struct rusage *ru2) 427 { 428 long *ip, *ip2; 429 int i; 430 431 timeradd(&ru->ru_utime, &ru2->ru_utime, &ru->ru_utime); 432 timeradd(&ru->ru_stime, &ru2->ru_stime, &ru->ru_stime); 433 if (ru->ru_maxrss < ru2->ru_maxrss) 434 ru->ru_maxrss = ru2->ru_maxrss; 435 ip = &ru->ru_first; ip2 = &ru2->ru_first; 436 for (i = &ru->ru_last - &ru->ru_first; i >= 0; i--) 437 *ip++ += *ip2++; 438 } 439 440 struct pool plimit_pool; 441 442 /* 443 * Make a copy of the plimit structure. 444 * We share these structures copy-on-write after fork, 445 * and copy when a limit is changed. 446 */ 447 struct plimit * 448 limcopy(struct plimit *lim) 449 { 450 struct plimit *newlim; 451 static int initialized; 452 453 if (!initialized) { 454 pool_init(&plimit_pool, sizeof(struct plimit), 0, 0, 0, 455 "plimitpl", &pool_allocator_nointr); 456 initialized = 1; 457 } 458 459 newlim = pool_get(&plimit_pool, PR_WAITOK); 460 bcopy(lim->pl_rlimit, newlim->pl_rlimit, 461 sizeof(struct rlimit) * RLIM_NLIMITS); 462 newlim->p_refcnt = 1; 463 return (newlim); 464 } 465 466 void 467 limfree(struct plimit *lim) 468 { 469 if (--lim->p_refcnt > 0) 470 return; 471 pool_put(&plimit_pool, lim); 472 } 473