Return-Path: Delivered-To: apmail-incubator-stdcxx-dev-archive@www.apache.org Received: (qmail 75775 invoked from network); 22 Aug 2006 21:29:14 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 22 Aug 2006 21:29:14 -0000 Received: (qmail 59159 invoked by uid 500); 22 Aug 2006 21:29:14 -0000 Delivered-To: apmail-incubator-stdcxx-dev-archive@incubator.apache.org Received: (qmail 59099 invoked by uid 500); 22 Aug 2006 21:29:14 -0000 Mailing-List: contact stdcxx-dev-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: stdcxx-dev@incubator.apache.org Delivered-To: mailing list stdcxx-dev@incubator.apache.org Received: (qmail 59086 invoked by uid 99); 22 Aug 2006 21:29:14 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 22 Aug 2006 14:29:14 -0700 X-ASF-Spam-Status: No, hits=0.0 required=10.0 tests= X-Spam-Check-By: apache.org Received-SPF: neutral (asf.osuosl.org: local policy) Received: from [208.30.140.160] (HELO moroha.quovadx.com) (208.30.140.160) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 22 Aug 2006 14:29:10 -0700 Received: from [10.70.3.48] ([10.70.3.48]) by moroha.quovadx.com (8.13.6/8.13.4) with ESMTP id k7MLSgwx009364 for ; Tue, 22 Aug 2006 21:28:42 GMT Message-ID: <44EB7711.30706@roguewave.com> Date: Tue, 22 Aug 2006 15:28:49 -0600 From: Andrew Black User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.5) Gecko/20060720 SeaMonkey/1.0.3 MIME-Version: 1.0 To: stdcxx-dev@incubator.apache.org Subject: [patch] exec utility child process limits (unix) Content-Type: multipart/mixed; boundary="------------000307030709030007010308" X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N --------------000307030709030007010308 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Greetings all. Attached is a patch that enables the exec utility to place limits on its child process, using the setrlimit() command. These changes are based on the _rw_setopt_ulimit function in tests/src/driver.cpp. It should be noted that the sys/resource.h header is an XSI extension to the POSIX standard, so it may not be possible to rely on this header being present, though an assumption to this effect is made in this patch and in tests/src/driver.cpp. Log: * cmdopt.h [!_WIN32 && !_WIN64]: Include sys/resource.h. [!_WIN32 && !_WIN64 && (RLIMIT_CORE || RLIMIT_CPU || RLIMIT_DATA)]: Define HAS_RLIMIT, define struct limit_set, declare child_limits. * cmdopt.c [!_WIN32 && !_WIN64]: Include sys/resource.h, define RLIM_SAVED_CUR and RLIM_SAVED_MAX if not defined. [HAS_RLIMIT]: Initialize child_limits. usage_text[] [HAS_RLIMIT]: Document --ulimit switch. [HAS_RLIMIT] parse_limit_opts: Define helper function for parsing --rlimit option (borrowed in part from _rw_setopt_ulimit in tests/src/driver.cpp). eval_options() [HAS_RLIMIT]: Define opt_ulimit character string, use with parse_limit_opts to handle --ulimit command line switch. * exec.cpp [!_WIN32 && !_WIN64 && HAS_RLIMIT] LIMIT: Define helper macro for... [!_WIN32 && !_WIN64 && HAS_RLIMIT] limit_process(): New helper function to set resource limits, based on the values in child_limits (borrowed in part from _rw_setopt_ulimit in tests/src/driver.cpp). [!_WIN32 && !_WIN64 && HAS_RLIMIT] exec_file(): Call above prior to execv. --------------000307030709030007010308 Content-Type: text/x-patch; name="ulimit.diff" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="ulimit.diff" Index: cmdopt.h =================================================================== --- cmdopt.h (revision 433677) +++ cmdopt.h (working copy) @@ -27,6 +27,10 @@ #ifndef RW_PARSE_OPTS_H #define RW_PARSE_OPTS_H +#if !defined (_WIN32) && !defined (_WIN64) +# include /* for rlim_t, RLIMIT_CORE, ... */ +#endif + extern int timeout; extern int compat; extern unsigned verbose; /**< Verbose output mode switch. Defaults to 0 */ @@ -39,6 +43,21 @@ extern const char suffix_sep; /**< File suffix seperator. */ extern const size_t exe_suffix_len; /**< Length of executable suffix. */ +#if !defined (_WIN32) && !defined (_WIN64) && \ + (defined (RLIMIT_CORE) || defined (RLIMIT_CPU) || defined (RLIMIT_DATA)) +# define HAS_RLIMIT +extern struct limit_set { + struct rlimit core; + struct rlimit cpu; + struct rlimit data; + struct rlimit fsize; + struct rlimit nofile; + struct rlimit stack; + struct rlimit mem; + struct rlimit as; +} child_limits; /**< Container holding child process limits. */ +#endif + void show_usage (int status); Index: cmdopt.cpp =================================================================== --- cmdopt.cpp (revision 433677) +++ cmdopt.cpp (working copy) @@ -37,6 +37,15 @@ #include /* for str* */ #if !defined (_WIN32) && !defined (_WIN64) # include /* for sleep */ +# include /* for setlimit(), RLIMIT_CORE, ... */ + +# ifndef RLIM_SAVED_CUR +# define RLIM_SAVED_CUR RLIM_INFINITY +# endif // RLIM_SAVED_CUR + +# ifndef RLIM_SAVED_MAX +# define RLIM_SAVED_MAX RLIM_INFINITY +# endif // RLIM_SAVED_MAX #else # include /* for Sleep */ #endif /* _WIN{32,64} */ @@ -66,6 +75,19 @@ const size_t exe_suffix_len = 4; /* strlen(".exe") == 4 */ #endif +#ifdef HAS_RLIMIT +struct limit_set child_limits = { + { RLIM_SAVED_CUR, RLIM_SAVED_MAX }, + { RLIM_SAVED_CUR, RLIM_SAVED_MAX }, + { RLIM_SAVED_CUR, RLIM_SAVED_MAX }, + { RLIM_SAVED_CUR, RLIM_SAVED_MAX }, + { RLIM_SAVED_CUR, RLIM_SAVED_MAX }, + { RLIM_SAVED_CUR, RLIM_SAVED_MAX }, + { RLIM_SAVED_CUR, RLIM_SAVED_MAX }, + { RLIM_SAVED_CUR, RLIM_SAVED_MAX } +}; +#endif /* HAS_RLIMIT */ + static const char usage_text[] = { "Usage: %s [OPTIONS] [targets]\n" @@ -91,12 +113,53 @@ " --sleep=sec Sleep for the specified number of seconds.\n" " --signal=sig Send itself the specified signal.\n" " --ignore=sig Ignore the specified signal.\n" +#ifdef HAS_RLIMIT + " --ulimit=lim Set child process usage limits (see below).\n" +#endif /* HAS_RLIMIT */ "\n" " All short (single dash) options must be specified seperately.\n" " If a short option takes a value, it may either be provided like\n" " '-sval' or '-s val'.\n" " If a long option take a value, it may either be provided like\n" " '--option=value' or '--option value'.\n" +#ifdef HAS_RLIMIT + "\n" + " --ulimit sets limits on how much of a given resource or resorces\n" + " child processes are allowed to utilize. These limits take on two\n" + " forms, 'soft' and 'hard' limits. Options are specified in the form\n" + " 'resource:limit', where resource is a resource named below, and and\n" + " limit is a number, with value specifing the limit for the named\n" + " resource. If multiple limits are to be set, they can be specified\n" + " either in multiple instances of the --ulimit switch, or by specifying\n" + " additional limits in the same call by seperating the pairs with\n" + " commas. 'Soft' limits are specified by providing the resource name\n" + " in lowercase letters, while 'hard' limits are specified by providing\n" + " the resource name in uppercase letters. To set both limits, specify\n" + " the resource name in title case.\n" + "\n" + " --limit modes:\n" +#ifdef RLIMIT_CORE + " core Maximum size of core file, in bytes.\n" +#endif // RLIMIT_CORE +#ifdef RLIMIT_CPU + " cpu Maximum CPU time, in seconds.\n" +#endif // RLIMIT_CPU +#ifdef RLIMIT_DATA + " data Maximum data segment size, in bytes.\n" +#endif // RLIMIT_DATA +#ifdef RLIMIT_FSIZE + " fsize Maximum size of generated files, in bytes.\n" +#endif // RLIMIT_FSIZE +#ifdef RLIMIT_NOFILE + " nofile Maximum number of open file descriptors.\n" +#endif // RLIMIT_NOFILE +#ifdef RLIMIT_STACK + " stack Maximum size of initial thread's stack, in bytes." +#endif // RLIMIT_STACK +#ifdef RLIMIT_AS + " as Maximum size of available memory, in bytes." +#endif // RLIMIT_AS +#endif /* HAS_RLIMIT */ }; #if !defined (_WIN32) && !defined (_WIN64) @@ -200,7 +263,100 @@ return (char*)0; } +#ifdef HAS_RLIMIT /** + Helper function to parse a ulimit value string + + @param opts ulimit value string to pares + @see child_limits +*/ +static bool +parse_limit_opts (const char* opts) +{ + static const struct { + struct rlimit* limit; + const char* name; + const char* caps; + const char* mixd; + size_t len; + } limits[] = { +#ifdef RLIMIT_CORE + { &child_limits.core, "core", "CORE", "Core", 4 }, +#endif // RLIMIT_CORE +#ifdef RLIMIT_CPU + { &child_limits.cpu, "cpu", "CPU", "Cpu", 3 }, +#endif // RLIMIT_CPU +#ifdef RLIMIT_DATA + { &child_limits.data, "data", "DATA", "Data", 4 }, +#endif // RLIMIT_DATA +#ifdef RLIMIT_FSIZE + { &child_limits.fsize, "fsize", "FSIZE", "Fsize", 5 }, +#endif // RLIMIT_FSIZE +#ifdef RLIMIT_NOFILE + { &child_limits.nofile, "nofile", "NOFILE", "Nofile", 6 }, +#endif // RLIMIT_NOFILE +#ifdef RLIMIT_STACK + { &child_limits.stack, "stack", "STACK", "Stack", 5 }, +#endif // RLIMIT_STACK +#ifdef RLIMIT_AS + { &child_limits.as, "as", "AS", "As", 2 }, +#endif // RLIMIT_AS + { 0, 0, 0, 0, 0 } + }; + + const char* arg = opts; + + assert (0 != opts); + + while (arg && *arg) { + + const size_t arglen = strlen (arg); + + for (size_t i = 0; limits [i].name; ++i) { + if ( limits [i].len < arglen + && ( 0 == memcmp (limits [i].name, arg, limits [i].len) + || 0 == memcmp (limits [i].caps, arg, limits [i].len) + || 0 == memcmp (limits [i].mixd, arg, limits [i].len)) + && ':' == arg [limits [i].len]) { + + // determine whether the hard limit and/or + // the soft limit should be set + const bool hard = isupper (arg [0]); + const bool soft = islower (arg [1]); + + arg += limits [i].len + 1; + + char *end; + const long lim = strtol (arg, &end, 10); + + arg = end; + + if ('\0' != *arg && ',' != *arg) + break; + + if (soft) + limits [i].limit->rlim_cur = lim; + + if (hard) + limits [i].limit->rlim_max = lim; + + break; + } + } + + if (',' == *arg) { + ++arg; + } + else if ('\0' != *arg) { + return 1; + } + } + + return 0; +} +#endif /* HAS_RLIMIT */ + +/** Helper function to produce 'Bad argument' error message. @param opt name of option encountered @@ -266,6 +422,9 @@ const char opt_nocompat[] = "--nocompat"; const char opt_signal[] = "--signal"; const char opt_sleep[] = "--sleep"; +#ifdef HAS_RLIMIT + const char opt_ulimit[] = "--ulimit"; +#endif int i; @@ -397,7 +556,18 @@ } } } - +#ifdef HAS_RLIMIT + else if ( sizeof opt_ulimit <= arglen + && !memcmp (opt_ulimit, argv [i], sizeof opt_ulimit - 1)) { + optname = opt_ulimit; + optarg = get_long_val (argv, &i, sizeof opt_ulimit); + if (optarg && *optarg) { + if (!parse_limit_opts (optarg)) { + break; + } + } + } +#endif /* fall through */ } default: Index: exec.cpp =================================================================== --- exec.cpp (revision 433677) +++ exec.cpp (working copy) @@ -625,7 +625,71 @@ name, strerror (errno)); } +#ifdef HAS_RLIMIT /** + Utility macro to generate an rlimit tuple. + + @parm val 'short' resource name (no leading RLIMIT_). + @param idx limit structure name in child_limits global + @see limit_process + @see child_limits +*/ +#undef LIMIT +#define LIMIT(val, idx) { RLIMIT_ ## val, &child_limits.idx, #val } + +/** + Set process resource limits, based on the child_limits global. + + This method uses the LIMIT macro to build an internal array of limits + to try setting. If setrlimit fails +*/ +static void +limit_process () +{ + static const struct { + int resource; + struct rlimit* limit; + const char* name; + } limits[] = { +#ifdef RLIMIT_CORE + LIMIT (CORE, core), +#endif // RLIMIT_CORE +#ifdef RLIMIT_CPU + LIMIT (CPU, cpu), +#endif // RLIMIT_CPU +#ifdef RLIMIT_DATA + LIMIT (DATA, data), +#endif // RLIMIT_DATA +#ifdef RLIMIT_FSIZE + LIMIT (FSIZE, fsize), +#endif // RLIMIT_FSIZE +#ifdef RLIMIT_NOFILE + LIMIT (NOFILE, nofile), +#endif // RLIMIT_NOFILE +#ifdef RLIMIT_STACK + LIMIT (STACK, stack), +#endif // RLIMIT_STACK +#ifdef RLIMIT_AS + LIMIT (AS, as), +#endif // RLIMIT_AS + { 0, 0, 0 } + }; + + for (size_t i = 0; limits [i].limit; ++i) { + struct rlimit local; + + memcpy (&local, limits [i].limit, sizeof (struct rlimit)); + + if (setrlimit (limits [i].resource, &local)) { + warn ("error setting process limits for %s (soft: %lu, hard: " + "%lu): %s\n", limits [i].name, local.rlim_cur, + local.rlim_max, strerror (errno)); + } + } +} +#endif /* HAS_RLIMIT */ + +/** Entry point to the child process (watchdog) subsystem. This method fork ()s, creating a child process. This child process becomes @@ -701,6 +765,10 @@ terminate (1, "Redirection of stderr to stdout failed: %s\n", strerror (errno)); +#ifdef HAS_RLIMIT + limit_process (); +#endif + execv (argv [0], argv); fprintf (error_file, "%s (%s): execv (\"%s\", ...) error: %s\n", --------------000307030709030007010308--