logind: port over to use scopes+slices for all cgroup stuff

In order to prepare things for the single-writer cgroup scheme, let's
make logind use systemd's own primitives for cgroup management.

Every login user now gets his own private slice unit, in which his sessions
live in a scope unit each. Also, add user@$UID.service to the same
slice, and implicitly start it on first login.
This commit is contained in:
Lennart Poettering 2013-07-02 01:46:30 +02:00
parent 358712f3de
commit fb6becb443
21 changed files with 1029 additions and 1129 deletions

16
TODO
View File

@ -28,6 +28,22 @@ Fedora 19:
Features:
* libsystemd-logind: recognize new session/user/machine units
* logind: implement session kill exceptions
* fix machine regstration to forward property array
* fix loginctl cgroup enumeration
* move "systemctl dump" to systemd-analyze
* introduce "mainpid" for scopes
* add a fixed dbus path for "my own unit", "my own session", ... to PID1, logind, ...
* add implicit slice for instantiated services
* service_coldplug() appears to reinstall the wrong stop timeout watch?
* transient units: allow creating auxiliary units with the same call

View File

@ -40,3 +40,4 @@
#define BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC "org.freedesktop.systemd1.TransactionOrderIsCyclic"
#define BUS_ERROR_SHUTTING_DOWN "org.freedesktop.systemd1.ShuttingDown"
#define BUS_ERROR_NO_SUCH_PROCESS "org.freedesktop.systemd1.NoSuchProcess"
#define BUS_ERROR_JOB_FAILED "org.freedesktop.systemd1.JobFailed"

View File

