121923Sdavidn/*- 221923Sdavidn * Copyright (c) 1997 by 321923Sdavidn * David L. Nugent <davidn@blaze.net.au> 421923Sdavidn * All rights reserved. 521923Sdavidn * 621923Sdavidn * Redistribution and use in source and binary forms, with or without 721923Sdavidn * modification, is permitted provided that the following conditions 821923Sdavidn * are met: 921923Sdavidn * 1. Redistributions of source code must retain the above copyright 1021923Sdavidn * notice immediately at the beginning of the file, without modification, 1121923Sdavidn * this list of conditions, and the following disclaimer. 1221923Sdavidn * 2. Redistributions in binary form must reproduce the above copyright 1321923Sdavidn * notice, this list of conditions and the following disclaimer in the 1421923Sdavidn * documentation and/or other materials provided with the distribution. 1521923Sdavidn * 3. This work was done expressly for inclusion into FreeBSD. Other use 1621923Sdavidn * is permitted provided this notation is included. 1721923Sdavidn * 4. Absolutely no warranty of function or purpose is made by the authors. 1821923Sdavidn * 5. Modifications may be freely made to this file providing the above 1921923Sdavidn * conditions are met. 2021923Sdavidn * 2121923Sdavidn * Display/change(+runprogram)/eval resource limits. 2221923Sdavidn */ 2321923Sdavidn 2499112Sobrien#include <sys/cdefs.h> 2599112Sobrien__FBSDID("$FreeBSD: stable/10/usr.bin/limits/limits.c 319256 2017-05-30 21:58:53Z asomers $"); 2699112Sobrien 2721923Sdavidn#include <err.h> 2821923Sdavidn#include <stdio.h> 2921923Sdavidn#include <string.h> 3021923Sdavidn#include <sys/types.h> 3121923Sdavidn#include <sys/stat.h> 32230549Strociny#include <sys/sysctl.h> 3321923Sdavidn#include <sys/param.h> 3421923Sdavidn#include <stdlib.h> 3521923Sdavidn#include <unistd.h> 36200462Sdelphij#include <stdarg.h> 3794611Sdwmalone#include <stdint.h> 3821923Sdavidn#include <ctype.h> 3921923Sdavidn#include <errno.h> 4021923Sdavidn#include <pwd.h> 4121923Sdavidn#include <login_cap.h> 4221923Sdavidn#include <sys/time.h> 4321923Sdavidn#include <sys/resource.h> 4421923Sdavidn 4521923Sdavidnenum 4621923Sdavidn{ 4725672Sdavidn SH_NONE, 4825672Sdavidn SH_SH, /* sh */ 4925672Sdavidn SH_CSH, /* csh */ 5025672Sdavidn SH_BASH, /* gnu bash */ 5125672Sdavidn SH_TCSH, /* tcsh */ 5225672Sdavidn SH_KSH, /* (pd)ksh */ 5325672Sdavidn SH_ZSH, /* zsh */ 5425672Sdavidn SH_RC, /* rc or es */ 5525672Sdavidn SH_NUMBER 5621923Sdavidn}; 5721923Sdavidn 5821923Sdavidn 5921923Sdavidn/* eval emitter for popular shells. 6021923Sdavidn * Why aren't there any standards here? Most shells support either 6121923Sdavidn * the csh 'limit' or sh 'ulimit' command, but each varies just 6221923Sdavidn * enough that they aren't very compatible from one to the other. 6321923Sdavidn */ 6421923Sdavidnstatic struct { 6525672Sdavidn const char * name; /* Name of shell */ 6625672Sdavidn const char * inf; /* Name used for 'unlimited' resource */ 6725672Sdavidn const char * cmd; /* Intro text */ 6825672Sdavidn const char * hard; /* Hard limit text */ 6925672Sdavidn const char * soft; /* Soft limit text */ 7025672Sdavidn const char * both; /* Hard+Soft limit text */ 7125672Sdavidn struct { 7225672Sdavidn const char * pfx; 7325672Sdavidn const char * sfx; 7425672Sdavidn int divisor; 7525672Sdavidn } lprm[RLIM_NLIMITS]; 7621923Sdavidn} shellparm[] = 7721923Sdavidn{ 7825672Sdavidn { "", "infinity", "Resource limits%s%s:\n", "-max", "-cur", "", 7925672Sdavidn { 80182685Sed { " cputime%-4s %8s", " secs\n", 1 }, 81182685Sed { " filesize%-4s %8s", " kB\n", 1024 }, 82182685Sed { " datasize%-4s %8s", " kB\n", 1024 }, 83182685Sed { " stacksize%-4s %8s", " kB\n", 1024 }, 84182685Sed { " coredumpsize%-4s %8s", " kB\n", 1024 }, 85182685Sed { " memoryuse%-4s %8s", " kB\n", 1024 }, 86182685Sed { " memorylocked%-4s %8s", " kB\n", 1024 }, 87182685Sed { " maxprocesses%-4s %8s", "\n", 1 }, 88182685Sed { " openfiles%-4s %8s", "\n", 1 }, 89182685Sed { " sbsize%-4s %8s", " bytes\n", 1 }, 90182685Sed { " vmemoryuse%-4s %8s", " kB\n", 1024 }, 91194767Skib { " pseudo-terminals%-4s %8s", "\n", 1 }, 92194767Skib { " swapuse%-4s %8s", " kB\n", 1024 } 9325672Sdavidn } 9425672Sdavidn }, 9525672Sdavidn { "sh", "unlimited", "", " -H", " -S", "", 9625672Sdavidn { 9725672Sdavidn { "ulimit%s -t %s", ";\n", 1 }, 9825672Sdavidn { "ulimit%s -f %s", ";\n", 512 }, 9925672Sdavidn { "ulimit%s -d %s", ";\n", 1024 }, 10025672Sdavidn { "ulimit%s -s %s", ";\n", 1024 }, 10125672Sdavidn { "ulimit%s -c %s", ";\n", 512 }, 10225672Sdavidn { "ulimit%s -m %s", ";\n", 1024 }, 10325672Sdavidn { "ulimit%s -l %s", ";\n", 1024 }, 10425672Sdavidn { "ulimit%s -u %s", ";\n", 1 }, 10552071Sgreen { "ulimit%s -n %s", ";\n", 1 }, 10698850Sdillon { "ulimit%s -b %s", ";\n", 1 }, 107182685Sed { "ulimit%s -v %s", ";\n", 1024 }, 108194767Skib { "ulimit%s -p %s", ";\n", 1 }, 109194767Skib { "ulimit%s -w %s", ";\n", 1024 } 11025672Sdavidn } 11125672Sdavidn }, 11225672Sdavidn { "csh", "unlimited", "", " -h", "", NULL, 11325672Sdavidn { 114182685Sed { "limit%s cputime %s", ";\n", 1 }, 115182685Sed { "limit%s filesize %s", ";\n", 1024 }, 116182685Sed { "limit%s datasize %s", ";\n", 1024 }, 117182685Sed { "limit%s stacksize %s", ";\n", 1024 }, 118182685Sed { "limit%s coredumpsize %s", ";\n", 1024 }, 119182685Sed { "limit%s memoryuse %s", ";\n", 1024 }, 120182685Sed { "limit%s memorylocked %s", ";\n", 1024 }, 121182685Sed { "limit%s maxproc %s", ";\n", 1 }, 122182685Sed { "limit%s openfiles %s", ";\n", 1 }, 123182685Sed { "limit%s sbsize %s", ";\n", 1 }, 124182685Sed { "limit%s vmemoryuse %s", ";\n", 1024 }, 125194767Skib { "limit%s pseudoterminals %s", ";\n", 1 }, 126194767Skib { "limit%s swapuse %s", ";\n", 1024 } 12725672Sdavidn } 12825672Sdavidn }, 12925672Sdavidn { "bash|bash2", "unlimited", "", " -H", " -S", "", 13025672Sdavidn { 13125672Sdavidn { "ulimit%s -t %s", ";\n", 1 }, 13225672Sdavidn { "ulimit%s -f %s", ";\n", 1024 }, 13325672Sdavidn { "ulimit%s -d %s", ";\n", 1024 }, 13425672Sdavidn { "ulimit%s -s %s", ";\n", 1024 }, 13525672Sdavidn { "ulimit%s -c %s", ";\n", 1024 }, 13625672Sdavidn { "ulimit%s -m %s", ";\n", 1024 }, 13725672Sdavidn { "ulimit%s -l %s", ";\n", 1024 }, 13825672Sdavidn { "ulimit%s -u %s", ";\n", 1 }, 13952071Sgreen { "ulimit%s -n %s", ";\n", 1 }, 14098850Sdillon { "ulimit%s -b %s", ";\n", 1 }, 141182685Sed { "ulimit%s -v %s", ";\n", 1024 }, 142194767Skib { "ulimit%s -p %s", ";\n", 1 }, 143194767Skib { "ulimit%s -w %s", ";\n", 1024 } 14425672Sdavidn } 14525672Sdavidn }, 14625672Sdavidn { "tcsh", "unlimited", "", " -h", "", NULL, 14725672Sdavidn { 148182685Sed { "limit%s cputime %s", ";\n", 1 }, 149182685Sed { "limit%s filesize %s", ";\n", 1024 }, 150182685Sed { "limit%s datasize %s", ";\n", 1024 }, 151182685Sed { "limit%s stacksize %s", ";\n", 1024 }, 152182685Sed { "limit%s coredumpsize %s", ";\n", 1024 }, 153182685Sed { "limit%s memoryuse %s", ";\n", 1024 }, 154182685Sed { "limit%s memorylocked %s", ";\n", 1024 }, 155182685Sed { "limit%s maxproc %s", ";\n", 1 }, 156182685Sed { "limit%s descriptors %s", ";\n", 1 }, 157182685Sed { "limit%s sbsize %s", ";\n", 1 }, 158182685Sed { "limit%s vmemoryuse %s", ";\n", 1024 }, 159194767Skib { "limit%s pseudoterminals %s", ";\n", 1 }, 160194767Skib { "limit%s swapuse %s", ";\n", 1024 } 16125672Sdavidn } 16225672Sdavidn }, 16325672Sdavidn { "ksh|pdksh", "unlimited", "", " -H", " -S", "", 16425672Sdavidn { 16525672Sdavidn { "ulimit%s -t %s", ";\n", 1 }, 16625672Sdavidn { "ulimit%s -f %s", ";\n", 512 }, 16725672Sdavidn { "ulimit%s -d %s", ";\n", 1024 }, 16825672Sdavidn { "ulimit%s -s %s", ";\n", 1024 }, 16925672Sdavidn { "ulimit%s -c %s", ";\n", 512 }, 17025672Sdavidn { "ulimit%s -m %s", ";\n", 1024 }, 17125672Sdavidn { "ulimit%s -l %s", ";\n", 1024 }, 17225672Sdavidn { "ulimit%s -p %s", ";\n", 1 }, 17352071Sgreen { "ulimit%s -n %s", ";\n", 1 }, 17498850Sdillon { "ulimit%s -b %s", ";\n", 1 }, 175182685Sed { "ulimit%s -v %s", ";\n", 1024 }, 176194767Skib { "ulimit%s -p %s", ";\n", 1 }, 177194767Skib { "ulimit%s -w %s", ";\n", 1024 } 17825672Sdavidn } 17925672Sdavidn }, 18025672Sdavidn { "zsh", "unlimited", "", " -H", " -S", "", 18125672Sdavidn { 18225672Sdavidn { "ulimit%s -t %s", ";\n", 1 }, 18325672Sdavidn { "ulimit%s -f %s", ";\n", 512 }, 18425672Sdavidn { "ulimit%s -d %s", ";\n", 1024 }, 18525672Sdavidn { "ulimit%s -s %s", ";\n", 1024 }, 18625672Sdavidn { "ulimit%s -c %s", ";\n", 512 }, 18725672Sdavidn { "ulimit%s -m %s", ";\n", 1024 }, 18825672Sdavidn { "ulimit%s -l %s", ";\n", 1024 }, 18925672Sdavidn { "ulimit%s -u %s", ";\n", 1 }, 19052071Sgreen { "ulimit%s -n %s", ";\n", 1 }, 19198850Sdillon { "ulimit%s -b %s", ";\n", 1 }, 192182685Sed { "ulimit%s -v %s", ";\n", 1024 }, 193194767Skib { "ulimit%s -p %s", ";\n", 1 }, 194194767Skib { "ulimit%s -w %s", ";\n", 1024 } 19525672Sdavidn } 19625672Sdavidn }, 19725672Sdavidn { "rc|es", "unlimited", "", " -h", "", NULL, 19825672Sdavidn { 199182685Sed { "limit%s cputime %s", ";\n", 1 }, 200182685Sed { "limit%s filesize %s", ";\n", 1024 }, 201182685Sed { "limit%s datasize %s", ";\n", 1024 }, 202182685Sed { "limit%s stacksize %s", ";\n", 1024 }, 203182685Sed { "limit%s coredumpsize %s", ";\n", 1024 }, 204182685Sed { "limit%s memoryuse %s", ";\n", 1024 }, 205182685Sed { "limit%s lockedmemory %s", ";\n", 1024 }, 206182685Sed { "limit%s processes %s", ";\n", 1 }, 207182685Sed { "limit%s descriptors %s", ";\n", 1 }, 208182685Sed { "limit%s sbsize %s", ";\n", 1 }, 209182685Sed { "limit%s vmemoryuse %s", ";\n", 1024 }, 210194767Skib { "limit%s pseudoterminals %s", ";\n", 1 }, 211194767Skib { "limit%s swapuse %s", ";\n", 1024 } 21225672Sdavidn } 21325672Sdavidn }, 21494611Sdwmalone { NULL, NULL, NULL, NULL, NULL, NULL, 21594611Sdwmalone { } 21694611Sdwmalone } 21721923Sdavidn}; 21821923Sdavidn 21921923Sdavidnstatic struct { 22025672Sdavidn const char * cap; 22125672Sdavidn rlim_t (*func)(login_cap_t *, const char *, rlim_t, rlim_t); 22221923Sdavidn} resources[RLIM_NLIMITS] = { 22325672Sdavidn { "cputime", login_getcaptime }, 22425672Sdavidn { "filesize", login_getcapsize }, 22525672Sdavidn { "datasize", login_getcapsize }, 22625672Sdavidn { "stacksize", login_getcapsize }, 22725672Sdavidn { "coredumpsize", login_getcapsize }, 22825672Sdavidn { "memoryuse", login_getcapsize }, 22925672Sdavidn { "memorylocked", login_getcapsize }, 23052071Sgreen { "maxproc", login_getcapnum }, 23152071Sgreen { "openfiles", login_getcapnum }, 232194767Skib { "sbsize", login_getcapsize }, 233194767Skib { "vmemoryuse", login_getcapsize }, 234182685Sed { "pseudoterminals",login_getcapnum }, 235194767Skib { "swapuse", login_getcapsize } 23621923Sdavidn}; 23721923Sdavidn 23821923Sdavidn/* 23921923Sdavidn * One letter for each resource levels. 240228992Suqs * NOTE: There is a dependency on the corresponding 24121923Sdavidn * letter index being equal to the resource number. 24221923Sdavidn * If sys/resource.h defines are changed, this needs 24321923Sdavidn * to be modified accordingly! 24421923Sdavidn */ 24521923Sdavidn 246194767Skib#define RCS_STRING "tfdscmlunbvpw" 24721923Sdavidn 24821923Sdavidnstatic rlim_t resource_num(int which, int ch, const char *str); 24958609Scharnierstatic void usage(void); 25021923Sdavidnstatic int getshelltype(void); 25125672Sdavidnstatic void print_limit(rlim_t limit, unsigned divisor, const char *inf, 25225672Sdavidn const char *pfx, const char *sfx, const char *which); 253230549Strocinystatic void getrlimit_proc(pid_t pid, int resource, struct rlimit *rlp); 254230549Strocinystatic void setrlimit_proc(pid_t pid, int resource, const struct rlimit *rlp); 25521923Sdavidnextern char **environ; 25621923Sdavidn 25721923Sdavidnstatic const char rcs_string[] = RCS_STRING; 25821923Sdavidn 25921923Sdavidnint 26021923Sdavidnmain(int argc, char *argv[]) 26121923Sdavidn{ 26225672Sdavidn char *p, *cls = NULL; 26325672Sdavidn char *cleanenv[1]; 26425672Sdavidn struct passwd * pwd = NULL; 26525672Sdavidn int rcswhich, shelltype; 26625672Sdavidn int i, num_limits = 0; 26725672Sdavidn int ch, doeval = 0, doall = 0; 268230549Strociny int rtrn, setproc; 26925672Sdavidn login_cap_t * lc = NULL; 27025672Sdavidn enum { ANY=0, SOFT=1, HARD=2, BOTH=3, DISPLAYONLY=4 } type = ANY; 27125672Sdavidn enum { RCSUNKNOWN=0, RCSSET=1, RCSSEL=2 } todo = RCSUNKNOWN; 27225672Sdavidn int which_limits[RLIM_NLIMITS]; 27325672Sdavidn rlim_t set_limits[RLIM_NLIMITS]; 27425672Sdavidn struct rlimit limits[RLIM_NLIMITS]; 275230549Strociny pid_t pid; 27621923Sdavidn 27725672Sdavidn /* init resource tables */ 27825672Sdavidn for (i = 0; i < RLIM_NLIMITS; i++) { 27925672Sdavidn which_limits[i] = 0; /* Don't set/display any */ 28025672Sdavidn set_limits[i] = RLIM_INFINITY; 28125672Sdavidn } 28221923Sdavidn 283230549Strociny pid = -1; 28425672Sdavidn optarg = NULL; 285230549Strociny while ((ch = getopt(argc, argv, ":EeC:U:BSHP:ab:c:d:f:l:m:n:s:t:u:v:p:w:")) != -1) { 28625672Sdavidn switch(ch) { 28725672Sdavidn case 'a': 28825672Sdavidn doall = 1; 28925672Sdavidn break; 29025672Sdavidn case 'E': 29125672Sdavidn environ = cleanenv; 29225672Sdavidn cleanenv[0] = NULL; 29325672Sdavidn break; 29425672Sdavidn case 'e': 29525672Sdavidn doeval = 1; 29625672Sdavidn break; 29725672Sdavidn case 'C': 29825672Sdavidn cls = optarg; 29925672Sdavidn break; 30025672Sdavidn case 'U': 30125672Sdavidn if ((pwd = getpwnam(optarg)) == NULL) { 30225672Sdavidn if (!isdigit(*optarg) || 30325672Sdavidn (pwd = getpwuid(atoi(optarg))) == NULL) { 30458609Scharnier warnx("invalid user `%s'", optarg); 30558609Scharnier usage(); 30625672Sdavidn } 30725672Sdavidn } 30825672Sdavidn break; 30925672Sdavidn case 'H': 31025672Sdavidn type = HARD; 31125672Sdavidn break; 31225672Sdavidn case 'S': 31325672Sdavidn type = SOFT; 31425672Sdavidn break; 31525672Sdavidn case 'B': 31625672Sdavidn type = SOFT|HARD; 31725672Sdavidn break; 318230549Strociny case 'P': 319230549Strociny if (!isdigit(*optarg) || (pid = atoi(optarg)) < 0) { 320230549Strociny warnx("invalid pid `%s'", optarg); 321230549Strociny usage(); 322230549Strociny } 323230549Strociny break; 32425672Sdavidn default: 32525672Sdavidn case ':': /* Without arg */ 32625672Sdavidn if ((p = strchr(rcs_string, optopt)) != NULL) { 32794611Sdwmalone int rcswhich1 = p - rcs_string; 32825672Sdavidn if (optarg && *optarg == '-') { /* 'arg' is actually a switch */ 32925672Sdavidn --optind; /* back one arg, and make arg NULL */ 33025672Sdavidn optarg = NULL; 33125672Sdavidn } 33225672Sdavidn todo = optarg == NULL ? RCSSEL : RCSSET; 33325672Sdavidn if (type == ANY) 33425672Sdavidn type = BOTH; 33594611Sdwmalone which_limits[rcswhich1] = optarg ? type : DISPLAYONLY; 33694611Sdwmalone set_limits[rcswhich1] = resource_num(rcswhich1, optopt, optarg); 33725672Sdavidn num_limits++; 33825672Sdavidn break; 33925672Sdavidn } 34025672Sdavidn /* FALLTHRU */ 34125672Sdavidn case '?': 34258609Scharnier usage(); 34325672Sdavidn } 34425672Sdavidn optarg = NULL; 34521923Sdavidn } 34621923Sdavidn 347230549Strociny if (pid != -1) { 348230549Strociny if (cls != NULL) { 349230549Strociny warnx("-C cannot be used with -P option"); 350230549Strociny usage(); 351230549Strociny } 352230549Strociny if (pwd != NULL) { 353230549Strociny warnx("-U cannot be used with -P option"); 354230549Strociny usage(); 355230549Strociny } 356230549Strociny } 357230549Strociny 358230549Strociny /* Get current resource values */ 359230549Strociny setproc = 0; 360230549Strociny for (i = 0; i < RLIM_NLIMITS; i++) { 361230549Strociny if (pid == -1) { 362230549Strociny getrlimit(i, &limits[i]); 363230549Strociny } else if (doall || num_limits == 0) { 364230549Strociny getrlimit_proc(pid, i, &limits[i]); 365230549Strociny } else if (which_limits[i] != 0) { 366230549Strociny getrlimit_proc(pid, i, &limits[i]); 367230549Strociny setproc = 1; 368230549Strociny } 369230549Strociny } 370230549Strociny 37125672Sdavidn /* If user was specified, get class from that */ 37225672Sdavidn if (pwd != NULL) 37325672Sdavidn lc = login_getpwclass(pwd); 37425672Sdavidn else if (cls != NULL && *cls != '\0') { 37525672Sdavidn lc = login_getclassbyname(cls, NULL); 37625672Sdavidn if (lc == NULL || strcmp(cls, lc->lc_class) != 0) 37725672Sdavidn fprintf(stderr, "login class '%s' non-existent, using %s\n", 37825672Sdavidn cls, lc?lc->lc_class:"current settings"); 37925672Sdavidn } 38021923Sdavidn 38125672Sdavidn /* If we have a login class, update resource table from that */ 38225672Sdavidn if (lc != NULL) { 38325672Sdavidn for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) { 38425672Sdavidn char str[40]; 38525672Sdavidn rlim_t val; 38621923Sdavidn 38725672Sdavidn /* current value overridden by resourcename or resourcename-cur */ 38825672Sdavidn sprintf(str, "%s-cur", resources[rcswhich].cap); 38925672Sdavidn val = resources[rcswhich].func(lc, resources[rcswhich].cap, limits[rcswhich].rlim_cur, limits[rcswhich].rlim_cur); 39025672Sdavidn limits[rcswhich].rlim_cur = resources[rcswhich].func(lc, str, val, val); 39125672Sdavidn /* maximum value overridden by resourcename or resourcename-max */ 39225672Sdavidn sprintf(str, "%s-max", resources[rcswhich].cap); 39325672Sdavidn val = resources[rcswhich].func(lc, resources[rcswhich].cap, limits[rcswhich].rlim_max, limits[rcswhich].rlim_max); 39425672Sdavidn limits[rcswhich].rlim_max = resources[rcswhich].func(lc, str, val, val); 39525672Sdavidn } 39621923Sdavidn } 39721923Sdavidn 39825672Sdavidn /* now, let's determine what we wish to do with all this */ 39921923Sdavidn 40025672Sdavidn argv += optind; 40121923Sdavidn 40225672Sdavidn /* If we're setting limits or doing an eval (ie. we're not just 40325672Sdavidn * displaying), then check that hard limits are not lower than 40425672Sdavidn * soft limits, and force rasing the hard limit if we need to if 40525672Sdavidn * we are raising the soft limit, or lower the soft limit if we 40625672Sdavidn * are lowering the hard limit. 40725672Sdavidn */ 40825672Sdavidn if ((*argv || doeval) && getuid() == 0) { 40925672Sdavidn 41025672Sdavidn for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) { 41125672Sdavidn if (limits[rcswhich].rlim_max != RLIM_INFINITY) { 41225672Sdavidn if (limits[rcswhich].rlim_cur == RLIM_INFINITY) { 41325672Sdavidn limits[rcswhich].rlim_max = RLIM_INFINITY; 41425672Sdavidn which_limits[rcswhich] |= HARD; 41525672Sdavidn } else if (limits[rcswhich].rlim_cur > limits[rcswhich].rlim_max) { 41625672Sdavidn if (which_limits[rcswhich] == SOFT) { 41725672Sdavidn limits[rcswhich].rlim_max = limits[rcswhich].rlim_cur; 41825672Sdavidn which_limits[rcswhich] |= HARD; 41925672Sdavidn } else if (which_limits[rcswhich] == HARD) { 42025672Sdavidn limits[rcswhich].rlim_cur = limits[rcswhich].rlim_max; 42125672Sdavidn which_limits[rcswhich] |= SOFT; 42225672Sdavidn } else { 42325672Sdavidn /* else.. if we're specifically setting both to 42425672Sdavidn * silly values, then let it error out. 42525672Sdavidn */ 42625672Sdavidn } 42725672Sdavidn } 42825672Sdavidn } 42921923Sdavidn } 43021923Sdavidn } 43121923Sdavidn 43225672Sdavidn /* See if we've overridden anything specific on the command line */ 43325672Sdavidn if (num_limits && todo == RCSSET) { 43425672Sdavidn for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) { 43525672Sdavidn if (which_limits[rcswhich] & HARD) 43625672Sdavidn limits[rcswhich].rlim_max = set_limits[rcswhich]; 43725672Sdavidn if (which_limits[rcswhich] & SOFT) 43825672Sdavidn limits[rcswhich].rlim_cur = set_limits[rcswhich]; 43925672Sdavidn } 44021923Sdavidn } 44121923Sdavidn 44225672Sdavidn /* If *argv is not NULL, then we are being asked to 44325672Sdavidn * (perhaps) set environment variables and run a program 44425672Sdavidn */ 44525672Sdavidn if (*argv) { 44658609Scharnier if (doeval) { 44758609Scharnier warnx("-e cannot be used with `cmd' option"); 44858609Scharnier usage(); 44958609Scharnier } 450230549Strociny if (pid != -1) { 451230549Strociny warnx("-P cannot be used with `cmd' option"); 452230549Strociny usage(); 453230549Strociny } 45421923Sdavidn 45525672Sdavidn login_close(lc); 45621923Sdavidn 45725672Sdavidn /* set leading environment variables, like eval(1) */ 458171195Sscf while (*argv && (p = strchr(*argv, '='))) { 459171195Sscf *p = '\0'; 460171195Sscf rtrn = setenv(*argv++, p + 1, 1); 461171195Sscf *p = '='; 462171195Sscf if (rtrn == -1) 463171195Sscf err(EXIT_FAILURE, "setenv %s", *argv); 464171195Sscf } 46521923Sdavidn 46625672Sdavidn /* Set limits */ 46725672Sdavidn for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) { 46825672Sdavidn if (doall || num_limits == 0 || which_limits[rcswhich] != 0) 46925672Sdavidn if (setrlimit(rcswhich, &limits[rcswhich]) == -1) 47025672Sdavidn err(1, "setrlimit %s", resources[rcswhich].cap); 47125672Sdavidn } 47225672Sdavidn 47325672Sdavidn if (*argv == NULL) 47458609Scharnier usage(); 47525672Sdavidn 47625672Sdavidn execvp(*argv, argv); 47725672Sdavidn err(1, "%s", *argv); 47821923Sdavidn } 47921923Sdavidn 480230549Strociny if (setproc) { 481230549Strociny for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) { 482230549Strociny if (which_limits[rcswhich] != 0) 483230549Strociny setrlimit_proc(pid, rcswhich, &limits[rcswhich]); 484230549Strociny } 485230549Strociny exit(EXIT_SUCCESS); 486230549Strociny } 487230549Strociny 48825672Sdavidn shelltype = doeval ? getshelltype() : SH_NONE; 48921923Sdavidn 49025672Sdavidn if (type == ANY) /* Default to soft limits */ 49125672Sdavidn type = SOFT; 49221923Sdavidn 49325672Sdavidn /* Display limits */ 49425672Sdavidn printf(shellparm[shelltype].cmd, 49525672Sdavidn lc ? " for class " : " (current)", 49625672Sdavidn lc ? lc->lc_class : ""); 49721923Sdavidn 49825672Sdavidn for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) { 49925672Sdavidn if (doall || num_limits == 0 || which_limits[rcswhich] != 0) { 50025672Sdavidn if (which_limits[rcswhich] == ANY || which_limits[rcswhich]) 50125672Sdavidn which_limits[rcswhich] = type; 50225672Sdavidn if (shellparm[shelltype].lprm[rcswhich].pfx) { 50325672Sdavidn if (shellparm[shelltype].both && limits[rcswhich].rlim_cur == limits[rcswhich].rlim_max) { 50425672Sdavidn print_limit(limits[rcswhich].rlim_max, 50525672Sdavidn shellparm[shelltype].lprm[rcswhich].divisor, 50625672Sdavidn shellparm[shelltype].inf, 50725672Sdavidn shellparm[shelltype].lprm[rcswhich].pfx, 50825672Sdavidn shellparm[shelltype].lprm[rcswhich].sfx, 50925672Sdavidn shellparm[shelltype].both); 51025672Sdavidn } else { 51125672Sdavidn if (which_limits[rcswhich] & HARD) { 51225672Sdavidn print_limit(limits[rcswhich].rlim_max, 51325672Sdavidn shellparm[shelltype].lprm[rcswhich].divisor, 51425672Sdavidn shellparm[shelltype].inf, 51525672Sdavidn shellparm[shelltype].lprm[rcswhich].pfx, 51625672Sdavidn shellparm[shelltype].lprm[rcswhich].sfx, 51725672Sdavidn shellparm[shelltype].hard); 51825672Sdavidn } 51925672Sdavidn if (which_limits[rcswhich] & SOFT) { 52025672Sdavidn print_limit(limits[rcswhich].rlim_cur, 52125672Sdavidn shellparm[shelltype].lprm[rcswhich].divisor, 52225672Sdavidn shellparm[shelltype].inf, 52325672Sdavidn shellparm[shelltype].lprm[rcswhich].pfx, 52425672Sdavidn shellparm[shelltype].lprm[rcswhich].sfx, 52525672Sdavidn shellparm[shelltype].soft); 52625672Sdavidn } 52725672Sdavidn } 52825672Sdavidn } 52921923Sdavidn } 53021923Sdavidn } 53121923Sdavidn 53225672Sdavidn login_close(lc); 53325672Sdavidn exit(EXIT_SUCCESS); 53421923Sdavidn} 53521923Sdavidn 53621923Sdavidn 53721923Sdavidnstatic void 53858609Scharnierusage(void) 53921923Sdavidn{ 54025672Sdavidn (void)fprintf(stderr, 541230549Strociny "usage: limits [-C class|-P pid|-U user] [-eaSHBE] " 542230549Strociny "[-bcdflmnstuvpw [val]] [[name=val ...] cmd]\n"); 54325672Sdavidn exit(EXIT_FAILURE); 54421923Sdavidn} 54521923Sdavidn 54621923Sdavidnstatic void 54721923Sdavidnprint_limit(rlim_t limit, unsigned divisor, const char * inf, const char * pfx, const char * sfx, const char * which) 54821923Sdavidn{ 54925672Sdavidn char numbr[64]; 55021923Sdavidn 55125672Sdavidn if (limit == RLIM_INFINITY) 552319256Sasomers strlcpy(numbr, inf, sizeof(numbr)); 55325672Sdavidn else 55494611Sdwmalone sprintf(numbr, "%jd", (intmax_t)((limit + divisor/2) / divisor)); 55525672Sdavidn printf(pfx, which, numbr); 55625672Sdavidn printf(sfx, which); 55725672Sdavidn 55821923Sdavidn} 55921923Sdavidn 56021923Sdavidn 56121923Sdavidnstatic rlim_t 56221923Sdavidnresource_num(int which, int ch, const char *str) 56321923Sdavidn{ 56425672Sdavidn rlim_t res = RLIM_INFINITY; 56521923Sdavidn 56625672Sdavidn if (str != NULL && 56725672Sdavidn !(strcasecmp(str, "inf") == 0 || 56825672Sdavidn strcasecmp(str, "infinity") == 0 || 56925672Sdavidn strcasecmp(str, "unlimit") == 0 || 57025672Sdavidn strcasecmp(str, "unlimited") == 0)) { 57125672Sdavidn const char * s = str; 57225672Sdavidn char *e; 57325672Sdavidn 57425672Sdavidn switch (which) { 57525672Sdavidn case RLIMIT_CPU: /* time values */ 57625672Sdavidn errno = 0; 57725672Sdavidn res = 0; 57825672Sdavidn while (*s) { 57925672Sdavidn rlim_t tim = strtoq(s, &e, 0); 58025672Sdavidn if (e == NULL || e == s || errno) 58125672Sdavidn break; 58225672Sdavidn switch (*e++) { 58325672Sdavidn case 0: /* end of string */ 58425672Sdavidn e--; 58525672Sdavidn default: 58625672Sdavidn case 's': case 'S': /* seconds */ 58725672Sdavidn break; 58825672Sdavidn case 'm': case 'M': /* minutes */ 58925672Sdavidn tim *= 60L; 59025672Sdavidn break; 59125672Sdavidn case 'h': case 'H': /* hours */ 59225672Sdavidn tim *= (60L * 60L); 59325672Sdavidn break; 59425672Sdavidn case 'd': case 'D': /* days */ 59525672Sdavidn tim *= (60L * 60L * 24L); 59625672Sdavidn break; 59725672Sdavidn case 'w': case 'W': /* weeks */ 59825672Sdavidn tim *= (60L * 60L * 24L * 7L); 59925672Sdavidn case 'y': case 'Y': /* Years */ 60025672Sdavidn tim *= (60L * 60L * 24L * 365L); 60125672Sdavidn } 60225672Sdavidn s = e; 60325672Sdavidn res += tim; 60425672Sdavidn } 60525672Sdavidn break; 60625672Sdavidn case RLIMIT_FSIZE: /* Size values */ 60725672Sdavidn case RLIMIT_DATA: 60825672Sdavidn case RLIMIT_STACK: 60925672Sdavidn case RLIMIT_CORE: 61025672Sdavidn case RLIMIT_RSS: 61125672Sdavidn case RLIMIT_MEMLOCK: 612103549Sgreen case RLIMIT_SBSIZE: 61398850Sdillon case RLIMIT_VMEM: 614194767Skib case RLIMIT_SWAP: 61525672Sdavidn errno = 0; 61625672Sdavidn res = 0; 61725672Sdavidn while (*s) { 61825672Sdavidn rlim_t mult, tim = strtoq(s, &e, 0); 61925672Sdavidn if (e == NULL || e == s || errno) 62025672Sdavidn break; 62125672Sdavidn switch (*e++) { 62225672Sdavidn case 0: /* end of string */ 62325672Sdavidn e--; 62425672Sdavidn default: 62525672Sdavidn mult = 1; 62625672Sdavidn break; 62725672Sdavidn case 'b': case 'B': /* 512-byte blocks */ 62825672Sdavidn mult = 512; 62925672Sdavidn break; 63025672Sdavidn case 'k': case 'K': /* 1024-byte Kilobytes */ 63125672Sdavidn mult = 1024; 63225672Sdavidn break; 63325672Sdavidn case 'm': case 'M': /* 1024-k kbytes */ 63425672Sdavidn mult = 1024 * 1024; 63525672Sdavidn break; 63625672Sdavidn case 'g': case 'G': /* 1Gbyte */ 63725672Sdavidn mult = 1024 * 1024 * 1024; 63825672Sdavidn break; 63925672Sdavidn case 't': case 'T': /* 1TBte */ 64025672Sdavidn mult = 1024LL * 1024LL * 1024LL * 1024LL; 64125672Sdavidn break; 64225672Sdavidn } 64325672Sdavidn s = e; 64425672Sdavidn res += (tim * mult); 64525672Sdavidn } 64625672Sdavidn break; 64725672Sdavidn case RLIMIT_NPROC: 64825672Sdavidn case RLIMIT_NOFILE: 649182685Sed case RLIMIT_NPTS: 65025672Sdavidn res = strtoq(s, &e, 0); 65125672Sdavidn s = e; 65225672Sdavidn break; 65325672Sdavidn } 65458609Scharnier if (*s) { 65558609Scharnier warnx("invalid value -%c `%s'", ch, str); 65658609Scharnier usage(); 65758609Scharnier } 65821923Sdavidn } 65925672Sdavidn return res; 66021923Sdavidn} 66121923Sdavidn 66221923Sdavidn 66321923Sdavidnstatic int 66421923Sdavidngetshellbyname(const char * shell) 66521923Sdavidn{ 66625672Sdavidn int i; 66725672Sdavidn const char * q; 66825672Sdavidn const char * p = strrchr(shell, '/'); 66921923Sdavidn 67094611Sdwmalone p = p ? p+1 : shell; 67125672Sdavidn for (i = 0; (q = shellparm[i].name) != NULL; i++) { 67225672Sdavidn while (*q) { 67325672Sdavidn int j = strcspn(q, "|"); 67425672Sdavidn 67525672Sdavidn if (j == 0) 67625672Sdavidn break; 67725672Sdavidn if (strncmp(p, q, j) == 0) 67825672Sdavidn return i; 67925672Sdavidn if (*(q += j)) 68025672Sdavidn ++q; 68125672Sdavidn } 68221923Sdavidn } 68325672Sdavidn return SH_SH; 68421923Sdavidn} 68521923Sdavidn 68625672Sdavidn 68721923Sdavidn/* 68821923Sdavidn * Determine the type of shell our parent process is 68921923Sdavidn * This is quite tricky, not 100% reliable and probably 69021923Sdavidn * not nearly as thorough as it should be. Basically, this 69121923Sdavidn * is a "best guess" only, but hopefully will work in 69221923Sdavidn * most cases. 69321923Sdavidn */ 69421923Sdavidn 69521923Sdavidnstatic int 69621923Sdavidngetshelltype(void) 69721923Sdavidn{ 69825672Sdavidn pid_t ppid = getppid(); 69921923Sdavidn 70025672Sdavidn if (ppid != 1) { 70125672Sdavidn FILE * fp; 70225672Sdavidn struct stat st; 70325672Sdavidn char procdir[MAXPATHLEN], buf[128]; 70425672Sdavidn int l = sprintf(procdir, "/proc/%ld/", (long)ppid); 70525672Sdavidn char * shell = getenv("SHELL"); 70621923Sdavidn 70725672Sdavidn if (shell != NULL && stat(shell, &st) != -1) { 70825672Sdavidn struct stat st1; 70925672Sdavidn 71025672Sdavidn strcpy(procdir+l, "file"); 71125672Sdavidn /* $SHELL is actual shell? */ 71225672Sdavidn if (stat(procdir, &st1) != -1 && memcmp(&st, &st1, sizeof st) == 0) 71325672Sdavidn return getshellbyname(shell); 71425672Sdavidn } 71525672Sdavidn strcpy(procdir+l, "status"); 71625672Sdavidn if (stat(procdir, &st) == 0 && (fp = fopen(procdir, "r")) != NULL) { 71725672Sdavidn char * p = fgets(buf, sizeof buf, fp)==NULL ? NULL : strtok(buf, " \t"); 71825672Sdavidn fclose(fp); 71925672Sdavidn if (p != NULL) 72025672Sdavidn return getshellbyname(p); 72125672Sdavidn } 72221923Sdavidn } 72325672Sdavidn return SH_SH; 72421923Sdavidn} 72521923Sdavidn 726230549Strocinystatic void 727230549Strocinygetrlimit_proc(pid_t pid, int resource, struct rlimit *rlp) 728230549Strociny{ 729230549Strociny int error; 730230549Strociny int name[5]; 731230549Strociny size_t len; 732230549Strociny 733230549Strociny name[0] = CTL_KERN; 734230549Strociny name[1] = KERN_PROC; 735230549Strociny name[2] = KERN_PROC_RLIMIT; 736230549Strociny name[3] = pid; 737230549Strociny name[4] = resource; 738230549Strociny len = sizeof(*rlp); 739230549Strociny error = sysctl(name, 5, rlp, &len, NULL, 0); 740230549Strociny if (error == -1) 741230549Strociny err(EXIT_FAILURE, "sysctl: kern.proc.rlimit: %d", pid); 742230549Strociny if (len != sizeof(*rlp)) 743230549Strociny errx(EXIT_FAILURE, "sysctl() returns wrong size"); 744230549Strociny} 745230549Strociny 746230549Strocinystatic void 747230549Strocinysetrlimit_proc(pid_t pid, int resource, const struct rlimit *rlp) 748230549Strociny{ 749230549Strociny int error; 750230549Strociny int name[5]; 751230549Strociny 752230549Strociny name[0] = CTL_KERN; 753230549Strociny name[1] = KERN_PROC; 754230549Strociny name[2] = KERN_PROC_RLIMIT; 755230549Strociny name[3] = pid; 756230549Strociny name[4] = resource; 757230549Strociny error = sysctl(name, 5, NULL, 0, rlp, sizeof(*rlp)); 758230549Strociny if (error == -1) 759230549Strociny err(EXIT_FAILURE, "sysctl: kern.proc.rlimit: %d", pid); 760230549Strociny} 761