chiark / gitweb /
cgroup: add cgroupsification
authorLennart Poettering <lennart@poettering.net>
Wed, 31 Mar 2010 14:29:55 +0000 (16:29 +0200)
committerLennart Poettering <lennart@poettering.net>
Wed, 31 Mar 2010 14:29:55 +0000 (16:29 +0200)
23 files changed:
.gitignore
Makefile.am
cgroup.c [new file with mode: 0644]
cgroup.h [new file with mode: 0644]
cgroups-agent.c [new file with mode: 0644]
configure.ac
dbus.c
execute.c
execute.h
load-fragment.c
main.c
manager.c
manager.h
mount-setup.c [new file with mode: 0644]
mount-setup.h [new file with mode: 0644]
service.c
service.h
socket.c
test-engine.c
test1/exec-demo.service
unit.c
unit.h
util.c

index 581edae..06ef3d0 100644 (file)
@@ -1,3 +1,4 @@
+systemd-cgroups-agent
 systemd
 *.o
 test-engine
index 3fc221c..57a2dd9 100644 (file)
@@ -25,15 +25,19 @@ AM_CPPFLAGS = \
        -DSYSTEM_DATA_UNIT_PATH=\"$(pkgdatadir)/system\" \
        -DSYSTEM_SYSVINIT_PATH=\"$(sysconfdir)/init.d\" \
        -DSESSION_CONFIG_UNIT_PATH=\"$(pkgsysconfdir)/session\" \
-       -DSESSION_DATA_UNIT_PATH=\"$(pkgdatadir)/session\"
+       -DSESSION_DATA_UNIT_PATH=\"$(pkgdatadir)/session\" \
+       -DCGROUP_AGENT_PATH=\"$(pkglibexecdir)/systemd-cgroups-agent\"
 
 sbin_PROGRAMS = \
        systemd
 
 bin_PROGRAMS = \
        systemctl \
-       systemadm \
-       systemd-logger
+       systemadm
+
+pkglibexec_PROGRAMS = \
+       systemd-logger \
+       systemd-cgroups-agent
 
 noinst_PROGRAMS = \
        test-engine \
@@ -41,34 +45,64 @@ noinst_PROGRAMS = \
 
 BASIC_SOURCES= \
         util.c \
+        util.h \
         hashmap.c \
+        hashmap.h \
         set.c \
+        set.h \
         strv.c \
+        strv.h \
         conf-parser.c \
+        conf-parser.h \
         socket-util.c \
+        socket-util.h \
         log.c \
-        ratelimit.c
+        log.h \
+        ratelimit.c \
+        ratelimit.h
 
 COMMON_SOURCES= \
        $(BASIC_SOURCES) \
        unit.c \
+       unit.h \
         job.c \
+       job.h \
         manager.c \
+        manager.h \
         load-fragment.c \
+        load-fragment.h \
         service.c \
+        service.h \
         automount.c \
+        automount.h \
         mount.c \
+        mount.h \
         device.c \
+        device.h \
         target.c \
+        target.h \
         snapshot.c \
+        snapshot.h \
         socket.c \
+        socket.h \
         timer.c \
+        timer.h \
         load-dropin.c \
+        load-dropin.h \
         execute.c \
+        execute.h \
         dbus.c \
+        dbus.h \
         dbus-manager.c \
+        dbus-manager.h \
         dbus-unit.c \
-        dbus-job.c
+        dbus-unit.h \
+        dbus-job.c \
+        dbus-job.h \
+       cgroup.c \
+       cgroup.h \
+       mount-setup.c \
+       mount-setup.h
 
 systemd_SOURCES = \
        $(COMMON_SOURCES) \
@@ -77,11 +111,13 @@ systemd_SOURCES = \
 systemd_CPPFLAGS = \
        $(AM_CPPFLAGS) \
        $(DBUS_CFLAGS) \
-       $(UDEV_CFLAGS)
+       $(UDEV_CFLAGS) \
+       $(CGROUP_CFLAGS)
 
 systemd_LDADD = \
        $(DBUS_LIBS) \
-       $(UDEV_LIBS)
+       $(UDEV_LIBS) \
+       $(CGROUP_LIBS)
 
 test_engine_SOURCES = \
        $(COMMON_SOURCES) \
@@ -101,6 +137,17 @@ systemd_logger_SOURCES = \
        $(BASIC_SOURCES) \
        logger.c
 
+systemd_cgroups_agent_SOURCES = \
+       $(BASIC_SOURCES) \
+       cgroups-agent.c
+
+systemd_cgroups_agent_CPPFLAGS = \
+       $(AM_CPPFLAGS) \
+       $(DBUS_CFLAGS)
+
+systemd_cgroups_agent_LDADD = \
+       $(DBUS_LIBS)
+
 VALAFLAGS = -g --save-temps --pkg=dbus-glib-1 --pkg=posix --pkg gee-1.0 --pkg gtk+-2.0
 
 systemctl_SOURCES = \
@@ -120,4 +167,5 @@ systemadm_LDADD = $(DBUSGLIB_LIBS) $(GTK_LIBS)
 CLEANFILES = \
        systemd-interfaces.c \
        systemctl.c \
