]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/login/logind-core.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[thirdparty/systemd.git] / src / login / logind-core.c
index f97aa9143091ae998ff6c1d493df1632807a0386..60831bb1c327b463e79cb7f1b70fbbf58e08d3aa 100644 (file)
@@ -5,6 +5,9 @@
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <linux/vt.h>
+#if ENABLE_UTMP
+#include <utmpx.h>
+#endif
 
 #include "sd-device.h"
 
@@ -17,6 +20,7 @@
 #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"
@@ -130,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;
 
@@ -139,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;
         }
@@ -150,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;
@@ -158,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, 0);
+        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) {
@@ -175,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) {
@@ -252,7 +268,7 @@ int manager_process_seat_device(Manager *m, sd_device *d) {
                         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;
                 }
 
@@ -482,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;
@@ -510,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);
@@ -550,6 +566,17 @@ int manager_spawn_autovt(Manager *m, unsigned int vtnr) {
         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) {
         Iterator i;
         Button *b;
@@ -579,9 +606,8 @@ static int manager_count_external_displays(Manager *m) {
                 return r;
 
         FOREACH_DEVICE(e, d) {
+                const char *status, *enabled, *dash, *nn, *subsys;
                 sd_device *p;
-                const char *status, *enabled, *dash, *nn, *i, *subsys;
-                bool external = false;
 
                 if (sd_device_get_parent(d, &p) < 0)
                         continue;
@@ -604,16 +630,10 @@ static int manager_count_external_displays(Manager *m) {
                         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 */
@@ -654,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) {
@@ -684,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
+}