chiark / gitweb /
unit: disallow configuration of more than one on_failure dependencies if OnFailureIso...
[elogind.git] / src / manager.c
index 7d0b351c9d14de68c978e486bcea151cbf6498cd..f9b3b5956be65c85b7a6eca1eebf5d36162df4ca 100644 (file)
@@ -66,7 +66,8 @@
 #define GC_QUEUE_USEC_MAX (10*USEC_PER_SEC)
 
 /* Where clients shall send notification messages to */
-#define NOTIFY_SOCKET "/org/freedesktop/systemd1/notify"
+#define NOTIFY_SOCKET_SYSTEM "/run/systemd/notify"
+#define NOTIFY_SOCKET_USER "@/org/freedesktop/systemd1/notify"
 
 static int manager_setup_notify(Manager *m) {
         union {
@@ -88,9 +89,14 @@ static int manager_setup_notify(Manager *m) {
         sa.sa.sa_family = AF_UNIX;
 
         if (getpid() != 1)
-                snprintf(sa.un.sun_path+1, sizeof(sa.un.sun_path)-1, NOTIFY_SOCKET "/%llu", random_ull());
-        else
-                strncpy(sa.un.sun_path+1, NOTIFY_SOCKET, sizeof(sa.un.sun_path)-1);
+                snprintf(sa.un.sun_path, sizeof(sa.un.sun_path), NOTIFY_SOCKET_USER "/%llu", random_ull());
+        else {
+                unlink(NOTIFY_SOCKET_SYSTEM);
+                strncpy(sa.un.sun_path, NOTIFY_SOCKET_SYSTEM, sizeof(sa.un.sun_path));
+        }
+
+        if (sa.un.sun_path[0] == '@')
+                sa.un.sun_path[0] = 0;
 
         if (bind(m->notify_watch.fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
                 log_error("bind() failed: %m");
@@ -109,7 +115,10 @@ static int manager_setup_notify(Manager *m) {
         if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->notify_watch.fd, &ev) < 0)
                 return -errno;
 
-        if (!(m->notify_socket = strdup(sa.un.sun_path+1)))
+        if (sa.un.sun_path[0] == 0)
+                sa.un.sun_path[0] = '@';
+
+        if (!(m->notify_socket = strdup(sa.un.sun_path)))
                 return -ENOMEM;
 
         log_debug("Using notification socket %s", m->notify_socket);
@@ -118,7 +127,7 @@ static int manager_setup_notify(Manager *m) {
 }
 
 static int enable_special_signals(Manager *m) {
-        char fd;
+        int fd;
 
         assert(m);
 
@@ -269,6 +278,8 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) {
                 log_error("Failed to connect to audit log: %m");
 #endif
 
+        m->taint_usr = dir_is_empty("/usr") > 0;
+
         *_m = m;
         return 0;
 
@@ -294,7 +305,7 @@ static unsigned manager_dispatch_cleanup_queue(Manager *m) {
 }
 
 enum {
-        GC_OFFSET_IN_PATH,  /* This one is on the path we were travelling */
+        GC_OFFSET_IN_PATH,  /* This one is on the path we were traveling */
         GC_OFFSET_UNSURE,   /* No clue */
         GC_OFFSET_GOOD,     /* We still need this unit */
         GC_OFFSET_BAD,      /* We don't need this unit anymore */
@@ -768,7 +779,7 @@ static int delete_one_unmergeable_job(Manager *m, Job *j) {
 
         /* Tries to delete one item in the linked list
          * j->transaction_next->transaction_next->... that conflicts
-         * whith another one, in an attempt to make an inconsistent
+         * with another one, in an attempt to make an inconsistent
          * transaction work. */
 
         /* We rely here on the fact that if a merged with b does not
@@ -913,7 +924,8 @@ static void transaction_drop_redundant(Manager *m) {
                         LIST_FOREACH(transaction, k, j) {
 
                                 if (!job_is_anchor(k) &&
-                                    (j->installed || job_type_is_redundant(k->type, unit_active_state(k->unit))))
+                                    (k->installed || job_type_is_redundant(k->type, unit_active_state(k->unit))) &&
+                                    (!k->unit->meta.job || !job_type_is_conflicting(k->type, k->unit->meta.job->type)))
                                         continue;
 
                                 changes_something = true;
@@ -1261,7 +1273,8 @@ static int transaction_activate(Manager *m, JobMode mode, DBusError *e) {
         for (;;) {
                 /* Fourth step: Let's remove unneeded jobs that might
                  * be lurking. */
-                transaction_collect_garbage(m);
+                if (mode != JOB_ISOLATE)
+                        transaction_collect_garbage(m);
 
                 /* Fifth step: verify order makes sense and correct
                  * cycles if necessary and possible */
@@ -1285,13 +1298,14 @@ static int transaction_activate(Manager *m, JobMode mode, DBusError *e) {
                         break;
 
                 if (r != -EAGAIN) {
-                        log_warning("Requested transaction contains unmergable jobs: %s", bus_error(e, r));
+                        log_warning("Requested transaction contains unmergeable jobs: %s", bus_error(e, r));
                         goto rollback;
                 }
 
                 /* Seventh step: an entry got dropped, let's garbage
                  * collect its dependencies. */
-                transaction_collect_garbage(m);
+                if (mode != JOB_ISOLATE)
+                        transaction_collect_garbage(m);
 
                 /* Let's see if the resulting transaction still has
                  * unmergeable entries ... */
@@ -1329,7 +1343,7 @@ static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool o
         assert(m);
         assert(unit);
 
-        /* Looks for an axisting prospective job and returns that. If
+        /* Looks for an existing prospective job and returns that. If
          * it doesn't exist it is created and added to the prospective
          * jobs list. */
 
@@ -1411,6 +1425,8 @@ static int transaction_add_job_and_dependencies(
                 bool matters,
                 bool override,
                 bool conflicts,
+                bool ignore_requirements,
+                bool ignore_order,
                 DBusError *e,
                 Job **_ret) {
         Job *ret;
@@ -1423,6 +1439,11 @@ static int transaction_add_job_and_dependencies(
         assert(type < _JOB_TYPE_MAX);
         assert(unit);
 
+        /* log_debug("Pulling in %s/%s from %s/%s", */
+        /*           unit->meta.id, job_type_to_string(type), */
+        /*           by ? by->unit->meta.id : "NA", */
+        /*           by ? job_type_to_string(by->type) : "NA"); */
+
         if (unit->meta.load_state != UNIT_LOADED &&
             unit->meta.load_state != UNIT_ERROR &&
             unit->meta.load_state != UNIT_MASKED) {
@@ -1453,18 +1474,20 @@ static int transaction_add_job_and_dependencies(
         if (!(ret = transaction_add_one_job(m, type, unit, override, &is_new)))
                 return -ENOMEM;
 
+        ret->ignore_order = ret->ignore_order || ignore_order;
+
         /* Then, add a link to the job. */
         if (!job_dependency_new(by, ret, matters, conflicts))
                 return -ENOMEM;
 
-        if (is_new) {
+        if (is_new && !ignore_requirements) {
                 Set *following;
 
                 /* If we are following some other unit, make sure we
                  * add all dependencies of everybody following. */
                 if (unit_following_set(ret->unit, &following) > 0) {
                         SET_FOREACH(dep, following, i)
-                                if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, false, override, false, e, NULL)) < 0) {
+                                if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, false, override, false, false, ignore_order, e, NULL)) < 0) {
                                         log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, bus_error(e, r));
 
                                         if (e)
@@ -1477,7 +1500,7 @@ static int transaction_add_job_and_dependencies(
                 /* Finally, recursively add in all dependencies. */
                 if (type == JOB_START || type == JOB_RELOAD_OR_START) {
                         SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRES], i)
-                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, override, false, e, NULL)) < 0) {
+                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) {
                                         if (r != -EBADR)
                                                 goto fail;
 
@@ -1486,7 +1509,7 @@ static int transaction_add_job_and_dependencies(
                                 }
 
                         SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_BIND_TO], i)
-                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, override, false, e, NULL)) < 0) {
+                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) {
 
                                         if (r != -EBADR)
                                                 goto fail;
@@ -1496,7 +1519,7 @@ static int transaction_add_job_and_dependencies(
                                 }
 
                         SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRES_OVERRIDABLE], i)
-                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !override, override, false, e, NULL)) < 0) {
+                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !override, override, false, false, ignore_order, e, NULL)) < 0) {
                                         log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, bus_error(e, r));
 
                                         if (e)
