diff --git a/src/Environment.zig b/src/Environment.zig index 47d4d7f..05ab149 100644 --- a/src/Environment.zig +++ b/src/Environment.zig @@ -6,17 +6,17 @@ const Ini = ini.Ini; pub const DesktopEntry = struct { Exec: []const u8 = "", - Name: [:0]const u8 = "", - DesktopNames: ?[:0]u8 = null, + Name: []const u8 = "", + DesktopNames: ?[]u8 = null, Terminal: ?bool = null, }; pub const Entry = struct { @"Desktop Entry": DesktopEntry = .{} }; entry_ini: ?Ini(Entry) = null, -name: [:0]const u8 = "", -xdg_session_desktop: ?[:0]const u8 = null, -xdg_desktop_names: ?[:0]const u8 = null, +name: []const u8 = "", +xdg_session_desktop: ?[]const u8 = null, +xdg_desktop_names: ?[]const u8 = null, cmd: []const u8 = "", specifier: []const u8 = "", display_server: DisplayServer = .wayland, diff --git a/src/UidRange.zig b/src/UidRange.zig index 13eb979..da78988 100644 --- a/src/UidRange.zig +++ b/src/UidRange.zig @@ -2,5 +2,5 @@ const std = @import("std"); // We set both values to 0 by default so that, in case they aren't present in // the login.defs for some reason, then only the root username will be shown -uid_min: std.c.uid_t = 0, -uid_max: std.c.uid_t = 0, +uid_min: std.posix.uid_t = 0, +uid_max: std.posix.uid_t = 0, diff --git a/src/auth.zig b/src/auth.zig index bbe00c3..ebb0505 100644 --- a/src/auth.zig +++ b/src/auth.zig @@ -14,7 +14,7 @@ const Utmp = utmp.utmpx; pub const AuthOptions = struct { tty: u8, service_name: [:0]const u8, - path: ?[:0]const u8, + path: ?[]const u8, session_log: ?[]const u8, xauth_cmd: []const u8, setup_cmd: []const u8, @@ -33,15 +33,15 @@ pub fn sessionSignalHandler(i: c_int) callconv(.c) void { if (child_pid > 0) _ = std.c.kill(child_pid, i); } -pub fn authenticate(options: AuthOptions, current_environment: Environment, login: [:0]const u8, password: [:0]const u8) !void { +pub fn authenticate(allocator: std.mem.Allocator, options: AuthOptions, current_environment: Environment, login: [:0]const u8, password: [:0]const u8) !void { var tty_buffer: [3]u8 = undefined; - const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{options.tty}); + const tty_str = try std.fmt.bufPrint(&tty_buffer, "{d}", .{options.tty}); var pam_tty_buffer: [6]u8 = undefined; const pam_tty_str = try std.fmt.bufPrintZ(&pam_tty_buffer, "tty{d}", .{options.tty}); // Set the XDG environment variables - try setXdgEnv(tty_str, current_environment); + try setXdgEnv(allocator, tty_str, current_environment); // Open the PAM session var credentials = [_:null]?[*:0]const u8{ login, password }; @@ -75,27 +75,23 @@ pub fn authenticate(options: AuthOptions, current_environment: Environment, logi if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); defer status = interop.pam.pam_close_session(handle, 0); - var pwd: *interop.pwd.passwd = undefined; + var user_entry: interop.UsernameEntry = undefined; { - defer interop.pwd.endpwent(); + defer interop.closePasswordDatabase(); // Get password structure from username - pwd = interop.pwd.getpwnam(login) orelse return error.GetPasswordNameFailed; + user_entry = interop.getUsernameEntry(login) orelse return error.GetPasswordNameFailed; } // Set user shell if it hasn't already been set - if (pwd.pw_shell == null) { - interop.unistd.setusershell(); - pwd.pw_shell = interop.unistd.getusershell(); - interop.unistd.endusershell(); - } + if (user_entry.shell == null) interop.setUserShell(&user_entry); var shared_err = try SharedError.init(); defer shared_err.deinit(); child_pid = try std.posix.fork(); if (child_pid == 0) { - startSession(options, tty_str, pwd, handle, current_environment) catch |e| { + startSession(allocator, options, tty_str, user_entry, handle, current_environment) catch |e| { shared_err.writeError(e); std.process.exit(1); }; @@ -119,7 +115,7 @@ pub fn authenticate(options: AuthOptions, current_environment: Environment, logi }; std.posix.sigaction(std.posix.SIG.TERM, &act, null); - try addUtmpEntry(&entry, pwd.pw_name.?, child_pid); + try addUtmpEntry(&entry, user_entry.username.?, child_pid); } // Wait for the session to stop _ = std.posix.waitpid(child_pid, 0); @@ -130,99 +126,89 @@ pub fn authenticate(options: AuthOptions, current_environment: Environment, logi } fn startSession( + allocator: std.mem.Allocator, options: AuthOptions, - tty_str: [:0]u8, - pwd: *interop.pwd.passwd, + tty_str: []u8, + user_entry: interop.UsernameEntry, handle: ?*interop.pam.pam_handle, current_environment: Environment, ) !void { - if (builtin.os.tag == .freebsd) { - // FreeBSD has initgroups() in unistd - const status = interop.unistd.initgroups(pwd.pw_name, pwd.pw_gid); - if (status != 0) return error.GroupInitializationFailed; - - // FreeBSD sets the GID and UID with setusercontext() - const result = interop.pwd.setusercontext(null, pwd, pwd.pw_uid, interop.pwd.LOGIN_SETALL); - if (result != 0) return error.SetUserUidFailed; - } else { - const status = interop.grp.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 the user's GID & PID + try interop.setUserContext(allocator, user_entry); // Set up the environment - try initEnv(pwd, options.path); + try initEnv(allocator, user_entry, options.path); // Reset the XDG environment variables - try setXdgEnv(tty_str, current_environment); + try setXdgEnv(allocator, tty_str, current_environment); // Set the PAM variables const pam_env_vars: ?[*:null]?[*:0]u8 = interop.pam.pam_getenvlist(handle); if (pam_env_vars == null) return error.GetEnvListFailed; const env_list = std.mem.span(pam_env_vars.?); - for (env_list) |env_var| _ = interop.stdlib.putenv(env_var); + for (env_list) |env_var| try interop.putEnvironmentVariable(env_var); // Change to the user's home directory - std.posix.chdirZ(pwd.pw_dir.?) catch return error.ChangeDirectoryFailed; + std.posix.chdir(user_entry.home.?) catch return error.ChangeDirectoryFailed; // Signal to the session process to give up control on the TTY std.posix.kill(options.session_pid, std.posix.SIG.CHLD) catch return error.TtyControlTransferFailed; // Execute what the user requested switch (current_environment.display_server) { - .wayland => try executeWaylandCmd(pwd.pw_shell.?, options, current_environment.cmd), - .shell => try executeShellCmd(pwd.pw_shell.?, options), + .wayland => try executeWaylandCmd(allocator, user_entry.shell.?, options, current_environment.cmd), + .shell => try executeShellCmd(allocator, user_entry.shell.?, options), .xinitrc, .x11 => if (build_options.enable_x11_support) { var vt_buf: [5]u8 = undefined; const vt = try std.fmt.bufPrint(&vt_buf, "vt{d}", .{options.tty}); - try executeX11Cmd(pwd.pw_shell.?, pwd.pw_dir.?, options, current_environment.cmd, vt); + try executeX11Cmd(allocator, user_entry.shell.?, user_entry.home.?, options, current_environment.cmd, vt); }, - .custom => try executeCustomCmd(pwd.pw_shell.?, options, current_environment.is_terminal, current_environment.cmd), + .custom => try executeCustomCmd(allocator, user_entry.shell.?, options, current_environment.is_terminal, current_environment.cmd), } } -fn initEnv(pwd: *interop.pwd.passwd, path_env: ?[:0]const u8) !void { - _ = interop.stdlib.setenv("HOME", pwd.pw_dir, 1); - _ = interop.stdlib.setenv("PWD", pwd.pw_dir, 1); - _ = interop.stdlib.setenv("SHELL", pwd.pw_shell, 1); - _ = interop.stdlib.setenv("USER", pwd.pw_name, 1); - _ = interop.stdlib.setenv("LOGNAME", pwd.pw_name, 1); +fn initEnv(allocator: std.mem.Allocator, entry: interop.UsernameEntry, path_env: ?[]const u8) !void { + if (entry.home) |home| { + try interop.setEnvironmentVariable(allocator, "HOME", home, true); + try interop.setEnvironmentVariable(allocator, "PWD", home, true); + } else return error.NoHomeDirectory; + + try interop.setEnvironmentVariable(allocator, "SHELL", entry.shell.?, true); + try interop.setEnvironmentVariable(allocator, "USER", entry.username.?, true); + try interop.setEnvironmentVariable(allocator, "LOGNAME", entry.username.?, true); if (path_env) |path| { - const status = interop.stdlib.setenv("PATH", path, 1); - if (status != 0) return error.SetPathFailed; + interop.setEnvironmentVariable(allocator, "PATH", path, true) catch return error.SetPathFailed; } } -fn setXdgEnv(tty_str: [:0]u8, environment: Environment) !void { - _ = interop.stdlib.setenv("XDG_SESSION_TYPE", switch (environment.display_server) { +fn setXdgEnv(allocator: std.mem.Allocator, tty_str: []u8, environment: Environment) !void { + try interop.setEnvironmentVariable(allocator, "XDG_SESSION_TYPE", switch (environment.display_server) { .wayland => "wayland", .shell => "tty", .xinitrc, .x11 => "x11", .custom => "unspecified", - }, 0); + }, false); // The "/run/user/%d" directory is not available on FreeBSD. It is much // better to stick to the defaults and let applications using // XDG_RUNTIME_DIR to fall back to directories inside user's home // directory. if (builtin.os.tag != .freebsd) { - const uid = interop.unistd.getuid(); + const uid = std.posix.getuid(); var uid_buffer: [32]u8 = undefined; // No UID can be larger than this - const uid_str = try std.fmt.bufPrintZ(&uid_buffer, "/run/user/{d}", .{uid}); + const uid_str = try std.fmt.bufPrint(&uid_buffer, "/run/user/{d}", .{uid}); - _ = interop.stdlib.setenv("XDG_RUNTIME_DIR", uid_str, 0); + try interop.setEnvironmentVariable(allocator, "XDG_RUNTIME_DIR", uid_str, false); } - if (environment.xdg_desktop_names) |xdg_desktop_names| _ = interop.stdlib.setenv("XDG_CURRENT_DESKTOP", xdg_desktop_names, 0); - _ = interop.stdlib.setenv("XDG_SESSION_CLASS", "user", 0); - _ = interop.stdlib.setenv("XDG_SESSION_ID", "1", 0); - if (environment.xdg_session_desktop) |desktop_name| _ = interop.stdlib.setenv("XDG_SESSION_DESKTOP", desktop_name, 0); - _ = interop.stdlib.setenv("XDG_SEAT", "seat0", 0); - _ = interop.stdlib.setenv("XDG_VTNR", tty_str, 0); + if (environment.xdg_desktop_names) |xdg_desktop_names| try interop.setEnvironmentVariable(allocator, "XDG_CURRENT_DESKTOP", xdg_desktop_names, false); + try interop.setEnvironmentVariable(allocator, "XDG_SESSION_CLASS", "user", false); + try interop.setEnvironmentVariable(allocator, "XDG_SESSION_ID", "1", false); + if (environment.xdg_session_desktop) |desktop_name| try interop.setEnvironmentVariable(allocator, "XDG_SESSION_DESKTOP", desktop_name, false); + try interop.setEnvironmentVariable(allocator, "XDG_SEAT", "seat0", false); + try interop.setEnvironmentVariable(allocator, "XDG_VTNR", tty_str, false); } fn loginConv( @@ -274,8 +260,8 @@ fn loginConv( if (status != interop.pam.PAM_SUCCESS) { // Memory is freed by pam otherwise allocator.free(response); - if (username != null) allocator.free(username.?); - if (password != null) allocator.free(password.?); + if (username) |str| allocator.free(str); + if (password) |str| allocator.free(str); } else { resp.?.* = response.ptr; } @@ -310,7 +296,7 @@ fn getXPid(display_num: u8) !i32 { return std.fmt.parseInt(i32, std.mem.trim(u8, buffer[0..written], " "), 10); } -fn createXauthFile(pwd: [:0]const u8) ![:0]const u8 { +fn createXauthFile(pwd: [:0]const u8) ![]const u8 { var xauth_buf: [100]u8 = undefined; var xauth_dir: [:0]const u8 = undefined; const xdg_rt_dir = std.posix.getenv("XDG_RUNTIME_DIR"); @@ -351,8 +337,8 @@ fn createXauthFile(pwd: [:0]const u8) ![:0]const u8 { const trimmed_xauth_dir = xauth_dir[0 .. i + 1]; 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.createFileAbsoluteZ(xauthority, .{}); + const xauthority: []u8 = try std.fmt.bufPrint(&buf, "{s}/{s}", .{ trimmed_xauth_dir, xauth_file }); + const file = try std.fs.createFileAbsolute(xauthority, .{}); file.close(); return xauthority; @@ -368,13 +354,13 @@ fn mcookie() [Md5.digest_length * 2]u8 { return std.fmt.bytesToHex(&out, .lower); } -fn xauth(display_name: [:0]u8, shell: [*:0]const u8, pw_dir: [*:0]const u8, options: AuthOptions) !void { +fn xauth(allocator: std.mem.Allocator, display_name: []u8, shell: [*:0]const u8, pw_dir: [*:0]const u8, options: AuthOptions) !void { var pwd_buf: [100]u8 = undefined; const pwd = try std.fmt.bufPrintZ(&pwd_buf, "{s}", .{pw_dir}); const xauthority = try createXauthFile(pwd); - _ = interop.stdlib.setenv("XAUTHORITY", xauthority, 1); - _ = interop.stdlib.setenv("DISPLAY", display_name, 1); + try interop.setEnvironmentVariable(allocator, "XAUTHORITY", xauthority, true); + try interop.setEnvironmentVariable(allocator, "DISPLAY", display_name, true); const magic_cookie = mcookie(); @@ -391,40 +377,53 @@ fn xauth(display_name: [:0]u8, shell: [*:0]const u8, pw_dir: [*:0]const u8, opti if (status.status != 0) return error.XauthFailed; } -fn executeShellCmd(shell: [*:0]const u8, options: AuthOptions) !void { +fn executeShellCmd(allocator: std.mem.Allocator, shell: []const u8, options: AuthOptions) !void { // We don't want to redirect stdout and stderr in a shell session + const shell_z = try allocator.dupeZ(u8, shell); + defer allocator.free(shell_z); + var cmd_buffer: [1024]u8 = undefined; const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.setup_cmd, options.login_cmd orelse "", shell }); - const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str }; - return std.posix.execveZ(shell, &args, std.c.environ); + const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str }; + return std.posix.execveZ(shell_z, &args, std.c.environ); } -fn executeWaylandCmd(shell: [*:0]const u8, options: AuthOptions, desktop_cmd: []const u8) !void { +fn executeWaylandCmd(allocator: std.mem.Allocator, shell: []const u8, options: AuthOptions, desktop_cmd: []const u8) !void { var maybe_log_file: ?std.fs.File = null; if (options.session_log) |log_path| { maybe_log_file = try redirectStandardStreams(log_path, true); } defer if (maybe_log_file) |log_file| log_file.close(); + const shell_z = try allocator.dupeZ(u8, shell); + defer allocator.free(shell_z); + var cmd_buffer: [1024]u8 = undefined; const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.setup_cmd, options.login_cmd orelse "", desktop_cmd }); - const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str }; - return std.posix.execveZ(shell, &args, std.c.environ); + const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str }; + return std.posix.execveZ(shell_z, &args, std.c.environ); } -fn executeX11Cmd(shell: [*:0]const u8, pw_dir: [*:0]const u8, options: AuthOptions, desktop_cmd: []const u8, vt: []const u8) !void { +fn executeX11Cmd(allocator: std.mem.Allocator, shell: []const u8, home: []const u8, options: AuthOptions, desktop_cmd: []const u8, vt: []const u8) !void { const display_num = try getFreeDisplay(); var buf: [5]u8 = undefined; - const display_name = try std.fmt.bufPrintZ(&buf, ":{d}", .{display_num}); - try xauth(display_name, shell, pw_dir, options); + const display_name = try std.fmt.bufPrint(&buf, ":{d}", .{display_num}); + + const shell_z = try allocator.dupeZ(u8, shell); + defer allocator.free(shell_z); + + const home_z = try allocator.dupeZ(u8, home); + defer allocator.free(home_z); + + try xauth(allocator, display_name, shell_z, home_z, options); 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}", .{ options.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 {}; + const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str }; + std.posix.execveZ(shell_z, &args, std.c.environ) catch {}; std.process.exit(1); } @@ -446,8 +445,8 @@ fn executeX11Cmd(shell: [*:0]const u8, pw_dir: [*:0]const u8, options: AuthOptio if (xorg_pid == 0) { var cmd_buffer: [1024]u8 = undefined; const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.setup_cmd, options.login_cmd orelse "", 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 {}; + const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str }; + std.posix.execveZ(shell_z, &args, std.c.environ) catch {}; std.process.exit(1); } @@ -467,11 +466,10 @@ fn executeX11Cmd(shell: [*:0]const u8, pw_dir: [*:0]const u8, options: AuthOptio std.Thread.sleep(std.time.ns_per_s * 1); // Wait 1 second before sending SIGKILL std.posix.kill(x_pid, std.posix.SIG.KILL) catch return; - var status: c_int = 0; - _ = std.c.waitpid(x_pid, &status, 0); + _ = std.posix.waitpid(x_pid, 0); } -fn executeCustomCmd(shell: [*:0]const u8, options: AuthOptions, is_terminal: bool, exec_cmd: []const u8) !void { +fn executeCustomCmd(allocator: std.mem.Allocator, shell: []const u8, options: AuthOptions, is_terminal: bool, exec_cmd: []const u8) !void { var maybe_log_file: ?std.fs.File = null; if (!is_terminal) { // For custom desktop entries, the "Terminal" value here determines if @@ -483,10 +481,13 @@ fn executeCustomCmd(shell: [*:0]const u8, options: AuthOptions, is_terminal: boo } defer if (maybe_log_file) |log_file| log_file.close(); + const shell_z = try allocator.dupeZ(u8, shell); + defer allocator.free(shell_z); + var cmd_buffer: [1024]u8 = undefined; const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ options.setup_cmd, options.login_cmd orelse "", exec_cmd }); - const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str }; - return std.posix.execveZ(shell, &args, std.c.environ); + const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str }; + return std.posix.execveZ(shell_z, &args, std.c.environ); } fn redirectStandardStreams(session_log: []const u8, create: bool) !std.fs.File { @@ -498,7 +499,7 @@ fn redirectStandardStreams(session_log: []const u8, create: bool) !std.fs.File { return log_file; } -fn addUtmpEntry(entry: *Utmp, username: [*:0]const u8, pid: c_int) !void { +fn addUtmpEntry(entry: *Utmp, username: []const u8, pid: c_int) !void { entry.ut_type = utmp.USER_PROCESS; entry.ut_pid = pid; @@ -520,12 +521,11 @@ fn addUtmpEntry(entry: *Utmp, username: [*:0]const u8, pid: c_int) !void { host[0] = 0; entry.ut_host = host; - var tv: interop.system_time.timeval = undefined; - _ = interop.system_time.gettimeofday(&tv, null); + const time = try interop.getTimeOfDay(); entry.ut_tv = .{ - .tv_sec = @intCast(tv.tv_sec), - .tv_usec = @intCast(tv.tv_usec), + .tv_sec = @intCast(time.seconds), + .tv_usec = @intCast(time.microseconds), }; entry.ut_addr_v6[0] = 0; diff --git a/src/bigclock.zig b/src/bigclock.zig index a1c860a..63aa02a 100644 --- a/src/bigclock.zig +++ b/src/bigclock.zig @@ -11,13 +11,11 @@ pub const WIDTH = Lang.WIDTH; pub const HEIGHT = Lang.HEIGHT; pub const SIZE = Lang.SIZE; -pub fn clockCell(animate: bool, char: u8, fg: u32, bg: u32, bigclock: Bigclock) [SIZE]Cell { +pub fn clockCell(animate: bool, char: u8, fg: u32, bg: u32, bigclock: Bigclock) ![SIZE]Cell { var cells: [SIZE]Cell = undefined; - var tv: interop.system_time.timeval = undefined; - _ = interop.system_time.gettimeofday(&tv, null); - - const clock_chars = toBigNumber(if (animate and char == ':' and @divTrunc(tv.tv_usec, 500000) != 0) ' ' else char, bigclock); + const time = try interop.getTimeOfDay(); + const clock_chars = toBigNumber(if (animate and char == ':' and @divTrunc(time.microseconds, 500000) != 0) ' ' else char, bigclock); for (0..cells.len) |i| cells[i] = Cell.init(clock_chars[i], fg, bg); return cells; diff --git a/src/bigclock/Lang.zig b/src/bigclock/Lang.zig index d11aac9..3e3be4e 100644 --- a/src/bigclock/Lang.zig +++ b/src/bigclock/Lang.zig @@ -1,10 +1,10 @@ -const builtin = @import("builtin"); +const interop = @import("../interop.zig"); pub const WIDTH = 5; pub const HEIGHT = 5; pub const SIZE = WIDTH * HEIGHT; -pub const X: u32 = if (builtin.os.tag == .linux or builtin.os.tag.isBSD()) 0x2593 else '#'; +pub const X: u32 = if (interop.supportsUnicode()) 0x2593 else '#'; pub const O: u32 = 0; // zig fmt: off diff --git a/src/config/Config.zig b/src/config/Config.zig index ef91cb1..89919a6 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -61,7 +61,7 @@ margin_box_h: u8 = 2, margin_box_v: u8 = 1, min_refresh_delta: u16 = 5, numlock: bool = false, -path: ?[:0]const u8 = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", +path: ?[]const u8 = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", restart_cmd: []const u8 = "/sbin/shutdown -r now", restart_key: []const u8 = "F2", save: bool = true, diff --git a/src/interop.zig b/src/interop.zig index 68d4332..c04f458 100644 --- a/src/interop.zig +++ b/src/interop.zig @@ -2,6 +2,19 @@ const std = @import("std"); const builtin = @import("builtin"); const Allocator = std.mem.Allocator; +pub const TimeOfDay = struct { + seconds: i64, + microseconds: i64, +}; + +pub const UsernameEntry = struct { + username: ?[]const u8, + uid: std.posix.uid_t, + gid: std.posix.gid_t, + home: ?[]const u8, + shell: ?[]const u8, +}; + pub const termbox = @import("termbox2"); pub const pam = @cImport({ @@ -17,44 +30,44 @@ pub const xcb = @cImport({ @cInclude("xcb/xcb.h"); }); -pub const unistd = @cImport({ - @cInclude("unistd.h"); -}); - -pub const time = @cImport({ - @cInclude("time.h"); -}); - -pub const system_time = @cImport({ - @cInclude("sys/time.h"); -}); - -pub const stdlib = @cImport({ - @cInclude("stdlib.h"); -}); - -pub const pwd = @cImport({ +const pwd = @cImport({ @cInclude("pwd.h"); // We include a FreeBSD-specific header here since login_cap.h references // the passwd struct directly, so we can't import it separately if (builtin.os.tag == .freebsd) @cInclude("login_cap.h"); }); -pub const grp = @cImport({ +const stdlib = @cImport({ + @cInclude("stdlib.h"); +}); + +const unistd = @cImport({ + @cInclude("unistd.h"); +}); + +const grp = @cImport({ @cInclude("grp.h"); }); +const system_time = @cImport({ + @cInclude("sys/time.h"); +}); + +const time = @cImport({ + @cInclude("time.h"); +}); + // BSD-specific headers -pub const kbio = @cImport({ +const kbio = @cImport({ @cInclude("sys/kbio.h"); }); // Linux-specific headers -pub const kd = @cImport({ +const kd = @cImport({ @cInclude("sys/kd.h"); }); -pub const vt = @cImport({ +const vt = @cImport({ @cInclude("sys/vt.h"); }); @@ -65,6 +78,10 @@ const set_led_state = if (builtin.os.tag.isBSD()) kbio.KDSETLED else kd.KDSKBLED const numlock_led = if (builtin.os.tag.isBSD()) kbio.LED_NUM else kd.K_NUMLOCK; const capslock_led = if (builtin.os.tag.isBSD()) kbio.LED_CAP else kd.K_CAPSLOCK; +pub fn supportsUnicode() bool { + return builtin.os.tag == .linux or builtin.os.tag.isBSD(); +} + pub fn timeAsString(buf: [:0]u8, format: [:0]const u8) []u8 { const timer = std.time.timestamp(); const tm_info = time.localtime(&timer); @@ -73,11 +90,23 @@ pub fn timeAsString(buf: [:0]u8, format: [:0]const u8) []u8 { return buf[0..len]; } +pub fn getTimeOfDay() !TimeOfDay { + var tv: system_time.timeval = undefined; + const status = system_time.gettimeofday(&tv, null); + + if (status != 0) return error.FailedToGetTimeOfDay; + + return .{ + .seconds = @intCast(tv.tv_sec), + .microseconds = @intCast(tv.tv_usec), + }; +} + pub fn switchTty(tty: u8) !void { - var status = std.c.ioctl(std.c.STDIN_FILENO, vt.VT_ACTIVATE, tty); + var status = std.c.ioctl(std.posix.STDIN_FILENO, vt.VT_ACTIVATE, tty); if (status != 0) return error.FailedToActivateTty; - status = std.c.ioctl(std.c.STDIN_FILENO, vt.VT_WAITACTIVE, tty); + status = std.c.ioctl(std.posix.STDIN_FILENO, vt.VT_WAITACTIVE, tty); if (status != 0) return error.FailedToWaitForActiveTty; } @@ -86,7 +115,7 @@ pub fn getLockState() !struct { capslock: bool, } { var led: LedState = undefined; - const status = std.c.ioctl(std.c.STDIN_FILENO, get_led_state, &led); + const status = std.c.ioctl(std.posix.STDIN_FILENO, get_led_state, &led); if (status != 0) return error.FailedToGetLockState; return .{ @@ -97,7 +126,7 @@ pub fn getLockState() !struct { pub fn setNumlock(val: bool) !void { var led: LedState = undefined; - var status = std.c.ioctl(std.c.STDIN_FILENO, get_led_state, &led); + var status = std.c.ioctl(std.posix.STDIN_FILENO, get_led_state, &led); if (status != 0) return error.FailedToGetNumlock; const numlock = (led & numlock_led) != 0; @@ -106,3 +135,80 @@ pub fn setNumlock(val: bool) !void { if (status != 0) return error.FailedToSetNumlock; } } + +pub fn setUserContext(allocator: std.mem.Allocator, entry: UsernameEntry) !void { + const username_z = try allocator.dupeZ(u8, entry.username.?); + defer allocator.free(username_z); + + if (builtin.os.tag == .freebsd) { + // FreeBSD has initgroups() in unistd + const status = unistd.initgroups(username_z.ptr, @intCast(entry.gid)); + if (status != 0) return error.GroupInitializationFailed; + + // FreeBSD sets the GID and UID with setusercontext() + // TODO + const result = pwd.setusercontext(null, entry, @intCast(entry.uid), pwd.LOGIN_SETALL); + if (result != 0) return error.SetUserUidFailed; + } else { + const status = grp.initgroups(username_z.ptr, @intCast(entry.gid)); + if (status != 0) return error.GroupInitializationFailed; + + std.posix.setgid(@intCast(entry.gid)) catch return error.SetUserGidFailed; + std.posix.setuid(@intCast(entry.uid)) catch return error.SetUserUidFailed; + } +} + +pub fn setUserShell(entry: *UsernameEntry) void { + unistd.setusershell(); + + const shell = unistd.getusershell(); + entry.shell = shell[0..std.mem.len(shell)]; + + unistd.endusershell(); +} + +pub fn setEnvironmentVariable(allocator: std.mem.Allocator, name: []const u8, value: []const u8, replace: bool) !void { + const name_z = try allocator.dupeZ(u8, name); + defer allocator.free(name_z); + + const value_z = try allocator.dupeZ(u8, value); + defer allocator.free(value_z); + + const status = stdlib.setenv(name_z.ptr, value_z.ptr, @intFromBool(replace)); + if (status != 0) return error.SetEnvironmentVariableFailed; +} + +pub fn putEnvironmentVariable(name_and_value: [*c]u8) !void { + const status = stdlib.putenv(name_and_value); + if (status != 0) return error.PutEnvironmentVariableFailed; +} + +pub fn getNextUsernameEntry() ?UsernameEntry { + const entry = pwd.getpwent(); + if (entry == null) return null; + + return .{ + .username = if (entry.*.pw_name) |name| name[0..std.mem.len(name)] else null, + .uid = @intCast(entry.*.pw_uid), + .gid = @intCast(entry.*.pw_gid), + .home = if (entry.*.pw_dir) |dir| dir[0..std.mem.len(dir)] else null, + .shell = if (entry.*.pw_shell) |shell| shell[0..std.mem.len(shell)] else null, + }; +} + +pub fn getUsernameEntry(username: [:0]const u8) ?UsernameEntry { + const entry = pwd.getpwnam(username); + if (entry == null) return null; + + return .{ + .username = if (entry.*.pw_name) |name| name[0..std.mem.len(name)] else null, + .uid = @intCast(entry.*.pw_uid), + .gid = @intCast(entry.*.pw_gid), + .home = if (entry.*.pw_dir) |dir| dir[0..std.mem.len(dir)] else null, + .shell = if (entry.*.pw_shell) |shell| shell[0..std.mem.len(shell)] else null, + }; +} + +pub fn closePasswordDatabase() void { + pwd.endpwent(); +} diff --git a/src/main.zig b/src/main.zig index 424a3a2..e91ac04 100644 --- a/src/main.zig +++ b/src/main.zig @@ -31,7 +31,6 @@ const Ini = ini.Ini; const DisplayServer = enums.DisplayServer; const Entry = Environment.Entry; const termbox = interop.termbox; -const unistd = interop.unistd; const temporary_allocator = std.heap.page_allocator; const ly_top_str = "Ly version " ++ build_options.version; @@ -85,8 +84,7 @@ pub fn main() !void { defer _ = gpa.deinit(); // Allows stopping an animation after some time - var tv_zero: interop.system_time.timeval = undefined; - _ = interop.system_time.gettimeofday(&tv_zero, null); + const time_start = try interop.getTimeOfDay(); var animation_timed_out: bool = false; const allocator = gpa.allocator(); @@ -547,7 +545,8 @@ pub fn main() !void { const clock_str = interop.timeAsString(&clock_buf, format); for (clock_str, 0..) |c, i| { - const clock_cell = bigclock.clockCell(animate, c, buffer.fg, buffer.bg, config.bigclock); + // TODO: Show error + const clock_cell = try bigclock.clockCell(animate, c, buffer.fg, buffer.bg, config.bigclock); bigclock.alphaBlit(xo + i * (bigclock.WIDTH + 1), yo, buffer.width, buffer.height, clock_cell); } } @@ -682,24 +681,21 @@ pub fn main() !void { if (animate and !animation_timed_out) { timeout = config.min_refresh_delta; - // check how long we have been running so we can turn off the animation - var tv: interop.system_time.timeval = undefined; - _ = interop.system_time.gettimeofday(&tv, null); + // Check how long we've been running so we can turn off the animation + const time = try interop.getTimeOfDay(); - if (config.animation_timeout_sec > 0 and tv.tv_sec - tv_zero.tv_sec > config.animation_timeout_sec) { + if (config.animation_timeout_sec > 0 and time.seconds - time_start.seconds > config.animation_timeout_sec) { animation_timed_out = true; animation.deinit(); } } else if (config.bigclock != .none and config.clock == null) { - var tv: interop.system_time.timeval = undefined; - _ = interop.system_time.gettimeofday(&tv, null); + const time = try interop.getTimeOfDay(); - timeout = @intCast((60 - @rem(tv.tv_sec, 60)) * 1000 - @divTrunc(tv.tv_usec, 1000) + 1); + timeout = @intCast((60 - @rem(time.seconds, 60)) * 1000 - @divTrunc(time.microseconds, 1000) + 1); } else if (config.clock != null or auth_fails >= config.auth_fails) { - var tv: interop.system_time.timeval = undefined; - _ = interop.system_time.gettimeofday(&tv, null); + const time = try interop.getTimeOfDay(); - timeout = @intCast(1000 - @divTrunc(tv.tv_usec, 1000) + 1); + timeout = @intCast(1000 - @divTrunc(time.microseconds, 1000) + 1); } const event_error = if (timeout == -1) termbox.tb_poll_event(&event) else termbox.tb_peek_event(&event, timeout); @@ -864,7 +860,7 @@ pub fn main() !void { }; std.posix.sigaction(std.posix.SIG.CHLD, &tty_control_transfer_act, null); - auth.authenticate(auth_options, current_environment, login_text, password_text) catch |err| { + auth.authenticate(allocator, auth_options, current_environment, login_text, password_text) catch |err| { shared_err.writeError(err); std.process.exit(1); }; @@ -1016,7 +1012,7 @@ fn crawl(session: *Session, lang: Lang, path: []const u8, display_server: Displa // Prepare the XDG_CURRENT_DESKTOP environment variable here const entry = entry_ini.data.@"Desktop Entry"; - var maybe_xdg_desktop_names: ?[:0]const u8 = null; + var maybe_xdg_desktop_names: ?[]const u8 = null; if (entry.DesktopNames) |desktop_names| { for (desktop_names) |*c| { if (c.* == ';') c.* = ':'; @@ -1024,13 +1020,10 @@ fn crawl(session: *Session, lang: Lang, path: []const u8, display_server: Displa maybe_xdg_desktop_names = desktop_names; } - const maybe_session_desktop = if (maybe_xdg_session_desktop) |xdg_session_desktop| try session.label.allocator.dupeZ(u8, xdg_session_desktop) else null; - errdefer if (maybe_session_desktop) |session_desktop| session.label.allocator.free(session_desktop); - try session.addEnvironment(.{ .entry_ini = entry_ini, .name = entry.Name, - .xdg_session_desktop = maybe_session_desktop, + .xdg_session_desktop = maybe_xdg_session_desktop, .xdg_desktop_names = maybe_xdg_desktop_names, .cmd = entry.Exec, .specifier = switch (display_server) { @@ -1049,24 +1042,20 @@ fn getAllUsernames(allocator: std.mem.Allocator, login_defs_path: []const u8) !S const uid_range = try getUserIdRange(allocator, login_defs_path); var usernames: StringList = .empty; - var maybe_entry = interop.pwd.getpwent(); - - while (maybe_entry != null) { - const entry = maybe_entry.*; + var maybe_entry = interop.getNextUsernameEntry(); + while (maybe_entry) |entry| { // We check if the UID is equal to 0 because we always want to add root // as a username (even if you can't log into it) - if (entry.pw_uid >= uid_range.uid_min and entry.pw_uid <= uid_range.uid_max or entry.pw_uid == 0) { - const pw_name_slice = entry.pw_name[0..std.mem.len(entry.pw_name)]; - const username = try allocator.dupe(u8, pw_name_slice); - + if (entry.uid >= uid_range.uid_min and entry.uid <= uid_range.uid_max or entry.uid == 0 and entry.username != null) { + const username = try allocator.dupe(u8, entry.username.?); try usernames.append(allocator, username); } - maybe_entry = interop.pwd.getpwent(); + maybe_entry = interop.getNextUsernameEntry(); } - interop.pwd.endpwent(); + interop.closePasswordDatabase(); return usernames; } @@ -1086,9 +1075,9 @@ fn getUserIdRange(allocator: std.mem.Allocator, login_defs_path: []const u8) !Ui const trimmed_line = std.mem.trim(u8, line, " \n\r\t"); if (std.mem.startsWith(u8, trimmed_line, "UID_MIN")) { - uid_range.uid_min = try parseValue(std.c.uid_t, "UID_MIN", trimmed_line); + uid_range.uid_min = try parseValue(std.posix.uid_t, "UID_MIN", trimmed_line); } else if (std.mem.startsWith(u8, trimmed_line, "UID_MAX")) { - uid_range.uid_max = try parseValue(std.c.uid_t, "UID_MAX", trimmed_line); + uid_range.uid_max = try parseValue(std.posix.uid_t, "UID_MAX", trimmed_line); } } diff --git a/src/tui/TerminalBuffer.zig b/src/tui/TerminalBuffer.zig index 4987208..c5e15d9 100644 --- a/src/tui/TerminalBuffer.zig +++ b/src/tui/TerminalBuffer.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const builtin = @import("builtin"); const interop = @import("../interop.zig"); const Cell = @import("Cell.zig"); @@ -82,7 +81,7 @@ pub fn init(options: InitOptions, labels_max_length: usize, random: Random) Term .fg = options.fg, .bg = options.bg, .border_fg = options.border_fg, - .box_chars = if (builtin.os.tag == .linux or builtin.os.tag.isBSD()) .{ + .box_chars = if (interop.supportsUnicode()) .{ .left_up = 0x250C, .left_down = 0x2514, .right_up = 0x2510, diff --git a/src/tui/components/Session.zig b/src/tui/components/Session.zig index 2656025..3c39a29 100644 --- a/src/tui/components/Session.zig +++ b/src/tui/components/Session.zig @@ -23,7 +23,6 @@ pub fn init(allocator: Allocator, buffer: *TerminalBuffer) Session { pub fn deinit(self: *Session) void { for (self.label.list.items) |*environment| { if (environment.entry_ini) |*entry_ini| entry_ini.deinit(); - if (environment.xdg_session_desktop) |session_desktop| self.label.allocator.free(session_desktop); } self.label.deinit();