| /* $OpenBSD: c_ulimit.c,v 1.19 2013/11/28 10:33:37 sobrado Exp $ */ |
| |
| /*- |
| * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, |
| * 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, |
| * 2019, 2020 |
| * mirabilos <[email protected]> |
| * |
| * Provided that these terms and disclaimer and all copyright notices |
| * are retained or reproduced in an accompanying document, permission |
| * is granted to deal in this work without restriction, including un- |
| * limited rights to use, publicly perform, distribute, sell, modify, |
| * merge, give away, or sublicence. |
| * |
| * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to |
| * the utmost extent permitted by applicable law, neither express nor |
| * implied; without malicious intent or gross negligence. In no event |
| * may a licensor, author or contributor be held liable for indirect, |
| * direct, other damage, loss, or other issues arising in any way out |
| * of dealing in the work, even if advised of the possibility of such |
| * damage or existence of a defect, except proven that it results out |
| * of said person's immediate fault when using the work as intended. |
| */ |
| |
| #include "sh.h" |
| |
| __RCSID("$MirOS: src/bin/mksh/ulimit.c,v 1.3 2020/07/24 21:08:26 tg Exp $"); |
| |
| #define SOFT 0x1 |
| #define HARD 0x2 |
| |
| #if HAVE_RLIMIT |
| |
| #if !HAVE_RLIM_T |
| typedef unsigned long rlim_t; |
| #endif |
| |
| /* Magic to divine the 'm' and 'v' limits */ |
| |
| #ifdef RLIMIT_AS |
| #if !defined(RLIMIT_VMEM) || (RLIMIT_VMEM == RLIMIT_AS) || \ |
| !defined(RLIMIT_RSS) || (RLIMIT_VMEM == RLIMIT_RSS) |
| #define ULIMIT_V_IS_AS |
| #elif defined(RLIMIT_VMEM) |
| #if !defined(RLIMIT_RSS) || (RLIMIT_RSS == RLIMIT_AS) |
| #define ULIMIT_V_IS_AS |
| #else |
| #define ULIMIT_V_IS_VMEM |
| #endif |
| #endif |
| #endif |
| |
| #ifdef RLIMIT_RSS |
| #ifdef ULIMIT_V_IS_VMEM |
| #define ULIMIT_M_IS_RSS |
| #elif defined(RLIMIT_VMEM) && (RLIMIT_VMEM == RLIMIT_RSS) |
| #define ULIMIT_M_IS_VMEM |
| #else |
| #define ULIMIT_M_IS_RSS |
| #endif |
| #if defined(ULIMIT_M_IS_RSS) && defined(RLIMIT_AS) && \ |
| !defined(__APPLE__) && (RLIMIT_RSS == RLIMIT_AS) |
| /* On Mac OSX keep -m as -v alias for pkgsrc and other software expecting it */ |
| #undef ULIMIT_M_IS_RSS |
| #endif |
| #endif |
| |
| #if !defined(RLIMIT_AS) && !defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_VMEM) |
| #define ULIMIT_V_IS_VMEM |
| #endif |
| |
| #if !defined(ULIMIT_V_IS_VMEM) && defined(RLIMIT_VMEM) && \ |
| (!defined(RLIMIT_RSS) || (defined(RLIMIT_AS) && (RLIMIT_RSS == RLIMIT_AS))) |
| #define ULIMIT_M_IS_VMEM |
| #endif |
| |
| #if defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_AS) && \ |
| (RLIMIT_VMEM == RLIMIT_AS) |
| #undef ULIMIT_M_IS_VMEM |
| #endif |
| |
| #if defined(ULIMIT_M_IS_RSS) && defined(ULIMIT_M_IS_VMEM) |
| # error nonsensical m ulimit |
| #endif |
| |
| #if defined(ULIMIT_V_IS_VMEM) && defined(ULIMIT_V_IS_AS) |
| # error nonsensical v ulimit |
| #endif |
| |
| #define LIMITS_GEN "rlimits.gen" |
| |
| #else /* !HAVE_RLIMIT */ |
| |
| #undef RLIMIT_CORE /* just in case */ |
| |
| #if defined(UL_GETFSIZE) |
| #define KSH_UL_GFIL UL_GETFSIZE |
| #elif defined(UL_GFILLIM) |
| #define KSH_UL_GFIL UL_GFILLIM |
| #elif defined(__A_UX__) || defined(KSH_ULIMIT2_TEST) |
| #define KSH_UL_GFIL 1 |
| #endif |
| |
| #if defined(UL_SETFSIZE) |
| #define KSH_UL_SFIL UL_SETFSIZE |
| #elif defined(UL_SFILLIM) |
| #define KSH_UL_SFIL UL_SFILLIM |
| #elif defined(__A_UX__) || defined(KSH_ULIMIT2_TEST) |
| #define KSH_UL_SFIL 2 |
| #endif |
| |
| #if defined(KSH_UL_SFIL) |
| #define KSH_UL_WFIL true |
| #else |
| #define KSH_UL_WFIL false |
| #define KSH_UL_SFIL 0 |
| #endif |
| |
| #if defined(UL_GETMAXBRK) |
| #define KSH_UL_GBRK UL_GETMAXBRK |
| #elif defined(UL_GMEMLIM) |
| #define KSH_UL_GBRK UL_GMEMLIM |
| #elif defined(__A_UX__) || defined(KSH_ULIMIT2_TEST) |
| #define KSH_UL_GBRK 3 |
| #endif |
| |
| #if defined(UL_GDESLIM) |
| #define KSH_UL_GDES UL_GDESLIM |
| #elif defined(__GLIBC__) || defined(KSH_ULIMIT2_TEST) |
| #define KSH_UL_GDES 4 |
| #endif |
| |
| extern char etext; |
| extern long ulimit(int, long); |
| |
| #define LIMITS_GEN "ulimits.gen" |
| |
| #endif /* !HAVE_RLIMIT */ |
| |
| struct limits { |
| /* limit resource / read command */ |
| int resource; |
| #if HAVE_RLIMIT |
| /* multiply by to get rlim_{cur,max} values */ |
| unsigned int factor; |
| #else |
| /* write command */ |
| int wesource; |
| /* writable? */ |
| bool writable; |
| #endif |
| /* getopts char */ |
| char optchar; |
| /* limit name */ |
| char name[1]; |
| }; |
| |
| #define RLIMITS_DEFNS |
| #if HAVE_RLIMIT |
| #define FN(lname,lid,lfac,lopt) \ |
| static const struct { \ |
| int resource; \ |
| unsigned int factor; \ |
| char optchar; \ |
| char name[sizeof(lname)]; \ |
| } rlimits_ ## lid = { \ |
| lid, lfac, lopt, lname \ |
| }; |
| #else |
| #define FN(lname,lg,ls,lw,lopt) \ |
| static const struct { \ |
| int rcmd; \ |
| int wcmd; \ |
| bool writable; \ |
| char optchar; \ |
| char name[sizeof(lname)]; \ |
| } rlimits_ ## lg = { \ |
| lg, ls, lw, lopt, lname \ |
| }; |
| #endif |
| #include LIMITS_GEN |
| |
| static void print_ulimit(const struct limits *, int); |
| static int set_ulimit(const struct limits *, const char *, int); |
| |
| static const struct limits * const rlimits[] = { |
| #define RLIMITS_ITEMS |
| #include LIMITS_GEN |
| }; |
| |
| static const char rlimits_opts[] = |
| #define RLIMITS_OPTCS |
| #include LIMITS_GEN |
| #ifndef RLIMIT_CORE |
| "c" |
| #endif |
| ; |
| |
| int |
| c_ulimit(const char **wp) |
| { |
| size_t i = 0; |
| int how = SOFT | HARD, optc; |
| char what = 'f'; |
| bool all = false; |
| |
| while ((optc = ksh_getopt(wp, &builtin_opt, rlimits_opts)) != -1) |
| switch (optc) { |
| case ORD('H'): |
| how = HARD; |
| break; |
| case ORD('S'): |
| how = SOFT; |
| break; |
| case ORD('a'): |
| all = true; |
| break; |
| case ORD('?'): |
| bi_errorf("usage: ulimit [-%s] [value]", rlimits_opts); |
| return (1); |
| default: |
| what = optc; |
| } |
| |
| while (i < NELEM(rlimits)) { |
| if (rlimits[i]->optchar == what) |
| goto found; |
| ++i; |
| } |
| #ifndef RLIMIT_CORE |
| if (what == ORD('c')) |
| /* silently accept */ |
| return 0; |
| #endif |
| internal_warningf("ulimit: %c", what); |
| return (1); |
| found: |
| if (wp[builtin_opt.optind]) { |
| if (all || wp[builtin_opt.optind + 1]) { |
| bi_errorf(Ttoo_many_args); |
| return (1); |
| } |
| return (set_ulimit(rlimits[i], wp[builtin_opt.optind], how)); |
| } |
| if (!all) |
| print_ulimit(rlimits[i], how); |
| else for (i = 0; i < NELEM(rlimits); ++i) { |
| shprintf("-%c: %-20s ", rlimits[i]->optchar, rlimits[i]->name); |
| print_ulimit(rlimits[i], how); |
| } |
| return (0); |
| } |
| |
| #if HAVE_RLIMIT |
| #define RL_T rlim_t |
| #define RL_U (rlim_t)RLIM_INFINITY |
| #else |
| #define RL_T long |
| #define RL_U LONG_MAX |
| #endif |
| |
| static int |
| set_ulimit(const struct limits *l, const char *v, int how MKSH_A_UNUSED) |
| { |
| RL_T val = (RL_T)0; |
| #if HAVE_RLIMIT |
| struct rlimit limit; |
| #endif |
| |
| if (strcmp(v, "unlimited") == 0) |
| val = RL_U; |
| else { |
| mksh_uari_t rval; |
| |
| if (!evaluate(v, (mksh_ari_t *)&rval, KSH_RETURN_ERROR, false)) |
| return (1); |
| /* |
| * Avoid problems caused by typos that evaluate misses due |
| * to evaluating unset parameters to 0... |
| * If this causes problems, will have to add parameter to |
| * evaluate() to control if unset params are 0 or an error. |
| */ |
| if (!rval && !ctype(v[0], C_DIGIT)) { |
| bi_errorf("invalid %s limit: %s", l->name, v); |
| return (1); |
| } |
| #if HAVE_RLIMIT |
| val = (rlim_t)((rlim_t)rval * l->factor); |
| #else |
| val = (RL_T)rval; |
| #endif |
| } |
| |
| #if HAVE_RLIMIT |
| if (getrlimit(l->resource, &limit) < 0) { |
| #ifndef MKSH_SMALL |
| bi_errorf("limit %s could not be read, contact the mksh developers: %s", |
| l->name, cstrerror(errno)); |
| #endif |
| /* some can't be read */ |
| limit.rlim_cur = RLIM_INFINITY; |
| limit.rlim_max = RLIM_INFINITY; |
| } |
| if (how & SOFT) |
| limit.rlim_cur = val; |
| if (how & HARD) |
| limit.rlim_max = val; |
| if (!setrlimit(l->resource, &limit)) |
| return (0); |
| #else |
| if (l->writable == false) { |
| /* check.t:ulimit-2 fails if we return 1 and/or do: |
| bi_errorf(Tf_ro, l->name); |
| */ |
| return (0); |
| } |
| if (ulimit(l->wesource, val) != -1L) |
| return (0); |
| #endif |
| if (errno == EPERM) |
| bi_errorf("%s exceeds allowable %s limit", v, l->name); |
| else |
| bi_errorf("bad %s limit: %s", l->name, cstrerror(errno)); |
| return (1); |
| } |
| |
| static void |
| print_ulimit(const struct limits *l, int how MKSH_A_UNUSED) |
| { |
| RL_T val = (RL_T)0; |
| #if HAVE_RLIMIT |
| struct rlimit limit; |
| |
| if (getrlimit(l->resource, &limit)) |
| #else |
| if ((val = ulimit(l->resource, 0)) < 0) |
| #endif |
| { |
| shf_puts("unknown\n", shl_stdout); |
| return; |
| } |
| #if HAVE_RLIMIT |
| if (how & SOFT) |
| val = limit.rlim_cur; |
| else if (how & HARD) |
| val = limit.rlim_max; |
| #endif |
| if (val == RL_U) |
| shf_puts("unlimited\n", shl_stdout); |
| else { |
| #if HAVE_RLIMIT |
| val /= l->factor; |
| #elif defined(KSH_UL_GBRK) |
| if (l->resource == KSH_UL_GBRK) |
| val = (RL_T)(((size_t)val - (size_t)&etext) / |
| (size_t)1024); |
| #endif |
| shprintf("%lu\n", (unsigned long)val); |
| } |
| } |