@@ -1504,7 +1527,7 @@ static int transaction_add_job_and_dependencies(
                                 }
 
                         SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_WANTS], i)
-                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, false, false, e, NULL)) < 0) {
+                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, false, false, false, ignore_order, e, NULL)) < 0) {
                                         log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, bus_error(e, r));
 
                                         if (e)
@@ -1512,7 +1535,7 @@ static int transaction_add_job_and_dependencies(
                                 }
 
                         SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUISITE], i)
-                                if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, e, NULL)) < 0) {
+                                if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) {
 
                                         if (r != -EBADR)
                                                 goto fail;
@@ -1522,7 +1545,7 @@ static int transaction_add_job_and_dependencies(
                                 }
 
                         SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUISITE_OVERRIDABLE], i)
-                                if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, e, NULL)) < 0) {
+                                if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, false, ignore_order, e, NULL)) < 0) {
                                         log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, bus_error(e, r));
 
                                         if (e)
@@ -1530,7 +1553,7 @@ static int transaction_add_job_and_dependencies(
                                 }
 
                         SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_CONFLICTS], i)
-                                if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, override, true, e, NULL)) < 0) {
+                                if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, override, true, false, ignore_order, e, NULL)) < 0) {
 
                                         if (r != -EBADR)
                                                 goto fail;
@@ -1540,7 +1563,7 @@ static int transaction_add_job_and_dependencies(
                                 }
 
                         SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_CONFLICTED_BY], i)
