X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flogin%2Flogind-session.c;h=c4fe53bc6119fcdfa74bc289908643e8645126a5;hb=e2cc6eca73cd1df8be552d7c23f9ff3d69c06f1e;hp=fdeacb150acb42a84f203cb052fc412a613c6a50;hpb=558c6490b1df7f82a63d0a747fda7412c4d28b0c;p=elogind.git diff --git a/src/login/logind-session.c b/src/login/logind-session.c index fdeacb150..c4fe53bc6 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -44,21 +44,6 @@ static void session_remove_fifo(Session *s); -static unsigned long devt_hash_func(const void *p, const uint8_t hash_key[HASH_KEY_SIZE]) { - uint64_t u = *(const dev_t*)p; - - return uint64_hash_func(&u, hash_key); -} - -static int devt_compare_func(const void *_a, const void *_b) { - dev_t a, b; - - a = *(const dev_t*) _a; - b = *(const dev_t*) _b; - - return a < b ? -1 : (a > b ? 1 : 0); -} - Session* session_new(Manager *m, const char *id) { Session *s; @@ -76,7 +61,7 @@ Session* session_new(Manager *m, const char *id) { return NULL; } - s->devices = hashmap_new(devt_hash_func, devt_compare_func); + s->devices = hashmap_new(&devt_hash_ops); if (!s->devices) { free(s->state_file); free(s); @@ -153,8 +138,6 @@ void session_free(Session *s) { hashmap_remove(s->manager->sessions, s->id); - s->vt_source = sd_event_source_unref(s->vt_source); - free(s->state_file); free(s); } @@ -566,11 +549,11 @@ int session_start(Session *s) { return r; log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO, - MESSAGE_ID(SD_MESSAGE_SESSION_START), + LOG_MESSAGE_ID(SD_MESSAGE_SESSION_START), "SESSION_ID=%s", s->id, "USER_ID=%s", s->user->name, "LEADER="PID_FMT, s->leader, - "MESSAGE=New session %s of user %s.", s->id, s->user->name, + LOG_MESSAGE("New session %s of user %s.", s->id, s->user->name), NULL); if (!dual_timestamp_is_set(&s->timestamp)) @@ -669,11 +652,11 @@ int session_finalize(Session *s) { if (s->started) log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO, - MESSAGE_ID(SD_MESSAGE_SESSION_STOP), + LOG_MESSAGE_ID(SD_MESSAGE_SESSION_STOP), "SESSION_ID=%s", s->id, "USER_ID=%s", s->user->name, "LEADER="PID_FMT, s->leader, - "MESSAGE=Removed session %s.", s->id, + LOG_MESSAGE("Removed session %s.", s->id), NULL); s->timer_event_source = sd_event_source_unref(s->timer_event_source); @@ -978,8 +961,8 @@ int session_kill(Session *s, KillWho who, int signo) { static int session_open_vt(Session *s) { char path[sizeof("/dev/tty") + DECIMAL_STR_MAX(s->vtnr)]; - if (!s->vtnr) - return -1; + if (s->vtnr < 1) + return -ENODEV; if (s->vtfd >= 0) return s->vtfd; @@ -994,59 +977,56 @@ static int session_open_vt(Session *s) { return s->vtfd; } -static int session_vt_fn(sd_event_source *source, const struct signalfd_siginfo *si, void *data) { - Session *s = data; - - if (s->vtfd >= 0) - ioctl(s->vtfd, VT_RELDISP, 1); - - return 0; -} - -void session_prepare_vt(Session *s) { +int session_prepare_vt(Session *s) { int vt, r; struct vt_mode mode = { 0 }; - sigset_t mask; + + if (s->vtnr < 1) + return 0; vt = session_open_vt(s); if (vt < 0) - return; + return vt; r = fchown(vt, s->user->uid, -1); - if (r < 0) + if (r < 0) { + r = -errno; + log_error("Cannot change owner of /dev/tty%u: %m", s->vtnr); goto error; + } r = ioctl(vt, KDSKBMODE, K_OFF); - if (r < 0) + if (r < 0) { + r = -errno; + log_error("Cannot set K_OFF on /dev/tty%u: %m", s->vtnr); goto error; + } r = ioctl(vt, KDSETMODE, KD_GRAPHICS); - if (r < 0) - goto error; - - sigemptyset(&mask); - sigaddset(&mask, SIGUSR1); - sigprocmask(SIG_BLOCK, &mask, NULL); - - r = sd_event_add_signal(s->manager->event, &s->vt_source, SIGUSR1, session_vt_fn, s); - if (r < 0) + if (r < 0) { + r = -errno; + log_error("Cannot set KD_GRAPHICS on /dev/tty%u: %m", s->vtnr); goto error; + } /* Oh, thanks to the VT layer, VT_AUTO does not work with KD_GRAPHICS. * So we need a dummy handler here which just acknowledges *all* VT * switch requests. */ mode.mode = VT_PROCESS; - mode.relsig = SIGUSR1; - mode.acqsig = SIGUSR1; + mode.relsig = SIGRTMIN; + mode.acqsig = SIGRTMIN + 1; r = ioctl(vt, VT_SETMODE, &mode); - if (r < 0) + if (r < 0) { + r = -errno; + log_error("Cannot set VT_PROCESS on /dev/tty%u: %m", s->vtnr); goto error; + } - return; + return 0; error: - log_error("cannot mute VT %u for session %s (%d/%d)", s->vtnr, s->id, r, errno); session_restore_vt(s); + return r; } void session_restore_vt(Session *s) { @@ -1058,8 +1038,6 @@ void session_restore_vt(Session *s) { if (vt < 0) return; - s->vt_source = sd_event_source_unref(s->vt_source); - ioctl(vt, KDSETMODE, KD_TEXT); if (read_one_line_file("/sys/module/vt/parameters/default_utf8", &utf8) >= 0 && *utf8 == '1') @@ -1075,36 +1053,61 @@ void session_restore_vt(Session *s) { s->vtfd = safe_close(s->vtfd); } +void session_leave_vt(Session *s) { + int r; + + assert(s); + + /* This is called whenever we get a VT-switch signal from the kernel. + * We acknowledge all of them unconditionally. Note that session are + * free to overwrite those handlers and we only register them for + * sessions with controllers. Legacy sessions are not affected. + * However, if we switch from a non-legacy to a legacy session, we must + * make sure to pause all device before acknowledging the switch. We + * process the real switch only after we are notified via sysfs, so the + * legacy session might have already started using the devices. If we + * don't pause the devices before the switch, we might confuse the + * session we switch to. */ + + if (s->vtfd < 0) + return; + + session_device_pause_all(s); + r = ioctl(s->vtfd, VT_RELDISP, 1); + if (r < 0) + log_debug("Cannot release VT of session %s: %m", s->id); +} + bool session_is_controller(Session *s, const char *sender) { assert(s); return streq_ptr(s->controller, sender); } -static void session_swap_controller(Session *s, char *name) { +static void session_release_controller(Session *s, bool notify) { + _cleanup_free_ char *name = NULL; SessionDevice *sd; - if (s->controller) { - manager_drop_busname(s->manager, s->controller); - free(s->controller); - s->controller = NULL; + if (!s->controller) + return; - /* Drop all devices as they're now unused. Do that after the - * controller is released to avoid sending out useles - * dbus signals. */ - while ((sd = hashmap_first(s->devices))) - session_device_free(sd); + name = s->controller; - if (!name) - session_restore_vt(s); - } + /* By resetting the controller before releasing the devices, we won't + * send notification signals. This avoids sending useless notifications + * if the controller is released on disconnects. */ + if (!notify) + s->controller = NULL; - s->controller = name; - session_save(s); + while ((sd = hashmap_first(s->devices))) + session_device_free(sd); + + s->controller = NULL; + manager_drop_busname(s->manager, name); } int session_set_controller(Session *s, const char *sender, bool force) { - char *t; + _cleanup_free_ char *name = NULL; int r; assert(s); @@ -1115,17 +1118,13 @@ int session_set_controller(Session *s, const char *sender, bool force) { if (s->controller && !force) return -EBUSY; - t = strdup(sender); - if (!t) + name = strdup(sender); + if (!name) return -ENOMEM; - r = manager_watch_busname(s->manager, sender); - if (r) { - free(t); + r = manager_watch_busname(s->manager, name); + if (r) return r; - } - - session_swap_controller(s, t); /* When setting a session controller, we forcibly mute the VT and set * it into graphics-mode. Applications can override that by changing @@ -1135,7 +1134,16 @@ int session_set_controller(Session *s, const char *sender, bool force) { * exits. * If logind crashes/restarts, we restore the controller during restart * or reset the VT in case it crashed/exited, too. */ - session_prepare_vt(s); + r = session_prepare_vt(s); + if (r < 0) { + manager_drop_busname(s->manager, name); + return r; + } + + session_release_controller(s, true); + s->controller = name; + name = NULL; + session_save(s); return 0; } @@ -1146,7 +1154,9 @@ void session_drop_controller(Session *s) { if (!s->controller) return; - session_swap_controller(s, NULL); + session_release_controller(s, false); + session_save(s); + session_restore_vt(s); } static const char* const session_state_table[_SESSION_STATE_MAX] = { @@ -1164,6 +1174,7 @@ static const char* const session_type_table[_SESSION_TYPE_MAX] = { [SESSION_X11] = "x11", [SESSION_WAYLAND] = "wayland", [SESSION_MIR] = "mir", + [SESSION_WEB] = "web", }; DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);