mirror of
https://github.com/systemd/systemd.git
synced 2026-08-05 23:50:42 +00:00
@@ -20,6 +20,7 @@
|
||||
#include "fd-util.h"
|
||||
#include "limits-util.h"
|
||||
#include "logind.h"
|
||||
#include "logind-utmp.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
@@ -29,7 +30,6 @@
|
||||
#include "udev-util.h"
|
||||
#include "user-util.h"
|
||||
#include "userdb.h"
|
||||
#include "utmp-wtmp.h"
|
||||
|
||||
void manager_reset_config(Manager *m) {
|
||||
assert(m);
|
||||
@@ -711,137 +711,6 @@ bool manager_all_buttons_ignored(Manager *m) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int manager_read_utmp(Manager *m) {
|
||||
#if ENABLE_UTMP
|
||||
int r;
|
||||
_unused_ _cleanup_(utxent_cleanup) bool utmpx = false;
|
||||
|
||||
assert(m);
|
||||
|
||||
if (utmpxname(UTMPX_FILE) < 0)
|
||||
return log_error_errno(errno, "Failed to set utmp path to " UTMPX_FILE ": %m");
|
||||
|
||||
utmpx = utxent_start();
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
struct utmpx *u;
|
||||
const char *c;
|
||||
Session *s;
|
||||
|
||||
errno = 0;
|
||||
u = getutxent();
|
||||
if (!u) {
|
||||
if (errno == ENOENT)
|
||||
log_debug_errno(errno, UTMPX_FILE " does not exist, ignoring.");
|
||||
else if (errno != 0)
|
||||
log_warning_errno(errno, "Failed to read " UTMPX_FILE ", ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
return log_oom();
|
||||
|
||||
c = path_startswith(t, "/dev/");
|
||||
if (c) {
|
||||
r = free_and_strdup(&t, c);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
if (isempty(t))
|
||||
continue;
|
||||
|
||||
if (manager_get_session_by_pidref(m, &PIDREF_MAKE_FROM_PID(u->ut_pid), &s) <= 0)
|
||||
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);
|
||||
}
|
||||
|
||||
#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 = ASSERT_PTR(userdata);
|
||||
|
||||
/* 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, UTMPX_FILE, 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 " UTMPX_FILE ", 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
|
||||
}
|
||||
|
||||
int manager_read_efi_boot_loader_entries(Manager *m) {
|
||||
#if ENABLE_EFI
|
||||
|
||||
@@ -32,13 +32,14 @@
|
||||
#include "fileio.h"
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
#include "logind.h"
|
||||
#include "logind-action.h"
|
||||
#include "logind-dbus.h"
|
||||
#include "logind-polkit.h"
|
||||
#include "logind-seat-dbus.h"
|
||||
#include "logind-session-dbus.h"
|
||||
#include "logind-user-dbus.h"
|
||||
#include "logind.h"
|
||||
#include "logind-utmp.h"
|
||||
#include "mkdir-label.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
|
||||
135
src/login/logind-utmp.c
Normal file
135
src/login/logind-utmp.c
Normal file
@@ -0,0 +1,135 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "logind-utmp.h"
|
||||
#include "path-util.h"
|
||||
#include "utmp-wtmp.h"
|
||||
|
||||
int manager_read_utmp(Manager *m) {
|
||||
#if ENABLE_UTMP
|
||||
assert(m);
|
||||
|
||||
if (utmpxname(UTMPX_FILE) < 0)
|
||||
return log_error_errno(errno, "Failed to set utmp path to " UTMPX_FILE ": %m");
|
||||
|
||||
_unused_ _cleanup_(utxent_cleanup) bool utmpx = utxent_start();
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *t = NULL;
|
||||
struct utmpx *u;
|
||||
const char *c;
|
||||
Session *s;
|
||||
|
||||
errno = 0;
|
||||
u = getutxent();
|
||||
if (!u) {
|
||||
if (errno == ENOENT)
|
||||
log_debug_errno(errno, UTMPX_FILE " does not exist, ignoring.");
|
||||
else if (errno != 0)
|
||||
log_warning_errno(errno, "Failed to read " UTMPX_FILE ", ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
return log_oom();
|
||||
|
||||
c = path_startswith(t, "/dev/");
|
||||
if (c) {
|
||||
if (free_and_strdup(&t, c) < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
if (isempty(t))
|
||||
continue;
|
||||
|
||||
if (manager_get_session_by_pidref(m, &PIDREF_MAKE_FROM_PID(u->ut_pid), &s) <= 0)
|
||||
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);
|
||||
session_save(s);
|
||||
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);
|
||||
session_save(s);
|
||||
}
|
||||
|
||||
#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 = ASSERT_PTR(userdata);
|
||||
|
||||
/* 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, UTMPX_FILE, 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 " UTMPX_FILE ", 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
|
||||
}
|
||||
8
src/login/logind-utmp.h
Normal file
8
src/login/logind-utmp.h
Normal file
@@ -0,0 +1,8 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "logind.h"
|
||||
|
||||
int manager_read_utmp(Manager *m);
|
||||
void manager_connect_utmp(Manager *m);
|
||||
void manager_reconnect_utmp(Manager *m);
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "logind-seat-dbus.h"
|
||||
#include "logind-session-dbus.h"
|
||||
#include "logind-user-dbus.h"
|
||||
#include "logind-utmp.h"
|
||||
#include "logind-varlink.h"
|
||||
#include "main-func.h"
|
||||
#include "mkdir-label.h"
|
||||
|
||||
@@ -181,10 +181,6 @@ bool manager_is_docked_or_external_displays(Manager *m);
|
||||
bool manager_is_on_external_power(void);
|
||||
bool manager_all_buttons_ignored(Manager *m);
|
||||
|
||||
int manager_read_utmp(Manager *m);
|
||||
void manager_connect_utmp(Manager *m);
|
||||
void manager_reconnect_utmp(Manager *m);
|
||||
|
||||
/* gperf lookup function */
|
||||
const struct ConfigPerfItem* logind_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ liblogind_core_sources = files(
|
||||
'logind-session.c',
|
||||
'logind-user-dbus.c',
|
||||
'logind-user.c',
|
||||
'logind-utmp.c',
|
||||
'logind-varlink.c',
|
||||
'logind-wall.c',
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user