1/* $OpenBSD: c_ulimit.c,v 1.19 2013/11/28 10:33:37 sobrado Exp $ */ 2 3/*- 4 * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 5 * 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 6 * 2019, 2020 7 * mirabilos <m@mirbsd.org> 8 * 9 * Provided that these terms and disclaimer and all copyright notices 10 * are retained or reproduced in an accompanying document, permission 11 * is granted to deal in this work without restriction, including un- 12 * limited rights to use, publicly perform, distribute, sell, modify, 13 * merge, give away, or sublicence. 14 * 15 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to 16 * the utmost extent permitted by applicable law, neither express nor 17 * implied; without malicious intent or gross negligence. In no event 18 * may a licensor, author or contributor be held liable for indirect, 19 * direct, other damage, loss, or other issues arising in any way out 20 * of dealing in the work, even if advised of the possibility of such 21 * damage or existence of a defect, except proven that it results out 22 * of said person's immediate fault when using the work as intended. 23 */ 24 25#include "sh.h" 26 27__RCSID("$MirOS: src/bin/mksh/ulimit.c,v 1.3 2020/07/24 21:08:26 tg Exp $"); 28 29#define SOFT 0x1 30#define HARD 0x2 31 32#if HAVE_RLIMIT 33 34#if !HAVE_RLIM_T 35typedef unsigned long rlim_t; 36#endif 37 38/* Magic to divine the 'm' and 'v' limits */ 39 40#ifdef RLIMIT_AS 41#if !defined(RLIMIT_VMEM) || (RLIMIT_VMEM == RLIMIT_AS) || \ 42 !defined(RLIMIT_RSS) || (RLIMIT_VMEM == RLIMIT_RSS) 43#define ULIMIT_V_IS_AS 44#elif defined(RLIMIT_VMEM) 45#if !defined(RLIMIT_RSS) || (RLIMIT_RSS == RLIMIT_AS) 46#define ULIMIT_V_IS_AS 47#else 48#define ULIMIT_V_IS_VMEM 49#endif 50#endif 51#endif 52 53#ifdef RLIMIT_RSS 54#ifdef ULIMIT_V_IS_VMEM 55#define ULIMIT_M_IS_RSS 56#elif defined(RLIMIT_VMEM) && (RLIMIT_VMEM == RLIMIT_RSS) 57#define ULIMIT_M_IS_VMEM 58#else 59#define ULIMIT_M_IS_RSS 60#endif 61#if defined(ULIMIT_M_IS_RSS) && defined(RLIMIT_AS) && \ 62 !defined(__APPLE__) && (RLIMIT_RSS == RLIMIT_AS) 63/* On Mac OSX keep -m as -v alias for pkgsrc and other software expecting it */ 64#undef ULIMIT_M_IS_RSS 65#endif 66#endif 67 68#if !defined(RLIMIT_AS) && !defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_VMEM) 69#define ULIMIT_V_IS_VMEM 70#endif 71 72#if !defined(ULIMIT_V_IS_VMEM) && defined(RLIMIT_VMEM) && \ 73 (!defined(RLIMIT_RSS) || (defined(RLIMIT_AS) && (RLIMIT_RSS == RLIMIT_AS))) 74#define ULIMIT_M_IS_VMEM 75#endif 76 77#if defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_AS) && \ 78 (RLIMIT_VMEM == RLIMIT_AS) 79#undef ULIMIT_M_IS_VMEM 80#endif 81 82#if defined(ULIMIT_M_IS_RSS) && defined(ULIMIT_M_IS_VMEM) 83# error nonsensical m ulimit 84#endif 85 86#if defined(ULIMIT_V_IS_VMEM) && defined(ULIMIT_V_IS_AS) 87# error nonsensical v ulimit 88#endif 89 90#define LIMITS_GEN "rlimits.gen" 91 92#else /* !HAVE_RLIMIT */ 93 94#undef RLIMIT_CORE /* just in case */ 95 96#if defined(UL_GETFSIZE) 97#define KSH_UL_GFIL UL_GETFSIZE 98#elif defined(UL_GFILLIM) 99#define KSH_UL_GFIL UL_GFILLIM 100#elif defined(__A_UX__) || defined(KSH_ULIMIT2_TEST) 101#define KSH_UL_GFIL 1 102#endif 103 104#if defined(UL_SETFSIZE) 105#define KSH_UL_SFIL UL_SETFSIZE 106#elif defined(UL_SFILLIM) 107#define KSH_UL_SFIL UL_SFILLIM 108#elif defined(__A_UX__) || defined(KSH_ULIMIT2_TEST) 109#define KSH_UL_SFIL 2 110#endif 111 112#if defined(KSH_UL_SFIL) 113#define KSH_UL_WFIL true 114#else 115#define KSH_UL_WFIL false 116#define KSH_UL_SFIL 0 117#endif 118 119#if defined(UL_GETMAXBRK) 120#define KSH_UL_GBRK UL_GETMAXBRK 121#elif defined(UL_GMEMLIM) 122#define KSH_UL_GBRK UL_GMEMLIM 123#elif defined(__A_UX__) || defined(KSH_ULIMIT2_TEST) 124#define KSH_UL_GBRK 3 125#endif 126 127#if defined(UL_GDESLIM) 128#define KSH_UL_GDES UL_GDESLIM 129#elif defined(__GLIBC__) || defined(KSH_ULIMIT2_TEST) 130#define KSH_UL_GDES 4 131#endif 132 133extern char etext; 134extern long ulimit(int, long); 135 136#define LIMITS_GEN "ulimits.gen" 137 138#endif /* !HAVE_RLIMIT */ 139 140struct limits { 141 /* limit resource / read command */ 142 int resource; 143#if HAVE_RLIMIT 144 /* multiply by to get rlim_{cur,max} values */ 145 unsigned int factor; 146#else 147 /* write command */ 148 int wesource; 149 /* writable? */ 150 bool writable; 151#endif 152 /* getopts char */ 153 char optchar; 154 /* limit name */ 155 char name[1]; 156}; 157 158#define RLIMITS_DEFNS 159#if HAVE_RLIMIT 160#define FN(lname,lid,lfac,lopt) \ 161 static const struct { \ 162 int resource; \ 163 unsigned int factor; \ 164 char optchar; \ 165 char name[sizeof(lname)]; \ 166 } rlimits_ ## lid = { \ 167 lid, lfac, lopt, lname \ 168 }; 169#else 170#define FN(lname,lg,ls,lw,lopt) \ 171 static const struct { \ 172 int rcmd; \ 173 int wcmd; \ 174 bool writable; \ 175 char optchar; \ 176 char name[sizeof(lname)]; \ 177 } rlimits_ ## lg = { \ 178 lg, ls, lw, lopt, lname \ 179 }; 180#endif 181#include LIMITS_GEN 182 183static void print_ulimit(const struct limits *, int); 184static int set_ulimit(const struct limits *, const char *, int); 185 186static const struct limits * const rlimits[] = { 187#define RLIMITS_ITEMS 188#include LIMITS_GEN 189}; 190 191static const char rlimits_opts[] = 192#define RLIMITS_OPTCS 193#include LIMITS_GEN 194#ifndef RLIMIT_CORE 195 "c" 196#endif 197 ; 198 199int 200c_ulimit(const char **wp) 201{ 202 size_t i = 0; 203 int how = SOFT | HARD, optc; 204 char what = 'f'; 205 bool all = false; 206 207 while ((optc = ksh_getopt(wp, &builtin_opt, rlimits_opts)) != -1) 208 switch (optc) { 209 case ORD('H'): 210 how = HARD; 211 break; 212 case ORD('S'): 213 how = SOFT; 214 break; 215 case ORD('a'): 216 all = true; 217 break; 218 case ORD('?'): 219 bi_errorf("usage: ulimit [-%s] [value]", rlimits_opts); 220 return (1); 221 default: 222 what = optc; 223 } 224 225 while (i < NELEM(rlimits)) { 226 if (rlimits[i]->optchar == what) 227 goto found; 228 ++i; 229 } 230#ifndef RLIMIT_CORE 231 if (what == ORD('c')) 232 /* silently accept */ 233 return 0; 234#endif 235 internal_warningf("ulimit: %c", what); 236 return (1); 237 found: 238 if (wp[builtin_opt.optind]) { 239 if (all || wp[builtin_opt.optind + 1]) { 240 bi_errorf(Ttoo_many_args); 241 return (1); 242 } 243 return (set_ulimit(rlimits[i], wp[builtin_opt.optind], how)); 244 } 245 if (!all) 246 print_ulimit(rlimits[i], how); 247 else for (i = 0; i < NELEM(rlimits); ++i) { 248 shprintf("-%c: %-20s ", rlimits[i]->optchar, rlimits[i]->name); 249 print_ulimit(rlimits[i], how); 250 } 251 return (0); 252} 253 254#if HAVE_RLIMIT 255#define RL_T rlim_t 256#define RL_U (rlim_t)RLIM_INFINITY 257#else 258#define RL_T long 259#define RL_U LONG_MAX 260#endif 261 262static int 263set_ulimit(const struct limits *l, const char *v, int how MKSH_A_UNUSED) 264{ 265 RL_T val = (RL_T)0; 266#if HAVE_RLIMIT 267 struct rlimit limit; 268#endif 269 270 if (strcmp(v, "unlimited") == 0) 271 val = RL_U; 272 else { 273 mksh_uari_t rval; 274 275 if (!evaluate(v, (mksh_ari_t *)&rval, KSH_RETURN_ERROR, false)) 276 return (1); 277 /* 278 * Avoid problems caused by typos that evaluate misses due 279 * to evaluating unset parameters to 0... 280 * If this causes problems, will have to add parameter to 281 * evaluate() to control if unset params are 0 or an error. 282 */ 283 if (!rval && !ctype(v[0], C_DIGIT)) { 284 bi_errorf("invalid %s limit: %s", l->name, v); 285 return (1); 286 } 287#if HAVE_RLIMIT 288 val = (rlim_t)((rlim_t)rval * l->factor); 289#else 290 val = (RL_T)rval; 291#endif 292 } 293 294#if HAVE_RLIMIT 295 if (getrlimit(l->resource, &limit) < 0) { 296#ifndef MKSH_SMALL 297 bi_errorf("limit %s could not be read, contact the mksh developers: %s", 298 l->name, cstrerror(errno)); 299#endif 300 /* some can't be read */ 301 limit.rlim_cur = RLIM_INFINITY; 302 limit.rlim_max = RLIM_INFINITY; 303 } 304 if (how & SOFT) 305 limit.rlim_cur = val; 306 if (how & HARD) 307 limit.rlim_max = val; 308 if (!setrlimit(l->resource, &limit)) 309 return (0); 310#else 311 if (l->writable == false) { 312 /* check.t:ulimit-2 fails if we return 1 and/or do: 313 bi_errorf(Tf_ro, l->name); 314 */ 315 return (0); 316 } 317 if (ulimit(l->wesource, val) != -1L) 318 return (0); 319#endif 320 if (errno == EPERM) 321 bi_errorf("%s exceeds allowable %s limit", v, l->name); 322 else 323 bi_errorf("bad %s limit: %s", l->name, cstrerror(errno)); 324 return (1); 325} 326 327static void 328print_ulimit(const struct limits *l, int how MKSH_A_UNUSED) 329{ 330 RL_T val = (RL_T)0; 331#if HAVE_RLIMIT 332 struct rlimit limit; 333 334 if (getrlimit(l->resource, &limit)) 335#else 336 if ((val = ulimit(l->resource, 0)) < 0) 337#endif 338 { 339 shf_puts("unknown\n", shl_stdout); 340 return; 341 } 342#if HAVE_RLIMIT 343 if (how & SOFT) 344 val = limit.rlim_cur; 345 else if (how & HARD) 346 val = limit.rlim_max; 347#endif 348 if (val == RL_U) 349 shf_puts("unlimited\n", shl_stdout); 350 else { 351#if HAVE_RLIMIT 352 val /= l->factor; 353#elif defined(KSH_UL_GBRK) 354 if (l->resource == KSH_UL_GBRK) 355 val = (RL_T)(((size_t)val - (size_t)&etext) / 356 (size_t)1024); 357#endif 358 shprintf("%lu\n", (unsigned long)val); 359 } 360} 361