[systemd-commits] 10 commits - Makefile.am man/systemd-activate.xml man/systemd.exec.xml man/systemd.service.xml src/activate src/core src/python-systemd src/shared src/test TODO units/systemd-sysctl.service.in

Zbigniew Jędrzejewski-Szmek zbyszek at kemper.freedesktop.org
Fri Mar 15 19:58:54 PDT 2013


 Makefile.am                        |   23 ++
 TODO                               |    6 
 man/systemd-activate.xml           |   14 +
 man/systemd.exec.xml               |    4 
 man/systemd.service.xml            |    2 
 src/activate/activate.c            |   33 +++
 src/core/execute.c                 |   43 ++++
 src/core/execute.h                 |   10 -
 src/core/manager.c                 |    6 
 src/core/manager.h                 |    3 
 src/core/mount-setup.c             |    1 
 src/core/mount.c                   |   29 +++
 src/core/namespace.c               |  226 ++++++++++---------------
 src/core/namespace.h               |   16 +
 src/core/service.c                 |   29 +++
 src/core/smack-setup.c             |  109 ++++++++----
 src/core/socket.c                  |   20 ++
 src/core/swap.c                    |   20 ++
 src/core/timer.c                   |    4 
 src/python-systemd/_daemon.c       |  323 +++++++++++++++++++++++++++++++++++++
 src/python-systemd/_reader.c       |   17 -
 src/python-systemd/daemon.py       |   53 ++++++
 src/python-systemd/docs/daemon.rst |   16 +
 src/python-systemd/docs/index.rst  |    1 
 src/python-systemd/pyutil.h        |   19 ++
 src/shared/util.c                  |   44 +++++
 src/shared/util.h                  |    1 
 src/test/test-ns.c                 |   14 +
 units/systemd-sysctl.service.in    |    1 
 29 files changed, 878 insertions(+), 209 deletions(-)

New commits:
commit 1a512bf48cdc05d135c9b2770ca5d0bdd6310666
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Fri Mar 15 22:48:45 2013 -0400

    timer: fix grammar in message

