Commit 8e274523 authored by Lennart Poettering's avatar Lennart Poettering

cgroup: add cgroupsification

parent c9dae904
systemd-cgroups-agent
systemd
*.o
test-engine
......
......@@ -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
This diff is collapsed.
/*-*- 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
/*-*- 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_LIBS)
PKG_CHECK_MODULES(CGROUP, [ libcgroup ])
AC_SUBST(CGROUP_CFLAGS)
AC_SUBST(CGROUP_LIBS)
AM_PROG_VALAC()
AC_SUBST(VAPIDIR)
......
......@@ -29,16 +29,28 @@
#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) {
......
......@@ -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];
......
......@@ -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);
......
......@@ -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" },
......
......@@ -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;
}
......
......@@ -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;
}