chiark / gitweb /
systemctl: beef up highlighting of service states a little
[elogind.git] / src / manager.c
index 9b6cbc94e062087be122b8866e2454fb8b6dd2aa..e32b24ff516da78bf895e82044f67b474fbeaa13 100644 (file)
@@ -27,7 +27,6 @@
 #include <sys/signalfd.h>
 #include <sys/wait.h>
 #include <unistd.h>
-#include <utmpx.h>
 #include <sys/poll.h>
 #include <sys/reboot.h>
 #include <sys/ioctl.h>
@@ -37,6 +36,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <dirent.h>
+#include <libaudit.h>
 
 #include "manager.h"
 #include "hashmap.h"
@@ -47,7 +47,6 @@
 #include "ratelimit.h"
 #include "cgroup.h"
 #include "mount-setup.h"
-#include "utmp-wtmp.h"
 #include "unit-name.h"
 #include "dbus-unit.h"
 #include "dbus-job.h"
@@ -202,6 +201,10 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) {
         m->exit_code = _MANAGER_EXIT_CODE_INVALID;
         m->pin_cgroupfs_fd = -1;
 
+#ifdef HAVE_AUDIT
+        m->audit_fd = -1;
+#endif
+
         m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = m->dev_autofs_fd = -1;
         m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
 
@@ -245,6 +248,9 @@ int manager_new(ManagerRunningAs running_as, Manager **_m) {
         if ((r = bus_init(m)) < 0)
                 goto fail;
 
+        if ((m->audit_fd = audit_open()) < 0)
+                log_error("Failed to connect to audit log: %m");
+
         *_m = m;
         return 0;
 
@@ -429,6 +435,11 @@ void manager_free(Manager *m) {
         if (m->notify_watch.fd >= 0)
                 close_nointr_nofail(m->notify_watch.fd);
 
+#ifdef HAVE_AUDIT
+        if (m->audit_fd >= 0)
+                audit_close(m->audit_fd);
+#endif
+
         free(m->notify_socket);
 
         lookup_paths_free(&m->lookup_paths);
@@ -567,10 +578,6 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) {
                 m->n_deserializing --;
         }
 
-        /* Now that the initial devices are available, let's see if we
-         * can write the utmp file */
-        manager_write_utmp_reboot(m);
-
         return r;
 }
 
@@ -715,6 +722,20 @@ static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, Job
         other->object_list = NULL;
         transaction_delete_job(m, other, true);
 }
