chiark / gitweb /
implement coldpluggin
[elogind.git] / unit.c
diff --git a/unit.c b/unit.c
index 15401e24fe0660cd3540b146e00b84ffcf439715..7954c041854fa5a35fea717af62ac4e5ab6782a5 100644 (file)
--- a/unit.c
+++ b/unit.c
@@ -6,6 +6,8 @@
 #include <sys/epoll.h>
 #include <sys/timerfd.h>
 #include <sys/poll.h>
+#include <stdlib.h>
+#include <unistd.h>
 
 #include "set.h"
 #include "unit.h"
@@ -42,7 +44,7 @@ UnitType unit_name_to_type(const char *n) {
         "0123456789"                            \
         "abcdefghijklmnopqrstuvwxyz"            \
         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"            \
-        "-_"
+        "-_.\\"
 
 bool unit_name_is_valid(const char *n) {
         UnitType t;
@@ -60,6 +62,9 @@ bool unit_name_is_valid(const char *n) {
         if (!(e = strrchr(n, '.')))
                 return false;
 
+        if (e == n)
+                return false;
+
         for (i = n; i < e; i++)
                 if (!strchr(VALID_CHARS, *i))
                         return false;
@@ -150,6 +155,34 @@ int unit_add_name(Unit *u, const char *text) {
         return 0;
 }
 
+int unit_choose_id(Unit *u, const char *name) {
+        char *s;
+
+        assert(u);
+        assert(name);
+
+        /* Selects one of the names of this unit as the id */
+
+        if (!(s = set_get(u->meta.names, (char*) name)))
+                return -ENOENT;
+
+        u->meta.id = s;
+        return 0;
+}
+
+int unit_set_description(Unit *u, const char *description) {
+        char *s;
+
+        assert(u);
+
+        if (!(s = strdup(description)))
+                return -ENOMEM;
+
+        free(u->meta.description);
+        u->meta.description = s;
+        return 0;
+}
+
 void unit_add_to_load_queue(Unit *u) {
         assert(u);
 
@@ -206,6 +239,7 @@ void unit_free(Unit *u) {
                 bidi_set_free(u, u->meta.dependencies[d]);
 
         free(u->meta.description);
+        free(u->meta.load_path);
 
         while ((t = set_steal_first(u->meta.names)))
                 free(t);
@@ -237,7 +271,8 @@ static int ensure_merge(Set **s, Set *other) {
         return 0;
 }
 
-/* FIXME: Does not rollback on failure! */
+/* FIXME: Does not rollback on failure! Needs to fix special unit
+ * pointers. Needs to merge names and dependencies properly.*/
 int unit_merge(Unit *u, Unit *other) {
         int r;
         UnitDependency d;
@@ -338,6 +373,9 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
                 prefix, load_state_table[u->meta.load_state],
                 prefix, active_state_table[unit_active_state(u)]);
 
+        if (u->meta.load_path)
+                fprintf(f, "%s\tLoad Path: %s\n", prefix, u->meta.load_path);
+
         SET_FOREACH(t, u->meta.names, i)
                 fprintf(f, "%s\tName: %s\n", prefix, t);
 
@@ -362,7 +400,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
 
 /* Common implementation for multiple backends */
 int unit_load_fragment_and_dropin(Unit *u) {
-        int r;
+        int r, ret;
 
         assert(u);
 
@@ -370,11 +408,13 @@ int unit_load_fragment_and_dropin(Unit *u) {
         if ((r = unit_load_fragment(u)) < 0)
                 return r;
 
+        ret = r > 0;
+
         /* Load drop-in directory data */
         if ((r = unit_load_dropin(u)) < 0)
                 return r;
 
-        return 0;
+        return ret;
 }
 
 int unit_load(Unit *u) {
@@ -559,7 +599,7 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) {
                 else {
                         assert(u->meta.job->state == JOB_RUNNING);
 
-                        /* Let's check of this state change
+                        /* Let's check whether this state change
                          * constitutes a finished job, or maybe
                          * cotradicts a running job and hence needs to
                          * invalidate jobs. */
@@ -621,33 +661,44 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns) {
                 retroactively_stop_dependencies(u);
 }
 
-int unit_watch_fd(Unit *u, int fd, uint32_t events) {
+int unit_watch_fd(Unit *u, int fd, uint32_t events, Watch *w) {
         struct epoll_event ev;
 
         assert(u);
         assert(fd >= 0);
+        assert(w);
+        assert(w->type == WATCH_INVALID || (w->type == WATCH_FD && w->fd == fd && w->unit == u));
 
         zero(ev);
-        ev.data.fd = fd;
-        ev.data.ptr = u;
-        ev.data.u32 = MANAGER_FD;
+        ev.data.ptr = w;
         ev.events = events;
 
-        if (epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) >= 0)
-                return 0;
+        if (epoll_ctl(u->meta.manager->epoll_fd,
+                      w->type == WATCH_INVALID ? EPOLL_CTL_ADD : EPOLL_CTL_MOD,
+                      fd,
+                      &ev) < 0)
+                return -errno;
 
-        if (errno == EEXIST)
-                if (epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_MOD, fd, &ev) >= 0)
-                        return 0;
+        w->fd = fd;
+        w->type = WATCH_FD;
+        w->unit = u;
 
-        return -errno;
+        return 0;
 }
 
-void unit_unwatch_fd(Unit *u, int fd) {
+void unit_unwatch_fd(Unit *u, Watch *w) {
         assert(u);
-        assert(fd >= 0);
+        assert(w);
+
+        if (w->type == WATCH_INVALID)
+                return;
 
-        assert_se(epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_DEL, fd, NULL) >= 0 || errno == ENOENT);
+        assert(w->type == WATCH_FD && w->unit == u);
+        assert_se(epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0);
+
+        w->fd = -1;
+        w->type = WATCH_INVALID;
+        w->unit = NULL;
 }
 
 int unit_watch_pid(Unit *u, pid_t pid) {
@@ -664,25 +715,22 @@ void unit_unwatch_pid(Unit *u, pid_t pid) {
         hashmap_remove(u->meta.manager->watch_pids, UINT32_TO_PTR(pid));
 }
 
-int unit_watch_timer(Unit *u, usec_t delay, int *id) {
-        struct epoll_event ev;
-        int fd;
+int unit_watch_timer(Unit *u, usec_t delay, Watch *w) {
         struct itimerspec its;
-        int flags;
+        int flags, fd;
         bool ours;
 
         assert(u);
-        assert(id);
+        assert(w);
+        assert(w->type == WATCH_INVALID || (w->type == WATCH_TIMER && w->unit == u));
 
         /* This will try to reuse the old timer if there is one */
 
-        if (*id >= 0) {
+        if (w->type == WATCH_TIMER) {
                 ours = false;
-                fd = *id;
-
+                fd = w->fd;
         } else {
                 ours = true;
-
                 if ((fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC)) < 0)
                         return -errno;
         }
@@ -705,16 +753,21 @@ int unit_watch_timer(Unit *u, usec_t delay, int *id) {
         if (timerfd_settime(fd, flags, &its, NULL) < 0)
                 goto fail;
 
-        zero(ev);
-        ev.data.fd = fd;
-        ev.data.ptr = u;
-        ev.data.u32 = MANAGER_TIMER;
-        ev.events = POLLIN;
+        if (w->type == WATCH_INVALID) {
+                struct epoll_event ev;
 
-        if (epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0)
-                goto fail;
+                zero(ev);
+                ev.data.ptr = w;
+                ev.events = POLLIN;
+
+                if (epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0)
+                        goto fail;
+        }
+
+        w->fd = fd;
+        w->type = WATCH_TIMER;
+        w->unit = u;
 
-        *id = fd;
         return 0;
 
 fail:
@@ -724,16 +777,21 @@ fail:
         return -errno;
 }
 
-void unit_unwatch_timer(Unit *u, int *id) {
+void unit_unwatch_timer(Unit *u, Watch *w) {
         assert(u);
-        assert(id);
+        assert(w);
 
-        if (*id < 0)
+        if (w->type == WATCH_INVALID)
                 return;
 
-        assert_se(epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_DEL, *id, NULL) >= 0);
-        assert_se(close_nointr(*id) == 0);
-        *id = -1;
+        assert(w->type == WATCH_TIMER && w->unit == u);
+
+        assert_se(epoll_ctl(u->meta.manager->epoll_fd, EPOLL_CTL_DEL, w->fd, NULL) >= 0);
+        assert_se(close_nointr(w->fd) == 0);
+
+        w->fd = -1;
+        w->type = WATCH_INVALID;
+        w->unit = NULL;
 }
 
 bool unit_job_is_applicable(Unit *u, JobType j) {
@@ -805,3 +863,103 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other) {
 
         return 0;
 }
+
+int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name) {
+        Unit *other;
+        int r;
+
+        if ((r = manager_load_unit(u->meta.manager, name, &other)) < 0)
+                return r;
+
+        if ((r = unit_add_dependency(u, d, other)) < 0)
+                return r;
+
+        return 0;
+}
+
+const char *unit_path(void) {
+        char *e;
+
+        if ((e = getenv("UNIT_PATH")))
+                if (path_is_absolute(e))
+                    return e;
+
+        return UNIT_PATH;
+}
+
+int set_unit_path(const char *p) {
+        char *cwd, *c;
+        int r;
+
+        /* This is mostly for debug purposes */
+
+        if (path_is_absolute(p)) {
+                if (!(c = strdup(p)))
+                        return -ENOMEM;
+        } else {
+                if (!(cwd = get_current_dir_name()))
+                        return -errno;
+
+                r = asprintf(&c, "%s/%s", cwd, p);
+                free(cwd);
+
+                if (r < 0)
+                        return -ENOMEM;
+        }
+
+        if (setenv("UNIT_PATH", c, 0) < 0) {
+                r = -errno;
+                free(c);
+                return r;
+        }
+
+        return 0;
+}
+
+char *unit_name_escape_path(const char *prefix, const char *path, const char *suffix) {
+        char *r, *t;
+        const char *f;
+        size_t a, b, c;
+
+        assert(path);
+
+        /* Takes a path and a suffix and prefix and makes a nice
+         * string suitable as unit name of it, escaping all weird
+         * chars on the way.
+         *
+         * / becomes ., and all chars not alloweed in a unit name get
+         * escaped as \xFF, including \ and ., of course. This
+         * escaping is hence reversible.
+         */
+
+        if (!prefix)
+                prefix = "";
+
+        if (!suffix)
+                suffix = "";
+
+        a = strlen(prefix);
+        b = strlen(path);
+        c = strlen(suffix);
+
+        if (!(r = new(char, a+b*4+c+1)))
+                return NULL;
+
+        memcpy(r, prefix, a);
+
+        for (f = path, t = r+a; *f; f++) {
+                if (*f == '/')
+                        *(t++) = '.';
+                else if (*f == '.' || *f == '\\' || !strchr(VALID_CHARS, *f)) {
+                        *(t++) = '\\';
+                        *(t++) = 'x';
+                        *(t++) = hexchar(*f > 4);
+                        *(t++) = hexchar(*f);
+                } else
+                        *(t++) = *f;
+        }
+
+        memcpy(t, suffix, c+1);
+
+        return r;
+}