Merge branch 'master' into box-movement

This commit is contained in:
AnErrupTion 2026-05-01 19:14:37 +02:00
commit b7261ce8ea
26 changed files with 846 additions and 648 deletions

1
.gitignore vendored
View file

@ -3,3 +3,4 @@ zig-cache/
zig-out/
valgrind.log
.zig-cache
zig-pkg

275
build.zig
View file

@ -12,7 +12,7 @@ const InitSystem = enum {
freebsd,
};
const min_zig_string = "0.15.0";
const min_zig_string = "0.16.0";
const current_zig = builtin.zig_version;
// Implementing zig version detection through compile time
@ -23,7 +23,7 @@ comptime {
}
}
const ly_version = std.SemanticVersion{ .major = 1, .minor = 4, .patch = 0 };
const ly_version = std.SemanticVersion{ .major = 1, .minor = 5, .patch = 0 };
var dest_directory: []const u8 = undefined;
var config_directory: []const u8 = undefined;
@ -67,8 +67,8 @@ pub fn build(b: *std.Build) !void {
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
}),
// Here until the native backend matures in terms of performance
.use_llvm = true,
});
@ -80,9 +80,8 @@ pub fn build(b: *std.Build) !void {
const clap = b.dependency("clap", .{ .target = target, .optimize = optimize });
exe.root_module.addImport("clap", clap.module("clap"));
exe.linkSystemLibrary("pam");
if (enable_x11_support) exe.linkSystemLibrary("xcb");
exe.linkLibC();
exe.root_module.linkSystemLibrary("pam", .{});
if (enable_x11_support) exe.root_module.linkSystemLibrary("xcb", .{});
b.installArtifact(exe);
@ -113,6 +112,8 @@ pub fn build(b: *std.Build) !void {
pub fn Installer(install_config: bool) type {
return struct {
pub fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
var threaded: std.Io.Threaded = .init_single_threaded;
const io = threaded.io();
const allocator = step.owner.allocator;
var patch_map = PatchMap.init(allocator);
@ -127,75 +128,75 @@ pub fn Installer(install_config: bool) type {
// instead to shutdown the system.
try patch_map.put("$PLATFORM_SHUTDOWN_ARG", if (init_system == .freebsd) "-p" else "-a");
try install_ly(allocator, patch_map, install_config);
try install_service(allocator, patch_map);
try install_ly(allocator, io, patch_map, install_config);
try install_service(allocator, io, patch_map);
}
};
}
fn install_ly(allocator: std.mem.Allocator, patch_map: PatchMap, install_config: bool) !void {
const ly_config_directory = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly" });
fn install_ly(allocator: std.mem.Allocator, io: std.Io, patch_map: PatchMap, install_config: bool) !void {
const ly_config_directory = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly" });
std.fs.cwd().makePath(ly_config_directory) catch {
std.Io.Dir.cwd().createDirPath(io, ly_config_directory) catch {
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_config_directory});
};
const ly_custom_sessions_directory = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly/custom-sessions" });
const ly_custom_sessions_directory = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly/custom-sessions" });
std.fs.cwd().makePath(ly_custom_sessions_directory) catch {
std.Io.Dir.cwd().createDirPath(io, ly_custom_sessions_directory) catch {
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_custom_sessions_directory});
};
const ly_lang_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly/lang" });
std.fs.cwd().makePath(ly_lang_path) catch {
const ly_lang_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/ly/lang" });
std.Io.Dir.cwd().createDirPath(io, ly_lang_path) catch {
std.debug.print("warn: {s} already exists as a directory.\n", .{ly_lang_path});
};
{
const exe_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/bin" });
std.fs.cwd().makePath(exe_path) catch {
const exe_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/bin" });
std.Io.Dir.cwd().createDirPath(io, exe_path) catch {
if (!std.mem.eql(u8, dest_directory, "")) {
std.debug.print("warn: {s} already exists as a directory.\n", .{exe_path});
}
};
var executable_dir = std.fs.cwd().openDir(exe_path, .{}) catch unreachable;
defer executable_dir.close();
var executable_dir = std.Io.Dir.cwd().openDir(io, exe_path, .{}) catch unreachable;
defer executable_dir.close(io);
try installFile("zig-out/bin/ly", executable_dir, exe_path, executable_name, .{});
try installFile(io, "zig-out/bin/ly", executable_dir, exe_path, executable_name, .{});
}
{
var config_dir = std.fs.cwd().openDir(ly_config_directory, .{}) catch unreachable;
defer config_dir.close();
var config_dir = std.Io.Dir.cwd().openDir(io, ly_config_directory, .{}) catch unreachable;
defer config_dir.close(io);
if (install_config) {
const patched_config = try patchFile(allocator, "res/config.ini", patch_map);
try installText(patched_config, config_dir, ly_config_directory, "config.ini", .{});
const patched_config = try patchFile(allocator, io, "res/config.ini", patch_map);
try installText(io, patched_config, config_dir, ly_config_directory, "config.ini", .{});
try installFile("res/startup.sh", config_dir, ly_config_directory, "startup.sh", .{ .override_mode = 0o755 });
try installFile(io, "res/startup.sh", config_dir, ly_config_directory, "startup.sh", .{ .permissions = .fromMode(0o755) });
}
const patched_example_config = try patchFile(allocator, "res/config.ini", patch_map);
try installText(patched_example_config, config_dir, ly_config_directory, "config.ini.example", .{});
const patched_example_config = try patchFile(allocator, io, "res/config.ini", patch_map);
try installText(io, patched_example_config, config_dir, ly_config_directory, "config.ini.example", .{});
const patched_setup = try patchFile(allocator, "res/setup.sh", patch_map);
try installText(patched_setup, config_dir, ly_config_directory, "setup.sh", .{ .mode = 0o755 });
const patched_setup = try patchFile(allocator, io, "res/setup.sh", patch_map);
try installText(io, patched_setup, config_dir, ly_config_directory, "setup.sh", .{ .permissions = .fromMode(0o755) });
try installFile("res/example.dur", config_dir, ly_config_directory, "example.dur", .{ .override_mode = 0o755 });
try installFile(io, "res/example.dur", config_dir, ly_config_directory, "example.dur", .{ .permissions = .fromMode(0o755) });
}
{
var custom_sessions_dir = std.fs.cwd().openDir(ly_custom_sessions_directory, .{}) catch unreachable;
defer custom_sessions_dir.close();
var custom_sessions_dir = std.Io.Dir.cwd().openDir(io, ly_custom_sessions_directory, .{}) catch unreachable;
defer custom_sessions_dir.close(io);
const patched_readme = try patchFile(allocator, "res/custom-sessions/README", patch_map);
try installText(patched_readme, custom_sessions_dir, ly_custom_sessions_directory, "README", .{});
const patched_readme = try patchFile(allocator, io, "res/custom-sessions/README", patch_map);
try installText(io, patched_readme, custom_sessions_dir, ly_custom_sessions_directory, "README", .{});
}
{
var lang_dir = std.fs.cwd().openDir(ly_lang_path, .{}) catch unreachable;
defer lang_dir.close();
var lang_dir = std.Io.Dir.cwd().openDir(io, ly_lang_path, .{}) catch unreachable;
defer lang_dir.close(io);
const languages = [_][]const u8{
"ar.ini",
@ -221,66 +222,66 @@ fn install_ly(allocator: std.mem.Allocator, patch_map: PatchMap, install_config:
};
inline for (languages) |language| {
try installFile("res/lang/" ++ language, lang_dir, ly_lang_path, language, .{});
try installFile(io, "res/lang/" ++ language, lang_dir, ly_lang_path, language, .{});
}
}
{
const pam_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/pam.d" });
std.fs.cwd().makePath(pam_path) catch {
const pam_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/pam.d" });
std.Io.Dir.cwd().createDirPath(io, pam_path) catch {
if (!std.mem.eql(u8, dest_directory, "")) {
std.debug.print("warn: {s} already exists as a directory.\n", .{pam_path});
}
};
var pam_dir = std.fs.cwd().openDir(pam_path, .{}) catch unreachable;
defer pam_dir.close();
var pam_dir = std.Io.Dir.cwd().openDir(io, pam_path, .{}) catch unreachable;
defer pam_dir.close(io);
try installFile(if (init_system == .freebsd) "res/pam.d/ly-freebsd" else "res/pam.d/ly-linux", pam_dir, pam_path, "ly", .{ .override_mode = 0o644 });
try installFile(if (init_system == .freebsd) "res/pam.d/ly-freebsd-autologin" else "res/pam.d/ly-linux-autologin", pam_dir, pam_path, "ly-autologin", .{ .override_mode = 0o644 });
try installFile(io, if (init_system == .freebsd) "res/pam.d/ly-freebsd" else "res/pam.d/ly-linux", pam_dir, pam_path, "ly", .{ .permissions = .fromMode(0o644) });
try installFile(io, if (init_system == .freebsd) "res/pam.d/ly-freebsd-autologin" else "res/pam.d/ly-linux-autologin", pam_dir, pam_path, "ly-autologin", .{ .permissions = .fromMode(0o644) });
}
}
fn install_service(allocator: std.mem.Allocator, patch_map: PatchMap) !void {
fn install_service(allocator: std.mem.Allocator, io: std.Io, patch_map: PatchMap) !void {
switch (init_system) {
.systemd => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/lib/systemd/system" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/lib/systemd/system" });
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
defer service_dir.close(io);
const patched_service = try patchFile(allocator, "res/ly@.service", patch_map);
try installText(patched_service, service_dir, service_path, "ly@.service", .{ .mode = 0o644 });
const patched_service = try patchFile(allocator, io, "res/ly@.service", patch_map);
try installText(io, patched_service, service_dir, service_path, "ly@.service", .{ .permissions = .fromMode(0o644) });
const patched_kmsconvt_service = try patchFile(allocator, "res/ly-kmsconvt@.service", patch_map);
try installText(patched_kmsconvt_service, service_dir, service_path, "ly-kmsconvt@.service", .{ .mode = 0o644 });
const patched_kmsconvt_service = try patchFile(allocator, io, "res/ly-kmsconvt@.service", patch_map);
try installText(io, patched_kmsconvt_service, service_dir, service_path, "ly-kmsconvt@.service", .{ .permissions = .fromMode(0o644) });
},
.openrc => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/init.d" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/init.d" });
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
defer service_dir.close(io);
const patched_service = try patchFile(allocator, "res/ly-openrc", patch_map);
try installText(patched_service, service_dir, service_path, executable_name, .{ .mode = 0o755 });
const patched_service = try patchFile(allocator, io, "res/ly-openrc", patch_map);
try installText(io, patched_service, service_dir, service_path, executable_name, .{ .permissions = .fromMode(0o755) });
},
.runit => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/sv/ly" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/sv/ly" });
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
defer service_dir.close(io);
const supervise_path = try std.fs.path.join(allocator, &[_][]const u8{ service_path, "supervise" });
const supervise_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ service_path, "supervise" });
const patched_conf = try patchFile(allocator, "res/ly-runit-service/conf", patch_map);
try installText(patched_conf, service_dir, service_path, "conf", .{});
const patched_conf = try patchFile(allocator, io, "res/ly-runit-service/conf", patch_map);
try installText(io, patched_conf, service_dir, service_path, "conf", .{});
try installFile("res/ly-runit-service/finish", service_dir, service_path, "finish", .{ .override_mode = 0o755 });
try installFile(io, "res/ly-runit-service/finish", service_dir, service_path, "finish", .{ .permissions = .fromMode(0o755) });
const patched_run = try patchFile(allocator, "res/ly-runit-service/run", patch_map);
try installText(patched_run, service_dir, service_path, "run", .{ .mode = 0o755 });
const patched_run = try patchFile(allocator, io, "res/ly-runit-service/run", patch_map);
try installText(io, patched_run, service_dir, service_path, "run", .{ .permissions = .fromMode(0o755) });
std.fs.cwd().symLink("/run/runit/supervise.ly", supervise_path, .{}) catch |err| {
std.Io.Dir.cwd().symLink(io, "/run/runit/supervise.ly", supervise_path, .{}) catch |err| {
if (err == error.PathAlreadyExists) {
std.debug.print("warn: /run/runit/supervise.ly already exists as a symbolic link.\n", .{});
} else {
@ -290,49 +291,49 @@ fn install_service(allocator: std.mem.Allocator, patch_map: PatchMap) !void {
std.debug.print("info: installed symlink /run/runit/supervise.ly\n", .{});
},
.s6 => {
const admin_service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/s6/adminsv/default/contents.d" });
std.fs.cwd().makePath(admin_service_path) catch {};
var admin_service_dir = std.fs.cwd().openDir(admin_service_path, .{}) catch unreachable;
defer admin_service_dir.close();
const admin_service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/s6/adminsv/default/contents.d" });
std.Io.Dir.cwd().createDirPath(io, admin_service_path) catch {};
var admin_service_dir = std.Io.Dir.cwd().openDir(io, admin_service_path, .{}) catch unreachable;
defer admin_service_dir.close(io);
const file = try admin_service_dir.createFile("ly-srv", .{});
file.close();
const file = try admin_service_dir.createFile(io, "ly-srv", .{});
file.close(io);
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/s6/sv/ly-srv" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/s6/sv/ly-srv" });
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
defer service_dir.close(io);
const patched_run = try patchFile(allocator, "res/ly-s6/run", patch_map);
try installText(patched_run, service_dir, service_path, "run", .{ .mode = 0o755 });
const patched_run = try patchFile(allocator, io, "res/ly-s6/run", patch_map);
try installText(io, patched_run, service_dir, service_path, "run", .{ .permissions = .fromMode(0o755) });
try installFile("res/ly-s6/type", service_dir, service_path, "type", .{});
try installFile(io, "res/ly-s6/type", service_dir, service_path, "type", .{});
},
.dinit => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/dinit.d" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/dinit.d" });
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
defer service_dir.close(io);
const patched_service = try patchFile(allocator, "res/ly-dinit", patch_map);
try installText(patched_service, service_dir, service_path, "ly", .{});
const patched_service = try patchFile(allocator, io, "res/ly-dinit", patch_map);
try installText(io, patched_service, service_dir, service_path, "ly", .{});
},
.sysvinit => {
const service_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/init.d" });
std.fs.cwd().makePath(service_path) catch {};
var service_dir = std.fs.cwd().openDir(service_path, .{}) catch unreachable;
defer service_dir.close();
const service_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, config_directory, "/init.d" });
std.Io.Dir.cwd().createDirPath(io, service_path) catch {};
var service_dir = std.Io.Dir.cwd().openDir(io, service_path, .{}) catch unreachable;
defer service_dir.close(io);
const patched_service = try patchFile(allocator, "res/ly-sysvinit", patch_map);
try installText(patched_service, service_dir, service_path, "ly", .{ .mode = 0o755 });
const patched_service = try patchFile(allocator, io, "res/ly-sysvinit", patch_map);
try installText(io, patched_service, service_dir, service_path, "ly", .{ .permissions = .fromMode(0o755) });
},
.freebsd => {
const exe_path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/bin" });
var executable_dir = std.fs.cwd().openDir(exe_path, .{}) catch unreachable;
defer executable_dir.close();
const exe_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix_directory, "/bin" });
var executable_dir = std.Io.Dir.cwd().openDir(io, exe_path, .{}) catch unreachable;
defer executable_dir.close(io);
const patched_wrapper = try patchFile(allocator, "res/ly-freebsd-wrapper", patch_map);
try installText(patched_wrapper, executable_dir, exe_path, "ly_wrapper", .{ .mode = 0o755 });
const patched_wrapper = try patchFile(allocator, io, "res/ly-freebsd-wrapper", patch_map);
try installText(io, patched_wrapper, executable_dir, exe_path, "ly_wrapper", .{ .permissions = .fromMode(0o755) });
},
}
}
@ -340,33 +341,35 @@ fn install_service(allocator: std.mem.Allocator, patch_map: PatchMap) !void {
pub fn Uninstaller(uninstall_config: bool) type {
return struct {
pub fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) !void {
var threaded: std.Io.Threaded = .init_single_threaded;
const io = threaded.io();
const allocator = step.owner.allocator;
if (uninstall_config) {
try deleteTree(allocator, config_directory, "/ly", "ly config directory not found");
try deleteTree(allocator, io, config_directory, "/ly", "ly config directory not found");
}
const exe_path = try std.fs.path.join(allocator, &[_][]const u8{ prefix_directory, "/bin/", executable_name });
const exe_path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ prefix_directory, "/bin/", executable_name });
var success = true;
std.fs.cwd().deleteFile(exe_path) catch {
std.Io.Dir.cwd().deleteFile(io, exe_path) catch {
std.debug.print("warn: ly executable not found\n", .{});
success = false;
};
if (success) std.debug.print("info: deleted {s}\n", .{exe_path});
try deleteFile(allocator, config_directory, "/pam.d/ly", "ly pam file not found");
try deleteFile(allocator, io, config_directory, "/pam.d/ly", "ly pam file not found");
switch (init_system) {
.systemd => try deleteFile(allocator, prefix_directory, "/lib/systemd/system/ly@.service", "systemd service not found"),
.openrc => try deleteFile(allocator, config_directory, "/init.d/ly", "openrc service not found"),
.runit => try deleteTree(allocator, config_directory, "/sv/ly", "runit service not found"),
.systemd => try deleteFile(allocator, io, prefix_directory, "/lib/systemd/system/ly@.service", "systemd service not found"),
.openrc => try deleteFile(allocator, io, config_directory, "/init.d/ly", "openrc service not found"),
.runit => try deleteTree(allocator, io, config_directory, "/sv/ly", "runit service not found"),
.s6 => {
try deleteTree(allocator, config_directory, "/s6/sv/ly-srv", "s6 service not found");
try deleteFile(allocator, config_directory, "/s6/adminsv/default/contents.d/ly-srv", "s6 admin service not found");
try deleteTree(allocator, io, config_directory, "/s6/sv/ly-srv", "s6 service not found");
try deleteFile(allocator, io, config_directory, "/s6/adminsv/default/contents.d/ly-srv", "s6 admin service not found");
},
.dinit => try deleteFile(allocator, config_directory, "/dinit.d/ly", "dinit service not found"),
.sysvinit => try deleteFile(allocator, config_directory, "/init.d/ly", "sysvinit service not found"),
.freebsd => try deleteFile(allocator, prefix_directory, "/bin/ly_wrapper", "freebsd wrapper not found"),
.dinit => try deleteFile(allocator, io, config_directory, "/dinit.d/ly", "dinit service not found"),
.sysvinit => try deleteFile(allocator, io, config_directory, "/init.d/ly", "sysvinit service not found"),
.freebsd => try deleteFile(allocator, io, prefix_directory, "/bin/ly_wrapper", "freebsd wrapper not found"),
}
}
};
@ -384,11 +387,11 @@ fn getVersionStr(b: *std.Build, name: []const u8, version: std.SemanticVersion)
"--match",
"*.*.*",
"--tags",
}, &status, .Ignore) catch {
}, &status, .ignore) catch {
return version_str;
};
var git_describe = std.mem.trim(u8, git_describe_raw, " \n\r");
git_describe = std.mem.trimLeft(u8, git_describe, "v");
git_describe = std.mem.trimStart(u8, git_describe, "v");
switch (std.mem.count(u8, git_describe, "-")) {
0 => {
@ -401,7 +404,7 @@ fn getVersionStr(b: *std.Build, name: []const u8, version: std.SemanticVersion)
2 => {
// Untagged development build (e.g. 0.10.0-dev.2025+ecf0050a9).
var it = std.mem.splitScalar(u8, git_describe, '-');
const tagged_ancestor = std.mem.trimLeft(u8, it.first(), "v");
const tagged_ancestor = std.mem.trimStart(u8, it.first(), "v");
const commit_height = it.next().?;
const commit_id = it.next().?;
@ -428,25 +431,26 @@ fn getVersionStr(b: *std.Build, name: []const u8, version: std.SemanticVersion)
}
fn installFile(
io: std.Io,
source_file: []const u8,
destination_directory: std.fs.Dir,
destination_directory: std.Io.Dir,
destination_directory_path: []const u8,
destination_file: []const u8,
options: std.fs.Dir.CopyFileOptions,
options: std.Io.Dir.CopyFileOptions,
) !void {
try std.fs.cwd().copyFile(source_file, destination_directory, destination_file, options);
try std.Io.Dir.cwd().copyFile(source_file, destination_directory, destination_file, io, options);
std.debug.print("info: installed {s}/{s}\n", .{ destination_directory_path, destination_file });
}
fn patchFile(allocator: std.mem.Allocator, source_file: []const u8, patch_map: PatchMap) ![]const u8 {
var file = try std.fs.cwd().openFile(source_file, .{});
defer file.close();
fn patchFile(allocator: std.mem.Allocator, io: std.Io, source_file: []const u8, patch_map: PatchMap) ![]const u8 {
var file = try std.Io.Dir.cwd().openFile(io, source_file, .{});
defer file.close(io);
const stat = try file.stat();
const stat = try file.stat(io);
var buffer: [4096]u8 = undefined;
var reader = file.reader(&buffer);
var text = try reader.interface.readAlloc(allocator, stat.size);
var reader = file.reader(io, &buffer);
var text = try reader.interface.readAlloc(allocator, @intCast(stat.size));
var iterator = patch_map.iterator();
while (iterator.next()) |kv| {
@ -459,17 +463,18 @@ fn patchFile(allocator: std.mem.Allocator, source_file: []const u8, patch_map: P
}
fn installText(
io: std.Io,
text: []const u8,
destination_directory: std.fs.Dir,
destination_directory: std.Io.Dir,
destination_directory_path: []const u8,
destination_file: []const u8,
options: std.fs.File.CreateFlags,
options: std.Io.File.CreateFlags,
) !void {
var file = try destination_directory.createFile(destination_file, options);
defer file.close();
var file = try destination_directory.createFile(io, destination_file, options);
defer file.close(io);
var buffer: [1024]u8 = undefined;
var writer = file.writer(&buffer);
var writer = file.writer(io, &buffer);
try writer.interface.writeAll(text);
try writer.interface.flush();
@ -478,13 +483,14 @@ fn installText(
fn deleteFile(
allocator: std.mem.Allocator,
io: std.Io,
prefix: []const u8,
file: []const u8,
warning: []const u8,
) !void {
const path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix, file });
const path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix, file });
std.fs.cwd().deleteFile(path) catch |err| {
std.Io.Dir.cwd().deleteFile(io, path) catch |err| {
if (err == error.FileNotFound) {
std.debug.print("warn: {s}\n", .{warning});
return;
@ -498,13 +504,14 @@ fn deleteFile(
fn deleteTree(
allocator: std.mem.Allocator,
io: std.Io,
prefix: []const u8,
directory: []const u8,
warning: []const u8,
) !void {
const path = try std.fs.path.join(allocator, &[_][]const u8{ dest_directory, prefix, directory });
const path = try std.Io.Dir.path.join(allocator, &[_][]const u8{ dest_directory, prefix, directory });
var dir = std.fs.cwd().openDir(path, .{}) catch |err| {
var dir = std.Io.Dir.cwd().openDir(io, path, .{}) catch |err| {
if (err == error.FileNotFound) {
std.debug.print("warn: {s}\n", .{warning});
return;
@ -512,9 +519,9 @@ fn deleteTree(
return err;
};
dir.close();
dir.close(io);
try std.fs.cwd().deleteTree(path);
try std.Io.Dir.cwd().deleteTree(io, path);
std.debug.print("info: deleted {s}\n", .{path});
}

View file

@ -1,16 +1,20 @@
.{
.name = .ly,
.version = "1.4.0",
.version = "1.5.0",
.fingerprint = 0xa148ffcc5dc2cb59,
.minimum_zig_version = "0.15.0",
.minimum_zig_version = "0.16.0",
.dependencies = .{
.ly_ui = .{
.path = "ly-ui",
},
.clap = .{
.url = "git+https://github.com/Hejsil/zig-clap#5289e0753cd274d65344bef1c114284c633536ea",
.hash = "clap-0.11.0-oBajB-HnAQDPCKYzwF7rO3qDFwRcD39Q0DALlTSz5H7e",
.url = "git+https://github.com/Hejsil/zig-clap#fc1e5cc3f6d9d3001112385ee6256d694e959d2f",
.hash = "clap-0.11.0-oBajB7foAQC3Iyn4IVCkUdYaOVVng5IZkSncySTjNig1",
},
},
.paths = .{""},
.paths = .{
"build.zig",
"build.zig.zon",
"src",
},
}

2
create_vendor_tarball.sh Executable file
View file

@ -0,0 +1,2 @@
#!/bin/sh
tar --zstd -cvf vendor.tar.zst zig-pkg ly-ui/zig-pkg ly-core/zig-pkg

View file

@ -1,4 +1,5 @@
const std = @import("std");
const Translator = @import("translate_c").Translator;
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
@ -12,6 +13,37 @@ pub fn build(b: *std.Build) void {
const zigini = b.dependency("zigini", .{ .target = target, .optimize = optimize });
mod.addImport("zigini", zigini.module("zigini"));
const translate_c = b.dependency("translate_c", .{
.target = target,
.optimize = optimize,
});
addCImport(b, mod, translate_c, target, optimize, "pam", "#include <security/pam_appl.h>");
addCImport(b, mod, translate_c, target, optimize, "utmp", "#include <utmpx.h>");
addCImport(b, mod, translate_c, target, optimize, "xcb", "#include <xcb/xcb.h>");
if (target.result.os.tag == .freebsd) {
addCImport(b, mod, translate_c, target, optimize, "pwd",
\\#include <pwd.h>
\\#include <sys/types.h>
\\#include <login_cap.h>
);
} else {
addCImport(b, mod, translate_c, target, optimize, "pwd", "#include <pwd.h>");
}
addCImport(b, mod, translate_c, target, optimize, "stdlib", "#include <stdlib.h>");
addCImport(b, mod, translate_c, target, optimize, "unistd", "#include <unistd.h>");
addCImport(b, mod, translate_c, target, optimize, "grp", "#include <grp.h>");
addCImport(b, mod, translate_c, target, optimize, "system_time", "#include <sys/time.h>");
addCImport(b, mod, translate_c, target, optimize, "time", "#include <time.h>");
if (target.result.os.tag == .linux) {
addCImport(b, mod, translate_c, target, optimize, "kd", "#include <sys/kd.h>");
addCImport(b, mod, translate_c, target, optimize, "vt", "#include <sys/vt.h>");
} else if (target.result.os.tag == .freebsd) {
addCImport(b, mod, translate_c, target, optimize, "kbio", "#include <sys/kbio.h>");
addCImport(b, mod, translate_c, target, optimize, "consio", "#include <sys/consio.h>");
}
const mod_tests = b.addTest(.{
.root_module = mod,
});
@ -20,3 +52,20 @@ pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Run tests");
test_step.dependOn(&run_mod_tests.step);
}
fn addCImport(
b: *std.Build,
mod: *std.Build.Module,
translate_c: *std.Build.Dependency,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
comptime name: []const u8,
comptime bytes: []const u8,
) void {
const pam: Translator = .init(translate_c, .{
.c_source_file = b.addWriteFiles().add(name ++ ".h", bytes),
.target = target,
.optimize = optimize,
});
mod.addImport(name, pam.mod);
}

View file

@ -1,12 +1,16 @@
.{
.name = .ly_core,
.version = "1.0.0",
.version = "1.1.0",
.fingerprint = 0xddda7afda795472,
.minimum_zig_version = "0.15.0",
.minimum_zig_version = "0.16.0",
.dependencies = .{
.zigini = .{
.url = "git+https://github.com/AshAmetrine/zigini?ref=master#831f6aff55703b7fa34b43c972d60eb3d5d6f4a4",
.hash = "zigini-0.3.2-BSkB7UVDAAAErcEC6s7zF9bKTe7CjdBgrV35K3DGHpbr",
.url = "git+https://github.com/AshAmetrine/zigini?ref=master#a665d081dda42664a96da2840ea09c5ccf9d0692",
.hash = "zigini-0.5.0-BSkB7e9WAACfyCBABNZiWL3gFMw18GKn3qBcPs8L1Ec1",
},
.translate_c = .{
.url = "git+https://codeberg.org/ziglang/translate-c#7a1a9fdc4ab00835748a6657ecbb835e3d5d45f7",
.hash = "translate_c-0.0.0-Q_BUWvP1BgCjAk6PWv5286tOlvzD9-X-NkuTzh0KxY0Q",
},
},
.paths = .{

View file

@ -5,27 +5,27 @@ const LogFile = @This();
path: []const u8,
could_open_log_file: bool = undefined,
file: std.fs.File = undefined,
file: std.Io.File = undefined,
buffer: []u8,
file_writer: std.fs.File.Writer = undefined,
file_writer: std.Io.File.Writer = undefined,
pub fn init(path: []const u8, buffer: []u8) !LogFile {
pub fn init(io: std.Io, path: []const u8, buffer: []u8) !LogFile {
var log_file = LogFile{ .path = path, .buffer = buffer };
log_file.could_open_log_file = try openLogFile(path, &log_file);
log_file.could_open_log_file = try openLogFile(io, path, &log_file);
return log_file;
}
pub fn reinit(self: *LogFile) !void {
self.could_open_log_file = try openLogFile(self.path, self);
pub fn reinit(self: *LogFile, io: std.Io) !void {
self.could_open_log_file = try openLogFile(io, self.path, self);
}
pub fn deinit(self: *LogFile) void {
self.file.close();
pub fn deinit(self: *LogFile, io: std.Io) void {
self.file.close(io);
}
pub fn info(self: *LogFile, category: []const u8, comptime message: []const u8, args: anytype) !void {
pub fn info(self: *LogFile, io: std.Io, category: []const u8, comptime message: []const u8, args: anytype) !void {
var buffer: [128:0]u8 = undefined;
const time = interop.timeAsString(&buffer, "%Y-%m-%d %H:%M:%S");
const time = interop.timeAsString(io, &buffer, "%Y-%m-%d %H:%M:%S");
try self.file_writer.interface.print("{s} [info/{s}] ", .{ time, category });
try self.file_writer.interface.print(message, args);
@ -33,9 +33,9 @@ pub fn info(self: *LogFile, category: []const u8, comptime message: []const u8,
try self.file_writer.interface.flush();
}
pub fn err(self: *LogFile, category: []const u8, comptime message: []const u8, args: anytype) !void {
pub fn err(self: *LogFile, io: std.Io, category: []const u8, comptime message: []const u8, args: anytype) !void {
var buffer: [128:0]u8 = undefined;
const time = interop.timeAsString(&buffer, "%Y-%m-%d %H:%M:%S");
const time = interop.timeAsString(io, &buffer, "%Y-%m-%d %H:%M:%S");
try self.file_writer.interface.print("{s} [err/{s}] ", .{ time, category });
try self.file_writer.interface.print(message, args);
@ -43,10 +43,10 @@ pub fn err(self: *LogFile, category: []const u8, comptime message: []const u8, a
try self.file_writer.interface.flush();
}
fn openLogFile(path: []const u8, log_file: *LogFile) !bool {
fn openLogFile(io: std.Io, path: []const u8, log_file: *LogFile) !bool {
var could_open_log_file = true;
open_log_file: {
log_file.file = std.fs.cwd().openFile(path, .{ .mode = .write_only }) catch std.fs.cwd().createFile(path, .{ .mode = 0o666 }) catch {
log_file.file = std.Io.Dir.cwd().openFile(io, path, .{ .mode = .write_only }) catch std.Io.Dir.cwd().createFile(io, path, .{ .permissions = .fromMode(0o666) }) catch {
// If we could neither open an existing log file nor create a new
// one, abort.
could_open_log_file = false;
@ -55,14 +55,14 @@ fn openLogFile(path: []const u8, log_file: *LogFile) !bool {
}
if (!could_open_log_file) {
log_file.file = try std.fs.openFileAbsolute("/dev/null", .{ .mode = .write_only });
log_file.file = try std.Io.Dir.openFileAbsolute(io, "/dev/null", .{ .mode = .write_only });
}
var log_file_writer = log_file.file.writer(log_file.buffer);
var log_file_writer = log_file.file.writer(io, log_file.buffer);
// Seek to the end of the log file
if (could_open_log_file) {
const stat = try log_file.file.stat();
const stat = try log_file.file.stat(io);
try log_file_writer.seekTo(stat.size);
}

View file

@ -1,10 +1,12 @@
const std = @import("std");
const ErrInt = std.meta.Int(.unsigned, @bitSizeOf(anyerror));
const PaddingInt = std.meta.Int(.unsigned, 8 - (@bitSizeOf(ErrInt) + @bitSizeOf(bool)) % 8);
const ErrorHandler = packed struct {
has_error: bool = false,
err_int: ErrInt = 0,
padding: PaddingInt = 0,
};
const SharedError = @This();
@ -17,7 +19,7 @@ pub fn init(
write_error_event_fn: ?*const fn (anyerror, *anyopaque) anyerror!void,
ctx: ?*anyopaque,
) !SharedError {
const data = try std.posix.mmap(null, @sizeOf(ErrorHandler), std.posix.PROT.READ | std.posix.PROT.WRITE, .{ .TYPE = .SHARED, .ANONYMOUS = true }, -1, 0);
const data = try std.posix.mmap(null, @sizeOf(ErrorHandler), .{ .READ = true, .WRITE = true }, .{ .TYPE = .SHARED, .ANONYMOUS = true }, -1, 0);
return .{
.data = data,
@ -31,9 +33,8 @@ pub fn deinit(self: *SharedError) void {
}
pub fn writeError(self: SharedError, err: anyerror) void {
var buf_stream = std.io.fixedBufferStream(self.data);
const writer = buf_stream.writer();
writer.writeStruct(ErrorHandler{ .has_error = true, .err_int = @intFromError(err) }) catch {};
var writer: std.Io.Writer = .fixed(self.data);
writer.writeStruct(ErrorHandler{ .has_error = true, .err_int = @intFromError(err) }, .native) catch {};
if (self.write_error_event_fn) |write_error_event_fn| {
@call(.auto, write_error_event_fn, .{ err, self.ctx.? }) catch {};
@ -41,9 +42,8 @@ pub fn writeError(self: SharedError, err: anyerror) void {
}
pub fn readError(self: SharedError) ?anyerror {
var buf_stream = std.io.fixedBufferStream(self.data);
const reader = buf_stream.reader();
const err_handler = try reader.readStruct(ErrorHandler);
var reader: std.Io.Reader = .fixed(self.data);
const err_handler = try reader.takeStruct(ErrorHandler, .native);
if (err_handler.has_error)
return @errorFromInt(err_handler.err_int);

View file

@ -1,49 +1,17 @@
const std = @import("std");
const builtin = @import("builtin");
const UidRange = @import("UidRange.zig");
const pwd = @import("pwd");
const stdlib = @import("stdlib");
const unistd = @import("unistd");
const grp = @import("grp");
const system_time = @import("system_time");
const time = @import("time");
pub const pam = @cImport({
@cInclude("security/pam_appl.h");
});
pub const utmp = @cImport({
@cInclude("utmpx.h");
});
pub const pam = @import("pam");
pub const utmp = @import("utmp");
// Exists for X11 support only
pub const xcb = @cImport({
@cInclude("xcb/xcb.h");
});
const pwd = @cImport({
@cInclude("pwd.h");
// We include a FreeBSD-specific header here since login_cap.h references
// the passwd struct directly, so we can't import it separately
if (builtin.os.tag == .freebsd) {
@cInclude("sys/types.h");
@cInclude("login_cap.h");
}
});
const stdlib = @cImport({
@cInclude("stdlib.h");
});
const unistd = @cImport({
@cInclude("unistd.h");
});
const grp = @cImport({
@cInclude("grp.h");
});
const system_time = @cImport({
@cInclude("sys/time.h");
});
const time = @cImport({
@cInclude("time.h");
});
pub const xcb = @import("xcb");
pub const TimeOfDay = struct {
seconds: i64,
@ -63,13 +31,8 @@ pub const UsernameEntry = struct {
fn PlatformStruct() type {
return switch (builtin.os.tag) {
.linux => struct {
pub const kd = @cImport({
@cInclude("sys/kd.h");
});
pub const vt = @cImport({
@cInclude("sys/vt.h");
});
pub const kd = @import("kd");
pub const vt = @import("vt");
pub const LedState = c_char;
pub const get_led_state = kd.KDGKBLED;
@ -83,8 +46,8 @@ fn PlatformStruct() type {
const status = grp.initgroups(username, @intCast(entry.gid));
if (status != 0) return error.GroupInitializationFailed;
std.posix.setgid(@intCast(entry.gid)) catch return error.SetUserGidFailed;
std.posix.setuid(@intCast(entry.uid)) catch return error.SetUserUidFailed;
if (isError(std.posix.system.setgid(@intCast(entry.gid)))) return error.SetUserGidFailed;
if (isError(std.posix.system.setuid(@intCast(entry.uid)))) return error.SetUserUidFailed;
}
// Procedure:
@ -96,14 +59,14 @@ fn PlatformStruct() type {
// 4. Finally, compare the major and minor device numbers with the
// extracted values. If they correspond, parse [dir] to get the
// TTY ID
pub fn getActiveTtyImpl(allocator: std.mem.Allocator, use_kmscon_vt: bool) !u8 {
pub fn getActiveTtyImpl(allocator: std.mem.Allocator, io: std.Io, use_kmscon_vt: bool) !u8 {
var file_buffer: [256]u8 = undefined;
if (use_kmscon_vt) {
var file = try std.fs.openFileAbsolute("/sys/class/tty/tty0/active", .{});
defer file.close();
var file = try std.Io.Dir.openFileAbsolute(io, "/sys/class/tty/tty0/active", .{});
defer file.close(io);
var reader = file.reader(&file_buffer);
var reader = file.reader(io, &file_buffer);
var buffer: [16]u8 = undefined;
const read = try readBuffer(&reader.interface, &buffer);
@ -115,10 +78,10 @@ fn PlatformStruct() type {
var tty_minor: u16 = undefined;
{
var file = try std.fs.openFileAbsolute("/proc/self/stat", .{});
defer file.close();
var file = try std.Io.Dir.openFileAbsolute(io, "/proc/self/stat", .{});
defer file.close(io);
var reader = file.reader(&file_buffer);
var reader = file.reader(io, &file_buffer);
var buffer: [1024]u8 = undefined;
const read = try readBuffer(&reader.interface, &buffer);
@ -136,18 +99,18 @@ fn PlatformStruct() type {
tty_minor = tty_nr % 256;
}
var directory = try std.fs.openDirAbsolute("/sys/class/tty", .{ .iterate = true });
defer directory.close();
var directory = try std.Io.Dir.openDirAbsolute(io, "/sys/class/tty", .{ .iterate = true });
defer directory.close(io);
var iterator = directory.iterate();
while (try iterator.next()) |entry| {
while (try iterator.next(io)) |entry| {
const path = try std.fmt.allocPrint(allocator, "/sys/class/tty/{s}/dev", .{entry.name});
defer allocator.free(path);
var file = try std.fs.openFileAbsolute(path, .{});
defer file.close();
var file = try std.Io.Dir.openFileAbsolute(io, path, .{});
defer file.close(io);
var reader = file.reader(&file_buffer);
var reader = file.reader(io, &file_buffer);
var buffer: [16]u8 = undefined;
const read = try readBuffer(&reader.interface, &buffer);
@ -170,11 +133,14 @@ fn PlatformStruct() type {
// This is very bad parsing, but we only need to get 2 values..
// and the format of the file seems to be standard? So this should
// be fine...
pub fn getUserIdRange(allocator: std.mem.Allocator, file_path: []const u8) !UidRange {
const login_defs_file = try std.fs.cwd().openFile(file_path, .{});
defer login_defs_file.close();
pub fn getUserIdRange(allocator: std.mem.Allocator, io: std.Io, file_path: []const u8) !UidRange {
const login_defs_file = try std.Io.Dir.cwd().openFile(io, file_path, .{});
defer login_defs_file.close(io);
const login_defs_buffer = try login_defs_file.readToEndAlloc(allocator, std.math.maxInt(u16));
var buffer: [4096]u8 = undefined;
var reader = login_defs_file.reader(io, &buffer);
const login_defs_buffer = try reader.interface.allocRemaining(allocator, .unlimited);
defer allocator.free(login_defs_buffer);
var iterator = std.mem.splitScalar(u8, login_defs_buffer, '\n');
@ -226,13 +192,8 @@ fn PlatformStruct() type {
}
},
.freebsd => struct {
pub const kbio = @cImport({
@cInclude("sys/kbio.h");
});
pub const consio = @cImport({
@cInclude("sys/consio.h");
});
pub const kbio = @import("kbio");
pub const consio = @import("consio");
pub const LedState = c_int;
pub const get_led_state = kbio.KDGETLED;
@ -255,11 +216,11 @@ fn PlatformStruct() type {
if (result != 0) return error.SetUserUidFailed;
}
pub fn getActiveTtyImpl(_: std.mem.Allocator, _: bool) !u8 {
pub fn getActiveTtyImpl(_: std.mem.Allocator, _: std.Io, _: bool) !u8 {
return error.FeatureUnimplemented;
}
pub fn getUserIdRange(_: std.mem.Allocator, _: []const u8) !UidRange {
pub fn getUserIdRange(_: std.mem.Allocator, _: std.Io, _: []const u8) !UidRange {
return .{
// Hardcoded default values chosen from
// /usr/src/usr.sbin/pw/pw_conf.c
@ -274,12 +235,28 @@ fn PlatformStruct() type {
const platform_struct = PlatformStruct();
// TODO 0.16.0: Can we get away with this?
pub fn isError(result: anytype) bool {
if (@typeInfo(@TypeOf(result)).int.signedness == .signed) {
return result < 0;
}
if (@typeInfo(@TypeOf(result)).int.signedness == .unsigned) {
return switch (builtin.os.tag) {
.linux => std.os.linux.errno(result) != .SUCCESS,
else => @compileError("interop.isError() not implemented for current target!"),
};
}
unreachable;
}
pub fn supportsUnicode() bool {
return builtin.os.tag == .linux or builtin.os.tag == .freebsd;
}
pub fn timeAsString(buf: [:0]u8, format: [:0]const u8) []u8 {
const timer = std.time.timestamp();
pub fn timeAsString(io: std.Io, buf: [:0]u8, format: [:0]const u8) []u8 {
const timer: isize = @intCast(std.Io.Timestamp.now(io, .real).toSeconds());
const tm_info = time.localtime(&timer);
const len = time.strftime(buf, buf.len, format, tm_info);
@ -298,8 +275,8 @@ pub fn getTimeOfDay() !TimeOfDay {
};
}
pub fn getActiveTty(allocator: std.mem.Allocator, use_kmscon_vt: bool) !u8 {
return platform_struct.getActiveTtyImpl(allocator, use_kmscon_vt);
pub fn getActiveTty(allocator: std.mem.Allocator, io: std.Io, use_kmscon_vt: bool) !u8 {
return platform_struct.getActiveTtyImpl(allocator, io, use_kmscon_vt);
}
pub fn switchTty(tty: u8) !void {
@ -402,6 +379,6 @@ pub fn closePasswordDatabase() void {
// This is very bad parsing, but we only need to get 2 values... and the format
// of the file doesn't seem to be standard? So this should be fine...
pub fn getUserIdRange(allocator: std.mem.Allocator, file_path: []const u8) !UidRange {
return platform_struct.getUserIdRange(allocator, file_path);
pub fn getUserIdRange(allocator: std.mem.Allocator, io: std.Io, file_path: []const u8) !UidRange {
return platform_struct.getUserIdRange(allocator, io, file_path);
}

View file

@ -27,6 +27,7 @@ pub fn IniParser(comptime Struct: type) type {
pub fn init(
allocator: std.mem.Allocator,
io: std.Io,
path: []const u8,
field_handler: ?fn (allocator: std.mem.Allocator, field: ini.IniField) ?ini.IniField,
) !Self {
@ -35,7 +36,7 @@ pub fn IniParser(comptime Struct: type) type {
var maybe_load_error: ?anyerror = null;
const structure = ini_struct.readFileToStruct(path, .{
const structure = ini_struct.readFileToStruct(io, path, .{
.fieldHandler = field_handler,
.errorHandler = errorHandler,
.comment_characters = "#",

View file

@ -1,4 +1,5 @@
const std = @import("std");
const Translator = @import("translate_c").Translator;
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
@ -17,15 +18,30 @@ pub fn build(b: *std.Build) void {
.optimize = optimize,
});
const translate_c = b.addTranslateC(.{
.root_source_file = termbox_dep.path("termbox2.h"),
const translate_c_dep = b.dependency("translate_c", .{
.target = target,
.optimize = optimize,
});
translate_c.defineCMacroRaw("TB_IMPL");
translate_c.defineCMacro("TB_OPT_ATTR_W", "32"); // Enable 24-bit color support + styling (32-bit)
const termbox2 = translate_c.addModule("termbox2");
mod.addImport("termbox2", termbox2);
const termbox2: Translator = .init(translate_c_dep, .{
.c_source_file = termbox_dep.path("termbox2.h"),
.target = target,
.optimize = optimize,
});
termbox2.defineCMacro("TB_IMPL", null);
// TODO 0.16.0: Workaround until Aro gets better...
// https://codeberg.org/ziglang/translate-c/issues/319
termbox2.defineCMacro("_XOPEN_SOURCE", "700");
termbox2.defineCMacro("TB_OPT_ATTR_W", "32"); // Enable 24-bit color support + styling (32-bit)
// TODO 0.16.0: Including <fcntl.h> with -OReleaseSafe causes
// __attribute__(__error__()) to be called. Below
// is the workaround.
termbox2.defineCMacro("_FORTIFY_SOURCE", "0");
// TODO 0.16.0: Needed for now
if (target.result.os.tag == .freebsd) {
termbox2.defineCMacro("__BSD_VISIBLE", "1");
}
mod.addImport("termbox2", termbox2.mod);
const mod_tests = b.addTest(.{
.root_module = mod,

View file

@ -1,15 +1,19 @@
.{
.name = .ly_ui,
.version = "1.0.0",
.version = "1.1.0",
.fingerprint = 0x8d11bf85a74ec803,
.minimum_zig_version = "0.15.0",
.minimum_zig_version = "0.16.0",
.dependencies = .{
.ly_core = .{
.path = "../ly-core",
},
.termbox2 = .{
.url = "git+https://github.com/AnErrupTion/termbox2?ref=master#496730697c662893eec43192f48ff616c2539da6",
.hash = "N-V-__8AAOEWBQDt5tNdIzIFY6n8DdZsCP-6MyLoNS20wgpA",
.url = "git+https://github.com/AnErrupTion/termbox2?ref=master#c7f241e8888ce243e1748b05c26a42fcfaaad936",
.hash = "N-V-__8AAAUXBQD6Fwpi9m0MBqWXFFaqW5l1lVrJC2Ynj7a-",
},
.translate_c = .{
.url = "git+https://codeberg.org/ziglang/translate-c#7a1a9fdc4ab00835748a6657ecbb835e3d5d45f7",
.hash = "translate_c-0.0.0-Q_BUWvP1BgCjAk6PWv5286tOlvzD9-X-NkuTzh0KxY0Q",
},
},
.paths = .{

View file

@ -97,6 +97,7 @@ active_widget_index: usize,
pub fn init(
allocator: Allocator,
io: std.Io,
options: InitOptions,
log_file: *LogFile,
random: Random,
@ -106,9 +107,9 @@ pub fn init(
if (options.full_color) {
_ = termbox.tb_set_output_mode(termbox.TB_OUTPUT_TRUECOLOR);
try log_file.info("tui", "termbox2 set to 24-bit color output mode", .{});
try log_file.info(io, "tui", "termbox2 set to 24-bit color output mode", .{});
} else {
try log_file.info("tui", "termbox2 set to eight-color output mode", .{});
try log_file.info(io, "tui", "termbox2 set to eight-color output mode", .{});
}
_ = termbox.tb_clear();
@ -119,7 +120,7 @@ pub fn init(
const width: usize = @intCast(termbox.tb_width());
const height: usize = @intCast(termbox.tb_height());
try log_file.info("tui", "screen resolution is {d}x{d}", .{ width, height });
try log_file.info(io, "tui", "screen resolution is {d}x{d}", .{ width, height });
return .{
.log_file = log_file,
@ -168,6 +169,7 @@ pub fn deinit(self: *TerminalBuffer) void {
pub fn runEventLoop(
self: *TerminalBuffer,
allocator: Allocator,
io: std.Io,
shared_error: SharedError,
layers: [][]*Widget,
active_widget: *Widget,
@ -176,14 +178,14 @@ pub fn runEventLoop(
inactivity_event_fn: ?*const fn (*anyopaque) anyerror!void,
context: *anyopaque,
) !void {
try self.registerGlobalKeybind("Ctrl+K", &moveCursorUp, self);
try self.registerGlobalKeybind("Up", &moveCursorUp, self);
try self.registerGlobalKeybind(io, "Ctrl+K", &moveCursorUp, self);
try self.registerGlobalKeybind(io, "Up", &moveCursorUp, self);
try self.registerGlobalKeybind("Ctrl+J", &moveCursorDown, self);
try self.registerGlobalKeybind("Down", &moveCursorDown, self);
try self.registerGlobalKeybind(io, "Ctrl+J", &moveCursorDown, self);
try self.registerGlobalKeybind(io, "Down", &moveCursorDown, self);
try self.registerGlobalKeybind("Tab", &wrapCursor, self);
try self.registerGlobalKeybind("Shift+Tab", &wrapCursorReverse, self);
try self.registerGlobalKeybind(io, "Tab", &wrapCursor, self);
try self.registerGlobalKeybind(io, "Shift+Tab", &wrapCursorReverse, self);
defer self.handlable_widgets.deinit(allocator);
@ -218,6 +220,7 @@ pub fn runEventLoop(
current_widget.handle(null) catch |err| {
shared_error.writeError(error.SetCursorFailed);
try self.log_file.err(
io,
"tui",
"failed to set cursor in active widget '{s}': {s}",
.{ current_widget.display_name, @errorName(err) },
@ -261,6 +264,7 @@ pub fn runEventLoop(
self.height = TerminalBuffer.getHeight();
try self.log_file.info(
io,
"tui",
"screen resolution updated to {d}x{d}",
.{ self.width, self.height },
@ -271,6 +275,7 @@ pub fn runEventLoop(
widget.realloc() catch |err| {
shared_error.writeError(error.WidgetReallocationFailed);
try self.log_file.err(
io,
"tui",
"failed to reallocate widget '{s}': {s}",
.{ widget.display_name, @errorName(err) },
@ -294,6 +299,7 @@ pub fn runEventLoop(
current_widget.handle(key) catch |err| {
shared_error.writeError(error.CurrentWidgetHandlingFailed);
try self.log_file.err(
io,
"tui",
"failed to handle active widget '{s}': {s}",
.{ current_widget.display_name, @errorName(err) },
@ -390,18 +396,20 @@ pub fn reclaim(self: TerminalBuffer) !void {
pub fn registerKeybind(
self: *TerminalBuffer,
io: std.Io,
keybinds: *KeybindMap,
keybind: []const u8,
callback: KeybindCallbackFn,
context: *anyopaque,
) !void {
const key = try self.parseKeybind(keybind);
const key = try self.parseKeybind(io, keybind);
keybinds.put(key, .{
.callback = callback,
.context = context,
}) catch |err| {
try self.log_file.err(
io,
"tui",
"failed to register keybind {s}: {s}",
.{ keybind, @errorName(err) },
@ -411,15 +419,16 @@ pub fn registerKeybind(
pub fn registerGlobalKeybind(
self: *TerminalBuffer,
io: std.Io,
keybind: []const u8,
callback: KeybindCallbackFn,
context: *anyopaque,
) !void {
try self.registerKeybind(&self.keybinds, keybind, callback, context);
try self.registerKeybind(io, &self.keybinds, keybind, callback, context);
}
pub fn simulateKeybind(self: *TerminalBuffer, keybind: []const u8) !bool {
const key = try self.parseKeybind(keybind);
pub fn simulateKeybind(self: *TerminalBuffer, io: std.Io, keybind: []const u8) !bool {
const key = try self.parseKeybind(io, keybind);
if (self.keybinds.get(key)) |binding| {
return try @call(
@ -509,10 +518,13 @@ fn clearBackBuffer() !void {
// Clear the TTY because termbox2 doesn't seem to do it properly
const capability = termbox.global.caps[termbox.TB_CAP_CLEAR_SCREEN];
const capability_slice = std.mem.span(capability);
_ = try std.posix.write(termbox.global.ttyfd, capability_slice);
const result = std.posix.system.write(termbox.global.ttyfd, capability_slice.ptr, capability_slice.len);
if (result != capability_slice.len) return error.PartialClearBackBuffer;
if (result < 0) return error.ClearBackBufferFailed;
}
fn parseKeybind(self: *TerminalBuffer, keybind: []const u8) !keyboard.Key {
fn parseKeybind(self: *TerminalBuffer, io: std.Io, keybind: []const u8) !keyboard.Key {
var key = std.mem.zeroes(keyboard.Key);
var iterator = std.mem.splitScalar(u8, keybind, '+');
@ -529,6 +541,7 @@ fn parseKeybind(self: *TerminalBuffer, keybind: []const u8) !keyboard.Key {
if (!found) {
try self.log_file.err(
io,
"tui",
"failed to parse key {s} of keybind {s}",
.{ item, keybind },

View file

@ -29,6 +29,7 @@ keybinds: TerminalBuffer.KeybindMap,
pub fn init(
allocator: Allocator,
io: std.Io,
buffer: *TerminalBuffer,
should_insert: bool,
masked: bool,
@ -57,11 +58,11 @@ pub fn init(
.keybinds = .init(allocator),
};
try buffer.registerKeybind(&self.keybinds, "Left", &goLeft, self);
try buffer.registerKeybind(&self.keybinds, "Right", &goRight, self);
try buffer.registerKeybind(&self.keybinds, "Delete", &delete, self);
try buffer.registerKeybind(&self.keybinds, "Backspace", &backspace, self);
try buffer.registerKeybind(&self.keybinds, "Ctrl+U", &clearTextEntry, self);
try buffer.registerKeybind(io, &self.keybinds, "Left", &goLeft, self);
try buffer.registerKeybind(io, &self.keybinds, "Right", &goRight, self);
try buffer.registerKeybind(io, &self.keybinds, "Delete", &delete, self);
try buffer.registerKeybind(io, &self.keybinds, "Backspace", &backspace, self);
try buffer.registerKeybind(io, &self.keybinds, "Ctrl+U", &clearTextEntry, self);
return self;
}

View file

@ -32,6 +32,7 @@ pub fn CyclableLabel(comptime ItemType: type, comptime ChangeItemType: type) typ
pub fn init(
allocator: Allocator,
io: std.Io,
buffer: *TerminalBuffer,
draw_item_fn: DrawItemFn,
change_item_fn: ?ChangeItemFn,
@ -60,10 +61,10 @@ pub fn CyclableLabel(comptime ItemType: type, comptime ChangeItemType: type) typ
.keybinds = .init(allocator),
};
try buffer.registerKeybind(&self.keybinds, "Left", &goLeft, self);
try buffer.registerKeybind(&self.keybinds, "Ctrl+H", &goLeft, self);
try buffer.registerKeybind(&self.keybinds, "Right", &goRight, self);
try buffer.registerKeybind(&self.keybinds, "Ctrl+L", &goRight, self);
try buffer.registerKeybind(io, &self.keybinds, "Left", &goLeft, self);
try buffer.registerKeybind(io, &self.keybinds, "Ctrl+H", &goLeft, self);
try buffer.registerKeybind(io, &self.keybinds, "Right", &goRight, self);
try buffer.registerKeybind(io, &self.keybinds, "Ctrl+L", &goRight, self);
return self;
}

View file

@ -14,7 +14,7 @@ Join us on Matrix over at [#ly-dm:matrix.org](https://matrix.to/#/#ly-dm:matrix.
## Dependencies
- Compile-time:
- zig 0.15.x
- zig 0.16.x
- libc
@ -226,6 +226,12 @@ You can, of course, still select the init system of your choice when using this
You can find all the configuration in `/etc/ly/config.ini`. The file is fully commented, and includes the default values.
You may also check the validity of your configuration file (i.e. if there are any errors in it) with the following command:
```
$ ly --validate-config /etc/ly/config.ini
```
## Controls
Use the Up/Down arrow keys to change the current field, and the Left/Right arrow keys to scroll through the different fields (whether it be the info line, the desktop environment, or the username). The info line is where messages and errors are displayed.

View file

@ -8,17 +8,20 @@ const Widget = ly_ui.Widget;
const Cascade = @This();
io: std.Io,
instance: ?Widget = null,
buffer: *TerminalBuffer,
current_auth_fails: *usize,
max_auth_fails: usize,
current_auth_fails: *u64,
max_auth_fails: u64,
pub fn init(
io: std.Io,
buffer: *TerminalBuffer,
current_auth_fails: *usize,
max_auth_fails: usize,
current_auth_fails: *u64,
max_auth_fails: u64,
) Cascade {
return .{
.io = io,
.instance = null,
.buffer = buffer,
.current_auth_fails = current_auth_fails,
@ -44,7 +47,7 @@ pub fn widget(self: *Cascade) *Widget {
fn draw(self: *Cascade) void {
while (self.current_auth_fails.* >= self.max_auth_fails) {
std.Thread.sleep(std.time.ns_per_ms * 10);
self.io.sleep(.fromMilliseconds(10), .real) catch {};
var changed = false;
var y = self.buffer.height - 2;
@ -80,7 +83,7 @@ fn draw(self: *Cascade) void {
}
if (!changed) {
std.Thread.sleep(std.time.ns_per_s * 7);
self.io.sleep(.fromSeconds(7), .real) catch {};
self.current_auth_fails.* = 0;
}

View file

@ -113,7 +113,7 @@ fn draw(self: *ColorMix) void {
uv -= @splat(1.0 * math.cos(uv[0] + uv[1]) - math.sin(uv[0] * 0.7 - uv[1]));
}
const cell = self.palette[@as(usize, @intFromFloat(math.floor(length(uv) * 5.0))) % palette_len];
const cell = self.palette[@as(usize, @trunc(math.floor(length(uv) * 5.0))) % palette_len];
cell.put(x, y);
}
}

View file

@ -19,16 +19,16 @@ const LogFile = ly_core.LogFile;
const enums = @import("../enums.zig");
const DurOffsetAlignment = enums.DurOffsetAlignment;
fn read_decompress_file(allocator: Allocator, file_path: []const u8) ![]u8 {
const file_buffer = std.fs.cwd().openFile(file_path, .{}) catch {
fn read_decompress_file(allocator: Allocator, io: std.Io, file_path: []const u8) ![]u8 {
const file_buffer = std.Io.Dir.cwd().openFile(io, file_path, .{}) catch {
return error.FileNotFound;
};
defer file_buffer.close();
defer file_buffer.close(io);
var file_reader_buffer: [4096]u8 = undefined;
var decompress_buffer: [flate.max_window_len]u8 = undefined;
var file_reader = file_buffer.reader(&file_reader_buffer);
var file_reader = file_buffer.reader(io, &file_reader_buffer);
var decompress: flate.Decompress = .init(&file_reader.interface, .gzip, &decompress_buffer);
const file_decompressed = decompress.reader.allocRemaining(allocator, .unlimited) catch {
@ -150,8 +150,8 @@ const DurFormat = struct {
}
}
pub fn create_from_file(self: *DurFormat, allocator: Allocator, file_path: []const u8) !void {
const file_decompressed = try read_decompress_file(allocator, file_path);
pub fn create_from_file(self: *DurFormat, allocator: Allocator, io: std.Io, file_path: []const u8) !void {
const file_decompressed = try read_decompress_file(allocator, io, file_path);
defer allocator.free(file_decompressed);
const parsed = try Json.parseFromSlice(Json.Value, allocator, file_decompressed, .{});
@ -307,9 +307,10 @@ const DurFile = @This();
instance: ?Widget = null,
start_time: TimeOfDay,
allocator: Allocator,
io: std.Io,
terminal_buffer: *TerminalBuffer,
dur_movie: DurFormat,
frames: u64,
frames: usize,
frame_size: UVec2,
start_pos: IVec2,
full_color: bool,
@ -368,6 +369,7 @@ fn calc_frame_size(terminal_buffer: *TerminalBuffer, dur_movie: *DurFormat) UVec
pub fn init(
allocator: Allocator,
io: std.Io,
terminal_buffer: *TerminalBuffer,
log_file: *LogFile,
file_path: []const u8,
@ -381,13 +383,13 @@ pub fn init(
) !DurFile {
var dur_movie: DurFormat = .init(allocator);
dur_movie.create_from_file(allocator, file_path) catch |err| switch (err) {
dur_movie.create_from_file(allocator, io, file_path) catch |err| switch (err) {
error.FileNotFound => {
try log_file.err("tui", "dur_file was not found at: {s}", .{file_path});
try log_file.err(io, "tui", "dur_file was not found at: {s}", .{file_path});
return err;
},
error.NotValidFile => {
try log_file.err("tui", "dur_file loaded was invalid or not a dur file!", .{});
try log_file.err(io, "tui", "dur_file loaded was invalid or not a dur file!", .{});
return err;
},
else => return err,
@ -395,7 +397,7 @@ pub fn init(
// 4 bit mode with 256 color is unsupported
if (!full_color and eql(u8, dur_movie.colorFormat.?, "256")) {
try log_file.err("tui", "dur_file can not be 256 color encoded when not using full_color option!", .{});
try log_file.err(io, "tui", "dur_file can not be 256 color encoded when not using full_color option!", .{});
dur_movie.deinit();
return error.InvalidColorFormat;
}
@ -406,15 +408,16 @@ pub fn init(
const frame_size = calc_frame_size(terminal_buffer, &dur_movie);
// Convert dur fps to frames per ms
const frame_time: u32 = @intFromFloat(1000 / dur_movie.framerate.?);
const frame_time: u32 = @trunc(1000 / dur_movie.framerate.?);
return .{
.instance = null,
.start_time = try interop.getTimeOfDay(),
.allocator = allocator,
.io = io,
.terminal_buffer = terminal_buffer,
.frames = 0,
.time_previous = std.time.milliTimestamp(),
.time_previous = std.Io.Timestamp.now(io, .real).toMilliseconds(),
.frame_size = frame_size,
.start_pos = start_pos,
.full_color = full_color,
@ -499,11 +502,11 @@ fn draw(self: *DurFile) void {
}
}
const time_current = std.time.milliTimestamp();
const time_current = std.Io.Timestamp.now(self.io, .real).toMilliseconds();
const delta_time = time_current - self.time_previous;
// Convert delay from sec to ms
const delay_time: u32 = @intFromFloat(current_frame.delay * 1000);
const delay_time: u32 = @trunc(current_frame.delay * 1000);
if (delta_time > (self.frame_time + delay_time)) {
self.time_previous = time_current;

View file

@ -27,16 +27,16 @@ pub const AuthOptions = struct {
};
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);
pub fn xorgSignalHandler(sig: std.posix.SIG) callconv(.c) void {
if (xorg_pid > 0) _ = std.c.kill(xorg_pid, sig);
}
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 sessionSignalHandler(sig: std.posix.SIG) callconv(.c) void {
if (child_pid > 0) _ = std.c.kill(child_pid, sig);
}
pub fn authenticate(allocator: std.mem.Allocator, log_file: *LogFile, options: AuthOptions, current_environment: Environment, login: []const u8, password: []const u8) !void {
pub fn authenticate(allocator: std.mem.Allocator, io: std.Io, log_file: *LogFile, options: AuthOptions, current_environment: Environment, login: []const u8, password: []const u8) !void {
var tty_buffer: [3]u8 = undefined;
const tty_str = try std.fmt.bufPrint(&tty_buffer, "{d}", .{options.tty});
@ -44,11 +44,11 @@ pub fn authenticate(allocator: std.mem.Allocator, log_file: *LogFile, options: A
const pam_tty_str = try std.fmt.bufPrintZ(&pam_tty_buffer, "tty{d}", .{options.tty});
// Set the XDG environment variables
try log_file.info("auth/env", "setting xdg environment variables", .{});
try log_file.info(io, "auth/env", "setting xdg environment variables", .{});
try setXdgEnv(allocator, tty_str, current_environment);
// Open the PAM session
try log_file.info("auth/pam", "encoding credentials", .{});
try log_file.info(io, "auth/pam", "encoding credentials", .{});
const login_z = try allocator.dupeZ(u8, login);
defer allocator.free(login_z);
@ -63,36 +63,36 @@ pub fn authenticate(allocator: std.mem.Allocator, log_file: *LogFile, options: A
};
var handle: ?*interop.pam.pam_handle = undefined;
try log_file.info("auth/pam", "starting session", .{});
try log_file.info(io, "auth/pam", "starting session", .{});
var status = interop.pam.pam_start(options.service_name, null, &conv, &handle);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
defer _ = interop.pam.pam_end(handle, status);
// Set PAM_TTY as the current TTY. This is required in case it isn't being set by another PAM module
try log_file.info("auth/pam", "setting tty", .{});
try log_file.info(io, "auth/pam", "setting tty", .{});
status = interop.pam.pam_set_item(handle, interop.pam.PAM_TTY, pam_tty_str.ptr);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
// Do the PAM routine
try log_file.info("auth/pam", "authenticating", .{});
try log_file.info(io, "auth/pam", "authenticating", .{});
status = interop.pam.pam_authenticate(handle, 0);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
try log_file.info("auth/pam", "validating account", .{});
try log_file.info(io, "auth/pam", "validating account", .{});
status = interop.pam.pam_acct_mgmt(handle, 0);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
try log_file.info("auth/pam", "setting credentials", .{});
try log_file.info(io, "auth/pam", "setting credentials", .{});
status = interop.pam.pam_setcred(handle, interop.pam.PAM_ESTABLISH_CRED);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
defer status = interop.pam.pam_setcred(handle, interop.pam.PAM_DELETE_CRED);
try log_file.info("auth/pam", "opening session", .{});
try log_file.info(io, "auth/pam", "opening session", .{});
status = interop.pam.pam_open_session(handle, 0);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
defer status = interop.pam.pam_close_session(handle, 0);
try log_file.info("auth/passwd", "getting struct", .{});
try log_file.info(io, "auth/passwd", "getting struct", .{});
var user_entry: interop.UsernameEntry = undefined;
{
defer interop.closePasswordDatabase();
@ -102,27 +102,27 @@ pub fn authenticate(allocator: std.mem.Allocator, log_file: *LogFile, options: A
}
// Set user shell if it hasn't already been set
try log_file.info("auth/passwd", "setting user shell", .{});
try log_file.info(io, "auth/passwd", "setting user shell", .{});
if (user_entry.shell == null) interop.setUserShell(&user_entry);
var shared_err = try SharedError.init(null, null);
defer shared_err.deinit();
log_file.deinit();
log_file.deinit(io);
child_pid = try std.posix.fork();
child_pid = std.posix.system.fork();
if (child_pid == 0) {
try log_file.reinit();
try log_file.info("auth/sys", "starting session", .{});
try log_file.reinit(io);
try log_file.info(io, "auth/sys", "starting session", .{});
startSession(log_file, allocator, options, tty_str, user_entry, handle, current_environment) catch |e| {
startSession(log_file, allocator, io, options, tty_str, user_entry, handle, current_environment) catch |e| {
shared_err.writeError(e);
log_file.deinit();
log_file.deinit(io);
std.process.exit(1);
};
log_file.deinit();
log_file.deinit(io);
std.process.exit(0);
}
@ -132,7 +132,8 @@ pub fn authenticate(allocator: std.mem.Allocator, log_file: *LogFile, options: A
// If an error occurs here, we can send SIGTERM to the session
errdefer cleanup: {
std.posix.kill(child_pid, std.posix.SIG.TERM) catch break :cleanup;
_ = std.posix.waitpid(child_pid, 0);
var child_status: c_int = undefined;
_ = std.posix.system.waitpid(child_pid, &child_status, 0);
}
// If we receive SIGTERM, forward it to child_pid
@ -143,14 +144,15 @@ pub fn authenticate(allocator: std.mem.Allocator, log_file: *LogFile, options: A
};
std.posix.sigaction(std.posix.SIG.TERM, &act, null);
try addUtmpEntry(&entry, user_entry.username.?, child_pid);
try addUtmpEntry(io, &entry, user_entry.username.?, child_pid);
}
// Wait for the session to stop
_ = std.posix.waitpid(child_pid, 0);
var child_status: c_int = undefined;
_ = std.posix.system.waitpid(child_pid, &child_status, 0);
try log_file.reinit();
try log_file.reinit(io);
try log_file.info("auth/utmp", "removing utmp entry", .{});
try log_file.info(io, "auth/utmp", "removing utmp entry", .{});
removeUtmpEntry(&entry);
if (shared_err.readError()) |err| return err;
@ -159,6 +161,7 @@ pub fn authenticate(allocator: std.mem.Allocator, log_file: *LogFile, options: A
fn startSession(
log_file: *LogFile,
allocator: std.mem.Allocator,
io: std.Io,
options: AuthOptions,
tty_str: []u8,
user_entry: interop.UsernameEntry,
@ -166,15 +169,15 @@ fn startSession(
current_environment: Environment,
) !void {
// Set the user's GID & PID
try log_file.info("auth/passwd", "setting user context", .{});
try log_file.info(io, "auth/passwd", "setting user context", .{});
try interop.setUserContext(allocator, user_entry);
// Set up the environment
try log_file.info("auth/env", "setting environment variables", .{});
try log_file.info(io, "auth/env", "setting environment variables", .{});
try initEnv(allocator, user_entry, options.path);
// Reset the XDG environment variables
try log_file.info("auth/env", "resetting xdg environment variables", .{});
try log_file.info(io, "auth/env", "resetting xdg environment variables", .{});
try setXdgEnv(allocator, tty_str, current_environment);
try setXdgRuntimeDir(allocator);
@ -185,27 +188,30 @@ fn startSession(
const env_list = std.mem.span(pam_env_vars.?);
for (env_list) |env_var| {
if (env_var == null) continue;
try log_file.info("auth/env", "setting pam environment variable: {s}", .{std.mem.span(env_var.?)});
try log_file.info(io, "auth/env", "setting pam environment variable: {s}", .{std.mem.span(env_var.?)});
try interop.putEnvironmentVariable(env_var);
}
const home_z = try allocator.dupeZ(u8, user_entry.home.?);
defer allocator.free(home_z);
// Change to the user's home directory
try log_file.info("auth/sys", "changing cwd to user home", .{});
std.posix.chdir(user_entry.home.?) catch return error.ChangeDirectoryFailed;
try log_file.info(io, "auth/sys", "changing cwd to user home", .{});
if (std.posix.system.chdir(home_z.ptr) < 0) return error.ChangeDirectoryFailed;
// Signal to the session process to give up control on the TTY
try log_file.info("auth/sys", "releasing tty", .{});
std.posix.kill(options.session_pid, std.posix.SIG.CHLD) catch return error.TtyControlTransferFailed;
try log_file.info(io, "auth/sys", "releasing tty", .{});
std.posix.kill(options.session_pid, std.posix.SIG.INT) catch return error.TtyControlTransferFailed;
// Execute what the user requested
switch (current_environment.display_server) {
.wayland, .shell, .custom => try executeCmd(log_file, allocator, user_entry.shell.?, options, current_environment.is_terminal, current_environment.cmd),
.wayland, .shell, .custom => try executeCmd(log_file, allocator, io, user_entry.shell.?, options, current_environment.is_terminal, current_environment.cmd),
.xinitrc, .x11 => if (build_options.enable_x11_support) {
var vt_buf: [5]u8 = undefined;
const vt = try std.fmt.bufPrint(&vt_buf, "vt{d}", .{options.x_vt orelse options.tty});
try log_file.info("auth/x11", "setting vt to {s}", .{vt});
try executeX11Cmd(log_file, allocator, user_entry.shell.?, user_entry.home.?, options, current_environment.cmd orelse "", vt);
try log_file.info(io, "auth/x11", "setting vt to {s}", .{vt});
try executeX11Cmd(log_file, allocator, io, user_entry.shell.?, user_entry.home.?, options, current_environment.cmd orelse "", vt);
},
}
}
@ -247,7 +253,7 @@ fn setXdgRuntimeDir(allocator: std.mem.Allocator) !void {
// XDG_RUNTIME_DIR to fall back to directories inside user's home
// directory.
if (builtin.os.tag != .freebsd) {
const uid = std.posix.getuid();
const uid = std.posix.system.getuid();
var uid_buffer: [32]u8 = undefined; // No UID can be larger than this
const uid_str = try std.fmt.bufPrint(&uid_buffer, "/run/user/{d}", .{uid});
@ -317,20 +323,20 @@ fn getFreeDisplay() !u8 {
var buf: [15]u8 = undefined;
var i: u8 = 0;
while (i < 200) : (i += 1) {
const xlock = try std.fmt.bufPrint(&buf, "/tmp/.X{d}-lock", .{i});
std.posix.access(xlock, std.posix.F_OK) catch break;
const xlock = try std.fmt.bufPrintZ(&buf, "/tmp/.X{d}-lock", .{i});
if (interop.isError(std.posix.system.access(xlock.ptr, std.posix.F_OK))) break;
}
return i;
}
fn getXPid(display_num: u8) !i32 {
fn getXPid(io: std.Io, display_num: u8) !i32 {
var buf: [15]u8 = undefined;
const file_name = try std.fmt.bufPrint(&buf, "/tmp/.X{d}-lock", .{display_num});
const file = try std.fs.openFileAbsolute(file_name, .{});
defer file.close();
const file = try std.Io.Dir.openFileAbsolute(io, file_name, .{});
defer file.close(io);
var file_buffer: [32]u8 = undefined;
var file_reader = file.reader(&file_buffer);
var file_reader = file.reader(io, &file_buffer);
var reader = &file_reader.interface;
var buffer: [20]u8 = undefined;
@ -340,41 +346,41 @@ fn getXPid(display_num: u8) !i32 {
return std.fmt.parseInt(i32, std.mem.trim(u8, buffer[0..written], " "), 10);
}
fn createXauthFile(log_file: *LogFile, pwd: []const u8, buffer: []u8) ![]const u8 {
fn createXauthFile(log_file: *LogFile, io: std.Io, pwd: []const u8, buffer: []u8) ![]const u8 {
var xauth_buf: [100]u8 = undefined;
var xauth_dir: []const u8 = undefined;
const xdg_rt_dir = std.posix.getenv("XDG_RUNTIME_DIR");
const xdg_rt_dir = std.posix.system.getenv("XDG_RUNTIME_DIR");
var xauth_file: []const u8 = "lyxauth";
if (xdg_rt_dir == null) no_rt_dir: {
const xdg_cfg_home = std.posix.getenv("XDG_CONFIG_HOME");
const xdg_cfg_home = std.posix.system.getenv("XDG_CONFIG_HOME");
if (xdg_cfg_home == null) no_cfg_home: {
xauth_dir = try std.fmt.bufPrint(&xauth_buf, "{s}/.config", .{pwd});
var dir = std.fs.cwd().openDir(xauth_dir, .{}) catch {
var dir = std.Io.Dir.cwd().openDir(io, xauth_dir, .{}) catch {
// xauth_dir isn't a directory
xauth_dir = pwd;
xauth_file = ".lyxauth";
break :no_cfg_home;
};
dir.close();
dir.close(io);
// xauth_dir is a directory, use it to store Xauthority
xauth_dir = try std.fmt.bufPrint(&xauth_buf, "{s}/.config/ly", .{pwd});
} else {
xauth_dir = try std.fmt.bufPrint(&xauth_buf, "{s}/ly", .{xdg_cfg_home.?});
xauth_dir = try std.fmt.bufPrint(&xauth_buf, "{s}/ly", .{std.mem.span(xdg_cfg_home.?)});
}
const file = std.fs.cwd().openFile(xauth_dir, .{}) catch break :no_rt_dir;
file.close();
const file = std.Io.Dir.cwd().openFile(io, xauth_dir, .{}) catch break :no_rt_dir;
file.close(io);
// xauth_dir is a file, create the parent directory
std.posix.mkdir(xauth_dir, 777) catch {
std.Io.Dir.createDirAbsolute(io, xauth_dir, .fromMode(777)) catch {
xauth_dir = pwd;
xauth_file = ".lyxauth";
};
} else {
xauth_dir = xdg_rt_dir.?;
xauth_dir = std.mem.span(xdg_rt_dir.?);
}
// Trim trailing slashes
@ -384,19 +390,19 @@ fn createXauthFile(log_file: *LogFile, pwd: []const u8, buffer: []u8) ![]const u
const xauthority: []u8 = try std.fmt.bufPrint(buffer, "{s}/{s}", .{ trimmed_xauth_dir, xauth_file });
std.fs.cwd().makePath(trimmed_xauth_dir) catch {};
std.Io.Dir.cwd().createDirPath(io, trimmed_xauth_dir) catch {};
try log_file.info("auth/x11", "creating xauth file: {s}", .{xauthority});
try log_file.info(io, "auth/x11", "creating xauth file: {s}", .{xauthority});
const file = try std.fs.createFileAbsolute(xauthority, .{});
file.close();
const file = try std.Io.Dir.createFileAbsolute(io, xauthority, .{});
file.close(io);
return xauthority;
}
fn mcookie() [Md5.digest_length * 2]u8 {
fn mcookie(io: std.Io) [Md5.digest_length * 2]u8 {
var buf: [4096]u8 = undefined;
std.crypto.random.bytes(&buf);
io.random(&buf);
var out: [Md5.digest_length]u8 = undefined;
Md5.hash(&buf, &out, .{});
@ -404,86 +410,92 @@ fn mcookie() [Md5.digest_length * 2]u8 {
return std.fmt.bytesToHex(&out, .lower);
}
fn xauth(log_file: *LogFile, allocator: std.mem.Allocator, display_name: []u8, shell: [*:0]const u8, home: []const u8, xauth_buffer: []u8, options: AuthOptions) ![]const u8 {
const xauthority = try createXauthFile(log_file, home, xauth_buffer);
fn xauth(log_file: *LogFile, allocator: std.mem.Allocator, io: std.Io, display_name: []u8, shell: [*:0]const u8, home: []const u8, xauth_buffer: []u8, options: AuthOptions) ![]const u8 {
const xauthority = try createXauthFile(log_file, io, home, xauth_buffer);
try interop.setEnvironmentVariable(allocator, "XAUTHORITY", xauthority, true);
try interop.setEnvironmentVariable(allocator, "DISPLAY", display_name, true);
const magic_cookie = mcookie();
const magic_cookie = mcookie(io);
const pid = try std.posix.fork();
const pid = std.posix.system.fork();
if (pid == 0) {
var cmd_buffer: [1024]u8 = undefined;
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} add {s} . {s}", .{ options.xauth_cmd, display_name, magic_cookie }) catch std.process.exit(1);
try log_file.info("auth/x11", "executing: {s} -c {s}", .{ shell, cmd_str });
try log_file.info(io, "auth/x11", "executing: {s} -c {s}", .{ shell, cmd_str });
const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str };
std.posix.execveZ(shell, &args, std.c.environ) catch {};
_ = std.posix.system.execve(shell, &args, std.c.environ);
std.process.exit(1);
}
const status = std.posix.waitpid(pid, 0);
if (status.status != 0) {
try log_file.file_writer.interface.print("xauth command failed with status {d}\n", .{status.status});
var status: c_int = undefined;
const result = std.posix.system.waitpid(pid, &status, 0);
if (interop.isError(result) or status != 0) {
try log_file.err(
io,
"auth/x11",
"xauth command failed with status: {d}",
.{status},
);
return error.XauthFailed;
}
return xauthority;
}
fn executeX11Cmd(log_file: *LogFile, allocator: std.mem.Allocator, shell: []const u8, home: []const u8, options: AuthOptions, desktop_cmd: []const u8, vt: []const u8) !void {
fn executeX11Cmd(log_file: *LogFile, allocator: std.mem.Allocator, io: std.Io, shell: []const u8, home: []const u8, options: AuthOptions, desktop_cmd: []const u8, vt: []const u8) !void {
var xauth_buffer: [256]u8 = undefined;
try log_file.info("auth/x11", "getting free display", .{});
try log_file.info(io, "auth/x11", "getting free display", .{});
const display_num = try getFreeDisplay();
var buf: [4]u8 = undefined;
const display_name = try std.fmt.bufPrint(&buf, ":{d}", .{display_num});
try log_file.info("auth/x11", "got free display: {d}", .{display_num});
try log_file.info(io, "auth/x11", "got free display: {d}", .{display_num});
const shell_z = try allocator.dupeZ(u8, shell);
defer allocator.free(shell_z);
try log_file.info("auth/x11", "creating xauth file", .{});
const xauthority = try xauth(log_file, allocator, display_name, shell_z, home, &xauth_buffer, options);
try log_file.info(io, "auth/x11", "creating xauth file", .{});
const xauthority = try xauth(log_file, allocator, io, display_name, shell_z, home, &xauth_buffer, options);
try log_file.info("auth/x11", "starting x server", .{});
const pid = try std.posix.fork();
try log_file.info(io, "auth/x11", "starting x server", .{});
const pid = std.posix.system.fork();
if (pid == 0) {
var cmd_buffer: [1024]u8 = undefined;
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s} -auth {s}", .{ options.x_cmd, display_name, vt, xauthority }) catch std.process.exit(1);
try log_file.info("auth/x11", "executing: {s} -c {s} -auth {s}", .{ shell, cmd_str, xauthority });
try log_file.info(io, "auth/x11", "executing: {s} -c {s} -auth {s}", .{ shell, cmd_str, xauthority });
const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str };
std.posix.execveZ(shell_z, &args, std.c.environ) catch {};
_ = std.posix.system.execve(shell_z, &args, std.c.environ);
std.process.exit(1);
}
try log_file.info("auth/x11", "waiting for xcb connection", .{});
try log_file.info(io, "auth/x11", "waiting for xcb connection", .{});
var ok: c_int = -1;
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);
std.posix.kill(pid, 0) catch |e| {
std.posix.kill(pid, @enumFromInt(0)) catch |e| {
if (e == error.ProcessNotFound and ok != 0) return error.XcbConnectionFailed;
};
}
// X Server detaches from the process.
// PID can be fetched from /tmp/X{d}.lock
try log_file.info("auth/x11", "getting x server pid", .{});
const x_pid = try getXPid(display_num);
try log_file.info("auth/x11", "got x server pid: {d}", .{x_pid});
try log_file.info(io, "auth/x11", "getting x server pid", .{});
const x_pid = try getXPid(io, display_num);
try log_file.info(io, "auth/x11", "got x server pid: {d}", .{x_pid});
try log_file.info("auth/x11", "launching environment", .{});
xorg_pid = try std.posix.fork();
try log_file.info(io, "auth/x11", "launching environment", .{});
xorg_pid = std.posix.system.fork();
if (xorg_pid == 0) {
var cmd_buffer: [1024]u8 = undefined;
const cmd_str = std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s} {s}", .{ if (options.use_kmscon_vt) "kmscon-launch-gui" else "", options.setup_cmd, options.login_cmd orelse "", desktop_cmd }) catch std.process.exit(1);
try log_file.info("auth/x11", "executing: {s} -c {s}", .{ shell, cmd_str });
try log_file.info(io, "auth/x11", "executing: {s} -c {s}", .{ shell, cmd_str });
const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str };
std.posix.execveZ(shell_z, &args, std.c.environ) catch {};
_ = std.posix.system.execve(shell_z, &args, std.c.environ);
std.process.exit(1);
}
@ -495,26 +507,28 @@ fn executeX11Cmd(log_file: *LogFile, allocator: std.mem.Allocator, shell: []cons
};
std.posix.sigaction(std.posix.SIG.TERM, &act, null);
_ = std.posix.waitpid(xorg_pid, 0);
var xorg_status: c_int = undefined;
_ = std.posix.system.waitpid(xorg_pid, &xorg_status, 0);
try log_file.info("auth/x11", "disconnecting xcb", .{});
try log_file.info(io, "auth/x11", "disconnecting xcb", .{});
interop.xcb.xcb_disconnect(xcb);
// TODO: Find a more robust way to ensure that X has been terminated (pidfds?)
std.posix.kill(x_pid, std.posix.SIG.TERM) catch {};
std.Thread.sleep(std.time.ns_per_s * 1); // Wait 1 second before sending SIGKILL
io.sleep(.fromSeconds(1), .real) catch {}; // Wait 1 second before sending SIGKILL
std.posix.kill(x_pid, std.posix.SIG.KILL) catch return;
_ = std.posix.waitpid(x_pid, 0);
var x_status: c_int = undefined;
_ = std.posix.system.waitpid(x_pid, &x_status, 0);
}
fn executeCmd(global_log_file: *LogFile, allocator: std.mem.Allocator, shell: []const u8, options: AuthOptions, is_terminal: bool, exec_cmd: ?[]const u8) !void {
try global_log_file.info("auth/sys", "launching wayland/shell/custom session", .{});
fn executeCmd(global_log_file: *LogFile, allocator: std.mem.Allocator, io: std.Io, shell: []const u8, options: AuthOptions, is_terminal: bool, exec_cmd: ?[]const u8) !void {
try global_log_file.info(io, "auth/sys", "launching wayland/shell/custom session", .{});
var maybe_log_file: ?std.fs.File = null;
var maybe_log_file: ?std.Io.File = null;
if (!is_terminal) redirect_streams: {
if (options.use_kmscon_vt) {
try global_log_file.err("auth/sys", "cannot redirect stdio & stderr with kmscon", .{});
try global_log_file.err(io, "auth/sys", "cannot redirect stdio & stderr with kmscon", .{});
break :redirect_streams;
}
@ -522,11 +536,11 @@ fn executeCmd(global_log_file: *LogFile, allocator: std.mem.Allocator, shell: []
// we redirect standard output & error or not. That is, we redirect only
// if it's equal to false (so if it's not running in a TTY).
if (options.session_log) |log_path| {
try global_log_file.info("auth/sys", "setting up stdio & stderr redirection", .{});
maybe_log_file = try redirectStandardStreams(global_log_file, log_path, true);
try global_log_file.info(io, "auth/sys", "setting up stdio & stderr redirection", .{});
maybe_log_file = try redirectStandardStreams(global_log_file, io, log_path, true);
}
}
defer if (maybe_log_file) |log_file| log_file.close();
defer if (maybe_log_file) |log_file| log_file.close(io);
const shell_z = try allocator.dupeZ(u8, shell);
defer allocator.free(shell_z);
@ -534,40 +548,42 @@ fn executeCmd(global_log_file: *LogFile, allocator: std.mem.Allocator, shell: []
var cmd_buffer: [1024]u8 = undefined;
const cmd_str = try std.fmt.bufPrintZ(&cmd_buffer, "{s} {s} {s} {s}", .{ if (!is_terminal and options.use_kmscon_vt) "kmscon-launch-gui" else "", options.setup_cmd, options.login_cmd orelse "", exec_cmd orelse shell });
try global_log_file.info("auth/sys", "executing: {s} -c {s}", .{ shell, cmd_str });
try global_log_file.info(io, "auth/sys", "executing: {s} -c {s}", .{ shell, cmd_str });
const args = [_:null]?[*:0]const u8{ shell_z, "-c", cmd_str };
return std.posix.execveZ(shell_z, &args, std.c.environ);
_ = std.posix.system.execve(shell_z, &args, std.c.environ);
return error.CmdExecveFailed;
}
fn redirectStandardStreams(global_log_file: *LogFile, session_log: []const u8, create: bool) !std.fs.File {
fn redirectStandardStreams(global_log_file: *LogFile, io: std.Io, session_log: []const u8, create: bool) !std.Io.File {
create_session_log_dir: {
const session_log_dir = std.fs.path.dirname(session_log) orelse break :create_session_log_dir;
std.fs.cwd().makePath(session_log_dir) catch |err| {
try global_log_file.err("auth/sys", "failed to create session log file directory: {s}", .{@errorName(err)});
const session_log_dir = std.Io.Dir.path.dirname(session_log) orelse break :create_session_log_dir;
std.Io.Dir.cwd().createDirPath(io, session_log_dir) catch |err| {
try global_log_file.err(io, "auth/sys", "failed to create session log file directory: {s}", .{@errorName(err)});
return err;
};
}
const log_file = if (create) (std.fs.cwd().createFile(session_log, .{ .mode = 0o666 }) catch |err| {
try global_log_file.err("auth/sys", "failed to create new session log file: {s}", .{@errorName(err)});
const log_file = if (create) (std.Io.Dir.cwd().createFile(io, session_log, .{ .permissions = .fromMode(0o666) }) catch |err| {
try global_log_file.err(io, "auth/sys", "failed to create new session log file: {s}", .{@errorName(err)});
return err;
}) else (std.fs.cwd().openFile(session_log, .{ .mode = .read_write }) catch |err| {
try global_log_file.err("auth/sys", "failed to open existing session log file: {s}", .{@errorName(err)});
}) else (std.Io.Dir.cwd().openFile(io, session_log, .{ .mode = .read_write }) catch |err| {
try global_log_file.err(io, "auth/sys", "failed to open existing session log file: {s}", .{@errorName(err)});
return err;
});
try std.posix.dup2(std.posix.STDOUT_FILENO, std.posix.STDERR_FILENO);
try std.posix.dup2(log_file.handle, std.posix.STDOUT_FILENO);
if (interop.isError(std.posix.system.dup2(std.posix.STDOUT_FILENO, std.posix.STDERR_FILENO))) return error.StdoutDup2Failed;
if (interop.isError(std.posix.system.dup2(log_file.handle, std.posix.STDOUT_FILENO))) return error.LogFileDup2Failed;
return log_file;
}
fn addUtmpEntry(entry: *Utmp, username: []const u8, pid: c_int) !void {
fn addUtmpEntry(io: std.Io, entry: *Utmp, username: []const u8, pid: c_int) !void {
entry.ut_type = utmp.USER_PROCESS;
entry.ut_pid = pid;
var buf: [std.fs.max_path_bytes]u8 = undefined;
const tty_path = try std.os.getFdPath(std.posix.STDIN_FILENO, &buf);
var buf: [std.Io.Dir.max_path_bytes]u8 = undefined;
const length = try std.Io.File.stdin().realPath(io, &buf);
const tty_path = buf[0..length];
// Get the TTY name (i.e. without the /dev/ prefix)
var ttyname_buf: [@sizeOf(@TypeOf(entry.ut_line))]u8 = undefined;

View file

@ -23,6 +23,7 @@ label: *MessageLabel,
pub fn init(
allocator: Allocator,
io: std.Io,
buffer: *TerminalBuffer,
width: usize,
arrow_fg: u32,
@ -32,6 +33,7 @@ pub fn init(
.instance = null,
.label = try MessageLabel.init(
allocator,
io,
buffer,
drawItem,
null,

View file

@ -24,6 +24,7 @@ user_list: *UserList,
pub fn init(
allocator: Allocator,
io: std.Io,
buffer: *TerminalBuffer,
user_list: *UserList,
width: usize,
@ -35,6 +36,7 @@ pub fn init(
.instance = null,
.label = try EnvironmentLabel.init(
allocator,
io,
buffer,
drawItem,
sessionChanged,

View file

@ -26,6 +26,7 @@ label: *UserLabel,
pub fn init(
allocator: Allocator,
io: std.Io,
buffer: *TerminalBuffer,
usernames: StringList,
saved_users: *SavedUsers,
@ -39,6 +40,7 @@ pub fn init(
.instance = null,
.label = try UserLabel.init(
allocator,
io,
buffer,
drawItem,
usernameChanged,

View file

@ -21,5 +21,5 @@ pub const CustomCommandInfo = struct {
counter: u32 = 0,
};
pub var binds: std.StringArrayHashMap(CustomCommandBind) = undefined;
pub var labels: std.StringArrayHashMap(CustomCommandInfo) = undefined;
pub var binds: std.array_hash_map.String(CustomCommandBind) = undefined;
pub var labels: std.array_hash_map.String(CustomCommandInfo) = undefined;

View file

@ -190,7 +190,7 @@ pub fn configFieldHandler(_: std.mem.Allocator, field: ini.IniField) ?ini.IniFie
const key = field.header["cmd:".len..];
const keyZ = temporary_allocator.dupe(u8, key) catch "";
if (!custom.binds.contains(key)) {
custom.binds.put(keyZ, .{}) catch {};
custom.binds.put(temporary_allocator, keyZ, .{}) catch {};
}
if (custom.binds.getPtr(keyZ)) |command| {
if (std.mem.eql(u8, field.key, "name")) {
@ -206,7 +206,7 @@ pub fn configFieldHandler(_: std.mem.Allocator, field: ini.IniField) ?ini.IniFie
const key = field.header["lbl:".len..];
const keyZ = temporary_allocator.dupe(u8, key) catch "";
if (!custom.labels.contains(keyZ)) {
custom.labels.put(keyZ, .{ .name = keyZ }) catch {};
custom.labels.put(temporary_allocator, keyZ, .{ .name = keyZ }) catch {};
}
if (custom.labels.getPtr(keyZ)) |label| {
if (std.mem.eql(u8, field.key, "cmd")) {
@ -254,12 +254,12 @@ pub fn lateConfigFieldHandler(config: *Config) void {
}
}
pub fn tryMigrateIniSaveFile(allocator: std.mem.Allocator, path: []const u8, saved_users: *SavedUsers, usernames: [][]const u8) !?IniParser(OldSave) {
var save_parser = try IniParser(OldSave).init(allocator, path, null);
pub fn tryMigrateIniSaveFile(allocator: std.mem.Allocator, io: std.Io, path: []const u8, saved_users: *SavedUsers, usernames: [][]const u8) !?IniParser(OldSave) {
var save_parser = try IniParser(OldSave).init(allocator, io, path, null);
errdefer save_parser.deinit();
var user_buf: [32]u8 = undefined;
const maybe_save = if (save_parser.maybe_load_error == null) save_parser.structure else tryMigrateFirstSaveFile(&user_buf);
const maybe_save = if (save_parser.maybe_load_error == null) save_parser.structure else tryMigrateFirstSaveFile(io, &user_buf);
if (maybe_save) |save| {
// Add all other users to the list
@ -282,16 +282,16 @@ pub fn tryMigrateIniSaveFile(allocator: std.mem.Allocator, path: []const u8, sav
return null;
}
fn tryMigrateFirstSaveFile(user_buf: *[32]u8) ?OldSave {
fn tryMigrateFirstSaveFile(io: std.Io, user_buf: *[32]u8) ?OldSave {
if (maybe_save_file) |path| {
defer temporary_allocator.free(path);
var save = OldSave{};
var file = std.fs.openFileAbsolute(path, .{}) catch return null;
defer file.close();
var file = std.Io.Dir.openFileAbsolute(io, path, .{}) catch return null;
defer file.close(io);
var file_buffer: [64]u8 = undefined;
var file_reader = file.reader(&file_buffer);
var file_reader = file.reader(io, &file_buffer);
var reader = &file_reader.interface;
var user_writer = std.Io.Writer.fixed(user_buf);

File diff suppressed because it is too large Load diff