diff --git a/src/main.zig b/src/main.zig index 70b6809..24fca63 100644 --- a/src/main.zig +++ b/src/main.zig @@ -126,7 +126,7 @@ const UiState = struct { bigclock_buf: [32:0]u8, custom_binds: std.ArrayList(CustomBindLabel), custom_info: std.ArrayList(CustomInfoLabel), - tty_cache: [256]?u8, + tty_cache: [std.math.maxInt(u8)]?u8, }; var shutdown = false; @@ -135,6 +135,7 @@ var restart = false; pub fn main(init: std.process.Init) !void { var state: UiState = undefined; + state.tty_cache = @splat(null); state.io = init.io; var stderr_buffer: [128]u8 = undefined; @@ -187,8 +188,6 @@ pub fn main(init: std.process.Init) !void { state.saved_users = SavedUsers.init(); defer state.saved_users.deinit(state.allocator); - state.tty_cache = @splat(null); - var config_parent_path: []const u8 = build_options.config_directory ++ "/ly"; if (maybe_res) |*res| { if (res.args.help != 0) { @@ -324,36 +323,21 @@ pub fn main(init: std.process.Init) !void { while (reader.seek < reader.buffer.len) { var line = reader.takeDelimiterInclusive('\n') catch break; - if (std.mem.startsWith(u8, line, "ly/tty")) { - line = line[6..]; - var entry = std.mem.splitScalar(u8, line[0..(line.len - 1)], ':'); - const tty_num_str = entry.next() orelse continue; - const username = entry.next() orelse continue; + var user = std.mem.splitScalar(u8, line[0..(line.len - 1)], ':'); + const username = user.next() orelse continue; + const session_index_str = user.next() orelse continue; - const tty_num = std.fmt.parseInt(usize, tty_num_str, 10) catch continue; + const session_index = std.fmt.parseInt(usize, session_index_str, 10) catch continue; - // Cache tty users only if they exist - for (usernames.items, 0..) |u, u_index| { - if (std.mem.eql(u8, u, username)) { - if (tty_num < std.math.maxInt(@TypeOf(state.active_tty))) - state.tty_cache[tty_num] = @intCast(u_index); - } - } - } else { - var user = std.mem.splitScalar(u8, line[0..(line.len - 1)], ':'); - const username = user.next() orelse continue; - const session_index_str = user.next() orelse continue; - - const session_index = std.fmt.parseInt(usize, session_index_str, 10) catch continue; - - try state.saved_users.user_list.append(state.allocator, .{ - .username = try state.allocator.dupe(u8, username), - .session_index = session_index, - .first_run = false, - .allocated_username = true, - }); - } + try state.saved_users.user_list.append(state.allocator, .{ + .username = try state.allocator.dupe(u8, username), + .session_index = session_index, + .first_run = false, + .allocated_username = true, + }); } + + updateTtyCache(&state, .{ .usernames = usernames.items }) catch break :read_save_file; } var log_file_buffer: [1024]u8 = undefined; @@ -1128,6 +1112,8 @@ pub fn main(init: std.process.Init) !void { // Skip if autologin is active to prevent overriding autologin session var default_input = state.config.default_input; + const min_session_index = state.session.label.list.items.len - 1; + if (state.config.save_file_dir != null and !state.is_autologin) { if (state.login_text) |box| { if (state.saved_username) |username| { @@ -1139,19 +1125,23 @@ pub fn main(init: std.process.Init) !void { for (state.saved_users.user_list.items) |user| { if (std.mem.eql(u8, username, user.username)) { - state.session.label.current = @min(user.session_index, state.session.label.list.items.len - 1); + state.session.label.current = @min(user.session_index, min_session_index); break; } } } - } else if (state.tty_cache[state.active_tty]) |tty_index| { - state.login.?.label.current = tty_index; - state.session.label.current = @min(state.login.?.label.list.items[tty_index].session_index.*, state.session.label.list.items.len - 1); - } else if (state.saved_users.last_username_index) |last_index| load_last_user: { - // If the saved index isn't valid, bail out - if (last_index >= state.saved_users.user_list.items.len) break :load_last_user; + } else if (state.tty_cache[state.active_tty]) |user_index| { + const user_session_index = state.login.?.label.list.items[user_index].session_index.*; - const user = state.saved_users.user_list.items[last_index]; + state.login.?.label.current = user_index; + state.session.label.current = @min(user_session_index, min_session_index); + } else if (state.saved_users.last_username_index) |last_user_index| load_last_user: { + const saved_users = state.saved_users.user_list.items; + + // If the saved index isn't valid, bail out + if (last_user_index >= saved_users.len) break :load_last_user; + + const user = saved_users[last_user_index]; // Find user with saved name, and switch over to it // If it doesn't exist (anymore), we don't change the value @@ -1164,7 +1154,7 @@ pub fn main(init: std.process.Init) !void { default_input = .password; - state.session.label.current = @min(user.session_index, state.session.label.list.items.len - 1); + state.session.label.current = @min(user.session_index, min_session_index); } } @@ -1527,62 +1517,40 @@ fn authenticate(ptr: *anyopaque) !bool { ) catch {}; const current: u8 = @intCast(state.login.?.label.current); + const login_users = state.login.?.label.list.items; - var file = std.Io.Dir.cwd().createFile(state.io, state.save_path, .{}) catch |err| { + // Try to update the local tty cache before overwriting, + // since multiple instances can be running + updateTtyCache(state, .{ .user_list = login_users }) catch |err| { state.log_file.err( state.io, "sys", - "failed to create save file: {s}", + "failed to update cache: {s}", .{@errorName(err)}, + ) catch {}; + }; + + var save_file = std.Io.Dir.cwd().createFile(state.io, state.save_path, .{}) catch |err| { + state.log_file.err( + state.io, + "sys", + "failed to create save file: {s} {s}", + .{ @errorName(err), state.save_path }, ) catch break :save_last_settings; break :save_last_settings; }; - defer file.close(state.io); + defer save_file.close(state.io); var file_buffer: [256]u8 = undefined; - var file_writer = file.writer(state.io, &file_buffer); + var file_writer = save_file.writer(state.io, &file_buffer); var writer = &file_writer.interface; - // Update the local tty cache before overwriting - // since multiple instances can be running - // - // A little code dupe but the end comparison - // is on a different type - update_tty_cache: { - var save_file = std.Io.Dir.cwd().openFile(state.io, state.save_path, .{}) catch break :update_tty_cache; - defer save_file.close(state.io); - - var file_reader = save_file.reader(state.io, &file_buffer); - var reader = &file_reader.interface; - - while (reader.seek < reader.buffer.len) { - var line = reader.takeDelimiterInclusive('\n') catch break; - - if (std.mem.startsWith(u8, line, "ly/tty")) { - line = line[6..]; - var entry = std.mem.splitScalar(u8, line[0..(line.len - 1)], ':'); - const tty_num_str = entry.next() orelse continue; - const username = entry.next() orelse continue; - - const tty_num = std.fmt.parseInt(usize, tty_num_str, 10) catch continue; - - // Cache tty users only if they exist - for (state.login.?.label.list.items, 0..) |u, u_index| { - if (std.mem.eql(u8, u.name, username)) { - if (tty_num < std.math.maxInt(@TypeOf(state.active_tty))) - state.tty_cache[tty_num] = @intCast(u_index); - } - } - } - } - } - if (state.login_text) |box| { try writer.print("0-{s}\n", .{box.text.items}); } else { try writer.print("{d}\n", .{current}); } - for (state.login.?.label.list.items) |user| { + for (login_users) |user| { try writer.print("{s}:{d}\n", .{ user.name, user.session_index.* }); } @@ -1591,7 +1559,7 @@ fn authenticate(ptr: *anyopaque) !bool { if (maybe_user_index) |user_index| { // Posix usernames can't contain a '/' // And, well, if your username is this string... - try writer.print("ly/tty{d}:{s}\n", .{ tty_num, state.login.?.label.list.items[user_index].name }); + try writer.print("ly/tty{d}:{s}\n", .{ tty_num, login_users[user_index].name }); } } try writer.flush(); @@ -2631,3 +2599,50 @@ fn getAuthErrorMsg(err: anyerror, lang: Lang) []const u8 { else => @errorName(err), }; } + +// Updates the UiState's tty_cache using the save file. +// Matched against the current truthful user list to ensure it's a valid index. +fn updateTtyCache(state: *UiState, real_users: union(enum) { user_list: []UserList.User, usernames: [][]const u8 }) !void { + var save_file = try std.Io.Dir.cwd().openFile(state.io, state.save_path, .{}); + defer save_file.close(state.io); + + var file_buffer: [256]u8 = undefined; + var file_reader = save_file.reader(state.io, &file_buffer); + var reader = &file_reader.interface; + + while (reader.seek < reader.buffer.len) { + var line = reader.takeDelimiterInclusive('\n') catch break; + + if (std.mem.startsWith(u8, line, "ly/tty")) { + line = line["ly/tty".len..]; + var entry = std.mem.splitScalar(u8, line[0..(line.len - 1)], ':'); + const tty_num_str = entry.next() orelse continue; + const saved_username = entry.next() orelse continue; + + const tty_num = std.fmt.parseInt(usize, tty_num_str, 10) catch continue; + + switch (real_users) { + .usernames => |usernames| { + for (usernames, 0..) |username, u_index| { + if (std.mem.eql(u8, username, saved_username)) { + if (tty_num < std.math.maxInt(u8)) { + state.tty_cache[tty_num] = @intCast(u_index); + } + break; + } + } + }, + .user_list => |user_list| { + for (user_list, 0..) |user, u_index| { + if (std.mem.eql(u8, user.name, saved_username)) { + if (tty_num < std.math.maxInt(u8)) { + state.tty_cache[tty_num] = @intCast(u_index); + } + break; + } + } + }, + } + } + } +}