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
pull/603/head
アシュ 1 month ago committed by GitHub
parent 0803de8ad3
commit 5a9cc881fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -35,12 +35,12 @@ pub fn build(b: *std.Build) void {
}); });
const zigini = b.dependency("zigini", .{ .target = target, .optimize = optimize }); 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 }); 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("pam");
exe.linkSystemLibrary("xcb"); exe.linkSystemLibrary("xcb");

@ -3,12 +3,13 @@
.version = "1.0.0", .version = "1.0.0",
.dependencies = .{ .dependencies = .{
.clap = .{ .clap = .{
.url = "https://github.com/Hejsil/zig-clap/archive/f49b94700e0761b7514abdca0e4f0e7f3f938a93.tar.gz", .url = "https://github.com/Hejsil/zig-clap/archive/8c98e6404b22aafc0184e999d8f068b81cc22fa1.tar.gz",
.hash = "1220f48518ce22882e102255ed3bcdb7aeeb4891f50b2cdd3bd74b5b2e24d3149ba2", .hash = "122014e73fd712190e109950837b97f6143f02d7e2b6986e1db70b6f4aadb5ba6a0d",
}, },
.zigini = .{ .zigini = .{
.url = "https://github.com/Kawaii-Ash/zigini/archive/91f47e46591982fc559afa3248749c1d29a0fa2a.tar.gz", .url = "https://github.com/Kawaii-Ash/zigini/archive/a5b5caf43f0a0246a242df4aebd00a0649deebad.tar.gz",
.hash = "12209908f2773f730fbca024c80dc7f48dce15a6527b2387f3768968f5bae0d3931e", .hash = "12202e097a54cbb7c002a2a5b3a3435939fa3902cd14308b7b66ab710e3d88d76e94",
}, },
}, },
.paths = .{""},
} }

@ -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. + xinitrc can be set to null to hide it.
+ `blank_password` has been renamed to `clear_password`. + `blank_password` has been renamed to `clear_password`.
+ `save_file` has been deprecated and will be removed in a future version.
### Removals ### Removals
@ -24,7 +25,8 @@ Note: `sleep_cmd` is unset by default, meaning it's hidden and has no effect.
## Save File ## 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: Example:

@ -76,6 +76,9 @@ load = true
# Save the current desktop and login as defaults # Save the current desktop and login as defaults
save = true 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 # File in which to save and load the default desktop and login
save_file = /etc/ly/save save_file = /etc/ly/save

@ -29,6 +29,7 @@ err_perm_dir = failed to change current directory
err_perm_group = failed to downgrade group permissions err_perm_group = failed to downgrade group permissions
err_perm_user = failed to downgrade user permissions err_perm_user = failed to downgrade user permissions
err_pwnam = failed to get user info err_pwnam = failed to get user info
err_unknown = an unknown error occurred
err_user_gid = failed to set user GID err_user_gid = failed to set user GID
err_user_init = failed to initialize user err_user_init = failed to initialize user
err_user_uid = failed to set user UID err_user_uid = failed to set user UID

