From 83cb38d1609efcb886c198a379037fd06645f4d1 Mon Sep 17 00:00:00 2001 From: Kristaps Kaupe Date: Thu, 4 Sep 2025 18:08:49 +0300 Subject: [PATCH] Move to Zig 0.14 --- .github/workflows/ci.yml | 14 +++-- .gitignore | 1 + README.md | 2 +- build.zig | 121 ++++++++++++++++++++++---------------- build.zig.zon | 3 +- lib/ini | 2 +- lib/nif/build.zig | 12 ++-- src/lightning/LndConf.zig | 4 +- src/nd.zig | 6 +- src/nd/Config.zig | 39 ++++++------ src/nd/Daemon.zig | 15 +++-- src/nd/network.zig | 8 +-- src/ngui.zig | 4 +- src/sys/Service.zig | 4 +- src/sys/sysimpl.zig | 4 +- src/test.zig | 28 ++++----- src/test/guiplay.zig | 8 +-- src/types.zig | 2 +- src/ui/lightning.zig | 2 +- 19 files changed, 155 insertions(+), 124 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 15ba230..b395a89 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,17 +15,19 @@ jobs: - name: Set up Zig uses: goto-bus-stop/setup-zig@v2 with: - version: '0.12.1' + version: '0.14.1' - name: Lint run: ./tools/fmt-check.sh - name: Test run: zig build test - - name: Build SDL2 - run: | - sudo apt-get install -y libsdl2-dev - zig build -Ddriver=sdl2 +# - name: Build SDL2 +# run: | +# sudo apt-get install -y libsdl2-dev +# zig build -Ddriver=sdl2 - name: Build X11 - run: zig build -Ddriver=x11 + run: | + sudo apt-get install -y libx11-dev + zig build -Ddriver=x11 - name: Build Aarch64 run: | zig build -Ddriver=fbev -Dtarget=aarch64-linux-musl -Doptimize=ReleaseSafe -Dstrip diff --git a/.gitignore b/.gitignore index 3c2fc15..cd60c09 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store .ccls-cache +.zig-cache zig-cache zig-out diff --git a/README.md b/README.md index 8dfdec2..6f1f4b3 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ with `zig build`. otherwise, for macOS or non-X11 platforms use SDL2: ## local development -you'll need [zig v0.12.x](https://ziglang.org/download/). +you'll need [zig v0.14.x](https://ziglang.org/download/). if working on the gui using sdl2 driver, also [SDL2](https://www.libsdl.org/). note that compiling the daemon on macOS is currently unsupported since diff --git a/build.zig b/build.zig index ac00456..f1b430f 100644 --- a/build.zig +++ b/build.zig @@ -31,18 +31,21 @@ pub fn build(b: *std.Build) void { }; // gui build - const ngui = b.addExecutable(.{ - .name = "ngui", + const ngui = b.createModule(.{ .root_source_file = b.path("src/ngui.zig"), .target = target, .optimize = optimize, .link_libc = true, .strip = strip, }); - ngui.pie = true; - ngui.root_module.addImport("build_options", buildopts_mod); - ngui.addIncludePath(b.path("lib")); - ngui.addIncludePath(b.path("src/ui/c")); + const ngui_exe = b.addExecutable(.{ + .name = "ngui", + .root_module = ngui, + }); + ngui_exe.pie = true; + ngui_exe.root_module.addImport("build_options", buildopts_mod); + ngui_exe.addIncludePath(b.path("lib")); + ngui_exe.addIncludePath(b.path("src/ui/c")); const lvgl_flags = .{ "-std=c11", @@ -50,7 +53,7 @@ pub fn build(b: *std.Build) void { "-Wformat", "-Wformat-security", } ++ common_cflags; - ngui.addCSourceFiles(.{ .files = lvgl_generic_src, .flags = &lvgl_flags }); + ngui_exe.addCSourceFiles(.{ .files = lvgl_generic_src, .flags = &lvgl_flags }); const ngui_cflags = .{ "-std=c11", @@ -58,7 +61,7 @@ pub fn build(b: *std.Build) void { "-Wunused-parameter", "-Werror", } ++ common_cflags; - ngui.addCSourceFiles(.{ + ngui_exe.addCSourceFiles(.{ .root = b.path("src/ui/c"), .files = &.{ "ui.c", @@ -69,69 +72,77 @@ pub fn build(b: *std.Build) void { .flags = &ngui_cflags, }); - ngui.root_module.addCMacro("NM_DISP_HOR", b.fmt("{d}", .{disp_horiz})); - ngui.root_module.addCMacro("NM_DISP_VER", b.fmt("{d}", .{disp_vert})); - ngui.defineCMacro("LV_CONF_INCLUDE_SIMPLE", "1"); - ngui.defineCMacro("LV_LOG_LEVEL", lvgl_loglevel.text()); - ngui.defineCMacro("LV_TICK_CUSTOM", "1"); - ngui.defineCMacro("LV_TICK_CUSTOM_INCLUDE", "\"lv_custom_tick.h\""); - ngui.defineCMacro("LV_TICK_CUSTOM_SYS_TIME_EXPR", "(nm_get_curr_tick())"); + ngui_exe.root_module.addCMacro("NM_DISP_HOR", b.fmt("{d}", .{disp_horiz})); + ngui_exe.root_module.addCMacro("NM_DISP_VER", b.fmt("{d}", .{disp_vert})); + ngui_exe.root_module.addCMacro("LV_CONF_INCLUDE_SIMPLE", "1"); + ngui_exe.root_module.addCMacro("LV_LOG_LEVEL", lvgl_loglevel.text()); + ngui_exe.root_module.addCMacro("LV_TICK_CUSTOM", "1"); + ngui_exe.root_module.addCMacro("LV_TICK_CUSTOM_INCLUDE", "\"lv_custom_tick.h\""); + ngui_exe.root_module.addCMacro("LV_TICK_CUSTOM_SYS_TIME_EXPR", "(nm_get_curr_tick())"); switch (drv) { .sdl2 => { - ngui.addCSourceFiles(.{ .files = lvgl_sdl2_src, .flags = &lvgl_flags }); - ngui.addCSourceFile(.{ .file = b.path("src/ui/c/drv_sdl2.c"), .flags = &ngui_cflags }); - ngui.defineCMacro("USE_SDL", "1"); - ngui.linkSystemLibrary("SDL2"); + ngui_exe.addCSourceFiles(.{ .files = lvgl_sdl2_src, .flags = &lvgl_flags }); + ngui_exe.addCSourceFile(.{ .file = b.path("src/ui/c/drv_sdl2.c"), .flags = &ngui_cflags }); + ngui_exe.root_module.addCMacro("USE_SDL", "1"); + ngui_exe.linkSystemLibrary("SDL2"); }, .x11 => { - ngui.addCSourceFiles(.{ .files = lvgl_x11_src, .flags = &lvgl_flags }); - ngui.addCSourceFiles(.{ + ngui_exe.addCSourceFiles(.{ .files = lvgl_x11_src, .flags = &lvgl_flags }); + ngui_exe.addCSourceFiles(.{ .files = &.{ "src/ui/c/drv_x11.c", "src/ui/c/mouse_cursor_icon.c", }, .flags = &ngui_cflags, }); - ngui.defineCMacro("USE_X11", "1"); - ngui.linkSystemLibrary("X11"); + ngui_exe.root_module.addCMacro("USE_X11", "1"); + ngui_exe.linkSystemLibrary("X11"); }, .fbev => { - ngui.addCSourceFiles(.{ .files = lvgl_fbev_src, .flags = &lvgl_flags }); - ngui.addCSourceFile(.{ .file = b.path("src/ui/c/drv_fbev.c"), .flags = &ngui_cflags }); - ngui.defineCMacro("USE_FBDEV", "1"); - ngui.defineCMacro("USE_EVDEV", "1"); + ngui_exe.addCSourceFiles(.{ .files = lvgl_fbev_src, .flags = &lvgl_flags }); + ngui_exe.addCSourceFile(.{ .file = b.path("src/ui/c/drv_fbev.c"), .flags = &ngui_cflags }); + ngui_exe.root_module.addCMacro("USE_FBDEV", "1"); + ngui_exe.root_module.addCMacro("USE_EVDEV", "1"); }, } const ngui_build_step = b.step("ngui", "build ngui (nakamochi gui)"); - ngui_build_step.dependOn(&b.addInstallArtifact(ngui, .{}).step); + ngui_build_step.dependOn(&b.addInstallArtifact(ngui_exe, .{}).step); // daemon build - const nd = b.addExecutable(.{ - .name = "nd", + const nd = b.createModule(.{ .root_source_file = b.path("src/nd.zig"), .target = target, .optimize = optimize, + .link_libc = true, .strip = strip, }); - nd.pie = true; - nd.root_module.addImport("build_options", buildopts_mod); - nd.root_module.addImport("nif", libnif_dep.module("nif")); - nd.root_module.addImport("ini", libini_dep.module("ini")); - nd.linkLibrary(libnif); + const nd_exe = b.addExecutable(.{ + .name = "nd", + .root_module = nd, + }); + nd_exe.pie = true; + nd_exe.root_module.addImport("build_options", buildopts_mod); + nd_exe.root_module.addImport("nif", libnif_dep.module("nif")); + nd_exe.root_module.addImport("ini", libini_dep.module("ini")); + nd_exe.linkLibrary(libnif); const nd_build_step = b.step("nd", "build nd (nakamochi daemon)"); - nd_build_step.dependOn(&b.addInstallArtifact(nd, .{}).step); + nd_build_step.dependOn(&b.addInstallArtifact(nd_exe, .{}).step); // automated tests { - const tests = b.addTest(.{ + const mod_tests = b.createModule(.{ .root_source_file = b.path("src/test.zig"), .target = target, .optimize = optimize, .link_libc = true, - .filter = b.option([]const u8, "test-filter", "run tests matching the filter"), }); + const tests = b.addTest(.{ + .root_module = mod_tests, + .filters = &.{b.option([]const u8, "test-filter", "run tests matching the filter") orelse ""}, + }); + tests.root_module.addImport("build_options", buildopts_mod); tests.root_module.addImport("nif", libnif_dep.module("nif")); tests.root_module.addImport("ini", libini_dep.module("ini")); @@ -144,47 +155,53 @@ pub fn build(b: *std.Build) void { // GUI playground { - const guiplay = b.addExecutable(.{ - .name = "guiplay", + const guiplay = b.createModule(.{ .root_source_file = b.path("src/test/guiplay.zig"), .target = target, .optimize = optimize, }); - guiplay.root_module.addImport("comm", b.createModule(.{ .root_source_file = b.path("src/comm.zig") })); + const guiplay_exe = b.addExecutable(.{ + .name = "guiplay", + .root_module = guiplay, + }); + guiplay_exe.root_module.addImport("comm", b.createModule(.{ .root_source_file = b.path("src/comm.zig") })); const guiplay_build_step = b.step("guiplay", "build GUI playground"); - guiplay_build_step.dependOn(&b.addInstallArtifact(guiplay, .{}).step); + guiplay_build_step.dependOn(&b.addInstallArtifact(guiplay_exe, .{}).step); guiplay_build_step.dependOn(ngui_build_step); } // bitcoind RPC client playground { - const btcrpc = b.addExecutable(.{ - .name = "btcrpc", + const btcrpc = b.createModule(.{ .root_source_file = b.path("src/test/btcrpc.zig"), .target = target, .optimize = optimize, .strip = strip, }); - btcrpc.root_module.addImport("bitcoindrpc", b.createModule(.{ .root_source_file = b.path("src/bitcoindrpc.zig") })); + const btcrpc_exe = b.addExecutable(.{ + .name = "btcrpc", + .root_module = btcrpc, + }); + btcrpc_exe.root_module.addImport("bitcoindrpc", b.createModule(.{ .root_source_file = b.path("src/bitcoindrpc.zig") })); const btcrpc_build_step = b.step("btcrpc", "bitcoind RPC client playground"); - btcrpc_build_step.dependOn(&b.addInstallArtifact(btcrpc, .{}).step); + btcrpc_build_step.dependOn(&b.addInstallArtifact(btcrpc_exe, .{}).step); } // lnd HTTP API client playground { - const lndhc = b.addExecutable(.{ - .name = "lndhc", + const lndhc = b.createModule(.{ .root_source_file = b.path("src/test/lndhc.zig"), .target = target, .optimize = optimize, .strip = strip, }); - lndhc.root_module.addImport("lightning", b.createModule(.{ .root_source_file = b.path("src/lightning.zig") })); + const lndhc_exe = b.addExecutable(.{ .name = "lndhc", .root_module = lndhc }); + lndhc_exe.root_module.addImport("lightning", b.createModule(.{ .root_source_file = b.path("src/lightning.zig") })); const lndhc_build_step = b.step("lndhc", "lnd HTTP API client playground"); - lndhc_build_step.dependOn(&b.addInstallArtifact(lndhc, .{}).step); + lndhc_build_step.dependOn(&b.addInstallArtifact(lndhc_exe, .{}).step); } // default build step @@ -376,7 +393,7 @@ const LVGLLogLevel = enum { none, /// returns default mode based on the compiler optimization flags. - fn default(mode: std.builtin.Mode) @This() { + fn default(mode: std.builtin.OptimizeMode) @This() { return switch (mode) { .Debug => .warn, .ReleaseSafe => .warn, @@ -427,7 +444,7 @@ const VersionStep = struct { return &vstep.step; } - fn make(step: *std.Build.Step, _: *std.Progress.Node) anyerror!void { + fn make(step: *std.Build.Step, _: std.Build.Step.MakeOptions) anyerror!void { const self: *@This() = @fieldParentPtr("step", step); const semver = try self.eval(); std.log.info("build version: {any}", .{semver}); diff --git a/build.zig.zon b/build.zig.zon index 326febc..13a2b0c 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,5 +1,6 @@ .{ - .name = "ndg", + .name = .ndg, + .fingerprint = 0x6f4f65601db92599, .version = "0.8.1", .dependencies = .{ .nif = .{ diff --git a/lib/ini b/lib/ini index 19e1210..da0af3a 160000 --- a/lib/ini +++ b/lib/ini @@ -1 +1 @@ -Subproject commit 19e1210063882ab7db73a8aaa60e733d4aaafe9f +Subproject commit da0af3a32e3403e3113e103767065cbe9584f505 diff --git a/lib/nif/build.zig b/lib/nif/build.zig index 307afdb..4d7d8c7 100644 --- a/lib/nif/build.zig +++ b/lib/nif/build.zig @@ -5,15 +5,19 @@ pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); - const lib = b.addStaticLibrary(.{ - .name = "nif", + const lib_mod = b.createModule(.{ .root_source_file = b.path("nif.zig"), .target = target, .optimize = optimize, .link_libc = true, }); - lib.defineCMacro("CONFIG_CTRL_IFACE", null); - lib.defineCMacro("CONFIG_CTRL_IFACE_UNIX", null); + const lib = b.addLibrary(.{ + .linkage = .static, + .name = "nif", + .root_module = lib_mod, + }); + lib.root_module.addCMacro("CONFIG_CTRL_IFACE", ""); + lib.root_module.addCMacro("CONFIG_CTRL_IFACE_UNIX", ""); lib.addIncludePath(b.path("wpa_supplicant")); lib.addCSourceFiles(.{ .files = &.{ diff --git a/src/lightning/LndConf.zig b/src/lightning/LndConf.zig index 3d62d9b..745de51 100644 --- a/src/lightning/LndConf.zig +++ b/src/lightning/LndConf.zig @@ -229,7 +229,7 @@ test "lnd: conf load dump" { var tmp = try tt.TempDir.create(); defer tmp.cleanup(); - try tmp.dir.writeFile("conf.ini", + try tmp.dir.writeFile(.{ .sub_path = "conf.ini", .data = \\; top comment \\[application options] \\foo = bar @@ -239,7 +239,7 @@ test "lnd: conf load dump" { \\ \\[AutopiloT] \\autopilot.active=false - ); + }); const clean_conf = \\[application options] \\foo=bar diff --git a/src/nd.zig b/src/nd.zig index abf4d56..e3ad38e 100644 --- a/src/nd.zig +++ b/src/nd.zig @@ -170,7 +170,7 @@ pub fn main() !void { if (conf.data.slock != null) { try ngui_args.append("-slock"); } - var ngui = std.ChildProcess.init(ngui_args.items, gpa); + var ngui = std.process.Child.init(ngui_args.items, gpa); ngui.stdin_behavior = .Pipe; ngui.stdout_behavior = .Pipe; ngui.stderr_behavior = .Inherit; @@ -224,8 +224,8 @@ pub fn main() !void { .mask = posix.empty_sigset, .flags = 0, }; - try posix.sigaction(posix.SIG.INT, &sa, null); - try posix.sigaction(posix.SIG.TERM, &sa, null); + posix.sigaction(posix.SIG.INT, &sa, null); + posix.sigaction(posix.SIG.TERM, &sa, null); sigquit.wait(); logger.info("sigquit: terminating ...", .{}); diff --git a/src/nd/Config.zig b/src/nd/Config.zig index cb9cbaf..cfcfd69 100644 --- a/src/nd/Config.zig +++ b/src/nd/Config.zig @@ -200,7 +200,7 @@ fn inferBitcoindRpcPass(allocator: std.mem.Allocator) ![]const u8 { /// calls F while holding a readonly lock and passes on F's result as is. /// F is expected to take `Data` and `StaticData` args. -pub fn safeReadOnly(self: *Config, comptime F: anytype) @typeInfo(@TypeOf(F)).Fn.return_type.? { +pub fn safeReadOnly(self: *Config, comptime F: anytype) @typeInfo(@TypeOf(F)).@"fn".return_type.? { self.mu.lockShared(); defer self.mu.unlockShared(); return F(self.data, self.static); @@ -214,7 +214,7 @@ pub fn verifySlockPin(self: *Config, input: []const u8) !void { defer self.mu.unlock(); const slock = self.data.slock orelse return; defer self.dumpUnguarded() catch |errdump| logger.err("dumpUnguarded: {!}", .{errdump}); - std.crypto.pwhash.bcrypt.strVerify(slock.bcrypt_hash, input, .{}) catch |err| { + std.crypto.pwhash.bcrypt.strVerify(slock.bcrypt_hash, input, .{ .silently_truncate_password = false }) catch |err| { if (err == error.PasswordVerificationFailed) { self.data.slock.?.incorrect_attempts += 1; return error.IncorrectSlockPin; @@ -234,9 +234,8 @@ pub fn setSlockPin(self: *Config, code: ?[]const u8) !void { if (code) |s| { const bcrypt = std.crypto.pwhash.bcrypt; const opt: bcrypt.HashOptions = .{ - .params = .{ .rounds_log = 12 }, + .params = .{ .rounds_log = 12, .silently_truncate_password = false }, .encoding = .phc, - .silently_truncate_password = false, }; var buf: [bcrypt.hash_length * 2]u8 = undefined; const hash = try bcrypt.strHash(s, opt, &buf); @@ -307,7 +306,7 @@ pub fn dump(self: *Config) !void { fn dumpUnguarded(self: Config) !void { const allocator = self.arena.child_allocator; - const opt = .{ .mode = 0o600 }; + const opt: std.fs.Dir.AtomicFileOptions = .{ .mode = 0o600 }; const file = try std.io.BufferedAtomicFile.create(allocator, std.fs.cwd(), self.confpath, opt); defer file.destroy(); try std.json.stringify(self.data, .{ .whitespace = .indent_2 }, file.writer()); @@ -351,7 +350,7 @@ fn genSysupdatesCronScript(self: Config) !void { return error.NoSysRunScriptPath; } const allocator = self.arena.child_allocator; - const opt = .{ .mode = 0o755 }; + const opt: std.fs.Dir.AtomicFileOptions = .{ .mode = 0o755 }; const file = try std.io.BufferedAtomicFile.create(allocator, std.fs.cwd(), self.data.syscronscript, opt); defer file.destroy(); @@ -371,7 +370,7 @@ fn genSysupdatesCronScript(self: Config) !void { /// /// the caller must serialize this function calls. fn runSysupdates(allocator: std.mem.Allocator, scriptpath: []const u8) !void { - const res = try std.ChildProcess.run(.{ .allocator = allocator, .argv = &.{scriptpath} }); + const res = try std.process.Child.run(.{ .allocator = allocator, .argv = &.{scriptpath} }); defer { allocator.free(res.stdout); allocator.free(res.stderr); @@ -431,7 +430,7 @@ pub fn makeWalletUnlockFile(self: Config, outbuf: []u8, comptime raw_size: usize const filepath = LND_WALLETUNLOCK_PATH; const allocator = self.arena.child_allocator; - const opt = .{ .mode = 0o400 }; + const opt: std.fs.Dir.AtomicFileOptions = .{ .mode = 0o400 }; const file = try std.io.BufferedAtomicFile.create(allocator, std.fs.cwd(), filepath, opt); defer file.destroy(); // frees resources; does NOT delete the file @@ -532,14 +531,14 @@ test "ndconfig: init existing" { var tmp = try tt.TempDir.create(); defer tmp.cleanup(); - try tmp.dir.writeFile("conf.json", + try tmp.dir.writeFile(.{ .sub_path = "conf.json", .data = \\{ \\"sysurl": "https://github.com/nakamochi/sysupdates.git", \\"syschannel": "dev", \\"syscronscript": "/cron/sysupdates.sh", \\"sysrunscript": "/sysupdates/run.sh" \\} - ); + }); const conf = try init(t.allocator, try tmp.join(&.{"conf.json"})); defer conf.deinit(); try t.expectEqualStrings("https://github.com/nakamochi/sysupdates.git", conf.data.sysurl); @@ -602,7 +601,7 @@ test "ndconfig: switch sysupdates and infer" { var tmp = try tt.TempDir.create(); defer tmp.cleanup(); - try tmp.dir.writeFile("conf.json", ""); + try tmp.dir.writeFile(.{ .sub_path = "conf.json", .data = "" }); const confpath = try tmp.join(&.{"conf.json"}); const cronscript = try tmp.join(&.{"cronscript.sh"}); var conf = Config{ @@ -635,10 +634,10 @@ test "ndconfig: switch sysupdates with .run=true" { defer tmp.cleanup(); const runscript = "runscript.sh"; - try tmp.dir.writeFile(runscript, + try tmp.dir.writeFile(.{ .sub_path = runscript, .data = \\#!/bin/sh \\printf "$1" > "$(dirname "$0")/success" - ); + }); { const file = try tmp.dir.openFile(runscript, .{}); defer file.close(); @@ -666,7 +665,7 @@ fn testLoadConfigData(path: []const u8) !std.json.Parsed(Data) { const allocator = std.testing.allocator; const bytes = try std.fs.cwd().readFileAlloc(allocator, path, 1 << 20); defer allocator.free(bytes); - const jopt = .{ .ignore_unknown_fields = true, .allocate = .alloc_always }; + const jopt: std.json.ParseOptions = .{ .ignore_unknown_fields = true, .allocate = .alloc_always }; return try std.json.parseFromSlice(Data, allocator, bytes, jopt); } @@ -745,11 +744,11 @@ test "ndconfig: mutate LndConf" { }; defer conf.deinit(); const lndconf_path = try tmp.join(&.{"lndconf.ini"}); - try tmp.dir.writeFile(lndconf_path, + try tmp.dir.writeFile(.{ .sub_path = lndconf_path, .data = \\[application options] \\alias=noname \\ - ); + }); var mut = try conf.beginMutateLndConf(.{ .filepath = lndconf_path }); try mut.lndconf.setAlias("newalias"); try mut.persist(); @@ -786,14 +785,14 @@ test "ndconfig: screen lock" { // conf file without slock field { const confpath = try tmp.join(&.{"conf.json"}); - try tmp.dir.writeFile(confpath, + try tmp.dir.writeFile(.{ .sub_path = confpath, .data = \\{ \\"sysurl": "https://github.com/nakamochi/sysupdates.git", \\"syschannel": "dev", \\"syscronscript": "/cron/sysupdates.sh", \\"sysrunscript": "/sysupdates/run.sh" \\} - ); + }); var conf = try init(t.allocator, confpath); defer conf.deinit(); try t.expect(conf.data.slock == null); @@ -804,7 +803,7 @@ test "ndconfig: screen lock" { // conf file with null slock { const confpath = try tmp.join(&.{"conf.json"}); - try tmp.dir.writeFile(confpath, + try tmp.dir.writeFile(.{ .sub_path = confpath, .data = \\{ \\"slock": null, \\"sysurl": "https://github.com/nakamochi/sysupdates.git", @@ -812,7 +811,7 @@ test "ndconfig: screen lock" { \\"syscronscript": "/cron/sysupdates.sh", \\"sysrunscript": "/sysupdates/run.sh" \\} - ); + }); var conf = try init(t.allocator, confpath); defer conf.deinit(); try t.expect(conf.data.slock == null); diff --git a/src/nd/Daemon.zig b/src/nd/Daemon.zig index 97f5114..48726ba 100644 --- a/src/nd/Daemon.zig +++ b/src/nd/Daemon.zig @@ -143,6 +143,13 @@ pub fn init(opt: InitOpt) !Daemon { logger.debug("conf = {any}", .{opt.conf}); + // Transfer ownership to a slice; ensure cleanup if subsequent initialisation fails. + const services_list = try svlist.toOwnedSlice(); + errdefer { + for (services_list) |*sv| sv.deinit(); + opt.allocator.free(services_list); + } + return .{ .allocator = opt.allocator, .conf = opt.conf, @@ -151,7 +158,7 @@ pub fn init(opt: InitOpt) !Daemon { .wpa_ctrl = try types.WpaControl.open(opt.wpa), .state = .stopped, .screenstate = if (opt.conf.data.slock != null) .locked else .unlocked, - .services = .{ .list = try svlist.toOwnedSlice() }, + .services = .{ .list = services_list }, // send persisted settings immediately on start .want_settings = true, // send a network report right at start without wifi scan to make it faster. @@ -697,9 +704,9 @@ fn readWPACtrlMsg(self: *Daemon) !void { if (mem.indexOf(u8, m, "CTRL-EVENT-SSID-TEMP-DISABLED") != null) { // TODO: what about CTRL-EVENT-DISCONNECTED bssid=xx:xx:xx:xx:xx:xx reason=15 // CTRL-EVENT-SSID-TEMP-DISABLED id=1 ssid="" auth_failures=3 duration=49 reason=WRONG_KEY - var it = mem.tokenize(u8, m, " "); + var it = mem.tokenizeScalar(u8, m, ' '); while (it.next()) |kv_str| { - var kv = mem.split(u8, kv_str, "="); + var kv = mem.splitScalar(u8, kv_str, '='); if (mem.eql(u8, kv.first(), "auth_failures")) { const v = kv.next(); if (v != null and !mem.eql(u8, v.?, "0")) { @@ -862,7 +869,7 @@ fn sendLightningReport(self: *Daemon) !void { try feemap.put(item.chan_id, .{ .base = item.base_fee_msat, .ppm = item.fee_per_mil }); } - var channels = std.ArrayList(@typeInfo(@TypeOf(lndrep.channels)).Pointer.child).init(self.allocator); + var channels = std.ArrayList(@typeInfo(@TypeOf(lndrep.channels)).pointer.child).init(self.allocator); defer channels.deinit(); for (pending.value.pending_open_channels) |item| { try channels.append(.{ diff --git a/src/nd/network.zig b/src/nd/network.zig index d669afc..e4f9f55 100644 --- a/src/nd/network.zig +++ b/src/nd/network.zig @@ -104,7 +104,7 @@ fn queryWifiSSID(gpa: mem.Allocator, wpa_ctrl: *types.WpaControl) !?[]const u8 { var buf: [512:0]u8 = undefined; const resp = try wpa_ctrl.request("STATUS", &buf, null); const ssid = "ssid="; - var it = mem.tokenize(u8, resp, "\n"); + var it = mem.tokenizeScalar(u8, resp, '\n'); while (it.next()) |line| { if (mem.startsWith(u8, line, ssid)) { // TODO: check line.len vs ssid.len @@ -124,7 +124,7 @@ fn queryWifiScanResults(gpa: mem.Allocator, wpa_ctrl: *types.WpaControl) !types. var buf: [8192:0]u8 = undefined; // TODO: what if isn't enough? // first line is banner: "bssid / frequency / signal level / flags / ssid" const resp = try wpa_ctrl.request("SCAN_RESULTS", &buf, null); - var it = mem.tokenize(u8, resp, "\n"); + var it = mem.tokenizeScalar(u8, resp, '\n'); if (it.next() == null) { return error.MissingWifiScanHeader; } @@ -157,14 +157,14 @@ fn queryWifiNetworksList(gpa: mem.Allocator, wpa_ctrl: *types.WpaControl, filter var buf: [8192:0]u8 = undefined; // TODO: is this enough? // first line is banner: "network id / ssid / bssid / flags" const resp = try wpa_ctrl.request("LIST_NETWORKS", &buf, null); - var it = mem.tokenize(u8, resp, "\n"); + var it = mem.tokenizeScalar(u8, resp, '\n'); if (it.next() == null) { return error.MissingWifiNetworksListHeader; } var list = std.ArrayList(u32).init(gpa); while (it.next()) |line| { - var cols = mem.tokenize(u8, line, "\t"); + var cols = mem.tokenizeScalar(u8, line, '\t'); const id_str = cols.next() orelse continue; // bad line format? const ssid = cols.next() orelse continue; // bad line format? const id = std.fmt.parseUnsigned(u32, id_str, 10) catch continue; // skip bad line diff --git a/src/ngui.zig b/src/ngui.zig index 2f72f3b..0a4f818 100644 --- a/src/ngui.zig +++ b/src/ngui.zig @@ -445,8 +445,8 @@ pub fn main() anyerror!void { .mask = posix.empty_sigset, .flags = 0, }; - try posix.sigaction(posix.SIG.INT, &sa, null); - try posix.sigaction(posix.SIG.TERM, &sa, null); + posix.sigaction(posix.SIG.INT, &sa, null); + posix.sigaction(posix.SIG.TERM, &sa, null); sigquit.wait(); logger.info("sigquit: terminating ...", .{}); diff --git a/src/sys/Service.zig b/src/sys/Service.zig index 5ccc153..1e2848d 100644 --- a/src/sys/Service.zig +++ b/src/sys/Service.zig @@ -37,9 +37,9 @@ pub const Status = enum(u8) { const State = union(Status) { initial: void, - started: std.ChildProcess.Term, + started: std.process.Child.Term, stopping: void, - stopped: std.ChildProcess.Term, + stopped: std.process.Child.Term, }; const SysService = @This(); diff --git a/src/sys/sysimpl.zig b/src/sys/sysimpl.zig index be38d25..365dec8 100644 --- a/src/sys/sysimpl.zig +++ b/src/sys/sysimpl.zig @@ -47,7 +47,7 @@ pub fn setHostname(allocator: std.mem.Allocator, name: []const u8) !void { } // make persistent change first - const opt = .{ .mode = 0o644 }; + const opt: std.fs.Dir.AtomicFileOptions = .{ .mode = 0o644 }; const file = try std.io.BufferedAtomicFile.create(allocator, std.fs.cwd(), hostname_filepath, opt); defer file.destroy(); // releases resources; does NOT deletes the file try file.writer().writeAll(newname); @@ -72,7 +72,7 @@ test "setHostname" { var tmp = try tt.TempDir.create(); defer tmp.cleanup(); hostname_filepath = try tmp.join(&.{"hostname"}); - try tmp.dir.writeFile(hostname_filepath, "dummy"); + try tmp.dir.writeFile(.{ .sub_path = hostname_filepath, .data = "dummy" }); try setHostname(arena, "123_-newhostname$%/3-4hello5\xef\x83\xa7end"); diff --git a/src/test.zig b/src/test.zig index 4cc583b..b81fb9e 100644 --- a/src/test.zig +++ b/src/test.zig @@ -106,9 +106,9 @@ pub const TestTimer = struct { /// the caller must deinit in the end. pub const TestChildProcess = struct { // test hooks - spawn_callback: ?*const fn (*TestChildProcess) std.ChildProcess.SpawnError!void = null, - wait_callback: ?*const fn (*TestChildProcess) anyerror!std.ChildProcess.Term = null, - kill_callback: ?*const fn (*TestChildProcess) anyerror!std.ChildProcess.Term = null, + spawn_callback: ?*const fn (*TestChildProcess) std.process.Child.SpawnError!void = null, + wait_callback: ?*const fn (*TestChildProcess) anyerror!std.process.Child.Term = null, + kill_callback: ?*const fn (*TestChildProcess) anyerror!std.process.Child.Term = null, spawned: bool = false, waited: bool = false, killed: bool = false, @@ -133,14 +133,14 @@ pub const TestChildProcess = struct { self.allocator.free(self.argv); } - pub fn spawn(self: *TestChildProcess) std.ChildProcess.SpawnError!void { + pub fn spawn(self: *TestChildProcess) std.process.Child.SpawnError!void { defer self.spawned = true; if (self.spawn_callback) |cb| { return cb(self); } } - pub fn wait(self: *TestChildProcess) anyerror!std.ChildProcess.Term { + pub fn wait(self: *TestChildProcess) anyerror!std.process.Child.Term { defer self.waited = true; if (self.wait_callback) |cb| { return cb(self); @@ -148,12 +148,12 @@ pub const TestChildProcess = struct { return .{ .Exited = 0 }; } - pub fn spawnAndWait(self: *TestChildProcess) !std.ChildProcess.Term { + pub fn spawnAndWait(self: *TestChildProcess) !std.process.Child.Term { try self.spawn(); return self.wait(); } - pub fn kill(self: *TestChildProcess) !std.ChildProcess.Term { + pub fn kill(self: *TestChildProcess) !std.process.Child.Term { defer self.killed = true; if (self.kill_callback) |cb| { return cb(self); @@ -236,12 +236,12 @@ pub const TestWpaControl = struct { /// unhandled types are passed to std.testing.expectEqualDeep. pub fn expectDeepEqual(expected: anytype, actual: @TypeOf(expected)) !void { switch (@typeInfo(@TypeOf(actual))) { - .Pointer => |p| { + .pointer => |p| { switch (p.size) { - .One => try expectDeepEqual(expected.*, actual.*), - .Slice => { + .one => try expectDeepEqual(expected.*, actual.*), + .slice => { switch (@typeInfo(p.child)) { - .Pointer, .Struct, .Optional, .Union => { + .pointer, .@"struct", .optional, .@"union" => { const err: ?anyerror = blk: { if (expected.len != actual.len) { std.debug.print("expected.len = {d}, actual.len = {d}\n", .{ expected.len, actual.len }); @@ -273,7 +273,7 @@ pub fn expectDeepEqual(expected: anytype, actual: @TypeOf(expected)) !void { else => try std.testing.expectEqualDeep(expected, actual), } }, - .Struct => |st| { + .@"struct" => |st| { inline for (st.fields) |f| { expectDeepEqual(@field(expected, f.name), @field(actual, f.name)) catch |err| { std.debug.print("unequal field '{s}' of struct {any}\n", .{ f.name, @TypeOf(actual) }); @@ -281,7 +281,7 @@ pub fn expectDeepEqual(expected: anytype, actual: @TypeOf(expected)) !void { }; } }, - .Optional => { + .optional => { if (expected) |x| { if (actual) |v| { try expectDeepEqual(x, v); @@ -296,7 +296,7 @@ pub fn expectDeepEqual(expected: anytype, actual: @TypeOf(expected)) !void { } } }, - .Union => |u| { + .@"union" => |u| { if (u.tag_type == null) { @compileError("unable to compare untagged union values"); } diff --git a/src/test/guiplay.zig b/src/test/guiplay.zig index 4a3a388..e84704a 100644 --- a/src/test/guiplay.zig +++ b/src/test/guiplay.zig @@ -8,7 +8,7 @@ const types = @import("../types.zig"); const logger = std.log.scoped(.play); const stderr = std.io.getStdErr().writer(); -var ngui_proc: std.ChildProcess = undefined; +var ngui_proc: std.process.Child = undefined; var sigquit: std.Thread.ResetEvent = .{}; fn sighandler(sig: c_int) callconv(.C) void { @@ -358,7 +358,7 @@ pub fn main() !void { if (flags.slock) { try a.append("-slock"); } - ngui_proc = std.ChildProcess.init(a.items, gpa); + ngui_proc = std.process.Child.init(a.items, gpa); ngui_proc.stdin_behavior = .Pipe; ngui_proc.stdout_behavior = .Pipe; ngui_proc.stderr_behavior = .Inherit; @@ -379,8 +379,8 @@ pub fn main() !void { .mask = posix.empty_sigset, .flags = 0, }; - try posix.sigaction(posix.SIG.INT, &sa, null); - try posix.sigaction(posix.SIG.TERM, &sa, null); + posix.sigaction(posix.SIG.INT, &sa, null); + posix.sigaction(posix.SIG.TERM, &sa, null); sigquit.wait(); logger.info("killing ngui", .{}); diff --git a/src/types.zig b/src/types.zig index d83676f..6213679 100644 --- a/src/types.zig +++ b/src/types.zig @@ -22,7 +22,7 @@ pub usingnamespace if (builtin.is_test) struct { } else struct { // regular types for production code. pub const Timer = std.time.Timer; - pub const ChildProcess = std.ChildProcess; + pub const ChildProcess = std.process.Child; pub const WpaControl = nif.wpa.Control; pub fn getUserInfo(name: []const u8) !std.process.UserInfo { diff --git a/src/ui/lightning.zig b/src/ui/lightning.zig index 790f097..7328d25 100644 --- a/src/ui/lightning.zig +++ b/src/ui/lightning.zig @@ -14,7 +14,7 @@ const logger = std.log.scoped(.ui_lnd); const appBitBanana = "BitBanana"; const appZeus = "Zeus"; -const app_description = std.ComptimeStringMap([:0]const u8, .{ +const app_description = std.StaticStringMap([:0]const u8).initComptime(.{ .{ appBitBanana, \\lightning node management for android.