-       systemadm.c
+       systemadm.c \
+       systemd-cgroups-agent
diff --git a/cgroup.c b/cgroup.c
new file mode 100644 (file)
index 0000000..04736ac
--- /dev/null
+++ b/cgroup.c
@@ -0,0 +1,523 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/mount.h>
+
+#include "cgroup.h"
+#include "log.h"
+
+static int translate_error(int error, int _errno) {
+
+        switch (error) {
+
+        case ECGROUPNOTCOMPILED:
+        case ECGROUPNOTMOUNTED:
+        case ECGROUPNOTEXIST:
+        case ECGROUPNOTCREATED:
+                return -ENOENT;
+
+        case ECGINVAL:
+                return -EINVAL;
+
+        case ECGROUPNOTALLOWED:
+                return -EPERM;
+
+        case ECGOTHER:
+                return -_errno;
+        }
+
+        return -EIO;
+}
+
+int cgroup_bonding_realize(CGroupBonding *b) {
+        int r;
+
+        assert(b);
+        assert(b->path);
+        assert(b->controller);
+
+        if (b->cgroup)
+                return 0;
+
+        if (!(b->cgroup = cgroup_new_cgroup(b->path)))
+                return -ENOMEM;
+
+        if (!cgroup_add_controller(b->cgroup, b->controller)) {
+                r = -ENOMEM;
+                goto fail;
+        }
+
+        if (b->inherit)
+                r = cgroup_create_cgroup_from_parent(b->cgroup, true);
+        else
+                r = cgroup_create_cgroup(b->cgroup, true);
+
+        if (r != 0) {
+                r = translate_error(r, errno);
+                goto fail;
+        }
+
+        return 0;
+
+fail:
+        cgroup_free(&b->cgroup);
+        b->cgroup = NULL;
+        return r;
+}
+
+int cgroup_bonding_realize_list(CGroupBonding *first) {
+        CGroupBonding *b;
+
+        LIST_FOREACH(by_unit, b, first) {
+                int r;
+
+                if ((r = cgroup_bonding_realize(b)) < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+void cgroup_bonding_free(CGroupBonding *b) {
+        assert(b);
+
+        if (b->unit) {
+                CGroupBonding *f;
+
+                LIST_REMOVE(CGroupBonding, by_unit, b->unit->meta.cgroup_bondings, b);
+
+                assert_se(f = hashmap_get(b->unit->meta.manager->cgroup_bondings, b->path));
+                LIST_REMOVE(CGroupBonding, by_path, f, b);
+
+                if (f)
+                        hashmap_replace(b->unit->meta.manager->cgroup_bondings, b->path, f);
+                else
+                        hashmap_remove(b->unit->meta.manager->cgroup_bondings, b->path);
+        }
+
+        free(b->controller);
+        free(b->path);
+
+        if (b->cgroup) {
+
+                if (b->only_us && b->clean_up)
+                        cgroup_delete_cgroup(b->cgroup, true);
+
+                cgroup_free(&b->cgroup);
+        }
+
+        free(b);
+}
+
+void cgroup_bonding_free_list(CGroupBonding *first) {
+        CGroupBonding *b, *n;
+
+        LIST_FOREACH_SAFE(by_unit, b, n, first)
+                cgroup_bonding_free(b);
+}
+
+int cgroup_bonding_install(CGroupBonding *b, pid_t pid) {
+        int r;
+
+        assert(b);
+        assert(pid >= 0);
+
+        if (pid == 0)
+                pid = getpid();
+
+        if (!b->cgroup)
+                return -ENOENT;
+
+        if ((r = cgroup_attach_task_pid(b->cgroup, pid)))
+                return translate_error(r, errno);
+
+        return 0;
+}
+
+int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid) {
+        CGroupBonding *b;
+
+        LIST_FOREACH(by_unit, b, first) {
+                int r;
+
+                if ((r = cgroup_bonding_install(b, pid)) < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+int cgroup_bonding_kill(CGroupBonding *b, int sig) {
+        int r;
+        Set *s;
+        bool done;
+
+        assert(b);
+        assert(sig > 0);
+
+        if (!(s = set_new(trivial_hash_func, trivial_compare_func)))
+                return -ENOMEM;
+
+        do {
+                void *iterator;
+                pid_t pid;
+
+                done = true;
+
+                if ((r = cgroup_get_task_begin(b->path, b->controller, &iterator, &pid)) != 0) {
+                        if (r == ECGEOF) {
+                                r = 0;
+                                goto kill_done;
+                        } else {
+                                r = translate_error(r, errno);
+                                break;
+                        }
+                }
+
+                for (;;) {
+                        if (set_get(s, INT_TO_PTR(pid)) != INT_TO_PTR(pid)) {
+
+                                /* If we haven't killed this process
+                                 * yet, kill it */
+
+                                if (kill(pid, sig) < 0 && errno != ESRCH) {
+                                        r = -errno;
+                                        break;
+                                }
+
+                                done = false;
+
+                                if ((r = set_put(s, INT_TO_PTR(pid))) < 0)
+                                    break;
+                        }
+
+                        if ((r = cgroup_get_task_next(&iterator, &pid)) != 0) {
+
+                                if (r == ECGEOF)
+                                        r = 0;
+                                else
+                                        r = translate_error(r, errno);
+
+                                break;
+                        }
+                }
+
+        kill_done:
+                assert_se(cgroup_get_task_end(&iterator) == 0);
+
+                /* To avoid racing against processes which fork
+                 * quicker than we can kill them we repeat this until
+                 * no new pids need to be killed. */
+
+        } while (!done && r >= 0);
+
+        set_free(s);
+        return r;
+}
+
+int cgroup_bonding_kill_list(CGroupBonding *first, int sig) {
+        CGroupBonding *b;
+
+        LIST_FOREACH(by_unit, b, first) {
+                int r;
+
+                if ((r = cgroup_bonding_kill(b, sig)) < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
+/* Returns 1 if the group is empty, 0 if it is not, -EAGAIN if we
+ * cannot know */
+int cgroup_bonding_is_empty(CGroupBonding *b) {
+        void *iterator;
+        pid_t pid;
+        int r;
+
+        assert(b);
+
+        r = cgroup_get_task_begin(b->path, b->controller, &iterator, &pid);
+
+        if (r == 0 || r == ECGEOF)
+                cgroup_get_task_end(&iterator);
+
+        /* Hmm, no PID in this group? Then it is definitely empty */
+        if (r == ECGEOF)
+                return 1;
+
+        /* Some error? Let's return it */
+        if (r != 0)
+                return translate_error(r, errno);
+
+        /* It's not empty, and we are the only user, then it is
+         * definitely not empty */
+        if (b->only_us)
+                return 0;
+
+        /* There are PIDs in the group but we aren't the only users,
+         * hence we cannot say */
+        return -EAGAIN;
+}
+
+int cgroup_bonding_is_empty_list(CGroupBonding *first) {
+        CGroupBonding *b;
+
+        LIST_FOREACH(by_unit, b, first) {
+                int r;
+
+                if ((r = cgroup_bonding_is_empty(b)) < 0) {
+                        /* If this returned -EAGAIN, then we don't know if the
+                         * group is empty, so let's see if another group can
+                         * tell us */
+
+                        if (r != -EAGAIN)
+                                return r;
+                } else
+                        return r;
+        }
+
+        return -EAGAIN;
+}
+
+static int install_release_agent(Manager *m, const char *mount_point) {
+        char *p, *c, *sc;
+        int r;
+
+        assert(m);
+        assert(mount_point);
+
+        if (asprintf(&p, "%s/release_agent", mount_point) < 0)
+                return -ENOMEM;
+
+        if ((r = read_one_line_file(p, &c)) < 0) {
+                free(p);
+                return r;
+        }
+
+        sc = strstrip(c);
+
+        if (sc[0] == 0) {
+                if ((r = write_one_line_file(p, CGROUP_AGENT_PATH "\n" )) < 0) {
+                        free(p);
+                        free(c);
+                        return r;
+                }
+        } else if (!streq(sc, CGROUP_AGENT_PATH)) {
+                free(p);
+                free(c);
+                return -EEXIST;
+        }
+
+        free(c);
+        free(p);
+
+        if (asprintf(&p, "%s/notify_on_release", mount_point) < 0)
+                return -ENOMEM;
+
+        if ((r = read_one_line_file(p, &c)) < 0) {
+                free(p);
+                return r;
+        }
+
+        sc = strstrip(c);
+
+        if (streq(sc, "0")) {
+                if ((r = write_one_line_file(p, "1\n")) < 0) {
+                        free(p);
+                        free(c);
+                        return r;
+                }
+        } else if (!streq(sc, "1")) {
+                free(p);
+                free(c);
+                return -EIO;
+        }
+
+        return 0;
+}
+
+static int create_hierarchy_cgroup(Manager *m) {
+        struct cgroup *cg;
+        int r;
+
+        assert(m);
+
+        if (!(cg = cgroup_new_cgroup(m->cgroup_hierarchy)))
+                return -ENOMEM;
+
+        if (!(cgroup_add_controller(cg, m->cgroup_controller))) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if ((r = cgroup_create_cgroup(cg, true)) != 0) {
+                log_error("Failed to create cgroup hierarchy group: %s", cgroup_strerror(r));
+                r = translate_error(r, errno);
+                goto finish;
+        }
+
+        if ((r = cgroup_attach_task(cg)) != 0) {
+                log_error("Failed to add ourselves to hierarchy group: %s", cgroup_strerror(r));
+                r = translate_error(r, errno);
+                goto finish;
+        }
+
+        r = 0;
+
+finish:
+        cgroup_free(&cg);
+        return r;
+}
+
+int manager_setup_cgroup(Manager *m) {
+        char *mp, *cp;
+        int r;
+        pid_t pid;
+
+        assert(m);
+
+        if ((r = cgroup_init()) != 0) {
+                log_error("Failed to initialize libcg: %s", cgroup_strerror(r));
+                return translate_error(r, errno);
+        }
+
+        free(m->cgroup_controller);
+        if (!(m->cgroup_controller = strdup("debug")))
+                return -ENOMEM;
+
+        if ((r = cgroup_get_subsys_mount_point(m->cgroup_controller, &mp)))
+                return translate_error(r, errno);
+
+        pid = getpid();
+
+        if ((r = cgroup_get_current_controller_path(pid, m->cgroup_controller, &cp))) {
+                free(mp);
+                return translate_error(r, errno);
+        }
+
+        free(m->cgroup_hierarchy);
+        m->cgroup_hierarchy = NULL;
+        if (asprintf(&m->cgroup_hierarchy, "%s/systemd-%llu", strcmp(cp, "/") == 0 ? "" : cp, (unsigned long long) pid) < 0) {
+                free(cp);
+                free(mp);
+                return -ENOMEM;
+        }
+
+        log_info("Using cgroup controller <%s>, hierarchy mounted at <%s>, using root group <%s>.",
+                 m->cgroup_controller,
+                 mp,
+                 m->cgroup_hierarchy);
+
+        if ((r = install_release_agent(m, mp)) < 0)
+                log_warning("Failed to install release agent, ignoring: %s", strerror(-r));
+        else
+                log_info("Installed release agent, or already installed.");
+
+        free(mp);
+        free(cp);
+
+        if ((r = create_hierarchy_cgroup(m)) < 0)
+                log_error("Failed to create root cgroup hierarchy: %s", strerror(-r));
+        else
+                log_info("Created root group.");
+
+        return r;
+}
+
+int manager_shutdown_cgroup(Manager *m) {
+        struct cgroup *cg;
+        int r;
+
+        assert(m);
+
+        if (!m->cgroup_hierarchy)
+                return 0;
+
+        if (!(cg = cgroup_new_cgroup(m->cgroup_hierarchy)))
+                return -ENOMEM;
+
+        if (!(cgroup_add_controller(cg, m->cgroup_controller))) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if ((r = cgroup_delete_cgroup_ext(cg, CGFLAG_DELETE_IGNORE_MIGRATION|CGFLAG_DELETE_RECURSIVE)) != 0) {
+                log_error("Failed to delete cgroup hierarchy group: %s", cgroup_strerror(r));
+                r = translate_error(r, errno);
+                goto finish;
+        }
+        r = 0;
+
+finish:
+        cgroup_free(&cg);
+        return r;
+
+}
+
+int cgroup_notify_empty(Manager *m, const char *group) {
+        CGroupBonding *l, *b;
+
+        assert(m);
+        assert(group);
+
+        if (!(l = hashmap_get(m->cgroup_bondings, group)))
+                return 0;
+
+        LIST_FOREACH(by_path, b, l) {
+                int t;
+
+                if (!b->unit)
+                        continue;
+
+                if ((t = cgroup_bonding_is_empty_list(b)) < 0) {
+
+                        /* If we don't know, we don't know */
+                        if (t != -EAGAIN)
+                                log_warning("Failed to check whether cgroup is empty: %s", strerror(errno));
+
+                        continue;
+                }
+
+                if (t > 0)
+                        if (UNIT_VTABLE(b->unit)->cgroup_notify_empty)
+                                UNIT_VTABLE(b->unit)->cgroup_notify_empty(b->unit);
+        }
+
+        return 0;
+}
+
+CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller) {
+        CGroupBonding *b;
+
+        assert(controller);
+
+        LIST_FOREACH(by_unit, b, first)
+                if (streq(b->controller, controller))
+                        return b;
+
+        return NULL;
+}
diff --git a/cgroup.h b/cgroup.h
new file mode 100644 (file)
index 0000000..66ddb95
--- /dev/null
+++ b/cgroup.h
@@ -0,0 +1,83 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#ifndef foocgrouphfoo
+#define foocgrouphfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <libcgroup.h>
+
+typedef struct CGroupBonding CGroupBonding;
+
+#include "unit.h"
+
+/* Binds a cgroup to a name */
+struct CGroupBonding {
+        char *controller;
+        char *path;
+
+        Unit *unit;
+
+        struct cgroup *cgroup;
+
+        /* When shutting down, kill all tasks? */
+        bool kill_all:1;
+
+        /* When shutting down, remove cgroup? */
+        bool clean_up:1;
+
+        /* When our tasks are the only ones in this group */
+        bool only_us:1;
+
+        /* Inherit parameters from parent group */
+        bool inherit:1;
+
+        /* For the Unit::cgroup_bondings list */
+        LIST_FIELDS(CGroupBonding, by_unit);
+
+        /* For the Manager::cgroup_bondings hashmap */
+        LIST_FIELDS(CGroupBonding, by_path);
+};
+
+int cgroup_bonding_realize(CGroupBonding *b);
+int cgroup_bonding_realize_list(CGroupBonding *first);
+
+void cgroup_bonding_free(CGroupBonding *b);
+void cgroup_bonding_free_list(CGroupBonding *first);
+
+int cgroup_bonding_install(CGroupBonding *b, pid_t pid);
+int cgroup_bonding_install_list(CGroupBonding *first, pid_t pid);
+
+int cgroup_bonding_kill(CGroupBonding *b, int sig);
+int cgroup_bonding_kill_list(CGroupBonding *first, int sig);
+
+int cgroup_bonding_is_empty(CGroupBonding *b);
+int cgroup_bonding_is_empty_list(CGroupBonding *first);
+
+CGroupBonding *cgroup_bonding_find_list(CGroupBonding *first, const char *controller);
+
+#include "manager.h"
+
+int manager_setup_cgroup(Manager *m);
+int manager_shutdown_cgroup(Manager *m);
+
+int cgroup_notify_empty(Manager *m, const char *group);
+
+#endif
diff --git a/cgroups-agent.c b/cgroups-agent.c
new file mode 100644 (file)
index 0000000..232b63e
--- /dev/null
@@ -0,0 +1,72 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <dbus/dbus.h>
+
+#include "log.h"
+
+int main(int argc, char *argv[]) {
+        DBusError error;
+        DBusConnection *bus = NULL;
+        DBusMessage *m = NULL;
+        int r = 1;
+
+        dbus_error_init(&error);
+
+        if (argc != 2) {
+                log_error("Incorrect number of arguments.");
+                goto finish;
+        }
+
+        if (!(bus = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) {
+                log_error("Failed to get D-Bus connection: %s", error.message);
+                goto finish;
+        }
+
+        if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1/agent", "org.freedesktop.systemd1.Agent", "Released"))) {
+                log_error("Could not allocate signal message.");
+                goto finish;
+        }
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_STRING, &argv[1],
+                                      DBUS_TYPE_INVALID)) {
+                log_error("Could not attach group information to signal message.");
+                goto finish;
+        }
+
+        if (!dbus_connection_send(bus, m, NULL)) {
+                log_error("Failed to send signal message.");
+                goto finish;
+        }
+
+        r = 0;
+
+finish:
+        if (bus)
+                dbus_connection_unref(bus);
+
+        if (m)
+                dbus_message_unref(m);
+
+        dbus_error_free(&error);
+        return r;
+}
index 95a9ef5..f0beb1f 100644 (file)
@@ -69,6 +69,10 @@ PKG_CHECK_MODULES(GTK, [ gtk+-2.0 ])
 AC_SUBST(GTK_CFLAGS)
 AC_SUBST(GTK_LIBS)
 
+PKG_CHECK_MODULES(CGROUP, [ libcgroup ])
+AC_SUBST(CGROUP_CFLAGS)
+AC_SUBST(CGROUP_LIBS)
+
 AM_PROG_VALAC()
 AC_SUBST(VAPIDIR)
 
diff --git a/dbus.c b/dbus.c
index bef8bb8..0227844 100644 (file)
--- a/dbus.c
+++ b/dbus.c
 #include "dbus.h"
 #include "log.h"
 #include "strv.h"
+#include "cgroup.h"
 
 static void bus_dispatch_status(DBusConnection *bus, DBusDispatchStatus status, void *data)  {
         Manager *m = data;
 
         assert(bus);
         assert(m);
+        assert(m->bus == bus);
 
         m->request_bus_dispatch = status != DBUS_DISPATCH_COMPLETE;
 }
 
+static void system_bus_dispatch_status(DBusConnection *bus, DBusDispatchStatus status, void *data)  {
+        Manager *m = data;
+
+        assert(bus);
+        assert(m);
+        assert(m->system_bus == bus);
+
+        m->request_system_bus_dispatch = status != DBUS_DISPATCH_COMPLETE;
+}
+
 static uint32_t bus_flags_to_events(DBusWatch *bus_watch) {
         unsigned flags;
         uint32_t events = 0;
@@ -81,7 +93,7 @@ void bus_watch_event(Manager *m, Watch *w, int events) {
         /* This is called by the event loop whenever there is
          * something happening on D-Bus' file handles. */
 
-        if (!(dbus_watch_get_enabled(w->data.bus_watch)))
+        if (!dbus_watch_get_enabled(w->data.bus_watch))
                 return;
 
         dbus_watch_handle(w->data.bus_watch, events_to_bus_flags(events));
@@ -315,16 +327,57 @@ static DBusHandlerResult bus_message_filter(DBusConnection  *connection, DBusMes
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
 
+static DBusHandlerResult system_bus_message_filter(DBusConnection  *connection, DBusMessage  *message, void *data) {
+        Manager *m = data;
+        DBusError error;
+
+        assert(connection);
+        assert(message);
+        assert(m);
+
+        dbus_error_init(&error);
+
+        /* log_debug("Got D-Bus request: %s.%s() on %s", */
+        /*           dbus_message_get_interface(message), */
+        /*           dbus_message_get_member(message), */
+        /*           dbus_message_get_path(message)); */
+
+        if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
+                log_error("Warning! D-Bus connection terminated.");
+
+                /* FIXME: we probably should restart D-Bus here */
+
+        } if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
+                const char *cgroup;
+
+                if (!dbus_message_get_args(message, &error,
+                                           DBUS_TYPE_STRING, &cgroup,
+                                           DBUS_TYPE_INVALID))
+                        log_error("Failed to parse Released message: %s", error.message);
+                else
+                        cgroup_notify_empty(m, cgroup);
+        }
+
+        dbus_error_free(&error);
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
 unsigned bus_dispatch(Manager *m) {
         assert(m);
 
-        if (!m->request_bus_dispatch)
-                return 0;
+        if (m->request_bus_dispatch)
+                if (dbus_connection_dispatch(m->bus) == DBUS_DISPATCH_COMPLETE) {
+                        m->request_bus_dispatch = false;
+                        return 1;
+                }
 
-        if (dbus_connection_dispatch(m->bus) == DBUS_DISPATCH_COMPLETE)
-                m->request_bus_dispatch = false;
+        if (m->request_system_bus_dispatch)
+                if (dbus_connection_dispatch(m->system_bus) == DBUS_DISPATCH_COMPLETE) {
+                        m->request_system_bus_dispatch = false;
+                        return 1;
+                }
 
-        return 1;
+        return 0;
 }
 
 static int request_name(Manager *m) {
@@ -362,6 +415,18 @@ static int request_name(Manager *m) {
         return 0;
 }
 
+static int bus_setup_loop(Manager *m, DBusConnection *bus) {
+        assert(m);
+        assert(bus);
+
+        dbus_connection_set_exit_on_disconnect(bus, FALSE);
+        if (!dbus_connection_set_watch_functions(bus, bus_add_watch, bus_remove_watch, bus_toggle_watch, m, NULL) ||
+            !dbus_connection_set_timeout_functions(bus, bus_add_timeout, bus_remove_timeout, bus_toggle_timeout, m, NULL))
+                return -ENOMEM;
+
+        return 0;
+}
+
 int bus_init(Manager *m) {
         DBusError error;
         char *id;
@@ -381,17 +446,39 @@ int bus_init(Manager *m) {
         if (!(m->bus = dbus_bus_get_private(m->running_as == MANAGER_SESSION ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error))) {
                 log_error("Failed to get D-Bus connection: %s", error.message);
                 dbus_error_free(&error);
+                bus_done(m);
                 return -ECONNREFUSED;
         }
 
-        dbus_connection_set_exit_on_disconnect(m->bus, FALSE);
+        if ((r = bus_setup_loop(m, m->bus)) < 0) {
+                bus_done(m);
+                return r;
+        }
+
         dbus_connection_set_dispatch_status_function(m->bus, bus_dispatch_status, m, NULL);
-        if (!dbus_connection_set_watch_functions(m->bus, bus_add_watch, bus_remove_watch, bus_toggle_watch, m, NULL) ||
-            !dbus_connection_set_timeout_functions(m->bus, bus_add_timeout, bus_remove_timeout, bus_toggle_timeout, m, NULL) ||
-            !dbus_connection_register_object_path(m->bus, "/org/freedesktop/systemd1", &bus_manager_vtable, m) ||
+
+        if (m->running_as == MANAGER_SESSION) {
+                if (!(m->system_bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error))) {
+                        log_error("Failed to get D-Bus connection: %s", error.message);
+                        dbus_error_free(&error);
+                        bus_done(m);
+                        return -ECONNREFUSED;
+                }
+
+                if ((r = bus_setup_loop(m, m->system_bus)) < 0) {
+                        bus_done(m);
+                        return r;
+                }
+
+                dbus_connection_set_dispatch_status_function(m->system_bus, system_bus_dispatch_status, m, NULL);
+        } else
+                m->system_bus = m->bus;
+
+        if (!dbus_connection_register_object_path(m->bus, "/org/freedesktop/systemd1", &bus_manager_vtable, m) ||
             !dbus_connection_register_fallback(m->bus, "/org/freedesktop/systemd1/unit", &bus_unit_vtable, m) ||
             !dbus_connection_register_fallback(m->bus, "/org/freedesktop/systemd1/job", &bus_job_vtable, m) ||
-            !dbus_connection_add_filter(m->bus, bus_message_filter, m, NULL)) {
+            !dbus_connection_add_filter(m->bus, bus_message_filter, m, NULL) ||
+            !dbus_connection_add_filter(m->system_bus, system_bus_message_filter, m, NULL)) {
                 bus_done(m);
                 return -ENOMEM;
         }
@@ -406,7 +493,6 @@ int bus_init(Manager *m) {
         if (dbus_error_is_set(&error)) {
                 log_error("Failed to register match: %s", error.message);
                 dbus_error_free(&error);
-                bus_done(m);
                 return -ENOMEM;
         }
 
@@ -415,12 +501,31 @@ int bus_init(Manager *m) {
                 return r;
         }
 
+        dbus_bus_add_match(m->system_bus,
+                           "type='signal',"
+                           "interface='org.freedesktop.systemd1.Agent',"
+                           "path='/org/freedesktop/systemd1/agent'",
+                           &error);
+
+        if (dbus_error_is_set(&error)) {
+                log_error("Failed to register match: %s", error.message);
+                dbus_error_free(&error);
+                bus_done(m);
+                return -ENOMEM;
+        }
+
         log_debug("Successfully connected to D-Bus bus %s as %s",
                   strnull((id = dbus_connection_get_server_id(m->bus))),
                   strnull(dbus_bus_get_unique_name(m->bus)));
         dbus_free(id);
 
+        log_debug("Successfully connected to system D-Bus bus %s as %s",
+                  strnull((id = dbus_connection_get_server_id(m->system_bus))),
+                  strnull(dbus_bus_get_unique_name(m->system_bus)));
+        dbus_free(id);
+
         m->request_bus_dispatch = true;
+        m->request_system_bus_dispatch = true;
 
         return 0;
 }
@@ -428,6 +533,12 @@ int bus_init(Manager *m) {
 void bus_done(Manager *m) {
         assert(m);
 
+        if (m->system_bus && m->system_bus != m->bus) {
+                dbus_connection_close(m->system_bus);
+                dbus_connection_unref(m->system_bus);
+                m->system_bus = NULL;
+        }
+
         if (m->bus) {
                 dbus_connection_close(m->bus);
                 dbus_connection_unref(m->bus);
@@ -571,8 +682,6 @@ oom:
         return DBUS_HANDLER_RESULT_NEED_MEMORY;
 }
 
-
-
 static const char *error_to_dbus(int error) {
 
         switch(error) {
index 85d9873..dd36038 100644 (file)
--- a/execute.c
+++ b/execute.c
@@ -42,6 +42,7 @@
 #include "log.h"
 #include "ioprio.h"
 #include "securebits.h"
+#include "cgroup.h"
 
 static int close_fds(int except[], unsigned n_except) {
         DIR *d;
@@ -508,9 +509,11 @@ int exec_spawn(const ExecCommand *command,
                int *fds, unsigned n_fds,
                bool apply_permissions,
                bool apply_chroot,
+               CGroupBonding *cgroup_bondings,
                pid_t *ret) {
 
         pid_t pid;
+        int r;
 
         assert(command);
         assert(context);
@@ -519,11 +522,15 @@ int exec_spawn(const ExecCommand *command,
 
         log_debug("About to execute %s", command->path);
 
+        if (cgroup_bondings)
+                if ((r = cgroup_bonding_realize_list(cgroup_bondings)))
+                        return r;
+
         if ((pid = fork()) < 0)
                 return -errno;
 
         if (pid == 0) {
-                int i, r;
+                int i;
                 sigset_t ss;
                 const char *username = NULL, *home = NULL;
                 uid_t uid = (uid_t) -1;
@@ -556,6 +563,12 @@ int exec_spawn(const ExecCommand *command,
                         goto fail;
                 }
 
+                if (cgroup_bondings)
+                        if ((r = cgroup_bonding_install_list(cgroup_bondings, 0)) < 0) {
+                                r = EXIT_CGROUP;
+                                goto fail;
+                        }
+
                 if (context->oom_adjust_set) {
                         char t[16];
 
index d8073e1..cb69bb3 100644 (file)
--- a/execute.h
+++ b/execute.h
@@ -33,6 +33,8 @@ typedef struct ExecContext ExecContext;
 #include <stdio.h>
 #include <sched.h>
 
+struct CGroupBonding;
+
 #include "list.h"
 #include "util.h"
 
@@ -145,7 +147,8 @@ typedef enum ExitStatus {
         EXIT_CPUAFFINITY,
         EXIT_GROUP,
         EXIT_USER,
-        EXIT_CAPABILITIES
+        EXIT_CAPABILITIES,
+        EXIT_CGROUP
 } ExitStatus;
 
 int exec_spawn(const ExecCommand *command,
@@ -153,6 +156,7 @@ int exec_spawn(const ExecCommand *command,
                int *fds, unsigned n_fds,
                bool apply_permissions,
                bool apply_chroot,
+               struct CGroupBonding *cgroup_bondings,
                pid_t *ret);
 
 void exec_command_free_list(ExecCommand *c);
index 0b43c81..bf17111 100644 (file)
@@ -943,6 +943,37 @@ static int config_parse_limit(
         return 0;
 }
 
+static int config_parse_cgroup(
+                const char *filename,
+                unsigned line,
+                const char *section,
+                const char *lvalue,
+                const char *rvalue,
+                void *data,
+                void *userdata) {
+
+        Unit *u = userdata;
+        char *w;
+        size_t l;
+        char *state;
+
+        FOREACH_WORD(w, l, rvalue, state) {
+                char *t;
+                int r;
+
+                if (!(t = strndup(w, l)))
+                        return -ENOMEM;
+
+                r = unit_add_cgroup_from_text(u, t);
+                free(t);
+
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 #define FOLLOW_MAX 8
 
 static int open_follow(char **filename, FILE **_f, Set *names, char **_id) {
@@ -1067,7 +1098,8 @@ static int load_from_path(Unit *u, const char *path) {
                 { "LimitNICE",              config_parse_limit,           &(context).rlimit[RLIMIT_NICE],                  section   }, \
                 { "LimitRTPRIO",            config_parse_limit,           &(context).rlimit[RLIMIT_RTPRIO],                section   }, \
                 { "LimitRTTIME",            config_parse_limit,           &(context).rlimit[RLIMIT_RTTIME],                section   }, \
-                { "NonBlocking",            config_parse_bool,            &(context).non_blocking,                         section   }
+                { "NonBlocking",            config_parse_bool,            &(context).non_blocking,                         section   }, \
+                { "ControlGroup",           config_parse_cgroup,          u,                                               section   }  \
 
         const ConfigItem items[] = {
                 { "Names",                  config_parse_names,           u,                                               "Meta"    },
@@ -1096,6 +1128,7 @@ static int load_from_path(Unit *u, const char *path) {
                 { "Restart",                config_parse_service_restart, &u->service,                                     "Service" },
                 { "PermissionsStartOnly",   config_parse_bool,            &u->service.permissions_start_only,              "Service" },
                 { "RootDirectoryStartOnly", config_parse_bool,            &u->service.root_directory_start_only,           "Service" },
+                { "ValidNoProcess",         config_parse_bool,            &u->service.valid_no_process,                    "Service" },
                 EXEC_CONTEXT_CONFIG_ITEMS(u->service.exec_context, "Service"),
 
                 { "ListenStream",           config_parse_listen,          &u->socket,                                      "Socket"  },
diff --git a/main.c b/main.c
index 830a9a4..4456bde 100644 (file)
--- a/main.c
+++ b/main.c
@@ -37,8 +37,8 @@ int main(int argc, char *argv[]) {
 
         assert_se(set_unit_path("test1") >= 0);
 
-        if (!(m = manager_new())) {
-                log_error("Failed to allocate manager object: %s", strerror(ENOMEM));
+        if ((r = manager_new(&m)) < 0) {
+                log_error("Failed to allocate manager object: %s", strerror(-r));
                 goto finish;
         }
 
index 4f38979..e9bd366 100644 (file)
--- a/manager.c
+++ b/manager.c
@@ -31,6 +31,7 @@
 #include <sys/reboot.h>
 #include <sys/ioctl.h>
 #include <linux/kd.h>
+#include <libcgroup.h>
 
 #include "manager.h"
 #include "hashmap.h"
@@ -39,6 +40,8 @@
 #include "log.h"
 #include "util.h"
 #include "ratelimit.h"
+#include "cgroup.h"
+#include "mount-setup.h"
 
 static int manager_setup_signals(Manager *m) {
         sigset_t mask;
@@ -254,11 +257,14 @@ static int manager_find_paths(Manager *m) {
         return 0;
 }
 
-Manager* manager_new(void) {
+int manager_new(Manager **_m) {
         Manager *m;
+        int r = -ENOMEM;
+
+        assert(_m);
 
         if (!(m = new0(Manager, 1)))
-                return NULL;
+                return -ENOMEM;
 
         m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = -1;
         m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
@@ -275,6 +281,9 @@ Manager* manager_new(void) {
         if (!(m->watch_pids = hashmap_new(trivial_hash_func, trivial_compare_func)))
                 goto fail;
 
+        if (!(m->cgroup_bondings = hashmap_new(string_hash_func, string_compare_func)))
+                goto fail;
+
         if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
                 goto fail;
 
@@ -287,7 +296,7 @@ Manager* manager_new(void) {
 
         log_debug("systemd running in %s mode.", manager_running_as_to_string(m->running_as));
 
-        if (manager_find_paths(m) < 0)
+        if ((r = manager_find_paths(m)) < 0)
                 goto fail;
 
         if (chdir("/") < 0)
@@ -296,18 +305,25 @@ Manager* manager_new(void) {
         /* Become a session leader if we aren't one yet. */
         setsid();
 
-        if (manager_setup_signals(m) < 0)
+        if ((r = manager_setup_signals(m)) < 0)
+                goto fail;
+
+        if ((r = mount_setup()) < 0)
+                goto fail;
+
+        if ((r = manager_setup_cgroup(m)) < 0)
                 goto fail;
 
         /* FIXME: this should be called only when the D-Bus bus daemon is running */
-        if (bus_init(m) < 0)
+        if ((r = bus_init(m)) < 0)
                 goto fail;
 
-        return m;
+        *_m = m;
+        return 0;
 
 fail:
         manager_free(m);
-        return NULL;
+        return r;
 }
 
 void manager_free(Manager *m) {
@@ -327,6 +343,8 @@ void manager_free(Manager *m) {
                 if (unit_vtable[c]->shutdown)
                         unit_vtable[c]->shutdown(m);
 
+        manager_shutdown_cgroup(m);
+
         bus_done(m);
 
         hashmap_free(m->units);
@@ -342,6 +360,12 @@ void manager_free(Manager *m) {
         strv_free(m->unit_path);
         strv_free(m->sysvinit_path);
 
+        free(m->cgroup_controller);
+        free(m->cgroup_hierarchy);
+
+        assert(hashmap_isempty(m->cgroup_bondings));
+        hashmap_free(m->cgroup_bondings);
+
         free(m);
 }
 
index 870db97..8d46323 100644 (file)
--- a/manager.h
+++ b/manager.h
@@ -133,6 +133,7 @@ struct Manager {
         bool dispatching_dbus_queue:1;
 
         bool request_bus_dispatch:1;
+        bool request_system_bus_dispatch:1;
 
         Hashmap *watch_pids;  /* pid => Unit object n:1 */
 
@@ -153,11 +154,16 @@ struct Manager {
         Watch mount_watch;
 
         /* Data specific to the D-Bus subsystem */
-        DBusConnection *bus;
+        DBusConnection *bus, *system_bus;
         Set *subscribed;
+
+        /* Data specific to the cgroup subsystem */
+        Hashmap *cgroup_bondings; /* path string => CGroupBonding object 1:n */
+        char *cgroup_controller;
+        char *cgroup_hierarchy;
 };
 
-Manager* manager_new(void);
+int manager_new(Manager **m);
 void manager_free(Manager *m);
 
 int manager_coldplug(Manager *m);
diff --git a/mount-setup.c b/mount-setup.c
new file mode 100644 (file)
index 0000000..4a5e2cc
--- /dev/null
@@ -0,0 +1,112 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <sys/mount.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+#include <assert.h>
+
+#include "mount-setup.h"
+#include "log.h"
+
+enum {
+        MOUNT_WHAT,
+        MOUNT_WHERE,
+        MOUNT_TYPE,
+        MOUNT_OPTIONS,
+        MOUNT_SKIP
+};
+
+static const char *table[] = {
+        "/proc",  "/proc",         "proc",   "rw",
+        "/sys",   "/sys",          "sysfs",  "rw",
+        "cgroup", "/cgroup/debug", "cgroup", "debug",
+        NULL
+};
+
+static int is_mount_point(const char *t) {
+        struct stat a, b;
+        char *copy;
+
+        if (lstat(t, &a) < 0) {
+
+                if (errno == ENOENT)
+                        return 0;
+
+                return -errno;
+        }
+
+        if (!(copy = strdup(t)))
+                return -ENOMEM;
+
+        if (lstat(dirname(copy), &b) < 0) {
+                free(copy);
+                return -errno;
+        }
+
+        free(copy);
+
+        return a.st_dev != b.st_dev;
+
+}
+
+static int mount_one(const char *t[]) {
+        int r;
+
+        assert(t);
+
+        if ((r = is_mount_point(t[MOUNT_WHERE])) < 0)
+                return r;
+
+        if (r > 0)
+                return 0;
+
+        log_debug("Mounting %s to %s of type %s with options %s.",
+                  t[MOUNT_WHAT],
+                  t[MOUNT_WHERE],
+                  t[MOUNT_TYPE],
+                  t[MOUNT_OPTIONS]);
+
+        if (mount(t[MOUNT_WHAT],
+                  t[MOUNT_WHERE],
+                  t[MOUNT_TYPE],
+                  0,
+                  t[MOUNT_OPTIONS]) < 0) {
+                log_error("Failed to mount %s: %s", t[MOUNT_WHERE], strerror(errno));
+                return -errno;
+        }
+
+        return 0;
+}
+
+int mount_setup(void) {
+        int r;
+        const char **t;
+
+        for (t = table; *t; t += MOUNT_SKIP)
+                if ((r = mount_one(t)) < 0)
+                        return r;
+
+        return 0;
+}
diff --git a/mount-setup.h b/mount-setup.h
new file mode 100644 (file)
index 0000000..df768de
--- /dev/null
@@ -0,0 +1,27 @@
+/*-*- Mode: C; c-basic-offset: 8 -*-*/
+
+#ifndef foomountsetuphfoo
+#define foomountsetuphfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+int mount_setup(void);
+
+#endif
index a693c33..2841689 100644 (file)
--- a/service.c
+++ b/service.c
@@ -581,6 +581,12 @@ static int service_init(Unit *u) {
                 return r;
         }
 
+        /* Add default cgroup */
+        if ((r = unit_add_default_cgroup(u)) < 0) {
+                service_done(u);
+                return r;
+        }
+
         return 0;
 }
 
@@ -599,10 +605,12 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
         fprintf(f,
                 "%sService State: %s\n"
                 "%sPermissionsStartOnly: %s\n"
-                "%sRootDirectoryStartOnly: %s\n",
+                "%sRootDirectoryStartOnly: %s\n"
+                "%sValidNoProcess: %s\n",
                 prefix, service_state_to_string(s->state),
                 prefix, yes_no(s->permissions_start_only),
-                prefix, yes_no(s->root_directory_start_only));
+                prefix, yes_no(s->root_directory_start_only),
+                prefix, yes_no(s->valid_no_process));
 
         if (s->pid_file)
                 fprintf(f,
@@ -898,6 +906,7 @@ static int service_spawn(
                             fds, n_fds,
                             apply_permissions,
                             apply_chroot,
+                            UNIT(s)->meta.cgroup_bondings,
                             &pid)) < 0)
                 goto fail;
 
@@ -1336,7 +1345,7 @@ static int main_pid_good(Service *s) {
                 return s->main_pid > 0;
 
         /* We don't know the pid */
-        return -1;
+        return -EAGAIN;
 }
 
 static bool control_pid_good(Service *s) {
@@ -1345,6 +1354,15 @@ static bool control_pid_good(Service *s) {
         return s->control_pid > 0;
 }
 
+static int cgroup_good(Service *s) {
+        assert(s);
+
+        if (s->valid_no_process)
+                return -EAGAIN;
+
+        return cgroup_bonding_is_empty_list(UNIT(s)->meta.cgroup_bondings);
+}
+
 static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
         Service *s = SERVICE(u);
         bool success;
@@ -1477,7 +1495,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
 
                         case SERVICE_RELOAD:
                                 if (success) {
-                                        if (main_pid_good(s) != 0)
+                                        if (main_pid_good(s) != 0 && cgroup_good(s) != 0)
                                                 service_set_state(s, SERVICE_RUNNING);
                                         else
                                                 service_enter_stop(s, true);
@@ -1580,6 +1598,33 @@ static void service_timer_event(Unit *u, uint64_t elapsed, Watch* w) {
         }
 }
 
+static void service_cgroup_notify_event(Unit *u) {
+        Service *s = SERVICE(u);
+
+        assert(u);
+
+        log_debug("%s: cgroup is empty", unit_id(u));
+
+        switch (s->state) {
+
+                /* Waiting for SIGCHLD is usually more interesting,
+                 * because it includes return codes/signals. Which is
+                 * why we ignore the cgroup events for most cases,
+                 * except when we don't know pid which to expect the
+                 * SIGCHLD for. */
+
+        case SERVICE_RUNNING:
+
+                if (!s->valid_no_process && main_pid_good(s) <= 0)
+                        service_enter_stop(s, true);
+
+                break;
+
+        default:
+                ;
+        }
+}
+
 static int service_enumerate(Manager *m) {
 
         static const char * const rcnd[] = {
@@ -1753,5 +1798,7 @@ const UnitVTable service_vtable = {
         .sigchld_event = service_sigchld_event,
         .timer_event = service_timer_event,
 
+        .cgroup_notify_empty = service_cgroup_notify_event,
+
         .enumerate = service_enumerate
 };
index 632e5ea..5ffdde1 100644 (file)
--- a/service.h
+++ b/service.h
@@ -90,6 +90,7 @@ struct Service {
 
         bool permissions_start_only;
         bool root_directory_start_only;
+        bool valid_no_process;
 
         ServiceState state;
 
index aec0d39..7993334 100644 (file)
--- a/socket.c
+++ b/socket.c
@@ -129,6 +129,10 @@ static int socket_init(Unit *u) {
         if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service))) < 0)
                 goto fail;
 
+        /* Add default cgroup */
+        if ((r = unit_add_default_cgroup(u)) < 0)
+                goto fail;
+
         return 0;
 
 fail:
@@ -394,7 +398,13 @@ static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) {
         } else
                 unit_unwatch_timer(UNIT(s), &s->timer_watch);
 
-        if ((r = exec_spawn(c, &s->exec_context, NULL, 0, true, true, &pid)) < 0)
+        if ((r = exec_spawn(c,
+                            &s->exec_context,
+                            NULL, 0,
+                            true,
+                            true,
+                            UNIT(s)->meta.cgroup_bondings,
+                            &pid)) < 0)
                 goto fail;
 
         if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
index 5914f5f..43a68fc 100644 (file)
@@ -33,7 +33,7 @@ int main(int argc, char *argv[]) {
 
         assert_se(set_unit_path("test2") >= 0);
 
-        assert_se(m = manager_new());
+        assert_se(manager_new(&m) >= 0);
 
         printf("Load1:\n");
         assert_se(manager_load_unit(m, "a.service", &a) == 0);
index b772518..5802e37 100644 (file)
@@ -2,6 +2,8 @@
 Description=Simple Execution Demo
 
 [Service]
-ExecStart=/bin/cat /etc/hosts
-Type=simple
+ExecStartPre=/bin/ps -eo pid,uid,args,cgroup
+ExecStartPre=/bin/cat /etc/hosts
+ExecStart=/bin/bash -c '/bin/sleep 5 &'
+Type=forking
 Output=syslog
diff --git a/unit.c b/unit.c
index 39c9e5e..673aac1 100644 (file)
--- a/unit.c
+++ b/unit.c
@@ -260,6 +260,8 @@ void unit_free(Unit *u) {
 
         /* Detach from next 'bigger' objects */
 
+        cgroup_bonding_free_list(u->meta.cgroup_bondings);
+
         SET_FOREACH(t, u->meta.names, i)
                 hashmap_remove_value(u->meta.manager->units, t, u);
 
@@ -369,12 +371,12 @@ const char *unit_description(Unit *u) {
 }
 
 void unit_dump(Unit *u, FILE *f, const char *prefix) {
-
         char *t;
         UnitDependency d;
         Iterator i;
         char *p2;
         const char *prefix2;
+        CGroupBonding *b;
 
         assert(u);
 
@@ -413,6 +415,10 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
                         fprintf(f, "%s\t%s: %s\n", prefix, unit_dependency_to_string(d), unit_id(other));
         }
 
+        LIST_FOREACH(by_unit, b, u->meta.cgroup_bondings)
+                fprintf(f, "%s\tControlGroup: %s:%s\n",
+                        prefix, b->controller, b->path);
+
         if (UNIT_VTABLE(u)->dump)
                 UNIT_VTABLE(u)->dump(u, f, prefix2);
 
@@ -1060,6 +1066,138 @@ char *unit_dbus_path(Unit *u) {
         return p;
 }
 
+int unit_add_cgroup(Unit *u, CGroupBonding *b) {
+        CGroupBonding *l;
+        int r;
+
+        assert(u);
+        assert(b);
+        assert(b->path);
+
+        /* Ensure this hasn't been added yet */
+        assert(!b->unit);
+
+        l = hashmap_get(u->meta.manager->cgroup_bondings, b->path);
+        LIST_PREPEND(CGroupBonding, by_path, l, b);
+
+        if ((r = hashmap_replace(u->meta.manager->cgroup_bondings, b->path, l)) < 0) {
+                LIST_REMOVE(CGroupBonding, by_path, l, b);
+                return r;
+        }
+
+        LIST_PREPEND(CGroupBonding, by_unit, u->meta.cgroup_bondings, b);
+        b->unit = u;
+
+        return 0;
+}
+
+int unit_add_cgroup_from_text(Unit *u, const char *name) {
+        size_t n;
+        const char *p;
+        char *controller;
+        CGroupBonding *b;
+        int r;
+
+        assert(u);
+        assert(name);
+
+        /* Detect controller name */
+        n = strcspn(name, ":/");
+
+        /* Only controller name, no path? No path? */
+        if (name[n] == 0)
+                return -EINVAL;
+
+        if (n > 0) {
+                if (name[n] != ':')
+                        return -EINVAL;
+
+                p = name+n+1;
+        } else
+                p = name;
+
+        /* Insist in absolute paths */
+        if (p[0] != '/')
+                return -EINVAL;
+
+        if (!(controller = strndup(name, n)))
+                return -ENOMEM;
+
+        if (cgroup_bonding_find_list(u->meta.cgroup_bondings, controller)) {
+                free(controller);
+                return -EEXIST;
+        }
+
+        if (!(b = new0(CGroupBonding, 1))) {
+                free(controller);
+                return -ENOMEM;
+        }
+
+        b->controller = controller;
+
+        if (!(b->path = strdup(p))) {
+                r = -ENOMEM;
+                goto fail;
+        }
+
+        b->only_us = false;
+        b->clean_up = false;
+
+        if ((r = unit_add_cgroup(u, b)) < 0)
+                goto fail;
+
+        return 0;
+
+fail:
+        free(b->path);
+        free(b->controller);
+        free(b);
+
+        return r;
+}
+
+int unit_add_default_cgroup(Unit *u) {
+        CGroupBonding *b;
+        int r = -ENOMEM;
+
+        assert(u);
+
+        /* Adds in the default cgroup data, if it wasn't specified yet */
+
+        if (unit_get_default_cgroup(u))
+                return 0;
+
+        if (!(b = new0(CGroupBonding, 1)))
+                return -ENOMEM;
+
+        if (!(b->controller = strdup(u->meta.manager->cgroup_controller)))
+                goto fail;
+
+        if (asprintf(&b->path, "%s/%s", u->meta.manager->cgroup_hierarchy, unit_id(u)) < 0)
+                goto fail;
+
+        b->clean_up = true;
+        b->only_us = true;
+
+        if ((r = unit_add_cgroup(u, b)) < 0)
+                goto fail;
+
+        return 0;
+
+fail:
+        free(b->path);
+        free(b->controller);
+        free(b);
+
+        return r;
+}
+
+CGroupBonding* unit_get_default_cgroup(Unit *u) {
+        assert(u);
+
+        return cgroup_bonding_find_list(u->meta.cgroup_bondings, u->meta.manager->cgroup_controller);
+}
+
 static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
         [UNIT_SERVICE] = "service",
         [UNIT_TIMER] = "timer",
diff --git a/unit.h b/unit.h
index f5eac29..4b3b6fb 100644 (file)
--- a/unit.h
+++ b/unit.h
@@ -112,6 +112,7 @@ enum UnitDependency {
 
 #include "manager.h"
 #include "job.h"
+#include "cgroup.h"
 
 struct Meta {
         Manager *manager;
@@ -143,6 +144,9 @@ struct Meta {
         usec_t active_enter_timestamp;
         usec_t active_exit_timestamp;
 
+        /* Counterparts in the cgroup filesystem */
+        CGroupBonding *cgroup_bondings;
+
         /* Load queue */
         LIST_FIELDS(Meta, load_queue);
 
@@ -197,6 +201,8 @@ struct UnitVTable {
         void (*sigchld_event)(Unit *u, pid_t pid, int code, int status);
         void (*timer_event)(Unit *u, uint64_t n_elapsed, Watch *w);
 
+        void (*cgroup_notify_empty)(Unit *u);
+
         /* This is called for each unit type and should be used to
          * enumerate existing devices and load them. However,
          * everything that is loaded here should still stay in
@@ -244,6 +250,11 @@ int unit_add_name(Unit *u, const char *name);
 int unit_add_dependency(Unit *u, UnitDependency d, Unit *other);
 int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name);
 
+int unit_add_cgroup(Unit *u, CGroupBonding *b);
+int unit_add_cgroup_from_text(Unit *u, const char *name);
+int unit_add_default_cgroup(Unit *u);
+CGroupBonding* unit_get_default_cgroup(Unit *u);
+
 int unit_choose_id(Unit *u, const char *name);
 int unit_set_description(Unit *u, const char *description);
 
diff --git a/util.c b/util.c
index e316498..d2b7312 100644 (file)
--- a/util.c
+++ b/util.c
@@ -585,7 +585,7 @@ int reset_all_signal_handlers(void) {
                                 return -errno;
         }
 
-    return 0;
+        return 0;
 }
 
 char *strstrip(char *s) {