-                                if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, false, override, false, e, NULL)) < 0) {
+                                if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, false, override, false, false, ignore_order, e, NULL)) < 0) {
                                         log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, bus_error(e, r));
 
                                         if (e)
@@ -1550,7 +1573,7 @@ static int transaction_add_job_and_dependencies(
                 } else if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) {
 
                         SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRED_BY], i)
-                                if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, override, false, e, NULL)) < 0) {
+                                if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) {
 
                                         if (r != -EBADR)
                                                 goto fail;
@@ -1560,7 +1583,7 @@ static int transaction_add_job_and_dependencies(
                                 }
 
                         SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_BOUND_BY], i)
-                                if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, override, false, e, NULL)) < 0) {
+                                if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, override, false, false, ignore_order, e, NULL)) < 0) {
 
                                         if (r != -EBADR)
                                                 goto fail;
@@ -1607,7 +1630,7 @@ static int transaction_add_isolate_jobs(Manager *m) {
                 if (hashmap_get(m->transaction_jobs, u))
                         continue;
 
-                if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, u, NULL, true, false, false, NULL, NULL)) < 0)
+                if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, u, NULL, true, false, false, false, false, NULL, NULL)) < 0)
                         log_warning("Cannot add isolate job for unit %s, ignoring: %s", u->meta.id, strerror(-r));
         }
 
@@ -1635,7 +1658,9 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove
 
         log_debug("Trying to enqueue job %s/%s/%s", unit->meta.id, job_type_to_string(type), job_mode_to_string(mode));
 
-        if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, override, false, e, &ret)) < 0) {
+        if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, override, false,
+                                                      mode == JOB_IGNORE_DEPENDENCIES || mode == JOB_IGNORE_REQUIREMENTS,
+                                                      mode == JOB_IGNORE_DEPENDENCIES, e, &ret)) < 0) {
                 transaction_abort(m);
                 return r;
         }
