Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package waylock for openSUSE:Factory checked in at 2026-09-28 10:48:23 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/waylock (Old) and /work/SRC/openSUSE:Factory/.waylock.new.383539 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "waylock" Mon Sep 28 10:48:23 2026 rev:11 rq:1381069 version:1.6.0 Changes: -------- --- /work/SRC/openSUSE:Factory/waylock/waylock.changes 2025-10-08 21:53:25.841908044 +0200 +++ /work/SRC/openSUSE:Factory/.waylock.new.383539/waylock.changes 2026-09-28 10:49:28.239539353 +0200 @@ -1,0 +2,17 @@ +Sat Sep 26 16:30:08 UTC 2026 - Andrea Manzini <[email protected]> + +- Update to version 1.6.0: + * Tracks Zig's 0.16 release; no functional changes to waylock + itself (upstream: "no changes to waylock's behavior are + intended") +- Raise the Zig toolchain BuildRequires floor for the 0.16 + requirement: (zig >= 0.16.0 with zig < 0.17) and + (zig-rpm-macros >= 0.16.0 with zig-rpm-macros < 0.17) +- Drop add-experimental-non-llvm-zig-backend.patch: upstream's + build.zig now has its own -Dllvm option, defaulting to the same + non-LLVM backend the patch forced +- Regenerate vendor.tar.zst: bump the vendored zig-wayland + dependency to 0.6.0 (zig-xkbcommon stays at 0.3.0) and rebuild it + for Zig 0.16's reworked package-cache layout + +------------------------------------------------------------------- Old: ---- add-experimental-non-llvm-zig-backend.patch waylock-1.5.0.tar.gz waylock-1.5.0.tar.gz.sig New: ---- waylock-1.6.0.tar.gz waylock-1.6.0.tar.gz.sig ----------(Old B)---------- Old: (zig-rpm-macros >= 0.16.0 with zig-rpm-macros < 0.17) - Drop add-experimental-non-llvm-zig-backend.patch: upstream's build.zig now has its own -Dllvm option, defaulting to the same ----------(Old E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ waylock.spec ++++++ --- /var/tmp/diff_new_pack.360pA4/_old 2026-09-28 10:49:29.038572831 +0200 +++ /var/tmp/diff_new_pack.360pA4/_new 2026-09-28 10:49:29.040572915 +0200 @@ -1,7 +1,7 @@ # # spec file for package waylock # -# Copyright (c) 2025 SUSE LLC and contributors +# Copyright (c) 2026 SUSE LLC and contributors # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: waylock -Version: 1.5.0 +Version: 1.6.0 Release: 0 Summary: Small screenlocker for Wayland compositors License: ISC @@ -27,12 +27,11 @@ Source2: https://isaacfreund.com/public_key.txt#/%{name}.keyring Source3: https://codeberg.org/ifreund/waylock/releases/download/v%{version}/waylock-%{version}.tar.gz.sig Source4: vendor.tar.zst -Patch1: add-experimental-non-llvm-zig-backend.patch BuildRequires: pkgconfig BuildRequires: scdoc >= 1.9.2 BuildRequires: zstd -BuildRequires: (zig >= 0.15.0 with zig < 0.16) -BuildRequires: (zig-rpm-macros >= 0.15.0 with zig-rpm-macros < 0.16) +BuildRequires: (zig >= 0.16.0 with zig < 0.17) +BuildRequires: (zig-rpm-macros >= 0.16.0 with zig-rpm-macros < 0.17) BuildRequires: pkgconfig(pam) BuildRequires: pkgconfig(wayland-protocols) >= 1.24 BuildRequires: pkgconfig(wayland-server) >= 1.20.0 @@ -45,17 +44,17 @@ cause the session to be unlocked.) %prep -%autosetup -a4 -p1 +%autosetup -a4 # Replace with configuration that works in openSUSE cp %{SOURCE1} ./pam.d/waylock %build -%global zig_opts --global-cache-dir vendor/ -Dpie -Dno-llvm -%zig_build %{zig_opts} +%global zig_opts --global-cache-dir vendor/ -Dpie +%{zig_build} %{zig_opts} %install -%zig_install %{zig_opts} +%{zig_install} %{zig_opts} # Removes rpmlint error: filelist-forbidden-move-to-usr error mkdir -p %{buildroot}%{_pam_vendordir} ++++++ vendor.tar.zst ++++++ /work/SRC/openSUSE:Factory/waylock/vendor.tar.zst /work/SRC/openSUSE:Factory/.waylock.new.383539/vendor.tar.zst differ: char 6, line 1 ++++++ waylock-1.5.0.tar.gz -> waylock-1.6.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waylock-1.5.0/.builds/alpine.yml new/waylock-1.6.0/.builds/alpine.yml --- old/waylock-1.5.0/.builds/alpine.yml 2025-09-10 10:24:33.000000000 +0200 +++ new/waylock-1.6.0/.builds/alpine.yml 2026-04-27 11:40:52.000000000 +0200 @@ -14,10 +14,10 @@ tasks: - install_deps: | # Eat Github's resources rather than the Zig Software Foundation's resources! - wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.15.1/zig-x86_64-linux-0.15.1.tar.xz - tar xf zig-x86_64-linux-0.15.1.tar.xz - sudo mv zig-x86_64-linux-0.15.1/zig /usr/bin/ - sudo mv zig-x86_64-linux-0.15.1/lib /usr/lib/zig + wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.16.0/zig-x86_64-linux-0.16.0.tar.xz + tar xf zig-x86_64-linux-0.16.0.tar.xz + sudo mv zig-x86_64-linux-0.16.0/zig /usr/bin/ + sudo mv zig-x86_64-linux-0.16.0/lib /usr/lib/zig - build: | cd waylock zig build -Dman-pages diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waylock-1.5.0/.builds/archlinux.yml new/waylock-1.6.0/.builds/archlinux.yml --- old/waylock-1.5.0/.builds/archlinux.yml 2025-09-10 10:24:33.000000000 +0200 +++ new/waylock-1.6.0/.builds/archlinux.yml 2026-04-27 11:40:52.000000000 +0200 @@ -14,13 +14,15 @@ tasks: - install_deps: | # Eat Github's resources rather than the Zig Software Foundation's resources! - wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.15.1/zig-x86_64-linux-0.15.1.tar.xz - tar xf zig-x86_64-linux-0.15.1.tar.xz - sudo mv zig-x86_64-linux-0.15.1/zig /usr/bin/ - sudo mv zig-x86_64-linux-0.15.1/lib /usr/lib/zig + wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.16.0/zig-x86_64-linux-0.16.0.tar.xz + tar xf zig-x86_64-linux-0.16.0.tar.xz + sudo mv zig-x86_64-linux-0.16.0/zig /usr/bin/ + sudo mv zig-x86_64-linux-0.16.0/lib /usr/lib/zig - build: | cd waylock - zig build -Dman-pages + # Arch's glibc version has SFrame sections in crt objects, force llvm/lld usage as a workaround + # https://codeberg.org/ziglang/zig/issues/31272 + zig build -Dllvm -Dman-pages --summary all - fmt: | cd waylock zig fmt --check build.zig diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waylock-1.5.0/.builds/freebsd.yml new/waylock-1.6.0/.builds/freebsd.yml --- old/waylock-1.5.0/.builds/freebsd.yml 2025-09-10 10:24:33.000000000 +0200 +++ new/waylock-1.6.0/.builds/freebsd.yml 2026-04-27 11:40:52.000000000 +0200 @@ -12,10 +12,10 @@ tasks: - install_deps: | # Eat Github's resources rather than the Zig Software Foundation's resources! - wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.15.1/zig-x86_64-freebsd-0.15.1.tar.xz - tar xf zig-x86_64-freebsd-0.15.1.tar.xz - sudo mv zig-x86_64-freebsd-0.15.1/zig /usr/bin/ - sudo mv zig-x86_64-freebsd-0.15.1/lib /usr/lib/zig + wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.16.0/zig-x86_64-freebsd-0.16.0.tar.xz + tar xf zig-x86_64-freebsd-0.16.0.tar.xz + sudo mv zig-x86_64-freebsd-0.16.0/zig /usr/bin/ + sudo mv zig-x86_64-freebsd-0.16.0/lib /usr/lib/zig - build: | cd waylock zig build -Dman-pages diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waylock-1.5.0/.gitignore new/waylock-1.6.0/.gitignore --- old/waylock-1.5.0/.gitignore 2025-09-10 10:24:33.000000000 +0200 +++ new/waylock-1.6.0/.gitignore 2026-04-27 11:40:52.000000000 +0200 @@ -1,3 +1,3 @@ .zig-cache zig-out -/doc/waylock.1 +zig-pkg diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waylock-1.5.0/README.md new/waylock-1.6.0/README.md --- old/waylock-1.5.0/README.md 2025-09-10 10:24:33.000000000 +0200 +++ new/waylock-1.6.0/README.md 2026-04-27 11:40:52.000000000 +0200 @@ -21,7 +21,7 @@ To compile waylock first ensure that you have the following dependencies installed: -- [zig](https://ziglang.org/download/) 0.15 +- [zig](https://ziglang.org/download/) 0.16 - wayland - wayland-protocols - xkbcommon diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waylock-1.5.0/build.zig new/waylock-1.6.0/build.zig --- old/waylock-1.5.0/build.zig 2025-09-10 10:24:33.000000000 +0200 +++ new/waylock-1.6.0/build.zig 2026-04-27 11:40:52.000000000 +0200 @@ -18,6 +18,7 @@ const strip = b.option(bool, "strip", "Omit debug information") orelse false; const pie = b.option(bool, "pie", "Build a Position Independent Executable") orelse false; + const use_llvm = b.option(bool, "llvm", "Force use of Zig's LLVM backend and the lld linker") orelse false; const man_pages = b.option( bool, @@ -26,7 +27,6 @@ ) orelse scdoc_found: { _ = b.findProgram(&.{"scdoc"}, &.{}) catch |err| switch (err) { error.FileNotFound => break :scdoc_found false, - else => return err, }; break :scdoc_found true; }; @@ -38,7 +38,7 @@ // This makes the caching work for the Workaround, and the extra argument is ignored by /bin/sh. scdoc.addFileArg(b.path("doc/waylock.1.scd")); - const stdout = scdoc.captureStdOut(); + const stdout = scdoc.captureStdOut(.{}); b.getInstallStep().dependOn(&b.addInstallFile(stdout, "share/man/man1/waylock.1").step); } @@ -56,7 +56,7 @@ const git_describe_long = b.runAllowFail( &.{ "git", "-C", b.build_root.path orelse ".", "describe", "--long" }, &ret, - .Inherit, + .inherit, ) catch break :blk version; var it = mem.splitScalar(u8, mem.trim(u8, git_describe_long, &std.ascii.whitespace), '-'); @@ -97,20 +97,22 @@ .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, + .link_libc = true, + .strip = strip, }), + .use_llvm = use_llvm, + .use_lld = use_llvm, }); waylock.root_module.addOptions("build_options", options); - waylock.linkLibC(); - waylock.linkSystemLibrary("pam"); + waylock.root_module.linkSystemLibrary("pam", .{}); waylock.root_module.addImport("wayland", wayland); - waylock.linkSystemLibrary("wayland-client"); + waylock.root_module.linkSystemLibrary("wayland-client", .{}); waylock.root_module.addImport("xkbcommon", xkbcommon); - waylock.linkSystemLibrary("xkbcommon"); + waylock.root_module.linkSystemLibrary("xkbcommon", .{}); - waylock.root_module.strip = strip; waylock.pie = pie; b.installArtifact(waylock); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waylock-1.5.0/build.zig.zon new/waylock-1.6.0/build.zig.zon --- old/waylock-1.5.0/build.zig.zon 2025-09-10 10:24:33.000000000 +0200 +++ new/waylock-1.6.0/build.zig.zon 2026-04-27 11:40:52.000000000 +0200 @@ -1,11 +1,11 @@ .{ .name = .waylock, - .version = "1.5.0", + .version = "1.6.0", .paths = .{""}, .dependencies = .{ .wayland = .{ - .url = "https://codeberg.org/ifreund/zig-wayland/archive/v0.4.0.tar.gz", - .hash = "wayland-0.4.0-lQa1khbMAQAsLS2eBR7M5lofyEGPIbu2iFDmoz8lPC27", + .url = "https://codeberg.org/ifreund/zig-wayland/archive/v0.6.0.tar.gz", + .hash = "wayland-0.6.0-lQa1kqz8AQADQmdNJsNhLoNHcnEGEUjrOaPV-dtEnEmX", }, .xkbcommon = .{ .url = "https://codeberg.org/ifreund/zig-xkbcommon/archive/v0.3.0.tar.gz", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waylock-1.5.0/src/Lock.zig new/waylock-1.6.0/src/Lock.zig --- old/waylock-1.5.0/src/Lock.zig 2025-09-10 10:24:33.000000000 +0200 +++ new/waylock-1.6.0/src/Lock.zig 2026-04-27 11:40:52.000000000 +0200 @@ -3,9 +3,13 @@ const std = @import("std"); const builtin = @import("builtin"); const assert = std.debug.assert; +const Io = std.Io; const log = std.log; const mem = std.mem; const posix = std.posix; +const system = std.posix.system; +const process = std.process; +const fatal = process.fatal; const wayland = @import("wayland"); const wl = wayland.client.wl; @@ -20,8 +24,6 @@ const Seat = @import("Seat.zig"); const PasswordBuffer = @import("PasswordBuffer.zig"); -const gpa = std.heap.c_allocator; - pub const Color = enum { init, input, @@ -48,6 +50,9 @@ } }; +io: Io, +gpa: mem.Allocator, + state: enum { /// The session lock object has not yet been created. initializing, @@ -84,8 +89,10 @@ password: PasswordBuffer, auth_connection: auth.Connection, -pub fn run(options: Options) void { +pub fn run(io: Io, gpa: mem.Allocator, options: Options) void { var lock: Lock = .{ + .io = io, + .gpa = gpa, .fork_on_lock = options.fork_on_lock, .ready_fd = options.ready_fd, .ignore_empty_password = options.ignore_empty_password, @@ -98,9 +105,7 @@ .outputs = undefined, .xkb_context = xkb.Context.new(.no_flags) orelse fatal_oom(), .password = PasswordBuffer.init(), - .auth_connection = auth.fork_child() catch |err| { - fatal("failed to fork child authentication process: {s}", .{@errorName(err)}); - }, + .auth_connection = auth.fork_child(io), }; defer lock.deinit(); @@ -180,7 +185,7 @@ if (lock.pollfds[poll_auth].revents & posix.POLL.IN != 0) { var byte: [1]u8 = undefined; - var reader = lock.auth_connection.reader(); + var reader = lock.auth_connection.reader(lock.io); reader.interface.readSliceAll(&byte) catch |err| { fatal("failed to read response from child authentication process: {s}", .{@errorName(err)}); }; @@ -307,7 +312,7 @@ const wl_output = try registry.bind(ev.name, wl.Output, 3); errdefer wl_output.release(); - const output = try gpa.create(Output); + const output = try lock.gpa.create(Output); errdefer output.destroy(); output.* = .{ @@ -366,12 +371,11 @@ assert(lock.state == .locking); lock.state = .locked; if (lock.ready_fd) |ready_fd| { - const file = std.fs.File{ .handle = ready_fd }; - file.writeAll("\n") catch |err| { - log.err("failed to send readiness notification: {s}", .{@errorName(err)}); - posix.exit(1); + const file: Io.File = .{ .handle = ready_fd, .flags = .{ .nonblocking = false } }; + defer file.close(lock.io); + file.writeStreamingAll(lock.io, "\n") catch |err| { + fatal("failed to send readiness notification: {s}", .{@errorName(err)}); }; - file.close(); lock.ready_fd = null; } if (lock.fork_on_lock) { @@ -385,11 +389,11 @@ .locking => { log.err("the wayland compositor has denied our attempt to lock the session, " ++ "is another ext-session-lock client already running?", .{}); - posix.exit(1); + process.exit(1); }, .locked => { log.info("the wayland compositor has unlocked the session, exiting", .{}); - posix.exit(0); + process.exit(0); }, .exiting => unreachable, } @@ -412,7 +416,7 @@ fn send_password_to_auth(lock: *Lock) !void { defer lock.password.clear(); - var writer = lock.auth_connection.writer(); + var writer = lock.auth_connection.writer(lock.io); const len_bytes: [4]u8 = @bitCast(@as(u32, @intCast(lock.password.buffer.len))); try writer.interface.writeAll(&len_bytes); try writer.interface.writeAll(lock.password.buffer); @@ -429,11 +433,6 @@ } } -fn fatal(comptime format: []const u8, args: anytype) noreturn { - log.err(format, args); - posix.exit(1); -} - fn fatal_oom() noreturn { fatal("out of memory during initialization", .{}); } @@ -459,25 +458,31 @@ return buffers; } -// TODO: Upstream this to the Zig standard library -extern fn setsid() posix.pid_t; - fn fork_to_background() void { - const pid = posix.fork() catch |err| fatal("fork failed: {s}", .{@errorName(err)}); + const pid: system.pid_t = fork: { + const rc = system.fork(); + switch (system.errno(rc)) { + .SUCCESS => break :fork @intCast(rc), + else => |err| fatal("fork failed: {}", .{err}), + } + }; if (pid == 0) { // This can't fail as we are the child of a fork() and therefore not // a process group leader. - assert(setsid() != -1); + _ = system.setsid(); // Ensure the working directory is on the root filesystem to avoid potentially // blocking some other filesystem from being unmounted. - posix.chdirZ("/") catch |err| { - // While this is a nice thing to do, it is not critical to the locking functionality - // and it is better to allow potentially unlocking the session rather than aborting - // and leaving the session locked if this fails. - log.warn("failed to change working directory to / on fork: {s}", .{@errorName(err)}); - }; + switch (system.errno(system.chdir("/"))) { + .SUCCESS => {}, + else => |err| { + // While this is a nice thing to do, it is not critical to the locking functionality + // and it is better to allow potentially unlocking the session rather than aborting + // and leaving the session locked if this fails. + log.warn("failed to change working directory to / on fork: {}", .{err}); + }, + } } else { // Terminate the parent process with a clean exit code. - posix.exit(0); + process.exit(0); } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waylock-1.5.0/src/Output.zig new/waylock-1.6.0/src/Output.zig --- old/waylock-1.5.0/src/Output.zig 2025-09-10 10:24:33.000000000 +0200 +++ new/waylock-1.6.0/src/Output.zig 2026-04-27 11:40:52.000000000 +0200 @@ -13,8 +13,6 @@ const Lock = @import("Lock.zig"); -const gpa = std.heap.c_allocator; - lock: *Lock, name: u32, wl_output: *wl.Output, @@ -47,7 +45,7 @@ if (output.surface) |surface| surface.destroy(); output.link.remove(); - gpa.destroy(output); + output.lock.gpa.destroy(output); } fn lock_surface_listener( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waylock-1.5.0/src/PasswordBuffer.zig new/waylock-1.6.0/src/PasswordBuffer.zig --- old/waylock-1.5.0/src/PasswordBuffer.zig 2025-09-10 10:24:33.000000000 +0200 +++ new/waylock-1.6.0/src/PasswordBuffer.zig 2026-04-27 11:40:52.000000000 +0200 @@ -6,6 +6,7 @@ const log = std.log; const heap = std.heap; const posix = std.posix; +const process = std.process; const auth = @import("auth.zig"); @@ -17,8 +18,7 @@ pub fn init() PasswordBuffer { var password: PasswordBuffer = .{ .buffer = gpa.alignedAlloc(u8, .fromByteUnits(heap.page_size_max), size_max) catch { - log.err("failed to allocate password buffer", .{}); - posix.exit(1); + process.fatal("failed to allocate password buffer", .{}); }, }; @@ -79,13 +79,11 @@ .SUCCESS => return, .AGAIN => continue, else => { - log.err("mlock() on password buffer failed: E{s}", .{@tagName(errno)}); - posix.exit(1); + process.fatal("mlock() on password buffer failed: E{s}", .{@tagName(errno)}); }, } } - log.err("mlock() on password buffer failed: EAGAIN after 10 attempts", .{}); - posix.exit(1); + process.fatal("mlock() on password buffer failed: EAGAIN after 10 attempts", .{}); } fn prevent_dumping_best_effort(buffer: []align(heap.page_size_max) u8) void { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waylock-1.5.0/src/Seat.zig new/waylock-1.6.0/src/Seat.zig --- old/waylock-1.5.0/src/Seat.zig 2025-09-10 10:24:33.000000000 +0200 +++ new/waylock-1.6.0/src/Seat.zig 2026-04-27 11:40:52.000000000 +0200 @@ -12,8 +12,6 @@ const Lock = @import("Lock.zig"); -const gpa = std.heap.c_allocator; - lock: *Lock, name: u32, wl_seat: *wl.Seat, @@ -24,8 +22,8 @@ link: wl.list.Link, pub fn create(lock: *Lock, name: u32, wl_seat: *wl.Seat) !void { - const seat = try gpa.create(Seat); - errdefer gpa.destroy(seat); + const seat = try lock.gpa.create(Seat); + errdefer lock.gpa.destroy(seat); seat.* = .{ .lock = lock, @@ -45,7 +43,7 @@ if (seat.xkb_state) |xkb_state| xkb_state.unref(); seat.link.remove(); - gpa.destroy(seat); + seat.lock.gpa.destroy(seat); } fn seat_listener(wl_seat: *wl.Seat, event: wl.Seat.Event, seat: *Seat) void { @@ -98,14 +96,14 @@ // only care about press events, not release. }, .keymap => |ev| { - defer posix.close(ev.fd); + defer _ = posix.system.close(ev.fd); if (ev.format != .xkb_v1) { log.err("unsupported keymap format {d}", .{@intFromEnum(ev.format)}); return; } - const keymap_string = posix.mmap(null, ev.size, posix.PROT.READ, .{ .TYPE = .PRIVATE }, ev.fd, 0) catch |err| { + const keymap_string = posix.mmap(null, ev.size, .{ .READ = true }, .{ .TYPE = .PRIVATE }, ev.fd, 0) catch |err| { log.err("failed to mmap() keymap fd: {s}", .{@errorName(err)}); return; }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waylock-1.5.0/src/auth.zig new/waylock-1.6.0/src/auth.zig --- old/waylock-1.5.0/src/auth.zig 2025-09-10 10:24:33.000000000 +0200 +++ new/waylock-1.6.0/src/auth.zig 2026-04-27 11:40:52.000000000 +0200 @@ -1,9 +1,12 @@ const builtin = @import("builtin"); const std = @import("std"); const assert = std.debug.assert; +const Io = std.Io; const log = std.log; const mem = std.mem; -const posix = std.posix; +const process = std.process; +const fatal = process.fatal; +const system = std.posix.system; const c = @cImport({ @cInclude("unistd.h"); // getuid() @@ -15,38 +18,53 @@ const PasswordBuffer = @import("PasswordBuffer.zig"); pub const Connection = struct { - read_fd: posix.fd_t, - write_fd: posix.fd_t, + read_fd: system.fd_t, + write_fd: system.fd_t, - pub fn reader(conn: Connection) std.fs.File.Reader { - const file = std.fs.File{ .handle = conn.read_fd }; - return file.readerStreaming(&.{}); + pub fn reader(conn: Connection, io: Io) Io.File.Reader { + const file = Io.File{ .handle = conn.read_fd, .flags = .{ .nonblocking = false } }; + return file.readerStreaming(io, &.{}); } - pub fn writer(conn: Connection) std.fs.File.Writer { - const file = std.fs.File{ .handle = conn.write_fd }; - return file.writerStreaming(&.{}); + pub fn writer(conn: Connection, io: Io) Io.File.Writer { + const file = Io.File{ .handle = conn.write_fd, .flags = .{ .nonblocking = false } }; + return file.writerStreaming(io, &.{}); } }; -pub fn fork_child() !Connection { - const parent_to_child = try posix.pipe(); - const child_to_parent = try posix.pipe(); +pub fn fork_child(io: Io) Connection { + var parent_to_child: [2]system.fd_t = undefined; + var child_to_parent: [2]system.fd_t = undefined; + switch (system.errno(system.pipe(&parent_to_child))) { + .SUCCESS => {}, + else => |err| fatal("failed to fork child authentication process: {}", .{err}), + } + switch (system.errno(system.pipe(&child_to_parent))) { + .SUCCESS => {}, + else => |err| fatal("failed to fork child authentication process: {}", .{err}), + } + + const pid: system.pid_t = fork: { + const rc = system.fork(); + switch (system.errno(rc)) { + .SUCCESS => break :fork @intCast(rc), + else => |err| fatal("failed to fork child authentication process: {}", .{err}), + } + }; - const pid = try posix.fork(); if (pid == 0) { // We are the child - posix.close(parent_to_child[1]); - posix.close(child_to_parent[0]); + _ = system.close(parent_to_child[1]); + _ = system.close(child_to_parent[0]); - run(.{ + run(io, .{ .read_fd = parent_to_child[0], .write_fd = child_to_parent[1], }); } else { // We are the parent - posix.close(parent_to_child[0]); - posix.close(child_to_parent[1]); + _ = system.close(parent_to_child[0]); + _ = system.close(child_to_parent[1]); return Connection{ .read_fd = child_to_parent[0], @@ -57,7 +75,7 @@ var password: PasswordBuffer = undefined; -pub fn run(conn: Connection) noreturn { +fn run(io: Io, conn: Connection) noreturn { password = PasswordBuffer.init(); const conv: pam.Conv = .{ @@ -69,20 +87,20 @@ { const pw = @as(?*c.struct_passwd, c.getpwuid(c.getuid())) orelse { log.err("failed to get name of current user", .{}); - posix.exit(1); + process.exit(1); }; const result = pam.start("waylock", pw.pw_name, &conv, &pamh); if (result != .success) { log.err("failed to initialize PAM: {s}", .{result.description()}); - posix.exit(1); + process.exit(1); } } while (true) { - read_password(conn) catch |err| { + read_password(io, conn) catch |err| { log.err("failed to read password from pipe: {s}", .{@errorName(err)}); - posix.exit(1); + process.exit(1); }; const auth_result = pamh.authenticate(0); @@ -92,10 +110,10 @@ if (auth_result == .success) { log.debug("PAM authentication succeeded", .{}); - var writer = conn.writer(); + var writer = conn.writer(io); writer.interface.writeByte(@intFromBool(true)) catch |err| { log.err("failed to notify parent of success: {s}", .{@errorName(err)}); - posix.exit(1); + process.exit(1); }; // We don't need to prevent unlocking if this fails. Failure just @@ -113,14 +131,14 @@ log.err("PAM deinitialization failed: {s}", .{end_result.description()}); } - posix.exit(0); + process.exit(0); } else { log.err("PAM authentication failed: {s}", .{auth_result.description()}); - var writer = conn.writer(); + var writer = conn.writer(io); writer.interface.writeByte(@intFromBool(false)) catch |err| { log.err("failed to notify parent of failure: {s}", .{@errorName(err)}); - posix.exit(1); + process.exit(1); }; if (auth_result == .abort) { @@ -128,16 +146,16 @@ if (end_result != .success) { log.err("PAM deinitialization failed: {s}", .{end_result.description()}); } - posix.exit(1); + process.exit(1); } } } } -fn read_password(conn: Connection) !void { +fn read_password(io: Io, conn: Connection) !void { assert(password.buffer.len == 0); - var reader = conn.reader(); + var reader = conn.reader(io); var len_bytes: [4]u8 = undefined; try reader.interface.readSliceAll(&len_bytes); try password.grow(@as(u32, @bitCast(len_bytes))); @@ -150,7 +168,7 @@ resp: *[*]pam.Response, _: ?*anyopaque, ) callconv(.c) pam.Result { - const ally = std.heap.raw_c_allocator; + const ally = std.heap.c_allocator; const responses = ally.alloc(pam.Response, @intCast(num_msg)) catch { return .buf_err; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waylock-1.5.0/src/flags.zig new/waylock-1.6.0/src/flags.zig --- old/waylock-1.5.0/src/flags.zig 2025-09-10 10:24:33.000000000 +0200 +++ new/waylock-1.6.0/src/flags.zig 2026-04-27 11:40:52.000000000 +0200 @@ -1,82 +1,51 @@ -// Zero allocation argument parsing for unix-like systems. -// Released under the Zero Clause BSD (0BSD) license: -// -// Copyright 2023 Isaac Freund -// -// Permission to use, copy, modify, and/or distribute this software for any -// purpose with or without fee is hereby granted. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +// SPDX-FileCopyrightText: © 2023 Isaac Freund +// SPDX-License-Identifier: 0BSD const std = @import("std"); const mem = std.mem; pub const Flag = struct { - name: [:0]const u8, + name: []const u8, kind: enum { boolean, arg }, }; -pub fn parser(comptime Arg: type, comptime flags: []const Flag) type { - switch (Arg) { - // TODO consider allowing []const u8 - [:0]const u8, [*:0]const u8 => {}, // ok - else => @compileError("invalid argument type: " ++ @typeName(Arg)), - } +pub fn parser(comptime flags: []const Flag) type { return struct { pub const Result = struct { /// Remaining args after the recognized flags - args: []const Arg, + args: []const [:0]const u8, /// Data obtained from parsed flags flags: Flags, pub const Flags = flags_type: { - var fields: []const std.builtin.Type.StructField = &.{}; - for (flags) |flag| { - const field: std.builtin.Type.StructField = switch (flag.kind) { - .boolean => .{ - .name = flag.name, - .type = bool, - .default_value_ptr = &false, - .is_comptime = false, - .alignment = @alignOf(bool), + const Attributes = std.builtin.Type.StructField.Attributes; + var names: [flags.len][]const u8 = undefined; + var types: [flags.len]type = undefined; + var attrs: [flags.len]Attributes = undefined; + for (flags, &names, &types, &attrs) |flag, *name, *ty, *attr| { + name.* = flag.name; + switch (flag.kind) { + .boolean => { + ty.* = bool; + attr.* = .{ .default_value_ptr = &false }; }, - .arg => .{ - .name = flag.name, - .type = ?[:0]const u8, - .default_value_ptr = &@as(?[:0]const u8, null), - .is_comptime = false, - .alignment = @alignOf(?[:0]const u8), + .arg => { + ty.* = ?[:0]const u8; + attr.* = .{ .default_value_ptr = &@as(ty.*, null) }; }, - }; - fields = fields ++ [_]std.builtin.Type.StructField{field}; + } } - break :flags_type @Type(.{ .@"struct" = .{ - .layout = .auto, - .fields = fields, - .decls = &.{}, - .is_tuple = false, - } }); + break :flags_type @Struct(.auto, null, &names, &types, &attrs); }; }; - pub fn parse(args: []const Arg) !Result { + pub fn parse(args: []const [:0]const u8) error{MissingFlagArgument}!Result { var result_flags: Result.Flags = .{}; var i: usize = 0; outer: while (i < args.len) : (i += 1) { - const arg = switch (Arg) { - [*:0]const u8 => mem.sliceTo(args[i], 0), - [:0]const u8 => args[i], - else => unreachable, - }; inline for (flags) |flag| { - if (mem.eql(u8, "-" ++ flag.name, arg)) { + if (mem.eql(u8, "-" ++ flag.name, args[i])) { switch (flag.kind) { .boolean => @field(result_flags, flag.name) = true, .arg => { @@ -86,11 +55,7 @@ "' requires an argument but none was provided!", .{}); return error.MissingFlagArgument; } - @field(result_flags, flag.name) = switch (Arg) { - [*:0]const u8 => mem.sliceTo(args[i], 0), - [:0]const u8 => args[i], - else => unreachable, - }; + @field(result_flags, flag.name) = args[i]; }, } continue :outer; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/waylock-1.5.0/src/main.zig new/waylock-1.6.0/src/main.zig --- old/waylock-1.5.0/src/main.zig 2025-09-10 10:24:33.000000000 +0200 +++ new/waylock-1.6.0/src/main.zig 2026-04-27 11:40:52.000000000 +0200 @@ -1,9 +1,9 @@ const std = @import("std"); const assert = std.debug.assert; -const fs = std.fs; -const io = std.io; +const Io = std.Io; const mem = std.mem; const posix = std.posix; +const process = std.process; const log = std.log; const build_options = @import("build_options"); @@ -30,8 +30,20 @@ \\ ; -pub fn main() void { - const result = flags.parser([*:0]const u8, &.{ +pub fn main(init: process.Init) error{ OutOfMemory, Unexpected }!void { + const io = init.io; + const arena = init.arena.allocator(); + + var stdout_buffer: [512]u8 = undefined; + var stdout_writer = Io.File.stdout().writer(io, &stdout_buffer); + const stdout = &stdout_writer.interface; + + var stderr_buffer: [512]u8 = undefined; + var stderr_writer = Io.File.stderr().writer(io, &stderr_buffer); + const stderr = &stderr_writer.interface; + + const args = try init.minimal.args.toSlice(arena); + const result = flags.parser(&.{ .{ .name = "h", .kind = .boolean }, .{ .name = "version", .kind = .boolean }, .{ .name = "log-level", .kind = .arg }, @@ -42,23 +54,23 @@ .{ .name = "input-color", .kind = .arg }, .{ .name = "input-alt-color", .kind = .arg }, .{ .name = "fail-color", .kind = .arg }, - }).parse(std.os.argv[1..]) catch { - fs.File.stderr().writeAll(usage) catch {}; - posix.exit(1); + }).parse(args[1..]) catch { + stderr.writeAll(usage) catch {}; + process.exit(1); }; if (result.flags.h) { - fs.File.stdout().writeAll(usage) catch posix.exit(1); - posix.exit(0); + stdout.writeAll(usage) catch process.exit(1); + process.exit(0); } if (result.args.len != 0) { log.err("unknown option '{s}'", .{result.args[0]}); - fs.File.stderr().writeAll(usage) catch {}; - posix.exit(1); + stderr.writeAll(usage) catch {}; + process.exit(1); } if (result.flags.version) { - fs.File.stdout().writeAll(build_options.version ++ "\n") catch posix.exit(1); - posix.exit(0); + stdout.writeAll(build_options.version ++ "\n") catch process.exit(1); + process.exit(0); } if (result.flags.@"log-level") |level| { if (mem.eql(u8, level, "error")) { @@ -71,7 +83,7 @@ runtime_log_level = .debug; } else { log.err("invalid log level '{s}'", .{level}); - posix.exit(1); + process.exit(1); } } @@ -82,7 +94,7 @@ if (result.flags.@"ready-fd") |raw| { options.ready_fd = std.fmt.parseInt(posix.fd_t, raw, 10) catch { log.err("invalid file descriptor '{s}'", .{raw}); - posix.exit(1); + process.exit(1); }; } if (result.flags.@"init-color") |raw| options.init_color = parse_color(raw); @@ -93,7 +105,7 @@ if (result.flags.@"input-alt-color") |raw| options.input_alt_color = parse_color(raw); if (result.flags.@"fail-color") |raw| options.fail_color = parse_color(raw); - Lock.run(options); + Lock.run(io, init.gpa, options); } fn parse_color(raw: []const u8) u24 { @@ -105,7 +117,7 @@ fn fatal_bad_color(raw: []const u8) noreturn { log.err("invalid color '{s}', expected format '0xRRGGBB'", .{raw}); - posix.exit(1); + process.exit(1); } /// Set the default log level based on the build mode. @@ -126,14 +138,7 @@ comptime format: []const u8, args: anytype, ) void { - // waylock is small enough that we don't need scopes - comptime assert(scope == .default); - if (@intFromEnum(level) > @intFromEnum(runtime_log_level)) return; - var buffer: [256]u8 = undefined; - const stderr = std.debug.lockStderrWriter(&buffer); - defer std.debug.unlockStderrWriter(); - - stderr.print(level.asText() ++ ": " ++ format ++ "\n", args) catch {}; + log.defaultLog(level, scope, format, args); }
