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
アシュ 2 weeks 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 });
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");

@ -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 = .{""},
}

@ -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:

@ -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

@ -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

@ -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 {

@ -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 {

@ -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| {

@ -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,

@ -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",

@ -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;
}

@ -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,

@ -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, &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 {};
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, &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");
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,
};
}

@ -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;

@ -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);
}
}

@ -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);

Loading…
Cancel
Save