@@ -1814,7 +1839,7 @@ void manager_clear_jobs(Manager *m) {
         transaction_abort(m);
 
         while ((j = hashmap_first(m->jobs)))
-                job_free(j);
+                job_finish_and_invalidate(j, JOB_CANCELED);
 }
 
 unsigned manager_dispatch_run_queue(Manager *m) {
@@ -2050,7 +2075,17 @@ static int manager_process_signal_fd(Manager *m) {
                         return -errno;
                 }
 
-                log_debug("Received SIG%s", strna(signal_to_string(sfsi.ssi_signo)));
+                if (sfsi.ssi_pid > 0) {
+                        char *p = NULL;
+
+                        get_process_name(sfsi.ssi_pid, &p);
+
+                        log_debug("Received SIG%s from PID %lu (%s).",
+                                  strna(signal_to_string(sfsi.ssi_signo)),
+                                  (unsigned long) sfsi.ssi_pid, strna(p));
+                        free(p);
+                } else
+                        log_debug("Received SIG%s.", strna(signal_to_string(sfsi.ssi_signo)));
 
                 switch (sfsi.ssi_signo) {
 
@@ -2300,7 +2335,7 @@ static int process_event(Manager *m, struct epoll_event *ev) {
 int manager_loop(Manager *m) {
         int r;
 
-        RATELIMIT_DEFINE(rl, 1*USEC_PER_SEC, 1000);
+        RATELIMIT_DEFINE(rl, 1*USEC_PER_SEC, 50000);
 
         assert(m);
         m->exit_code = MANAGER_RUNNING;
@@ -2425,13 +2460,30 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
         if (m->n_deserializing > 0)
                 return;
 
+        if (m->running_as != MANAGER_SYSTEM)
+                return;
+
+        if (u->meta.type != UNIT_SERVICE)
+                return;
+
         if (!(p = unit_name_to_prefix_and_instance(u->meta.id))) {
                 log_error("Failed to allocate unit name for audit message: %s", strerror(ENOMEM));
                 return;
         }
 
-        if (audit_log_user_comm_message(m->audit_fd, type, "", p, NULL, NULL, NULL, success) < 0)
-                log_error("Failed to send audit message: %m");
+        if (audit_log_user_comm_message(m->audit_fd, type, "", p, NULL, NULL, NULL, success) < 0) {
+                log_warning("Failed to send audit message: %m");
+
+                if (errno == EPERM) {
+                        /* We aren't allowed to send audit messages?
+                         * Then let's not retry again, to avoid
+                         * spamming the user with the same and same
+                         * messages over and over. */
+
+                        audit_close(m->audit_fd);
+                        m->audit_fd = -1;
+                }
+        }
 
         free(p);
 #endif
@@ -2541,22 +2593,20 @@ void manager_dispatch_bus_query_pid_done(
 }
 
 int manager_open_serialization(Manager *m, FILE **_f) {
-        char *path;
+        char *path = NULL;
         mode_t saved_umask;
         int fd;
         FILE *f;
 
         assert(_f);
 
-        if (m->running_as == MANAGER_SYSTEM) {
-                mkdir_p("/dev/.systemd", 0755);
+        if (m->running_as == MANAGER_SYSTEM)
+                asprintf(&path, "/run/systemd/dump-%lu-XXXXXX", (unsigned long) getpid());
+        else
+                asprintf(&path, "/tmp/systemd-dump-%lu-XXXXXX", (unsigned long) getpid());
 
-                if (asprintf(&path, "/dev/.systemd/dump-%lu-XXXXXX", (unsigned long) getpid()) < 0)
-                        return -ENOMEM;
-        } else {
-                if (asprintf(&path, "/tmp/systemd-dump-%lu-XXXXXX", (unsigned long) getpid()) < 0)
-                        return -ENOMEM;
-        }
+        if (!path)
+                return -ENOMEM;
 
         saved_umask = umask(0077);
         fd = mkostemp(path, O_RDWR|O_CLOEXEC);
@@ -2572,7 +2622,7 @@ int manager_open_serialization(Manager *m, FILE **_f) {
         log_debug("Serializing state to %s", path);
         free(path);
 
-        if (!(f = fdopen(fd, "w+")) < 0)
+        if (!(f = fdopen(fd, "w+")))
                 return -errno;
 
         *_f = f;
@@ -2590,6 +2640,9 @@ int manager_serialize(Manager *m, FILE *f, FDSet *fds) {
         assert(f);
         assert(fds);
 
+        fprintf(f, "current-job-id=%i\n", m->current_job_id);
+        fprintf(f, "taint-usr=%s\n", yes_no(m->taint_usr));
+
         dual_timestamp_serialize(f, "initrd-timestamp", &m->initrd_timestamp);
         dual_timestamp_serialize(f, "startup-timestamp", &m->startup_timestamp);
         dual_timestamp_serialize(f, "finish-timestamp", &m->finish_timestamp);
@@ -2645,7 +2698,21 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
                 if (l[0] == 0)
                         break;
 
-                if (startswith(l, "initrd-timestamp="))
+                if (startswith(l, "current-job-id=")) {
+                        uint32_t id;
+
+                        if (safe_atou32(l+15, &id) < 0)
+                                log_debug("Failed to parse current job id value %s", l+15);
+                        else
+                                m->current_job_id = MAX(m->current_job_id, id);
+                } else if (startswith(l, "taint-usr=")) {
+                        int b;
+
+                        if ((b = parse_boolean(l+10)) < 0)
+                                log_debug("Failed to parse taint /usr flag %s", l+10);
+                        else
+                                m->taint_usr = m->taint_usr || b;
+                } else if (startswith(l, "initrd-timestamp="))
                         dual_timestamp_deserialize(l+17, &m->initrd_timestamp);
                 else if (startswith(l, "startup-timestamp="))
                         dual_timestamp_deserialize(l+18, &m->startup_timestamp);
@@ -2808,7 +2875,8 @@ void manager_check_finished(Manager *m) {
 
         dual_timestamp_get(&m->finish_timestamp);
 
-        if (m->running_as == MANAGER_SYSTEM) {
+        if (m->running_as == MANAGER_SYSTEM && detect_container(NULL) <= 0) {
+
                 if (dual_timestamp_is_set(&m->initrd_timestamp)) {
                         log_info("Startup finished in %s (kernel) + %s (initrd) + %s (userspace) = %s.",
                                  format_timespan(kernel, sizeof(kernel),
@@ -2853,7 +2921,7 @@ void manager_run_generators(Manager *m) {
 
         if (!m->generator_unit_path) {
                 char *p;
-                char system_path[] = "/dev/.systemd/generator-XXXXXX",
+                char system_path[] = "/run/systemd/generator-XXXXXX",
                         user_path[] = "/tmp/systemd-generator-XXXXXX";
 
                 if (!(p = mkdtemp(m->running_as == MANAGER_SYSTEM ? system_path : user_path))) {
@@ -2929,6 +2997,47 @@ int manager_set_default_controllers(Manager *m, char **controllers) {
         return 0;
 }
 
+void manager_recheck_syslog(Manager *m) {
+        Unit *u;
+
+        assert(m);
+
+        if (m->running_as != MANAGER_SYSTEM)
+                return;
+
+        if ((u = manager_get_unit(m, SPECIAL_SYSLOG_SOCKET))) {
+                SocketState state;
+
+                state = SOCKET(u)->state;
+
+                if (state != SOCKET_DEAD &&
+                    state != SOCKET_FAILED &&
+                    state != SOCKET_RUNNING) {
+
+                        /* Hmm, the socket is not set up, or is still
+                         * listening, let's better not try to use
+                         * it. Note that we have no problem if the
+                         * socket is completely down, since there
+                         * might be a foreign /dev/log socket around
+                         * and we want to make use of that.
+                         */
+
+                        log_close_syslog();
+                        return;
+                }
+        }
+
+        if ((u = manager_get_unit(m, SPECIAL_SYSLOG_TARGET)))
+                if (TARGET(u)->state != TARGET_ACTIVE) {
+                        log_close_syslog();
+                        return;
+                }
+
+        /* Hmm, OK, so the socket is either fully up, or fully down,
+         * and the target is up, then let's make use of the socket */
+        log_open();
+}
+
 static const char* const manager_running_as_table[_MANAGER_RUNNING_AS_MAX] = {
         [MANAGER_SYSTEM] = "system",
         [MANAGER_USER] = "user"