@ -12,13 +12,13 @@ const SharedError = @This();
data: []align(std.mem.page_size) u8, data: []align(std.mem.page_size) u8,
pub fn init() !SharedError { 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 }; return .{ .data = data };
} }
pub fn deinit(self: *SharedError) void { 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 { pub fn writeError(self: SharedError, err: anyerror) void {

@ -10,52 +10,35 @@ const utmp = interop.utmp;
const Utmp = utmp.utmp; const Utmp = utmp.utmp;
const SharedError = @import("SharedError.zig"); 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 { pub fn xorgSignalHandler(i: c_int) callconv(.C) void {
if (xorg_pid > 0) _ = std.c.kill(xorg_pid, i); 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 { pub fn sessionSignalHandler(i: c_int) callconv(.C) void {
if (child_pid > 0) _ = std.c.kill(child_pid, i); 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; var tty_buffer: [2]u8 = undefined;
const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{config.tty}); const tty_str = try std.fmt.bufPrintZ(&tty_buffer, "{d}", .{config.tty});
const current_environment = desktop.environments.items[desktop.current]; const current_environment = desktop.environments.items[desktop.current];
// Set the XDG environment variables // Set the XDG environment variables
setXdgSessionEnv(current_environment.display_server); setXdgSessionEnv(current_environment.display_server);
if (current_environment.entry_ini) |entry| { try setXdgEnv(tty_str, current_environment.xdg_session_desktop, current_environment.xdg_desktop_names orelse "");
try setXdgEnv(allocator, tty_str, entry.DesktopNames);
} else {
try setXdgEnv(allocator, tty_str, "");
}
// Open the PAM session // Open the PAM session
const login_text_z = try allocator.dupeZ(u8, login.text.items); var credentials = [_:null]?[*:0]const u8{ login, password };
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;
const conv = interop.pam.pam_conv{ const conv = interop.pam.pam_conv{
.conv = loginConv, .conv = loginConv,
.appdata_ptr = @ptrCast(credentials.ptr), .appdata_ptr = @ptrCast(&credentials),
}; };
var handle: ?*interop.pam.pam_handle = undefined; var handle: ?*interop.pam.pam_handle = undefined;
const service_name_z = try allocator.dupeZ(u8, config.service_name); var status = interop.pam.pam_start(config.service_name.ptr, null, &conv, &handle);
defer allocator.free(service_name_z);
var status = interop.pam.pam_start(service_name_z.ptr, null, &conv, &handle);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
// Do the PAM routine // 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); status = interop.pam.pam_open_session(handle, 0);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
// Get password structure from username var pwd: *interop.passwd = undefined;
const maybe_pwd = interop.getpwnam(login_text_z.ptr); {
interop.endpwent(); defer interop.endpwent();
if (maybe_pwd == null) return error.GetPasswordNameFailed; // Get password structure from username
const pwd = maybe_pwd.?; pwd = interop.getpwnam(login.ptr) orelse return error.GetPasswordNameFailed;
}
// Set user shell if it hasn't already been set // Set user shell if it hasn't already been set
if (pwd.pw_shell[0] == 0) { if (pwd.pw_shell[0] == 0) {
interop.setusershell(); interop.setusershell();
defer interop.endusershell(); pwd.pw_shell = interop.getusershell();
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;
}
}
} }
var shared_err = try SharedError.init(); var shared_err = try SharedError.init();
defer shared_err.deinit(); defer shared_err.deinit();
child_pid = try std.os.fork(); child_pid = try std.posix.fork();
if (child_pid == 0) { if (child_pid == 0) {
// Set the user information startSession(config, pwd, handle, current_environment) catch |e| {
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| {
shared_err.writeError(e); shared_err.writeError(e);
std.os.exit(1); std.process.exit(1);
}; };
std.process.exit(0);
// 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);
} }
var entry: Utmp = std.mem.zeroes(Utmp); var entry: Utmp = std.mem.zeroes(Utmp);
addUtmpEntry(&entry, pwd.pw_name, child_pid) catch {}; addUtmpEntry(&entry, pwd.pw_name, child_pid) catch {};
// If we receive SIGTERM, forward it to child_pid // If we receive SIGTERM, forward it to child_pid
const act = std.os.Sigaction{ const act = std.posix.Sigaction{
.handler = .{ .handler = &sessionSignalHandler }, .handler = .{ .handler = &sessionSignalHandler },
.mask = std.os.empty_sigset, .mask = std.posix.empty_sigset,
.flags = 0, .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 // Wait for the session to stop
_ = std.os.waitpid(child_pid, 0); _ = std.posix.waitpid(child_pid, 0);
removeUtmpEntry(&entry); 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 // Close the PAM session
status = interop.pam.pam_close_session(handle, 0); 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; if (shared_err.readError()) |err| return err;
} }
fn initEnv(allocator: Allocator, pwd: *interop.passwd, path: ?[]const u8) !void { fn startSession(
const term_env = std.os.getenv("TERM"); 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); if (term_env) |term| _ = interop.setenv("TERM", term, 1);
_ = interop.setenv("HOME", pwd.pw_dir, 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("USER", pwd.pw_name, 1);
_ = interop.setenv("LOGNAME", pwd.pw_name, 1); _ = interop.setenv("LOGNAME", pwd.pw_name, 1);
if (path != null) { if (path_env) |path| {
const path_z = try allocator.dupeZ(u8, path.?); const status = interop.setenv("PATH", path, 1);
defer allocator.free(path_z);
const status = interop.setenv("PATH", path_z, 1);
if (status != 0) return error.SetPathFailed; if (status != 0) return error.SetPathFailed;
} }
} }
@ -230,22 +179,16 @@ fn setXdgSessionEnv(display_server: enums.DisplayServer) void {
}, 0); }, 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(); const uid = interop.getuid();
var uid_buffer: [10 + @sizeOf(u32) + 1]u8 = undefined; var uid_buffer: [10 + @sizeOf(u32) + 1]u8 = undefined;
const uid_str = try std.fmt.bufPrintZ(&uid_buffer, "/run/user/{d}", .{uid}); const uid_str = try std.fmt.bufPrintZ(&uid_buffer, "/run/user/{d}", .{uid});
if (desktop_name.len > 0) { _ = interop.setenv("XDG_CURRENT_DESKTOP", xdg_desktop_names.ptr, 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_RUNTIME_DIR", uid_str.ptr, 0); _ = interop.setenv("XDG_RUNTIME_DIR", uid_str.ptr, 0);
_ = interop.setenv("XDG_SESSION_CLASS", "user", 0); _ = interop.setenv("XDG_SESSION_CLASS", "user", 0);
_ = interop.setenv("XDG_SESSION_ID", "1", 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_SEAT", "seat0", 0);
_ = interop.setenv("XDG_VTNR", tty_str.ptr, 0); _ = interop.setenv("XDG_VTNR", tty_str.ptr, 0);
} }
@ -298,18 +241,15 @@ fn loginConv(
return status; return status;
} }
fn resetTerminal(allocator: Allocator, shell: [*:0]const u8, term_reset_cmd: []const u8) !void { fn resetTerminal(shell: [*:0]const u8, term_reset_cmd: [:0]const u8) !void {
const term_reset_cmd_z = try allocator.dupeZ(u8, term_reset_cmd); const pid = try std.posix.fork();
defer allocator.free(term_reset_cmd_z);
const pid = try std.os.fork();
if (pid == 0) { if (pid == 0) {
_ = interop.execl(shell, shell, "-c", term_reset_cmd_z.ptr, @as([*c]const u8, 0)); const args = [_:null]?[*:0]const u8{ shell, "-c", term_reset_cmd };
std.os.exit(0); 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 { fn getFreeDisplay() !u8 {
@ -317,7 +257,7 @@ fn getFreeDisplay() !u8 {
var i: u8 = 0; var i: u8 = 0;
while (i < 200) : (i += 1) { while (i < 200) : (i += 1) {
const xlock = try std.fmt.bufPrint(&buf, "/tmp/.X{d}-lock", .{i}); 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; return i;
} }
@ -340,17 +280,17 @@ fn getXPid(display_num: u8) !i32 {
fn createXauthFile(pwd: [:0]const u8) ![:0]const u8 { fn createXauthFile(pwd: [:0]const u8) ![:0]const u8 {
var xauth_buf: [100]u8 = undefined; var xauth_buf: [100]u8 = undefined;
var xauth_dir: [:0]const 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"; var xauth_file: []const u8 = "lyxauth";
if (xdg_rt_dir == null) { 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; var sb: std.c.Stat = undefined;
if (xdg_cfg_home == null) { if (xdg_cfg_home == null) {
xauth_dir = try std.fmt.bufPrintZ(&xauth_buf, "{s}/.config", .{pwd}); xauth_dir = try std.fmt.bufPrintZ(&xauth_buf, "{s}/.config", .{pwd});
_ = std.c.stat(xauth_dir, &sb); _ = std.c.stat(xauth_dir, &sb);
const mode = sb.mode & std.os.S.IFMT; const mode = sb.mode & std.posix.S.IFMT;
if (mode == std.os.S.IFDIR) { if (mode == std.posix.S.IFDIR) {
xauth_dir = try std.fmt.bufPrintZ(&xauth_buf, "{s}/ly", .{xauth_dir}); xauth_dir = try std.fmt.bufPrintZ(&xauth_buf, "{s}/ly", .{xauth_dir});
} else { } else {
xauth_dir = pwd; xauth_dir = pwd;
@ -361,9 +301,9 @@ fn createXauthFile(pwd: [:0]const u8) ![:0]const u8 {
} }
_ = std.c.stat(xauth_dir, &sb); _ = std.c.stat(xauth_dir, &sb);
const mode = sb.mode & std.os.S.IFMT; const mode = sb.mode & std.posix.S.IFMT;
if (mode != std.os.S.IFDIR) { if (mode != std.posix.S.IFDIR) {
std.os.mkdir(xauth_dir, 777) catch { std.posix.mkdir(xauth_dir, 777) catch {
xauth_dir = pwd; xauth_dir = pwd;
xauth_file = ".lyxauth"; xauth_file = ".lyxauth";
}; };
@ -379,7 +319,7 @@ fn createXauthFile(pwd: [:0]const u8) ![:0]const u8 {
var buf: [256]u8 = undefined; var buf: [256]u8 = undefined;
const xauthority: [:0]u8 = try std.fmt.bufPrintZ(&buf, "{s}/{s}", .{ trimmed_xauth_dir, xauth_file }); 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(); file.close();
return xauthority; 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 { 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_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); const xauthority = try createXauthFile(pwd);
_ = interop.setenv("XAUTHORITY", xauthority, 1); _ = interop.setenv("XAUTHORITY", xauthority, 1);
_ = interop.setenv("DISPLAY", display_name, 1); _ = interop.setenv("DISPLAY", display_name, 1);
const pid = try std.os.fork(); const pid = try std.posix.fork();
if (pid == 0) { if (pid == 0) {
var cmd_buffer: [1024]u8 = undefined; 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); const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} add {s} . $({s})", .{ xauth_cmd, display_name, mcookie_cmd }) catch std.process.exit(1);
_ = interop.execl(shell, shell, "-c", cmd_str.ptr, @as([*c]const u8, 0)); const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
std.os.exit(0); 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 { fn executeWaylandCmd(shell: [*:0]const u8, wayland_cmd: []const u8, desktop_cmd: []const u8) !void {
var cmd_buffer: [1024]u8 = undefined; var cmd_buffer: [1024]u8 = undefined;
const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s}", .{ wayland_cmd, desktop_cmd }); 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 { 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(); const display_num = try getFreeDisplay();
var buf: [5]u8 = undefined; 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); 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) { if (pid == 0) {
var cmd_buffer: [1024]u8 = undefined; 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); const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s}", .{ config.x_cmd, display_name, vt }) catch std.process.exit(1);
_ = interop.execl(shell, shell, "-c", cmd_str.ptr, @as([*c]const u8, 0)); const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
std.os.exit(0); std.posix.execveZ(shell, &args, std.c.environ) catch {};
std.process.exit(1);
} }
var status: c_int = 0;
var ok: c_int = undefined; var ok: c_int = undefined;
var xcb: ?*interop.xcb.xcb_connection_t = null; var xcb: ?*interop.xcb.xcb_connection_t = null;
while (ok != 0) { while (ok != 0) {
xcb = interop.xcb.xcb_connect(null, null); xcb = interop.xcb.xcb_connect(null, null);
ok = interop.xcb.xcb_connection_has_error(xcb); ok = interop.xcb.xcb_connection_has_error(xcb);
status = std.c.kill(pid, 0); std.posix.kill(pid, 0) catch |e| {
if (std.os.errno(status) == .SRCH and ok != 0) return; if (e == error.ProcessNotFound and ok != 0) return;
};
} }
// X Server detaches from the process. // X Server detaches from the process.
// PID can be fetched from /tmp/X{d}.lock // PID can be fetched from /tmp/X{d}.lock
const x_pid = try getXPid(display_num); const x_pid = try getXPid(display_num);
xorg_pid = try std.os.fork(); xorg_pid = try std.posix.fork();
if (xorg_pid == 0) { if (xorg_pid == 0) {
var cmd_buffer: [1024]u8 = undefined; 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); const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s}", .{ config.x_cmd_setup, desktop_cmd }) catch std.process.exit(1);
_ = interop.execl(shell, shell, "-c", cmd_str.ptr, @as([*c]const u8, 0)); const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
std.os.exit(0); 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 // 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 }, .handler = .{ .handler = &xorgSignalHandler },
.mask = std.os.empty_sigset, .mask = std.posix.empty_sigset,
.flags = 0, .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); interop.xcb.xcb_disconnect(xcb);
status = std.c.kill(x_pid, 0); std.posix.kill(x_pid, 0) catch return;
if (std.os.errno(status) != .SRCH) { std.posix.kill(x_pid, std.posix.SIG.TERM) catch {};
_ = std.c.kill(x_pid, std.os.SIG.TERM);
_ = std.c.waitpid(x_pid, &status, 0);
}
}
fn executeShellCmd(shell: [*:0]const u8) void { var status: c_int = 0;
_ = interop.execl(shell, shell, @as([*c]const u8, 0)); _ = std.c.waitpid(x_pid, &status, 0);
} }
fn addUtmpEntry(entry: *Utmp, username: [*:0]const u8, pid: c_int) !void { fn addUtmpEntry(entry: *Utmp, username: [*:0]const u8, pid: c_int) !void {

@ -111,7 +111,7 @@ pub fn clockCell(animate: bool, char: u8, fg: u8, bg: u8) [SIZE]termbox.tb_cell
return cells; 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; if (x + WIDTH >= tb_width or y + HEIGHT >= tb_height) return;
for (0..HEIGHT) |yy| { for (0..HEIGHT) |yy| {

@ -27,17 +27,17 @@ max_login_len: u8 = 255,
max_password_len: u8 = 255, max_password_len: u8 = 255,
mcookie_cmd: []const u8 = "/usr/bin/mcookie", mcookie_cmd: []const u8 = "/usr/bin/mcookie",
min_refresh_delta: u16 = 5, 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_cmd: []const u8 = "/sbin/shutdown -r now",
restart_key: []const u8 = "F2", restart_key: []const u8 = "F2",
save: bool = true, save: bool = true,
save_file: []const u8 = "/etc/ly/save", 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_cmd: []const u8 = "/sbin/shutdown -a now",
shutdown_key: []const u8 = "F1", shutdown_key: []const u8 = "F1",
sleep_cmd: ?[]const u8 = null, sleep_cmd: ?[]const u8 = null,
sleep_key: []const u8 = "F3", 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", term_restore_cursor_cmd: []const u8 = "/usr/bin/tput cnorm",
tty: u8 = 2, tty: u8 = 2,
vi_mode: bool = false, vi_mode: bool = false,

@ -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_group: []const u8 = "failed to downgrade group permissions",
err_perm_user: []const u8 = "failed to downgrade user permissions", err_perm_user: []const u8 = "failed to downgrade user permissions",
err_pwnam: []const u8 = "failed to get user info", 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_gid: []const u8 = "failed to set user GID",
err_user_init: []const u8 = "failed to initialize user", err_user_init: []const u8 = "failed to initialize user",
err_user_uid: []const u8 = "failed to set user UID", err_user_uid: []const u8 = "failed to set user UID",
@ -42,9 +43,9 @@ numlock: []const u8 = "numlock",
other: []const u8 = "other", other: []const u8 = "other",
password: []const u8 = "password:", password: []const u8 = "password:",
restart: []const u8 = "reboot", restart: []const u8 = "reboot",
shell: []const u8 = "shell", shell: [:0]const u8 = "shell",
shutdown: []const u8 = "shutdown", shutdown: []const u8 = "shutdown",
sleep: []const u8 = "sleep", sleep: []const u8 = "sleep",
wayland: []const u8 = "wayland", wayland: []const u8 = "wayland",
xinitrc: []const u8 = "xinitrc", xinitrc: [:0]const u8 = "xinitrc",
x11: []const u8 = "x11", x11: []const u8 = "x11",

@ -2,29 +2,29 @@ const std = @import("std");
const ini = @import("zigini"); const ini = @import("zigini");
const Save = @import("Save.zig"); 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, .{}) catch return save;
var file = std.fs.openFileAbsolute(path, .{ .mode = .read_write }) catch return .{};
defer file.close(); defer file.close();
const reader = file.reader(); const reader = file.reader();
const user_length = reader.readIntLittle(u64) catch return .{};
const user_buffer = allocator.alloc(u8, user_length) catch return .{}; var user_fbs = std.io.fixedBufferStream(user_buf);
defer allocator.free(user_buffer); reader.streamUntilDelimiter(user_fbs.writer(), '\n', 32) catch return save;
const user = user_fbs.getWritten();
const read_user_length = reader.read(user_buffer) catch return .{}; if (user.len > 0) save.user = user;
if (read_user_length != user_length) return .{};
var session_buf: [20]u8 = undefined;
const session_index = reader.readIntLittle(u64) catch return .{}; var session_fbs = std.io.fixedBufferStream(&session_buf);
reader.streamUntilDelimiter(session_fbs.writer(), '\n', 20) catch {};
const save = .{
.user = user_buffer, const session_index_str = session_fbs.getWritten();
.session_index = session_index, var session_index: ?u64 = null;
}; if (session_index_str.len > 0) {
session_index = std.fmt.parseUnsigned(u64, session_index_str, 10) catch return save;
ini.writeFromStruct(save, file.writer(), null) catch return save; }
save.session_index = session_index;
return save; return save;
} }

@ -44,9 +44,6 @@ pub const passwd = extern struct {
pw_shell: [*:0]u8, 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_ACTIVATE: c_int = 0x5606;
pub const VT_WAITACTIVE: c_int = 0x5607; 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_NUMLOCK: c_int = 0x02;
pub const K_CAPSLOCK: c_int = 0x04; 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 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 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 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 putenv(name: [*:0]u8) c_int;
pub extern "c" fn clearenv() c_int;
pub extern "c" fn getuid() c_uid; pub extern "c" fn getuid() c_uid;
pub extern "c" fn getpwnam(name: [*:0]const u8) ?*passwd; pub extern "c" fn getpwnam(name: [*:0]const u8) ?*passwd;
pub extern "c" fn endpwent() void; 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 getusershell() [*:0]u8;
pub extern "c" fn endusershell() void; pub extern "c" fn endusershell() void;
pub extern "c" fn initgroups(user: [*:0]const u8, group: c_gid) c_int; 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); pub fn timeAsString(buf: [:0]u8, format: [:0]const u8) ![]u8 {
errdefer allocator.free(buffer); const timer = std.time.timestamp();
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);
const tm_info = localtime(&timer); 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 { pub fn getLockState(console_dev: [:0]const u8) !struct {
numlock: bool, numlock: bool,
capslock: 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; if (fd < 0) return error.CannotOpenConsoleDev;
defer _ = std.c.close(fd);
var numlock = false; var numlock = false;
var capslock = false; var capslock = false;
@ -142,8 +101,6 @@ pub fn getLockState(console_dev: [:0]const u8) !struct {
capslock = (led & K_CAPSLOCK) != 0; capslock = (led & K_CAPSLOCK) != 0;
} }
_ = std.c.close(fd);
return .{ return .{
.numlock = numlock, .numlock = numlock,
.capslock = capslock, .capslock = capslock,

@ -16,14 +16,13 @@ const Config = @import("config/Config.zig");
const Lang = @import("config/Lang.zig"); const Lang = @import("config/Lang.zig");
const Save = @import("config/Save.zig"); const Save = @import("config/Save.zig");
const migrator = @import("config/migrator.zig"); const migrator = @import("config/migrator.zig");
const ViMode = @import("enums.zig").ViMode;
const SharedError = @import("SharedError.zig"); const SharedError = @import("SharedError.zig");
const utils = @import("tui/utils.zig"); const utils = @import("tui/utils.zig");
const Ini = ini.Ini; const Ini = ini.Ini;
const termbox = interop.termbox; 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 { pub fn signalHandler(i: c_int) callconv(.C) void {
if (session_pid == 0) return; if (session_pid == 0) return;
@ -53,7 +52,7 @@ pub fn main() !void {
); );
var diag = clap.Diagnostic{}; var diag = clap.Diagnostic{};
var res = clap.parse(clap.Help, &params, clap.parsers.default, .{ .diagnostic = &diag }) catch |err| { var res = clap.parse(clap.Help, &params, clap.parsers.default, .{ .diagnostic = &diag, .allocator = allocator }) catch |err| {
diag.report(stderr, err) catch {}; diag.report(stderr, err) catch {};
return err; return err;
}; };
@ -61,17 +60,18 @@ pub fn main() !void {
var config: Config = undefined; var config: Config = undefined;
var lang: Lang = undefined; var lang: Lang = undefined;
var save: Save = undefined;
var info_line = InfoLine{}; var info_line = InfoLine{};
if (res.args.help != 0) { if (res.args.help != 0) {
try clap.help(stderr, clap.Help, &params, .{}); try clap.help(stderr, clap.Help, &params, .{});
_ = 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"); _ = 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) { if (res.args.version != 0) {
_ = try stderr.write("Ly version " ++ build_options.version ++ "\n"); _ = try stderr.write("Ly version " ++ build_options.version ++ "\n");
std.os.exit(0); std.process.exit(0);
} }
// Load configuration file // Load configuration file
@ -81,66 +81,78 @@ pub fn main() !void {
var lang_ini = Ini(Lang).init(allocator); var lang_ini = Ini(Lang).init(allocator);
defer lang_ini.deinit(); 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| { if (res.args.config) |s| {
const trailing_slash = if (s[s.len - 1] != '/') "/" else ""; const trailing_slash = if (s[s.len - 1] != '/') "/" else "";
const config_path = try std.fmt.allocPrint(allocator, "{s}{s}config.ini", .{ s, trailing_slash }); const config_path = try std.fmt.allocPrint(allocator, "{s}{s}config.ini", .{ s, trailing_slash });
defer allocator.free(config_path); 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 }); const lang_path = try std.fmt.allocPrint(allocator, "{s}{s}lang/{s}.ini", .{ s, trailing_slash, config.lang });
defer allocator.free(lang_path); 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 { } 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 }); const lang_path = try std.fmt.allocPrint(allocator, "{s}/lang/{s}.ini", .{ build_options.data_directory, config.lang });
defer allocator.free(lang_path); 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 // Initialize information line with host name
var got_host_name = false;
var host_name_buffer: []u8 = undefined;
get_host_name: { get_host_name: {
const host_name_struct = interop.getHostName(allocator) catch |err| { var name_buf: [std.posix.HOST_NAME_MAX]u8 = undefined;
if (err == error.CannotGetHostName) { const hostname = std.posix.gethostname(&name_buf) catch {
try info_line.setText(lang.err_hostname); try info_line.setText(lang.err_hostname);
} else {
try info_line.setText(lang.err_alloc);
}
break :get_host_name; break :get_host_name;
}; };
try info_line.setText(hostname);
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);
} }
// Initialize termbox // Initialize termbox
_ = termbox.tb_init(); _ = termbox.tb_init();
defer termbox.tb_shutdown(); defer termbox.tb_shutdown();
const act = std.os.Sigaction{ const act = std.posix.Sigaction{
.handler = .{ .handler = &signalHandler }, .handler = .{ .handler = &signalHandler },
.mask = std.os.empty_sigset, .mask = std.posix.empty_sigset,
.flags = 0, .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_select_output_mode(termbox.TB_OUTPUT_NORMAL);
termbox.tb_clear(); termbox.tb_clear();
// Needed to reset termbox after auth // 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 // Initialize terminal buffer
const labels_max_length = @max(lang.login.len, lang.password.len); 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); var desktop = try Desktop.init(allocator, &buffer, config.max_desktop_len, lang);
defer desktop.deinit(); defer desktop.deinit();
desktop.addEnvironment(lang.shell, "", .shell) catch { desktop.addEnvironment(.{ .Name = lang.shell }, "", .shell) catch {
try info_line.setText(lang.err_alloc); try info_line.setText(lang.err_alloc);
}; };
if (config.xinitrc) |xinitrc| { 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); try info_line.setText(lang.err_alloc);
}; };
} }
@ -174,16 +186,10 @@ pub fn main() !void {
// Load last saved username and desktop selection, if any // Load last saved username and desktop selection, if any
if (config.load) { 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| { if (save.user) |user| {
try login.text.appendSlice(user); try login.text.appendSlice(user);
login.end = user.len; login.end = user.len;
login.cursor = login.end;
active_input = .password; active_input = .password;
} }
@ -246,13 +252,7 @@ pub fn main() !void {
// Switch to selected TTY if possible // Switch to selected TTY if possible
open_console_dev: { open_console_dev: {
const console_dev_z = allocator.dupeZ(u8, config.console_dev) catch { const fd = std.c.open(config.console_dev, .{ .ACCMODE = .WRONLY });
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);
defer _ = std.c.close(fd); defer _ = std.c.close(fd);
if (fd < 0) { if (fd < 0) {
@ -328,14 +328,13 @@ pub fn main() !void {
const xo = buffer.width / 2 - (format.len * (bigclock.WIDTH + 1)) / 2; const xo = buffer.width / 2 - (format.len * (bigclock.WIDTH + 1)) / 2;
const yo = (buffer.height - buffer.box_height) / 2 - bigclock.HEIGHT - 2; const yo = (buffer.height - buffer.box_height) / 2 - bigclock.HEIGHT - 2;
const clock_str = interop.timeAsString(allocator, format, format.len + 1) catch { var clock_buf: [format.len + 1:0]u8 = undefined;
try info_line.setText(lang.err_alloc); const clock_str = interop.timeAsString(&clock_buf, format) catch {
break :draw_big_clock; break :draw_big_clock;
}; };
defer allocator.free(clock_str);
for (0..format.len) |i| { for (clock_str, 0..) |c, i| {
const clock_cell = bigclock.clockCell(animate, clock_str[i], buffer.fg, buffer.bg); 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); 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); buffer.drawBoxCenter(!config.hide_borders, config.blank_box);
if (config.clock) |clock| draw_clock: { if (config.clock) |clock| draw_clock: {
const clock_buffer = interop.timeAsString(allocator, clock, 32) catch { var clock_buf: [32:0]u8 = undefined;
try info_line.setText(lang.err_alloc); const clock_str = interop.timeAsString(&clock_buf, clock) catch {
break :draw_clock; 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; const label_x = buffer.box_x + buffer.margin_box_h;
@ -405,12 +395,8 @@ pub fn main() !void {
} }
draw_lock_state: { draw_lock_state: {
const lock_state = interop.getLockState(config.console_dev) catch |err| { const lock_state = interop.getLockState(config.console_dev) catch {
if (err == error.CannotOpenConsoleDev) { try info_line.setText(lang.err_console_dev);
try info_line.setText(lang.err_console_dev);
} else {
try info_line.setText(lang.err_alloc);
}
break :draw_lock_state; break :draw_lock_state;
}; };
@ -487,11 +473,8 @@ pub fn main() !void {
run = false; run = false;
} else if (pressed_key == sleep_key) { } else if (pressed_key == sleep_key) {
if (config.sleep_cmd) |sleep_cmd| { if (config.sleep_cmd) |sleep_cmd| {
const pid = try std.os.fork(); var sleep = std.ChildProcess.init(&[_][]const u8{ "/bin/sh", "-c", sleep_cmd }, allocator);
if (pid == 0) { _ = sleep.spawnAndWait() catch .{};
std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", sleep_cmd }) catch std.os.exit(1);
std.os.exit(0);
}
} }
} }
}, },
@ -529,7 +512,7 @@ pub fn main() !void {
}, },
termbox.TB_KEY_ENTER => { termbox.TB_KEY_ENTER => {
if (config.save) save_last_settings: { 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(); defer file.close();
const save_data = Save{ const save_data = Save{
@ -542,19 +525,26 @@ pub fn main() !void {
var shared_err = try SharedError.init(); var shared_err = try SharedError.init();
defer shared_err.deinit(); defer shared_err.deinit();
session_pid = try std.os.fork(); {
if (session_pid == 0) { const login_text = try allocator.dupeZ(u8, login.text.items);
auth.authenticate(allocator, config, desktop, login, &password) catch |err| { defer allocator.free(login_text);
shared_err.writeError(err); const password_text = try allocator.dupeZ(u8, password.text.items);
std.os.exit(1); defer allocator.free(password_text);
};
std.os.exit(0); 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); _ = std.posix.waitpid(session_pid, 0);
session_pid = -1; session_pid = -1;
}
var auth_err = shared_err.readError(); const auth_err = shared_err.readError();
if (auth_err) |err| { if (auth_err) |err| {
auth_fails += 1; auth_fails += 1;
active_input = .password; active_input = .password;
@ -565,17 +555,15 @@ pub fn main() !void {
try info_line.setText(lang.logout); 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_clear();
termbox.tb_present();
update = true; update = true;
const pid = try std.os.fork(); var restore_cursor = std.ChildProcess.init(&[_][]const u8{ "/bin/sh", "-c", config.term_restore_cursor_cmd }, allocator);
if (pid == 0) { _ = restore_cursor.spawnAndWait() catch .{};
std.process.execv(allocator, &[_][]const u8{ "/bin/sh", "-c", config.term_restore_cursor_cmd }) catch std.os.exit(1);
std.os.exit(0); termbox.tb_present();
}
}, },
else => { else => {
if (!insert_mode) { if (!insert_mode) {
@ -649,6 +637,6 @@ fn getAuthErrorMsg(err: anyerror, lang: Lang) []const u8 {
error.PamSystemError => lang.err_pam_sys, error.PamSystemError => lang.err_pam_sys,
error.PamUserUnknown => lang.err_pam_user_unknown, error.PamUserUnknown => lang.err_pam_user_unknown,
error.PamAbort => lang.err_pam_abort, error.PamAbort => lang.err_pam_abort,
else => "An unknown error occurred", else => lang.err_unknown,
}; };
} }

@ -4,7 +4,6 @@ const interop = @import("../interop.zig");
const utils = @import("utils.zig"); const utils = @import("utils.zig");
const Config = @import("../config/Config.zig"); const Config = @import("../config/Config.zig");
const Allocator = std.mem.Allocator;
const Random = std.rand.Random; const Random = std.rand.Random;
const termbox = interop.termbox; const termbox = interop.termbox;

@ -9,7 +9,6 @@ const Allocator = std.mem.Allocator;
const EnvironmentList = std.ArrayList(Environment); const EnvironmentList = std.ArrayList(Environment);
const DisplayServer = enums.DisplayServer; const DisplayServer = enums.DisplayServer;
const ViMode = enums.ViMode;
const termbox = interop.termbox; const termbox = interop.termbox;
@ -17,7 +16,9 @@ const Desktop = @This();
pub const Environment = struct { pub const Environment = struct {
entry_ini: ?Ini(Entry) = null, 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 = "", cmd: []const u8 = "",
specifier: []const u8 = "", specifier: []const u8 = "",
display_server: DisplayServer = .wayland, display_server: DisplayServer = .wayland,
@ -25,11 +26,11 @@ pub const Environment = struct {
const DesktopEntry = struct { const DesktopEntry = struct {
Exec: []const u8 = "", Exec: []const u8 = "",
Name: []const u8 = "", Name: [:0]const u8 = "",
DesktopNames: []const u8 = "", DesktopNames: ?[]const u8 = null,
}; };
pub const Entry = struct { Desktop_Entry: DesktopEntry = DesktopEntry{} }; pub const Entry = struct { @"Desktop Entry": DesktopEntry = DesktopEntry{} };
allocator: Allocator, allocator: Allocator,
buffer: *TerminalBuffer, buffer: *TerminalBuffer,
@ -56,6 +57,8 @@ pub fn init(allocator: Allocator, buffer: *TerminalBuffer, max_length: u64, lang
pub fn deinit(self: Desktop) void { pub fn deinit(self: Desktop) void {
for (self.environments.items) |*environment| { for (self.environments.items) |*environment| {
if (environment.entry_ini) |*entry_ini| entry_ini.deinit(); 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(); 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; 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(.{ try self.environments.append(.{
.entry_ini = null, .entry_ini = null,
.name = name, .name = entry.Name,
.cmd = cmd, .xdg_session_desktop = session_desktop,
.xdg_desktop_names = xdg_desktop_names,
.cmd = entry.Exec,
.specifier = switch (display_server) { .specifier = switch (display_server) {
.wayland => self.lang.wayland, .wayland => self.lang.wayland,
.x11 => self.lang.x11, .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; 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(.{ try self.environments.append(.{
.entry_ini = entry_ini, .entry_ini = entry_ini,
.name = name, .name = entry.Name,
.cmd = cmd, .xdg_session_desktop = session_desktop,
.xdg_desktop_names = xdg_desktop_names,
.cmd = entry.Exec,
.specifier = switch (display_server) { .specifier = switch (display_server) {
.wayland => self.lang.wayland, .wayland => self.lang.wayland,
.x11 => self.lang.x11, .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 { 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(); defer iterable_directory.close();
var iterator = iterable_directory.iterate(); 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 }); const entry_path = try std.fmt.allocPrint(self.allocator, "{s}/{s}", .{ path, item.name });
defer self.allocator.free(entry_path); defer self.allocator.free(entry_path);
var entry_ini = Ini(Entry).init(self.allocator); 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);
} }
} }

@ -2,7 +2,6 @@ const std = @import("std");
const interop = @import("../../interop.zig"); const interop = @import("../../interop.zig");
const TerminalBuffer = @import("../TerminalBuffer.zig"); const TerminalBuffer = @import("../TerminalBuffer.zig");
const utils = @import("../utils.zig"); const utils = @import("../utils.zig");
const ViMode = @import("../../enums.zig").ViMode;
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const DynamicString = std.ArrayList(u8); const DynamicString = std.ArrayList(u8);

Loading…
Cancel
Save