@ -38,6 +38,8 @@
#include "label.h"
#include "utf8.h"
#include "unit-name.h"
#include "bus-errors.h"
#include "virt.h"
#define BUS_MANAGER_INTERFACE \
" <interface name=\"org.freedesktop.login1.Manager\">\n" \
@ -94,9 +96,7 @@
" <arg name=\"remote\" type=\"b\" direction=\"in\"/>\n" \
" <arg name=\"remote_user\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"remote_host\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"controllers\" type=\"as\" direction=\"in\"/>\n" \
" <arg name=\"reset_controllers\" type=\"as\" direction=\"in\"/>\n" \
" <arg name=\"kill_processes\" type=\"b\" direction=\"in\"/>\n" \
" <arg name=\"scope_properties\" type=\"a(sv)\" direction=\"in\"/>\n" \
" <arg name=\"id\" type=\"s\" direction=\"out\"/>\n" \
" <arg name=\"path\" type=\"o\" direction=\"out\"/>\n" \
" <arg name=\"runtime_path\" type=\"o\" direction=\"out\"/>\n" \
@ -114,8 +114,8 @@
" <arg name=\"service\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"class\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n" \
" <arg name=\"slice\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"root_directory\" type=\"s\" direction=\"in\"/>\n" \
" <arg name=\"scope_properties\" type=\"a(sv)\" direction=\"in\"/>\n" \
" <arg name=\"path\" type=\"o\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"ActivateSession\">\n" \
@ -345,27 +345,24 @@ static int bus_manager_append_preparing(DBusMessageIter *i, const char *property
return 0;
}
static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMessage **_reply) {
static int bus_manager_create_session(Manager *m, DBusMessage *message) {
const char *type, *class, *cseat, *tty, *display, *remote_user, *remote_host, *service;
uint32_t uid, leader, audit_id = 0;
dbus_bool_t remote, kill_processes, exists;
_cleanup_strv_free_ char **controllers = NULL, **reset_controllers = NULL;
_cleanup_free_ char *cgroup = NULL, *id = NULL, *p = NULL;
SessionType t;
SessionClass c;
DBusMessageIter iter;
int r;
uint32_t vtnr = 0;
_cleanup_close_ int fifo_fd = -1;
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
_cleanup_free_ char *id = NULL;
Session *session = NULL;
User *user = NULL;
Seat *seat = NULL;
DBusMessageIter iter;
dbus_bool_t remote;
uint32_t vtnr = 0;
SessionType t;
SessionClass c;
bool b;
int r;
assert(m);
assert(message);
assert(_reply);
if (!dbus_message_iter_init(message, &iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
@ -515,67 +512,37 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
dbus_message_iter_get_basic(&iter, &remote_host);
if (!dbus_message_iter_next(&iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING)
return -EINVAL;
r = bus_parse_strv_iter(&iter, &controllers);
if (r < 0)
return -EINVAL;
if (!dbus_message_iter_next(&iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING) {
r = -EINVAL;
goto fail;
}
r = bus_parse_strv_iter(&iter, &reset_controllers);
if (r < 0)
goto fail;
if (!dbus_message_iter_next(&iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN) {
r = -EINVAL;
goto fail;
}
dbus_message_iter_get_basic(&iter, &kill_processes);
if (leader <= 0) {
leader = bus_get_unix_process_id(m->bus, dbus_message_get_sender(message), NULL);
if (leader == 0)
return -EINVAL;
}
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, leader, &cgroup);
if (r < 0)
goto fail;
r = manager_get_session_by_cgroup(m, cgroup, &session);
if (r < 0)
goto fail;
r = manager_get_session_by_pid(m, leader, &session);
if (session) {
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
_cleanup_free_ char *path = NULL;
_cleanup_close_ int fifo_fd = -1;
bool exists;
/* Session already exists, client is probably
* something like "su" which changes uid but is still
* the same session */
fifo_fd = session_create_fifo(session);
if (fifo_fd < 0) {
r = fifo_fd;
goto fail;
}
/* Session already exists, client is probably
* something like "su" which changes uid but
* is still the same audit session */
reply = dbus_message_new_method_return(message);
if (!reply) {
path = session_bus_path(session);
if (!path) {
r = -ENOMEM;
goto fail;
}
p = session_bus_path(session);
if (!p) {
reply = dbus_message_new_method_return(message);
if (!reply) {
r = -ENOMEM;
goto fail;
}
@ -587,7 +554,7 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
b = dbus_message_append_args(
reply,
DBUS_TYPE_STRING, &session->id,
DBUS_TYPE_OBJECT_PATH, &p,
DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_STRING, &session->user->runtime_path,
DBUS_TYPE_UNIX_FD, &fifo_fd,
DBUS_TYPE_STRING, &cseat,
@ -599,8 +566,10 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
goto fail;
}
*_reply = reply;
reply = NULL;
if (!dbus_connection_send(m->bus, reply, NULL)) {
r = -ENOMEM;
goto fail;
}
return 0;
}
@ -654,13 +623,8 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
session->type = t;
session->class = c;
session->remote = remote;
session->kill_processes = kill_processes;
session->vtnr = vtnr;
session->controllers = cg_shorten_controllers(controllers);
session->reset_controllers = cg_shorten_controllers(reset_controllers);
controllers = reset_controllers = NULL;
if (!isempty(tty)) {
session->tty = strdup(tty);
if (!session->tty) {
@ -701,12 +665,6 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
}
}
fifo_fd = session_create_fifo(session);
if (fifo_fd < 0) {
r = fifo_fd;
goto fail;
}
if (seat) {
r = seat_attach_session(seat, session);
if (r < 0)
@ -717,38 +675,7 @@ static int bus_manager_create_session(Manager *m, DBusMessage *message, DBusMess
if (r < 0)
goto fail;
reply = dbus_message_new_method_return(message);
if (!reply) {
r = -ENOMEM;
goto fail;
}
p = session_bus_path(session);
if (!p) {
r = -ENOMEM;
goto fail;
}
cseat = seat ? seat->id : "";
exists = false;
b = dbus_message_append_args(
reply,
DBUS_TYPE_STRING, &session->id,
DBUS_TYPE_OBJECT_PATH, &p,
DBUS_TYPE_STRING, &session->user->runtime_path,
DBUS_TYPE_UNIX_FD, &fifo_fd,
DBUS_TYPE_STRING, &cseat,
DBUS_TYPE_UINT32, &vtnr,
DBUS_TYPE_BOOLEAN, &exists,
DBUS_TYPE_INVALID);
if (!b) {
r = -ENOMEM;
goto fail;
}
*_reply = reply;
reply = NULL;
session->create_message = dbus_message_ref(message);
return 0;
@ -779,26 +706,20 @@ static bool valid_machine_name(const char *p) {
return true;
}
static int bus_manager_create_machine(
Manager *manager,
DBusMessage *message,
DBusMessage **_reply) {
static int bus_manager_create_machine(Manager *manager, DBusMessage *message) {
const char *name, *service, *class, *slice, *root_directory;
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
_cleanup_free_ char *p = NULL;
DBusMessageIter iter, sub;
MachineClass c;
uint32_t leader;
sd_id128_t id;
dbus_bool_t b;
Machine *m;
int n, r;
void *v;
assert(manager);
assert(message);
assert(_reply);
if (!dbus_message_iter_init(message, &iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
@ -887,14 +808,6 @@ static int bus_manager_create_machine(
}
}
if (!isempty(slice)) {
m->slice = strdup(slice);
if (!m->slice) {
r = -ENOMEM;
goto fail;
}
}
if (!isempty(root_directory)) {
m->root_directory = strdup(root_directory);
if (!m->root_directory) {
@ -907,29 +820,8 @@ static int bus_manager_create_machine(
if (r < 0)
goto fail;
reply = dbus_message_new_method_return(message);
if (!reply) {
r = -ENOMEM;
goto fail;
}
m->create_message = dbus_message_ref(message);
p = machine_bus_path(m);
if (!p) {
r = -ENOMEM;
goto fail;
}
b = dbus_message_append_args(
reply,
DBUS_TYPE_OBJECT_PATH, &p,
DBUS_TYPE_INVALID);
if (!b) {
r = -ENOMEM;
goto fail;
}
*_reply = reply;
reply = NULL;
return 0;
fail:
@ -1608,8 +1500,6 @@ static int bus_manager_do_shutdown_or_sleep(
static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_manager_append_handle_action, handle_action, HandleAction);
static const BusProperty bus_login_manager_properties[] = {
{ "Controllers", bus_property_append_strv, "as", offsetof(Manager, controllers), true },
{ "ResetControllers", bus_property_append_strv, "as", offsetof(Manager, reset_controllers), true },
{ "NAutoVTs", bus_property_append_unsigned, "u", offsetof(Manager, n_autovts) },
{ "KillOnlyUsers", bus_property_append_strv, "as", offsetof(Manager, kill_only_users), true },
{ "KillExcludeUsers", bus_property_append_strv, "as", offsetof(Manager, kill_exclude_users), true },
@ -2109,7 +1999,7 @@ static DBusHandlerResult manager_message_handler(
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CreateSession")) {
r = bus_manager_create_session(m, message, &reply);
r = bus_manager_create_session(m, message);
/* Don't delay the work on OOM here, since it might be
* triggered by a low RLIMIT_NOFILE here (since we
@ -2118,9 +2008,10 @@ static DBusHandlerResult manager_message_handler(
if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
} else if (dbus_message_is_method_call(message, "org.freedesktop.login1.Manager", "CreateMachine")) {
r = bus_manager_create_machine(m, message, &reply);
r = bus_manager_create_machine(m, message);
if (r < 0)
return bus_send_error_reply(connection, message, NULL, r);
@ -2753,7 +2644,7 @@ static DBusHandlerResult manager_message_handler(
if (reply) {
if (!bus_maybe_send_reply(connection, message, reply))
goto oom;
goto oom;
}
return DBUS_HANDLER_RESULT_HANDLED;
@ -2782,29 +2673,23 @@ DBusHandlerResult bus_message_filter(
dbus_error_init(&error);
if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Agent", "Released")) {
const char *cgroup;
log_debug("Got message: %s %s %s", strna(dbus_message_get_sender(message)), strna(dbus_message_get_interface(message)), strna(dbus_message_get_member(message)));
if (!dbus_message_get_args(message, &error,
DBUS_TYPE_STRING, &cgroup,
DBUS_TYPE_INVALID))
log_error("Failed to parse Released message: %s", bus_error_message(&error));
else
manager_cgroup_notify_empty(m, cgroup);
} else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
uint32_t id;
if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
const char *path, *result, *unit;
uint32_t id;
if (!dbus_message_get_args(message, &error,
DBUS_TYPE_UINT32, &id,
DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_STRING, &unit,
DBUS_TYPE_STRING, &result,
DBUS_TYPE_INVALID))
DBUS_TYPE_INVALID)) {
log_error("Failed to parse JobRemoved message: %s", bus_error_message(&error));
goto finish;
}
else if (m->action_job && streq(m->action_job, path)) {
if (m->action_job && streq(m->action_job, path)) {
log_info("Operation finished.");
/* Tell people that they now may take a lock again */
@ -2814,9 +2699,97 @@ DBusHandlerResult bus_message_filter(
m->action_job = NULL;
m->action_unit = NULL;
m->action_what = 0;
} else {
Machine *mm;
Session *s;
User *u;
s = hashmap_get(m->session_units, unit);
if (s) {
if (streq_ptr(path, s->scope_job)) {
free(s->scope_job);
s->scope_job = NULL;
if (s->started) {
if (streq(result, "done"))
session_send_create_reply(s, NULL);
else {
dbus_set_error(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
session_send_create_reply(s, &error);
}
}
}
session_add_to_gc_queue(s);
}
u = hashmap_get(m->user_units, unit);
if (u) {
if (streq_ptr(path, u->service_job)) {
free(u->service_job);
u->service_job = NULL;
}
if (streq_ptr(path, u->slice_job)) {
free(u->slice_job);
u->slice_job = NULL;
}
user_add_to_gc_queue(u);
}
mm = hashmap_get(m->machine_units, unit);
if (mm) {
if (streq_ptr(path, mm->scope_job)) {
free(mm->scope_job);
mm->scope_job = NULL;
if (mm->started) {
if (streq(result, "done"))
machine_send_create_reply(mm, NULL);
else {
dbus_set_error(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
machine_send_create_reply(mm, &error);
}
}
}
machine_add_to_gc_queue(mm);
}
}
} else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
_cleanup_free_ char *unit = NULL;
const char *path;
path = dbus_message_get_path(message);
if (!path)
goto finish;
unit_name_from_dbus_path(path, &unit);
if (unit) {
Machine *mm;
Session *s;
User *u;
s = hashmap_get(m->session_units, unit);
if (s)
session_add_to_gc_queue(s);
u = hashmap_get(m->user_units, unit);
if (u)
user_add_to_gc_queue(u);
mm = hashmap_get(m->machine_units, unit);
if (mm)
machine_add_to_gc_queue(mm);
}
}
finish:
dbus_error_free(&error);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@ -2871,3 +2844,288 @@ int manager_dispatch_delayed(Manager *manager) {
return 1;
}
int manager_start_scope(
Manager *manager,
const char *scope,
pid_t pid,
const char *slice,
const char *description,
DBusError *error,
char **job) {
_cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
DBusMessageIter iter, sub, sub2, sub3, sub4;
const char *timeout_stop_property = "TimeoutStopUSec";
const char *pids_property = "PIDs";
uint64_t timeout = 500 * USEC_PER_MSEC;
const char *fail = "fail";
uint32_t u;
assert(manager);
assert(scope);
assert(pid > 1);
if (!slice)
slice = "";
m = dbus_message_new_method_call(
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartTransientUnit");
if (!m)
return log_oom();
dbus_message_iter_init_append(m, &iter);
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
return log_oom();
if (!isempty(slice)) {
const char *slice_property = "Slice";
if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
!dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
!dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
!dbus_message_iter_close_container(&sub2, &sub3) ||
!dbus_message_iter_close_container(&sub, &sub2))
return log_oom();
}
if (!isempty(description)) {
const char *description_property = "Description";
if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
!dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
!dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
!dbus_message_iter_close_container(&sub2, &sub3) ||
!dbus_message_iter_close_container(&sub, &sub2))
return log_oom();
}
/* cgroup empty notification is not available in containers
* currently. To make this less problematic, let's shorten the
* stop timeout for sessions, so that we don't wait
* forever. */
if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
!dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
!dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
!dbus_message_iter_close_container(&sub2, &sub3) ||
!dbus_message_iter_close_container(&sub, &sub2))
return log_oom();
u = pid;
if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
!dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
!dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
!dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
!dbus_message_iter_close_container(&sub3, &sub4) ||
!dbus_message_iter_close_container(&sub2, &sub3) ||
!dbus_message_iter_close_container(&sub, &sub2) ||
!dbus_message_iter_close_container(&iter, &sub))
return log_oom();
reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
if (!reply)
return -EIO;
if (job) {
const char *j;
char *copy;
if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
return -EIO;
copy = strdup(j);
if (!copy)
return -ENOMEM;
*job = copy;
}
return 0;
}
int manager_start_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
const char *fail = "fail";
int r;
assert(manager);
assert(unit);
r = bus_method_call_with_reply(
manager->bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartUnit",
&reply,
error,
DBUS_TYPE_STRING, &unit,
DBUS_TYPE_STRING, &fail,
DBUS_TYPE_INVALID);
if (r < 0) {
log_error("Failed to start unit %s: %s", unit, bus_error(error, r));
return r;
}
if (job) {
const char *j;
char *copy;
if (!dbus_message_get_args(reply, error,
DBUS_TYPE_OBJECT_PATH, &j,
DBUS_TYPE_INVALID)) {
log_error("Failed to parse reply.");
return -EIO;
}
copy = strdup(j);
if (!copy)
return -ENOMEM;
*job = copy;
}
return 0;
}
int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
const char *fail = "fail";
int r;
assert(manager);
assert(unit);
r = bus_method_call_with_reply(
manager->bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StopUnit",
&reply,
error,
DBUS_TYPE_STRING, &unit,
DBUS_TYPE_STRING, &fail,
DBUS_TYPE_INVALID);
if (r < 0) {
log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
return r;
}
if (job) {
const char *j;
char *copy;
if (!dbus_message_get_args(reply, error,
DBUS_TYPE_OBJECT_PATH, &j,
DBUS_TYPE_INVALID)) {
log_error("Failed to parse reply.");
return -EIO;
}
copy = strdup(j);
if (!copy)
return -ENOMEM;
*job = copy;
}
return 0;
}
int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
const char *w;
int r;
assert(manager);
assert(unit);
w = who == KILL_LEADER ? "process" : "cgroup";
assert_cc(sizeof(signo) == sizeof(int32_t));
r = bus_method_call_with_reply(
manager->bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"KillUnit",
&reply,
error,
DBUS_TYPE_STRING, &unit,
DBUS_TYPE_STRING, &w,
DBUS_TYPE_INT32, &signo,
DBUS_TYPE_INVALID);
if (r < 0) {
log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
return r;
}
return 0;
}
int manager_unit_is_active(Manager *manager, const char *unit) {
const char *interface = "org.freedesktop.systemd1.Unit";
const char *property = "ActiveState";
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
_cleanup_free_ char *path = NULL;
DBusMessageIter iter, sub;
const char *state;
DBusError error;
int r;
assert(manager);
assert(unit);
dbus_error_init(&error);
path = unit_dbus_path_from_name(unit);
if (!path)
return -ENOMEM;
r = bus_method_call_with_reply(
manager->bus,
"org.freedesktop.systemd1",
path,
"org.freedesktop.DBus.Properties",
"Get",
&reply,
&error,
DBUS_TYPE_STRING, &interface,
DBUS_TYPE_STRING, &property,
DBUS_TYPE_INVALID);
if (r < 0) {
log_error("Failed to query ActiveState: %s", bus_error(&error, r));
dbus_error_free(&error);
return r;
}
if (!dbus_message_iter_init(reply, &iter) ||
dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
log_error("Failed to parse reply.");
return -EINVAL;
}
dbus_message_iter_recurse(&iter, &sub);
if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
log_error("Failed to parse reply.");
return -EINVAL;
}
dbus_message_iter_get_basic(&sub, &state);
return !streq(state, "inactive") && !streq(state, "failed");
}

View File

@ -19,8 +19,6 @@ Login.ReserveVT, config_parse_unsigned, 0, offsetof(Manag
Login.KillUserProcesses, config_parse_bool, 0, offsetof(Manager, kill_user_processes)
Login.KillOnlyUsers, config_parse_strv, 0, offsetof(Manager, kill_only_users)
Login.KillExcludeUsers, config_parse_strv, 0, offsetof(Manager, kill_exclude_users)
Login.Controllers, config_parse_strv, 0, offsetof(Manager, controllers)
Login.ResetControllers, config_parse_strv, 0, offsetof(Manager, reset_controllers)
Login.InhibitDelayMaxSec, config_parse_sec, 0, offsetof(Manager, inhibit_delay_max)
Login.HandlePowerKey, config_parse_handle_action, 0, offsetof(Manager, handle_power_key)
Login.HandleSuspendKey, config_parse_handle_action, 0, offsetof(Manager, handle_suspend_key)

View File

@ -37,11 +37,11 @@
" <property name=\"Id\" type=\"ay\" access=\"read\"/>\n" \
" <property name=\"Timestamp\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"TimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Service\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Slice\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Scope\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Leader\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"Class\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"State\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"RootDirectory\" type=\"s\" access=\"read\"/>\n" \
" </interface>\n"
@ -58,24 +58,6 @@
BUS_GENERIC_INTERFACES_LIST \
"org.freedesktop.login1.Machine\0"
static int bus_machine_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
_cleanup_free_ char *t = NULL;
Machine *m = data;
int r;
bool success;
assert(i);
assert(property);
assert(m);
r = cg_join_spec(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, &t);
if (r < 0)
return r;
success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
return success ? 0 : -ENOMEM;
}
static int bus_machine_append_id(DBusMessageIter *i, const char *property, void *data) {
DBusMessageIter sub;
Machine *m = data;
@ -100,6 +82,22 @@ static int bus_machine_append_id(DBusMessageIter *i, const char *property, void
return 0;
}
static int bus_machine_append_state(DBusMessageIter *i, const char *property, void *data) {
Machine *m = data;
const char *state;
assert(i);
assert(property);
assert(m);
state = machine_state_to_string(machine_get_state(m));
if (!dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &state))
return -ENOMEM;
return 0;
}
static int get_machine_for_path(Manager *m, const char *path, Machine **_machine) {
_cleanup_free_ char *e = NULL;
Machine *machine;
@ -130,11 +128,11 @@ static const BusProperty bus_login_machine_properties[] = {
{ "Id", bus_machine_append_id, "ay", 0 },
{ "Timestamp", bus_property_append_usec, "t", offsetof(Machine, timestamp.realtime) },
{ "TimestampMonotonic", bus_property_append_usec, "t", offsetof(Machine, timestamp.monotonic) },
{ "DefaultControlGroup", bus_machine_append_default_cgroup, "s", 0 },
{ "Service", bus_property_append_string, "s", offsetof(Machine, service), true },
{ "Slice", bus_property_append_string, "s", offsetof(Machine, slice), true },
{ "Scope", bus_property_append_string, "s", offsetof(Machine, scope), true },
{ "Leader", bus_property_append_pid, "u", offsetof(Session, leader) },
{ "Class", bus_machine_append_class, "s", offsetof(Machine, class) },
{ "State", bus_machine_append_state, "s", 0 },
{ "RootDirectory", bus_property_append_string, "s", offsetof(Machine, root_directory), true },
{ NULL, }
};
@ -313,3 +311,50 @@ int machine_send_changed(Machine *m, const char *properties) {
return 0;
}
int machine_send_create_reply(Machine *m, DBusError *error) {
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
assert(m);
if (!m->create_message)
return 0;
if (error) {
DBusError buffer;
dbus_error_init(&buffer);
if (!error || !dbus_error_is_set(error)) {
dbus_set_error_const(&buffer, DBUS_ERROR_INVALID_ARGS, "Invalid Arguments");
error = &buffer;
}
reply = dbus_message_new_error(m->create_message, error->name, error->message);
dbus_error_free(&buffer);
if (!reply)
return log_oom();
} else {
_cleanup_free_ char *p = NULL;
p = machine_bus_path(m);
if (!p)
return log_oom();
reply = dbus_message_new_method_return(m->create_message);
if (!reply)
return log_oom();
if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &p, DBUS_TYPE_INVALID))
return log_oom();
}
if (!dbus_connection_send(m->manager->bus, reply, NULL))
return log_oom();
dbus_message_unref(m->create_message);
m->create_message = NULL;
return 0;
}

View File

@ -23,7 +23,8 @@
#include <unistd.h>
#include <errno.h>
#include "logind-machine.h"
#include <systemd/sd-messages.h>
#include "util.h"
#include "mkdir.h"
#include "cgroup-util.h"
@ -31,7 +32,9 @@
#include "strv.h"
#include "fileio.h"
#include "special.h"
#include <systemd/sd-messages.h>
#include "unit-name.h"
#include "dbus-common.h"
#include "logind-machine.h"
Machine* machine_new(Manager *manager, const char *name) {
Machine *m;
@ -73,17 +76,21 @@ void machine_free(Machine *m) {
if (m->in_gc_queue)
LIST_REMOVE(Machine, gc_queue, m->manager->machine_gc_queue, m);
if (m->cgroup_path) {
hashmap_remove(m->manager->machine_cgroups, m->cgroup_path);
free(m->cgroup_path);
if (m->scope) {
hashmap_remove(m->manager->machine_units, m->scope);
free(m->scope);
}
free(m->scope_job);
hashmap_remove(m->manager->machines, m->name);
if (m->create_message)
dbus_message_unref(m->create_message);
free(m->name);
free(m->state_file);
free(m->service);
free(m->slice);
free(m->root_directory);
free(m);
}
@ -114,15 +121,15 @@ int machine_save(Machine *m) {
"NAME=%s\n",
m->name);
if (m->cgroup_path)
fprintf(f, "CGROUP=%s\n", m->cgroup_path);
if (m->scope)
fprintf(f, "SCOPE=%s\n", m->scope);
if (m->scope_job)
fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
if (m->service)
fprintf(f, "SERVICE=%s\n", m->service);
if (m->slice)
fprintf(f, "SLICE=%s\n", m->slice);
if (m->root_directory)
fprintf(f, "ROOT=%s\n", m->root_directory);
@ -164,9 +171,9 @@ int machine_load(Machine *m) {
assert(m);
r = parse_env_file(m->state_file, NEWLINE,
"CGROUP", &m->cgroup_path,
"SCOPE", &m->scope,
"SCOPE_JOB", &m->scope_job,
"SERVICE", &m->service,
"SLICE", &m->slice,
"ROOT", &m->root_directory,
"ID", &id,
"LEADER", &leader,
@ -211,86 +218,44 @@ int machine_load(Machine *m) {
return r;
}
static int machine_create_one_group(Machine *m, const char *controller, const char *path) {
int r;
assert(m);
assert(path);
if (m->leader > 0)
r = cg_create_and_attach(controller, path, m->leader);
else
r = -EINVAL;
if (r < 0) {
r = cg_create(controller, path);
if (r < 0)
return r;
}
return 0;
}
static int machine_create_cgroup(Machine *m) {
char **k;
static int machine_start_scope(Machine *m) {
_cleanup_free_ char *description = NULL;
DBusError error;
char *job;
int r;
assert(m);
if (!m->slice) {
m->slice = strdup(SPECIAL_MACHINE_SLICE);
if (!m->slice)
return log_oom();
}
dbus_error_init(&error);
if (!m->cgroup_path) {
_cleanup_free_ char *escaped = NULL, *slice = NULL;
char *name;
if (!m->scope) {
_cleanup_free_ char *escaped = NULL;
name = strappenda(m->name, ".machine");
escaped = cg_escape(name);
escaped = unit_name_escape(m->name);
if (!escaped)
return log_oom();
r = cg_slice_to_path(m->slice, &slice);
if (r < 0)
return r;
m->cgroup_path = strjoin(m->manager->cgroup_root, "/", slice, "/", escaped, NULL);
if (!m->cgroup_path)
m->scope = strjoin("machine.", m->name, ".scope", NULL);
if (!m->scope)
return log_oom();
}
r = machine_create_one_group(m, SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path);
if (r < 0) {
log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", m->cgroup_path, strerror(-r));
return r;
}
STRV_FOREACH(k, m->manager->controllers) {
if (strv_contains(m->manager->reset_controllers, *k))
continue;
r = machine_create_one_group(m, *k, m->cgroup_path);
r = hashmap_put(m->manager->machine_units, m->scope, m);
if (r < 0)
log_warning("Failed to create cgroup %s:%s: %s", *k, m->cgroup_path, strerror(-r));
log_warning("Failed to create mapping between unit and machine");
}
if (m->leader > 0) {
STRV_FOREACH(k, m->manager->reset_controllers) {
r = cg_attach(*k, "/", m->leader);
if (r < 0)
log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
}
description = strappend(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
r = manager_start_scope(m->manager, m->scope, m->leader, SPECIAL_MACHINE_SLICE, description, &error, &job);
if (r < 0) {
log_error("Failed to start machine scope: %s", bus_error(&error, r));
dbus_error_free(&error);
}
r = hashmap_put(m->manager->machine_cgroups, m->cgroup_path, m);
if (r < 0)
log_warning("Failed to create mapping between cgroup and machine");
free(m->scope_job);
m->scope_job = job;
return 0;
return r;
}
int machine_start(Machine *m) {
@ -301,6 +266,11 @@ int machine_start(Machine *m) {
if (m->started)
return 0;
/* Create cgroup */
r = machine_start_scope(m);
if (r < 0)
return r;
log_struct(LOG_INFO,
MESSAGE_ID(SD_MESSAGE_MACHINE_START),
"NAME=%s", m->name,
@ -308,11 +278,6 @@ int machine_start(Machine *m) {
"MESSAGE=New machine %s.", m->name,
NULL);
/* Create cgroup */
r = machine_create_cgroup(m);
if (r < 0)
return r;
if (!dual_timestamp_is_set(&m->timestamp))
dual_timestamp_get(&m->timestamp);
@ -326,28 +291,27 @@ int machine_start(Machine *m) {
return 0;
}
static int machine_terminate_cgroup(Machine *m) {
static int machine_stop_scope(Machine *m) {
DBusError error;
char *job;
int r;
char **k;
assert(m);
if (!m->cgroup_path)
dbus_error_init(&error);
if (!m->scope)
return 0;
cg_trim(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, false);
r = manager_stop_unit(m->manager, m->scope, &error, &job);
if (r < 0) {
log_error("Failed to stop machine scope: %s", bus_error(&error, r));
dbus_error_free(&error);
return r;
}
r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, true);
if (r < 0)
log_error("Failed to kill machine cgroup: %s", strerror(-r));
STRV_FOREACH(k, m->manager->controllers)
cg_trim(*k, m->cgroup_path, true);
hashmap_remove(m->manager->machine_cgroups, m->cgroup_path);
free(m->cgroup_path);
m->cgroup_path = NULL;
free(m->scope_job);
m->scope_job = job;
return r;
}
@ -365,7 +329,7 @@ int machine_stop(Machine *m) {
NULL);
/* Kill cgroup */
k = machine_terminate_cgroup(m);
k = machine_stop_scope(m);
if (k < 0)
r = k;
@ -381,21 +345,16 @@ int machine_stop(Machine *m) {
}
int machine_check_gc(Machine *m, bool drop_not_started) {
int r;
assert(m);
if (drop_not_started && !m->started)
return 0;
if (m->cgroup_path) {
r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, false);
if (r < 0)
return r;
if (m->scope_job)
return 1;
if (r <= 0)
return 1;
}
if (m->scope)
return manager_unit_is_active(m->manager, m->scope) != 0;
return 0;
}
@ -410,41 +369,22 @@ void machine_add_to_gc_queue(Machine *m) {
m->in_gc_queue = true;
}
int machine_kill(Machine *m, KillWho who, int signo) {
_cleanup_set_free_ Set *pid_set = NULL;
int r = 0;
MachineState machine_get_state(Machine *s) {
assert(s);
if (s->scope_job)
return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
return MACHINE_RUNNING;
}
int machine_kill(Machine *m, KillWho who, int signo) {
assert(m);
if (!m->cgroup_path)
if (!m->scope)
return -ESRCH;
if (m->leader <= 0 && who == KILL_LEADER)
return -ESRCH;
if (m->leader > 0)
if (kill(m->leader, signo) < 0)
r = -errno;
if (who == KILL_ALL) {
int q;
pid_set = set_new(trivial_hash_func, trivial_compare_func);
if (!pid_set)
return log_oom();
if (m->leader > 0) {
q = set_put(pid_set, LONG_TO_PTR(m->leader));
if (q < 0)
r = q;
}
q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, signo, false, true, false, pid_set);
if (q < 0 && (q != -EAGAIN && q != -ESRCH && q != -ENOENT))
r = q;
}
return r;
return manager_kill_unit(m->manager, m->scope, who, signo, NULL);
}
static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
@ -453,3 +393,11 @@ static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
};
DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
[MACHINE_OPENING] = "opening",
[MACHINE_RUNNING] = "running",
[MACHINE_CLOSING] = "closing"
};
DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);

View File

@ -28,6 +28,14 @@ typedef struct Machine Machine;
#include "logind.h"
#include "logind-session.h"
typedef enum MachineState {
MACHINE_OPENING, /* Machine is being registered */
MACHINE_RUNNING, /* Machine is running */
MACHINE_CLOSING, /* Machine is terminating */
_MACHINE_STATE_MAX,
_MACHINE_STATE_INVALID = -1
} MachineState;
typedef enum MachineClass {
MACHINE_CONTAINER,
MACHINE_VM,
@ -41,14 +49,16 @@ struct Machine {
char *name;
sd_id128_t id;
MachineState state;
MachineClass class;
char *state_file;
char *service;
char *cgroup_path;
char *slice;
char *root_directory;
char *scope;
char *scope_job;
pid_t leader;
dual_timestamp timestamp;
@ -56,6 +66,8 @@ struct Machine {
bool in_gc_queue:1;
bool started:1;
DBusMessage *create_message;
LIST_FIELDS(Machine, gc_queue);
};
@ -71,10 +83,17 @@ int machine_kill(Machine *m, KillWho who, int signo);
char *machine_bus_path(Machine *s);
MachineState machine_get_state(Machine *u);
extern const DBusObjectPathVTable bus_machine_vtable;
int machine_send_signal(Machine *m, bool new_machine);
int machine_send_changed(Machine *m, const char *properties);
int machine_send_create_reply(Machine *m, DBusError *error);
const char* machine_class_to_string(MachineClass t) _const_;
MachineClass machine_class_from_string(const char *s) _pure_;
const char* machine_state_to_string(MachineState t) _const_;
MachineState machine_state_from_string(const char *s) _pure_;

View File

@ -47,7 +47,6 @@
" <property name=\"Name\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Timestamp\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"TimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"VTNr\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"Seat\" type=\"(so)\" access=\"read\"/>\n" \
" <property name=\"TTY\" type=\"s\" access=\"read\"/>\n" \
@ -56,15 +55,13 @@
" <property name=\"RemoteHost\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"RemoteUser\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Service\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Slice\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Scope\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Leader\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"Audit\" type=\"u\" access=\"read\"/>\n" \
" <property name=\"Type\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Class\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Active\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"State\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Controllers\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"ResetControllers\" type=\"as\" access=\"read\"/>\n" \
" <property name=\"KillProcesses\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"IdleHint\" type=\"b\" access=\"read\"/>\n" \
" <property name=\"IdleSinceHint\" type=\"t\" access=\"read\"/>\n" \
@ -196,24 +193,6 @@ static int bus_session_append_idle_hint_since(DBusMessageIter *i, const char *pr
return 0;
}
static int bus_session_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
Session *s = data;
_cleanup_free_ char *t = NULL;
int r;
bool success;
assert(i);
assert(property);
assert(s);
r = cg_join_spec(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, &t);
if (r < 0)
return r;
success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
return success ? 0 : -ENOMEM;
}
static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_type, session_type, SessionType);
static DEFINE_BUS_PROPERTY_APPEND_ENUM(bus_session_append_class, session_class, SessionClass);
@ -260,7 +239,6 @@ static const BusProperty bus_login_session_properties[] = {
{ "Id", bus_property_append_string, "s", offsetof(Session, id), true },
{ "Timestamp", bus_property_append_usec, "t", offsetof(Session, timestamp.realtime) },
{ "TimestampMonotonic", bus_property_append_usec, "t", offsetof(Session, timestamp.monotonic) },
{ "DefaultControlGroup", bus_session_append_default_cgroup, "s", 0, },
{ "VTNr", bus_property_append_uint32, "u", offsetof(Session, vtnr) },
{ "Seat", bus_session_append_seat, "(so)", 0 },
{ "TTY", bus_property_append_string, "s", offsetof(Session, tty), true },
@ -269,16 +247,13 @@ static const BusProperty bus_login_session_properties[] = {
{ "RemoteUser", bus_property_append_string, "s", offsetof(Session, remote_user), true },
{ "RemoteHost", bus_property_append_string, "s", offsetof(Session, remote_host), true },
{ "Service", bus_property_append_string, "s", offsetof(Session, service), true },
{ "Slice", bus_property_append_string, "s", offsetof(Session, slice), true },
{ "Scope", bus_property_append_string, "s", offsetof(Session, scope), true },
{ "Leader", bus_property_append_pid, "u", offsetof(Session, leader) },
{ "Audit", bus_property_append_uint32, "u", offsetof(Session, audit_id) },
{ "Type", bus_session_append_type, "s", offsetof(Session, type) },
{ "Class", bus_session_append_class, "s", offsetof(Session, class) },
{ "Active", bus_session_append_active, "b", 0 },
{ "State", bus_session_append_state, "s", 0 },
{ "Controllers", bus_property_append_strv, "as", offsetof(Session, controllers), true },
{ "ResetControllers", bus_property_append_strv, "as", offsetof(Session, reset_controllers), true },
{ "KillProcesses", bus_property_append_bool, "b", offsetof(Session, kill_processes) },
{ "IdleHint", bus_session_append_idle_hint, "b", 0 },
{ "IdleSinceHint", bus_session_append_idle_hint_since, "t", 0 },
{ "IdleSinceHintMonotonic", bus_session_append_idle_hint_since, "t", 0 },
@ -552,3 +527,73 @@ int session_send_lock_all(Manager *m, bool lock) {
return r;
}
int session_send_create_reply(Session *s, DBusError *error) {
_cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
assert(s);
if (!s->create_message)
return 0;
if (error) {
DBusError buffer;
dbus_error_init(&buffer);
if (!dbus_error_is_set(error)) {
dbus_set_error_const(&buffer, DBUS_ERROR_INVALID_ARGS, "Invalid Arguments");
error = &buffer;
}
reply = dbus_message_new_error(s->create_message, error->name, error->message);
dbus_error_free(&buffer);
if (!reply)
return log_oom();
} else {
_cleanup_close_ int fifo_fd = -1;
_cleanup_free_ char *path = NULL;
const char *cseat;
uint32_t vtnr;
dbus_bool_t exists;
fifo_fd = session_create_fifo(s);
if (fifo_fd < 0) {
log_error("Failed to create fifo: %s", strerror(-fifo_fd));
return fifo_fd;
}
path = session_bus_path(s);
if (!path)
return log_oom();
reply = dbus_message_new_method_return(s->create_message);
if (!reply)
return log_oom();
cseat = s->seat ? s->seat->id : "";
vtnr = s->vtnr;
exists = false;
if (!dbus_message_append_args(
reply,
DBUS_TYPE_STRING, &s->id,
DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_STRING, &s->user->runtime_path,
DBUS_TYPE_UNIX_FD, &fifo_fd,
DBUS_TYPE_STRING, &cseat,
DBUS_TYPE_UINT32, &vtnr,
DBUS_TYPE_BOOLEAN, &exists,
DBUS_TYPE_INVALID))
return log_oom();
}
if (!dbus_connection_send(s->manager->bus, reply, NULL))
return log_oom();
dbus_message_unref(s->create_message);
s->create_message = NULL;
return 0;
}

View File

@ -25,15 +25,17 @@
#include <sys/epoll.h>
#include <fcntl.h>
#include "systemd/sd-id128.h"
#include "systemd/sd-messages.h"
#include <systemd/sd-id128.h>
#include <systemd/sd-messages.h>
#include "strv.h"
#include "util.h"
#include "mkdir.h"
#include "path-util.h"
#include "cgroup-util.h"
#include "logind-session.h"
#include "fileio.h"
#include "dbus-common.h"
#include "logind-session.h"
Session* session_new(Manager *m, const char *id) {
Session *s;
@ -85,18 +87,21 @@ void session_free(Session *s) {
LIST_REMOVE(Session, sessions_by_seat, s->seat->sessions, s);
}
if (s->cgroup_path)
hashmap_remove(s->manager->session_cgroups, s->cgroup_path);
if (s->scope) {
hashmap_remove(s->manager->session_units, s->scope);
free(s->scope);
}
free(s->cgroup_path);
strv_free(s->controllers);
free(s->scope_job);
if (s->create_message)
dbus_message_unref(s->create_message);
free(s->tty);
free(s->display);
free(s->remote_host);
free(s->remote_user);
free(s->service);
free(s->slice);
hashmap_remove(s->manager->sessions, s->id);
session_remove_fifo(s);
@ -144,14 +149,12 @@ int session_save(Session *s) {
"USER=%s\n"
"ACTIVE=%i\n"
"STATE=%s\n"
"REMOTE=%i\n"
"KILL_PROCESSES=%i\n",
"REMOTE=%i\n",
(unsigned long) s->user->uid,
s->user->name,
session_is_active(s),
session_state_to_string(session_get_state(s)),
s->remote,
s->kill_processes);
s->remote);
if (s->type >= 0)
fprintf(f, "TYPE=%s\n", session_type_to_string(s->type));
@ -159,8 +162,11 @@ int session_save(Session *s) {
if (s->class >= 0)
fprintf(f, "CLASS=%s\n", session_class_to_string(s->class));
if (s->cgroup_path)
fprintf(f, "CGROUP=%s\n", s->cgroup_path);
if (s->scope)
fprintf(f, "SCOPE=%s\n", s->scope);
if (s->scope_job)
fprintf(f, "SCOPE_JOB=%s\n", s->scope_job);
if (s->fifo_path)
fprintf(f, "FIFO=%s\n", s->fifo_path);
@ -183,9 +189,6 @@ int session_save(Session *s) {
if (s->service)
fprintf(f, "SERVICE=%s\n", s->service);
if (s->seat)
fprintf(f, "SLICE=%s\n", s->slice);
if (s->seat && seat_can_multi_session(s->seat))
fprintf(f, "VTNR=%i\n", s->vtnr);
@ -219,7 +222,6 @@ finish:
int session_load(Session *s) {
_cleanup_free_ char *remote = NULL,
*kill_processes = NULL,
*seat = NULL,
*vtnr = NULL,
*leader = NULL,
@ -236,8 +238,8 @@ int session_load(Session *s) {
r = parse_env_file(s->state_file, NEWLINE,
"REMOTE", &remote,
"KILL_PROCESSES", &kill_processes,
"CGROUP", &s->cgroup_path,
"SCOPE", &s->scope,
"SCOPE_JOB", &s->scope_job,
"FIFO", &s->fifo_path,
"SEAT", &seat,
"TTY", &s->tty,
@ -245,7 +247,6 @@ int session_load(Session *s) {
"REMOTE_HOST", &s->remote_host,
"REMOTE_USER", &s->remote_user,
"SERVICE", &s->service,
"SLICE", &s->slice,
"VTNR", &vtnr,
"LEADER", &leader,
"TYPE", &type,
@ -290,12 +291,6 @@ int session_load(Session *s) {
s->remote = k;
}
if (kill_processes) {
k = parse_boolean(kill_processes);
if (k >= 0)
s->kill_processes = k;
}
if (seat && !s->seat) {
Seat *o;
@ -459,124 +454,39 @@ done:
return 0;
}
static int session_create_one_group(Session *s, const char *controller, const char *path) {
static int session_start_scope(Session *s) {
_cleanup_free_ char *description = NULL;
DBusError error;
char *job;
int r;
assert(s);
assert(s->user);
assert(path);
assert(s->user->slice);
if (s->leader > 0)
r = cg_create_and_attach(controller, path, s->leader);
else
r = -EINVAL;
dbus_error_init(&error);
if (!s->scope) {
s->scope = strjoin("session.", s->id, ".scope", NULL);
if (!s->scope)
return log_oom();
r = hashmap_put(s->manager->session_units, s->scope, s);
if (r < 0)
log_warning("Failed to create mapping between unit and session");
}
description = strjoin("Session ", s->id, " of user ", s->user->name, NULL);
r = manager_start_scope(s->manager, s->scope, s->leader, s->user->slice, description, &error, &job);
if (r < 0) {
r = cg_create(controller, path);
if (r < 0)
return r;
log_error("Failed to start session scope: %s %s", bus_error(&error, r), error.name);
dbus_error_free(&error);
} else {
free(s->scope_job);
s->scope_job = job;
}
r = cg_set_task_access(controller, path, 0644, s->user->uid, s->user->gid);
if (r >= 0)
r = cg_set_group_access(controller, path, 0755, s->user->uid, s->user->gid);
return r;
}
static int session_create_cgroup(Session *s) {
char **k;
int r;
assert(s);
assert(s->user);
assert(s->user->cgroup_path);
if (!s->cgroup_path) {
_cleanup_free_ char *name = NULL, *escaped = NULL;
name = strappend(s->id, ".session");
if (!name)
return log_oom();
escaped = cg_escape(name);
if (!escaped)
return log_oom();
if (s->slice) {
_cleanup_free_ char *slice = NULL;
r = cg_slice_to_path(s->slice, &slice);
if (r < 0)
return r;
s->cgroup_path = strjoin(s->manager->cgroup_root, "/", slice, "/", escaped, NULL);
} else
s->cgroup_path = strjoin(s->user->cgroup_path, "/", escaped, NULL);
if (!s->cgroup_path)
return log_oom();
}
if (!s->slice) {
s->slice = strdup(s->user->slice);
if (!s->slice)
return log_oom();
}
r = session_create_one_group(s, SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path);
if (r < 0) {
log_error("Failed to create "SYSTEMD_CGROUP_CONTROLLER":%s: %s", s->cgroup_path, strerror(-r));
return r;
}
STRV_FOREACH(k, s->controllers) {
if (strv_contains(s->reset_controllers, *k))
continue;
r = session_create_one_group(s, *k, s->cgroup_path);
if (r < 0)
log_warning("Failed to create %s:%s: %s", *k, s->cgroup_path, strerror(-r));
}
STRV_FOREACH(k, s->manager->controllers) {
if (strv_contains(s->reset_controllers, *k) ||
strv_contains(s->manager->reset_controllers, *k) ||
strv_contains(s->controllers, *k))
continue;
r = session_create_one_group(s, *k, s->cgroup_path);
if (r < 0)
log_warning("Failed to create %s:%s: %s", *k, s->cgroup_path, strerror(-r));
}
if (s->leader > 0) {
STRV_FOREACH(k, s->reset_controllers) {
r = cg_attach(*k, "/", s->leader);
if (r < 0)
log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
}
STRV_FOREACH(k, s->manager->reset_controllers) {
if (strv_contains(s->reset_controllers, *k) ||
strv_contains(s->controllers, *k))
continue;
r = cg_attach(*k, "/", s->leader);
if (r < 0)
log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
}
}
r = hashmap_put(s->manager->session_cgroups, s->cgroup_path, s);
if (r < 0)
log_warning("Failed to create mapping between cgroup and session");
return 0;
}
@ -595,6 +505,11 @@ int session_start(Session *s) {
if (r < 0)
return r;
/* Create cgroup */
r = session_start_scope(s);
if (r < 0)
return r;
log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
MESSAGE_ID(SD_MESSAGE_SESSION_START),
"SESSION_ID=%s", s->id,
@ -603,11 +518,6 @@ int session_start(Session *s) {
"MESSAGE=New session %s of user %s.", s->id, s->user->name,
NULL);
/* Create cgroup */
r = session_create_cgroup(s);
if (r < 0)
return r;
/* Create X11 symlink */
session_link_x11_socket(s);
@ -639,73 +549,42 @@ int session_start(Session *s) {
return 0;
}
static bool session_shall_kill(Session *s) {
assert(s);
/* static bool session_shall_kill(Session *s) { */
/* assert(s); */
if (!s->kill_processes)
return false;
/* if (!s->kill_processes) */
/* return false; */
if (strv_contains(s->manager->kill_exclude_users, s->user->name))
return false;
/* if (strv_contains(s->manager->kill_exclude_users, s->user->name)) */
/* return false; */
if (strv_isempty(s->manager->kill_only_users))
return true;
/* if (strv_isempty(s->manager->kill_only_users)) */
/* return true; */
return strv_contains(s->manager->kill_only_users, s->user->name);
}
/* return strv_contains(s->manager->kill_only_users, s->user->name); */
/* } */
static int session_terminate_cgroup(Session *s) {
static int session_stop_scope(Session *s) {
DBusError error;
char *job;
int r;
char **k;
assert(s);
if (!s->cgroup_path)
dbus_error_init(&error);
if (!s->scope)
return 0;
cg_trim(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
if (session_shall_kill(s)) {
r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
if (r < 0)
log_error("Failed to kill session cgroup: %s", strerror(-r));
} else {
if (s->leader > 0) {
Session *t;
/* We still send a HUP to the leader process,
* even if we are not supposed to kill the
* whole cgroup. But let's first check the
* leader still exists and belongs to our
* session... */
r = manager_get_session_by_pid(s->manager, s->leader, &t);
if (r > 0 && t == s) {
kill(s->leader, SIGTERM); /* for normal processes */
kill(s->leader, SIGHUP); /* for shells */
kill(s->leader, SIGCONT); /* in case they are stopped */
}
}
r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
if (r < 0)
log_error("Failed to check session cgroup: %s", strerror(-r));
else if (r > 0) {
r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path);
if (r < 0)
log_error("Failed to delete session cgroup: %s", strerror(-r));
}
r = manager_stop_unit(s->manager, s->scope, &error, &job);
if (r < 0) {
log_error("Failed to stop session scope: %s", bus_error(&error, r));
dbus_error_free(&error);
return r;
}
STRV_FOREACH(k, s->user->manager->controllers)
cg_trim(*k, s->cgroup_path, true);
hashmap_remove(s->manager->session_cgroups, s->cgroup_path);
free(s->cgroup_path);
s->cgroup_path = NULL;
free(s->scope_job);
s->scope_job = job;
return 0;
}
@ -750,7 +629,7 @@ int session_stop(Session *s) {
NULL);
/* Kill cgroup */
k = session_terminate_cgroup(s);
k = session_stop_scope(s);
if (k < 0)
r = k;
@ -861,28 +740,6 @@ int session_get_idle_hint(Session *s, dual_timestamp *t) {
goto found_atime;
}
/* For other TTY sessions, let's find the most recent atime of
* the ttys of any of the processes of the session */
if (s->cgroup_path) {
_cleanup_fclose_ FILE *f = NULL;
if (cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, &f) >= 0) {
pid_t pid;
atime = 0;
while (cg_read_pid(f, &pid) > 0) {
usec_t a;
if (get_process_ctty_atime(pid, &a) >= 0)
if (atime == 0 || atime < a)
atime = a;
}
if (atime != 0)
goto found_atime;
}
}
dont_know:
if (t)
*t = s->idle_hint_timestamp;
@ -1018,15 +875,11 @@ int session_check_gc(Session *s, bool drop_not_started) {
return 1;
}
if (s->cgroup_path) {
if (s->scope_job)
return 1;
r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
if (r < 0)
return r;
if (r <= 0)
return 1;
}
if (s->scope)
return manager_unit_is_active(s->manager, s->scope) != 0;
return 0;
}
@ -1044,6 +897,9 @@ void session_add_to_gc_queue(Session *s) {
SessionState session_get_state(Session *s) {
assert(s);
if (s->scope_job)
return s->started ? SESSION_OPENING : SESSION_CLOSING;
if (s->fifo_fd < 0)
return SESSION_CLOSING;
@ -1054,44 +910,16 @@ SessionState session_get_state(Session *s) {
}
int session_kill(Session *s, KillWho who, int signo) {
_cleanup_set_free_ Set *pid_set = NULL;
int r = 0;
assert(s);
if (!s->cgroup_path)
if (!s->scope)
return -ESRCH;
if (s->leader <= 0 && who == KILL_LEADER)
return -ESRCH;
if (s->leader > 0)
if (kill(s->leader, signo) < 0)
r = -errno;
if (who == KILL_ALL) {
int q;
pid_set = set_new(trivial_hash_func, trivial_compare_func);
if (!pid_set)
return log_oom();
if (s->leader > 0) {
q = set_put(pid_set, LONG_TO_PTR(s->leader));
if (q < 0)
r = q;
}
q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, signo, false, true, false, pid_set);
if (q < 0)
if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
r = q;
}
return r;
return manager_kill_unit(s->manager, s->scope, who, signo, NULL);
}
static const char* const session_state_table[_SESSION_TYPE_MAX] = {
static const char* const session_state_table[_SESSION_STATE_MAX] = {
[SESSION_OPENING] = "opening",
[SESSION_ONLINE] = "online",
[SESSION_ACTIVE] = "active",
[SESSION_CLOSING] = "closing"

View File

@ -31,9 +31,10 @@ typedef enum KillWho KillWho;
#include "logind-user.h"
typedef enum SessionState {
SESSION_OPENING, /* Session scope is being created */
SESSION_ONLINE, /* Logged in */
SESSION_ACTIVE, /* Logged in and in the fg */
SESSION_CLOSING, /* Logged out, but processes still remain */
SESSION_CLOSING, /* Logged out, but scope is still there */
_SESSION_STATE_MAX,
_SESSION_STATE_INVALID = -1
} SessionState;
@ -81,9 +82,10 @@ struct Session {
bool remote;
char *remote_user;
char *remote_host;
char *service;
char *slice;
char *scope;
char *scope_job;
int vtnr;
Seat *seat;
@ -94,16 +96,14 @@ struct Session {
int fifo_fd;
char *fifo_path;
char *cgroup_path;
char **controllers, **reset_controllers;
bool idle_hint;
dual_timestamp idle_hint_timestamp;
bool kill_processes;
bool in_gc_queue:1;
bool started:1;
DBusMessage *create_message;
LIST_FIELDS(Session, sessions_by_user);
LIST_FIELDS(Session, sessions_by_seat);
@ -138,6 +138,8 @@ int session_send_changed(Session *s, const char *properties);
int session_send_lock(Session *s, bool lock);
int session_send_lock_all(Manager *m, bool lock);
int session_send_create_reply(Session *s, DBusError *error);
const char* session_state_to_string(SessionState t) _const_;
SessionState session_state_from_string(const char *s) _pure_;

View File

@ -38,7 +38,6 @@
" <property name=\"Timestamp\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"TimestampMonotonic\" type=\"t\" access=\"read\"/>\n" \
" <property name=\"RuntimePath\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"DefaultControlGroup\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Service\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Slice\" type=\"s\" access=\"read\"/>\n" \
" <property name=\"Display\" type=\"(so)\" access=\"read\"/>\n" \
@ -186,24 +185,6 @@ static int bus_user_append_idle_hint_since(DBusMessageIter *i, const char *prope
return 0;
}
static int bus_user_append_default_cgroup(DBusMessageIter *i, const char *property, void *data) {
User *u = data;
_cleanup_free_ char *t = NULL;
int r;
bool success;
assert(i);
assert(property);
assert(u);
r = cg_join_spec(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &t);
if (r < 0)
return r;
success = dbus_message_iter_append_basic(i, DBUS_TYPE_STRING, &t);
return success ? 0 : -ENOMEM;
}
static int get_user_for_path(Manager *m, const char *path, User **_u) {
User *u;
unsigned long lu;
@ -235,7 +216,6 @@ static const BusProperty bus_login_user_properties[] = {
{ "Timestamp", bus_property_append_usec, "t", offsetof(User, timestamp.realtime) },
{ "TimestampMonotonic", bus_property_append_usec, "t", offsetof(User, timestamp.monotonic) },
{ "RuntimePath", bus_property_append_string, "s", offsetof(User, runtime_path), true },
{ "DefaultControlGroup", bus_user_append_default_cgroup, "s", 0 },
{ "Service", bus_property_append_string, "s", offsetof(User, service), true },
{ "Slice", bus_property_append_string, "s", offsetof(User, slice), true },
{ "Display", bus_user_append_display, "(so)", 0 },

View File

@ -23,7 +23,6 @@
#include <unistd.h>
#include <errno.h>
#include "logind-user.h"
#include "util.h"
#include "mkdir.h"
#include "cgroup-util.h"
@ -31,6 +30,9 @@
#include "strv.h"
#include "fileio.h"
#include "special.h"
#include "unit-name.h"
#include "dbus-common.h"
#include "logind-user.h"
User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
User *u;
@ -75,19 +77,25 @@ void user_free(User *u) {
while (u->sessions)
session_free(u->sessions);
if (u->cgroup_path) {
hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
free(u->cgroup_path);
if (u->slice) {
hashmap_remove(u->manager->user_units, u->slice);
free(u->slice);
}
free(u->service);
if (u->service) {
hashmap_remove(u->manager->user_units, u->service);
free(u->service);
}
free(u->slice_job);
free(u->service_job);
free(u->runtime_path);
hashmap_remove(u->manager->users, ULONG_TO_PTR((unsigned long) u->uid));
free(u->name);
free(u->state_file);
free(u->slice);
free(u);
}
@ -119,17 +127,18 @@ int user_save(User *u) {
u->name,
user_state_to_string(user_get_state(u)));
if (u->cgroup_path)
fprintf(f, "CGROUP=%s\n", u->cgroup_path);
if (u->runtime_path)
fprintf(f, "RUNTIME=%s\n", u->runtime_path);
if (u->service)
fprintf(f, "SERVICE=%s\n", u->service);
if (u->service_job)
fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
if (u->slice)
fprintf(f, "SLICE=%s\n", u->slice);
if (u->slice_job)
fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
if (u->display)
fprintf(f, "DISPLAY=%s\n", u->display->id);
@ -251,13 +260,14 @@ int user_load(User *u) {
assert(u);
r = parse_env_file(u->state_file, NEWLINE,
"CGROUP", &u->cgroup_path,
"RUNTIME", &u->runtime_path,
"SERVICE", &u->service,
"DISPLAY", &display,
"SLICE", &u->slice,
"REALTIME", &realtime,
"MONOTONIC", &monotonic,
"RUNTIME", &u->runtime_path,
"SERVICE", &u->service,
"SERVICE_JOB", &u->service_job,
"SLICE", &u->slice,
"SLICE_JOB", &u->slice_job,
"DISPLAY", &display,
"REALTIME", &realtime,
"MONOTONIC", &monotonic,
NULL);
if (r < 0) {
if (r == -ENOENT)
@ -318,64 +328,70 @@ static int user_mkdir_runtime_path(User *u) {
return 0;
}
static int user_create_cgroup(User *u) {
char **k;
static int user_start_slice(User *u) {
DBusError error;
char *job;
int r;
assert(u);
dbus_error_init(&error);
if (!u->slice) {
u->slice = strdup(SPECIAL_USER_SLICE);
if (!u->slice)
return log_oom();
}
char lu[DECIMAL_STR_MAX(unsigned long) + 1];
sprintf(lu, "%lu", (unsigned long) u->uid);
if (!u->cgroup_path) {
_cleanup_free_ char *name = NULL, *escaped = NULL, *slice = NULL;
if (asprintf(&name, "%lu.user", (unsigned long) u->uid) < 0)
return log_oom();
escaped = cg_escape(name);
if (!escaped)
return log_oom();
r = cg_slice_to_path(u->slice, &slice);
r = build_subslice(SPECIAL_USER_SLICE, lu, &u->slice);
if (r < 0)
return r;
u->cgroup_path = strjoin(u->manager->cgroup_root, "/", slice, "/", escaped, NULL);
if (!u->cgroup_path)
return log_oom();
}
r = cg_create(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
if (r < 0) {
log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", u->cgroup_path, strerror(-r));
return r;
}
STRV_FOREACH(k, u->manager->controllers) {
if (strv_contains(u->manager->reset_controllers, *k))
continue;
r = cg_create(*k, u->cgroup_path);
r = hashmap_put(u->manager->user_units, u->slice, u);
if (r < 0)
log_warning("Failed to create cgroup %s:%s: %s", *k, u->cgroup_path, strerror(-r));
log_warning("Failed to create mapping between unit and user");
}
r = hashmap_put(u->manager->user_cgroups, u->cgroup_path, u);
if (r < 0)
log_warning("Failed to create mapping between cgroup and user");
r = manager_start_unit(u->manager, u->slice, &error, &job);
if (r < 0) {
log_error("Failed to start user slice: %s", bus_error(&error, r));
dbus_error_free(&error);
}
free(u->slice_job);
u->slice_job = job;
return 0;
}
static int user_start_service(User *u) {
DBusError error;
char *job;
int r;
assert(u);
/* FIXME: Fill me in later ... */
dbus_error_init(&error);
if (!u->service) {
char lu[DECIMAL_STR_MAX(unsigned long) + 1];
sprintf(lu, "%lu", (unsigned long) u->uid);
u->service = unit_name_build("user", lu, ".service");
if (!u->service)
return log_oom();
r = hashmap_put(u->manager->user_units, u->service, u);
if (r < 0)
log_warning("Failed to create mapping between service and user");
}
r = manager_start_unit(u->manager, u->service, &error, &job);
if (r < 0) {
log_error("Failed to start user service: %s", bus_error(&error, r));
dbus_error_free(&error);
}
free(u->service_job);
u->service_job = job;
return 0;
}
@ -396,7 +412,7 @@ int user_start(User *u) {
return r;
/* Create cgroup */
r = user_create_cgroup(u);
r = user_start_slice(u);
if (r < 0)
return r;
@ -418,70 +434,71 @@ int user_start(User *u) {
return 0;
}
static int user_stop_service(User *u) {
static int user_stop_slice(User *u) {
DBusError error;
char *job;
int r;
assert(u);
dbus_error_init(&error);
if (!u->slice)
return 0;
r = manager_stop_unit(u->manager, u->slice, &error, &job);
if (r < 0) {
log_error("Failed to stop user slice: %s", bus_error(&error, r));
dbus_error_free(&error);
return r;
}
free(u->slice_job);
u->slice_job = job;
return r;
}
static int user_stop_service(User *u) {
DBusError error;
char *job;
int r;
assert(u);
dbus_error_init(&error);
if (!u->service)
return 0;
return 0;
}
static int user_shall_kill(User *u) {
assert(u);
if (!u->manager->kill_user_processes)
return false;
if (strv_contains(u->manager->kill_exclude_users, u->name))
return false;
if (strv_isempty(u->manager->kill_only_users))
return true;
return strv_contains(u->manager->kill_only_users, u->name);
}
static int user_terminate_cgroup(User *u) {
int r;
char **k;
assert(u);
if (!u->cgroup_path)
return 0;
cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
if (user_shall_kill(u)) {
r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
if (r < 0)
log_error("Failed to kill user cgroup: %s", strerror(-r));
} else {
r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
if (r < 0)
log_error("Failed to check user cgroup: %s", strerror(-r));
else if (r > 0) {
r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
if (r < 0)
log_error("Failed to delete user cgroup: %s", strerror(-r));
} else
r = -EBUSY;
r = manager_stop_unit(u->manager, u->service, &error, &job);
if (r < 0) {
log_error("Failed to stop user service: %s", bus_error(&error, r));
dbus_error_free(&error);
return r;
}
STRV_FOREACH(k, u->manager->controllers)
cg_trim(*k, u->cgroup_path, true);
hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
free(u->cgroup_path);
u->cgroup_path = NULL;
free(u->service_job);
u->service_job = job;
return r;
}
/* static int user_shall_kill(User *u) { */
/* assert(u); */
/* if (!u->manager->kill_user_processes) */
/* return false; */
/* if (strv_contains(u->manager->kill_exclude_users, u->name)) */
/* return false; */
/* if (strv_isempty(u->manager->kill_only_users)) */
/* return true; */
/* return strv_contains(u->manager->kill_only_users, u->name); */
/* } */
static int user_remove_runtime_path(User *u) {
int r;
@ -520,7 +537,7 @@ int user_stop(User *u) {
r = k;
/* Kill cgroup */
k = user_terminate_cgroup(u);
k = user_stop_slice(u);
if (k < 0)
r = k;
@ -590,8 +607,6 @@ static int user_check_linger_file(User *u) {
}
int user_check_gc(User *u, bool drop_not_started) {
int r;
assert(u);
if (drop_not_started && !u->started)
@ -603,15 +618,6 @@ int user_check_gc(User *u, bool drop_not_started) {
if (user_check_linger_file(u) > 0)
return 1;
if (u->cgroup_path) {
r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
if (r < 0)
return r;
if (r <= 0)
return 1;
}
return 0;
}
@ -631,6 +637,8 @@ UserState user_get_state(User *u) {
assert(u);
if (u->slice_job || u->service_job)
return u->started ? USER_OPENING : USER_CLOSING;
LIST_FOREACH(sessions_by_user, i, u->sessions) {
if (session_is_active(i))
@ -649,27 +657,17 @@ UserState user_get_state(User *u) {
}
int user_kill(User *u, int signo) {
_cleanup_set_free_ Set *pid_set = NULL;
int r;
assert(u);
if (!u->cgroup_path)
if (!u->slice)
return -ESRCH;
pid_set = set_new(trivial_hash_func, trivial_compare_func);
if (!pid_set)
return -ENOMEM;
r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
if (r < 0 && (r != -EAGAIN && r != -ESRCH && r != -ENOENT))
return r;
return 0;
return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
}
static const char* const user_state_table[_USER_STATE_MAX] = {
[USER_OFFLINE] = "offline",
[USER_OPENING] = "opening",
[USER_LINGERING] = "lingering",
[USER_ONLINE] = "online",
[USER_ACTIVE] = "active",

View File

@ -30,6 +30,7 @@ typedef struct User User;
typedef enum UserState {
USER_OFFLINE, /* Not logged in at all */
USER_OPENING, /* Is logging in */
USER_LINGERING, /* Lingering has been enabled by the admin for this user */
USER_ONLINE, /* User logged in */
USER_ACTIVE, /* User logged in and has a session in the fg */
@ -47,16 +48,21 @@ struct User {
char *state_file;
char *runtime_path;
char *service;
char *cgroup_path;
char *slice;
char *service_job;
char *slice_job;
Session *display;
dual_timestamp timestamp;
bool in_gc_queue:1;
bool started:1;
bool slice_created:1;
bool service_created:1;
LIST_HEAD(Session, sessions);
LIST_FIELDS(User, gc_queue);

View File

@ -76,24 +76,23 @@ Manager *manager_new(void) {
m->buttons = hashmap_new(string_hash_func, string_compare_func);
m->machines = hashmap_new(string_hash_func, string_compare_func);
m->user_cgroups = hashmap_new(string_hash_func, string_compare_func);
m->session_cgroups = hashmap_new(string_hash_func, string_compare_func);
m->machine_cgroups = hashmap_new(string_hash_func, string_compare_func);
m->user_units = hashmap_new(string_hash_func, string_compare_func);
m->session_units = hashmap_new(string_hash_func, string_compare_func);
m->machine_units = hashmap_new(string_hash_func, string_compare_func);
m->session_fds = hashmap_new(trivial_hash_func, trivial_compare_func);
m->inhibitor_fds = hashmap_new(trivial_hash_func, trivial_compare_func);
m->button_fds = hashmap_new(trivial_hash_func, trivial_compare_func);
if (!m->devices || !m->seats || !m->sessions || !m->users || !m->inhibitors || !m->buttons || !m->machines ||
!m->user_cgroups || !m->session_cgroups || !m->machine_cgroups ||
!m->user_units || !m->session_units || !m->machine_units ||
!m->session_fds || !m->inhibitor_fds || !m->button_fds) {
manager_free(m);
return NULL;
}
m->reset_controllers = strv_new("cpu", NULL);
m->kill_exclude_users = strv_new("root", NULL);
if (!m->reset_controllers || !m->kill_exclude_users) {
if (!m->kill_exclude_users) {
manager_free(m);
return NULL;
}
@ -104,14 +103,6 @@ Manager *manager_new(void) {
return NULL;
}
if (cg_get_root_path(&m->cgroup_root) < 0) {
manager_free(m);
return NULL;
}
if (streq(m->cgroup_root, "/"))
m->cgroup_root[0] = 0;
return m;
}
@ -155,9 +146,9 @@ void manager_free(Manager *m) {
hashmap_free(m->buttons);
hashmap_free(m->machines);
hashmap_free(m->user_cgroups);
hashmap_free(m->session_cgroups);
hashmap_free(m->machine_cgroups);
hashmap_free(m->user_units);
hashmap_free(m->session_units);
hashmap_free(m->machine_units);
hashmap_free(m->session_fds);
hashmap_free(m->inhibitor_fds);
@ -194,14 +185,10 @@ void manager_free(Manager *m) {
if (m->idle_action_fd >= 0)
close_nointr_nofail(m->idle_action_fd);
strv_free(m->controllers);
strv_free(m->reset_controllers);
strv_free(m->kill_only_users);
strv_free(m->kill_exclude_users);
free(m->action_job);
free(m->cgroup_root);
free(m);
}
@ -989,177 +976,67 @@ static int manager_reserve_vt(Manager *m) {
return 0;
}
int manager_get_session_by_cgroup(Manager *m, const char *cgroup, Session **session) {
Session *s;
char *p;
assert(m);
assert(cgroup);
assert(session);
s = hashmap_get(m->session_cgroups, cgroup);
if (s) {
*session = s;
return 1;
}
p = strdupa(cgroup);
for (;;) {
char *e;
e = strrchr(p, '/');
if (!e || e == p) {
*session = NULL;
return 0;
}
*e = 0;
s = hashmap_get(m->session_cgroups, p);
if (s) {
*session = s;
return 1;
}
}
}
int manager_get_user_by_cgroup(Manager *m, const char *cgroup, User **user) {
User *u;
char *p;
assert(m);
assert(cgroup);
assert(user);
u = hashmap_get(m->user_cgroups, cgroup);
if (u) {
*user = u;
return 1;
}
p = strdupa(cgroup);
if (!p)
return log_oom();
for (;;) {
char *e;
e = strrchr(p, '/');
if (!e || e == p) {
*user = NULL;
return 0;
}
*e = 0;
u = hashmap_get(m->user_cgroups, p);
if (u) {
*user = u;
return 1;
}
}
}
int manager_get_machine_by_cgroup(Manager *m, const char *cgroup, Machine **machine) {
Machine *u;
char *p;
assert(m);
assert(cgroup);
assert(machine);
u = hashmap_get(m->machine_cgroups, cgroup);
if (u) {
*machine = u;
return 1;
}
p = strdupa(cgroup);
if (!p)
return log_oom();
for (;;) {
char *e;
e = strrchr(p, '/');
if (!e || e == p) {
*machine = NULL;
return 0;
}
*e = 0;
u = hashmap_get(m->machine_cgroups, p);
if (u) {
*machine = u;
return 1;
}
}
}
int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) {
_cleanup_free_ char *p = NULL;
_cleanup_free_ char *unit = NULL;
Session *s;
int r;
assert(m);
assert(pid >= 1);
assert(session);
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &p);
r = cg_pid_get_unit(pid, &unit);
if (r < 0)
return r;
return manager_get_session_by_cgroup(m, p, session);
s = hashmap_get(m->session_units, unit);
if (!s)
return 0;
*session = s;
return 1;
}
int manager_get_user_by_pid(Manager *m, pid_t pid, User **user) {
_cleanup_free_ char *p = NULL;
_cleanup_free_ char *unit = NULL;
User *u;
int r;
assert(m);
assert(pid >= 1);
assert(user);
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &p);
r = cg_pid_get_slice(pid, &unit);
if (r < 0)
return r;
return manager_get_user_by_cgroup(m, p, user);
u = hashmap_get(m->user_units, unit);
if (!u)
return 0;
*user = u;
return 1;
}
int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
_cleanup_free_ char *p = NULL;
_cleanup_free_ char *unit = NULL;
Machine *mm;
int r;
assert(m);
assert(pid >= 1);
assert(machine);
r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, pid, &p);
r = cg_pid_get_unit(pid, &unit);
if (r < 0)
return r;
return manager_get_machine_by_cgroup(m, p, machine);
}
mm = hashmap_get(m->machine_units, unit);
if (!mm)
return 0;
void manager_cgroup_notify_empty(Manager *m, const char *cgroup) {
Machine *machine;
Session *s;
User *u;
int r;
r = manager_get_session_by_cgroup(m, cgroup, &s);
if (r > 0)
session_add_to_gc_queue(s);
r = manager_get_user_by_cgroup(m, cgroup, &u);
if (r > 0)
user_add_to_gc_queue(u);
r = manager_get_machine_by_cgroup(m, cgroup, &machine);
if (r > 0)
machine_add_to_gc_queue(machine);
*machine = mm;
return 1;
}
static void manager_dispatch_other(Manager *m, int fd) {
@ -1229,15 +1106,40 @@ static int manager_connect_bus(Manager *m) {
dbus_bus_add_match(m->bus,
"type='signal',"
"interface='org.freedesktop.systemd1.Agent',"
"member='Released',"
"path='/org/freedesktop/systemd1/agent'",
"sender='org.freedesktop.systemd1',"
"interface='org.freedesktop.systemd1.Manager',"
"member='JobRemoved',"
"path='/org/freedesktop/systemd1'",
&error);
if (dbus_error_is_set(&error)) {
log_error("Failed to add match for JobRemoved: %s", bus_error_message(&error));
dbus_error_free(&error);
}
dbus_bus_add_match(m->bus,
"type='signal',"
"sender='org.freedesktop.systemd1',"
"interface='org.freedesktop.DBus.Properties',"
"member='PropertiesChanged'",
&error);
if (dbus_error_is_set(&error)) {
log_error("Failed to register match: %s", bus_error_message(&error));
r = -EIO;
goto fail;
log_error("Failed to add match for PropertiesChanged: %s", bus_error_message(&error));
dbus_error_free(&error);
}
r = bus_method_call_with_reply(
m->bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"Subscribe",
NULL,
&error,
DBUS_TYPE_INVALID);
if (r < 0) {
log_error("Failed to enable subscription: %s", bus_error(&error, r));
dbus_error_free(&error);
}
r = dbus_bus_request_name(m->bus, "org.freedesktop.login1", DBUS_NAME_FLAG_DO_NOT_QUEUE, &error);
@ -1563,9 +1465,6 @@ int manager_startup(Manager *m) {
assert(m);
assert(m->epoll_fd <= 0);
cg_shorten_controllers(m->reset_controllers);
cg_shorten_controllers(m->controllers);
m->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (m->epoll_fd < 0)
return -errno;

View File

@ -13,8 +13,6 @@
#KillUserProcesses=no
#KillOnlyUsers=
#KillExcludeUsers=root
#Controllers=
#ResetControllers=cpu
#InhibitDelayMaxSec=5
#HandlePowerKey=poweroff
#HandleSuspendKey=suspend

View File

@ -77,19 +77,15 @@ struct Manager {
Seat *vtconsole;
char *cgroup_root;
char **controllers, **reset_controllers;
char **kill_only_users, **kill_exclude_users;
bool kill_user_processes;
unsigned long session_counter;
unsigned long inhibit_counter;
Hashmap *session_cgroups;
Hashmap *user_cgroups;
Hashmap *machine_cgroups;
Hashmap *session_units;
Hashmap *user_units;
Hashmap *machine_units;
Hashmap *session_fds;
Hashmap *inhibitor_fds;
@ -171,17 +167,12 @@ int manager_startup(Manager *m);
int manager_run(Manager *m);
int manager_spawn_autovt(Manager *m, int vtnr);
void manager_cgroup_notify_empty(Manager *m, const char *cgroup);
void manager_gc(Manager *m, bool drop_not_started);
int manager_get_idle_hint(Manager *m, dual_timestamp *t);
int manager_get_user_by_cgroup(Manager *m, const char *cgroup, User **user);
int manager_get_user_by_pid(Manager *m, pid_t pid, User **user);
int manager_get_session_by_cgroup(Manager *m, const char *cgroup, Session **session);
int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session);
int manager_get_machine_by_cgroup(Manager *m, const char *cgroup, Machine **machine);
int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine);
extern const DBusObjectPathVTable bus_manager_vtable;
@ -194,5 +185,11 @@ int manager_send_changed(Manager *manager, const char *properties);
int manager_dispatch_delayed(Manager *manager);
int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, DBusError *error, char **job);
int manager_start_unit(Manager *manager, const char *unit, DBusError *error, char **job);
int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job);
int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error);
int manager_unit_is_active(Manager *manager, const char *unit);
/* gperf lookup function */
const struct ConfigPerfItem* logind_gperf_lookup(const char *key, unsigned length);

View File

@ -43,11 +43,6 @@
static int parse_argv(pam_handle_t *handle,
int argc, const char **argv,
char ***controllers,
char ***reset_controllers,
bool *kill_processes,
char ***kill_only_users,
char ***kill_exclude_users,
const char **class,
bool *debug) {
@ -59,89 +54,15 @@ static int parse_argv(pam_handle_t *handle,
for (i = 0; i < (unsigned) argc; i++) {
int k;
if (startswith(argv[i], "kill-session-processes=")) {
if ((k = parse_boolean(argv[i] + 23)) < 0) {
pam_syslog(handle, LOG_ERR, "Failed to parse kill-session-processes= argument.");
return k;
}
if (kill_processes)
*kill_processes = k;
} else if (startswith(argv[i], "kill-session=")) {
/* As compatibility for old versions */
if ((k = parse_boolean(argv[i] + 13)) < 0) {
pam_syslog(handle, LOG_ERR, "Failed to parse kill-session= argument.");
return k;
}
if (kill_processes)
*kill_processes = k;
} else if (startswith(argv[i], "controllers=")) {
if (controllers) {
char **l;
if (!(l = strv_split(argv[i] + 12, ","))) {
pam_syslog(handle, LOG_ERR, "Out of memory.");
return -ENOMEM;
}
strv_free(*controllers);
*controllers = l;
}
} else if (startswith(argv[i], "reset-controllers=")) {
if (reset_controllers) {
char **l;
if (!(l = strv_split(argv[i] + 18, ","))) {
pam_syslog(handle, LOG_ERR, "Out of memory.");
return -ENOMEM;
}
strv_free(*reset_controllers);
*reset_controllers = l;
}
} else if (startswith(argv[i], "kill-only-users=")) {
if (kill_only_users) {
char **l;
if (!(l = strv_split(argv[i] + 16, ","))) {
pam_syslog(handle, LOG_ERR, "Out of memory.");
return -ENOMEM;
}
strv_free(*kill_only_users);
*kill_only_users = l;
}
} else if (startswith(argv[i], "kill-exclude-users=")) {
if (kill_exclude_users) {
char **l;
if (!(l = strv_split(argv[i] + 19, ","))) {
pam_syslog(handle, LOG_ERR, "Out of memory.");
return -ENOMEM;
}
strv_free(*kill_exclude_users);
*kill_exclude_users = l;
}
} else if (startswith(argv[i], "class=")) {
if (startswith(argv[i], "class=")) {
if (class)
*class = argv[i] + 6;
} else if (startswith(argv[i], "debug=")) {
if ((k = parse_boolean(argv[i] + 6)) < 0) {
k = parse_boolean(argv[i] + 6);
if (k < 0) {
pam_syslog(handle, LOG_ERR, "Failed to parse debug= argument.");
return k;
}
@ -149,14 +70,9 @@ static int parse_argv(pam_handle_t *handle,
if (debug)
*debug = k;
} else if (startswith(argv[i], "create-session=") ||
startswith(argv[i], "kill-user=")) {
pam_syslog(handle, LOG_WARNING, "Option %s not supported anymore, ignoring.", argv[i]);
} else {
pam_syslog(handle, LOG_ERR, "Unknown parameter '%s'.", argv[i]);
return -EINVAL;
pam_syslog(handle, LOG_WARNING, "Unknown parameter '%s', ignoring", argv[i]);
return 0;
}
}
@ -206,55 +122,6 @@ static int get_user_data(
return PAM_SUCCESS;
}
static bool check_user_lists(
pam_handle_t *handle,
uid_t uid,
char **kill_only_users,
char **kill_exclude_users) {
const char *name = NULL;
char **l;
assert(handle);
if (uid == 0)
name = "root"; /* Avoid obvious NSS requests, to suppress network traffic */
else {
struct passwd *pw;
pw = pam_modutil_getpwuid(handle, uid);
if (pw)
name = pw->pw_name;
}
STRV_FOREACH(l, kill_exclude_users) {
uid_t u;
if (parse_uid(*l, &u) >= 0)
if (u == uid)
return false;
if (name && streq(name, *l))
return false;
}
if (strv_isempty(kill_only_users))
return true;
STRV_FOREACH(l, kill_only_users) {
uid_t u;
if (parse_uid(*l, &u) >= 0)
if (u == uid)
return true;
if (name && streq(name, *l))
return true;
}
return false;
}
static int get_seat_from_display(const char *display, const char **seat, uint32_t *vtnr) {
_cleanup_free_ char *p = NULL;
int r;
@ -316,13 +183,11 @@ _public_ PAM_EXTERN int pam_sm_open_session(
int argc, const char **argv) {
struct passwd *pw;
bool kill_processes = false, debug = false;
bool debug = false;
const char *username, *id, *object_path, *runtime_path, *service = NULL, *tty = NULL, *display = NULL, *remote_user = NULL, *remote_host = NULL, *seat = NULL, *type = NULL, *class = NULL, *class_pam = NULL, *cvtnr = NULL;
char **controllers = NULL, **reset_controllers = NULL, **kill_only_users = NULL, **kill_exclude_users = NULL;
DBusError error;
uint32_t uid, pid;
DBusMessageIter iter;
dbus_bool_t kp;
int session_fd = -1;
DBusConnection *bus = NULL;
DBusMessage *m = NULL, *reply = NULL;
@ -342,9 +207,8 @@ _public_ PAM_EXTERN int pam_sm_open_session(
if (parse_argv(handle,
argc, argv,
&controllers, &reset_controllers,
&kill_processes, &kill_only_users, &kill_exclude_users,
&class_pam, &debug) < 0) {
&class_pam,
&debug) < 0) {
r = PAM_SESSION_ERR;
goto finish;
}
@ -393,9 +257,6 @@ _public_ PAM_EXTERN int pam_sm_open_session(
goto finish;
}
if (kill_processes)
kill_processes = check_user_lists(handle, pw->pw_uid, kill_only_users, kill_exclude_users);
dbus_connection_set_change_sigpipe(FALSE);
bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
@ -510,27 +371,6 @@ _public_ PAM_EXTERN int pam_sm_open_session(
dbus_message_iter_init_append(m, &iter);
r = bus_append_strv_iter(&iter, controllers);
if (r < 0) {
pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
r = PAM_BUF_ERR;
goto finish;
}
r = bus_append_strv_iter(&iter, reset_controllers);
if (r < 0) {
pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
r = PAM_BUF_ERR;
goto finish;
}
kp = kill_processes;
if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &kp)) {
pam_syslog(handle, LOG_ERR, "Could not attach parameter to message.");
r = PAM_BUF_ERR;
goto finish;
}
if (debug)
pam_syslog(handle, LOG_DEBUG, "Asking logind to create session: "
"uid=%u pid=%u service=%s type=%s class=%s seat=%s vtnr=%u tty=%s display=%s remote=%s remote_user=%s remote_host=%s",
@ -613,11 +453,6 @@ _public_ PAM_EXTERN int pam_sm_open_session(
r = PAM_SUCCESS;
finish:
strv_free(controllers);
strv_free(reset_controllers);
strv_free(kill_only_users);
strv_free(kill_exclude_users);
dbus_error_free(&error);
if (bus) {

View File

@ -565,3 +565,30 @@ UnitType unit_name_to_type(const char *n) {
return unit_type_from_string(e + 1);
}
int build_subslice(const char *slice, const char*name, char **subslice) {
char *ret;
assert(slice);
assert(name);
assert(subslice);
if (streq(slice, "-.slice"))
ret = strappend(name, ".slice");
else {
char *e;
e = endswith(slice, ".slice");
if (!e)
return -EINVAL;
ret = new(char, (e - slice) + 1 + strlen(name) + 6 + 1);
if (!ret)
return -ENOMEM;
stpcpy(stpcpy(stpcpy(mempcpy(ret, slice, e - slice), "-"), name), ".slice");
}
*subslice = ret;
return 0;
}

View File

@ -99,3 +99,5 @@ int unit_name_from_dbus_path(const char *path, char **name);
char *unit_name_mangle(const char *name);
char *unit_name_mangle_with_suffix(const char *name, const char *suffix);
int build_subslice(const char *slice, const char*name, char **subslice);

View File

@ -3806,7 +3806,7 @@ static int snapshot(DBusConnection *bus, char **args) {
if (!n)
return log_oom();
r = bus_method_call_with_reply (
r = bus_method_call_with_reply(
bus,
"org.freedesktop.systemd1",
"/org/freedesktop/systemd1",

View File

@ -12,12 +12,10 @@ After=systemd-user-sessions.service
[Service]
User=%I
PAMName=systemd-shared
# in order to allow MEM_CG features to work, add "memory:/" here
ControlGroup=%R/user/%U.user/shared cpu:/
ControlGroupModify=yes
Type=notify
ExecStart=-@rootlibexecdir@/systemd --user
Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%U/dbus/user_bus_socket
Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/%I/dbus/user_bus_socket
Slice=user-%i.slice
[Install]
Alias=user@%i.service