+static bool job_is_conflicted_by(Job *j) {
+        JobDependency *l;
+
+        assert(j);
+
+        /* Returns true if this job is pulled in by a least one
+         * ConflictedBy dependency. */
+
+        LIST_FOREACH(object, l, j->object_list)
+                if (l->conflicts)
+                        return true;
+
+        return false;
+}
 
 static int delete_one_unmergeable_job(Manager *m, Job *j) {
         Job *k;
@@ -738,7 +759,36 @@ static int delete_one_unmergeable_job(Manager *m, Job *j) {
 
                         /* Ok, we found two that conflict, let's see if we can
                          * drop one of them */
-                        if (!j->matters_to_anchor)
+                        if (!j->matters_to_anchor && !k->matters_to_anchor) {
+
+                                /* Both jobs don't matter, so let's
+                                 * find the one that is smarter to
+                                 * remove. Let's think positive and
+                                 * rather remove stops then starts --
+                                 * except if something is being
+                                 * stopped because it is conflicted by
+                                 * another unit in which case we
+                                 * rather remove the start. */
+
+                                log_debug("Looking at job %s/%s conflicted_by=%s", j->unit->meta.id, job_type_to_string(j->type), yes_no(j->type == JOB_STOP && job_is_conflicted_by(j)));
+                                log_debug("Looking at job %s/%s conflicted_by=%s", k->unit->meta.id, job_type_to_string(k->type), yes_no(k->type == JOB_STOP && job_is_conflicted_by(k)));
+
+                                if (j->type == JOB_STOP) {
+
+                                        if (job_is_conflicted_by(j))
+                                                d = k;
+                                        else
+                                                d = j;
+
+                                } else if (k->type == JOB_STOP) {
+
+                                        if (job_is_conflicted_by(k))
+                                                d = j;
+                                        else
+                                                d = k;
+                                }
+
+                        } else if (!j->matters_to_anchor)
                                 d = j;
                         else if (!k->matters_to_anchor)
                                 d = k;
@@ -1324,6 +1374,7 @@ static int transaction_add_job_and_dependencies(
                 Job *by,
                 bool matters,
                 bool override,
+                bool conflicts,
                 DBusError *e,
                 Job **_ret) {
         Job *ret;
@@ -1352,46 +1403,50 @@ static int transaction_add_job_and_dependencies(
                 return -ENOMEM;
 
         /* Then, add a link to the job. */
-        if (!job_dependency_new(by, ret, matters))
+        if (!job_dependency_new(by, ret, matters, conflicts))
                 return -ENOMEM;
 
         if (is_new) {
                 /* 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, e, NULL)) < 0 && r != -EBADR)
+                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, override, false, e, NULL)) < 0 && r != -EBADR)
                                         goto fail;
 
                         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, e, NULL)) < 0 && r != -EBADR) {
+                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !override, override, false, e, NULL)) < 0 && r != -EBADR) {
                                         log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, bus_error(e, r));
                                         dbus_error_free(e);
                                 }
 
                         SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_WANTS], i)
-                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, false, e, NULL)) < 0) {
+                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, false, false, e, NULL)) < 0) {
                                         log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, bus_error(e, r));
                                         dbus_error_free(e);
                                 }
 
                         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, e, NULL)) < 0 && r != -EBADR)
+                                if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, override, false, e, NULL)) < 0 && r != -EBADR)
                                         goto fail;
 
                         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, e, NULL)) < 0 && r != -EBADR) {
+                                if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !override, override, false, e, NULL)) < 0 && r != -EBADR) {
                                         log_warning("Cannot add dependency job for unit %s, ignoring: %s", dep->meta.id, bus_error(e, r));
                                         dbus_error_free(e);
                                 }
 
                         SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_CONFLICTS], i)
-                                if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, override, e, NULL)) < 0 && r != -EBADR)
+                                if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, override, true, e, NULL)) < 0 && r != -EBADR)
+                                        goto fail;
+
+                        SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_CONFLICTED_BY], i)
+                                if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, override, false, e, NULL)) < 0 && r != -EBADR)
                                         goto fail;
 
                 } 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, e, NULL)) < 0 && r != -EBADR)
+                                if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, override, false, e, NULL)) < 0 && r != -EBADR)
                                         goto fail;
                 }
 
@@ -1432,7 +1487,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, NULL, NULL)) < 0)
+                if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, u, NULL, true, false, false, NULL, NULL)) < 0)
                         log_warning("Cannot add isolate job for unit %s, ignoring: %s", u->meta.id, strerror(-r));
         }
 
@@ -1455,7 +1510,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool ove
 
         log_debug("Trying to enqueue job %s/%s", unit->meta.id, job_type_to_string(type));
 
-        if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, override, e, &ret)) < 0) {
+        if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, override, false, e, &ret)) < 0) {
                 transaction_abort(m);
                 return r;
         }
@@ -2186,70 +2241,25 @@ int manager_get_job_from_dbus_path(Manager *m, const char *s, Job **_j) {
         return 0;
 }
 
-static bool manager_utmp_good(Manager *m) {
-        int r;
+void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
 
-        assert(m);
+#ifdef HAVE_AUDIT
+        char *p;
 
-        if ((r = mount_path_is_mounted(m, _PATH_UTMPX)) <= 0) {
-
-                if (r < 0)
-                        log_warning("Failed to determine whether " _PATH_UTMPX " is mounted: %s", strerror(-r));
-
-                return false;
-        }
-
-        return true;
-}
-
-void manager_write_utmp_reboot(Manager *m) {
-        int r;
-
-        assert(m);
-
-        if (m->utmp_reboot_written)
+        if (m->audit_fd < 0)
                 return;
 
-        if (m->running_as != MANAGER_SYSTEM)
-                return;
-
-        if (!manager_utmp_good(m))
-                return;
-
-        if ((r = utmp_put_reboot(m->startup_timestamp.realtime)) < 0) {
-
-                if (r != -ENOENT && r != -EROFS)
-                        log_warning("Failed to write utmp/wtmp: %s", strerror(-r));
-
+        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;
         }
 
-        m->utmp_reboot_written = true;
-}
+        if (audit_log_user_comm_message(m->audit_fd, type, "", p, NULL, NULL, NULL, success) < 0)
+                log_error("Failed to send audit message: %m");
 
-void manager_write_utmp_runlevel(Manager *m, Unit *u) {
-        int runlevel, r;
+        free(p);
+#endif
 
-        assert(m);
-        assert(u);
-
-        if (u->meta.type != UNIT_TARGET)
-                return;
-
-        if (m->running_as != MANAGER_SYSTEM)
-                return;
-
-        if (!manager_utmp_good(m))
-                return;
-
-        if ((runlevel = target_get_runlevel(TARGET(u))) <= 0)
-                return;
-
-        if ((r = utmp_put_runlevel(0, runlevel, 0)) < 0) {
-
-                if (r != -ENOENT && r != -EROFS)
-                        log_warning("Failed to write utmp/wtmp: %s", strerror(-r));
-        }
 }
 
 void manager_dispatch_bus_name_owner_changed(