From 4bf4de816eaf371902e64951c123602fb17f0bb7 Mon Sep 17 00:00:00 2001 From: Jaap Aarts Date: Sat, 24 Aug 2024 12:23:43 +0200 Subject: [PATCH] Reimplement most of the asynchronous handling --- res/pam.d/ly | 2 +- src/auth.zig | 109 ++++++++--- src/main.zig | 523 ++++++++++++++++++++++++++------------------------- 3 files changed, 350 insertions(+), 284 deletions(-) diff --git a/res/pam.d/ly b/res/pam.d/ly index d99690f..b6f28d2 100644 --- a/res/pam.d/ly +++ b/res/pam.d/ly @@ -1,6 +1,6 @@ #%PAM-1.0 -auth sufficient pam_unix-so try_first_pass likeauth nullok # empty-password will not pass this, but will not fail causing the next line to get executed +auth sufficient pam_unix.so try_first_pass likeauth nullok # empty-password will not pass this, but will not fail causing the next line to get executed -auth sufficient pam_fprintd.so # We do not want to get errors when pam_fprintd.so is not present auth include login -auth optional pam_gnome_keyring.so diff --git a/src/auth.zig b/src/auth.zig index ed25c33..da95799 100644 --- a/src/auth.zig +++ b/src/auth.zig @@ -13,6 +13,66 @@ const utmp = interop.utmp; const Utmp = utmp.utmpx; const SharedError = @import("SharedError.zig"); +// When setting the currentLogin you must deallocate the previous currentLogin first +pub var currentLogin: ?[:0]const u8 = null; +pub var asyncPamHandle: ?*interop.pam.pam_handle = null; +pub var current_environment: ?Session.Environment = null; + +fn environment_equals(e0: Session.Environment, e1: Session.Environment) bool { + if (!std.mem.eql(u8, e0.cmd, e1.cmd)) { + return false; + } + if (!std.mem.eql(u8, e0.name, e1.name)) { + return false; + } + if (!std.mem.eql(u8, e0.specifier, e1.specifier)) { + return false; + } + if (!(e0.xdg_desktop_names == null and e1.xdg_desktop_names == null) or + (e0.xdg_desktop_names != null and e1.xdg_desktop_names != null and !std.mem.eql(u8, e0.xdg_desktop_names.?, e1.xdg_desktop_names.?))) + { + return false; + } + if (!(e0.xdg_session_desktop == null and e1.xdg_session_desktop == null) or + (e0.xdg_session_desktop != null and e1.xdg_session_desktop != null and !std.mem.eql(u8, e0.xdg_session_desktop.?, e1.xdg_session_desktop.?))) + { + return false; + } + if (e0.display_server != e1.display_server) { + return false; + } + return true; +} + +pub fn automaticLogin(config: Config, login: [:0]const u8, environment: Session.Environment, wakesem: *std.Thread.Semaphore) !void { + while (asyncPamHandle == null and currentLogin != null and std.mem.eql(u8, currentLogin.?, login)) { + if (authenticate(config, login, "", environment)) |handle| { + if (currentLogin != null and !std.mem.eql(u8, currentLogin.?, login) and environment_equals(current_environment.?, environment)) { + return; + } + asyncPamHandle = handle; + wakesem.post(); + return; + } else |_| {} + } +} + +pub fn startAutomaticLogin(allocator: std.mem.Allocator, config: Config, login: Text, environment: Session.Environment, wakesem: *std.Thread.Semaphore) !void { + if (currentLogin) |clogin| { + allocator.free(clogin); + currentLogin = null; + } + const login_text = try allocator.dupeZ(u8, login.text.items); + currentLogin = login_text; + var handle = try std.Thread.spawn(.{}, automaticLogin, .{ + config, + login_text, + environment, + wakesem, + }); + handle.detach(); +} + var xorg_pid: std.posix.pid_t = 0; pub fn xorgSignalHandler(i: c_int) callconv(.C) void { if (xorg_pid > 0) _ = std.c.kill(xorg_pid, i); @@ -23,9 +83,20 @@ pub fn sessionSignalHandler(i: c_int) callconv(.C) void { if (child_pid > 0) _ = std.c.kill(child_pid, i); } -pub fn authenticate(config: Config, login: [:0]const u8, password: [:0]const u8) !*interop.pam.pam_handle { +pub fn authenticate( + config: Config, + login: [:0]const u8, + password: [:0]const u8, + environment: Session.Environment, +) !*interop.pam.pam_handle { var pam_tty_buffer: [6]u8 = undefined; const pam_tty_str = try std.fmt.bufPrintZ(&pam_tty_buffer, "tty{d}", .{config.tty}); + var tty_buffer: [2]u8 = undefined; + const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{config.tty}); + + // Set the XDG environment variables + setXdgSessionEnv(environment.display_server); + try setXdgEnv(tty_str, environment.xdg_session_desktop orelse "", environment.xdg_desktop_names orelse ""); // Open the PAM session var credentials = [_:null]?[*:0]const u8{ login, password }; @@ -39,7 +110,7 @@ pub fn authenticate(config: Config, login: [:0]const u8, password: [:0]const u8) // Do the PAM routine var status = interop.pam.pam_start(config.service_name, null, &conv, &handle); if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); - errdefer interop.pam.pam_end(handle, status); + errdefer _ = interop.pam.pam_end(handle, status); // Set PAM_TTY as the current TTY. This is required in case it isn't being set by another PAM module status = interop.pam.pam_set_item(handle, interop.pam.PAM_TTY, pam_tty_str.ptr); @@ -52,27 +123,21 @@ pub fn authenticate(config: Config, login: [:0]const u8, password: [:0]const u8) if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); status = interop.pam.pam_setcred(handle, interop.pam.PAM_ESTABLISH_CRED); - defer { - if (status != interop.pam.PAM_SUCCESS) _ = interop.pam.pam_end(handle, status); - } + errdefer _ = interop.pam.pam_end(handle, status); + if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); return handle.?; } -pub fn finaliseAuth(config: Config, current_environment: Session.Environment, handle: ?*interop.pam.pam_handle, login: [:0]const u8) !void { - var tty_buffer: [2]u8 = undefined; - const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{config.tty}); - - // Close the PAM session - var status = interop.pam.pam_open_session(handle, 0); - defer status = interop.pam.pam_close_session(handle, status); - if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); - defer status = interop.pam.pam_setcred(handle, interop.pam.PAM_DELETE_CRED); +pub fn finaliseAuth(config: Config, environment: Session.Environment, handle: ?*interop.pam.pam_handle, login: [:0]const u8) !void { + var status: c_int = undefined; defer status = interop.pam.pam_end(handle, status); + defer status = interop.pam.pam_setcred(handle, interop.pam.PAM_DELETE_CRED); - // Set the XDG environment variables - setXdgSessionEnv(current_environment.display_server); - try setXdgEnv(tty_str, current_environment.xdg_session_desktop orelse "", current_environment.xdg_desktop_names orelse ""); + // Open the PAM session + status = interop.pam.pam_open_session(handle, 0); + if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); + defer status = interop.pam.pam_close_session(handle, status); var pwd: *interop.pwd.passwd = undefined; { @@ -94,7 +159,7 @@ pub fn finaliseAuth(config: Config, current_environment: Session.Environment, ha child_pid = try std.posix.fork(); if (child_pid == 0) { - startSession(config, pwd, handle, current_environment) catch |e| { + startSession(config, pwd, handle, environment) catch |e| { shared_err.writeError(e); std.process.exit(1); }; @@ -132,7 +197,7 @@ fn startSession( config: Config, pwd: *interop.pwd.passwd, handle: ?*interop.pam.pam_handle, - current_environment: Session.Environment, + environment: Session.Environment, ) !void { if (builtin.os.tag == .freebsd) { // FreeBSD has initgroups() in unistd @@ -164,13 +229,13 @@ fn startSession( std.posix.chdirZ(pwd.pw_dir.?) catch return error.ChangeDirectoryFailed; // Execute what the user requested - switch (current_environment.display_server) { - .wayland => try executeWaylandCmd(pwd.pw_shell.?, config, current_environment.cmd), + switch (environment.display_server) { + .wayland => try executeWaylandCmd(pwd.pw_shell.?, config, environment.cmd), .shell => try executeShellCmd(pwd.pw_shell.?, config), .xinitrc, .x11 => if (build_options.enable_x11_support) { var vt_buf: [5]u8 = undefined; const vt = try std.fmt.bufPrint(&vt_buf, "vt{d}", .{config.tty}); - try executeX11Cmd(pwd.pw_shell.?, pwd.pw_dir.?, config, current_environment.cmd, vt); + try executeX11Cmd(pwd.pw_shell.?, pwd.pw_dir.?, config, environment.cmd, vt); }, } } diff --git a/src/main.zig b/src/main.zig index d361854..370585f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -39,31 +39,14 @@ pub fn signalHandler(i: c_int) callconv(.C) void { std.c.exit(i); } -// When setting the currentLogin you must deallocate the previous currentLogin first -var currentLogin: ?[:0]const u8 = null; -var asyncPamHandle: ?*interop.pam.pam_handle = null; - -fn fingerprintLogin(config: Config, login: [:0]const u8) !void { - // TODO: loop if there was an error - const pamHandle = try auth.authenticate(config, login, ""); - if (currentLogin != null and !std.mem.eql(u8, std.mem.span(@as([*:0]const u8, currentLogin.?)), login)) { - return; +fn eventThreadMain(event: *?termbox.tb_event, event_error: *c_int, timeout: *c_int, wake: *std.Thread.Semaphore, cont: *std.Thread.Semaphore) void { + while (true) { + cont.wait(); + var e: termbox.tb_event = undefined; + event_error.* = termbox.tb_peek_event(&e, timeout.*); + event.* = e; + wake.post(); } - asyncPamHandle = pamHandle; -} - -fn startFingerPrintLogin(allocator: std.mem.Allocator, config: Config, login: Text) !void { - if (currentLogin) |clogin| { - allocator.free(clogin); - currentLogin = null; - } - const login_text = try allocator.dupeZ(u8, login.text.items); - currentLogin = login_text; - var handle = try std.Thread.spawn(.{}, fingerprintLogin, .{ - config, - login_text, - }); - handle.detach(); } pub fn main() !void { @@ -305,9 +288,6 @@ pub fn main() !void { } } - try startFingerPrintLogin(allocator, config, login); - defer allocator.free(currentLogin.?); - // Place components on the screen { buffer.drawBoxCenter(!config.hide_borders, config.blank_box); @@ -359,7 +339,11 @@ pub fn main() !void { const brightness_up_key = try std.fmt.parseInt(u8, config.brightness_up_key[1..], 10); const brightness_up_len = try utils.strWidth(lang.brightness_up); - var event: termbox.tb_event = undefined; + var event_timeout: c_int = std.math.maxInt(c_int); + var event_error: c_int = undefined; + var event: ?termbox.tb_event = null; + var wake: std.Thread.Semaphore = .{}; + var doneEvent: std.Thread.Semaphore = .{}; var run = true; var update = true; var resolution_changed = false; @@ -370,6 +354,16 @@ pub fn main() !void { try info_line.addMessage(lang.err_console_dev, config.error_bg, config.error_fg); }; + doneEvent.post(); + const thandle = try std.Thread.spawn(.{}, eventThreadMain, .{ &event, &event_error, &event_timeout, &wake, &doneEvent }); + + thandle.detach(); + + { + const current_environment = session.label.list.items[session.label.current]; + try auth.startAutomaticLogin(allocator, config, login, current_environment, &wake); + } + defer allocator.free(auth.currentLogin.?); while (run) { // If there's no input or there's an animation, a resolution change needs to be checked if (!update or config.animation != .none) { @@ -556,7 +550,7 @@ pub fn main() !void { _ = termbox.tb_present(); } - var timeout: i32 = -1; + var timeout: u64 = std.math.maxInt(u64); // Calculate the maximum timeout based on current animations, or the (big) clock. If there's none, we wait for 100ms instead if (animate and !animation_timed_out) { @@ -586,273 +580,280 @@ pub fn main() !void { timeout = @intCast(1000 - @divTrunc(tv.tv_usec, 1000) + 1); } - const timeoutEnd: i64 = if (timeout == -1) std.math.maxInt(i64) else std.time.milliTimestamp() + timeout; - - const maxtimeout = 100; var skipEvent: bool = false; - while (timeoutEnd - std.time.milliTimestamp() > 0) { - const event_error = termbox.tb_peek_event(&event, @intCast(@min(timeoutEnd - std.time.milliTimestamp(), maxtimeout))); + _ = wake.timedWait(timeout) catch .{}; + if (auth.asyncPamHandle) |handle| { + var shared_err = try SharedError.init(); + defer shared_err.deinit(); - if (asyncPamHandle) |handle| { - var shared_err = try SharedError.init(); - defer shared_err.deinit(); + { + const login_text = try allocator.dupeZ(u8, login.text.items); + defer allocator.free(login_text); - { - const login_text = try allocator.dupeZ(u8, login.text.items); - defer allocator.free(login_text); + try info_line.addMessage(lang.authenticating, config.error_bg, config.error_fg); + InfoLine.clearRendered(allocator, buffer) catch { + try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); + }; + info_line.label.draw(); + _ = termbox.tb_present(); - InfoLine.clearRendered(allocator, buffer) catch {}; - info_line.draw(buffer); - _ = termbox.tb_present(); + session_pid = try std.posix.fork(); + if (session_pid == 0) { + const current_environment = session.label.list.items[session.label.current]; + auth.finaliseAuth(config, current_environment, handle, login_text) catch |err| { + shared_err.writeError(err); + std.process.exit(1); + }; - session_pid = try std.posix.fork(); - if (session_pid == 0) { - const current_environment = session.environments.items[session.current]; - auth.finaliseAuth(config, current_environment, handle, login_text) catch |err| { - shared_err.writeError(err); - std.process.exit(1); - }; + std.process.exit(0); + } + _ = std.posix.waitpid(session_pid, 0); - std.process.exit(0); - } - _ = std.posix.waitpid(session_pid, 0); + session_pid = -1; + } - session_pid = -1; - } + auth.asyncPamHandle = null; - const auth_err = shared_err.readError(); - if (auth_err) |err| { - try info_line.setText(getAuthErrorMsg(err, lang)); - } else { - try info_line.setText(lang.logout); - } - asyncPamHandle = null; - try startFingerPrintLogin(allocator, config, login); + const auth_err = shared_err.readError(); + if (auth_err) |err| { + try info_line.addMessage(getAuthErrorMsg(err, lang), config.error_bg, config.error_fg); + // We don't want to start login back in instantly. The user must first edit + // the login/desktop in order to login. Only in case of a failed login (so not a logout) + // should we automatically restart it. + const current_environment = session.label.list.items[session.label.current]; + try auth.startAutomaticLogin(allocator, config, login, current_environment, &wake); + } else { + try info_line.addMessage(lang.logout, config.error_bg, config.error_fg); + } - try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, tb_termios); - _ = termbox.tb_clear(); - _ = termbox.tb_present(); + try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, tb_termios); + _ = termbox.tb_clear(); + _ = termbox.tb_present(); - update = true; + update = true; - var restore_cursor = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", config.term_restore_cursor_cmd }, allocator); - _ = restore_cursor.spawnAndWait() catch .{}; - } + _ = termbox.tb_set_cursor(0, 0); + _ = termbox.tb_present(); + } else if (event) |e| { + defer doneEvent.post(); + update = timeout != -1; skipEvent = event_error < 0; - if ((event_error < 0 and event_error != -6) or event.type == termbox.TB_EVENT_KEY) break; - } - update = timeout != -1; - if (skipEvent or event.type != termbox.TB_EVENT_KEY) continue; + if (skipEvent or e.type != termbox.TB_EVENT_KEY) continue; - switch (event.key) { - termbox.TB_KEY_ESC => { - if (config.vi_mode and insert_mode) { - insert_mode = false; + switch (e.key) { + termbox.TB_KEY_ESC => { + if (config.vi_mode and insert_mode) { + insert_mode = false; + update = true; + } + }, + termbox.TB_KEY_F12...termbox.TB_KEY_F1 => { + const pressed_key = 0xFFFF - e.key + 1; + if (pressed_key == shutdown_key) { + shutdown = true; + run = false; + } else if (pressed_key == restart_key) { + restart = true; + run = false; + } else if (pressed_key == sleep_key) { + if (config.sleep_cmd) |sleep_cmd| { + var sleep = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", sleep_cmd }, allocator); + _ = sleep.spawnAndWait() catch .{}; + } + } else if (pressed_key == brightness_down_key) { + var brightness = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", config.brightness_down_cmd }, allocator); + _ = brightness.spawnAndWait() catch .{}; + } else if (pressed_key == brightness_up_key) { + var brightness = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", config.brightness_up_cmd }, allocator); + _ = brightness.spawnAndWait() catch .{}; + } + }, + termbox.TB_KEY_CTRL_C => run = false, + termbox.TB_KEY_CTRL_U => { + if (active_input == .login) { + login.clear(); + update = true; + } else if (active_input == .password) { + password.clear(); + update = true; + } + }, + termbox.TB_KEY_CTRL_K, termbox.TB_KEY_ARROW_UP => { + active_input = switch (active_input) { + .session, .info_line => .info_line, + .login => .session, + .password => .login, + }; update = true; - } - }, - termbox.TB_KEY_F12...termbox.TB_KEY_F1 => { - const pressed_key = 0xFFFF - event.key + 1; - if (pressed_key == shutdown_key) { - shutdown = true; - run = false; - } else if (pressed_key == restart_key) { - restart = true; - run = false; - } else if (pressed_key == sleep_key) { - if (config.sleep_cmd) |sleep_cmd| { - var sleep = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", sleep_cmd }, allocator); - _ = sleep.spawnAndWait() catch .{}; + }, + termbox.TB_KEY_CTRL_J, termbox.TB_KEY_ARROW_DOWN => { + if (active_input == .login) { + const current_environment = session.label.list.items[session.label.current]; + try auth.startAutomaticLogin(allocator, config, login, current_environment, &wake); } - } else if (pressed_key == brightness_down_key) { - var brightness = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", config.brightness_down_cmd }, allocator); - _ = brightness.spawnAndWait() catch .{}; - } else if (pressed_key == brightness_up_key) { - var brightness = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", config.brightness_up_cmd }, allocator); - _ = brightness.spawnAndWait() catch .{}; - } - }, - termbox.TB_KEY_CTRL_C => run = false, - termbox.TB_KEY_CTRL_U => { - if (active_input == .login) { - login.clear(); + active_input = switch (active_input) { + .info_line => .session, + .session => .login, + .login, .password => .password, + }; update = true; - } else if (active_input == .password) { - password.clear(); + }, + termbox.TB_KEY_TAB => { + if (active_input == .login) { + const current_environment = session.label.list.items[session.label.current]; + try auth.startAutomaticLogin(allocator, config, login, current_environment, &wake); + } + active_input = switch (active_input) { + .info_line => .session, + .session => .login, + .login => .password, + .password => .info_line, + }; update = true; - } - }, - termbox.TB_KEY_CTRL_K, termbox.TB_KEY_ARROW_UP => { - active_input = switch (active_input) { - .session, .info_line => .info_line, - .login => .session, - .password => .login, - }; - update = true; - }, - termbox.TB_KEY_CTRL_J, termbox.TB_KEY_ARROW_DOWN => { - active_input = switch (active_input) { - .info_line => .session, - .session => .login, - .login, .password => .password, - }; - update = true; - }, - termbox.TB_KEY_TAB => { - active_input = switch (active_input) { - .info_line => .session, - .session => .login, - .login => .password, - .password => .info_line, - }; - update = true; - }, - termbox.TB_KEY_BACK_TAB => { - active_input = switch (active_input) { - .info_line => .password, - .session => .info_line, - .login => .session, - .password => .login, - }; - - update = true; - }, - termbox.TB_KEY_ENTER => { - try info_line.addMessage(lang.authenticating, config.bg, config.fg); - InfoLine.clearRendered(allocator, buffer) catch { - try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); - }; - info_line.label.draw(); - _ = termbox.tb_present(); - - if (config.save) save_last_settings: { - var file = std.fs.cwd().createFile(save_path, .{}) catch break :save_last_settings; - defer file.close(); - - const save_data = Save{ - .user = login.text.items, - .session_index = session.label.current, + }, + termbox.TB_KEY_BACK_TAB => { + if (active_input == .info_line) { + const current_environment = session.label.list.items[session.label.current]; + try auth.startAutomaticLogin(allocator, config, login, current_environment, &wake); + } + active_input = switch (active_input) { + .info_line => .password, + .session => .info_line, + .login => .session, + .password => .login, }; - ini.writeFromStruct(save_data, file.writer(), null, true, .{}) catch break :save_last_settings; - // Delete previous save file if it exists - if (migrator.maybe_save_file) |path| std.fs.cwd().deleteFile(path) catch {}; - } - - var shared_err = try SharedError.init(); - defer shared_err.deinit(); + update = true; + }, + termbox.TB_KEY_ENTER => { + try info_line.addMessage(lang.authenticating, config.bg, config.fg); + InfoLine.clearRendered(allocator, buffer) catch { + try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); + }; + info_line.label.draw(); + _ = termbox.tb_present(); - { - const login_text = try allocator.dupeZ(u8, login.text.items); - defer allocator.free(login_text); - const password_text = try allocator.dupeZ(u8, password.text.items); - defer allocator.free(password_text); + if (config.save) save_last_settings: { + var file = std.fs.cwd().createFile(save_path, .{}) catch break :save_last_settings; + defer file.close(); - // Give up control on the TTY - _ = termbox.tb_shutdown(); + const save_data = Save{ + .user = login.text.items, + .session_index = session.label.current, + }; + ini.writeFromStruct(save_data, file.writer(), null, true, .{}) catch break :save_last_settings; - session_pid = try std.posix.fork(); - const current_environment = session.environments.items[session.current]; - if (auth.authenticate(config, login_text, password_text)) |handle| { - if (session_pid == 0) { - auth.finaliseAuth(config, current_environment, handle, login_text) catch |err| { - shared_err.writeError(err); - std.process.exit(1); - }; + // Delete previous save file if it exists + if (migrator.maybe_save_file) |path| std.fs.cwd().deleteFile(path) catch {}; + } - std.process.exit(0); + var shared_err = try SharedError.init(); + defer shared_err.deinit(); + + { + const login_text = try allocator.dupeZ(u8, login.text.items); + defer allocator.free(login_text); + const password_text = try allocator.dupeZ(u8, password.text.items); + defer allocator.free(password_text); + + // Give up control on the TTY + _ = termbox.tb_shutdown(); + + session_pid = try std.posix.fork(); + const current_environment = session.label.list.items[session.label.current]; + if (auth.authenticate(config, login_text, password_text, current_environment)) |handle| { + if (session_pid == 0) { + auth.finaliseAuth(config, current_environment, handle, login_text) catch |err| { + shared_err.writeError(err); + std.process.exit(1); + }; + + std.process.exit(0); + } + _ = std.posix.waitpid(session_pid, 0); + } else |err| { + shared_err.writeError(err); } - _ = std.posix.waitpid(session_pid, 0); - } else |err| { - shared_err.writeError(err); + + session_pid = -1; } - session_pid = -1; - } + // Take back control of the TTY + _ = termbox.tb_init(); + _ = termbox.tb_set_output_mode(termbox.TB_OUTPUT_NORMAL); + + const auth_err = shared_err.readError(); + if (auth_err) |err| { + auth_fails += 1; + active_input = .password; + try info_line.addMessage(getAuthErrorMsg(err, lang), config.error_bg, config.error_fg); + if (config.clear_password or err != error.PamAuthError) password.clear(); + } else { + if (config.logout_cmd) |logout_cmd| { + var logout_process = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", logout_cmd }, allocator); + _ = logout_process.spawnAndWait() catch .{}; + } - // Take back control of the TTY - _ = termbox.tb_init(); - _ = termbox.tb_set_output_mode(termbox.TB_OUTPUT_NORMAL); - - const auth_err = shared_err.readError(); - if (auth_err) |err| { - auth_fails += 1; - active_input = .password; - try info_line.addMessage(getAuthErrorMsg(err, lang), config.error_bg, config.error_fg); - if (config.clear_password or err != error.PamAuthError) password.clear(); - } else { - if (config.logout_cmd) |logout_cmd| { - var logout_process = std.process.Child.init(&[_][]const u8{ "/bin/sh", "-c", logout_cmd }, allocator); - _ = logout_process.spawnAndWait() catch .{}; + password.clear(); + try info_line.addMessage(lang.logout, config.bg, config.fg); } - password.clear(); - try info_line.addMessage(lang.logout, config.bg, config.fg); - } + try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, tb_termios); + if (auth_fails < config.auth_fails) _ = termbox.tb_clear(); - try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, tb_termios); - if (auth_fails < config.auth_fails) _ = termbox.tb_clear(); + update = true; - update = true; + // Restore the cursor + _ = termbox.tb_set_cursor(0, 0); + _ = termbox.tb_present(); + }, + else => { + if (!insert_mode) { + switch (e.ch) { + 'k' => { + active_input = switch (active_input) { + .session, .info_line => .info_line, + .login => .session, + .password => .login, + }; + update = true; + continue; + }, + 'j' => { + if (active_input == .login) { + const current_environment = session.label.list.items[session.label.current]; + try auth.startAutomaticLogin(allocator, config, login, current_environment, &wake); + } + active_input = switch (active_input) { + .info_line => .session, + .session => .login, + .login, .password => .password, + }; + update = true; + continue; + }, + 'i' => { + insert_mode = true; + update = true; + continue; + }, + else => {}, + } + } - // Restore the cursor - _ = termbox.tb_set_cursor(0, 0); - _ = termbox.tb_present(); - }, - else => { - if (!insert_mode) { - switch (event.ch) { - 'k' => { - active_input = switch (active_input) { - .session, .info_line => .info_line, - .login => .session, - .password => .login, - }; - update = true; - continue; - }, - 'j' => { - active_input = switch (active_input) { - .info_line => .session, - .session => .login, - .login, .password => .password, - }; - update = true; - continue; + switch (active_input) { + .info_line => info_line.label.handle(&event.?, insert_mode), + .session => session.label.handle(&event.?, insert_mode), + .login => login.handle(&event.?, insert_mode) catch { + try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); }, - 'i' => { - insert_mode = true; - update = true; - continue; + .password => password.handle(&event.?, insert_mode) catch { + try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); }, - else => {}, } - } - - switch (active_input) { - .info_line => info_line.label.handle(&event, insert_mode), - .session => session.label.handle(&event, insert_mode), - .login => { - const shouldFingerprint = switch (event.key) { - termbox.TB_KEY_DELETE => true, - termbox.TB_KEY_BACKSPACE, termbox.TB_KEY_BACKSPACE2 => true, - else => event.ch > 31 and event.ch < 127, - }; - - if (shouldFingerprint) { - try startFingerPrintLogin(allocator, config, login); - } - - login.handle(&event, insert_mode) catch { - try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); - }; - }, - .password => password.handle(&event, insert_mode) catch { - try info_line.addMessage(lang.err_alloc, config.error_bg, config.error_fg); - }, - } - update = true; - }, + update = true; + }, + } } } }