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"

File diff suppressed because it is too large Load Diff

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",

Some files were not shown because too many files have changed in this diff Show More