chiark / gitweb /
core/execute: add internal is_terminal_output()
[elogind.git] / src / core / execute.c
index e502c2490f1301b4ae5cc78d57d37a523b278a7a..1f6263519607eb8d3e33dad7ad8fd22afc2a9a12 100644 (file)
@@ -39,6 +39,7 @@
 #include <linux/oom.h>
 #include <sys/poll.h>
 #include <linux/seccomp-bpf.h>
+#include <glob.h>
 
 #ifdef HAVE_PAM
 #include <security/pam_appl.h>
@@ -50,6 +51,7 @@
 #include "capability.h"
 #include "util.h"
 #include "log.h"
+#include "sd-messages.h"
 #include "ioprio.h"
 #include "securebits.h"
 #include "cgroup.h"
@@ -62,8 +64,8 @@
 #include "loopback-setup.h"
 #include "path-util.h"
 #include "syscall-list.h"
-#include "sd-id128.h"
-#include "sd-messages.h"
+#include "env-util.h"
+#include "fileio.h"
 
 #define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
 
@@ -163,6 +165,14 @@ void exec_context_tty_reset(const ExecContext *context) {
                 vt_disallocate(context->tty_path);
 }
 
+static bool is_terminal_output(ExecOutput o) {
+        return
+                o == EXEC_OUTPUT_TTY ||
+                o == EXEC_OUTPUT_SYSLOG_AND_CONSOLE ||
+                o == EXEC_OUTPUT_KMSG_AND_CONSOLE ||
+                o == EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
+}
+
 static int open_null_as(int flags, int nfd) {
         int fd, r;
 
@@ -222,7 +232,7 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons
                 !!context->syslog_level_prefix,
                 output == EXEC_OUTPUT_SYSLOG || output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
                 output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE,
-                output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || output == EXEC_OUTPUT_KMSG_AND_CONSOLE || output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE);
+                is_terminal_output(output));
 
         if (fd != nfd) {
                 r = dup2(fd, nfd) < 0 ? -errno : nfd;
@@ -318,9 +328,10 @@ static int setup_input(const ExecContext *context, int socket_fd, bool apply_tty
         }
 }
 
