From 5a9cc881fe5d7085904be573075f05787c351beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=A2=E3=82=B7=E3=83=A5?= <120780645+Kawaii-Ash@users.noreply.github.com> Date: Wed, 1 May 2024 17:46:26 +0100 Subject: [PATCH] Zig 0.12.0 and more! (#599) * less alloc, update migrator, get DesktopNames from .desktop * small cleanup * Update zigini to improve compatibility with old config * Code improvements * Update to zig version 0.12.0 * Some fixes * tiny changes * remove useless comment * migrator changes, and small things * set XDG env vars differently * free memory on error when appending environments --- build.zig | 6 +- build.zig.zon | 9 +- changelog.md | 4 +- res/config.ini | 3 + res/lang/en.ini | 1 + src/SharedError.zig | 4 +- src/auth.zig | 306 ++++++++++++++------------------- src/bigclock.zig | 2 +- src/config/Config.zig | 6 +- src/config/Lang.zig | 5 +- src/config/migrator.zig | 36 ++-- src/interop.zig | 57 +----- src/main.zig | 178 +++++++++---------- src/tui/TerminalBuffer.zig | 1 - src/tui/components/Desktop.zig | 78 +++++++-- src/tui/components/Text.zig | 1 - 16 files changed, 321 insertions(+), 376 deletions(-) diff --git a/build.zig b/build.zig index 6ac8a4e..13c5140 100644 --- a/build.zig +++ b/build.zig @@ -35,12 +35,12 @@ pub fn build(b: *std.Build) void { }); const zigini = b.dependency("zigini", .{ .target = target, .optimize = optimize }); - exe.addModule("zigini", zigini.module("zigini")); + exe.root_module.addImport("zigini", zigini.module("zigini")); - exe.addOptions("build_options", build_options); + exe.root_module.addOptions("build_options", build_options); const clap = b.dependency("clap", .{ .target = target, .optimize = optimize }); - exe.addModule("clap", clap.module("clap")); + exe.root_module.addImport("clap", clap.module("clap")); exe.linkSystemLibrary("pam"); exe.linkSystemLibrary("xcb"); diff --git a/build.zig.zon b/build.zig.zon index d1e55cf..b7606a4 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -3,12 +3,13 @@ .version = "1.0.0", .dependencies = .{ .clap = .{ - .url = "https://github.com/Hejsil/zig-clap/archive/f49b94700e0761b7514abdca0e4f0e7f3f938a93.tar.gz", - .hash = "1220f48518ce22882e102255ed3bcdb7aeeb4891f50b2cdd3bd74b5b2e24d3149ba2", + .url = "https://github.com/Hejsil/zig-clap/archive/8c98e6404b22aafc0184e999d8f068b81cc22fa1.tar.gz", + .hash = "122014e73fd712190e109950837b97f6143f02d7e2b6986e1db70b6f4aadb5ba6a0d", }, .zigini = .{ - .url = "https://github.com/Kawaii-Ash/zigini/archive/91f47e46591982fc559afa3248749c1d29a0fa2a.tar.gz", - .hash = "12209908f2773f730fbca024c80dc7f48dce15a6527b2387f3768968f5bae0d3931e", + .url = "https://github.com/Kawaii-Ash/zigini/archive/a5b5caf43f0a0246a242df4aebd00a0649deebad.tar.gz", + .hash = "12202e097a54cbb7c002a2a5b3a3435939fa3902cd14308b7b66ab710e3d88d76e94", }, }, + .paths = .{""}, } diff --git a/changelog.md b/changelog.md index c1bf64d..2a1be49 100644 --- a/changelog.md +++ b/changelog.md @@ -17,6 +17,7 @@ Note: `sleep_cmd` is unset by default, meaning it's hidden and has no effect. + xinitrc can be set to null to hide it. + `blank_password` has been renamed to `clear_password`. ++ `save_file` has been deprecated and will be removed in a future version. ### Removals @@ -24,7 +25,8 @@ Note: `sleep_cmd` is unset by default, meaning it's hidden and has no effect. ## Save File -The save file is now in .ini format. +The save file is now in .ini format and stored in the same directory as the config. +Older save files will be migrated to the new format. Example: diff --git a/res/config.ini b/res/config.ini index 9d8fcd6..65c6baa 100644 --- a/res/config.ini +++ b/res/config.ini @@ -76,6 +76,9 @@ load = true # Save the current desktop and login as defaults save = true +# Deprecated - Will be removed in a future version +# New save files are now loaded from the same directory as the config +# Currently used to migrate old save files to the new version # File in which to save and load the default desktop and login save_file = /etc/ly/save diff --git a/res/lang/en.ini b/res/lang/en.ini index 1dc98d4..766dc86 100644 --- a/res/lang/en.ini +++ b/res/lang/en.ini @@ -29,6 +29,7 @@ err_perm_dir = failed to change current directory err_perm_group = failed to downgrade group permissions err_perm_user = failed to downgrade user permissions err_pwnam = failed to get user info +err_unknown = an unknown error occurred err_user_gid = failed to set user GID err_user_init = failed to initialize user err_user_uid = failed to set user UID diff --git a/src/SharedError.zig b/src/SharedError.zig index fa02100..dcf1f79 100644 --- a/src/SharedError.zig +++ b/src/SharedError.zig @@ -12,13 +12,13 @@ const SharedError = @This(); data: []align(std.mem.page_size) u8, pub fn init() !SharedError { - const data = try std.os.mmap(null, @sizeOf(ErrorHandler), std.os.PROT.READ | std.os.PROT.WRITE, std.os.MAP.SHARED | std.os.MAP.ANONYMOUS, -1, 0); + const data = try std.posix.mmap(null, @sizeOf(ErrorHandler), std.posix.PROT.READ | std.posix.PROT.WRITE, .{ .TYPE = .SHARED, .ANONYMOUS = true }, -1, 0); return .{ .data = data }; } pub fn deinit(self: *SharedError) void { - defer std.os.munmap(self.data); + std.posix.munmap(self.data); } pub fn writeError(self: SharedError, err: anyerror) void { diff --git a/src/auth.zig b/src/auth.zig index b463786..26de2cf 100644 --- a/src/auth.zig +++ b/src/auth.zig @@ -10,52 +10,35 @@ const utmp = interop.utmp; const Utmp = utmp.utmp; const SharedError = @import("SharedError.zig"); -var xorg_pid: std.os.pid_t = 0; +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); } -var child_pid: std.os.pid_t = 0; +var child_pid: std.posix.pid_t = 0; pub fn sessionSignalHandler(i: c_int) callconv(.C) void { if (child_pid > 0) _ = std.c.kill(child_pid, i); } -pub fn authenticate(allocator: Allocator, config: Config, desktop: Desktop, login: Text, password: *Text) !void { +pub fn authenticate(config: Config, desktop: Desktop, login: [:0]const u8, password: [:0]const u8) !void { var tty_buffer: [2]u8 = undefined; const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{config.tty}); const current_environment = desktop.environments.items[desktop.current]; // Set the XDG environment variables setXdgSessionEnv(current_environment.display_server); - if (current_environment.entry_ini) |entry| { - try setXdgEnv(allocator, tty_str, entry.DesktopNames); - } else { - try setXdgEnv(allocator, tty_str, ""); - } + try setXdgEnv(tty_str, current_environment.xdg_session_desktop, current_environment.xdg_desktop_names orelse ""); // Open the PAM session - const login_text_z = try allocator.dupeZ(u8, login.text.items); - defer allocator.free(login_text_z); - - const password_text_z = try allocator.dupeZ(u8, password.text.items); - defer allocator.free(password_text_z); - - var credentials = try allocator.allocSentinel([*c]const u8, 2, 0); - defer allocator.free(credentials); - - credentials[0] = login_text_z.ptr; - credentials[1] = password_text_z.ptr; + var credentials = [_:null]?[*:0]const u8{ login, password }; const conv = interop.pam.pam_conv{ .conv = loginConv, - .appdata_ptr = @ptrCast(credentials.ptr), + .appdata_ptr = @ptrCast(&credentials), }; var handle: ?*interop.pam.pam_handle = undefined; - const service_name_z = try allocator.dupeZ(u8, config.service_name); - defer allocator.free(service_name_z); - - var status = interop.pam.pam_start(service_name_z.ptr, null, &conv, &handle); + var status = interop.pam.pam_start(config.service_name.ptr, null, &conv, &handle); if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); // Do the PAM routine @@ -71,124 +54,50 @@ pub fn authenticate(allocator: Allocator, config: Config, desktop: Desktop, logi status = interop.pam.pam_open_session(handle, 0); if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); - // Get password structure from username - const maybe_pwd = interop.getpwnam(login_text_z.ptr); - interop.endpwent(); + var pwd: *interop.passwd = undefined; + { + defer interop.endpwent(); - if (maybe_pwd == null) return error.GetPasswordNameFailed; - const pwd = maybe_pwd.?; + // Get password structure from username + pwd = interop.getpwnam(login.ptr) orelse return error.GetPasswordNameFailed; + } // Set user shell if it hasn't already been set if (pwd.pw_shell[0] == 0) { interop.setusershell(); - defer interop.endusershell(); - - const shell = interop.getusershell(); - - if (shell[0] != 0) { - var index: usize = 0; - - while (true) : (index += 1) { - const char = shell[index]; - pwd.pw_shell[index] = char; - - if (char == 0) break; - } - } + pwd.pw_shell = interop.getusershell(); + interop.endusershell(); } var shared_err = try SharedError.init(); defer shared_err.deinit(); - child_pid = try std.os.fork(); + child_pid = try std.posix.fork(); if (child_pid == 0) { - // Set the user information - status = interop.initgroups(pwd.pw_name, pwd.pw_gid); - if (status != 0) { - shared_err.writeError(error.GroupInitializationFailed); - std.os.exit(1); - } - - status = std.c.setgid(pwd.pw_gid); - if (status != 0) { - shared_err.writeError(error.SetUserGidFailed); - std.os.exit(1); - } - - status = std.c.setuid(pwd.pw_uid); - if (status != 0) { - shared_err.writeError(error.SetUserUidFailed); - std.os.exit(1); - } - - // Set up the environment - initEnv(allocator, pwd, config.path) catch |e| { + startSession(config, pwd, handle, current_environment) catch |e| { shared_err.writeError(e); - std.os.exit(1); + std.process.exit(1); }; - - // Set the PAM variables - const pam_env_vars = interop.pam.pam_getenvlist(handle); - var index: usize = 0; - - while (true) : (index += 1) { - const pam_env_var = pam_env_vars[index]; - if (pam_env_var == null) break; - - _ = interop.putenv(pam_env_var); - } - - // Execute what the user requested - status = interop.chdir(pwd.pw_dir); - if (status != 0) { - shared_err.writeError(error.ChangeDirectoryFailed); - std.os.exit(1); - } - - resetTerminal(allocator, pwd.pw_shell, config.term_reset_cmd) catch |e| { - shared_err.writeError(e); - std.os.exit(1); - }; - - switch (current_environment.display_server) { - .wayland => executeWaylandCmd(pwd.pw_shell, config.wayland_cmd, current_environment.cmd) catch |e| { - shared_err.writeError(e); - std.os.exit(1); - }, - .shell => executeShellCmd(pwd.pw_shell), - .xinitrc, .x11 => { - var vt_buf: [5]u8 = undefined; - const vt = std.fmt.bufPrint(&vt_buf, "vt{d}", .{config.tty}) catch |e| { - shared_err.writeError(e); - std.os.exit(1); - }; - executeX11Cmd(pwd.pw_shell, pwd.pw_dir, config, current_environment.cmd, vt) catch |e| { - shared_err.writeError(e); - std.os.exit(1); - }; - }, - } - - std.os.exit(0); + std.process.exit(0); } var entry: Utmp = std.mem.zeroes(Utmp); addUtmpEntry(&entry, pwd.pw_name, child_pid) catch {}; // If we receive SIGTERM, forward it to child_pid - const act = std.os.Sigaction{ + const act = std.posix.Sigaction{ .handler = .{ .handler = &sessionSignalHandler }, - .mask = std.os.empty_sigset, + .mask = std.posix.empty_sigset, .flags = 0, }; - try std.os.sigaction(std.os.SIG.TERM, &act, null); + try std.posix.sigaction(std.posix.SIG.TERM, &act, null); // Wait for the session to stop - _ = std.os.waitpid(child_pid, 0); + _ = std.posix.waitpid(child_pid, 0); removeUtmpEntry(&entry); - try resetTerminal(allocator, pwd.pw_shell, config.term_reset_cmd); + try resetTerminal(pwd.pw_shell, config.term_reset_cmd); // Close the PAM session status = interop.pam.pam_close_session(handle, 0); @@ -203,8 +112,51 @@ pub fn authenticate(allocator: Allocator, config: Config, desktop: Desktop, logi if (shared_err.readError()) |err| return err; } -fn initEnv(allocator: Allocator, pwd: *interop.passwd, path: ?[]const u8) !void { - const term_env = std.os.getenv("TERM"); +fn startSession( + config: Config, + pwd: *interop.passwd, + handle: ?*interop.pam.pam_handle, + current_environment: Desktop.Environment, +) !void { + var status: c_int = 0; + status = interop.initgroups(pwd.pw_name, pwd.pw_gid); + if (status != 0) return error.GroupInitializationFailed; + + std.posix.setgid(pwd.pw_gid) catch return error.SetUserGidFailed; + std.posix.setuid(pwd.pw_uid) catch return error.SetUserUidFailed; + + // Set up the environment + try initEnv(pwd, config.path); + + // Set the PAM variables + const pam_env_vars = interop.pam.pam_getenvlist(handle); + + var index: usize = 0; + while (true) : (index += 1) { + const pam_env_var = pam_env_vars[index]; + if (pam_env_var == null) break; + + _ = interop.putenv(pam_env_var); + } + + // Execute what the user requested + std.posix.chdirZ(pwd.pw_dir) catch return error.ChangeDirectoryFailed; + + try resetTerminal(pwd.pw_shell, config.term_reset_cmd); + + switch (current_environment.display_server) { + .wayland => try executeWaylandCmd(pwd.pw_shell, config.wayland_cmd, current_environment.cmd), + .shell => try executeShellCmd(pwd.pw_shell), + .xinitrc, .x11 => { + 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); + }, + } +} + +fn initEnv(pwd: *interop.passwd, path_env: ?[:0]const u8) !void { + const term_env = std.posix.getenv("TERM"); if (term_env) |term| _ = interop.setenv("TERM", term, 1); _ = interop.setenv("HOME", pwd.pw_dir, 1); @@ -213,11 +165,8 @@ fn initEnv(allocator: Allocator, pwd: *interop.passwd, path: ?[]const u8) !void _ = interop.setenv("USER", pwd.pw_name, 1); _ = interop.setenv("LOGNAME", pwd.pw_name, 1); - if (path != null) { - const path_z = try allocator.dupeZ(u8, path.?); - defer allocator.free(path_z); - - const status = interop.setenv("PATH", path_z, 1); + if (path_env) |path| { + const status = interop.setenv("PATH", path, 1); if (status != 0) return error.SetPathFailed; } } @@ -230,22 +179,16 @@ fn setXdgSessionEnv(display_server: enums.DisplayServer) void { }, 0); } -fn setXdgEnv(allocator: Allocator, tty_str: [:0]u8, desktop_name: []const u8) !void { +fn setXdgEnv(tty_str: [:0]u8, desktop_name: [:0]const u8, xdg_desktop_names: [:0]const u8) !void { const uid = interop.getuid(); var uid_buffer: [10 + @sizeOf(u32) + 1]u8 = undefined; const uid_str = try std.fmt.bufPrintZ(&uid_buffer, "/run/user/{d}", .{uid}); - if (desktop_name.len > 0) { - const desktop_name_z = try allocator.dupeZ(u8, desktop_name); - defer allocator.free(desktop_name_z); - - _ = interop.setenv("XDG_CURRENT_DESKTOP", desktop_name_z.ptr, 0); - _ = interop.setenv("XDG_SESSION_DESKTOP", desktop_name_z.ptr, 0); - } - + _ = interop.setenv("XDG_CURRENT_DESKTOP", xdg_desktop_names.ptr, 0); _ = interop.setenv("XDG_RUNTIME_DIR", uid_str.ptr, 0); _ = interop.setenv("XDG_SESSION_CLASS", "user", 0); _ = interop.setenv("XDG_SESSION_ID", "1", 0); + _ = interop.setenv("XDG_SESSION_DESKTOP", desktop_name.ptr, 0); _ = interop.setenv("XDG_SEAT", "seat0", 0); _ = interop.setenv("XDG_VTNR", tty_str.ptr, 0); } @@ -298,18 +241,15 @@ fn loginConv( return status; } -fn resetTerminal(allocator: Allocator, shell: [*:0]const u8, term_reset_cmd: []const u8) !void { - const term_reset_cmd_z = try allocator.dupeZ(u8, term_reset_cmd); - defer allocator.free(term_reset_cmd_z); - - const pid = try std.os.fork(); - +fn resetTerminal(shell: [*:0]const u8, term_reset_cmd: [:0]const u8) !void { + const pid = try std.posix.fork(); if (pid == 0) { - _ = interop.execl(shell, shell, "-c", term_reset_cmd_z.ptr, @as([*c]const u8, 0)); - std.os.exit(0); + const args = [_:null]?[*:0]const u8{ shell, "-c", term_reset_cmd }; + std.posix.execveZ(shell, &args, std.c.environ) catch {}; + std.process.exit(1); } - _ = std.os.waitpid(pid, 0); + _ = std.posix.waitpid(pid, 0); } fn getFreeDisplay() !u8 { @@ -317,7 +257,7 @@ fn getFreeDisplay() !u8 { var i: u8 = 0; while (i < 200) : (i += 1) { const xlock = try std.fmt.bufPrint(&buf, "/tmp/.X{d}-lock", .{i}); - std.os.access(xlock, std.os.F_OK) catch break; + std.posix.access(xlock, std.posix.F_OK) catch break; } return i; } @@ -340,17 +280,17 @@ fn getXPid(display_num: u8) !i32 { fn createXauthFile(pwd: [:0]const u8) ![:0]const u8 { var xauth_buf: [100]u8 = undefined; var xauth_dir: [:0]const u8 = undefined; - var xdg_rt_dir = std.os.getenv("XDG_RUNTIME_DIR"); + const xdg_rt_dir = std.posix.getenv("XDG_RUNTIME_DIR"); var xauth_file: []const u8 = "lyxauth"; if (xdg_rt_dir == null) { - const xdg_cfg_home = std.os.getenv("XDG_CONFIG_HOME"); + const xdg_cfg_home = std.posix.getenv("XDG_CONFIG_HOME"); var sb: std.c.Stat = undefined; if (xdg_cfg_home == null) { xauth_dir = try std.fmt.bufPrintZ(&xauth_buf, "{s}/.config", .{pwd}); _ = std.c.stat(xauth_dir, &sb); - const mode = sb.mode & std.os.S.IFMT; - if (mode == std.os.S.IFDIR) { + const mode = sb.mode & std.posix.S.IFMT; + if (mode == std.posix.S.IFDIR) { xauth_dir = try std.fmt.bufPrintZ(&xauth_buf, "{s}/ly", .{xauth_dir}); } else { xauth_dir = pwd; @@ -361,9 +301,9 @@ fn createXauthFile(pwd: [:0]const u8) ![:0]const u8 { } _ = std.c.stat(xauth_dir, &sb); - const mode = sb.mode & std.os.S.IFMT; - if (mode != std.os.S.IFDIR) { - std.os.mkdir(xauth_dir, 777) catch { + const mode = sb.mode & std.posix.S.IFMT; + if (mode != std.posix.S.IFDIR) { + std.posix.mkdir(xauth_dir, 777) catch { xauth_dir = pwd; xauth_file = ".lyxauth"; }; @@ -379,7 +319,7 @@ fn createXauthFile(pwd: [:0]const u8) ![:0]const u8 { var buf: [256]u8 = undefined; const xauthority: [:0]u8 = try std.fmt.bufPrintZ(&buf, "{s}/{s}", .{ trimmed_xauth_dir, xauth_file }); - const file = try std.fs.createFileAbsolute(xauthority, .{}); + const file = try std.fs.createFileAbsoluteZ(xauthority, .{}); file.close(); return xauthority; @@ -387,86 +327,90 @@ fn createXauthFile(pwd: [:0]const u8) ![:0]const u8 { fn xauth(display_name: [:0]u8, shell: [*:0]const u8, pw_dir: [*:0]const u8, xauth_cmd: []const u8, mcookie_cmd: []const u8) !void { var pwd_buf: [100]u8 = undefined; - var pwd: [:0]u8 = try std.fmt.bufPrintZ(&pwd_buf, "{s}", .{pw_dir}); + const pwd = try std.fmt.bufPrintZ(&pwd_buf, "{s}", .{pw_dir}); const xauthority = try createXauthFile(pwd); _ = interop.setenv("XAUTHORITY", xauthority, 1); _ = interop.setenv("DISPLAY", display_name, 1); - const pid = try std.os.fork(); + const pid = try std.posix.fork(); if (pid == 0) { var cmd_buffer: [1024]u8 = undefined; - const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} add {s} . $({s})", .{ xauth_cmd, display_name, mcookie_cmd }) catch std.os.exit(1); - _ = interop.execl(shell, shell, "-c", cmd_str.ptr, @as([*c]const u8, 0)); - std.os.exit(0); + const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} add {s} . $({s})", .{ xauth_cmd, display_name, mcookie_cmd }) catch std.process.exit(1); + const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str }; + std.posix.execveZ(shell, &args, std.c.environ) catch {}; + std.process.exit(1); } - _ = std.os.waitpid(pid, 0); + _ = std.posix.waitpid(pid, 0); +} + +fn executeShellCmd(shell: [*:0]const u8) !void { + const args = [_:null]?[*:0]const u8{shell}; + return std.posix.execveZ(shell, &args, std.c.environ); } fn executeWaylandCmd(shell: [*:0]const u8, wayland_cmd: []const u8, desktop_cmd: []const u8) !void { var cmd_buffer: [1024]u8 = undefined; - const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s}", .{ wayland_cmd, desktop_cmd }); - _ = interop.execl(shell, shell, "-c", cmd_str.ptr, @as([*c]const u8, 0)); + const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str }; + return std.posix.execveZ(shell, &args, std.c.environ); } fn executeX11Cmd(shell: [*:0]const u8, pw_dir: [*:0]const u8, config: Config, desktop_cmd: []const u8, vt: []const u8) !void { const display_num = try getFreeDisplay(); var buf: [5]u8 = undefined; - var display_name = try std.fmt.bufPrintZ(&buf, ":{d}", .{display_num}); + const display_name = try std.fmt.bufPrintZ(&buf, ":{d}", .{display_num}); try xauth(display_name, shell, pw_dir, config.xauth_cmd, config.mcookie_cmd); - const pid = try std.os.fork(); + const pid = try std.posix.fork(); if (pid == 0) { var cmd_buffer: [1024]u8 = undefined; - const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ config.x_cmd, display_name, vt }) catch std.os.exit(1); - _ = interop.execl(shell, shell, "-c", cmd_str.ptr, @as([*c]const u8, 0)); - std.os.exit(0); + const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ config.x_cmd, display_name, vt }) catch std.process.exit(1); + const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str }; + std.posix.execveZ(shell, &args, std.c.environ) catch {}; + std.process.exit(1); } - var status: c_int = 0; var ok: c_int = undefined; var xcb: ?*interop.xcb.xcb_connection_t = null; while (ok != 0) { xcb = interop.xcb.xcb_connect(null, null); ok = interop.xcb.xcb_connection_has_error(xcb); - status = std.c.kill(pid, 0); - if (std.os.errno(status) == .SRCH and ok != 0) return; + std.posix.kill(pid, 0) catch |e| { + if (e == error.ProcessNotFound and ok != 0) return; + }; } // X Server detaches from the process. // PID can be fetched from /tmp/X{d}.lock const x_pid = try getXPid(display_num); - xorg_pid = try std.os.fork(); + xorg_pid = try std.posix.fork(); if (xorg_pid == 0) { var cmd_buffer: [1024]u8 = undefined; - const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s}", .{ config.x_cmd_setup, desktop_cmd }) catch std.os.exit(1); - _ = interop.execl(shell, shell, "-c", cmd_str.ptr, @as([*c]const u8, 0)); - std.os.exit(0); + const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s}", .{ config.x_cmd_setup, desktop_cmd }) catch std.process.exit(1); + const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str }; + std.posix.execveZ(shell, &args, std.c.environ) catch {}; + std.process.exit(1); } // If we receive SIGTERM, clean up by killing the xorg_pid process - const act = std.os.Sigaction{ + const act = std.posix.Sigaction{ .handler = .{ .handler = &xorgSignalHandler }, - .mask = std.os.empty_sigset, + .mask = std.posix.empty_sigset, .flags = 0, }; - try std.os.sigaction(std.os.SIG.TERM, &act, null); + try std.posix.sigaction(std.posix.SIG.TERM, &act, null); - _ = std.os.waitpid(xorg_pid, 0); + _ = std.posix.waitpid(xorg_pid, 0); interop.xcb.xcb_disconnect(xcb); - status = std.c.kill(x_pid, 0); - if (std.os.errno(status) != .SRCH) { - _ = std.c.kill(x_pid, std.os.SIG.TERM); - _ = std.c.waitpid(x_pid, &status, 0); - } -} + std.posix.kill(x_pid, 0) catch return; + std.posix.kill(x_pid, std.posix.SIG.TERM) catch {}; -fn executeShellCmd(shell: [*:0]const u8) void { - _ = interop.execl(shell, shell, @as([*c]const u8, 0)); + var status: c_int = 0; + _ = std.c.waitpid(x_pid, &status, 0); } fn addUtmpEntry(entry: *Utmp, username: [*:0]const u8, pid: c_int) !void { diff --git a/src/bigclock.zig b/src/bigclock.zig index 5c3007d..46d157c 100644 --- a/src/bigclock.zig +++ b/src/bigclock.zig @@ -111,7 +111,7 @@ pub fn clockCell(animate: bool, char: u8, fg: u8, bg: u8) [SIZE]termbox.tb_cell return cells; } -pub fn alphaBlit(buffer: [*c]termbox.tb_cell, x: u64, y: u64, tb_width: u64, tb_height: u64, cells: [SIZE]termbox.tb_cell) void { +pub fn alphaBlit(buffer: [*]termbox.tb_cell, x: u64, y: u64, tb_width: u64, tb_height: u64, cells: [SIZE]termbox.tb_cell) void { if (x + WIDTH >= tb_width or y + HEIGHT >= tb_height) return; for (0..HEIGHT) |yy| { diff --git a/src/config/Config.zig b/src/config/Config.zig index 8b21dee..b5fdeda 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -27,17 +27,17 @@ max_login_len: u8 = 255, max_password_len: u8 = 255, mcookie_cmd: []const u8 = "/usr/bin/mcookie", min_refresh_delta: u16 = 5, -path: ?[]const u8 = "/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin", +path: ?[:0]const u8 = "/sbin:/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/sbin", restart_cmd: []const u8 = "/sbin/shutdown -r now", restart_key: []const u8 = "F2", save: bool = true, save_file: []const u8 = "/etc/ly/save", -service_name: []const u8 = "ly", +service_name: [:0]const u8 = "ly", shutdown_cmd: []const u8 = "/sbin/shutdown -a now", shutdown_key: []const u8 = "F1", sleep_cmd: ?[]const u8 = null, sleep_key: []const u8 = "F3", -term_reset_cmd: []const u8 = "/usr/bin/tput reset", +term_reset_cmd: [:0]const u8 = "/usr/bin/tput reset", term_restore_cursor_cmd: []const u8 = "/usr/bin/tput cnorm", tty: u8 = 2, vi_mode: bool = false, diff --git a/src/config/Lang.zig b/src/config/Lang.zig index 9f199c0..0333e0b 100644 --- a/src/config/Lang.zig +++ b/src/config/Lang.zig @@ -29,6 +29,7 @@ err_perm_dir: []const u8 = "failed to change current directory", err_perm_group: []const u8 = "failed to downgrade group permissions", err_perm_user: []const u8 = "failed to downgrade user permissions", err_pwnam: []const u8 = "failed to get user info", +err_unknown: []const u8 = "an unknown error occurred", err_user_gid: []const u8 = "failed to set user GID", err_user_init: []const u8 = "failed to initialize user", err_user_uid: []const u8 = "failed to set user UID", @@ -42,9 +43,9 @@ numlock: []const u8 = "numlock", other: []const u8 = "other", password: []const u8 = "password:", restart: []const u8 = "reboot", -shell: []const u8 = "shell", +shell: [:0]const u8 = "shell", shutdown: []const u8 = "shutdown", sleep: []const u8 = "sleep", wayland: []const u8 = "wayland", -xinitrc: []const u8 = "xinitrc", +xinitrc: [:0]const u8 = "xinitrc", x11: []const u8 = "x11", diff --git a/src/config/migrator.zig b/src/config/migrator.zig index 9ce5673..0ae6001 100644 --- a/src/config/migrator.zig +++ b/src/config/migrator.zig @@ -2,29 +2,29 @@ const std = @import("std"); const ini = @import("zigini"); const Save = @import("Save.zig"); -const Allocator = std.mem.Allocator; +pub fn tryMigrateSaveFile(user_buf: *[32]u8, path: []const u8) Save { + var save = Save{}; -pub fn tryMigrateSaveFile(allocator: Allocator, path: []const u8) Save { - var file = std.fs.openFileAbsolute(path, .{ .mode = .read_write }) catch return .{}; + var file = std.fs.openFileAbsolute(path, .{}) catch return save; defer file.close(); const reader = file.reader(); - const user_length = reader.readIntLittle(u64) catch return .{}; - const user_buffer = allocator.alloc(u8, user_length) catch return .{}; - defer allocator.free(user_buffer); - - const read_user_length = reader.read(user_buffer) catch return .{}; - if (read_user_length != user_length) return .{}; - - const session_index = reader.readIntLittle(u64) catch return .{}; - - const save = .{ - .user = user_buffer, - .session_index = session_index, - }; - - ini.writeFromStruct(save, file.writer(), null) catch return save; + var user_fbs = std.io.fixedBufferStream(user_buf); + reader.streamUntilDelimiter(user_fbs.writer(), '\n', 32) catch return save; + const user = user_fbs.getWritten(); + if (user.len > 0) save.user = user; + + var session_buf: [20]u8 = undefined; + var session_fbs = std.io.fixedBufferStream(&session_buf); + reader.streamUntilDelimiter(session_fbs.writer(), '\n', 20) catch {}; + + const session_index_str = session_fbs.getWritten(); + var session_index: ?u64 = null; + if (session_index_str.len > 0) { + session_index = std.fmt.parseUnsigned(u64, session_index_str, 10) catch return save; + } + save.session_index = session_index; return save; } diff --git a/src/interop.zig b/src/interop.zig index c193c7d..a053747 100644 --- a/src/interop.zig +++ b/src/interop.zig @@ -44,9 +44,6 @@ pub const passwd = extern struct { pw_shell: [*:0]u8, }; -pub const _POSIX_HOST_NAME_MAX: c_int = 0xFF; -pub const _SC_HOST_NAME_MAX: c_int = 0xB4; - pub const VT_ACTIVATE: c_int = 0x5606; pub const VT_WAITACTIVE: c_int = 0x5607; @@ -59,19 +56,10 @@ pub const LED_CAP: c_int = 0x04; pub const K_NUMLOCK: c_int = 0x02; pub const K_CAPSLOCK: c_int = 0x04; -pub const O_RDONLY: c_uint = 0x00; -pub const O_WRONLY: c_uint = 0x01; -pub const O_RDWR: c_uint = 0x02; - -pub extern "c" fn fileno(stream: *std.c.FILE) c_int; -pub extern "c" fn sysconf(name: c_int) c_long; -pub extern "c" fn time(second: ?*c_time) c_time; pub extern "c" fn localtime(timer: *const c_time) *tm; pub extern "c" fn strftime(str: [*:0]u8, maxsize: c_size, format: [*:0]const u8, timeptr: *const tm) c_size; pub extern "c" fn setenv(name: [*:0]const u8, value: [*:0]const u8, overwrite: c_int) c_int; -pub extern "c" fn getenv(name: [*:0]const u8) [*:0]u8; pub extern "c" fn putenv(name: [*:0]u8) c_int; -pub extern "c" fn clearenv() c_int; pub extern "c" fn getuid() c_uid; pub extern "c" fn getpwnam(name: [*:0]const u8) ?*passwd; pub extern "c" fn endpwent() void; @@ -79,53 +67,24 @@ pub extern "c" fn setusershell() void; pub extern "c" fn getusershell() [*:0]u8; pub extern "c" fn endusershell() void; pub extern "c" fn initgroups(user: [*:0]const u8, group: c_gid) c_int; -pub extern "c" fn chdir(path: [*:0]const u8) c_int; -pub extern "c" fn execl(path: [*:0]const u8, arg: [*:0]const u8, ...) c_int; - -pub fn getHostName(allocator: Allocator) !struct { - buffer: []u8, - slice: []const u8, -} { - const hostname_sysconf = sysconf(_SC_HOST_NAME_MAX); - const hostname_max_length: u64 = if (hostname_sysconf < 0) @intCast(_POSIX_HOST_NAME_MAX) else @intCast(hostname_sysconf); - const buffer = try allocator.alloc(u8, hostname_max_length); - errdefer allocator.free(buffer); - - const error_code = std.c.gethostname(buffer.ptr, hostname_max_length); - if (error_code < 0) return error.CannotGetHostName; - - var hostname_length: u64 = 0; - for (buffer, 0..) |char, i| { - if (char == 0) { - hostname_length = i + 1; - break; - } - } - - return .{ - .buffer = buffer, - .slice = buffer[0..hostname_length], - }; -} - -pub fn timeAsString(allocator: Allocator, format: [:0]const u8, max_length: u64) ![:0]u8 { - const timer = time(null); +pub fn timeAsString(buf: [:0]u8, format: [:0]const u8) ![]u8 { + const timer = std.time.timestamp(); const tm_info = localtime(&timer); - const buffer = try allocator.allocSentinel(u8, max_length, 0); - errdefer allocator.free(buffer); - if (strftime(buffer, max_length, format, tm_info) < 0) return error.CannotGetFormattedTime; + const len = strftime(buf, buf.len, format, tm_info); + if (len < 0) return error.CannotGetFormattedTime; - return buffer; + return buf[0..len]; } pub fn getLockState(console_dev: [:0]const u8) !struct { numlock: bool, capslock: bool, } { - const fd = std.c.open(console_dev, O_RDONLY); + const fd = std.c.open(console_dev, .{ .ACCMODE = .RDONLY }); if (fd < 0) return error.CannotOpenConsoleDev; + defer _ = std.c.close(fd); var numlock = false; var capslock = false; @@ -142,8 +101,6 @@ pub fn getLockState(console_dev: [:0]const u8) !struct { capslock = (led & K_CAPSLOCK) != 0; } - _ = std.c.close(fd); - return .{ .numlock = numlock, .capslock = capslock, diff --git a/src/main.zig b/src/main.zig index 07bd6e8..bff277b 100644 --- a/src/main.zig +++ b/src/main.zig @@ -16,14 +16,13 @@ const Config = @import("config/Config.zig"); const Lang = @import("config/Lang.zig"); const Save = @import("config/Save.zig"); const migrator = @import("config/migrator.zig"); -const ViMode = @import("enums.zig").ViMode; const SharedError = @import("SharedError.zig"); const utils = @import("tui/utils.zig"); const Ini = ini.Ini; const termbox = interop.termbox; -var session_pid: std.os.pid_t = -1; +var session_pid: std.posix.pid_t = -1; pub fn signalHandler(i: c_int) callconv(.C) void { if (session_pid == 0) return; @@ -53,7 +52,7 @@ pub fn main() !void { ); var diag = clap.Diagnostic{}; - var res = clap.parse(clap.Help, ¶ms, clap.parsers.default, .{ .diagnostic = &diag }) catch |err| { + var res = clap.parse(clap.Help, ¶ms, clap.parsers.default, .{ .diagnostic = &diag, .allocator = allocator }) catch |err| { diag.report(stderr, err) catch {}; return err; }; @@ -61,17 +60,18 @@ pub fn main() !void { var config: Config = undefined; var lang: Lang = undefined; + var save: Save = undefined; var info_line = InfoLine{}; if (res.args.help != 0) { try clap.help(stderr, clap.Help, ¶ms, .{}); _ = try stderr.write("Note: if you want to configure Ly, please check the config file, which is usually located at /etc/ly/config.ini.\n"); - std.os.exit(0); + std.process.exit(0); } if (res.args.version != 0) { _ = try stderr.write("Ly version " ++ build_options.version ++ "\n"); - std.os.exit(0); + std.process.exit(0); } // Load configuration file @@ -81,66 +81,78 @@ 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 save_path: []const u8 = build_options.data_directory ++ "/save.ini"; + var save_path_alloc = false; + defer { + if (save_path_alloc) allocator.free(save_path); + } + + // Compatibility with v0.6.0 + const mapped_config_fields = .{.{ "blank_password", "clear_password" }}; + if (res.args.config) |s| { const trailing_slash = if (s[s.len - 1] != '/') "/" else ""; const config_path = try std.fmt.allocPrint(allocator, "{s}{s}config.ini", .{ s, trailing_slash }); defer allocator.free(config_path); - config = config_ini.readToStruct(config_path) catch Config{}; + config = config_ini.readFileToStructWithMap(config_path, mapped_config_fields) catch Config{}; const lang_path = try std.fmt.allocPrint(allocator, "{s}{s}lang/{s}.ini", .{ s, trailing_slash, config.lang }); defer allocator.free(lang_path); - lang = lang_ini.readToStruct(lang_path) catch Lang{}; + lang = lang_ini.readFileToStruct(lang_path) catch Lang{}; + + if (config.load) { + 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) catch migrator.tryMigrateSaveFile(&user_buf, config.save_file); + } } else { - config = config_ini.readToStruct(build_options.data_directory ++ "/config.ini") catch Config{}; + config = config_ini.readFileToStructWithMap(build_options.data_directory ++ "/config.ini", mapped_config_fields) catch Config{}; const lang_path = try std.fmt.allocPrint(allocator, "{s}/lang/{s}.ini", .{ build_options.data_directory, config.lang }); defer allocator.free(lang_path); - lang = lang_ini.readToStruct(lang_path) catch Lang{}; + lang = lang_ini.readFileToStruct(lang_path) catch Lang{}; + + if (config.load) { + var user_buf: [32]u8 = undefined; + save = save_ini.readFileToStruct(save_path) catch migrator.tryMigrateSaveFile(&user_buf, config.save_file); + } } // Initialize information line with host name - var got_host_name = false; - var host_name_buffer: []u8 = undefined; - get_host_name: { - const host_name_struct = interop.getHostName(allocator) catch |err| { - if (err == error.CannotGetHostName) { - try info_line.setText(lang.err_hostname); - } else { - try info_line.setText(lang.err_alloc); - } + var name_buf: [std.posix.HOST_NAME_MAX]u8 = undefined; + const hostname = std.posix.gethostname(&name_buf) catch { + try info_line.setText(lang.err_hostname); break :get_host_name; }; - - got_host_name = true; - host_name_buffer = host_name_struct.buffer; - try info_line.setText(host_name_struct.slice); - } - - defer { - if (got_host_name) allocator.free(host_name_buffer); + try info_line.setText(hostname); } // Initialize termbox _ = termbox.tb_init(); defer termbox.tb_shutdown(); - const act = std.os.Sigaction{ + const act = std.posix.Sigaction{ .handler = .{ .handler = &signalHandler }, - .mask = std.os.empty_sigset, + .mask = std.posix.empty_sigset, .flags = 0, }; - try std.os.sigaction(std.os.SIG.TERM, &act, null); + try std.posix.sigaction(std.posix.SIG.TERM, &act, null); _ = termbox.tb_select_output_mode(termbox.TB_OUTPUT_NORMAL); termbox.tb_clear(); // Needed to reset termbox after auth - const tb_termios = try std.os.tcgetattr(std.os.STDIN_FILENO); + const tb_termios = try std.posix.tcgetattr(std.posix.STDIN_FILENO); // Initialize terminal buffer const labels_max_length = @max(lang.login.len, lang.password.len); @@ -151,11 +163,11 @@ pub fn main() !void { var desktop = try Desktop.init(allocator, &buffer, config.max_desktop_len, lang); defer desktop.deinit(); - desktop.addEnvironment(lang.shell, "", .shell) catch { + desktop.addEnvironment(.{ .Name = lang.shell }, "", .shell) catch { try info_line.setText(lang.err_alloc); }; if (config.xinitrc) |xinitrc| { - desktop.addEnvironment(lang.xinitrc, xinitrc, .xinitrc) catch { + desktop.addEnvironment(.{ .Name = lang.xinitrc, .Exec = xinitrc }, "", .xinitrc) catch { try info_line.setText(lang.err_alloc); }; } @@ -174,16 +186,10 @@ pub fn main() !void { // Load last saved username and desktop selection, if any if (config.load) { - var save_ini = Ini(Save).init(allocator); - defer save_ini.deinit(); - - // If it fails, we try to migrate the potentially old save file. And if we can't do that, we just create - // a new save file - const save = save_ini.readToStruct(config.save_file) catch migrator.tryMigrateSaveFile(allocator, config.save_file); - if (save.user) |user| { try login.text.appendSlice(user); login.end = user.len; + login.cursor = login.end; active_input = .password; } @@ -246,13 +252,7 @@ pub fn main() !void { // Switch to selected TTY if possible open_console_dev: { - const console_dev_z = allocator.dupeZ(u8, config.console_dev) catch { - try info_line.setText(lang.err_alloc); - break :open_console_dev; - }; - defer allocator.free(console_dev_z); - - const fd = std.c.open(console_dev_z, interop.O_WRONLY); + const fd = std.c.open(config.console_dev, .{ .ACCMODE = .WRONLY }); defer _ = std.c.close(fd); if (fd < 0) { @@ -328,14 +328,13 @@ pub fn main() !void { const xo = buffer.width / 2 - (format.len * (bigclock.WIDTH + 1)) / 2; const yo = (buffer.height - buffer.box_height) / 2 - bigclock.HEIGHT - 2; - const clock_str = interop.timeAsString(allocator, format, format.len + 1) catch { - try info_line.setText(lang.err_alloc); + var clock_buf: [format.len + 1:0]u8 = undefined; + const clock_str = interop.timeAsString(&clock_buf, format) catch { break :draw_big_clock; }; - defer allocator.free(clock_str); - for (0..format.len) |i| { - const clock_cell = bigclock.clockCell(animate, clock_str[i], buffer.fg, buffer.bg); + for (clock_str, 0..) |c, i| { + const clock_cell = bigclock.clockCell(animate, c, buffer.fg, buffer.bg); bigclock.alphaBlit(buffer.buffer, xo + i * (bigclock.WIDTH + 1), yo, buffer.width, buffer.height, clock_cell); } } @@ -343,23 +342,14 @@ pub fn main() !void { buffer.drawBoxCenter(!config.hide_borders, config.blank_box); if (config.clock) |clock| draw_clock: { - const clock_buffer = interop.timeAsString(allocator, clock, 32) catch { - try info_line.setText(lang.err_alloc); + var clock_buf: [32:0]u8 = undefined; + const clock_str = interop.timeAsString(&clock_buf, clock) catch { break :draw_clock; }; - defer allocator.free(clock_buffer); - - var clock_str_length: u64 = 0; - for (clock_buffer, 0..) |char, i| { - if (char == 0) { - clock_str_length = i; - break; - } - } - if (clock_str_length == 0) return error.FormattedTimeEmpty; + if (clock_str.len == 0) return error.FormattedTimeEmpty; - buffer.drawLabel(clock_buffer[0..clock_str_length], buffer.width - clock_str_length, 0); + buffer.drawLabel(clock_str, buffer.width - clock_str.len, 0); } const label_x = buffer.box_x + buffer.margin_box_h; @@ -405,12 +395,8 @@ pub fn main() !void { } draw_lock_state: { - const lock_state = interop.getLockState(config.console_dev) catch |err| { - if (err == error.CannotOpenConsoleDev) { - try info_line.setText(lang.err_console_dev); - } else { - try info_line.setText(lang.err_alloc); - } + const lock_state = interop.getLockState(config.console_dev) catch { + try info_line.setText(lang.err_console_dev); break :draw_lock_state; }; @@ -487,11 +473,8 @@ pub fn main() !void { run = false; } else if (pressed_key == sleep_key) { if (config.sleep_cmd) |sleep_cmd| { - const pid = try std.os.fork(); - if (pid == 0) { - std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", sleep_cmd }) catch std.os.exit(1); - std.os.exit(0); - } + var sleep = std.ChildProcess.init(&[_][]const u8{ "/bin/sh", "-c", sleep_cmd }, allocator); + _ = sleep.spawnAndWait() catch .{}; } } }, @@ -529,7 +512,7 @@ pub fn main() !void { }, termbox.TB_KEY_ENTER => { if (config.save) save_last_settings: { - var file = std.fs.createFileAbsolute(config.save_file, .{}) catch break :save_last_settings; + var file = std.fs.createFileAbsolute(save_path, .{}) catch break :save_last_settings; defer file.close(); const save_data = Save{ @@ -542,19 +525,26 @@ pub fn main() !void { var shared_err = try SharedError.init(); defer shared_err.deinit(); - session_pid = try std.os.fork(); - if (session_pid == 0) { - auth.authenticate(allocator, config, desktop, login, &password) catch |err| { - shared_err.writeError(err); - std.os.exit(1); - }; - std.os.exit(0); - } + { + 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); + + session_pid = try std.posix.fork(); + if (session_pid == 0) { + auth.authenticate(config, desktop, login_text, password_text) catch |err| { + shared_err.writeError(err); + std.process.exit(1); + }; + std.process.exit(0); + } - _ = std.os.waitpid(session_pid, 0); - session_pid = -1; + _ = std.posix.waitpid(session_pid, 0); + session_pid = -1; + } - var auth_err = shared_err.readError(); + const auth_err = shared_err.readError(); if (auth_err) |err| { auth_fails += 1; active_input = .password; @@ -565,17 +555,15 @@ pub fn main() !void { try info_line.setText(lang.logout); } - try std.os.tcsetattr(std.os.STDIN_FILENO, .FLUSH, tb_termios); + try std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, tb_termios); termbox.tb_clear(); - termbox.tb_present(); update = true; - const pid = try std.os.fork(); - if (pid == 0) { - std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", config.term_restore_cursor_cmd }) catch std.os.exit(1); - std.os.exit(0); - } + var restore_cursor = std.ChildProcess.init(&[_][]const u8{ "/bin/sh", "-c", config.term_restore_cursor_cmd }, allocator); + _ = restore_cursor.spawnAndWait() catch .{}; + + termbox.tb_present(); }, else => { if (!insert_mode) { @@ -649,6 +637,6 @@ fn getAuthErrorMsg(err: anyerror, lang: Lang) []const u8 { error.PamSystemError => lang.err_pam_sys, error.PamUserUnknown => lang.err_pam_user_unknown, error.PamAbort => lang.err_pam_abort, - else => "An unknown error occurred", + else => lang.err_unknown, }; } diff --git a/src/tui/TerminalBuffer.zig b/src/tui/TerminalBuffer.zig index fd3d11f..8e95886 100644 --- a/src/tui/TerminalBuffer.zig +++ b/src/tui/TerminalBuffer.zig @@ -4,7 +4,6 @@ const interop = @import("../interop.zig"); const utils = @import("utils.zig"); const Config = @import("../config/Config.zig"); -const Allocator = std.mem.Allocator; const Random = std.rand.Random; const termbox = interop.termbox; diff --git a/src/tui/components/Desktop.zig b/src/tui/components/Desktop.zig index c8d6045..784918e 100644 --- a/src/tui/components/Desktop.zig +++ b/src/tui/components/Desktop.zig @@ -9,7 +9,6 @@ const Allocator = std.mem.Allocator; const EnvironmentList = std.ArrayList(Environment); const DisplayServer = enums.DisplayServer; -const ViMode = enums.ViMode; const termbox = interop.termbox; @@ -17,7 +16,9 @@ const Desktop = @This(); pub const Environment = struct { entry_ini: ?Ini(Entry) = null, - name: []const u8 = "", + name: [:0]const u8 = "", + xdg_session_desktop: [:0]const u8 = "", + xdg_desktop_names: ?[:0]const u8 = "", cmd: []const u8 = "", specifier: []const u8 = "", display_server: DisplayServer = .wayland, @@ -25,11 +26,11 @@ pub const Environment = struct { const DesktopEntry = struct { Exec: []const u8 = "", - Name: []const u8 = "", - DesktopNames: []const u8 = "", + Name: [:0]const u8 = "", + DesktopNames: ?[]const u8 = null, }; -pub const Entry = struct { Desktop_Entry: DesktopEntry = DesktopEntry{} }; +pub const Entry = struct { @"Desktop Entry": DesktopEntry = DesktopEntry{} }; allocator: Allocator, buffer: *TerminalBuffer, @@ -56,6 +57,8 @@ pub fn init(allocator: Allocator, buffer: *TerminalBuffer, max_length: u64, lang pub fn deinit(self: Desktop) void { for (self.environments.items) |*environment| { if (environment.entry_ini) |*entry_ini| entry_ini.deinit(); + if (environment.xdg_desktop_names) |desktop_name| self.allocator.free(desktop_name); + self.allocator.free(environment.xdg_session_desktop); } self.environments.deinit(); @@ -67,11 +70,29 @@ pub fn position(self: *Desktop, x: u64, y: u64, visible_length: u64) void { self.visible_length = visible_length; } -pub fn addEnvironment(self: *Desktop, name: []const u8, cmd: []const u8, display_server: DisplayServer) !void { +pub fn addEnvironment(self: *Desktop, entry: DesktopEntry, xdg_session_desktop: []const u8, display_server: DisplayServer) !void { + var xdg_desktop_names: ?[:0]const u8 = null; + if (entry.DesktopNames) |desktop_names| { + const desktop_names_z = try self.allocator.dupeZ(u8, desktop_names); + for (desktop_names_z) |*c| { + if (c.* == ';') c.* = ':'; + } + xdg_desktop_names = desktop_names_z; + } + + errdefer { + if (xdg_desktop_names) |desktop_names| self.allocator.free(desktop_names); + } + + const session_desktop = try self.allocator.dupeZ(u8, xdg_session_desktop); + errdefer self.allocator.free(session_desktop); + try self.environments.append(.{ .entry_ini = null, - .name = name, - .cmd = cmd, + .name = entry.Name, + .xdg_session_desktop = session_desktop, + .xdg_desktop_names = xdg_desktop_names, + .cmd = entry.Exec, .specifier = switch (display_server) { .wayland => self.lang.wayland, .x11 => self.lang.x11, @@ -83,11 +104,30 @@ pub fn addEnvironment(self: *Desktop, name: []const u8, cmd: []const u8, display self.current = self.environments.items.len - 1; } -pub fn addEnvironmentWithIni(self: *Desktop, entry_ini: Ini(Entry), name: []const u8, cmd: []const u8, display_server: DisplayServer) !void { +pub fn addEnvironmentWithIni(self: *Desktop, entry_ini: Ini(Entry), xdg_session_desktop: []const u8, display_server: DisplayServer) !void { + const entry = entry_ini.data.@"Desktop Entry"; + var xdg_desktop_names: ?[:0]const u8 = null; + if (entry.DesktopNames) |desktop_names| { + const desktop_names_z = try self.allocator.dupeZ(u8, desktop_names); + for (desktop_names_z) |*c| { + if (c.* == ';') c.* = ':'; + } + xdg_desktop_names = desktop_names_z; + } + + errdefer { + if (xdg_desktop_names) |desktop_names| self.allocator.free(desktop_names); + } + + const session_desktop = try self.allocator.dupeZ(u8, xdg_session_desktop); + errdefer self.allocator.free(session_desktop); + try self.environments.append(.{ .entry_ini = entry_ini, - .name = name, - .cmd = cmd, + .name = entry.Name, + .xdg_session_desktop = session_desktop, + .xdg_desktop_names = xdg_desktop_names, + .cmd = entry.Exec, .specifier = switch (display_server) { .wayland => self.lang.wayland, .x11 => self.lang.x11, @@ -100,7 +140,7 @@ pub fn addEnvironmentWithIni(self: *Desktop, entry_ini: Ini(Entry), name: []cons } pub fn crawl(self: *Desktop, path: []const u8, display_server: DisplayServer) !void { - var iterable_directory = std.fs.openIterableDirAbsolute(path, .{}) catch return; + var iterable_directory = std.fs.openDirAbsolute(path, .{ .iterate = true }) catch return; defer iterable_directory.close(); var iterator = iterable_directory.iterate(); @@ -110,9 +150,19 @@ pub fn crawl(self: *Desktop, path: []const u8, display_server: DisplayServer) !v const entry_path = try std.fmt.allocPrint(self.allocator, "{s}/{s}", .{ path, item.name }); defer self.allocator.free(entry_path); var entry_ini = Ini(Entry).init(self.allocator); - var entry = try entry_ini.readToStruct(entry_path); + _ = try entry_ini.readFileToStruct(entry_path); + errdefer entry_ini.deinit(); + + var xdg_session_desktop: []const u8 = undefined; + const maybe_desktop_names = entry_ini.data.@"Desktop Entry".DesktopNames; + if (maybe_desktop_names) |desktop_names| { + xdg_session_desktop = std.mem.sliceTo(desktop_names, ';'); + } else { + // if DesktopNames is empty, we'll take the name of the session file + xdg_session_desktop = std.fs.path.stem(item.name); + } - try self.addEnvironmentWithIni(entry_ini, entry.Desktop_Entry.Name, entry.Desktop_Entry.Exec, display_server); + try self.addEnvironmentWithIni(entry_ini, xdg_session_desktop, display_server); } } diff --git a/src/tui/components/Text.zig b/src/tui/components/Text.zig index 934e97e..22c0fc4 100644 --- a/src/tui/components/Text.zig +++ b/src/tui/components/Text.zig @@ -2,7 +2,6 @@ const std = @import("std"); const interop = @import("../../interop.zig"); const TerminalBuffer = @import("../TerminalBuffer.zig"); const utils = @import("../utils.zig"); -const ViMode = @import("../../enums.zig").ViMode; const Allocator = std.mem.Allocator; const DynamicString = std.ArrayList(u8);