#include "dbus-loop.h"
#include "strv.h"
#include "conf-parser.h"
+#include "mkdir.h"
Manager *manager_new(void) {
Manager *m;
m->inhibitors = hashmap_new(string_hash_func, string_compare_func);
m->buttons = 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->user_units = hashmap_new(string_hash_func, string_compare_func);
+ m->session_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->user_cgroups || !m->session_cgroups ||
+ !m->user_units || !m->session_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;
}
return NULL;
}
- if (cg_get_user_path(&m->cgroup_path) < 0) {
- manager_free(m);
- return NULL;
- }
-
return m;
}
hashmap_free(m->inhibitors);
hashmap_free(m->buttons);
- hashmap_free(m->user_cgroups);
- hashmap_free(m->session_cgroups);
+ hashmap_free(m->user_units);
+ hashmap_free(m->session_units);
hashmap_free(m->session_fds);
hashmap_free(m->inhibitor_fds);
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->cgroup_path);
+ free(m->action_job);
free(m);
}
return 0;
}
-int manager_add_session(Manager *m, User *u, const char *id, Session **_session) {
+int manager_add_session(Manager *m, const char *id, Session **_session) {
Session *s;
assert(m);
return 0;
}
- s = session_new(m, u, id);
+ s = session_new(m, id);
if (!s)
return -ENOMEM;
goto finish;
}
- r = udev_enumerate_add_match_tag(e, "seat-master");
+ r = udev_enumerate_add_match_tag(e, "master-of-seat");
if (r < 0)
goto finish;
}
int manager_enumerate_seats(Manager *m) {
- DIR *d;
+ _cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
int r = 0;
return -errno;
}
- while ((de = readdir(d))) {
+ FOREACH_DIRENT(de, d, return -errno) {
Seat *s;
int k;
r = k;
}
- closedir(d);
-
- return r;
-}
-
-static int manager_enumerate_users_from_cgroup(Manager *m) {
- int r = 0, k;
- char *name;
- DIR *d;
-
- r = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_path, &d);
- if (r < 0) {
- if (r == -ENOENT)
- return 0;
-
- log_error("Failed to open %s: %s", m->cgroup_path, strerror(-r));
- return r;
- }
-
- while ((k = cg_read_subgroup(d, &name)) > 0) {
- User *user;
-
- k = manager_add_user_by_name(m, name, &user);
- if (k < 0) {
- free(name);
- r = k;
- continue;
- }
-
- user_add_to_gc_queue(user);
-
- if (!user->cgroup_path)
- if (asprintf(&user->cgroup_path, "%s/%s", m->cgroup_path, name) < 0) {
- r = -ENOMEM;
- free(name);
- break;
- }
-
- free(name);
- }
-
- if (r >= 0 && k < 0)
- r = k;
-
- closedir(d);
-
return r;
}
static int manager_enumerate_linger_users(Manager *m) {
- DIR *d;
+ _cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
int r = 0;
+ assert(m);
+
d = opendir("/var/lib/systemd/linger");
if (!d) {
if (errno == ENOENT)
return -errno;
}
- while ((de = readdir(d))) {
+ FOREACH_DIRENT(de, d, return -errno) {
int k;
if (!dirent_is_file(de))
}
}
- closedir(d);
-
return r;
}
int manager_enumerate_users(Manager *m) {
- DIR *d;
+ _cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
int r, k;
assert(m);
- /* First, enumerate user cgroups */
- r = manager_enumerate_users_from_cgroup(m);
-
- /* Second, add lingering users on top */
- k = manager_enumerate_linger_users(m);
- if (k < 0)
- r = k;
+ /* Add lingering users */
+ r = manager_enumerate_linger_users(m);
- /* Third, read in user data stored on disk */
+ /* Read in user data stored on disk */
d = opendir("/run/systemd/users");
if (!d) {
if (errno == ENOENT)
return -errno;
}
- while ((de = readdir(d))) {
- uid_t uid;
+ FOREACH_DIRENT(de, d, return -errno) {
User *u;
if (!dirent_is_file(de))
continue;
- k = parse_uid(de->d_name, &uid);
+ k = manager_add_user_by_name(m, de->d_name, &u);
if (k < 0) {
- log_error("Failed to parse file name %s: %s", de->d_name, strerror(-k));
- continue;
- }
-
- u = hashmap_get(m->users, ULONG_TO_PTR(uid));
- if (!u) {
- unlinkat(dirfd(d), de->d_name, 0);
- continue;
- }
+ log_error("Failed to add user by file name %s: %s", de->d_name, strerror(-k));
- k = user_load(u);
- if (k < 0)
- r = k;
- }
-
- closedir(d);
-
- return r;
-}
-
-static int manager_enumerate_sessions_from_cgroup(Manager *m) {
- User *u;
- Iterator i;
- int r = 0;
-
- HASHMAP_FOREACH(u, m->users, i) {
- DIR *d;
- char *name;
- int k;
-
- if (!u->cgroup_path)
- continue;
-
- k = cg_enumerate_subgroups(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, &d);
- if (k < 0) {
- if (k == -ENOENT)
- continue;
-
- log_error("Failed to open %s: %s", u->cgroup_path, strerror(-k));
r = k;
continue;
}
- while ((k = cg_read_subgroup(d, &name)) > 0) {
- Session *session;
-
- if (streq(name, "shared"))
- continue;
-
- k = manager_add_session(m, u, name, &session);
- if (k < 0) {
- free(name);
- break;
- }
-
- session_add_to_gc_queue(session);
-
- if (!session->cgroup_path)
- if (asprintf(&session->cgroup_path, "%s/%s", u->cgroup_path, name) < 0) {
- k = -ENOMEM;
- free(name);
- break;
- }
-
- free(name);
- }
-
- closedir(d);
+ user_add_to_gc_queue(u);
+ k = user_load(u);
if (k < 0)
r = k;
}
}
int manager_enumerate_sessions(Manager *m) {
- DIR *d;
+ _cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
int r = 0;
assert(m);
- /* First enumerate session cgroups */
- r = manager_enumerate_sessions_from_cgroup(m);
-
- /* Second, read in session data stored on disk */
+ /* Read in session data stored on disk */
d = opendir("/run/systemd/sessions");
if (!d) {
if (errno == ENOENT)
return -errno;
}
- while ((de = readdir(d))) {
+ FOREACH_DIRENT(de, d, return -errno) {
struct Session *s;
int k;
if (!dirent_is_file(de))
continue;
- s = hashmap_get(m->sessions, de->d_name);
- if (!s) {
- unlinkat(dirfd(d), de->d_name, 0);
+ k = manager_add_session(m, de->d_name, &s);
+ if (k < 0) {
+ log_error("Failed to add session by file name %s: %s", de->d_name, strerror(-k));
+
+ r = k;
continue;
}
+ session_add_to_gc_queue(s);
+
k = session_load(s);
if (k < 0)
r = k;
}
- closedir(d);
-
return r;
}
int manager_enumerate_inhibitors(Manager *m) {
- DIR *d;
+ _cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
int r = 0;
return -errno;
}
- while ((de = readdir(d))) {
+ FOREACH_DIRENT(de, d, return -errno) {
int k;
Inhibitor *i;
r = k;
}
- closedir(d);
-
return r;
}
return 0;
}
-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) {
+ _cleanup_free_ char *unit = NULL;
Session *s;
- char *p;
+ int r;
assert(m);
- assert(cgroup);
+ assert(pid >= 1);
assert(session);
- s = hashmap_get(m->session_cgroups, cgroup);
- if (s) {
- *session = s;
- return 1;
- }
-
- p = strdup(cgroup);
- if (!p)
- return log_oom();
-
- for (;;) {
- char *e;
-
- e = strrchr(p, '/');
- if (!e || e == p) {
- free(p);
- *session = NULL;
- return 0;
- }
+ r = cg_pid_get_unit(pid, &unit);
+ if (r < 0)
+ return r;
- *e = 0;
+ s = hashmap_get(m->session_units, unit);
+ if (!s)
+ return 0;
- s = hashmap_get(m->session_cgroups, p);
- if (s) {
- free(p);
- *session = s;
- return 1;
- }
- }
+ *session = s;
+ return 1;
}
-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) {
+ _cleanup_free_ char *unit = NULL;
User *u;
- char *p;
-
- assert(m);
- assert(cgroup);
- assert(user);
-
- u = hashmap_get(m->user_cgroups, cgroup);
- if (u) {
- *user = u;
- return 1;
- }
-
- p = strdup(cgroup);
- if (!p)
- return log_oom();
-
- for (;;) {
- char *e;
-
- e = strrchr(p, '/');
- if (!e || e == p) {
- free(p);
- *user = NULL;
- return 0;
- }
-
- *e = 0;
-
- u = hashmap_get(m->user_cgroups, p);
- if (u) {
- free(p);
- *user = u;
- return 1;
- }
- }
-}
-
-int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) {
- char *p;
int r;
assert(m);
assert(pid >= 1);
- assert(session);
+ assert(user);
- r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, pid, &p);
+ r = cg_pid_get_slice(pid, &unit);
if (r < 0)
return r;
- r = manager_get_session_by_cgroup(m, p, session);
- free(p);
-
- return r;
-}
-
-void manager_cgroup_notify_empty(Manager *m, const char *cgroup) {
- Session *s;
- User *u;
- int r;
-
- r = manager_get_session_by_cgroup(m, cgroup, &s);
- if (r > 0)
- session_add_to_gc_queue(s);
+ u = hashmap_get(m->user_units, unit);
+ if (!u)
+ return 0;
- r = manager_get_user_by_cgroup(m, cgroup, &u);
- if (r > 0)
- user_add_to_gc_queue(u);
+ *user = u;
+ return 1;
}
static void manager_dispatch_other(Manager *m, int fd) {
static int manager_connect_bus(Manager *m) {
DBusError error;
int r;
- struct epoll_event ev;
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.u32 = FD_BUS,
+ };
assert(m);
assert(!m->bus);
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.systemd1.Manager',"
+ "member='UnitRemoved',"
+ "path='/org/freedesktop/systemd1'",
+ &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 UnitRemoved: %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 add match for PropertiesChanged: %s", bus_error_message(&error));
+ dbus_error_free(&error);
+ }
+
+ dbus_bus_add_match(m->bus,
+ "type='signal',"
+ "sender='org.freedesktop.systemd1',"
+ "interface='org.freedesktop.systemd1.Manager',"
+ "member='Reloading',"
+ "path='/org/freedesktop/systemd1'",
+ &error);
+ if (dbus_error_is_set(&error)) {
+ log_error("Failed to add match for Reloading: %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);
goto fail;
}
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.u32 = FD_BUS;
-
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->bus_fd, &ev) < 0)
goto fail;
}
static int manager_connect_console(Manager *m) {
- struct epoll_event ev;
+ struct epoll_event ev = {
+ .events = 0,
+ .data.u32 = FD_CONSOLE,
+ };
assert(m);
assert(m->console_active_fd < 0);
return -errno;
}
- zero(ev);
- ev.events = 0;
- ev.data.u32 = FD_CONSOLE;
-
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->console_active_fd, &ev) < 0)
return -errno;
}
static int manager_connect_udev(Manager *m) {
- struct epoll_event ev;
int r;
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.u32 = FD_SEAT_UDEV,
+ };
assert(m);
assert(!m->udev_seat_monitor);
if (!m->udev_seat_monitor)
return -ENOMEM;
- r = udev_monitor_filter_add_match_tag(m->udev_seat_monitor, "seat-master");
+ r = udev_monitor_filter_add_match_tag(m->udev_seat_monitor, "master-of-seat");
if (r < 0)
return r;
m->udev_seat_fd = udev_monitor_get_fd(m->udev_seat_monitor);
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.u32 = FD_SEAT_UDEV;
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->udev_seat_fd, &ev) < 0)
return -errno;
if (session_check_gc(session, drop_not_started) == 0) {
session_stop(session);
+ session_finalize(session);
session_free(session);
}
}
if (user_check_gc(user, drop_not_started) == 0) {
user_stop(user);
+ user_finalize(user);
user_free(user);
}
}
return idle_hint;
}
+bool manager_shall_kill(Manager *m, const char *user) {
+ assert(m);
+ assert(user);
+
+ if (!m->kill_user_processes)
+ return false;
+
+ if (strv_contains(m->kill_exclude_users, user))
+ return false;
+
+ if (strv_isempty(m->kill_only_users))
+ return true;
+
+ return strv_contains(m->kill_only_users, user);
+}
+
int manager_dispatch_idle_action(Manager *m) {
struct dual_timestamp since;
- struct itimerspec its;
+ struct itimerspec its = {};
int r;
usec_t n;
goto finish;
}
- zero(its);
n = now(CLOCK_MONOTONIC);
r = manager_get_idle_hint(m, &since);
if (r <= 0)
- /* Not idle. Let's check if after a timeout it it might be idle then. */
+ /* Not idle. Let's check if after a timeout it might be idle then. */
timespec_store(&its.it_value, n + m->idle_action_usec);
else {
/* Idle! Let's see if it's time to do something, or if
}
if (m->idle_action_fd < 0) {
- struct epoll_event ev;
+ struct epoll_event ev = {
+ .events = EPOLLIN,
+ .data.u32 = FD_IDLE_ACTION,
+ };
m->idle_action_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK|TFD_CLOEXEC);
if (m->idle_action_fd < 0) {
goto finish;
}
- zero(ev);
- ev.events = EPOLLIN;
- ev.data.u32 = FD_IDLE_ACTION;
-
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->idle_action_fd, &ev) < 0) {
log_error("Failed to add idle action timer to epoll: %m");
r = -errno;
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;
return r;
/* Deserialize state */
- manager_enumerate_devices(m);
- manager_enumerate_seats(m);
- manager_enumerate_users(m);
- manager_enumerate_sessions(m);
- manager_enumerate_inhibitors(m);
- manager_enumerate_buttons(m);
+ r = manager_enumerate_devices(m);
+ if (r < 0)
+ log_warning("Device enumeration failed: %s", strerror(-r));
+
+ r = manager_enumerate_seats(m);
+ if (r < 0)
+ log_warning("Seat enumeration failed: %s", strerror(-r));
+
+ r = manager_enumerate_users(m);
+ if (r < 0)
+ log_warning("User enumeration failed: %s", strerror(-r));
+
+ r = manager_enumerate_sessions(m);
+ if (r < 0)
+ log_warning("Session enumeration failed: %s", strerror(-r));
+
+ r = manager_enumerate_inhibitors(m);
+ if (r < 0)
+ log_warning("Inhibitor enumeration failed: %s", strerror(-r));
+
+ r = manager_enumerate_buttons(m);
+ if (r < 0)
+ log_warning("Button enumeration failed: %s", strerror(-r));
/* Remove stale objects before we start them */
manager_gc(m, false);
manager_gc(m, true);
- if (m->delayed_unit) {
+ if (m->action_what != 0 && !m->action_job) {
usec_t x, y;
x = now(CLOCK_MONOTONIC);
- y = m->delayed_timestamp + m->inhibit_delay_max;
+ y = m->action_timestamp + m->inhibit_delay_max;
msec = x >= y ? 0 : (int) ((y - x) / USEC_PER_MSEC);
}
}
static int manager_parse_config_file(Manager *m) {
- FILE *f;
- const char *fn;
+ static const char fn[] = "/etc/systemd/logind.conf";
+ _cleanup_fclose_ FILE *f = NULL;
int r;
assert(m);
- fn = "/etc/systemd/logind.conf";
f = fopen(fn, "re");
if (!f) {
if (errno == ENOENT)
return -errno;
}
- r = config_parse(fn, f, "Login\0", config_item_perf_lookup, (void*) logind_gperf_lookup, false, m);
+ r = config_parse(NULL, fn, f, "Login\0", config_item_perf_lookup,
+ (void*) logind_gperf_lookup, false, false, m);
if (r < 0)
log_warning("Failed to parse configuration file: %s", strerror(-r));
- fclose(f);
-
return r;
}
goto finish;
}
+ /* Always create the directories people can create inotify
+ * watches in. Note that some applications might check for the
+ * existence of /run/systemd/seats/ to determine whether
+ * logind is available, so please always make sure this check
+ * stays in. */
+ mkdir_label("/run/systemd/seats", 0755);
+ mkdir_label("/run/systemd/users", 0755);
+ mkdir_label("/run/systemd/sessions", 0755);
+
m = manager_new();
if (!m) {
r = log_oom();