-static int setup_output(const ExecContext *context, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin) {
+static int setup_output(const ExecContext *context, int fileno, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin) {
         ExecOutput o;
         ExecInput i;
+        int r;
 
         assert(context);
         assert(ident);
@@ -328,91 +339,55 @@ static int setup_output(const ExecContext *context, int socket_fd, const char *i
         i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
         o = fixup_output(context->std_output, socket_fd);
 
-        /* This expects the input is already set up */
+        if (fileno == STDERR_FILENO) {
+                ExecOutput e;
+                e = fixup_output(context->std_error, socket_fd);
 
-        switch (o) {
+                /* This expects the input and output are already set up */
+
+                /* Don't change the stderr file descriptor if we inherit all
+                 * the way and are not on a tty */
+                if (e == EXEC_OUTPUT_INHERIT &&
+                    o == EXEC_OUTPUT_INHERIT &&
+                    i == EXEC_INPUT_NULL &&
+                    !is_terminal_input(context->std_input) &&
+                    getppid () != 1)
+                        return fileno;
 
-        case EXEC_OUTPUT_INHERIT:
+                /* Duplicate from stdout if possible */
+                if (e == o || e == EXEC_OUTPUT_INHERIT)
+                        return dup2(STDOUT_FILENO, fileno) < 0 ? -errno : fileno;
 
+                o = e;
+
+        } else if (o == EXEC_OUTPUT_INHERIT) {
                 /* If input got downgraded, inherit the original value */
                 if (i == EXEC_INPUT_NULL && is_terminal_input(context->std_input))
-                        return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO);
+                        return open_terminal_as(tty_path(context), O_WRONLY, fileno);
 
                 /* If the input is connected to anything that's not a /dev/null, inherit that... */
                 if (i != EXEC_INPUT_NULL)
-                        return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
+                        return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
 
                 /* If we are not started from PID 1 we just inherit STDOUT from our parent process. */
                 if (getppid() != 1)
-                        return STDOUT_FILENO;
-
-                /* We need to open /dev/null here anew, to get the
-                 * right access mode. So we fall through */
-
-        case EXEC_OUTPUT_NULL:
-                return open_null_as(O_WRONLY, STDOUT_FILENO);
+                        return fileno;
 
-        case EXEC_OUTPUT_TTY:
-                if (is_terminal_input(i))
-                        return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
-
-                /* We don't reset the terminal if this is just about output */
-                return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO);
-
-        case EXEC_OUTPUT_SYSLOG:
-        case EXEC_OUTPUT_SYSLOG_AND_CONSOLE:
-        case EXEC_OUTPUT_KMSG:
-        case EXEC_OUTPUT_KMSG_AND_CONSOLE:
-        case EXEC_OUTPUT_JOURNAL:
-        case EXEC_OUTPUT_JOURNAL_AND_CONSOLE:
-                return connect_logger_as(context, o, ident, unit_id, STDOUT_FILENO);
-
-        case EXEC_OUTPUT_SOCKET:
-                assert(socket_fd >= 0);
-                return dup2(socket_fd, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
-
-        default:
-                assert_not_reached("Unknown output type");
+                /* We need to open /dev/null here anew, to get the right access mode. */
+                return open_null_as(O_WRONLY, fileno);
         }
-}
-
-static int setup_error(const ExecContext *context, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin) {
-        ExecOutput o, e;
-        ExecInput i;
 
-        assert(context);
-        assert(ident);
-
-        i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
-        o = fixup_output(context->std_output, socket_fd);
-        e = fixup_output(context->std_error, socket_fd);
-
-        /* This expects the input and output are already set up */
-
-        /* Don't change the stderr file descriptor if we inherit all
-         * the way and are not on a tty */
-        if (e == EXEC_OUTPUT_INHERIT &&
-            o == EXEC_OUTPUT_INHERIT &&
-            i == EXEC_INPUT_NULL &&
-            !is_terminal_input(context->std_input) &&
-            getppid () != 1)
-                return STDERR_FILENO;
-
-        /* Duplicate from stdout if possible */
-        if (e == o || e == EXEC_OUTPUT_INHERIT)
-                return dup2(STDOUT_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
-
-        switch (e) {
+        switch (o) {
 
         case EXEC_OUTPUT_NULL:
-                return open_null_as(O_WRONLY, STDERR_FILENO);
+                return open_null_as(O_WRONLY, fileno);
 
         case EXEC_OUTPUT_TTY:
                 if (is_terminal_input(i))
-                        return dup2(STDIN_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
+                        return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
 
                 /* We don't reset the terminal if this is just about output */
-                return open_terminal_as(tty_path(context), O_WRONLY, STDERR_FILENO);
+                return open_terminal_as(tty_path(context), O_WRONLY, fileno);
 
         case EXEC_OUTPUT_SYSLOG:
         case EXEC_OUTPUT_SYSLOG_AND_CONSOLE:
@@ -420,11 +395,21 @@ static int setup_error(const ExecContext *context, int socket_fd, const char *id
         case EXEC_OUTPUT_KMSG_AND_CONSOLE:
         case EXEC_OUTPUT_JOURNAL:
         case EXEC_OUTPUT_JOURNAL_AND_CONSOLE:
-                return connect_logger_as(context, e, ident, unit_id, STDERR_FILENO);
+                r = connect_logger_as(context, o, ident, unit_id, fileno);
+                if (r < 0) {
+                        log_struct_unit(LOG_CRIT, unit_id,
+                                "MESSAGE=Failed to connect std%s of %s to the journal socket: %s",
+                                fileno == STDOUT_FILENO ? "out" : "err",
+                                unit_id, strerror(-r),
+                                "ERRNO=%d", -r,
+                                NULL);
+                        r = open_null_as(O_WRONLY, fileno);
+                }
+                return r;
 
         case EXEC_OUTPUT_SOCKET:
                 assert(socket_fd >= 0);
-                return dup2(socket_fd, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
+                return dup2(socket_fd, fileno) < 0 ? -errno : fileno;
 
         default:
                 assert_not_reached("Unknown error type");
@@ -957,7 +942,7 @@ static int apply_seccomp(uint32_t *syscall_filter) {
         for (i = 0, n = 0; i < syscall_max(); i++)
                 if (syscall_filter[i >> 4] & (1 << (i & 31))) {
                         struct sock_filter item[] = {
-                                BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, i, 0, 1),
+                                BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, INDEX_TO_SYSCALL(i), 0, 1),
                                 BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)
                         };
 
@@ -1024,8 +1009,8 @@ int exec_spawn(ExecCommand *command,
 
         r = exec_context_load_environment(context, &files_env);
         if (r < 0) {
-                log_struct(LOG_ERR,
-                           "UNIT=%s", unit_id,
+                log_struct_unit(LOG_ERR,
+                           unit_id,
                            "MESSAGE=Failed to load environment files: %s", strerror(-r),
                            "ERRNO=%d", -r,
                            NULL);
@@ -1039,8 +1024,8 @@ int exec_spawn(ExecCommand *command,
         if (!line)
                 return log_oom();
 
-        log_struct(LOG_DEBUG,
-                   "UNIT=%s", unit_id,
+        log_struct_unit(LOG_DEBUG,
+                   unit_id,
                    "MESSAGE=About to execute %s", line,
                    NULL);
         free(line);
@@ -1165,13 +1150,13 @@ int exec_spawn(ExecCommand *command,
                         goto fail_child;
                 }
 
-                err = setup_output(context, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin);
+                err = setup_output(context, STDOUT_FILENO, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin);
                 if (err < 0) {
                         r = EXIT_STDOUT;
                         goto fail_child;
                 }
 
-                err = setup_error(context, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin);
+                err = setup_output(context, STDERR_FILENO, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin);
                 if (err < 0) {
                         r = EXIT_STDERR;
                         goto fail_child;
@@ -1512,8 +1497,8 @@ int exec_spawn(ExecCommand *command,
                 _exit(r);
         }
 
-        log_struct(LOG_DEBUG,
-                   "UNIT=%s", unit_id,
+        log_struct_unit(LOG_DEBUG,
+                   unit_id,
                    "MESSAGE=Forked %s as %lu",
                           command->path, (unsigned long) pid,
                    NULL);
@@ -1658,6 +1643,8 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
                 int k;
                 bool ignore = false;
                 char **p;
+                glob_t pglob;
+                int count, n;
 
                 fn = *i;
 
@@ -1675,29 +1662,55 @@ int exec_context_load_environment(const ExecContext *c, char ***l) {
                         return -EINVAL;
                 }
 
-                if ((k = load_env_file(fn, &p)) < 0) {
+                /* Filename supports globbing, take all matching files */
+                zero(pglob);
+                errno = 0;
+                if (glob(fn, 0, NULL, &pglob) != 0) {
+                        globfree(&pglob);
+                        if (ignore)
+                                continue;
 
+                        strv_free(r);
+                        return errno ? -errno : -EINVAL;
+                }
+                count = pglob.gl_pathc;
+                if (count == 0) {
+                        globfree(&pglob);
                         if (ignore)
                                 continue;
 
                         strv_free(r);
-                        return k;
+                        return -EINVAL;
                 }
+                for (n = 0; n < count; n++) {
+                        k = load_env_file(pglob.gl_pathv[n], &p);
+                        if (k < 0) {
+                                if (ignore)
+                                        continue;
 
-                if (r == NULL)
-                        r = p;
-                else {
-                        char **m;
+                                strv_free(r);
+                                globfree(&pglob);
+                                return k;
+                         }
 
-                        m = strv_env_merge(2, r, p);
-                        strv_free(r);
-                        strv_free(p);
+                        if (r == NULL)
+                                r = p;
+                        else {
+                                char **m;
 
-                        if (!m)
-                                return -ENOMEM;
+                                m = strv_env_merge(2, r, p);
+                                strv_free(r);
+                                strv_free(p);
 
-                        r = m;
+                                if (!m) {
+                                        globfree(&pglob);
+                                        return -ENOMEM;
+                                }
+
+                                r = m;
+                        }
                 }
+                globfree(&pglob);
         }
 
         *l = r;
@@ -1769,21 +1782,37 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
                 if (c->rlimit[i])
                         fprintf(f, "%s%s: %llu\n", prefix, rlimit_to_string(i), (unsigned long long) c->rlimit[i]->rlim_max);
 
-        if (c->ioprio_set)
+        if (c->ioprio_set) {
+                char *class_str;
+                int r;
+
+                r = ioprio_class_to_string_alloc(IOPRIO_PRIO_CLASS(c->ioprio), &class_str);
+                if (r < 0)
+                        class_str = NULL;
                 fprintf(f,
                         "%sIOSchedulingClass: %s\n"
                         "%sIOPriority: %i\n",
-                        prefix, ioprio_class_to_string(IOPRIO_PRIO_CLASS(c->ioprio)),
+                        prefix, strna(class_str),
                         prefix, (int) IOPRIO_PRIO_DATA(c->ioprio));
+                free(class_str);
+        }
+
+        if (c->cpu_sched_set) {
+                char *policy_str;
+                int r;
 
-        if (c->cpu_sched_set)
+                r = sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str);
+                if (r < 0)
+                        policy_str = NULL;
                 fprintf(f,
                         "%sCPUSchedulingPolicy: %s\n"
                         "%sCPUSchedulingPriority: %i\n"
                         "%sCPUSchedulingResetOnFork: %s\n",
-                        prefix, sched_policy_to_string(c->cpu_sched_policy),
+                        prefix, strna(policy_str),
                         prefix, c->cpu_sched_priority,
                         prefix, yes_no(c->cpu_sched_reset_on_fork));
+                free(policy_str);
+        }
 
         if (c->cpuset) {
                 fprintf(f, "%sCPUAffinity:", prefix);
@@ -1818,12 +1847,26 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
         if (c->std_output == EXEC_OUTPUT_SYSLOG || c->std_output == EXEC_OUTPUT_KMSG || c->std_output == EXEC_OUTPUT_JOURNAL ||
             c->std_output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_output == EXEC_OUTPUT_KMSG_AND_CONSOLE || c->std_output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE ||
             c->std_error == EXEC_OUTPUT_SYSLOG || c->std_error == EXEC_OUTPUT_KMSG || c->std_error == EXEC_OUTPUT_JOURNAL ||
-            c->std_error == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_KMSG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_JOURNAL_AND_CONSOLE)
+            c->std_error == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_KMSG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_JOURNAL_AND_CONSOLE) {
+                char *fac_str, *lvl_str;
+                int r;
+
+                r = log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str);
+                if (r < 0)
+                        fac_str = NULL;
+
+                r = log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str);
+                if (r < 0)
+                        lvl_str = NULL;
+
                 fprintf(f,
                         "%sSyslogFacility: %s\n"
                         "%sSyslogLevel: %s\n",
-                        prefix, log_facility_unshifted_to_string(c->syslog_priority >> 3),
-                        prefix, log_level_to_string(LOG_PRI(c->syslog_priority)));
+                        prefix, strna(fac_str),
+                        prefix, strna(lvl_str));
+                free(lvl_str);
+                free(fac_str);
+        }
 
         if (c->capabilities) {
                 char *t;