diff options
Diffstat (limited to 'src/login/logind-core.c')
-rw-r--r-- | src/login/logind-core.c | 349 |
1 files changed, 257 insertions, 92 deletions
diff --git a/src/login/logind-core.c b/src/login/logind-core.c index dbae4bf5af..60831bb1c3 100644 --- a/src/login/logind-core.c +++ b/src/login/logind-core.c @@ -5,19 +5,25 @@ #include <sys/ioctl.h> #include <sys/types.h> #include <linux/vt.h> +#if ENABLE_UTMP +#include <utmpx.h> +#endif + +#include "sd-device.h" #include "alloc-util.h" #include "bus-error.h" #include "bus-util.h" #include "cgroup-util.h" #include "conf-parser.h" +#include "device-util.h" #include "fd-util.h" #include "logind.h" #include "parse-util.h" +#include "path-util.h" #include "process-util.h" #include "strv.h" #include "terminal-util.h" -#include "udev-util.h" #include "user-util.h" void manager_reset_config(Manager *m) { @@ -27,6 +33,8 @@ void manager_reset_config(Manager *m) { m->reserve_vt = 6; m->remove_ipc = true; m->inhibit_delay_max = 5 * USEC_PER_SEC; + m->user_stop_delay = 10 * USEC_PER_SEC; + m->handle_power_key = HANDLE_POWEROFF; m->handle_suspend_key = HANDLE_SUSPEND; m->handle_hibernate_key = HANDLE_HIBERNATE; @@ -88,15 +96,16 @@ int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_dev int manager_add_seat(Manager *m, const char *id, Seat **_seat) { Seat *s; + int r; assert(m); assert(id); s = hashmap_get(m->seats, id); if (!s) { - s = seat_new(m, id); - if (!s) - return -ENOMEM; + r = seat_new(&s, m, id); + if (r < 0) + return r; } if (_seat) @@ -107,15 +116,16 @@ int manager_add_seat(Manager *m, const char *id, Seat **_seat) { int manager_add_session(Manager *m, const char *id, Session **_session) { Session *s; + int r; assert(m); assert(id); s = hashmap_get(m->sessions, id); if (!s) { - s = session_new(m, id); - if (!s) - return -ENOMEM; + r = session_new(&s, m, id); + if (r < 0) + return r; } if (_session) @@ -124,7 +134,14 @@ int manager_add_session(Manager *m, const char *id, Session **_session) { return 0; } -int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) { +int manager_add_user( + Manager *m, + uid_t uid, + gid_t gid, + const char *name, + const char *home, + User **_user) { + User *u; int r; @@ -133,7 +150,7 @@ int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User ** u = hashmap_get(m->users, UID_TO_PTR(uid)); if (!u) { - r = user_new(&u, m, uid, gid, name); + r = user_new(&u, m, uid, gid, name, home); if (r < 0) return r; } @@ -144,7 +161,12 @@ int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User ** return 0; } -int manager_add_user_by_name(Manager *m, const char *name, User **_user) { +int manager_add_user_by_name( + Manager *m, + const char *name, + User **_user) { + + const char *home = NULL; uid_t uid; gid_t gid; int r; @@ -152,11 +174,11 @@ int manager_add_user_by_name(Manager *m, const char *name, User **_user) { assert(m); assert(name); - r = get_user_creds(&name, &uid, &gid, NULL, NULL); + r = get_user_creds(&name, &uid, &gid, &home, NULL, 0); if (r < 0) return r; - return manager_add_user(m, uid, gid, name, _user); + return manager_add_user(m, uid, gid, name, home, _user); } int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) { @@ -169,7 +191,7 @@ int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) { if (!p) return errno > 0 ? -errno : -ENOENT; - return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user); + return manager_add_user(m, uid, p->pw_gid, p->pw_name, p->pw_dir, _user); } int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor) { @@ -215,15 +237,22 @@ int manager_add_button(Manager *m, const char *name, Button **_button) { return 0; } -int manager_process_seat_device(Manager *m, struct udev_device *d) { +int manager_process_seat_device(Manager *m, sd_device *d) { + const char *action; Device *device; int r; assert(m); - if (streq_ptr(udev_device_get_action(d), "remove")) { + if (sd_device_get_property_value(d, "ACTION", &action) >= 0 && + streq(action, "remove")) { + const char *syspath; + + r = sd_device_get_syspath(d, &syspath); + if (r < 0) + return 0; - device = hashmap_get(m->devices, udev_device_get_syspath(d)); + device = hashmap_get(m->devices, syspath); if (!device) return 0; @@ -231,27 +260,30 @@ int manager_process_seat_device(Manager *m, struct udev_device *d) { device_free(device); } else { - const char *sn; - Seat *seat = NULL; + const char *sn, *syspath; bool master; + Seat *seat; - sn = udev_device_get_property_value(d, "ID_SEAT"); - if (isempty(sn)) + if (sd_device_get_property_value(d, "ID_SEAT", &sn) < 0 || isempty(sn)) sn = "seat0"; if (!seat_name_is_valid(sn)) { - log_warning("Device with invalid seat name %s found, ignoring.", sn); + log_device_warning(d, "Device with invalid seat name %s found, ignoring.", sn); return 0; } seat = hashmap_get(m->seats, sn); - master = udev_device_has_tag(d, "master-of-seat"); + master = sd_device_has_tag(d, "master-of-seat") > 0; /* Ignore non-master devices for unknown seats */ if (!master && !seat) return 0; - r = manager_add_device(m, udev_device_get_syspath(d), master, &device); + r = sd_device_get_syspath(d, &syspath); + if (r < 0) + return r; + + r = manager_add_device(m, syspath, master, &device); if (r < 0) return r; @@ -272,16 +304,21 @@ int manager_process_seat_device(Manager *m, struct udev_device *d) { return 0; } -int manager_process_button_device(Manager *m, struct udev_device *d) { +int manager_process_button_device(Manager *m, sd_device *d) { + const char *action, *sysname; Button *b; - int r; assert(m); - if (streq_ptr(udev_device_get_action(d), "remove")) { + r = sd_device_get_sysname(d, &sysname); + if (r < 0) + return r; + + if (sd_device_get_property_value(d, "ACTION", &action) >= 0 && + streq(action, "remove")) { - b = hashmap_get(m->buttons, udev_device_get_sysname(d)); + b = hashmap_get(m->buttons, sysname); if (!b) return 0; @@ -290,12 +327,11 @@ int manager_process_button_device(Manager *m, struct udev_device *d) { } else { const char *sn; - r = manager_add_button(m, udev_device_get_sysname(d), &b); + r = manager_add_button(m, sysname, &b); if (r < 0) return r; - sn = udev_device_get_property_value(d, "ID_SEAT"); - if (isempty(sn)) + if (sd_device_get_property_value(d, "ID_SEAT", &sn) < 0 || isempty(sn)) sn = "seat0"; button_set_seat(b, sn); @@ -319,13 +355,16 @@ int manager_get_session_by_pid(Manager *m, pid_t pid, Session **ret) { if (!pid_is_valid(pid)) return -EINVAL; - r = cg_pid_get_unit(pid, &unit); - if (r < 0) - goto not_found; + s = hashmap_get(m->sessions_by_leader, PID_TO_PTR(pid)); + if (!s) { + r = cg_pid_get_unit(pid, &unit); + if (r < 0) + goto not_found; - s = hashmap_get(m->session_units, unit); - if (!s) - goto not_found; + s = hashmap_get(m->session_units, unit); + if (!s) + goto not_found; + } if (ret) *ret = s; @@ -459,7 +498,7 @@ int config_parse_n_autovts( return 0; } -static int vt_is_busy(unsigned int vtnr) { +static int vt_is_busy(unsigned vtnr) { struct vt_stat vt_stat; int r = 0; _cleanup_close_ int fd; @@ -487,9 +526,9 @@ static int vt_is_busy(unsigned int vtnr) { return r; } -int manager_spawn_autovt(Manager *m, unsigned int vtnr) { +int manager_spawn_autovt(Manager *m, unsigned vtnr) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char name[sizeof("autovt@tty.service") + DECIMAL_STR_MAX(unsigned int)]; + char name[sizeof("autovt@tty.service") + DECIMAL_STR_MAX(unsigned)]; int r; assert(m); @@ -522,9 +561,20 @@ int manager_spawn_autovt(Manager *m, unsigned int vtnr) { NULL, "ss", name, "fail"); if (r < 0) - log_error("Failed to start %s: %s", name, bus_error_message(&error, r)); + return log_error_errno(r, "Failed to start %s: %s", name, bus_error_message(&error, r)); - return r; + return 0; +} + +bool manager_is_lid_closed(Manager *m) { + Iterator i; + Button *b; + + HASHMAP_FOREACH(b, m->buttons, i) + if (b->lid_closed) + return true; + + return false; } static bool manager_is_docked(Manager *m) { @@ -539,82 +589,60 @@ static bool manager_is_docked(Manager *m) { } static int manager_count_external_displays(Manager *m) { - _cleanup_(udev_enumerate_unrefp) struct udev_enumerate *e = NULL; - struct udev_list_entry *item = NULL, *first = NULL; - int r; - int n = 0; - - e = udev_enumerate_new(m->udev); - if (!e) - return -ENOMEM; + _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; + sd_device *d; + int r, n = 0; - r = udev_enumerate_add_match_subsystem(e, "drm"); + r = sd_device_enumerator_new(&e); if (r < 0) return r; - r = udev_enumerate_scan_devices(e); + r = sd_device_enumerator_allow_uninitialized(e); if (r < 0) return r; - first = udev_enumerate_get_list_entry(e); - udev_list_entry_foreach(item, first) { - _cleanup_(udev_device_unrefp) struct udev_device *d = NULL; - struct udev_device *p; - const char *status, *enabled, *dash, *nn, *i; - bool external = false; + r = sd_device_enumerator_add_match_subsystem(e, "drm", true); + if (r < 0) + return r; - d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item)); - if (!d) - return -ENOMEM; + FOREACH_DEVICE(e, d) { + const char *status, *enabled, *dash, *nn, *subsys; + sd_device *p; - p = udev_device_get_parent(d); - if (!p) + if (sd_device_get_parent(d, &p) < 0) continue; /* If the parent shares the same subsystem as the * device we are looking at then it is a connector, * which is what we are interested in. */ - if (!streq_ptr(udev_device_get_subsystem(p), "drm")) + if (sd_device_get_subsystem(p, &subsys) < 0 || !streq(subsys, "drm")) continue; - nn = udev_device_get_sysname(d); - if (!nn) + if (sd_device_get_sysname(d, &nn) < 0) continue; - /* Ignore internal displays: the type is encoded in - * the sysfs name, as the second dash separated item - * (the first is the card name, the last the connector - * number). We implement a whitelist of external - * displays here, rather than a whitelist, to ensure - * we don't block suspends too eagerly. */ + /* Ignore internal displays: the type is encoded in the sysfs name, as the second dash separated item + * (the first is the card name, the last the connector number). We implement a blacklist of external + * displays here, rather than a whitelist of internal ones, to ensure we don't block suspends too + * eagerly. */ dash = strchr(nn, '-'); if (!dash) continue; dash++; - FOREACH_STRING(i, "VGA-", "DVI-I-", "DVI-D-", "DVI-A-" - "Composite-", "SVIDEO-", "Component-", - "DIN-", "DP-", "HDMI-A-", "HDMI-B-", "TV-") { - - if (startswith(dash, i)) { - external = true; - break; - } - } - if (!external) + if (!STARTSWITH_SET(dash, + "VGA-", "DVI-I-", "DVI-D-", "DVI-A-" + "Composite-", "SVIDEO-", "Component-", + "DIN-", "DP-", "HDMI-A-", "HDMI-B-", "TV-")) continue; /* Ignore ports that are not enabled */ - enabled = udev_device_get_sysattr_value(d, "enabled"); - if (!enabled) - continue; - if (!streq_ptr(enabled, "enabled")) + if (sd_device_get_sysattr_value(d, "enabled", &enabled) < 0 || !streq(enabled, "enabled")) continue; /* We count any connector which is not explicitly * "disconnected" as connected. */ - status = udev_device_get_sysattr_value(d, "status"); - if (!streq_ptr(status, "disconnected")) + if (sd_device_get_sysattr_value(d, "status", &status) < 0 || !streq(status, "disconnected")) n++; } @@ -646,15 +674,13 @@ bool manager_is_docked_or_external_displays(Manager *m) { bool manager_is_on_external_power(void) { int r; - /* For now we only check for AC power, but 'external power' can apply - * to anything that isn't an internal battery */ + /* For now we only check for AC power, but 'external power' can apply to anything that isn't an internal + * battery */ r = on_ac_power(); if (r < 0) log_warning_errno(r, "Failed to read AC power status: %m"); - else if (r > 0) - return true; - return false; + return r != 0; /* Treat failure as 'on AC' */ } bool manager_all_buttons_ignored(Manager *m) { @@ -676,3 +702,142 @@ bool manager_all_buttons_ignored(Manager *m) { return true; } + +int manager_read_utmp(Manager *m) { +#if ENABLE_UTMP + int r; + + assert(m); + + if (utmpxname(_PATH_UTMPX) < 0) + return log_error_errno(errno, "Failed to set utmp path to " _PATH_UTMPX ": %m"); + + setutxent(); + + for (;;) { + _cleanup_free_ char *t = NULL; + struct utmpx *u; + const char *c; + Session *s; + + errno = 0; + u = getutxent(); + if (!u) { + if (errno != 0) + log_warning_errno(errno, "Failed to read " _PATH_UTMPX ", ignoring: %m"); + r = 0; + break; + } + + if (u->ut_type != USER_PROCESS) + continue; + + if (!pid_is_valid(u->ut_pid)) + continue; + + t = strndup(u->ut_line, sizeof(u->ut_line)); + if (!t) { + r = log_oom(); + break; + } + + c = path_startswith(t, "/dev/"); + if (c) { + r = free_and_strdup(&t, c); + if (r < 0) { + log_oom(); + break; + } + } + + if (isempty(t)) + continue; + + s = hashmap_get(m->sessions_by_leader, PID_TO_PTR(u->ut_pid)); + if (!s) + continue; + + if (s->tty_validity == TTY_FROM_UTMP && !streq_ptr(s->tty, t)) { + /* This may happen on multiplexed SSH connection (i.e. 'SSH connection sharing'). In + * this case PAM and utmp sessions don't match. In such a case let's invalidate the TTY + * information and never acquire it again. */ + + s->tty = mfree(s->tty); + s->tty_validity = TTY_UTMP_INCONSISTENT; + log_debug("Session '%s' has inconsistent TTY information, dropping TTY information.", s->id); + continue; + } + + /* Never override what we figured out once */ + if (s->tty || s->tty_validity >= 0) + continue; + + s->tty = TAKE_PTR(t); + s->tty_validity = TTY_FROM_UTMP; + log_debug("Acquired TTY information '%s' from utmp for session '%s'.", s->tty, s->id); + } + + endutxent(); + return r; +#else + return 0; +#endif +} + +#if ENABLE_UTMP +static int manager_dispatch_utmp(sd_event_source *s, const struct inotify_event *event, void *userdata) { + Manager *m = userdata; + + assert(m); + + /* If there's indication the file itself might have been removed or became otherwise unavailable, then let's + * reestablish the watch on whatever there's now. */ + if ((event->mask & (IN_ATTRIB|IN_DELETE_SELF|IN_MOVE_SELF|IN_Q_OVERFLOW|IN_UNMOUNT)) != 0) + manager_connect_utmp(m); + + (void) manager_read_utmp(m); + return 0; +} +#endif + +void manager_connect_utmp(Manager *m) { +#if ENABLE_UTMP + sd_event_source *s = NULL; + int r; + + assert(m); + + /* Watch utmp for changes via inotify. We do this to deal with tools such as ssh, which will register the PAM + * session early, and acquire a TTY only much later for the connection. Thus during PAM the TTY won't be known + * yet. ssh will register itself with utmp when it finally acquired the TTY. Hence, let's make use of this, and + * watch utmp for the TTY asynchronously. We use the PAM session's leader PID as key, to find the right entry. + * + * Yes, relying on utmp is pretty ugly, but it's good enough for informational purposes, as well as idle + * detection (which, for tty sessions, relies on the TTY used) */ + + r = sd_event_add_inotify(m->event, &s, _PATH_UTMPX, IN_MODIFY|IN_MOVE_SELF|IN_DELETE_SELF|IN_ATTRIB, manager_dispatch_utmp, m); + if (r < 0) + log_full_errno(r == -ENOENT ? LOG_DEBUG: LOG_WARNING, r, "Failed to create inotify watch on " _PATH_UTMPX ", ignoring: %m"); + else { + r = sd_event_source_set_priority(s, SD_EVENT_PRIORITY_IDLE); + if (r < 0) + log_warning_errno(r, "Failed to adjust utmp event source priority, ignoring: %m"); + + (void) sd_event_source_set_description(s, "utmp"); + } + + sd_event_source_unref(m->utmp_event_source); + m->utmp_event_source = s; +#endif +} + +void manager_reconnect_utmp(Manager *m) { +#if ENABLE_UTMP + assert(m); + + if (m->utmp_event_source) + return; + + manager_connect_utmp(m); +#endif +} |