diff --git a/src/core/timer.c b/src/core/timer.c
index 8061f79..fd8e50e 100644
--- a/src/core/timer.c
+++ b/src/core/timer.c
@@ -328,7 +328,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
         if (found_monotonic) {
                 char buf[FORMAT_TIMESPAN_MAX];
                 log_debug_unit(UNIT(t)->id,
-                               "%s: Monotonic timer elapses in %s the next time.",
+                               "%s: Monotonic timer elapses in %s.",
                                UNIT(t)->id,
                                format_timespan(buf, sizeof(buf), t->next_elapse_monotonic > ts.monotonic ? t->next_elapse_monotonic - ts.monotonic : 0));
 
@@ -341,7 +341,7 @@ static void timer_enter_waiting(Timer *t, bool initial) {
         if (found_realtime) {
                 char buf[FORMAT_TIMESTAMP_MAX];
                 log_debug_unit(UNIT(t)->id,
-                               "%s: Realtime timer elapses at %s the next time.",
+                               "%s: Realtime timer elapses at %s.",
                                UNIT(t)->id,
                                format_timestamp(buf, sizeof(buf), t->next_elapse_realtime));
 

commit c17ec25e4d9bd6c8e8617416f813e25b2ebbafc5
Author: Michal Sekletar <msekleta at redhat.com>
Date:   Thu Mar 14 18:12:27 2013 +0100

    core: reuse the same /tmp, /var/tmp and inaccessible dir
    
    All Execs within the service, will get mounted the same
    /tmp and /var/tmp directories, if service is configured with
    PrivateTmp=yes. Temporary directories are cleaned up by service
    itself in addition to systemd-tmpfiles. Directory which is mounted
    as inaccessible is created at runtime in /run/systemd.

diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index 9c31baf..b1cd685 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -1107,7 +1107,9 @@
                                 processes via
                                 <filename>/tmp</filename> or
                                 <filename>/var/tmp</filename>
-                                impossible. Defaults to
+                                impossible. All temporary data created
+                                by service will be removed after service
+                                is stopped. Defaults to
                                 false.</para></listitem>
                         </varlistentry>
 
diff --git a/src/core/execute.c b/src/core/execute.c
index 92cf174..18e25fa 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -173,6 +173,18 @@ static bool is_terminal_output(ExecOutput o) {
                 o == EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
 }
 
+void exec_context_serialize(const ExecContext *context, Unit *u, FILE *f) {
+        assert(context);
+        assert(u);
+        assert(f);
+
+        if (context->tmp_dir)
+                unit_serialize_item(u, f, "tmp-dir", context->tmp_dir);
+
+        if (context->var_tmp_dir)
+                unit_serialize_item(u, f, "var-tmp-dir", context->var_tmp_dir);
+}
+
 static int open_null_as(int flags, int nfd) {
         int fd, r;
 
@@ -968,7 +980,7 @@ static int apply_seccomp(uint32_t *syscall_filter) {
 
 int exec_spawn(ExecCommand *command,
                char **argv,
-               const ExecContext *context,
+               ExecContext *context,
                int fds[], unsigned n_fds,
                char **environment,
                bool apply_permissions,
@@ -1036,6 +1048,12 @@ int exec_spawn(ExecCommand *command,
 
         cgroup_attribute_apply_list(cgroup_attributes, cgroup_bondings);
 
+        if (context->private_tmp && !context->tmp_dir && !context->var_tmp_dir) {
+                r = setup_tmpdirs(&context->tmp_dir, &context->var_tmp_dir);
+                if (r < 0)
+                        return r;
+        }
+
         pid = fork();
         if (pid < 0)
                 return -errno;
@@ -1302,6 +1320,8 @@ int exec_spawn(ExecCommand *command,
                         err = setup_namespace(context->read_write_dirs,
                                               context->read_only_dirs,
                                               context->inaccessible_dirs,
+                                              context->tmp_dir,
+                                              context->var_tmp_dir,
                                               context->private_tmp,
                                               context->mount_flags);
                         if (err < 0) {
@@ -1530,7 +1550,23 @@ void exec_context_init(ExecContext *c) {
         c->timer_slack_nsec = (nsec_t) -1;
 }
 
-void exec_context_done(ExecContext *c) {
+void exec_context_tmp_dirs_done(ExecContext *c) {
+        assert(c);
+
+        if (c->tmp_dir) {
+                rm_rf_dangerous(c->tmp_dir, false, true, false);
+                free(c->tmp_dir);
+                c->tmp_dir = NULL;
+        }
+
+        if (c->var_tmp_dir) {
+                rm_rf_dangerous(c->var_tmp_dir, false, true, false);
+                free(c->var_tmp_dir);
+                c->var_tmp_dir = NULL;
+        }
+}
+
+void exec_context_done(ExecContext *c, bool reloading_or_reexecuting) {
         unsigned l;
 
         assert(c);
@@ -1594,6 +1630,9 @@ void exec_context_done(ExecContext *c) {
 
         free(c->syscall_filter);
         c->syscall_filter = NULL;
+
+        if (!reloading_or_reexecuting)
+                exec_context_tmp_dirs_done(c);
 }
 
 void exec_command_done(ExecCommand *c) {
diff --git a/src/core/execute.h b/src/core/execute.h
index 001eb0e..3ce9221 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -36,6 +36,8 @@ typedef struct ExecContext ExecContext;
 struct CGroupBonding;
 struct CGroupAttribute;
 
+typedef struct Unit Unit;
+
 #include "list.h"
 #include "util.h"
 
@@ -141,6 +143,8 @@ struct ExecContext {
         bool non_blocking;
         bool private_tmp;
         bool private_network;
+        char *tmp_dir;
+        char *var_tmp_dir;
 
         bool no_new_privileges;
 
@@ -164,7 +168,7 @@ struct ExecContext {
 
 int exec_spawn(ExecCommand *command,
                char **argv,
-               const ExecContext *context,
+               ExecContext *context,
                int fds[], unsigned n_fds,
                char **environment,
                bool apply_permissions,
@@ -192,13 +196,15 @@ void exec_command_append_list(ExecCommand **l, ExecCommand *e);
 int exec_command_set(ExecCommand *c, const char *path, ...);
 
 void exec_context_init(ExecContext *c);
-void exec_context_done(ExecContext *c);
+void exec_context_done(ExecContext *c, bool reloading_or_reexecuting);
+void exec_context_tmp_dirs_done(ExecContext *c);
 void exec_context_dump(ExecContext *c, FILE* f, const char *prefix);
 void exec_context_tty_reset(const ExecContext *context);
 
 int exec_context_load_environment(const ExecContext *c, char ***l);
 
 bool exec_context_may_touch_console(ExecContext *c);
+void exec_context_serialize(const ExecContext *c, Unit *u, FILE *f);
 
 void exec_status_start(ExecStatus *s, pid_t pid);
 void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status);
diff --git a/src/core/manager.c b/src/core/manager.c
index 8e66732..a01710f 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -2333,6 +2333,12 @@ static bool manager_is_booting_or_shutting_down(Manager *m) {
         return false;
 }
 
+bool manager_is_reloading_or_reexecuting(Manager *m) {
+        assert(m);
+
+        return m->n_reloading != 0;
+}
+
 void manager_reset_failed(Manager *m) {
         Unit *u;
         Iterator i;
diff --git a/src/core/manager.h b/src/core/manager.h
index c486a16..9d8d943 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -85,6 +85,7 @@ struct Watch {
 #include "set.h"
 #include "dbus.h"
 #include "path-lookup.h"
+#include "execute.h"
 
 struct Manager {
         /* Note that the set of units we know of is allowed to be
@@ -283,6 +284,8 @@ int manager_distribute_fds(Manager *m, FDSet *fds);
 
 int manager_reload(Manager *m);
 
+bool manager_is_reloading_or_reexecuting(Manager *m);
+
 void manager_reset_failed(Manager *m);
 
 void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success);
diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c
index ce10be9..6140f56 100644
--- a/src/core/mount-setup.c
+++ b/src/core/mount-setup.c
@@ -447,6 +447,7 @@ int mount_setup(bool loaded_policy) {
          * systemd. */
         mkdir_label("/run/systemd", 0755);
         mkdir_label("/run/systemd/system", 0755);
+        mkdir_label("/run/systemd/inaccessible", 0000);
 
         return 0;
 }
diff --git a/src/core/mount.c b/src/core/mount.c
index 7a1b411..0adf04e 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -25,6 +25,7 @@
 #include <sys/epoll.h>
 #include <signal.h>
 
+#include "manager.h"
 #include "unit.h"
 #include "mount.h"
 #include "load-fragment.h"
@@ -126,7 +127,7 @@ static void mount_done(Unit *u) {
         mount_parameters_done(&m->parameters_proc_self_mountinfo);
         mount_parameters_done(&m->parameters_fragment);
 
-        exec_context_done(&m->exec_context);
+        exec_context_done(&m->exec_context, manager_is_reloading_or_reexecuting(u->manager));
         exec_command_done_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX);
         m->control_command = NULL;
 
@@ -870,6 +871,7 @@ static void mount_enter_dead(Mount *m, MountResult f) {
         if (f != MOUNT_SUCCESS)
                 m->result = f;
 
+        exec_context_tmp_dirs_done(&m->exec_context);
         mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD);
 }
 
@@ -1163,6 +1165,8 @@ static int mount_serialize(Unit *u, FILE *f, FDSet *fds) {
         if (m->control_command_id >= 0)
                 unit_serialize_item(u, f, "control-command", mount_exec_command_to_string(m->control_command_id));
 
+        exec_context_serialize(&m->exec_context, UNIT(m), f);
+
         return 0;
 }
 
@@ -1219,7 +1223,22 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F
                         m->control_command_id = id;
                         m->control_command = m->exec_command + id;
                 }
+        } else if (streq(key, "tmp-dir")) {
+                char *t;
+
+                t = strdup(value);
+                if (!t)
+                        return log_oom();
+
+                m->exec_context.tmp_dir = t;
+        } else if (streq(key, "var-tmp-dir")) {
+                char *t;
+
+                t = strdup(value);
+                if (!t)
+                        return log_oom();
 
+                m->exec_context.var_tmp_dir = t;
         } else
                 log_debug_unit(UNIT(m)->id,
                                "Unknown serialization key '%s'", key);
diff --git a/src/core/namespace.c b/src/core/namespace.c
index ba18ddc..ceeed2e 100644
--- a/src/core/namespace.c
+++ b/src/core/namespace.c
@@ -36,23 +36,24 @@
 #include "path-util.h"
 #include "namespace.h"
 #include "missing.h"
+#include "execute.h"
 
-typedef enum PathMode {
+typedef enum MountMode {
         /* This is ordered by priority! */
         INACCESSIBLE,
         READONLY,
         PRIVATE_TMP,
         PRIVATE_VAR_TMP,
         READWRITE
-} PathMode;
+} MountMode;
 
-typedef struct Path {
+typedef struct BindMount {
         const char *path;
-        PathMode mode;
+        MountMode mode;
         bool done;
-} Path;
+} BindMount;
 
-static int append_paths(Path **p, char **strv, PathMode mode) {
+static int append_mounts(BindMount **p, char **strv, MountMode mode) {
         char **i;
 
         STRV_FOREACH(i, strv) {
@@ -68,8 +69,8 @@ static int append_paths(Path **p, char **strv, PathMode mode) {
         return 0;
 }
 
-static int path_compare(const void *a, const void *b) {
-        const Path *p = a, *q = b;
+static int mount_path_compare(const void *a, const void *b) {
+        const BindMount *p = a, *q = b;
 
         if (path_equal(p->path, q->path)) {
 
@@ -93,14 +94,13 @@ static int path_compare(const void *a, const void *b) {
         return 0;
 }
 
-static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible) {
-        Path *f, *t, *previous;
+static void drop_duplicates(BindMount *m, unsigned *n) {
+        BindMount *f, *t, *previous;
 
-        assert(p);
+        assert(m);
         assert(n);
-        assert(need_inaccessible);
 
-        for (f = p, t = p, previous = NULL; f < p+*n; f++) {
+        for (f = m, t = m, previous = NULL; f < m+*n; f++) {
 
                 /* The first one wins */
                 if (previous && path_equal(f->path, previous->path))
@@ -109,37 +109,33 @@ static void drop_duplicates(Path *p, unsigned *n, bool *need_inaccessible) {
                 t->path = f->path;
                 t->mode = f->mode;
 
-                if (t->mode == INACCESSIBLE)
-                        *need_inaccessible = true;
-
                 previous = t;
 
                 t++;
         }
 
-        *n = t - p;
+        *n = t - m;
 }
 
 static int apply_mount(
-                Path *p,
+                BindMount *m,
                 const char *tmp_dir,
-                const char *var_tmp_dir,
-                const char *inaccessible_dir) {
+                const char *var_tmp_dir) {
 
         const char *what;
         int r;
 
-        assert(p);
+        assert(m);
 
-        switch (p->mode) {
+        switch (m->mode) {
 
         case INACCESSIBLE:
-                what = inaccessible_dir;
+                what = "/run/systemd/inaccessible";
                 break;
 
         case READONLY:
         case READWRITE:
-                what = p->path;
+                what = m->path;
                 break;
 
         case PRIVATE_TMP:
@@ -156,133 +152,99 @@ static int apply_mount(
 
         assert(what);
 
-        r = mount(what, p->path, NULL, MS_BIND|MS_REC, NULL);
+        r = mount(what, m->path, NULL, MS_BIND|MS_REC, NULL);
         if (r >= 0)
-                log_debug("Successfully mounted %s to %s", what, p->path);
+                log_debug("Successfully mounted %s to %s", what, m->path);
 
         return r;
 }
 
-static int make_read_only(Path *p) {
+static int make_read_only(BindMount *m) {
         int r;
 
-        assert(p);
+        assert(m);
 
-        if (p->mode != INACCESSIBLE && p->mode != READONLY)
+        if (m->mode != INACCESSIBLE && m->mode != READONLY)
                 return 0;
 
-        r = mount(NULL, p->path, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL);
+        r = mount(NULL, m->path, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL);
         if (r < 0)
                 return -errno;
 
         return 0;
 }
 
-int setup_namespace(
-                char **writable,
-                char **readable,
-                char **inaccessible,
-                bool private_tmp,
-                unsigned long flags) {
-
-        char
-                tmp_dir[] = "/tmp/systemd-private-XXXXXX",
-                var_tmp_dir[] = "/var/tmp/systemd-private-XXXXXX",
-                inaccessible_dir[] = "/tmp/systemd-inaccessible-XXXXXX";
-
-        Path *paths, *p;
-        unsigned n;
-        bool need_inaccessible = false;
-        bool remove_tmp = false, remove_var_tmp = false, remove_inaccessible = false;
-        int r;
-
-        if (!flags)
-                flags = MS_SHARED;
+int setup_tmpdirs(char **tmp_dir,
+                  char **var_tmp_dir) {
+        int r = 0;
+        char tmp_dir_template[] = "/tmp/systemd-private-XXXXXX",
+             var_tmp_dir_template[] = "/var/tmp/systemd-private-XXXXXX";
 
-        n =
-                strv_length(writable) +
-                strv_length(readable) +
-                strv_length(inaccessible) +
-                (private_tmp ? 2 : 0);
+        assert(tmp_dir);
+        assert(var_tmp_dir);
 
-        p = paths = alloca(sizeof(Path) * n);
-        if ((r = append_paths(&p, writable, READWRITE)) < 0 ||
-            (r = append_paths(&p, readable, READONLY)) < 0 ||
-            (r = append_paths(&p, inaccessible, INACCESSIBLE)) < 0)
-                goto fail;
+        r = create_tmp_dir(tmp_dir_template, 0000, true, tmp_dir);
+        if (r < 0)
+                goto fail2;
 
-        if (private_tmp) {
-                p->path = "/tmp";
-                p->mode = PRIVATE_TMP;
-                p++;
+        r = create_tmp_dir(var_tmp_dir_template, 0000, true, var_tmp_dir);
+        if (r < 0)
+                goto fail1;
 
-                p->path = "/var/tmp";
-                p->mode = PRIVATE_VAR_TMP;
-                p++;
-        }
+        return 0;
 
-        assert(paths + n == p);
+fail1:
+        rmdir(*tmp_dir);
+        free(*tmp_dir);
+        *tmp_dir = NULL;
 
-        qsort(paths, n, sizeof(Path), path_compare);
-        drop_duplicates(paths, &n, &need_inaccessible);
+fail2:
+        return r;
+}
 
-        if (need_inaccessible) {
-                mode_t u;
-                char *d;
+int setup_namespace(char** read_write_dirs,
+                    char** read_only_dirs,
+                    char** inaccessible_dirs,
+                    char* tmp_dir,
+                    char* var_tmp_dir,
+                    bool private_tmp,
+                    unsigned mount_flags) {
 
-                u = umask(0777);
-                d = mkdtemp(inaccessible_dir);
-                umask(u);
+        unsigned n = strv_length(read_write_dirs) +
+                     strv_length(read_only_dirs) +
+                     strv_length(inaccessible_dirs) +
+                     (private_tmp ? 2 : 0);
+        BindMount *m, *mounts;
+        int r = 0;
 
-                if (!d) {
-                        r = -errno;
-                        goto fail;
-                }
+        if (!mount_flags)
+                mount_flags = MS_SHARED;
 
-                remove_inaccessible = true;
+        if (unshare(CLONE_NEWNS) < 0) {
+                r = -errno;
+                goto fail;
         }
 
-        if (private_tmp) {
-                mode_t u;
-                char *d;
-
-                u = umask(0000);
-                d = mkdtemp(tmp_dir);
-                umask(u);
-
-                if (!d) {
-                        r = -errno;
-                        goto fail;
-                }
-
-                remove_tmp = true;
-
-                u = umask(0000);
-                d = mkdtemp(var_tmp_dir);
-                umask(u);
-
-                if (!d) {
-                        r = -errno;
-                        goto fail;
-                }
-
-                remove_var_tmp = true;
+        m = mounts = (BindMount *) alloca(n * sizeof(BindMount));
+        if ((r = append_mounts(&m, read_write_dirs, READWRITE)) < 0 ||
+                (r = append_mounts(&m, read_only_dirs, READONLY)) < 0 ||
+                (r = append_mounts(&m, inaccessible_dirs, INACCESSIBLE)) < 0)
+                goto fail;
 
-                if (chmod(tmp_dir, 0777 + S_ISVTX) < 0) {
-                        r = -errno;
-                        goto fail;
-                }
+        if (private_tmp) {
+                m->path = "/tmp";
+                m->mode = PRIVATE_TMP;
+                m++;
 
-                if (chmod(var_tmp_dir, 0777 + S_ISVTX) < 0) {
-                        r = -errno;
-                        goto fail;
-                }
+                m->path = "/var/tmp";
+                m->mode = PRIVATE_VAR_TMP;
+                m++;
         }
 
-        if (unshare(CLONE_NEWNS) < 0) {
-                r = -errno;
-                goto fail;
-        }
+        assert(mounts + n == m);
+
+        qsort(mounts, n, sizeof(BindMount), mount_path_compare);
+        drop_duplicates(mounts, &n);
 
         /* Remount / as SLAVE so that nothing now mounted in the namespace
            shows up in the parent */
@@ -291,20 +253,20 @@ int setup_namespace(
                 goto fail;
         }
 
-        for (p = paths; p < paths + n; p++) {
-                r = apply_mount(p, tmp_dir, var_tmp_dir, inaccessible_dir);
+        for (m = mounts; m < mounts + n; ++m) {
+                r = apply_mount(m, tmp_dir, var_tmp_dir);
                 if (r < 0)
                         goto undo_mounts;
         }
 
-        for (p = paths; p < paths + n; p++) {
-                r = make_read_only(p);
+        for (m = mounts; m < mounts + n; ++m) {
+                r = make_read_only(m);
                 if (r < 0)
                         goto undo_mounts;
         }
 
         /* Remount / as the desired mode */
-        if (mount(NULL, "/", NULL, flags|MS_REC, NULL) < 0) {
+        if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) {
                 r = -errno;
                 goto undo_mounts;
         }
@@ -312,19 +274,11 @@ int setup_namespace(
         return 0;
 
 undo_mounts:
-        for (p = paths; p < paths + n; p++)
-                if (p->done)
-                        umount2(p->path, MNT_DETACH);
+        for (m = mounts; m < mounts + n; ++m) {
+                if (m->done)
+                        umount2(m->path, MNT_DETACH);
+        }
 
 fail:
-        if (remove_inaccessible)
-                rmdir(inaccessible_dir);
-
-        if (remove_tmp)
-                rmdir(tmp_dir);
-
-        if (remove_var_tmp)
-                rmdir(var_tmp_dir);
-
         return r;
 }
diff --git a/src/core/namespace.h b/src/core/namespace.h
index 5d72ed9..7b886b8 100644
--- a/src/core/namespace.h
+++ b/src/core/namespace.h
@@ -23,9 +23,13 @@
 
 #include <stdbool.h>
 
-int setup_namespace(
-                char **writable,
-                char **readable,
-                char **inaccessible,
-                bool private_tmp,
-                unsigned long flags);
+typedef struct ExecContext ExecContext;
+
+int setup_tmpdirs(char **tmp_dir, char **var_tmp_dir);
+int setup_namespace(char **read_write_dirs,
+                    char **read_only_dirs,
+                    char **inaccessible_dirs,
+                    char *tmp_dir,
+                    char *var_tmp_dir,
+                    bool private_tmp,
+                    unsigned mount_flags);
diff --git a/src/core/service.c b/src/core/service.c
index fd90ceb..080d583 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -283,7 +283,7 @@ static void service_done(Unit *u) {
         free(s->status_text);
         s->status_text = NULL;
 
-        exec_context_done(&s->exec_context);
+        exec_context_done(&s->exec_context, manager_is_reloading_or_reexecuting(u->manager));
         exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
         s->control_command = NULL;
         s->main_command = NULL;
@@ -1932,6 +1932,9 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
 
         s->forbid_restart = false;
 
+        /* we want fresh tmpdirs in case service is started again immediately */
+        exec_context_tmp_dirs_done(&s->exec_context);
+
         return;
 
 fail:
@@ -2638,6 +2641,12 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
                 dual_timestamp_serialize(f, "watchdog-timestamp",
                                          &s->watchdog_timestamp);
 
+        if (s->exec_context.tmp_dir)
+                unit_serialize_item(u, f, "tmp-dir", s->exec_context.tmp_dir);
+
+        if (s->exec_context.var_tmp_dir)
+                unit_serialize_item(u, f, "var-tmp-dir", s->exec_context.var_tmp_dir);
+
         return 0;
 }
 
@@ -2756,7 +2765,23 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
                 dual_timestamp_deserialize(value, &s->main_exec_status.exit_timestamp);
         else if (streq(key, "watchdog-timestamp"))
                 dual_timestamp_deserialize(value, &s->watchdog_timestamp);
-        else
+        else if (streq(key, "tmp-dir")) {
+                char *t;
+
+                t = strdup(value);
+                if (!t)
+                        return log_oom();
+
+                s->exec_context.tmp_dir = t;
+        } else if (streq(key, "var-tmp-dir")) {
+                char *t;
+
+                t = strdup(value);
+                if (!t)
+                        return log_oom();
+
+                s->exec_context.var_tmp_dir = t;
+        } else
                 log_debug_unit(u->id, "Unknown serialization key '%s'", key);
 
         return 0;
diff --git a/src/core/socket.c b/src/core/socket.c
index ee9de4e..a3e3631 100644
--- a/src/core/socket.c
+++ b/src/core/socket.c
@@ -127,7 +127,7 @@ static void socket_done(Unit *u) {
 
         socket_free_ports(s);
 
-        exec_context_done(&s->exec_context);
+        exec_context_done(&s->exec_context, manager_is_reloading_or_reexecuting(u->manager));
         exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
         s->control_command = NULL;
 
@@ -1253,6 +1253,7 @@ static void socket_enter_dead(Socket *s, SocketResult f) {
         if (f != SOCKET_SUCCESS)
                 s->result = f;
 
+        exec_context_tmp_dirs_done(&s->exec_context);
         socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD);
 }
 
@@ -1742,6 +1743,8 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
                 }
         }
 
+        exec_context_serialize(&s->exec_context, UNIT(s), f);
+
         return 0;
 }
 
@@ -1901,7 +1904,22 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
                                 p->fd = fdset_remove(fds, fd);
                         }
                 }
+        } else if (streq(key, "tmp-dir")) {
+                char *t;
+
+                t = strdup(value);
+                if (!t)
+                        return log_oom();
+
+                s->exec_context.tmp_dir = t;
+        } else if (streq(key, "var-tmp-dir")) {
+                char *t;
+
+                t = strdup(value);
+                if (!t)
+                        return log_oom();
 
+                s->exec_context.var_tmp_dir = t;
         } else
                 log_debug_unit(UNIT(s)->id,
                                "Unknown serialization key '%s'", key);
diff --git a/src/core/swap.c b/src/core/swap.c
index a0e55a0..dc98f47 100644
--- a/src/core/swap.c
+++ b/src/core/swap.c
@@ -125,7 +125,7 @@ static void swap_done(Unit *u) {
         free(s->parameters_fragment.what);
         s->parameters_fragment.what = NULL;
 
-        exec_context_done(&s->exec_context);
+        exec_context_done(&s->exec_context, manager_is_reloading_or_reexecuting(u->manager));
         exec_command_done_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX);
         s->control_command = NULL;
 
@@ -632,6 +632,7 @@ static void swap_enter_dead(Swap *s, SwapResult f) {
         if (f != SWAP_SUCCESS)
                 s->result = f;
 
+        exec_context_tmp_dirs_done(&s->exec_context);
         swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD);
 }
 
@@ -831,6 +832,8 @@ static int swap_serialize(Unit *u, FILE *f, FDSet *fds) {
         if (s->control_command_id >= 0)
                 unit_serialize_item(u, f, "control-command", swap_exec_command_to_string(s->control_command_id));
 
+        exec_context_serialize(&s->exec_context, UNIT(s), f);
+
         return 0;
 }
 
@@ -874,7 +877,22 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD
                         s->control_command_id = id;
                         s->control_command = s->exec_command + id;
                 }
+        } else if (streq(key, "tmp-dir")) {
+                char *t;
+
+                t = strdup(value);
+                if (!t)
+                        return log_oom();
+
+                s->exec_context.tmp_dir = t;
+        } else if (streq(key, "var-tmp-dir")) {
+                char *t;
+
+                t = strdup(value);
+                if (!t)
+                        return log_oom();
 
+                s->exec_context.var_tmp_dir = t;
         } else
                 log_debug_unit(u->id, "Unknown serialization key '%s'", key);
 
diff --git a/src/shared/util.c b/src/shared/util.c
index dc2651f..34c5330 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -5682,3 +5682,47 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *sear
 
         return search_and_fopen_internal(path, mode, s, _f);
 }
+
+int create_tmp_dir(char template[], mode_t mask, bool need_sticky, char** dir_name) {
+        int r = 0;
+        char *d = NULL;
+        bool remove = false;
+        mode_t _cleanup_umask_ u;
+
+        assert(dir_name);
+
+        u = umask(mask);
+        d = mkdtemp(template);
+        if (!d) {
+                r = -errno;
+                log_debug("Can't create directory");
+                goto fail;
+        }
+
+        remove = true;
+
+        log_debug("Created temporary directory : %s", template);
+
+        d = strdup(template);
+        if (!d) {
+                r = log_oom();
+                goto fail;
+        }
+
+        if (need_sticky) {
+                r = chmod(template, 0777 | S_ISVTX);
+                if (r < 0) {
+                        r = -errno;
+                        goto fail;
+                }
+                log_debug("Setting sticky bit on : %s", template);
+        }
+
+        *dir_name = d;
+
+        return 0;
+fail:
+        if (remove)
+                rmdir(template);
+        return r;
+}
diff --git a/src/shared/util.h b/src/shared/util.h
index f0dfe19..8ac4bbc 100644
--- a/src/shared/util.h
+++ b/src/shared/util.h
@@ -574,6 +574,7 @@ int on_ac_power(void);
 
 int search_and_fopen(const char *path, const char *mode, const char **search, FILE **_f);
 int search_and_fopen_nulstr(const char *path, const char *mode, const char *search, FILE **_f);
+int create_tmp_dir(char template[], mode_t mask, bool need_sticky, char** dir_name);
 
 #define FOREACH_LINE(line, f, on_error)                         \
         for (;;)                                                \
diff --git a/src/test/test-ns.c b/src/test/test-ns.c
index b1c759f..ad0d041 100644
--- a/src/test/test-ns.c
+++ b/src/test/test-ns.c
@@ -26,6 +26,7 @@
 #include <linux/fs.h>
 
 #include "namespace.h"
+#include "execute.h"
 #include "log.h"
 
 int main(int argc, char *argv[]) {
@@ -47,8 +48,19 @@ int main(int argc, char *argv[]) {
         };
 
         int r;
+        char tmp_dir[] = "/tmp/systemd-private-XXXXXX",
+             var_tmp_dir[] = "/var/tmp/systemd-private-XXXXXX";
 
-        r = setup_namespace((char**) writable, (char**) readonly, (char**) inaccessible, true, 0);
+        assert_se(mkdtemp(tmp_dir));
+        assert_se(mkdtemp(var_tmp_dir));
+
+        r = setup_namespace((char **) writable,
+                            (char **) readonly,
+                            (char **) inaccessible,
+                            tmp_dir,
+                            var_tmp_dir,
+                            true,
+                            0);
         if (r < 0) {
                 log_error("Failed to setup namespace: %s", strerror(-r));
                 return 1;

commit 3b953d68c628c6ae70adba871719ac0f16083b51
Author: Josh Triplett <josh at joshtriplett.org>
Date:   Fri Mar 15 08:50:12 2013 -0700

    TODO: uses for SO_REUSEPORT
    
    The new socket option SO_REUSEPORT would enable some new functionality;
    add it to TODO.

diff --git a/TODO b/TODO
index addf32e..fcc3471 100644
--- a/TODO
+++ b/TODO
@@ -46,6 +46,11 @@ Fedora 19:
 
 Features:
 
+* Support SO_REUSEPORT with socket activation:
+  - Let systemd maintain a pool of servers.
+  - Use for seamless upgrades, by running the new server before stopping the
+    old.
+
 * BootLoaderSpec: drop allowing ext234 for $BOOT. Clarify that the
   kernel has to be in $BOOT. Clarify that the boot loader should be
   installed to the ESP. Define a way how an installer can figure out

commit abbacb1defaaecb8d2477685f7bb3fabcf58585b
Author: Nathaniel Chen <nathaniel.chen at intel.com>
Date:   Tue Mar 12 16:16:44 2013 -0700

    smack-setup: enable Smack/CIPSO mapping
    
    CIPSO is the Common IP Security Option, an IETF standard for setting
    security levels for a process sending packets. In Smack kernels,
    CIPSO headers are mapped to Smack labels automatically, but can be changed.
    
    This patch writes label/category mappings from /etc/smack/cipso/ to
    /sys/fs/smackfs/cipso2. The mapping format is "%s%4d%4d"["%4d"]...
    
    For more information about Smack and CIPSO, see:
      https://kernel.org/doc/Documentation/security/Smack.txt

diff --git a/src/core/smack-setup.c b/src/core/smack-setup.c
index 804678d..73eeb04 100644
--- a/src/core/smack-setup.c
+++ b/src/core/smack-setup.c
@@ -40,6 +40,7 @@
 #include "label.h"
 
 #define SMACK_CONFIG "/etc/smack/accesses.d/"
+#define CIPSO_CONFIG "/etc/smack/cipso/"
 
 static int write_rules(const char* dstpath, const char* srcdir) {
         _cleanup_fclose_ FILE *dst = NULL;
@@ -124,9 +125,26 @@ int smack_setup(void) {
                 return 0;
         case 0:
                 log_info("Successfully loaded Smack policies.");
+                break;
+        default:
+                log_warning("Failed to load Smack access rules: %s, ignoring.",
+                            strerror(abs(r)));
+                return 0;
+        }
+
+        r = write_rules("/sys/fs/smackfs/cipso2", CIPSO_CONFIG);
+        switch(r) {
+        case -ENOENT:
+                log_debug("Smack/CIPSO is not enabled in the kernel.");
+                return 0;
+        case ENOENT:
+                log_debug("Smack/CIPSO access rules directory " CIPSO_CONFIG " not found");
+                return 0;
+        case 0:
+                log_info("Successfully loaded Smack/CIPSO policies.");
                 return 0;
         default:
-                log_warning("Failed to load smack access rules: %s, ignoring.",
+                log_warning("Failed to load Smack/CIPSO access rules: %s, ignoring.",
                             strerror(abs(r)));
                 return 0;
         }

commit a4783bd17ad96f55b0fe83a50959da13555292bf
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Wed Mar 13 23:23:06 2013 -0400

    smack-setup: extract rule writing into a separate function
    
    Check all errors.

diff --git a/src/core/smack-setup.c b/src/core/smack-setup.c
index d0f1ac0..804678d 100644
--- a/src/core/smack-setup.c
+++ b/src/core/smack-setup.c
@@ -39,66 +39,95 @@
 #include "log.h"
 #include "label.h"
 
-#define ACCESSES_D_PATH "/etc/smack/accesses.d/"
+#define SMACK_CONFIG "/etc/smack/accesses.d/"
 
-int smack_setup(void) {
-        _cleanup_fclose_ FILE *smack = NULL;
+static int write_rules(const char* dstpath, const char* srcdir) {
+        _cleanup_fclose_ FILE *dst = NULL;
         _cleanup_closedir_ DIR *dir = NULL;
         struct dirent *entry;
         char buf[NAME_MAX];
         int dfd = -1;
+        int r = 0;
 
-        smack = fopen("/sys/fs/smackfs/load2", "we");
-        if (!smack)  {
-                if (errno == ENOENT)
-                        log_debug("Smack is not enabled in the kernel, not loading access rules.");
-                else
-                        log_warning("Failed to open /sys/fs/smackfs/load2: %m");
-                return 0;
+        dst = fopen(dstpath, "we");
+        if (!dst)  {
+                if (errno != ENOENT)
+                        log_warning("Failed to open %s: %m", dstpath);
+                return -errno; /* negative error */
         }
 
-        /* write rules to load2 from every file in the directory */
-        dir = opendir(ACCESSES_D_PATH);
+        /* write rules to dst from every file in the directory */
+        dir = opendir(srcdir);
         if (!dir) {
-                log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING,
-                         "Opening Smack access rules directory "
-                         ACCESSES_D_PATH ": %m");
-                return 0;
+                if (errno != ENOENT)
+                        log_warning("Failed to opendir %s: %m", srcdir);
+                return errno; /* positive on purpose */
         }
 
         dfd = dirfd(dir);
         assert(dfd >= 0);
 
         FOREACH_DIRENT(entry, dir, return 0) {
+                int fd;
                 _cleanup_fclose_ FILE *policy = NULL;
-                _cleanup_close_ int pol = -1;
 
-                pol = openat(dfd, entry->d_name, O_RDONLY|O_CLOEXEC);
-                if (pol < 0) {
-                        log_error("Smack access rule file %s not opened: %m",
-                                  entry->d_name);
+                fd = openat(dfd, entry->d_name, O_RDONLY|O_CLOEXEC);
+                if (fd < 0) {
+                        if (r == 0)
+                                r = -errno;
+                        log_warning("Failed to open %s: %m", entry->d_name);
                         continue;
                 }
 
-                policy = fdopen(pol, "re");
+                policy = fdopen(fd, "re");
                 if (!policy) {
-                        log_error("Smack access rule file %s not opened: %m",
-                                  entry->d_name);
+                        if (r == 0)
+                                r = -errno;
+                        close_nointr_nofail(fd);
+                        log_error("Failed to open %s: %m", entry->d_name);
                         continue;
                 }
 
-                pol = -1;
-
                 /* load2 write rules in the kernel require a line buffered stream */
                 FOREACH_LINE(buf, policy,
-                             log_error("Failed to read from Smack access rule file %s: %m",
+                             log_error("Failed to read line from %s: %m",
                                        entry->d_name)) {
-                        fputs(buf, smack);
-                        fflush(smack);
+                        if (!fputs(buf, dst)) {
+                                if (r == 0)
+                                        r = -EINVAL;
+                                log_error("Failed to write line to %s", dstpath);
+                                break;
+                        }
+                        if (fflush(dst)) {
+                                if (r == 0)
+                                        r = -errno;
+                                log_error("Failed to flush writes to %s: %m", dstpath);
+                                break;
+                        }
                 }
         }
 
-        log_info("Successfully loaded Smack policies.");
+       return r;
+}
+
 
-        return 0;
+int smack_setup(void) {
+        int r;
+
+        r = write_rules("/sys/fs/smackfs/load2", SMACK_CONFIG);
+        switch(r) {
+        case -ENOENT:
+                log_debug("Smack is not enabled in the kernel.");
+                return 0;
+        case ENOENT:
+                log_debug("Smack access rules directory " SMACK_CONFIG " not found");
+                return 0;
+        case 0:
+                log_info("Successfully loaded Smack policies.");
+                return 0;
+        default:
+                log_warning("Failed to load smack access rules: %s, ignoring.",
+                            strerror(abs(r)));
+                return 0;
+        }
 }

commit faeffa73a81ab5b59acfadeb571431fb0e42af70
Author: Umut Tezduyar <umut at tezduyar.com>
Date:   Wed Mar 13 10:27:38 2013 +0100

    units: ignore systemd-sysctl on shutdown
    
    No need to try to stop systemd-sysctl on shutdown as
    this service doesn't have ExecStop= anyways.

diff --git a/units/systemd-sysctl.service.in b/units/systemd-sysctl.service.in
index 45e1ceb..d914553 100644
--- a/units/systemd-sysctl.service.in
+++ b/units/systemd-sysctl.service.in
@@ -9,7 +9,6 @@
 Description=Apply Kernel Variables
 Documentation=man:systemd-sysctl.service(8) man:sysctl.d(5)
 DefaultDependencies=no
-Conflicts=shutdown.target
 After=systemd-readahead-collect.service systemd-readahead-replay.service
 Before=sysinit.target shutdown.target
 ConditionPathIsReadWrite=/proc/sys/

commit 89b1d5e0e49d3b3501e5f3aadcad712290bcd9bf
Author: Umut Tezduyar <umut at tezduyar.com>
Date:   Mon Mar 11 16:30:10 2013 +0100

    core: keep mountinfo .mounts until late shutdown
    
    .mount units coming from /proc/self/mountinfo file are
    unmounted after local-fs.target is reached during shutdown.
    
    Problem: .mount units popping up in mountinfo file are
    added to systemd without any dependency. For that reason,
    they are the first one to be unmounted during shutdown.
    Whichever program mounted the file system deserves a
    chance to also unmount it. This patch ensures that
    /proc/self/mountinfo units will be unmounted after
    local-fs.target during shutdown (if they haven't been
    unmounted already)

diff --git a/src/core/mount.c b/src/core/mount.c
index 73a7832..7a1b411 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -1501,6 +1501,14 @@ static int mount_add_one(
                         goto fail;
                 }
 
+                r = unit_add_dependency_by_name(u, UNIT_BEFORE, SPECIAL_LOCAL_FS_TARGET, NULL, true);
+                if (r < 0)
+                        goto fail;
+
+                r = unit_add_dependency_by_name(u, UNIT_CONFLICTS, SPECIAL_UMOUNT_TARGET, NULL, true);
+                if (r < 0)
+                        goto fail;
+
                 unit_add_to_load_queue(u);
         } else {
                 delete = false;

commit df34f169b8961c6ef1429cdb940206b42c3f9041
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Thu Mar 14 18:44:03 2013 -0400

    man: fix grammar

diff --git a/man/systemd.service.xml b/man/systemd.service.xml
index b8b19b3..2dd3b19 100644
--- a/man/systemd.service.xml
+++ b/man/systemd.service.xml
@@ -357,7 +357,7 @@
                                 word on the command line, in which
                                 case it will be replaced by the value
                                 of the environment variable split up
-                                at whitespace, resulting in no or more
+                                at whitespace, resulting in zero or more
                                 arguments. Note that the first
                                 argument (i.e. the program to execute)
                                 may not be a variable, since it must

commit b04c8c83e8d5670b0923c7cd7d6ea622b0187289
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Fri Mar 15 19:01:10 2013 -0400

    systemd-python: add systemd.daemon wrapping sd-daemon
    
    Please see the documentation (e.g. pydoc3 systemd.daemon) for full
    description. As usual, systemd._daemon wraps the raw interface, while
    systemd.daemon provides the more pythonic API. sd_listen_fds,
    sd_booted, sd_is_fifo, sd_is_socket, sd_is_socket_unix,
    sd_is_socket_inet, sd_is_mq, and SD_LISTEN_FDS_START are currently
    wrapped.

diff --git a/Makefile.am b/Makefile.am
index f687eca..7e9cdfd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3484,6 +3484,7 @@ if HAVE_PYTHON_DEVEL
 pkgpyexec_LTLIBRARIES = \
 	_journal.la \
 	id128.la \
+	_daemon.la \
 	_reader.la
 
 _journal_la_SOURCES = \
@@ -3526,6 +3527,27 @@ id128_la_LIBADD = \
 	$(PYTHON_LIBS) \
 	libsystemd-id128.la
 
+_daemon_la_SOURCES = \
+	src/python-systemd/_daemon.c \
+	src/python-systemd/pyutil.c \
+	src/python-systemd/pyutil.h
+
+_daemon_la_CFLAGS = \
+	$(AM_CFLAGS) \
+        -fvisibility=default \
+	$(PYTHON_CFLAGS) \
+	-I$(top_builddir)/src/python-systemd
+
+_daemon_la_LDFLAGS = \
+	$(AM_LDFLAGS) \
+	-shared \
+	-module \
+	-avoid-version
+
+_daemon_la_LIBADD = \
+	$(PYTHON_LIBS) \
+	libsystemd-daemon.la
+
 _reader_la_SOURCES = \
 	src/python-systemd/_reader.c \
 	src/python-systemd/pyutil.c \
@@ -3550,6 +3572,7 @@ _reader_la_LIBADD = \
 
 dist_pkgpyexec_PYTHON = \
 	src/python-systemd/journal.py \
+	src/python-systemd/daemon.py \
 	src/python-systemd/__init__.py
 
 src/python-systemd/id128-constants.h: src/systemd/sd-messages.h Makefile
diff --git a/TODO b/TODO
index fcafba0..addf32e 100644
--- a/TODO
+++ b/TODO
@@ -587,6 +587,7 @@ Features:
      be just "return self->j != NULL")
    - figure out a simple way to wait for journal events in a way that
      works with ^C
+   - add documentation to systemd.daemon
 
 External:
 
diff --git a/src/python-systemd/_daemon.c b/src/python-systemd/_daemon.c
new file mode 100644
index 0000000..8f93d91
--- /dev/null
+++ b/src/python-systemd/_daemon.c
@@ -0,0 +1,323 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2013 Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#define PY_SSIZE_T_CLEAN
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#include <Python.h>
+#pragma GCC diagnostic pop
+
+#include <stdbool.h>
+#include <assert.h>
+#include <sys/socket.h>
+
+#include <systemd/sd-daemon.h>
+#include "pyutil.h"
+
+PyDoc_STRVAR(module__doc__,
+        "Python interface to the libsystemd-daemon library.\n\n"
+        "Provides _listen_fds, notify, booted, and is_* functions\n"
+        "which wrap sd_listen_fds, sd_notify, sd_booted, sd_is_* and\n"
+        "useful for socket activation and checking if the system is\n"
+        "running under systemd."
+);
+
+static PyObject* set_error(int r, const char* invalid_message) {
+        assert (r < 0);
+
+        if (r == -EINVAL && invalid_message)
+                PyErr_SetString(PyExc_ValueError, invalid_message);
+        else if (r == -ENOMEM)
+                PyErr_SetString(PyExc_MemoryError, "Not enough memory");
+        else {
+                errno = -r;
+                PyErr_SetFromErrno(PyExc_OSError);
+        }
+
+        return NULL;
+}
+
+
+#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
+static int Unicode_FSConverter(PyObject* obj, void *_result) {
+        PyObject **result = _result;
+
+        assert(result);
+
+        if (!obj)
+                /* cleanup: we don't return Py_CLEANUP_SUPPORTED, so
+                 * we can assume that it was PyUnicode_FSConverter. */
+                return PyUnicode_FSConverter(obj, result);
+
+        if (obj == Py_None) {
+                *result = NULL;
+                return 1;
+        }
+
+        return PyUnicode_FSConverter(obj, result);
+}
+#endif
+
+
+PyDoc_STRVAR(booted__doc__,
+             "booted() -> bool\n\n"
+             "Return True iff this system is running under systemd.\n"
+             "Wraps sd_daemon_booted(3)."
+);
+
+static PyObject* booted(PyObject *self, PyObject *args) {
+        int r;
+        assert(args == NULL);
+
+        r = sd_booted();
+        if (r < 0)
+                return set_error(r, NULL);
+
+        return PyBool_FromLong(r);
+}
+
+
+PyDoc_STRVAR(listen_fds__doc__,
+             "_listen_fds(unset_environment=True) -> int\n\n"
+             "Return the number of descriptors passed to this process by the init system\n"
+             "as part of the socket-based activation logic.\n"
+             "Wraps sd_listen_fds(3)."
+);
+
+static PyObject* listen_fds(PyObject *self, PyObject *args) {
+        int r;
+        int unset = true;
+
+#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 3
+        if (!PyArg_ParseTuple(args, "|p:_listen_fds", &unset))
+                return NULL;
+#else
+        PyObject *obj = NULL;
+        if (!PyArg_ParseTuple(args, "|O:_listen_fds", &obj))
+                return NULL;
+        if (obj != NULL)
+                unset = PyObject_IsTrue(obj);
+        if (unset < 0)
+                return NULL;
+#endif
+
+        r = sd_listen_fds(unset);
+        if (r < 0)
+                return set_error(r, NULL);
+
+        return long_FromLong(r);
+}
+
+PyDoc_STRVAR(is_fifo__doc__,
+             "_is_fifo(fd, path) -> bool\n\n"
+             "Returns True iff the descriptor refers to a FIFO or a pipe.\n"
+             "Wraps sd_is_fifo(3)."
+);
+
+
+static PyObject* is_fifo(PyObject *self, PyObject *args) {
+        int r;
+        int fd;
+        const char *path = NULL;
+
+#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
+        if (!PyArg_ParseTuple(args, "i|O&:_is_fifo",
+                              &fd, Unicode_FSConverter, &path))
+                return NULL;
+#else
+        if (!PyArg_ParseTuple(args, "i|z:_is_fifo", &fd, &path))
+                return NULL;
+#endif
+
+        r = sd_is_fifo(fd, path);
+        if (r < 0)
+                return set_error(r, NULL);
+
+        return PyBool_FromLong(r);
+}
+
+
+PyDoc_STRVAR(is_mq__doc__,
+             "_is_mq(fd, path) -> bool\n\n"
+             "Returns True iff the descriptor refers to a POSIX message queue.\n"
+             "Wraps sd_is_mq(3)."
+);
+
+static PyObject* is_mq(PyObject *self, PyObject *args) {
+        int r;
+        int fd;
+        const char *path = NULL;
+
+#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
+        if (!PyArg_ParseTuple(args, "i|O&:_is_mq",
+                              &fd, Unicode_FSConverter, &path))
+                return NULL;
+#else
+        if (!PyArg_ParseTuple(args, "i|z:_is_mq", &fd, &path))
+                return NULL;
+#endif
+
+        r = sd_is_mq(fd, path);
+        if (r < 0)
+                return set_error(r, NULL);
+
+        return PyBool_FromLong(r);
+}
+
+
+
+PyDoc_STRVAR(is_socket__doc__,
+             "_is_socket(fd, family=AF_UNSPEC, type=0, listening=-1) -> bool\n\n"
+             "Returns True iff the descriptor refers to a socket.\n"
+             "Wraps sd_is_socket(3).\n\n"
+             "Constants for `family` are defined in the socket module."
+);
+
+static PyObject* is_socket(PyObject *self, PyObject *args) {
+        int r;
+        int fd, family = AF_UNSPEC, type = 0, listening = -1;
+
+        if (!PyArg_ParseTuple(args, "i|iii:_is_socket",
+                              &fd, &family, &type, &listening))
+                return NULL;
+
+        r = sd_is_socket(fd, family, type, listening);
+        if (r < 0)
+                return set_error(r, NULL);
+
+        return PyBool_FromLong(r);
+}
+
+
+PyDoc_STRVAR(is_socket_inet__doc__,
+             "_is_socket_inet(fd, family=AF_UNSPEC, type=0, listening=-1, port=0) -> bool\n\n"
+             "Wraps sd_is_socket_inet(3).\n\n"
+             "Constants for `family` are defined in the socket module."
+);
+
+static PyObject* is_socket_inet(PyObject *self, PyObject *args) {
+        int r;
+        int fd, family = AF_UNSPEC, type = 0, listening = -1, port = 0;
+
+        if (!PyArg_ParseTuple(args, "i|iiii:_is_socket_inet",
+                              &fd, &family, &type, &listening, &port))
+                return NULL;
+
+        if (port < 0 || port > INT16_MAX)
+                return set_error(-EINVAL, "port must fit into uint16_t");
+
+        r = sd_is_socket_inet(fd, family, type, listening, (uint16_t) port);
+        if (r < 0)
+                return set_error(r, NULL);
+
+        return PyBool_FromLong(r);
+}
+
+
+PyDoc_STRVAR(is_socket_unix__doc__,
+             "_is_socket_unix(fd, type, listening, path) -> bool\n\n"
+             "Wraps sd_is_socket_unix(3)."
+);
+
+static PyObject* is_socket_unix(PyObject *self, PyObject *args) {
+        int r;
+        int fd, type = 0, listening = -1;
+        char* path = NULL;
+        Py_ssize_t length = 0;
+
+#if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
+        PyObject _cleanup_Py_DECREF_ *_path = NULL;
+        if (!PyArg_ParseTuple(args, "i|iiO&:_is_socket_unix",
+                              &fd, &type, &listening, Unicode_FSConverter, &_path))
+                return NULL;
+        if (_path) {
+                assert(PyBytes_Check(_path));
+                if (PyBytes_AsStringAndSize(_path, &path, &length))
+                        return NULL;
+        }
+#else
+        if (!PyArg_ParseTuple(args, "i|iiz#:_is_socket_unix",
+                              &fd, &type, &listening, &path, &length))
+                return NULL;
+#endif
+
+        r = sd_is_socket_unix(fd, type, listening, path, length);
+        if (r < 0)
+                return set_error(r, NULL);
+
+        return PyBool_FromLong(r);
+}
+
+
+static PyMethodDef methods[] = {
+        { "booted", booted, METH_NOARGS, booted__doc__},
+        { "_listen_fds", listen_fds, METH_VARARGS, listen_fds__doc__},
+        { "_is_fifo", is_fifo, METH_VARARGS, is_fifo__doc__},
+        { "_is_mq", is_mq, METH_VARARGS, is_mq__doc__},
+        { "_is_socket", is_socket, METH_VARARGS, is_socket__doc__},
+        { "_is_socket_inet", is_socket_inet, METH_VARARGS, is_socket_inet__doc__},
+        { "_is_socket_unix", is_socket_unix, METH_VARARGS, is_socket_unix__doc__},
+        { NULL, NULL, 0, NULL }        /* Sentinel */
+};
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+
+#if PY_MAJOR_VERSION < 3
+
+PyMODINIT_FUNC init_daemon(void) {
+        PyObject *m;
+
+        m = Py_InitModule3("_daemon", methods, module__doc__);
+        if (m == NULL)
+                return;
+
+        PyModule_AddIntConstant(m, "LISTEN_FDS_START", SD_LISTEN_FDS_START);
+}
+
+#else
+
+static struct PyModuleDef module = {
+        PyModuleDef_HEAD_INIT,
+        "_daemon", /* name of module */
+        module__doc__, /* module documentation, may be NULL */
+        0, /* size of per-interpreter state of the module */
+        methods
+};
+
+PyMODINIT_FUNC PyInit__daemon(void) {
+        PyObject *m;
+
+        m = PyModule_Create(&module);
+        if (m == NULL)
+                return NULL;
+
+        if (PyModule_AddIntConstant(m, "LISTEN_FDS_START", SD_LISTEN_FDS_START)) {
+                Py_DECREF(m);
+                return NULL;
+        }
+
+        return m;
+}
+
+#endif
+
+#pragma GCC diagnostic pop
diff --git a/src/python-systemd/_reader.c b/src/python-systemd/_reader.c
index 160ab69..96634a1 100644
--- a/src/python-systemd/_reader.c
+++ b/src/python-systemd/_reader.c
@@ -30,23 +30,6 @@
 #include "macro.h"
 #include "util.h"
 
-#if PY_MAJOR_VERSION >=3
-# define unicode_FromStringAndSize PyUnicode_FromStringAndSize
-# define unicode_FromString PyUnicode_FromString
-# define long_FromLong PyLong_FromLong
-# define long_FromSize_t PyLong_FromSize_t
-# define long_Check PyLong_Check
-# define long_AsLong PyLong_AsLong
-#else
-/* Python 3 type naming convention is used */
-# define unicode_FromStringAndSize PyString_FromStringAndSize
-# define unicode_FromString PyString_FromString
-# define long_FromLong PyInt_FromLong
-# define long_FromSize_t PyInt_FromSize_t
-# define long_Check PyInt_Check
-# define long_AsLong PyInt_AsLong
-#endif
-
 typedef struct {
     PyObject_HEAD
     sd_journal *j;
diff --git a/src/python-systemd/daemon.py b/src/python-systemd/daemon.py
new file mode 100644
index 0000000..4a02204
--- /dev/null
+++ b/src/python-systemd/daemon.py
@@ -0,0 +1,53 @@
+from ._daemon import (booted,
+                      _listen_fds,
+                      _is_fifo,
+                      _is_socket,
+                      _is_socket_inet,
+                      _is_socket_unix,
+                      _is_mq,
+                      LISTEN_FDS_START)
+from socket import AF_UNSPEC as _AF_UNSPEC
+
+def _convert_fileobj(fileobj):
+    try:
+        return fileobj.fileno()
+    except AttributeError:
+        return fileobj
+
+def is_fifo(fileobj, path=None):
+    fd = _convert_fileobj(fileobj)
+    return _is_fifo(fd, path)
+
+def is_socket(fileobj, family=_AF_UNSPEC, type=0, listening=-1):
+    fd = _convert_fileobj(fileobj)
+    return _is_socket(fd, family, type, listening)
+
+def is_socket_inet(fileobj, family=_AF_UNSPEC, type=0, listening=-1, port=0):
+    fd = _convert_fileobj(fileobj)
+    return _is_socket_inet(fd, family, type, listening)
+
+def is_socket_unix(fileobj, type=0, listening=-1, path=None):
+    fd = _convert_fileobj(fileobj)
+    return _is_socket_unix(fd, type, listening, path)
+
+def is_mq(fileobj, path=None):
+    fd = _convert_fileobj(fileobj)
+    return _is_mq(fd, path)
+
+def listen_fds(unset_environment=True):
+    """Return a list of socket activated descriptors
+
+    Example::
+
+      (in primary window)
+      $ systemd-activate -l 2000 python3 -c \\
+          'from systemd.daemon import listen_fds; print(listen_fds())'
+      (in another window)
+      $ telnet localhost 2000
+      (in primary window)
+      ...
+      Execing python3 (...)
+      [3]
+    """
+    num = _listen_fds(unset_environment)
+    return list(range(LISTEN_FDS_START, LISTEN_FDS_START + num))
diff --git a/src/python-systemd/docs/daemon.rst b/src/python-systemd/docs/daemon.rst
new file mode 100644
index 0000000..72280ca
--- /dev/null
+++ b/src/python-systemd/docs/daemon.rst
@@ -0,0 +1,16 @@
+`systemd.daemon` module
+=======================
+
+.. automodule:: systemd.daemon
+   :members:
+   :undoc-members:
+   :inherited-members:
+
+   .. autoattribute:: systemd.daemon.LISTEN_FDS_START
+
+   .. autofunction:: _listen_fds
+   .. autofunction:: _is_fifo
+   .. autofunction:: _is_socket
+   .. autofunction:: _is_socket_unix
+   .. autofunction:: _is_socket_inet
+   .. autofunction:: _is_mq
diff --git a/src/python-systemd/docs/index.rst b/src/python-systemd/docs/index.rst
index f04d5a1..8a94d07 100644
--- a/src/python-systemd/docs/index.rst
+++ b/src/python-systemd/docs/index.rst
@@ -13,6 +13,7 @@ Contents:
 
    journal
    id128
+   daemon
 
 Indices and tables
 ==================
diff --git a/src/python-systemd/pyutil.h b/src/python-systemd/pyutil.h
index 3b7bc58..2163fda 100644
--- a/src/python-systemd/pyutil.h
+++ b/src/python-systemd/pyutil.h
@@ -1,5 +1,7 @@
 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
 
+#pragma once
+
 /***
   This file is part of systemd.
 
@@ -27,3 +29,20 @@
 void cleanup_Py_DECREFp(PyObject **p);
 
 #define _cleanup_Py_DECREF_ __attribute__((cleanup(cleanup_Py_DECREFp)))
+
+#if PY_MAJOR_VERSION >=3
+# define unicode_FromStringAndSize PyUnicode_FromStringAndSize
+# define unicode_FromString PyUnicode_FromString
+# define long_FromLong PyLong_FromLong
+# define long_FromSize_t PyLong_FromSize_t
+# define long_Check PyLong_Check
+# define long_AsLong PyLong_AsLong
+#else
+/* Python 3 type naming convention is used */
+# define unicode_FromStringAndSize PyString_FromStringAndSize
+# define unicode_FromString PyString_FromString
+# define long_FromLong PyInt_FromLong
+# define long_FromSize_t PyInt_FromSize_t
+# define long_Check PyInt_Check
+# define long_AsLong PyInt_AsLong
+#endif

commit 5e65c93a433447b15180249166f7b3944c3e6156
Author: Zbigniew Jędrzejewski-Szmek <zbyszek at in.waw.pl>
Date:   Fri Mar 15 18:57:44 2013 -0400

    systemd-activate: pass environment variables through
    
    I need this to test half-installed socket-activated python
    script, which requires PYTHONPATH and LD_LIBRARY_PATH set.
    I assume that other people might find it useful to.
    
    -E VAR passes through VAR from the environment, while
    -E VAR=value sets VAR=value.
    
    systemd-activate -E PYTHONPATH=/var/tmp/inst1/usr/lib64/python3.3/site-packages -E LD_LIBRARY_PATH=/var/tmp/inst1/usr/lib -l 2000 python3 -c 'from systemd.daemon import listen_fds; print(listen_fds())'

diff --git a/man/systemd-activate.xml b/man/systemd-activate.xml
index a5cab8e..b62cf44 100644
--- a/man/systemd-activate.xml
+++ b/man/systemd-activate.xml
@@ -99,7 +99,7 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
       </varlistentry>
 
       <varlistentry>
-        <term><option>-l</option></term>
+        <term><option>-l <replaceable>address</replaceable></option></term>
         <term><option>--listen=<replaceable>address</replaceable></option></term>
 
         <listitem><para>Listen on this <replaceable>address</replaceable>.
@@ -116,6 +116,18 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>.
         connection and pass the connection socket as standard input
         and standard output.</para></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><option>-E <replaceable>VAR</replaceable><optional>=<replaceable>VALUE</replaceable></optional></option></term>
+        <term><option>--environment=<replaceable>VAR</replaceable><optional>=<replaceable>VALUE</replaceable></optional></option></term>
+
+        <listitem><para>Add this variable to the environment of the
+        launched process. If <replaceable>VAR</replaceable> is
+        followed by <literal>=</literal> assume that it is a
+        variable–value pair. Otherwise obtain the value from the
+        environment of <command>systemd-activate</command> itself.
+        </para></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
diff --git a/src/activate/activate.c b/src/activate/activate.c
index 7fcb0a9..be40be4 100644
--- a/src/activate/activate.c
+++ b/src/activate/activate.c
@@ -38,6 +38,7 @@
 static char** arg_listen = NULL;
 static bool arg_accept = false;
 static char** arg_args = NULL;
+static char** arg_environ = NULL;
 
 static int add_epoll(int epoll_fd, int fd) {
         int r;
@@ -164,12 +165,29 @@ static int open_sockets(int *epoll_fd, bool accept) {
 }
 
 static int launch(char* name, char **argv, char **environ, int fds) {
-        unsigned n_env = 0;
-        char* envp[7] = {NULL}; /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID */
+        unsigned n_env = 0, length;
+        char **envp = NULL, **s;
         static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
         char _cleanup_free_ *tmp = NULL;
         unsigned i;
 
+        length = strv_length(arg_environ);
+        /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */
+        envp = new(char *, length + 7);
+
+        STRV_FOREACH(s, arg_environ) {
+                if (strchr(*s, '='))
+                        envp[n_env++] = *s;
+                else {
+                        char _cleanup_free_ *p = strappend(*s, "=");
+                        if (!p)
+                                return log_oom();
+                        envp[n_env] = strv_find_prefix(environ, p);
+                        if (envp[n_env])
+                                n_env ++;
+                }
+        }
+
         for (i = 0; i < ELEMENTSOF(tocopy); i++) {
                 envp[n_env] = strv_find_prefix(environ, tocopy[i]);
                 if (envp[n_env])
@@ -312,6 +330,7 @@ static int parse_argv(int argc, char *argv[]) {
                 { "version",      no_argument,       NULL, ARG_VERSION   },
                 { "listen",       required_argument, NULL, 'l'           },
                 { "accept",       no_argument,       NULL, 'a'           },
+                { "environment",  required_argument, NULL, 'E'           },
                 { NULL,           0,                 NULL, 0             }
         };
 
@@ -320,7 +339,7 @@ static int parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "+hl:sa", options, NULL)) >= 0)
+        while ((c = getopt_long(argc, argv, "+hl:saE:", options, NULL)) >= 0)
                 switch(c) {
                 case 'h':
                         help();
@@ -343,6 +362,14 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_accept = true;
                         break;
 
+                case 'E': {
+                        int r = strv_extend(&arg_environ, optarg);
+                        if (r < 0)
+                                return r;
+
+                        break;
+                }
+
                 case '?':
                         return -EINVAL;
 



More information about the systemd-commits mailing list