/* This file is part of GNU Pies. Copyright (C) 2008-2019 Sergey Poznyakoff GNU Pies is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Pies is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Pies. If not, see . */ #include #define SET_LIMIT_AS 0x0001 #define SET_LIMIT_CPU 0x0002 #define SET_LIMIT_DATA 0x0004 #define SET_LIMIT_FSIZE 0x0008 #define SET_LIMIT_NPROC 0x0010 #define SET_LIMIT_CORE 0x0020 #define SET_LIMIT_MEMLOCK 0x0040 #define SET_LIMIT_NOFILE 0x0080 #define SET_LIMIT_RSS 0x0100 #define SET_LIMIT_STACK 0x0200 #define SET_LIMIT_LOGINS 0x0400 #define SET_LIMIT_PRIO 0x0800 struct limits_rec { unsigned set; rlim_t limit_as; rlim_t limit_cpu; rlim_t limit_data; rlim_t limit_fsize; rlim_t limit_nproc; rlim_t limit_core; rlim_t limit_memlock; rlim_t limit_nofile; rlim_t limit_rss; rlim_t limit_stack; int limit_logins; int limit_prio; }; int do_set_limit (int rlimit, rlim_t limit) { struct rlimit rlim; debug (1, ("setting limit %d to %lu", rlimit, (unsigned long) limit)); rlim.rlim_cur = limit; rlim.rlim_max = limit; if (setrlimit(rlimit, &rlim)) { //FIXME: arg? logfuncall ("setrlimit", NULL, errno); return 1; } return 0; } static int set_prio (int prio) { debug (2, ("Setting priority to %d", prio)); if (setpriority (PRIO_PROCESS, 0, prio)) { logfuncall ("setpriority", NULL, errno); return 1; } return 0; } /* Counts the number of user logins and check against the limit */ static int check_logins (const char *name, int limit) { logmsg (LOG_NOTICE, _("L limit is not implemented")); return 0; } int set_limits (const char *name, struct limits_rec *lrec) { int rc = 0; if (!lrec) return 0; debug (2, ("setting limits for %s", name)); #if defined(RLIMIT_AS) if (lrec->set & SET_LIMIT_AS) rc |= do_set_limit(RLIMIT_AS, lrec->limit_as); #endif #if defined(RLIMIT_CPU) if (lrec->set & SET_LIMIT_CPU) rc |= do_set_limit(RLIMIT_CPU, lrec->limit_cpu); #endif #if defined(RLIMIT_DATA) if (lrec->set & SET_LIMIT_DATA) rc |= do_set_limit(RLIMIT_DATA, lrec->limit_data); #endif #if defined(RLIMIT_FSIZE) if (lrec->set & SET_LIMIT_FSIZE) rc |= do_set_limit(RLIMIT_FSIZE, lrec->limit_fsize); #endif #if defined(RLIMIT_NPROC) if (lrec->set & SET_LIMIT_NPROC) rc |= do_set_limit(RLIMIT_NPROC, lrec->limit_nproc); #endif #if defined(RLIMIT_CORE) if (lrec->set & SET_LIMIT_CORE) rc |= do_set_limit(RLIMIT_CORE, lrec->limit_core); #endif #if defined(RLIMIT_MEMLOCK) if (lrec->set & SET_LIMIT_MEMLOCK) rc |= do_set_limit(RLIMIT_MEMLOCK, lrec->limit_memlock); #endif #if defined(RLIMIT_NOFILE) if (lrec->set & SET_LIMIT_NOFILE) rc |= do_set_limit(RLIMIT_NOFILE, lrec->limit_nofile); #endif #if defined(RLIMIT_RSS) if (lrec->set & SET_LIMIT_RSS) rc |= do_set_limit(RLIMIT_RSS, lrec->limit_rss); #endif #if defined(RLIMIT_STACK) if (lrec->set & SET_LIMIT_STACK) rc |= do_set_limit(RLIMIT_STACK, lrec->limit_stack); #endif if (lrec->set & SET_LIMIT_LOGINS) rc |= check_logins(name, lrec->limit_logins); if (lrec->set & SET_LIMIT_PRIO) rc |= set_prio(lrec->limit_logins); return rc; } int getlimit (char **ptr, rlim_t *rlim, int mul) { if (**ptr == '-') { *rlim = RLIM_INFINITY; ++*ptr; } else { unsigned long val; val = strtoul (*ptr, ptr, 10); if (val == 0) return 1; *rlim = val * mul; } return 0; } /* Parse limits string and fill appropriate fields in lrec. The string consists of _commands_, optionally separated by any amount of whitespace. A command has the following form: [AaCcDdFfMmNnRrSsTtUuLlPp](-|[0-9]+) i.e. a letter followed by number or a dash. The latters stands for 'unlimited'. Commands are interpreted as follows: Command ulimit setrlimit() The limit it sets option arg ------------------------------------------------------------- [Aa] a RLIMIT_AS max address space (KB) [Cc] c RLIMIT_CORE max core file size (KB) [Dd] d RLIMIT_DATA max data size (KB) [Ff] f RLIMIT_FSIZE Maximum filesize (KB) [Mm] m RLIMIT_MEMLOCK max locked-in-memory address space (KB) [Nn] n RLIMIT_NOFILE max number of open files [Rr] r RLIMIT_RSS max resident set size (KB) [Ss] s RLIMIT_STACK max stack size (KB) [Tt] t RLIMIT_CPU max CPU time (MIN) [Uu] u RLIMIT_NPROC max number of processes [Ll] l (none) max number of logins (N/A) [Pp] p (none) process priority -20..20 (negative = high priority) */ int parse_limits (limits_record_t *plrec, char *str, char **endp) { int c; struct limits_rec *lrec = grecs_malloc (sizeof (*lrec)); *plrec = lrec; lrec->set = 0; while ((c = *str++)) { if (c == ' ' || c == '\t') continue; switch (c) { case 'a': case 'A': /* RLIMIT_AS - max address space (KB) */ if (!getlimit (&str, &lrec->limit_as, 1024)) lrec->set |= SET_LIMIT_AS; break; case 't': case 'T': /* RLIMIT_CPU - max CPU time (MIN) */ if (!getlimit (&str, &lrec->limit_cpu, 60)) lrec->set |= SET_LIMIT_CPU; break; case 'd': case 'D': /* RLIMIT_DATA - max data size (KB) */ if (!getlimit (&str, &lrec->limit_data, 1024)) lrec->set |= SET_LIMIT_DATA; break; case 'f': case 'F': /* RLIMIT_FSIZE - Maximum filesize (KB) */ if (!getlimit (&str, &lrec->limit_fsize, 1024)) lrec->set |= SET_LIMIT_FSIZE; break; case 'u': case 'U': /* RLIMIT_NPROC - max number of processes */ if (!getlimit (&str, &lrec->limit_nproc, 1)) lrec->set |= SET_LIMIT_NPROC; break; case 'c': case 'C': /* RLIMIT_CORE - max core file size (KB) */ if (!getlimit (&str, &lrec->limit_core, 1024)) lrec->set |= SET_LIMIT_CORE; break; case 'm': case 'M': /* RLIMIT_MEMLOCK - max locked-in-memory * address space (KB) */ if (!getlimit (&str, &lrec->limit_memlock, 1024)) lrec->set |= SET_LIMIT_MEMLOCK; break; case 'n': case 'N': /* RLIMIT_NOFILE - max number of open files */ if (!getlimit (&str, &lrec->limit_nofile, 1)) lrec->set |= SET_LIMIT_NOFILE; break; case 'r': case 'R': /* RLIMIT_RSS - max resident set size (KB) */ if (!getlimit (&str, &lrec->limit_rss, 1024)) lrec->set |= SET_LIMIT_RSS; break; case 's': case 'S': /* RLIMIT_STACK - max stack size (KB) */ if (!getlimit (&str, &lrec->limit_stack, 1024)) lrec->set |= SET_LIMIT_STACK; break; case 'l': case 'L': lrec->limit_logins = strtol (str, &str, 10); if (lrec->limit_logins >= 0) lrec->set |= SET_LIMIT_LOGINS; break; case 'p': case 'P': lrec->limit_prio = strtol (str, &str, 10); if (lrec->limit_prio > 0) lrec->set |= SET_LIMIT_PRIO; break; default: *endp = str-1; return 1; } } return 0; } void free_limits (limits_record_t rec) { if (rec) free (rec); } int limits_cmp (limits_record_t a, limits_record_t b) { if (!a) return !!b; else if (!b) return 1; return memcmp (a, b, sizeof (*a)); }