cgroup: add cgroupsification
This commit is contained in:
parent
c9dae904f3
commit
8e27452380
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
|
systemd-cgroups-agent
|
||||||
systemd
|
systemd
|
||||||
*.o
|
*.o
|
||||||
test-engine
|
test-engine
|
||||||
|
|
64
Makefile.am
64
Makefile.am
|
@ -25,15 +25,19 @@ AM_CPPFLAGS = \
|
||||||
-DSYSTEM_DATA_UNIT_PATH=\"$(pkgdatadir)/system\" \
|
-DSYSTEM_DATA_UNIT_PATH=\"$(pkgdatadir)/system\" \
|
||||||
-DSYSTEM_SYSVINIT_PATH=\"$(sysconfdir)/init.d\" \
|
-DSYSTEM_SYSVINIT_PATH=\"$(sysconfdir)/init.d\" \
|
||||||
-DSESSION_CONFIG_UNIT_PATH=\"$(pkgsysconfdir)/session\" \
|
-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 = \
|
sbin_PROGRAMS = \
|
||||||
systemd
|
systemd
|
||||||
|
|
||||||
bin_PROGRAMS = \
|
bin_PROGRAMS = \
|
||||||
systemctl \
|
systemctl \
|
||||||
systemadm \
|
systemadm
|
||||||
systemd-logger
|
|
||||||
|
pkglibexec_PROGRAMS = \
|
||||||
|
systemd-logger \
|
||||||
|
systemd-cgroups-agent
|
||||||
|
|
||||||
noinst_PROGRAMS = \
|
noinst_PROGRAMS = \
|
||||||
test-engine \
|
test-engine \
|
||||||
|
@ -41,34 +45,64 @@ noinst_PROGRAMS = \
|
||||||
|
|
||||||
BASIC_SOURCES= \
|
BASIC_SOURCES= \
|
||||||
util.c \
|
util.c \
|
||||||
|
util.h \
|
||||||
hashmap.c \
|
hashmap.c \
|
||||||
|
hashmap.h \
|
||||||
set.c \
|
set.c \
|
||||||
|
set.h \
|
||||||
strv.c \
|
strv.c \
|
||||||
|
strv.h \
|
||||||
conf-parser.c \
|
conf-parser.c \
|
||||||
|
conf-parser.h \
|
||||||
socket-util.c \
|
socket-util.c \
|
||||||
|
socket-util.h \
|
||||||
log.c \
|
log.c \
|
||||||
ratelimit.c
|
log.h \
|
||||||
|
ratelimit.c \
|
||||||
|
ratelimit.h
|
||||||
|
|
||||||
COMMON_SOURCES= \
|
COMMON_SOURCES= \
|
||||||
$(BASIC_SOURCES) \
|
$(BASIC_SOURCES) \
|
||||||
unit.c \
|
unit.c \
|
||||||
|
unit.h \
|
||||||
job.c \
|
job.c \
|
||||||
|
job.h \
|
||||||
manager.c \
|
manager.c \
|
||||||
|
manager.h \
|
||||||
load-fragment.c \
|
load-fragment.c \
|
||||||
|
load-fragment.h \
|
||||||
service.c \
|
service.c \
|
||||||
|
service.h \
|
||||||
automount.c \
|
automount.c \
|
||||||
|
automount.h \
|
||||||
mount.c \
|
mount.c \
|
||||||
|
mount.h \
|
||||||
device.c \
|
device.c \
|
||||||
|
device.h \
|
||||||
target.c \
|
target.c \
|
||||||
|
target.h \
|
||||||
snapshot.c \
|
snapshot.c \
|
||||||
|
snapshot.h \
|
||||||
socket.c \
|
socket.c \
|
||||||
|
socket.h \
|
||||||
timer.c \
|
timer.c \
|
||||||
|
timer.h \
|
||||||
load-dropin.c \
|
load-dropin.c \
|
||||||
|
load-dropin.h \
|
||||||
execute.c \
|
execute.c \
|
||||||
|
execute.h \
|
||||||
dbus.c \
|
dbus.c \
|
||||||
|
dbus.h \
|
||||||
dbus-manager.c \
|
dbus-manager.c \
|
||||||
|
dbus-manager.h \
|
||||||
dbus-unit.c \
|
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 = \
|
systemd_SOURCES = \
|
||||||
$(COMMON_SOURCES) \
|
$(COMMON_SOURCES) \
|
||||||
|
@ -77,11 +111,13 @@ systemd_SOURCES = \
|
||||||
systemd_CPPFLAGS = \
|
systemd_CPPFLAGS = \
|
||||||
$(AM_CPPFLAGS) \
|
$(AM_CPPFLAGS) \
|
||||||
$(DBUS_CFLAGS) \
|
$(DBUS_CFLAGS) \
|
||||||
$(UDEV_CFLAGS)
|
$(UDEV_CFLAGS) \
|
||||||
|
$(CGROUP_CFLAGS)
|
||||||
|
|
||||||
systemd_LDADD = \
|
systemd_LDADD = \
|
||||||
$(DBUS_LIBS) \
|
$(DBUS_LIBS) \
|
||||||
$(UDEV_LIBS)
|
$(UDEV_LIBS) \
|
||||||
|
$(CGROUP_LIBS)
|
||||||
|
|
||||||
test_engine_SOURCES = \
|
test_engine_SOURCES = \
|
||||||
$(COMMON_SOURCES) \
|
$(COMMON_SOURCES) \
|
||||||
|
@ -101,6 +137,17 @@ systemd_logger_SOURCES = \
|
||||||
$(BASIC_SOURCES) \
|
$(BASIC_SOURCES) \
|
||||||
logger.c
|
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
|
VALAFLAGS = -g --save-temps --pkg=dbus-glib-1 --pkg=posix --pkg gee-1.0 --pkg gtk+-2.0
|
||||||
|
|
||||||
systemctl_SOURCES = \
|
systemctl_SOURCES = \
|
||||||
|
@ -120,4 +167,5 @@ systemadm_LDADD = $(DBUSGLIB_LIBS) $(GTK_LIBS)
|
||||||
CLEANFILES = \
|
CLEANFILES = \
|
||||||
systemd-interfaces.c \
|
systemd-interfaces.c \
|
||||||
systemctl.c \
|
systemctl.c \
|
||||||
systemadm.c
|
systemadm.c \
|
||||||
|
systemd-cgroups-agent
|
||||||
|
|
523
cgroup.c
Normal file
523
cgroup.c
Normal file
|
@ -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;
|
||||||
|
}
|
83
cgroup.h
Normal file
83
cgroup.h
Normal file
|
@ -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
|
72
cgroups-agent.c
Normal file
72
cgroups-agent.c
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -69,6 +69,10 @@ PKG_CHECK_MODULES(GTK, [ gtk+-2.0 ])
|
||||||
AC_SUBST(GTK_CFLAGS)
|
AC_SUBST(GTK_CFLAGS)
|
||||||
AC_SUBST(GTK_LIBS)
|
AC_SUBST(GTK_LIBS)
|
||||||
|
|
||||||
|
PKG_CHECK_MODULES(CGROUP, [ libcgroup ])
|
||||||
|
AC_SUBST(CGROUP_CFLAGS)
|
||||||
|
AC_SUBST(CGROUP_LIBS)
|
||||||
|
|
||||||
AM_PROG_VALAC()
|
AM_PROG_VALAC()
|
||||||
AC_SUBST(VAPIDIR)
|
AC_SUBST(VAPIDIR)
|
||||||
|
|
||||||
|
|
137
dbus.c
137
dbus.c
|
@ -29,16 +29,28 @@
|
||||||
#include "dbus.h"
|
#include "dbus.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
|
#include "cgroup.h"
|
||||||
|
|
||||||
static void bus_dispatch_status(DBusConnection *bus, DBusDispatchStatus status, void *data) {
|
static void bus_dispatch_status(DBusConnection *bus, DBusDispatchStatus status, void *data) {
|
||||||
Manager *m = data;
|
Manager *m = data;
|
||||||
|
|
||||||
assert(bus);
|
assert(bus);
|
||||||
assert(m);
|
assert(m);
|
||||||
|
assert(m->bus == bus);
|
||||||
|
|
||||||
m->request_bus_dispatch = status != DBUS_DISPATCH_COMPLETE;
|
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) {
|
static uint32_t bus_flags_to_events(DBusWatch *bus_watch) {
|
||||||
unsigned flags;
|
unsigned flags;
|
||||||
uint32_t events = 0;
|
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
|
/* This is called by the event loop whenever there is
|
||||||
* something happening on D-Bus' file handles. */
|
* 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;
|
return;
|
||||||
|
|
||||||
dbus_watch_handle(w->data.bus_watch, events_to_bus_flags(events));
|
dbus_watch_handle(w->data.bus_watch, events_to_bus_flags(events));
|
||||||
|
@ -315,18 +327,59 @@ static DBusHandlerResult bus_message_filter(DBusConnection *connection, DBusMes
|
||||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
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) {
|
unsigned bus_dispatch(Manager *m) {
|
||||||
assert(m);
|
assert(m);
|
||||||
|
|
||||||
if (!m->request_bus_dispatch)
|
if (m->request_bus_dispatch)
|
||||||
return 0;
|
if (dbus_connection_dispatch(m->bus) == DBUS_DISPATCH_COMPLETE) {
|
||||||
|
|
||||||
if (dbus_connection_dispatch(m->bus) == DBUS_DISPATCH_COMPLETE)
|
|
||||||
m->request_bus_dispatch = false;
|
m->request_bus_dispatch = false;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int request_name(Manager *m) {
|
static int request_name(Manager *m) {
|
||||||
DBusMessage *message;
|
DBusMessage *message;
|
||||||
const char *name = "org.freedesktop.systemd1";
|
const char *name = "org.freedesktop.systemd1";
|
||||||
|
@ -362,6 +415,18 @@ static int request_name(Manager *m) {
|
||||||
return 0;
|
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) {
|
int bus_init(Manager *m) {
|
||||||
DBusError error;
|
DBusError error;
|
||||||
char *id;
|
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))) {
|
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);
|
log_error("Failed to get D-Bus connection: %s", error.message);
|
||||||
dbus_error_free(&error);
|
dbus_error_free(&error);
|
||||||
|
bus_done(m);
|
||||||
return -ECONNREFUSED;
|
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);
|
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) ||
|
if (m->running_as == MANAGER_SESSION) {
|
||||||
!dbus_connection_register_object_path(m->bus, "/org/freedesktop/systemd1", &bus_manager_vtable, m) ||
|
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/unit", &bus_unit_vtable, m) ||
|
||||||
!dbus_connection_register_fallback(m->bus, "/org/freedesktop/systemd1/job", &bus_job_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);
|
bus_done(m);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
@ -406,7 +493,6 @@ int bus_init(Manager *m) {
|
||||||
if (dbus_error_is_set(&error)) {
|
if (dbus_error_is_set(&error)) {
|
||||||
log_error("Failed to register match: %s", error.message);
|
log_error("Failed to register match: %s", error.message);
|
||||||
dbus_error_free(&error);
|
dbus_error_free(&error);
|
||||||
bus_done(m);
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -415,12 +501,31 @@ int bus_init(Manager *m) {
|
||||||
return r;
|
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",
|
log_debug("Successfully connected to D-Bus bus %s as %s",
|
||||||
strnull((id = dbus_connection_get_server_id(m->bus))),
|
strnull((id = dbus_connection_get_server_id(m->bus))),
|
||||||
strnull(dbus_bus_get_unique_name(m->bus)));
|
strnull(dbus_bus_get_unique_name(m->bus)));
|
||||||
dbus_free(id);
|
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_bus_dispatch = true;
|
||||||
|
m->request_system_bus_dispatch = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -428,6 +533,12 @@ int bus_init(Manager *m) {
|
||||||
void bus_done(Manager *m) {
|
void bus_done(Manager *m) {
|
||||||
assert(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) {
|
if (m->bus) {
|
||||||
dbus_connection_close(m->bus);
|
dbus_connection_close(m->bus);
|
||||||
dbus_connection_unref(m->bus);
|
dbus_connection_unref(m->bus);
|
||||||
|
@ -571,8 +682,6 @@ oom:
|
||||||
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
return DBUS_HANDLER_RESULT_NEED_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static const char *error_to_dbus(int error) {
|
static const char *error_to_dbus(int error) {
|
||||||
|
|
||||||
switch(error) {
|
switch(error) {
|
||||||
|
|
15
execute.c
15
execute.c
|
@ -42,6 +42,7 @@
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "ioprio.h"
|
#include "ioprio.h"
|
||||||
#include "securebits.h"
|
#include "securebits.h"
|
||||||
|
#include "cgroup.h"
|
||||||
|
|
||||||
static int close_fds(int except[], unsigned n_except) {
|
static int close_fds(int except[], unsigned n_except) {
|
||||||
DIR *d;
|
DIR *d;
|
||||||
|
@ -508,9 +509,11 @@ int exec_spawn(const ExecCommand *command,
|
||||||
int *fds, unsigned n_fds,
|
int *fds, unsigned n_fds,
|
||||||
bool apply_permissions,
|
bool apply_permissions,
|
||||||
bool apply_chroot,
|
bool apply_chroot,
|
||||||
|
CGroupBonding *cgroup_bondings,
|
||||||
pid_t *ret) {
|
pid_t *ret) {
|
||||||
|
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
int r;
|
||||||
|
|
||||||
assert(command);
|
assert(command);
|
||||||
assert(context);
|
assert(context);
|
||||||
|
@ -519,11 +522,15 @@ int exec_spawn(const ExecCommand *command,
|
||||||
|
|
||||||
log_debug("About to execute %s", command->path);
|
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)
|
if ((pid = fork()) < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
int i, r;
|
int i;
|
||||||
sigset_t ss;
|
sigset_t ss;
|
||||||
const char *username = NULL, *home = NULL;
|
const char *username = NULL, *home = NULL;
|
||||||
uid_t uid = (uid_t) -1;
|
uid_t uid = (uid_t) -1;
|
||||||
|
@ -556,6 +563,12 @@ int exec_spawn(const ExecCommand *command,
|
||||||
goto fail;
|
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) {
|
if (context->oom_adjust_set) {
|
||||||
char t[16];
|
char t[16];
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,8 @@ typedef struct ExecContext ExecContext;
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
|
|
||||||
|
struct CGroupBonding;
|
||||||
|
|
||||||
#include "list.h"
|
#include "list.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
@ -145,7 +147,8 @@ typedef enum ExitStatus {
|
||||||
EXIT_CPUAFFINITY,
|
EXIT_CPUAFFINITY,
|
||||||
EXIT_GROUP,
|
EXIT_GROUP,
|
||||||
EXIT_USER,
|
EXIT_USER,
|
||||||
EXIT_CAPABILITIES
|
EXIT_CAPABILITIES,
|
||||||
|
EXIT_CGROUP
|
||||||
} ExitStatus;
|
} ExitStatus;
|
||||||
|
|
||||||
int exec_spawn(const ExecCommand *command,
|
int exec_spawn(const ExecCommand *command,
|
||||||
|
@ -153,6 +156,7 @@ int exec_spawn(const ExecCommand *command,
|
||||||
int *fds, unsigned n_fds,
|
int *fds, unsigned n_fds,
|
||||||
bool apply_permissions,
|
bool apply_permissions,
|
||||||
bool apply_chroot,
|
bool apply_chroot,
|
||||||
|
struct CGroupBonding *cgroup_bondings,
|
||||||
pid_t *ret);
|
pid_t *ret);
|
||||||
|
|
||||||
void exec_command_free_list(ExecCommand *c);
|
void exec_command_free_list(ExecCommand *c);
|
||||||
|
|
|
@ -943,6 +943,37 @@ static int config_parse_limit(
|
||||||
return 0;
|
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
|
#define FOLLOW_MAX 8
|
||||||
|
|
||||||
static int open_follow(char **filename, FILE **_f, Set *names, char **_id) {
|
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 }, \
|
{ "LimitNICE", config_parse_limit, &(context).rlimit[RLIMIT_NICE], section }, \
|
||||||
{ "LimitRTPRIO", config_parse_limit, &(context).rlimit[RLIMIT_RTPRIO], section }, \
|
{ "LimitRTPRIO", config_parse_limit, &(context).rlimit[RLIMIT_RTPRIO], section }, \
|
||||||
{ "LimitRTTIME", config_parse_limit, &(context).rlimit[RLIMIT_RTTIME], 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[] = {
|
const ConfigItem items[] = {
|
||||||
{ "Names", config_parse_names, u, "Meta" },
|
{ "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" },
|
{ "Restart", config_parse_service_restart, &u->service, "Service" },
|
||||||
{ "PermissionsStartOnly", config_parse_bool, &u->service.permissions_start_only, "Service" },
|
{ "PermissionsStartOnly", config_parse_bool, &u->service.permissions_start_only, "Service" },
|
||||||
{ "RootDirectoryStartOnly", config_parse_bool, &u->service.root_directory_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"),
|
EXEC_CONTEXT_CONFIG_ITEMS(u->service.exec_context, "Service"),
|
||||||
|
|
||||||
{ "ListenStream", config_parse_listen, &u->socket, "Socket" },
|
{ "ListenStream", config_parse_listen, &u->socket, "Socket" },
|
||||||
|
|
4
main.c
4
main.c
|
@ -37,8 +37,8 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
assert_se(set_unit_path("test1") >= 0);
|
assert_se(set_unit_path("test1") >= 0);
|
||||||
|
|
||||||
if (!(m = manager_new())) {
|
if ((r = manager_new(&m)) < 0) {
|
||||||
log_error("Failed to allocate manager object: %s", strerror(ENOMEM));
|
log_error("Failed to allocate manager object: %s", strerror(-r));
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
38
manager.c
38
manager.c
|
@ -31,6 +31,7 @@
|
||||||
#include <sys/reboot.h>
|
#include <sys/reboot.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <linux/kd.h>
|
#include <linux/kd.h>
|
||||||
|
#include <libcgroup.h>
|
||||||
|
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
|
@ -39,6 +40,8 @@
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "ratelimit.h"
|
#include "ratelimit.h"
|
||||||
|
#include "cgroup.h"
|
||||||
|
#include "mount-setup.h"
|
||||||
|
|
||||||
static int manager_setup_signals(Manager *m) {
|
static int manager_setup_signals(Manager *m) {
|
||||||
sigset_t mask;
|
sigset_t mask;
|
||||||
|
@ -254,11 +257,14 @@ static int manager_find_paths(Manager *m) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Manager* manager_new(void) {
|
int manager_new(Manager **_m) {
|
||||||
Manager *m;
|
Manager *m;
|
||||||
|
int r = -ENOMEM;
|
||||||
|
|
||||||
|
assert(_m);
|
||||||
|
|
||||||
if (!(m = new0(Manager, 1)))
|
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->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 */
|
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)))
|
if (!(m->watch_pids = hashmap_new(trivial_hash_func, trivial_compare_func)))
|
||||||
goto fail;
|
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)
|
if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
|
||||||
goto fail;
|
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));
|
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;
|
goto fail;
|
||||||
|
|
||||||
if (chdir("/") < 0)
|
if (chdir("/") < 0)
|
||||||
|
@ -296,18 +305,25 @@ Manager* manager_new(void) {
|
||||||
/* Become a session leader if we aren't one yet. */
|
/* Become a session leader if we aren't one yet. */
|
||||||
setsid();
|
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;
|
goto fail;
|
||||||
|
|
||||||
/* FIXME: this should be called only when the D-Bus bus daemon is running */
|
/* 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;
|
goto fail;
|
||||||
|
|
||||||
return m;
|
*_m = m;
|
||||||
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
manager_free(m);
|
manager_free(m);
|
||||||
return NULL;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
void manager_free(Manager *m) {
|
void manager_free(Manager *m) {
|
||||||
|
@ -327,6 +343,8 @@ void manager_free(Manager *m) {
|
||||||
if (unit_vtable[c]->shutdown)
|
if (unit_vtable[c]->shutdown)
|
||||||
unit_vtable[c]->shutdown(m);
|
unit_vtable[c]->shutdown(m);
|
||||||
|
|
||||||
|
manager_shutdown_cgroup(m);
|
||||||
|
|
||||||
bus_done(m);
|
bus_done(m);
|
||||||
|
|
||||||
hashmap_free(m->units);
|
hashmap_free(m->units);
|
||||||
|
@ -342,6 +360,12 @@ void manager_free(Manager *m) {
|
||||||
strv_free(m->unit_path);
|
strv_free(m->unit_path);
|
||||||
strv_free(m->sysvinit_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);
|
free(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
manager.h
10
manager.h
|
@ -133,6 +133,7 @@ struct Manager {
|
||||||
bool dispatching_dbus_queue:1;
|
bool dispatching_dbus_queue:1;
|
||||||
|
|
||||||
bool request_bus_dispatch:1;
|
bool request_bus_dispatch:1;
|
||||||
|
bool request_system_bus_dispatch:1;
|
||||||
|
|
||||||
Hashmap *watch_pids; /* pid => Unit object n:1 */
|
Hashmap *watch_pids; /* pid => Unit object n:1 */
|
||||||
|
|
||||||
|
@ -153,11 +154,16 @@ struct Manager {
|
||||||
Watch mount_watch;
|
Watch mount_watch;
|
||||||
|
|
||||||
/* Data specific to the D-Bus subsystem */
|
/* Data specific to the D-Bus subsystem */
|
||||||
DBusConnection *bus;
|
DBusConnection *bus, *system_bus;
|
||||||
Set *subscribed;
|
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);
|
void manager_free(Manager *m);
|
||||||
|
|
||||||
int manager_coldplug(Manager *m);
|
int manager_coldplug(Manager *m);
|
||||||
|
|
112
mount-setup.c
Normal file
112
mount-setup.c
Normal file
|
@ -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;
|
||||||
|
}
|
27
mount-setup.h
Normal file
27
mount-setup.h
Normal file
|
@ -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
|
55
service.c
55
service.c
|
@ -581,6 +581,12 @@ static int service_init(Unit *u) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add default cgroup */
|
||||||
|
if ((r = unit_add_default_cgroup(u)) < 0) {
|
||||||
|
service_done(u);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -599,10 +605,12 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
|
||||||
fprintf(f,
|
fprintf(f,
|
||||||
"%sService State: %s\n"
|
"%sService State: %s\n"
|
||||||
"%sPermissionsStartOnly: %s\n"
|
"%sPermissionsStartOnly: %s\n"
|
||||||
"%sRootDirectoryStartOnly: %s\n",
|
"%sRootDirectoryStartOnly: %s\n"
|
||||||
|
"%sValidNoProcess: %s\n",
|
||||||
prefix, service_state_to_string(s->state),
|
prefix, service_state_to_string(s->state),
|
||||||
prefix, yes_no(s->permissions_start_only),
|
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)
|
if (s->pid_file)
|
||||||
fprintf(f,
|
fprintf(f,
|
||||||
|
@ -898,6 +906,7 @@ static int service_spawn(
|
||||||
fds, n_fds,
|
fds, n_fds,
|
||||||
apply_permissions,
|
apply_permissions,
|
||||||
apply_chroot,
|
apply_chroot,
|
||||||
|
UNIT(s)->meta.cgroup_bondings,
|
||||||
&pid)) < 0)
|
&pid)) < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
@ -1336,7 +1345,7 @@ static int main_pid_good(Service *s) {
|
||||||
return s->main_pid > 0;
|
return s->main_pid > 0;
|
||||||
|
|
||||||
/* We don't know the pid */
|
/* We don't know the pid */
|
||||||
return -1;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool control_pid_good(Service *s) {
|
static bool control_pid_good(Service *s) {
|
||||||
|
@ -1345,6 +1354,15 @@ static bool control_pid_good(Service *s) {
|
||||||
return s->control_pid > 0;
|
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) {
|
static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||||
Service *s = SERVICE(u);
|
Service *s = SERVICE(u);
|
||||||
bool success;
|
bool success;
|
||||||
|
@ -1477,7 +1495,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||||
|
|
||||||
case SERVICE_RELOAD:
|
case SERVICE_RELOAD:
|
||||||
if (success) {
|
if (success) {
|
||||||
if (main_pid_good(s) != 0)
|
if (main_pid_good(s) != 0 && cgroup_good(s) != 0)
|
||||||
service_set_state(s, SERVICE_RUNNING);
|
service_set_state(s, SERVICE_RUNNING);
|
||||||
else
|
else
|
||||||
service_enter_stop(s, true);
|
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 int service_enumerate(Manager *m) {
|
||||||
|
|
||||||
static const char * const rcnd[] = {
|
static const char * const rcnd[] = {
|
||||||
|
@ -1753,5 +1798,7 @@ const UnitVTable service_vtable = {
|
||||||
.sigchld_event = service_sigchld_event,
|
.sigchld_event = service_sigchld_event,
|
||||||
.timer_event = service_timer_event,
|
.timer_event = service_timer_event,
|
||||||
|
|
||||||
|
.cgroup_notify_empty = service_cgroup_notify_event,
|
||||||
|
|
||||||
.enumerate = service_enumerate
|
.enumerate = service_enumerate
|
||||||
};
|
};
|
||||||
|
|
|
@ -90,6 +90,7 @@ struct Service {
|
||||||
|
|
||||||
bool permissions_start_only;
|
bool permissions_start_only;
|
||||||
bool root_directory_start_only;
|
bool root_directory_start_only;
|
||||||
|
bool valid_no_process;
|
||||||
|
|
||||||
ServiceState state;
|
ServiceState state;
|
||||||
|
|
||||||
|
|
12
socket.c
12
socket.c
|
@ -129,6 +129,10 @@ static int socket_init(Unit *u) {
|
||||||
if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service))) < 0)
|
if ((r = unit_add_dependency(u, UNIT_BEFORE, UNIT(s->service))) < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
/* Add default cgroup */
|
||||||
|
if ((r = unit_add_default_cgroup(u)) < 0)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
@ -394,7 +398,13 @@ static int socket_spawn(Socket *s, ExecCommand *c, bool timeout, pid_t *_pid) {
|
||||||
} else
|
} else
|
||||||
unit_unwatch_timer(UNIT(s), &s->timer_watch);
|
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;
|
goto fail;
|
||||||
|
|
||||||
if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
|
if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
|
||||||
|
|
|
@ -33,7 +33,7 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
assert_se(set_unit_path("test2") >= 0);
|
assert_se(set_unit_path("test2") >= 0);
|
||||||
|
|
||||||
assert_se(m = manager_new());
|
assert_se(manager_new(&m) >= 0);
|
||||||
|
|
||||||
printf("Load1:\n");
|
printf("Load1:\n");
|
||||||
assert_se(manager_load_unit(m, "a.service", &a) == 0);
|
assert_se(manager_load_unit(m, "a.service", &a) == 0);
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
Description=Simple Execution Demo
|
Description=Simple Execution Demo
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=/bin/cat /etc/hosts
|
ExecStartPre=/bin/ps -eo pid,uid,args,cgroup
|
||||||
Type=simple
|
ExecStartPre=/bin/cat /etc/hosts
|
||||||
|
ExecStart=/bin/bash -c '/bin/sleep 5 &'
|
||||||
|
Type=forking
|
||||||
Output=syslog
|
Output=syslog
|
||||||
|
|
140
unit.c
140
unit.c
|
@ -260,6 +260,8 @@ void unit_free(Unit *u) {
|
||||||
|
|
||||||
/* Detach from next 'bigger' objects */
|
/* Detach from next 'bigger' objects */
|
||||||
|
|
||||||
|
cgroup_bonding_free_list(u->meta.cgroup_bondings);
|
||||||
|
|
||||||
SET_FOREACH(t, u->meta.names, i)
|
SET_FOREACH(t, u->meta.names, i)
|
||||||
hashmap_remove_value(u->meta.manager->units, t, u);
|
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) {
|
void unit_dump(Unit *u, FILE *f, const char *prefix) {
|
||||||
|
|
||||||
char *t;
|
char *t;
|
||||||
UnitDependency d;
|
UnitDependency d;
|
||||||
Iterator i;
|
Iterator i;
|
||||||
char *p2;
|
char *p2;
|
||||||
const char *prefix2;
|
const char *prefix2;
|
||||||
|
CGroupBonding *b;
|
||||||
|
|
||||||
assert(u);
|
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));
|
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)
|
if (UNIT_VTABLE(u)->dump)
|
||||||
UNIT_VTABLE(u)->dump(u, f, prefix2);
|
UNIT_VTABLE(u)->dump(u, f, prefix2);
|
||||||
|
|
||||||
|
@ -1060,6 +1066,138 @@ char *unit_dbus_path(Unit *u) {
|
||||||
return p;
|
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] = {
|
static const char* const unit_type_table[_UNIT_TYPE_MAX] = {
|
||||||
[UNIT_SERVICE] = "service",
|
[UNIT_SERVICE] = "service",
|
||||||
[UNIT_TIMER] = "timer",
|
[UNIT_TIMER] = "timer",
|
||||||
|
|
11
unit.h
11
unit.h
|
@ -112,6 +112,7 @@ enum UnitDependency {
|
||||||
|
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
#include "job.h"
|
#include "job.h"
|
||||||
|
#include "cgroup.h"
|
||||||
|
|
||||||
struct Meta {
|
struct Meta {
|
||||||
Manager *manager;
|
Manager *manager;
|
||||||
|
@ -143,6 +144,9 @@ struct Meta {
|
||||||
usec_t active_enter_timestamp;
|
usec_t active_enter_timestamp;
|
||||||
usec_t active_exit_timestamp;
|
usec_t active_exit_timestamp;
|
||||||
|
|
||||||
|
/* Counterparts in the cgroup filesystem */
|
||||||
|
CGroupBonding *cgroup_bondings;
|
||||||
|
|
||||||
/* Load queue */
|
/* Load queue */
|
||||||
LIST_FIELDS(Meta, 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 (*sigchld_event)(Unit *u, pid_t pid, int code, int status);
|
||||||
void (*timer_event)(Unit *u, uint64_t n_elapsed, Watch *w);
|
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
|
/* This is called for each unit type and should be used to
|
||||||
* enumerate existing devices and load them. However,
|
* enumerate existing devices and load them. However,
|
||||||
* everything that is loaded here should still stay in
|
* 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(Unit *u, UnitDependency d, Unit *other);
|
||||||
int unit_add_dependency_by_name(Unit *u, UnitDependency d, const char *name);
|
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_choose_id(Unit *u, const char *name);
|
||||||
int unit_set_description(Unit *u, const char *description);
|
int unit_set_description(Unit *u, const char *description);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue