Remember last session for each user (closes #619)

Signed-off-by: AnErrupTion <anerruption@disroot.org>
This commit is contained in:
AnErrupTion
2025-10-14 21:05:54 +02:00
parent aef1dd9c1a
commit b3f1e91cf6
9 changed files with 324 additions and 92 deletions

View File

@@ -21,7 +21,8 @@ const InfoLine = @import("tui/components/InfoLine.zig");
const UserList = @import("tui/components/UserList.zig");
const Config = @import("config/Config.zig");
const Lang = @import("config/Lang.zig");
const Save = @import("config/Save.zig");
const OldSave = @import("config/OldSave.zig");
const SavedUsers = @import("config/SavedUsers.zig");
const migrator = @import("config/migrator.zig");
const SharedError = @import("SharedError.zig");
const UidRange = @import("UidRange.zig");
@@ -118,11 +119,14 @@ pub fn main() !void {
var config: Config = undefined;
var lang: Lang = undefined;
var save: Save = undefined;
var old_save_file_exists = false;
var maybe_config_load_error: ?anyerror = null;
var can_get_lock_state = true;
var can_draw_clock = true;
var saved_users = SavedUsers.init();
defer saved_users.deinit(allocator);
if (res.args.help != 0) {
try clap.help(stderr, clap.Help, &params, .{});
@@ -143,13 +147,15 @@ pub fn main() !void {
var lang_ini = Ini(Lang).init(allocator);
defer lang_ini.deinit();
var save_ini = Ini(Save).init(allocator);
defer save_ini.deinit();
var old_save_ini = ini.Ini(OldSave).init(allocator);
defer old_save_ini.deinit();
var save_path: []const u8 = build_options.config_directory ++ "/ly/save.ini";
var save_path: []const u8 = build_options.config_directory ++ "/ly/save.txt";
var old_save_path: []const u8 = build_options.config_directory ++ "/ly/save.ini";
var save_path_alloc = false;
defer {
if (save_path_alloc) allocator.free(save_path);
if (save_path_alloc) allocator.free(old_save_path);
}
const comment_characters = "#";
@@ -178,18 +184,9 @@ pub fn main() !void {
}) catch Lang{};
if (config.save) {
save_path = try std.fmt.allocPrint(allocator, "{s}{s}save.ini", .{ s, trailing_slash });
save_path = try std.fmt.allocPrint(allocator, "{s}{s}save.txt", .{ s, trailing_slash });
old_save_path = try std.fmt.allocPrint(allocator, "{s}{s}save.ini", .{ s, trailing_slash });
save_path_alloc = true;
var user_buf: [32]u8 = undefined;
save = save_ini.readFileToStruct(save_path, .{
.fieldHandler = null,
.comment_characters = comment_characters,
}) catch migrator.tryMigrateSaveFile(&user_buf);
}
if (maybe_config_load_error == null) {
migrator.lateConfigFieldHandler(&config);
}
} else {
const config_path = build_options.config_directory ++ "/ly/config.ini";
@@ -210,17 +207,47 @@ pub fn main() !void {
.fieldHandler = null,
.comment_characters = comment_characters,
}) catch Lang{};
}
if (config.save) {
var user_buf: [32]u8 = undefined;
save = save_ini.readFileToStruct(save_path, .{
.fieldHandler = null,
.comment_characters = comment_characters,
}) catch migrator.tryMigrateSaveFile(&user_buf);
}
if (maybe_config_load_error == null) {
migrator.lateConfigFieldHandler(&config);
}
if (maybe_config_load_error == null) {
migrator.lateConfigFieldHandler(&config);
var usernames = try getAllUsernames(allocator, config.login_defs_path);
defer {
for (usernames.items) |username| allocator.free(username);
usernames.deinit(allocator);
}
if (config.save) read_save_file: {
old_save_file_exists = migrator.tryMigrateIniSaveFile(allocator, &old_save_ini, old_save_path, &saved_users, usernames.items) catch break :read_save_file;
// Don't read the new save file if the old one still exists
if (old_save_file_exists) break :read_save_file;
var save_file = std.fs.cwd().openFile(save_path, .{}) catch break :read_save_file;
defer save_file.close();
var file_buffer: [256]u8 = undefined;
var file_reader = save_file.reader(&file_buffer);
var reader = &file_reader.interface;
const last_username_index_str = reader.takeDelimiterInclusive('\n') catch break :read_save_file;
saved_users.last_username_index = std.fmt.parseInt(usize, last_username_index_str[0..(last_username_index_str.len - 1)], 10) catch break :read_save_file;
while (reader.seek < reader.buffer.len) {
const line = reader.takeDelimiterInclusive('\n') catch break;
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 saved_users.user_list.append(allocator, .{
.username = username,
.session_index = session_index,
});
}
}
@@ -345,7 +372,9 @@ pub fn main() !void {
try log_writer.print("failed to set numlock: {s}\n", .{@errorName(err)});
};
var session = Session.init(allocator, &buffer);
var login: UserList = undefined;
var session = Session.init(allocator, &buffer, &login);
defer session.deinit();
addOtherEnvironment(&session, lang, .shell, null) catch |err| {
@@ -396,12 +425,6 @@ pub fn main() !void {
try crawl(&session, lang, dir, .custom);
}
var usernames = try getAllUsernames(allocator, config.login_defs_path);
defer {
for (usernames.items) |username| allocator.free(username);
usernames.deinit(allocator);
}
if (usernames.items.len == 0) {
// If we have no usernames, simply add an error to the info line.
// This effectively means you can't login, since there would be no local
@@ -411,7 +434,7 @@ pub fn main() !void {
try log_writer.writeAll("no users found\n");
}
var login = try UserList.init(allocator, &buffer, usernames);
login = try UserList.init(allocator, &buffer, usernames, &saved_users, &session);
defer login.deinit();
var password = Text.init(allocator, &buffer, true, config.asterisk);
@@ -422,23 +445,21 @@ pub fn main() !void {
// Load last saved username and desktop selection, if any
if (config.save) {
if (save.user) |user| {
if (saved_users.last_username_index) |index| {
const user = saved_users.user_list.items[index];
// Find user with saved name, and switch over to it
// If it doesn't exist (anymore), we don't change the value
// Note that we could instead save the username index, but migrating
// from the raw username to an index is non-trivial and I'm lazy :P
for (usernames.items, 0..) |username, i| {
if (std.mem.eql(u8, username, user)) {
if (std.mem.eql(u8, username, user.username)) {
login.label.current = i;
break;
}
}
active_input = .password;
}
if (save.session_index) |session_index| {
if (session_index < session.label.list.items.len) session.label.current = session_index;
if (user.session_index < session.label.list.items.len) session.label.current = user.session_index;
}
}
@@ -853,22 +874,33 @@ pub fn main() !void {
_ = termbox.tb_present();
if (config.save) save_last_settings: {
var file = std.fs.cwd().createFile(save_path, .{}) catch break :save_last_settings;
// It isn't worth cluttering the code with precise error
// handling, so let's just report a generic error message,
// that should be good enough for debugging anyway.
errdefer log_writer.writeAll("failed to save current user data\n") catch {};
var file = std.fs.cwd().createFile(save_path, .{}) catch |err| {
log_writer.print("failed to create save file: {s}\n", .{@errorName(err)}) catch break :save_last_settings;
break :save_last_settings;
};
defer file.close();
var file_buffer: [64]u8 = undefined;
var file_buffer: [256]u8 = undefined;
var file_writer = file.writer(&file_buffer);
var writer = &file_writer.interface;
const save_data = Save{
.user = login.getCurrentUser(),
.session_index = session.label.current,
};
ini.writeFromStruct(save_data, writer, null, .{}) catch break :save_last_settings;
try writer.print("{d}\n", .{login.label.current});
for (saved_users.user_list.items) |user| {
try writer.print("{s}:{d}\n", .{ user.username, user.session_index });
}
try writer.flush();
// Delete previous save file if it exists
if (migrator.maybe_save_file) |path| std.fs.cwd().deleteFile(path) catch {};
if (migrator.maybe_save_file) |path| {
std.fs.cwd().deleteFile(path) catch {};
} else if (old_save_file_exists) {
std.fs.cwd().deleteFile(old_save_path) catch {};
}
}
var shared_err = try SharedError.init();
@@ -877,7 +909,7 @@ pub fn main() !void {
{
session_pid = try std.posix.fork();
if (session_pid == 0) {
const current_environment = session.label.list.items[session.label.current];
const current_environment = session.label.list.items[session.label.current].environment;
const auth_options = auth.AuthOptions{
.tty = active_tty,
.service_name = config.service_name,
@@ -898,7 +930,7 @@ pub fn main() !void {
};
std.posix.sigaction(std.posix.SIG.CHLD, &tty_control_transfer_act, null);
auth.authenticate(allocator, log_writer, auth_options, current_environment, login.getCurrentUser(), password.text.items) catch |err| {
auth.authenticate(allocator, log_writer, auth_options, current_environment, login.getCurrentUsername(), password.text.items) catch |err| {
shared_err.writeError(err);
std.process.exit(1);
};
@@ -1026,7 +1058,7 @@ fn addOtherEnvironment(session: *Session, lang: Lang, display_server: DisplaySer
.name = name,
.xdg_session_desktop = null,
.xdg_desktop_names = null,
.cmd = exec orelse "",
.cmd = exec,
.specifier = lang.other,
.display_server = display_server,
.is_terminal = display_server == .shell,