111be35a1SLionel Sambuc // Copyright 2012 Google Inc.
211be35a1SLionel Sambuc // All rights reserved.
311be35a1SLionel Sambuc //
411be35a1SLionel Sambuc // Redistribution and use in source and binary forms, with or without
511be35a1SLionel Sambuc // modification, are permitted provided that the following conditions are
611be35a1SLionel Sambuc // met:
711be35a1SLionel Sambuc //
811be35a1SLionel Sambuc // * Redistributions of source code must retain the above copyright
911be35a1SLionel Sambuc // notice, this list of conditions and the following disclaimer.
1011be35a1SLionel Sambuc // * Redistributions in binary form must reproduce the above copyright
1111be35a1SLionel Sambuc // notice, this list of conditions and the following disclaimer in the
1211be35a1SLionel Sambuc // documentation and/or other materials provided with the distribution.
1311be35a1SLionel Sambuc // * Neither the name of Google Inc. nor the names of its contributors
1411be35a1SLionel Sambuc // may be used to endorse or promote products derived from this software
1511be35a1SLionel Sambuc // without specific prior written permission.
1611be35a1SLionel Sambuc //
1711be35a1SLionel Sambuc // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1811be35a1SLionel Sambuc // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1911be35a1SLionel Sambuc // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2011be35a1SLionel Sambuc // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2111be35a1SLionel Sambuc // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2211be35a1SLionel Sambuc // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2311be35a1SLionel Sambuc // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2411be35a1SLionel Sambuc // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2511be35a1SLionel Sambuc // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2611be35a1SLionel Sambuc // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2711be35a1SLionel Sambuc // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2811be35a1SLionel Sambuc
2911be35a1SLionel Sambuc #if defined(HAVE_CONFIG_H)
3011be35a1SLionel Sambuc # include "config.h"
3111be35a1SLionel Sambuc #endif
3211be35a1SLionel Sambuc
3311be35a1SLionel Sambuc #include "run.h"
3411be35a1SLionel Sambuc
3511be35a1SLionel Sambuc #include <sys/resource.h>
3611be35a1SLionel Sambuc #include <sys/stat.h>
3711be35a1SLionel Sambuc #include <sys/time.h>
3811be35a1SLionel Sambuc #include <sys/wait.h>
3911be35a1SLionel Sambuc
4011be35a1SLionel Sambuc #include <assert.h>
4111be35a1SLionel Sambuc #include <err.h>
4211be35a1SLionel Sambuc #include <errno.h>
4311be35a1SLionel Sambuc #include <fcntl.h>
4411be35a1SLionel Sambuc #include <signal.h>
4511be35a1SLionel Sambuc #include <stdbool.h>
4611be35a1SLionel Sambuc #include <stdio.h>
4711be35a1SLionel Sambuc #include <stdlib.h>
4811be35a1SLionel Sambuc #include <string.h>
4911be35a1SLionel Sambuc #include <unistd.h>
5011be35a1SLionel Sambuc
5111be35a1SLionel Sambuc #include "defs.h"
5211be35a1SLionel Sambuc #include "env.h"
5311be35a1SLionel Sambuc #include "error.h"
5411be35a1SLionel Sambuc #include "fs.h"
5511be35a1SLionel Sambuc #include "text.h"
5611be35a1SLionel Sambuc
5711be35a1SLionel Sambuc
5811be35a1SLionel Sambuc /// Path to the temporary work directory to use.
5911be35a1SLionel Sambuc const char* kyua_run_tmpdir = KYUA_TMPDIR;
6011be35a1SLionel Sambuc #undef KYUA_TMPDIR // We really want to use the variable, not the macro.
6111be35a1SLionel Sambuc
6211be35a1SLionel Sambuc
6311be35a1SLionel Sambuc /// Whether we got a signal or not.
6411be35a1SLionel Sambuc static volatile bool signal_fired = false;
6511be35a1SLionel Sambuc
6611be35a1SLionel Sambuc
6711be35a1SLionel Sambuc /// Whether the process timed out or not.
6811be35a1SLionel Sambuc static volatile bool process_timed_out = false;
6911be35a1SLionel Sambuc
7011be35a1SLionel Sambuc
7111be35a1SLionel Sambuc /// If not -1, PID of the process to forcibly kill when we get a signal.
7211be35a1SLionel Sambuc ///
7311be35a1SLionel Sambuc /// Must be protected by protect() and unprotect().
7411be35a1SLionel Sambuc static volatile pid_t pid_to_kill = -1;
7511be35a1SLionel Sambuc
7611be35a1SLionel Sambuc
7711be35a1SLionel Sambuc /// Whether we are holding signals or not.
7811be35a1SLionel Sambuc static bool protected = false;
7911be35a1SLionel Sambuc
8011be35a1SLionel Sambuc
8111be35a1SLionel Sambuc /// Magic exit code to denote an error while preparing the subprocess.
8211be35a1SLionel Sambuc static const int exit_setup_child = 124;
8311be35a1SLionel Sambuc /// Magic exit code to denote an error in exec(3) that we do not handle.
8411be35a1SLionel Sambuc static const int exit_exec_unknown = 123;
8511be35a1SLionel Sambuc /// Magic exit code to denote an EACCES error in exec(3).
8611be35a1SLionel Sambuc static const int exit_exec_eacces = 122;
8711be35a1SLionel Sambuc /// Magic exit code to denote an ENOENT error in exec(3).
8811be35a1SLionel Sambuc static const int exit_exec_enoent = 121;
8911be35a1SLionel Sambuc
9011be35a1SLionel Sambuc
9111be35a1SLionel Sambuc /// Area to save the original SIGHUP handler.
9211be35a1SLionel Sambuc static struct sigaction old_sighup;
9311be35a1SLionel Sambuc /// Area to save the original SIGINT handler.
9411be35a1SLionel Sambuc static struct sigaction old_sigint;
9511be35a1SLionel Sambuc /// Area to save the original SIGTERM handler.
9611be35a1SLionel Sambuc static struct sigaction old_sigterm;
9711be35a1SLionel Sambuc /// Area to save the original SIGALRM handler.
9811be35a1SLionel Sambuc static struct sigaction old_sigalrm;
9911be35a1SLionel Sambuc /// Area to save the original realtime timer.
10011be35a1SLionel Sambuc static struct itimerval old_timer;
10111be35a1SLionel Sambuc
10211be35a1SLionel Sambuc
10311be35a1SLionel Sambuc /// Masks or unmasks all the signals programmed by this module.
10411be35a1SLionel Sambuc ///
10511be35a1SLionel Sambuc /// \param operation One of SIG_BLOCK or SIG_UNBLOCK.
10611be35a1SLionel Sambuc static void
mask_handlers(const int operation)10711be35a1SLionel Sambuc mask_handlers(const int operation)
10811be35a1SLionel Sambuc {
10911be35a1SLionel Sambuc sigset_t mask;
11011be35a1SLionel Sambuc sigemptyset(&mask);
11111be35a1SLionel Sambuc sigaddset(&mask, SIGALRM);
11211be35a1SLionel Sambuc sigaddset(&mask, SIGINT);
11311be35a1SLionel Sambuc sigaddset(&mask, SIGHUP);
11411be35a1SLionel Sambuc sigaddset(&mask, SIGTERM);
115*3260d16fSLionel Sambuc #if defined(__minix) && !defined(NDEBUG)
116*3260d16fSLionel Sambuc const int ret =
117*3260d16fSLionel Sambuc #endif /* defined(__minix) && !defined(NDEBUG) */
118*3260d16fSLionel Sambuc sigprocmask(operation, &mask, NULL);
11911be35a1SLionel Sambuc assert(ret != -1);
12011be35a1SLionel Sambuc }
12111be35a1SLionel Sambuc
12211be35a1SLionel Sambuc
12311be35a1SLionel Sambuc /// Masks all signals programmed by this module.
12411be35a1SLionel Sambuc static void
protect(void)12511be35a1SLionel Sambuc protect(void)
12611be35a1SLionel Sambuc {
12711be35a1SLionel Sambuc mask_handlers(SIG_BLOCK);
12811be35a1SLionel Sambuc protected = true;
12911be35a1SLionel Sambuc }
13011be35a1SLionel Sambuc
13111be35a1SLionel Sambuc
13211be35a1SLionel Sambuc /// Unmasks all signals programmed by this module.
13311be35a1SLionel Sambuc static void
unprotect(void)13411be35a1SLionel Sambuc unprotect(void)
13511be35a1SLionel Sambuc {
13611be35a1SLionel Sambuc protected = false;
13711be35a1SLionel Sambuc mask_handlers(SIG_UNBLOCK);
13811be35a1SLionel Sambuc }
13911be35a1SLionel Sambuc
14011be35a1SLionel Sambuc
14111be35a1SLionel Sambuc /// Handler for signals that should abort execution.
14211be35a1SLionel Sambuc ///
14311be35a1SLionel Sambuc /// When called the first time, this handler kills any running subprocess so
14411be35a1SLionel Sambuc /// that the cleanup routines can proceed. Calling this a second time aborts
14511be35a1SLionel Sambuc /// execution of the program.
14611be35a1SLionel Sambuc ///
14711be35a1SLionel Sambuc /// \param unused_signo Number of the captured signal.
14811be35a1SLionel Sambuc static void
cleanup_handler(const int KYUA_DEFS_UNUSED_PARAM (signo))14911be35a1SLionel Sambuc cleanup_handler(const int KYUA_DEFS_UNUSED_PARAM(signo))
15011be35a1SLionel Sambuc {
15111be35a1SLionel Sambuc static const char* clean_message = "Signal caught; cleaning up...\n";
15211be35a1SLionel Sambuc static const char* abort_message = "Double signal caught; aborting...\n";
15311be35a1SLionel Sambuc
15411be35a1SLionel Sambuc protect();
15511be35a1SLionel Sambuc if (!signal_fired) {
15611be35a1SLionel Sambuc signal_fired = true;
15711be35a1SLionel Sambuc if (write(STDERR_FILENO, clean_message, strlen(clean_message)) == -1) {
15811be35a1SLionel Sambuc // Ignore.
15911be35a1SLionel Sambuc }
16011be35a1SLionel Sambuc if (pid_to_kill != -1) {
16111be35a1SLionel Sambuc kill(pid_to_kill, SIGKILL);
16211be35a1SLionel Sambuc pid_to_kill = -1;
16311be35a1SLionel Sambuc }
16411be35a1SLionel Sambuc unprotect();
16511be35a1SLionel Sambuc } else {
16611be35a1SLionel Sambuc if (write(STDERR_FILENO, abort_message, strlen(abort_message)) == -1) {
16711be35a1SLionel Sambuc // Ignore.
16811be35a1SLionel Sambuc }
16911be35a1SLionel Sambuc if (pid_to_kill != -1) {
17011be35a1SLionel Sambuc kill(pid_to_kill, SIGKILL);
17111be35a1SLionel Sambuc pid_to_kill = -1;
17211be35a1SLionel Sambuc }
17311be35a1SLionel Sambuc abort();
17411be35a1SLionel Sambuc }
17511be35a1SLionel Sambuc }
17611be35a1SLionel Sambuc
17711be35a1SLionel Sambuc
17811be35a1SLionel Sambuc /// Handler for signals that should terminate the active subprocess.
17911be35a1SLionel Sambuc ///
18011be35a1SLionel Sambuc /// \param unused_signo Number of the captured signal.
18111be35a1SLionel Sambuc static void
timeout_handler(const int KYUA_DEFS_UNUSED_PARAM (signo))18211be35a1SLionel Sambuc timeout_handler(const int KYUA_DEFS_UNUSED_PARAM(signo))
18311be35a1SLionel Sambuc {
18411be35a1SLionel Sambuc static const char* message = "Subprocess timed out; sending KILL "
18511be35a1SLionel Sambuc "signal...\n";
18611be35a1SLionel Sambuc
18711be35a1SLionel Sambuc protect();
18811be35a1SLionel Sambuc process_timed_out = true;
18911be35a1SLionel Sambuc if (write(STDERR_FILENO, message, strlen(message)) == -1) {
19011be35a1SLionel Sambuc // Ignore.
19111be35a1SLionel Sambuc }
19211be35a1SLionel Sambuc if (pid_to_kill != -1) {
19311be35a1SLionel Sambuc kill(pid_to_kill, SIGKILL);
19411be35a1SLionel Sambuc pid_to_kill = -1;
19511be35a1SLionel Sambuc }
19611be35a1SLionel Sambuc unprotect();
19711be35a1SLionel Sambuc }
19811be35a1SLionel Sambuc
19911be35a1SLionel Sambuc
20011be35a1SLionel Sambuc /// Installs a signal handler.
20111be35a1SLionel Sambuc ///
20211be35a1SLionel Sambuc /// \param signo Number of the signal to program.
20311be35a1SLionel Sambuc /// \param handler Handler for the signal.
20411be35a1SLionel Sambuc /// \param [out] old_sa Pointer to the sigaction structure in which to save the
20511be35a1SLionel Sambuc /// current signal handler data.
20611be35a1SLionel Sambuc static void
setup_signal(const int signo,void (* handler)(const int),struct sigaction * old_sa)20711be35a1SLionel Sambuc setup_signal(const int signo, void (*handler)(const int),
20811be35a1SLionel Sambuc struct sigaction* old_sa)
20911be35a1SLionel Sambuc {
21011be35a1SLionel Sambuc struct sigaction sa;
21111be35a1SLionel Sambuc sa.sa_handler = handler;
21211be35a1SLionel Sambuc sigemptyset(&sa.sa_mask);
21311be35a1SLionel Sambuc sa.sa_flags = SA_RESTART;
21411be35a1SLionel Sambuc
215*3260d16fSLionel Sambuc #if defined(__minix) && !defined(NDEBUG)
216*3260d16fSLionel Sambuc const int ret =
217*3260d16fSLionel Sambuc #endif /* defined(__minix) && !defined(NDEBUG) */
218*3260d16fSLionel Sambuc sigaction(signo, &sa, old_sa);
21911be35a1SLionel Sambuc assert(ret != -1);
22011be35a1SLionel Sambuc }
22111be35a1SLionel Sambuc
22211be35a1SLionel Sambuc
22311be35a1SLionel Sambuc /// Installs a timer.
22411be35a1SLionel Sambuc ///
22511be35a1SLionel Sambuc /// \param seconds Deadline for the timer.
22611be35a1SLionel Sambuc /// \param [out] old_itimerval Pointer to the itimerval structure in which to
22711be35a1SLionel Sambuc /// save the current timer data into.
22811be35a1SLionel Sambuc static void
setup_timer(const int seconds,struct itimerval * old_itimerval)22911be35a1SLionel Sambuc setup_timer(const int seconds, struct itimerval* old_itimerval)
23011be35a1SLionel Sambuc {
23111be35a1SLionel Sambuc struct itimerval new_timer;
23211be35a1SLionel Sambuc new_timer.it_interval.tv_sec = 0;
23311be35a1SLionel Sambuc new_timer.it_interval.tv_usec = 0;
23411be35a1SLionel Sambuc new_timer.it_value.tv_sec = seconds;
23511be35a1SLionel Sambuc new_timer.it_value.tv_usec = 0;
236*3260d16fSLionel Sambuc #if defined(__minix) && !defined(NDEBUG)
237*3260d16fSLionel Sambuc const int ret =
238*3260d16fSLionel Sambuc #endif /* defined(__minix) && !defined(NDEBUG) */
239*3260d16fSLionel Sambuc setitimer(ITIMER_REAL, &new_timer, old_itimerval);
24011be35a1SLionel Sambuc assert(ret != -1);
24111be35a1SLionel Sambuc }
24211be35a1SLionel Sambuc
24311be35a1SLionel Sambuc
24411be35a1SLionel Sambuc /// Resets the environment of the process to a known state.
24511be35a1SLionel Sambuc ///
24611be35a1SLionel Sambuc /// \param work_directory Path to the work directory being used.
24711be35a1SLionel Sambuc ///
24811be35a1SLionel Sambuc /// \return An error if there is a problem configuring the environment
24911be35a1SLionel Sambuc /// variables, or OK if successful. Note that if this returns an error, we have
25011be35a1SLionel Sambuc /// left the environment in an inconsistent state.
25111be35a1SLionel Sambuc static kyua_error_t
prepare_environment(const char * work_directory)25211be35a1SLionel Sambuc prepare_environment(const char* work_directory)
25311be35a1SLionel Sambuc {
25411be35a1SLionel Sambuc kyua_error_t error;
25511be35a1SLionel Sambuc
25611be35a1SLionel Sambuc // TODO(jmmv): It might be better to do the opposite: just pass a good known
25711be35a1SLionel Sambuc // set of variables to the child (aka HOME, PATH, ...). But how do we
25811be35a1SLionel Sambuc // determine this minimum set?
25911be35a1SLionel Sambuc
26011be35a1SLionel Sambuc const char* to_unset[] = { "LANG", "LC_ALL", "LC_COLLATE", "LC_CTYPE",
26111be35a1SLionel Sambuc "LC_MESSAGES", "LC_MONETARY", "LC_NUMERIC",
26211be35a1SLionel Sambuc "LC_TIME", NULL };
26311be35a1SLionel Sambuc const char** iter;
26411be35a1SLionel Sambuc for (iter = to_unset; *iter != NULL; ++iter) {
26511be35a1SLionel Sambuc error = kyua_env_unset(*iter);
26611be35a1SLionel Sambuc if (kyua_error_is_set(error))
26711be35a1SLionel Sambuc return error;
26811be35a1SLionel Sambuc }
26911be35a1SLionel Sambuc
27011be35a1SLionel Sambuc error = kyua_env_set("HOME", work_directory);
27111be35a1SLionel Sambuc if (kyua_error_is_set(error))
27211be35a1SLionel Sambuc return error;
27311be35a1SLionel Sambuc
27411be35a1SLionel Sambuc error = kyua_env_set("TZ", "UTC");
27511be35a1SLionel Sambuc if (kyua_error_is_set(error))
27611be35a1SLionel Sambuc return error;
27711be35a1SLionel Sambuc
27811be35a1SLionel Sambuc error = kyua_env_set("__RUNNING_INSIDE_ATF_RUN", "internal-yes-value");
27911be35a1SLionel Sambuc if (kyua_error_is_set(error))
28011be35a1SLionel Sambuc return error;
28111be35a1SLionel Sambuc
28211be35a1SLionel Sambuc assert(!kyua_error_is_set(error));
28311be35a1SLionel Sambuc return error;
28411be35a1SLionel Sambuc }
28511be35a1SLionel Sambuc
28611be35a1SLionel Sambuc
28711be35a1SLionel Sambuc /// Resets all signals to their default handlers.
28811be35a1SLionel Sambuc static void
reset_signals(void)28911be35a1SLionel Sambuc reset_signals(void)
29011be35a1SLionel Sambuc {
29111be35a1SLionel Sambuc int signo;
29211be35a1SLionel Sambuc
29311be35a1SLionel Sambuc for (signo = 1; signo <= LAST_SIGNO; ++signo) {
29411be35a1SLionel Sambuc if (signo == SIGKILL || signo == SIGSTOP) {
29511be35a1SLionel Sambuc // Don't attempt to reset immutable signals.
29611be35a1SLionel Sambuc continue;
29711be35a1SLionel Sambuc }
29811be35a1SLionel Sambuc
29911be35a1SLionel Sambuc struct sigaction sa;
30011be35a1SLionel Sambuc sa.sa_handler = SIG_DFL;
30111be35a1SLionel Sambuc sigemptyset(&sa.sa_mask);
30211be35a1SLionel Sambuc sa.sa_flags = 0;
30311be35a1SLionel Sambuc (void)sigaction(signo, &sa, NULL);
30411be35a1SLionel Sambuc }
30511be35a1SLionel Sambuc }
30611be35a1SLionel Sambuc
30711be35a1SLionel Sambuc
30811be35a1SLionel Sambuc /// Raises core size limit to its possible maximum.
30911be35a1SLionel Sambuc ///
31011be35a1SLionel Sambuc /// This is a best-effort operation. There is no guarantee that the operation
31111be35a1SLionel Sambuc /// will yield a large-enough limit to generate any possible core file.
31211be35a1SLionel Sambuc static void
unlimit_core_size(void)31311be35a1SLionel Sambuc unlimit_core_size(void)
31411be35a1SLionel Sambuc {
31511be35a1SLionel Sambuc #if !defined(__minix)
31611be35a1SLionel Sambuc struct rlimit rl;
31711be35a1SLionel Sambuc {
31811be35a1SLionel Sambuc const int ret = getrlimit(RLIMIT_CORE, &rl);
31911be35a1SLionel Sambuc assert(ret != -1);
32011be35a1SLionel Sambuc }
32111be35a1SLionel Sambuc rl.rlim_cur = rl.rlim_max;
32211be35a1SLionel Sambuc const int ret = setrlimit(RLIMIT_CORE, &rl) != -1;
32311be35a1SLionel Sambuc assert(ret != -1);
32411be35a1SLionel Sambuc #endif /* !defined(__minix) */
32511be35a1SLionel Sambuc }
32611be35a1SLionel Sambuc
32711be35a1SLionel Sambuc
32811be35a1SLionel Sambuc /// Cleans up the container process to run a new child.
32911be35a1SLionel Sambuc ///
33011be35a1SLionel Sambuc /// If there is any error during the setup, the new process is terminated
33111be35a1SLionel Sambuc /// with an error code.
33211be35a1SLionel Sambuc ///
33311be35a1SLionel Sambuc /// \param run_params End-user parameters that describe how to isolate the
33411be35a1SLionel Sambuc /// newly-created process.
33511be35a1SLionel Sambuc static void
setup_child(const kyua_run_params_t * run_params)33611be35a1SLionel Sambuc setup_child(const kyua_run_params_t* run_params)
33711be35a1SLionel Sambuc {
33811be35a1SLionel Sambuc setpgid(getpid(), getpid());
33911be35a1SLionel Sambuc
34011be35a1SLionel Sambuc if (chdir(run_params->work_directory) == -1)
34111be35a1SLionel Sambuc err(exit_setup_child, "chdir(%s) failed", run_params->work_directory);
34211be35a1SLionel Sambuc
34311be35a1SLionel Sambuc unlimit_core_size();
34411be35a1SLionel Sambuc reset_signals();
34511be35a1SLionel Sambuc
34611be35a1SLionel Sambuc const kyua_error_t error = prepare_environment(run_params->work_directory);
34711be35a1SLionel Sambuc if (kyua_error_is_set(error))
34811be35a1SLionel Sambuc kyua_error_err(exit_setup_child, error, "Failed to configure the "
34911be35a1SLionel Sambuc "environment");
35011be35a1SLionel Sambuc
35111be35a1SLionel Sambuc (void)umask(0022);
35211be35a1SLionel Sambuc
35311be35a1SLionel Sambuc if (run_params->unprivileged_group != getgid()) {
35411be35a1SLionel Sambuc if (setgid(run_params->unprivileged_group) == -1)
35511be35a1SLionel Sambuc err(exit_setup_child, "setgid(%ld) failed; uid is %ld and gid "
35611be35a1SLionel Sambuc "is %ld", (long int)run_params->unprivileged_group,
35711be35a1SLionel Sambuc (long int)getuid(), (long int)getgid());
35811be35a1SLionel Sambuc }
35911be35a1SLionel Sambuc if (run_params->unprivileged_user != getuid()) {
36011be35a1SLionel Sambuc if (setuid(run_params->unprivileged_user) == -1)
36111be35a1SLionel Sambuc err(exit_setup_child, "setuid(%ld) failed; uid is %ld and gid "
36211be35a1SLionel Sambuc "is %ld", (long int)run_params->unprivileged_user,
36311be35a1SLionel Sambuc (long int)getuid(), (long int)getgid());
36411be35a1SLionel Sambuc }
36511be35a1SLionel Sambuc }
36611be35a1SLionel Sambuc
36711be35a1SLionel Sambuc
36811be35a1SLionel Sambuc /// Constructs a path to a work directory based on a template.
36911be35a1SLionel Sambuc ///
37011be35a1SLionel Sambuc /// \param template Template of the work directory to create. Should be a
37111be35a1SLionel Sambuc /// basename and must include the XXXXXX placeholder string. The directory
37211be35a1SLionel Sambuc /// is created within the temporary directory specified by the TMPDIR
37311be35a1SLionel Sambuc /// environment variable or the builtin value in kyua_run_tmpdir.
37411be35a1SLionel Sambuc /// \param [out] work_directory Pointer to a dynamically-allocated string
37511be35a1SLionel Sambuc /// containing the full path to the work directory. The caller is
37611be35a1SLionel Sambuc /// responsible for releasing this string.
37711be35a1SLionel Sambuc ///
37811be35a1SLionel Sambuc /// \return An error if there is a problem (most likely OOM), or OK otherwise.
37911be35a1SLionel Sambuc static kyua_error_t
build_work_directory_path(const char * template,char ** work_directory)38011be35a1SLionel Sambuc build_work_directory_path(const char* template, char** work_directory)
38111be35a1SLionel Sambuc {
38211be35a1SLionel Sambuc assert(strstr(template, "XXXXXX") != NULL);
38311be35a1SLionel Sambuc
38411be35a1SLionel Sambuc const char* tmpdir = getenv("TMPDIR");
38511be35a1SLionel Sambuc if (tmpdir == NULL)
38611be35a1SLionel Sambuc return kyua_text_printf(work_directory, "%s/%s", kyua_run_tmpdir,
38711be35a1SLionel Sambuc template);
38811be35a1SLionel Sambuc else
38911be35a1SLionel Sambuc return kyua_text_printf(work_directory, "%s/%s", tmpdir, template);
39011be35a1SLionel Sambuc }
39111be35a1SLionel Sambuc
39211be35a1SLionel Sambuc
39311be35a1SLionel Sambuc /// Initializes the run_params parameters with default values.
39411be35a1SLionel Sambuc ///
39511be35a1SLionel Sambuc /// \param [out] run_params The object to initialize.
39611be35a1SLionel Sambuc void
kyua_run_params_init(kyua_run_params_t * run_params)39711be35a1SLionel Sambuc kyua_run_params_init(kyua_run_params_t* run_params)
39811be35a1SLionel Sambuc {
39911be35a1SLionel Sambuc run_params->timeout_seconds = 60;
40011be35a1SLionel Sambuc run_params->unprivileged_user = getuid();
40111be35a1SLionel Sambuc run_params->unprivileged_group = getgid();
40211be35a1SLionel Sambuc run_params->work_directory = ".";
40311be35a1SLionel Sambuc }
40411be35a1SLionel Sambuc
40511be35a1SLionel Sambuc
40611be35a1SLionel Sambuc /// Executes a program and exits if there is a problem.
40711be35a1SLionel Sambuc ///
40811be35a1SLionel Sambuc /// This routine is supposed to be used in conjunction with kyua_run_fork and
40911be35a1SLionel Sambuc /// kyua_run_wait so that the various return codes of the exec system call are
41011be35a1SLionel Sambuc /// properly propagated to the parent process.
41111be35a1SLionel Sambuc ///
41211be35a1SLionel Sambuc /// \param program Path to the program to run.
41311be35a1SLionel Sambuc /// \param args Arguments to the program.
41411be35a1SLionel Sambuc void
kyua_run_exec(const char * program,const char * const * args)41511be35a1SLionel Sambuc kyua_run_exec(const char* program, const char* const* args)
41611be35a1SLionel Sambuc {
41711be35a1SLionel Sambuc (void)execvp(program, KYUA_DEFS_UNCONST(args));
41811be35a1SLionel Sambuc switch (errno) {
41911be35a1SLionel Sambuc case EACCES:
42011be35a1SLionel Sambuc exit(exit_exec_eacces);
42111be35a1SLionel Sambuc case ENOENT:
42211be35a1SLionel Sambuc exit(exit_exec_enoent);
42311be35a1SLionel Sambuc default:
42411be35a1SLionel Sambuc err(exit_exec_unknown, "execvp failed");
42511be35a1SLionel Sambuc }
42611be35a1SLionel Sambuc }
42711be35a1SLionel Sambuc
42811be35a1SLionel Sambuc
42911be35a1SLionel Sambuc /// Forks and isolates the child process.
43011be35a1SLionel Sambuc ///
43111be35a1SLionel Sambuc /// The created subprocess must be waited for with kyua_run_wait().
43211be35a1SLionel Sambuc ///
43311be35a1SLionel Sambuc /// \param run_params Parameters that describe how to isolate the newly-created
43411be35a1SLionel Sambuc /// process.
43511be35a1SLionel Sambuc /// \param [out] out_pid The PID of the child in the parent, or 0 in the child.
43611be35a1SLionel Sambuc /// The value left here should only be accessed if this function does not
43711be35a1SLionel Sambuc /// return an error.
43811be35a1SLionel Sambuc ///
43911be35a1SLionel Sambuc /// \return An error object if fork(2) fails.
44011be35a1SLionel Sambuc kyua_error_t
kyua_run_fork(const kyua_run_params_t * run_params,pid_t * const out_pid)44111be35a1SLionel Sambuc kyua_run_fork(const kyua_run_params_t* run_params, pid_t* const out_pid)
44211be35a1SLionel Sambuc {
44311be35a1SLionel Sambuc protect();
44411be35a1SLionel Sambuc pid_t pid = fork();
44511be35a1SLionel Sambuc if (pid == -1) {
44611be35a1SLionel Sambuc unprotect();
44711be35a1SLionel Sambuc *out_pid = pid; // Not necessary, but avoid mistakes in the caller.
44811be35a1SLionel Sambuc return kyua_libc_error_new(errno, "fork failed");
44911be35a1SLionel Sambuc } else if (pid == 0) {
45011be35a1SLionel Sambuc unprotect();
45111be35a1SLionel Sambuc setup_child(run_params);
45211be35a1SLionel Sambuc *out_pid = pid;
45311be35a1SLionel Sambuc return kyua_error_ok();
45411be35a1SLionel Sambuc } else {
45511be35a1SLionel Sambuc pid_to_kill = pid;
45611be35a1SLionel Sambuc unprotect();
45711be35a1SLionel Sambuc
45811be35a1SLionel Sambuc setup_signal(SIGALRM, timeout_handler, &old_sigalrm);
45911be35a1SLionel Sambuc process_timed_out = false;
46011be35a1SLionel Sambuc setup_timer(run_params->timeout_seconds, &old_timer);
46111be35a1SLionel Sambuc
46211be35a1SLionel Sambuc *out_pid = pid;
46311be35a1SLionel Sambuc return kyua_error_ok();
46411be35a1SLionel Sambuc }
46511be35a1SLionel Sambuc }
46611be35a1SLionel Sambuc
46711be35a1SLionel Sambuc
46811be35a1SLionel Sambuc /// Waits for a process started via kyua_run_fork.
46911be35a1SLionel Sambuc ///
47011be35a1SLionel Sambuc /// \param pid The PID of the child to wait for.
47111be35a1SLionel Sambuc /// \param [out] status The exit status of the awaited process.
47211be35a1SLionel Sambuc /// \param [out] timed_out Whether the process timed out or not.
47311be35a1SLionel Sambuc ///
47411be35a1SLionel Sambuc /// \return An error if the process failed due to an problem in kyua_run_exec.
47511be35a1SLionel Sambuc /// However, note that the wait for the process itself is expected to have been
47611be35a1SLionel Sambuc /// successful.
47711be35a1SLionel Sambuc kyua_error_t
kyua_run_wait(const pid_t pid,int * status,bool * timed_out)47811be35a1SLionel Sambuc kyua_run_wait(const pid_t pid, int* status, bool* timed_out)
47911be35a1SLionel Sambuc {
48011be35a1SLionel Sambuc int tmp_status;
481*3260d16fSLionel Sambuc #if defined(__minix) && !defined(NDEBUG)
482*3260d16fSLionel Sambuc const pid_t waited_pid =
483*3260d16fSLionel Sambuc #endif /* defined(__minix) && !defined(NDEBUG) */
484*3260d16fSLionel Sambuc waitpid(pid, &tmp_status, 0);
48511be35a1SLionel Sambuc assert(pid == waited_pid);
48611be35a1SLionel Sambuc
48711be35a1SLionel Sambuc protect();
48811be35a1SLionel Sambuc (void)setitimer(ITIMER_REAL, &old_timer, NULL);
48911be35a1SLionel Sambuc (void)sigaction(SIGALRM, &old_sigalrm, NULL);
49011be35a1SLionel Sambuc pid_to_kill = -1;
49111be35a1SLionel Sambuc unprotect();
49211be35a1SLionel Sambuc
49311be35a1SLionel Sambuc killpg(pid, SIGKILL);
49411be35a1SLionel Sambuc
49511be35a1SLionel Sambuc if (WIFEXITED(tmp_status)) {
49611be35a1SLionel Sambuc if (WEXITSTATUS(tmp_status) == exit_setup_child) {
49711be35a1SLionel Sambuc return kyua_generic_error_new("Failed to isolate subprocess; "
49811be35a1SLionel Sambuc "see stderr for details");
49911be35a1SLionel Sambuc } else if (WEXITSTATUS(tmp_status) == exit_exec_eacces) {
50011be35a1SLionel Sambuc return kyua_libc_error_new(EACCES, "execvp failed");
50111be35a1SLionel Sambuc } else if (WEXITSTATUS(tmp_status) == exit_exec_enoent) {
50211be35a1SLionel Sambuc return kyua_libc_error_new(ENOENT, "execvp failed");
50311be35a1SLionel Sambuc } else if (WEXITSTATUS(tmp_status) == exit_exec_unknown) {
50411be35a1SLionel Sambuc return kyua_generic_error_new("execvp failed; see stderr for "
50511be35a1SLionel Sambuc "details");
50611be35a1SLionel Sambuc } else {
50711be35a1SLionel Sambuc // Fall-through.
50811be35a1SLionel Sambuc }
50911be35a1SLionel Sambuc }
51011be35a1SLionel Sambuc *status = tmp_status;
51111be35a1SLionel Sambuc *timed_out = process_timed_out;
51211be35a1SLionel Sambuc return kyua_error_ok();
51311be35a1SLionel Sambuc }
51411be35a1SLionel Sambuc
51511be35a1SLionel Sambuc
51611be35a1SLionel Sambuc /// Creates a temporary directory for use by a subprocess.
51711be35a1SLionel Sambuc ///
51811be35a1SLionel Sambuc /// The temporary directory must be deleted with kyua_run_work_directory_leave.
51911be35a1SLionel Sambuc ///
52011be35a1SLionel Sambuc /// \param template Template of the work directory to create. Should be a
52111be35a1SLionel Sambuc /// basename and must include the XXXXXX placeholder string. The directory
52211be35a1SLionel Sambuc /// is created within the temporary directory specified by the TMPDIR
52311be35a1SLionel Sambuc /// environment variable or the builtin value.
52411be35a1SLionel Sambuc /// \param uid User to set the owner of the directory to.
52511be35a1SLionel Sambuc /// \param gid Group to set the owner of the directory to.
52611be35a1SLionel Sambuc /// \param [out] out_work_directory Updated with a pointer to a dynamic string
52711be35a1SLionel Sambuc /// holding the path to the created work directory. This must be passed as
52811be35a1SLionel Sambuc /// is to kyua_run_work_directory_leave, which takes care of freeing the
52911be35a1SLionel Sambuc /// memory.
53011be35a1SLionel Sambuc ///
53111be35a1SLionel Sambuc /// \return An error code if there is a problem creating the directory.
53211be35a1SLionel Sambuc kyua_error_t
kyua_run_work_directory_enter(const char * template,const uid_t uid,const gid_t gid,char ** out_work_directory)53311be35a1SLionel Sambuc kyua_run_work_directory_enter(const char* template, const uid_t uid,
53411be35a1SLionel Sambuc const gid_t gid, char** out_work_directory)
53511be35a1SLionel Sambuc {
53611be35a1SLionel Sambuc kyua_error_t error = kyua_error_ok();
53711be35a1SLionel Sambuc
53811be35a1SLionel Sambuc signal_fired = false;
53911be35a1SLionel Sambuc setup_signal(SIGHUP, cleanup_handler, &old_sighup);
54011be35a1SLionel Sambuc setup_signal(SIGINT, cleanup_handler, &old_sigint);
54111be35a1SLionel Sambuc setup_signal(SIGTERM, cleanup_handler, &old_sigterm);
54211be35a1SLionel Sambuc
54311be35a1SLionel Sambuc char* work_directory;
54411be35a1SLionel Sambuc error = build_work_directory_path(template, &work_directory);
54511be35a1SLionel Sambuc if (kyua_error_is_set(error))
54611be35a1SLionel Sambuc goto err_signals;
54711be35a1SLionel Sambuc
54811be35a1SLionel Sambuc if (mkdtemp(work_directory) == NULL) {
54911be35a1SLionel Sambuc error = kyua_libc_error_new(errno, "mkdtemp(%s) failed",
55011be35a1SLionel Sambuc work_directory);
55111be35a1SLionel Sambuc goto err_work_directory_variable;
55211be35a1SLionel Sambuc }
55311be35a1SLionel Sambuc
55411be35a1SLionel Sambuc if (uid != getuid() || gid != getgid()) {
55511be35a1SLionel Sambuc if (chown(work_directory, uid, gid) == -1) {
55611be35a1SLionel Sambuc error = kyua_libc_error_new(errno,
55711be35a1SLionel Sambuc "chown(%s, %ld, %ld) failed; uid is %ld and gid is %ld",
55811be35a1SLionel Sambuc work_directory, (long int)uid, (long int)gid,
55911be35a1SLionel Sambuc (long int)getuid(), (long int)getgid());
56011be35a1SLionel Sambuc goto err_work_directory_file;
56111be35a1SLionel Sambuc }
56211be35a1SLionel Sambuc }
56311be35a1SLionel Sambuc
56411be35a1SLionel Sambuc *out_work_directory = work_directory;
56511be35a1SLionel Sambuc assert(!kyua_error_is_set(error));
56611be35a1SLionel Sambuc goto out;
56711be35a1SLionel Sambuc
56811be35a1SLionel Sambuc err_work_directory_file:
56911be35a1SLionel Sambuc (void)rmdir(work_directory);
57011be35a1SLionel Sambuc err_work_directory_variable:
57111be35a1SLionel Sambuc free(work_directory);
57211be35a1SLionel Sambuc err_signals:
57311be35a1SLionel Sambuc (void)sigaction(SIGTERM, &old_sigterm, NULL);
57411be35a1SLionel Sambuc (void)sigaction(SIGINT, &old_sigint, NULL);
57511be35a1SLionel Sambuc (void)sigaction(SIGHUP, &old_sighup, NULL);
57611be35a1SLionel Sambuc out:
57711be35a1SLionel Sambuc return error;
57811be35a1SLionel Sambuc }
57911be35a1SLionel Sambuc
58011be35a1SLionel Sambuc
58111be35a1SLionel Sambuc /// Deletes a temporary directory created by kyua_run_work_directory_enter().
58211be35a1SLionel Sambuc ///
58311be35a1SLionel Sambuc /// \param [in,out] work_directory The pointer to the work_directory string as
58411be35a1SLionel Sambuc /// originally returned by kyua_run_work_directory_leave(). This is
58511be35a1SLionel Sambuc /// explicitly invalidated by this function to clearly denote that this
58611be35a1SLionel Sambuc /// performs the memory releasing.
58711be35a1SLionel Sambuc ///
58811be35a1SLionel Sambuc /// \return An error code if the cleanup of the directory fails. Note that any
58911be35a1SLionel Sambuc /// intermediate errors during the cleanup are sent to stderr.
59011be35a1SLionel Sambuc kyua_error_t
kyua_run_work_directory_leave(char ** work_directory)59111be35a1SLionel Sambuc kyua_run_work_directory_leave(char** work_directory)
59211be35a1SLionel Sambuc {
59311be35a1SLionel Sambuc kyua_error_t error = kyua_fs_cleanup(*work_directory);
59411be35a1SLionel Sambuc
59511be35a1SLionel Sambuc free(*work_directory);
59611be35a1SLionel Sambuc *work_directory = NULL;
59711be35a1SLionel Sambuc
59811be35a1SLionel Sambuc (void)sigaction(SIGTERM, &old_sigterm, NULL);
59911be35a1SLionel Sambuc (void)sigaction(SIGHUP, &old_sighup, NULL);
60011be35a1SLionel Sambuc (void)sigaction(SIGINT, &old_sigint, NULL);
60111be35a1SLionel Sambuc
60211be35a1SLionel Sambuc // At this point, we have cleaned up the work directory and we could
60311be35a1SLionel Sambuc // (should?) re-deliver the signal to ourselves so that we terminated with
60411be35a1SLionel Sambuc // the right code. However, we just let this return and allow the caller
60511be35a1SLionel Sambuc // code to perform any other cleanups instead.
60611be35a1SLionel Sambuc
60711be35a1SLionel Sambuc return error;
60811be35a1SLionel Sambuc }
609