From 8799dcbdf9d14803562f50bf4d4cc335ec2ea799 Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Thu, 9 Jul 2026 11:41:54 +0200 Subject: [PATCH 01/12] Update to Zig 0.17.0 Doesn't compile :( Signed-off-by: AnErrupTion --- build.zig | 4 ++-- build.zig.zon | 8 ++++---- ly-core/build.zig.zon | 8 ++++---- ly-ui/build.zig.zon | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/build.zig b/build.zig index cdb0d1f..3faa646 100644 --- a/build.zig +++ b/build.zig @@ -130,7 +130,7 @@ pub fn build(b: *std.Build) !void { const run_cmd = b.addRunArtifact(exe); run_cmd.step.dependOn(b.getInstallStep()); - if (b.args) |args| run_cmd.addArgs(args); + run_cmd.addPassthruArgs(); const run_step = b.step("run", "Run the app"); run_step.dependOn(&run_cmd.step); @@ -195,7 +195,7 @@ fn getVersionStr(b: *std.Build, name: []const u8, version: std.SemanticVersion) const git_describe_raw = b.runAllowFail(&[_][]const u8{ "git", "-C", - b.build_root.path orelse ".", + try b.root.toString(b.allocator), "describe", "--match", "*.*.*", diff --git a/build.zig.zon b/build.zig.zon index 1d19b69..211ed75 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -8,12 +8,12 @@ .path = "ly-ui", }, .clap = .{ - .url = "git+https://github.com/Hejsil/zig-clap#fc1e5cc3f6d9d3001112385ee6256d694e959d2f", - .hash = "clap-0.11.0-oBajB7foAQC3Iyn4IVCkUdYaOVVng5IZkSncySTjNig1", + .url = "git+https://github.com/Hejsil/zig-clap?ref=master#e91d66b1abba2024cd2e816426f14d233d3dad9a", + .hash = "clap-0.12.0-oBajB2LpAQD1BQpAukHcuwhIUoHWYNy2DzU6lDW2v2N8", }, .zlua = .{ - .url = "git+https://github.com/natecraddock/ziglua?ref=zig-0.16#8f271c82baa5fc43aa02a72f6da020c2025d9436", - .hash = "zlua-0.1.0-hGRpC2aABQD4D9PBVH3wAW8k32-I4969MRQ0CpOwoley", + .url = "git+https://github.com/AnErrupTion/ziglua?ref=zig-0.17#134fd6110e860672c32703d9c76588a1d5f6a3fa", + .hash = "zlua-0.1.0-hGRpC1mUBQDugHDyWzXaCjnAwr9wKKArY06QpVOFTFUM", }, }, .paths = .{ diff --git a/ly-core/build.zig.zon b/ly-core/build.zig.zon index 0d84326..d12c8ca 100644 --- a/ly-core/build.zig.zon +++ b/ly-core/build.zig.zon @@ -5,12 +5,12 @@ .minimum_zig_version = "0.16.0", .dependencies = .{ .zigini = .{ - .url = "git+https://github.com/AshAmetrine/zigini?ref=master#a665d081dda42664a96da2840ea09c5ccf9d0692", - .hash = "zigini-0.5.0-BSkB7e9WAACfyCBABNZiWL3gFMw18GKn3qBcPs8L1Ec1", + .url = "git+https://github.com/AnErrupTion/zigini?ref=zig-0.17#950eab7d96bb364379acca763695700670d75382", + .hash = "zigini-0.5.0-BSkB7eVXAADQTfcUPT8TUu79sONGQk1Xbaz4_Tu0VvQx", }, .translate_c = .{ - .url = "git+https://codeberg.org/ziglang/translate-c?ref=zig-0.16.x#6fe0ffc4549f15c5f2d9432c2b4460ba90ff85ac", - .hash = "translate_c-1.0.0-Q_BUWo_5BgD4flHdUhA31zOz0XvZk9k7lQv1ouzyNXj2", + .url = "git+https://codeberg.org/ziglang/translate-c?ref=master#ba4288f6c0e8054d7894266949d4fa08bfc0212a", + .hash = "translate_c-0.0.0-Q_BUWvI3BwAFZWG2aNKspzHT7uv3yKuNMC5sP4C0hoYl", }, }, .paths = .{ diff --git a/ly-ui/build.zig.zon b/ly-ui/build.zig.zon index 68131d1..e082936 100644 --- a/ly-ui/build.zig.zon +++ b/ly-ui/build.zig.zon @@ -12,8 +12,8 @@ .hash = "N-V-__8AAAUXBQD6Fwpi9m0MBqWXFFaqW5l1lVrJC2Ynj7a-", }, .translate_c = .{ - .url = "git+https://codeberg.org/ziglang/translate-c?ref=zig-0.16.x#6fe0ffc4549f15c5f2d9432c2b4460ba90ff85ac", - .hash = "translate_c-1.0.0-Q_BUWo_5BgD4flHdUhA31zOz0XvZk9k7lQv1ouzyNXj2", + .url = "git+https://codeberg.org/ziglang/translate-c?ref=master#ba4288f6c0e8054d7894266949d4fa08bfc0212a", + .hash = "translate_c-0.0.0-Q_BUWvI3BwAFZWG2aNKspzHT7uv3yKuNMC5sP4C0hoYl", }, }, .paths = .{ From f89e79c7f8e4df0b3db9d898faa132d877f3c518 Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Sun, 12 Jul 2026 12:06:43 +0200 Subject: [PATCH 02/12] Fix compilation Signed-off-by: AnErrupTion --- build.zig.zon | 6 +++--- ly-core/build.zig.zon | 10 +++++----- ly-core/src/LogFile.zig | 4 ++-- ly-core/src/SharedError.zig | 4 ++-- ly-core/src/interop.zig | 6 +++--- ly-ui/build.zig.zon | 6 +++--- src/auth.zig | 30 +++++++++++++++--------------- src/main.zig | 9 +++++---- 8 files changed, 38 insertions(+), 37 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 211ed75..63d15f1 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -2,7 +2,7 @@ .name = .ly, .version = "1.5.0", .fingerprint = 0xa148ffcc5dc2cb59, - .minimum_zig_version = "0.16.0", + .minimum_zig_version = "0.17.0", .dependencies = .{ .ly_ui = .{ .path = "ly-ui", @@ -12,8 +12,8 @@ .hash = "clap-0.12.0-oBajB2LpAQD1BQpAukHcuwhIUoHWYNy2DzU6lDW2v2N8", }, .zlua = .{ - .url = "git+https://github.com/AnErrupTion/ziglua?ref=zig-0.17#134fd6110e860672c32703d9c76588a1d5f6a3fa", - .hash = "zlua-0.1.0-hGRpC1mUBQDugHDyWzXaCjnAwr9wKKArY06QpVOFTFUM", + .url = "git+https://github.com/AnErrupTion/ziglua?ref=zig-0.17#4cca15ac75967115830708ca28d1d3dcde729f04", + .hash = "zlua-0.1.0-hGRpC-aUBQCrVgFkeX-mNintLyM1i9_4FJU4mGSw0DgC", }, }, .paths = .{ diff --git a/ly-core/build.zig.zon b/ly-core/build.zig.zon index d12c8ca..897fe14 100644 --- a/ly-core/build.zig.zon +++ b/ly-core/build.zig.zon @@ -2,15 +2,15 @@ .name = .ly_core, .version = "1.1.0", .fingerprint = 0xddda7afda795472, - .minimum_zig_version = "0.16.0", + .minimum_zig_version = "0.17.0", .dependencies = .{ .zigini = .{ - .url = "git+https://github.com/AnErrupTion/zigini?ref=zig-0.17#950eab7d96bb364379acca763695700670d75382", - .hash = "zigini-0.5.0-BSkB7eVXAADQTfcUPT8TUu79sONGQk1Xbaz4_Tu0VvQx", + .url = "git+https://github.com/AnErrupTion/zigini?ref=zig-0.17.0#29b30ec37efccb022a57e7acda83d63fa5832dd8", + .hash = "zigini-0.6.0-BSkB7UtYAAB6b6HEm3Pjj5WIy-uiuBYsNTNCUENVwL1t", }, .translate_c = .{ - .url = "git+https://codeberg.org/ziglang/translate-c?ref=master#ba4288f6c0e8054d7894266949d4fa08bfc0212a", - .hash = "translate_c-0.0.0-Q_BUWvI3BwAFZWG2aNKspzHT7uv3yKuNMC5sP4C0hoYl", + .url = "git+https://codeberg.org/ziglang/translate-c?ref=master#2d71d6e68dd9e9ee1da2a248a1dcb5aeba683dec", + .hash = "translate_c-0.0.0-Q_BUWtI4BwDTFS7O6WTn-3qixa_s81nw_C0FHbf2aLqj", }, }, .paths = .{ diff --git a/ly-core/src/LogFile.zig b/ly-core/src/LogFile.zig index 192f338..885aa22 100644 --- a/ly-core/src/LogFile.zig +++ b/ly-core/src/LogFile.zig @@ -54,7 +54,7 @@ pub fn info(self: *LogFile, io: std.Io, category: []const u8, comptime message: } else { var buffer: [1024]u8 = undefined; const slice = try std.fmt.bufPrint(&buffer, message, args); - const msg = try std.fmt.bufPrintZ(buffer[slice.len..], "[info/{s}] {s}", .{ category, slice }); + const msg = try std.fmt.bufPrintSentinel(buffer[slice.len..], "[info/{s}] {s}", .{ category, slice }, 0); std.posix.system.syslog(std.posix.LOG.INFO, msg.ptr); } @@ -72,7 +72,7 @@ pub fn err(self: *LogFile, io: std.Io, category: []const u8, comptime message: [ } else { var buffer: [1024]u8 = undefined; const slice = try std.fmt.bufPrint(&buffer, message, args); - const msg = try std.fmt.bufPrintZ(buffer[slice.len..], "[info/{s}] {s}", .{ category, slice }); + const msg = try std.fmt.bufPrintSentinel(buffer[slice.len..], "[info/{s}] {s}", .{ category, slice }, 0); std.posix.system.syslog(std.posix.LOG.ERR, msg.ptr); } diff --git a/ly-core/src/SharedError.zig b/ly-core/src/SharedError.zig index c3ce088..75ad8d8 100644 --- a/ly-core/src/SharedError.zig +++ b/ly-core/src/SharedError.zig @@ -1,7 +1,7 @@ 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 ErrInt = @Int(.unsigned, @bitSizeOf(anyerror)); +const PaddingInt = @Int(.unsigned, 8 - (@bitSizeOf(ErrInt) + @bitSizeOf(bool)) % 8); const ErrorHandler = packed struct { has_error: bool = false, diff --git a/ly-core/src/interop.zig b/ly-core/src/interop.zig index d894485..f4d6868 100644 --- a/ly-core/src/interop.zig +++ b/ly-core/src/interop.zig @@ -355,7 +355,7 @@ pub fn setNumlock(val: bool) !void { } pub fn setUserContext(allocator: std.mem.Allocator, entry: UsernameEntry) !void { - const username_z = try allocator.dupeZ(u8, entry.username.?); + const username_z = try allocator.dupeSentinel(u8, entry.username.?, 0); defer allocator.free(username_z); return platform_struct.setUserContextImpl(username_z.ptr, entry); @@ -371,10 +371,10 @@ pub fn setUserShell(entry: *UsernameEntry) void { } pub fn setEnvironmentVariable(allocator: std.mem.Allocator, name: []const u8, value: []const u8, replace: bool) !void { - const name_z = try allocator.dupeZ(u8, name); + const name_z = try allocator.dupeSentinel(u8, name, 0); defer allocator.free(name_z); - const value_z = try allocator.dupeZ(u8, value); + const value_z = try allocator.dupeSentinel(u8, value, 0); defer allocator.free(value_z); const status = stdlib.setenv(name_z.ptr, value_z.ptr, @intFromBool(replace)); diff --git a/ly-ui/build.zig.zon b/ly-ui/build.zig.zon index e082936..d328567 100644 --- a/ly-ui/build.zig.zon +++ b/ly-ui/build.zig.zon @@ -2,7 +2,7 @@ .name = .ly_ui, .version = "1.1.0", .fingerprint = 0x8d11bf85a74ec803, - .minimum_zig_version = "0.16.0", + .minimum_zig_version = "0.17.0", .dependencies = .{ .ly_core = .{ .path = "../ly-core", @@ -12,8 +12,8 @@ .hash = "N-V-__8AAAUXBQD6Fwpi9m0MBqWXFFaqW5l1lVrJC2Ynj7a-", }, .translate_c = .{ - .url = "git+https://codeberg.org/ziglang/translate-c?ref=master#ba4288f6c0e8054d7894266949d4fa08bfc0212a", - .hash = "translate_c-0.0.0-Q_BUWvI3BwAFZWG2aNKspzHT7uv3yKuNMC5sP4C0hoYl", + .url = "git+https://codeberg.org/ziglang/translate-c?ref=master#2d71d6e68dd9e9ee1da2a248a1dcb5aeba683dec", + .hash = "translate_c-0.0.0-Q_BUWtI4BwDTFS7O6WTn-3qixa_s81nw_C0FHbf2aLqj", }, }, .paths = .{ diff --git a/src/auth.zig b/src/auth.zig index c9abf15..6b122bf 100644 --- a/src/auth.zig +++ b/src/auth.zig @@ -41,7 +41,7 @@ pub fn authenticate(allocator: std.mem.Allocator, io: std.Io, log_file: *LogFile const tty_str = try std.fmt.bufPrint(&tty_buffer, "{d}", .{options.tty}); var pam_tty_buffer: [6]u8 = undefined; - const pam_tty_str = try std.fmt.bufPrintZ(&pam_tty_buffer, "tty{d}", .{options.tty}); + const pam_tty_str = try std.fmt.bufPrintSentinel(&pam_tty_buffer, "tty{d}", .{options.tty}, 0); // Set the XDG environment variables try log_file.info(io, "auth/env", "setting xdg environment variables", .{}); @@ -49,10 +49,10 @@ pub fn authenticate(allocator: std.mem.Allocator, io: std.Io, log_file: *LogFile // Open the PAM session try log_file.info(io, "auth/pam", "encoding credentials", .{}); - const login_z = try allocator.dupeZ(u8, login); + const login_z = try allocator.dupeSentinel(u8, login, 0); defer allocator.free(login_z); - const password_z = try allocator.dupeZ(u8, password); + const password_z = try allocator.dupeSentinel(u8, password, 0); defer allocator.free(password_z); var credentials = [_:null]?[*:0]const u8{ login_z, password_z }; @@ -199,7 +199,7 @@ fn startSession( } } - const home_z = try allocator.dupeZ(u8, user_entry.home.?); + const home_z = try allocator.dupeSentinel(u8, user_entry.home.?, 0); defer allocator.free(home_z); // Change to the user's home directory @@ -294,7 +294,7 @@ fn loginConv( switch (messages[i].?.msg_style) { interop.pam.PAM_PROMPT_ECHO_ON => { const data: [*][*:0]u8 = @ptrCast(@alignCast(appdata_ptr)); - username = allocator.dupeZ(u8, std.mem.span(data[0])) catch { + username = allocator.dupeSentinel(u8, std.mem.span(data[0]), 0) catch { status = interop.pam.PAM_BUF_ERR; break :set_credentials; }; @@ -302,7 +302,7 @@ fn loginConv( }, interop.pam.PAM_PROMPT_ECHO_OFF => { const data: [*][*:0]u8 = @ptrCast(@alignCast(appdata_ptr)); - password = allocator.dupeZ(u8, std.mem.span(data[1])) catch { + password = allocator.dupeSentinel(u8, std.mem.span(data[1]), 0) catch { status = interop.pam.PAM_BUF_ERR; break :set_credentials; }; @@ -332,7 +332,7 @@ fn getFreeDisplay() !u8 { var buf: [15]u8 = undefined; var i: u8 = 0; while (i < 200) : (i += 1) { - const xlock = try std.fmt.bufPrintZ(&buf, "/tmp/.X{d}-lock", .{i}); + const xlock = try std.fmt.bufPrintSentinel(&buf, "/tmp/.X{d}-lock", .{i}, 0); if (interop.isError(std.posix.system.access(xlock.ptr, std.posix.F_OK))) break; } return i; @@ -433,7 +433,7 @@ fn xauth(log_file: *LogFile, allocator: std.mem.Allocator, io: std.Io, display_n try log_file.reinit(io); 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); + const cmd_str = std.fmt.bufPrintSentinel(&cmd_buffer, "{s} add {s} . {s}", .{ options.xauth_cmd, display_name, magic_cookie }, 0) catch std.process.exit(1); try log_file.info(io, "auth/x11", "executing: {s} -c {s}", .{ shell, cmd_str }); const args = [_:null]?[*:0]const u8{ shell, "-c", cmd_str }; @@ -469,7 +469,7 @@ fn executeX11Cmd(log_file: *LogFile, allocator: std.mem.Allocator, io: std.Io, s const display_name = try std.fmt.bufPrint(&buf, ":{d}", .{display_num}); try log_file.info(io, "auth/x11", "got free display: {d}", .{display_num}); - const shell_z = try allocator.dupeZ(u8, shell); + const shell_z = try allocator.dupeSentinel(u8, shell, 0); defer allocator.free(shell_z); try log_file.info(io, "auth/x11", "creating xauth file", .{}); @@ -479,7 +479,7 @@ fn executeX11Cmd(log_file: *LogFile, allocator: std.mem.Allocator, io: std.Io, s 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); + const cmd_str = std.fmt.bufPrintSentinel(&cmd_buffer, "{s} {s} {s} -auth {s}", .{ options.x_cmd, display_name, vt, xauthority }, 0) catch std.process.exit(1); 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 }; @@ -508,7 +508,7 @@ fn executeX11Cmd(log_file: *LogFile, allocator: std.mem.Allocator, io: std.Io, s 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); + const cmd_str = std.fmt.bufPrintSentinel(&cmd_buffer, "{s} {s} {s} {s}", .{ if (options.use_kmscon_vt) "kmscon-launch-gui" else "", options.setup_cmd, options.login_cmd orelse "", desktop_cmd }, 0) catch std.process.exit(1); 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 }; @@ -559,11 +559,11 @@ fn executeCmd(global_log_file: *LogFile, allocator: std.mem.Allocator, io: std.I } defer if (maybe_log_file) |log_file| log_file.close(io); - const shell_z = try allocator.dupeZ(u8, shell); + const shell_z = try allocator.dupeSentinel(u8, shell, 0); defer allocator.free(shell_z); 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 }); + const cmd_str = try std.fmt.bufPrintSentinel(&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 }, 0); 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 }; @@ -604,7 +604,7 @@ fn addUtmpEntry(io: std.Io, entry: *Utmp, username: []const u8, pid: c_int) !voi // Get the TTY name (i.e. without the /dev/ prefix) var ttyname_buf: [@sizeOf(@TypeOf(entry.ut_line))]u8 = undefined; - _ = try std.fmt.bufPrintZ(&ttyname_buf, "{s}", .{tty_path["/dev/".len..]}); + _ = try std.fmt.bufPrintSentinel(&ttyname_buf, "{s}", .{tty_path["/dev/".len..]}, 0); entry.ut_line = ttyname_buf; // Get the TTY ID (i.e. without the tty prefix) and truncate it to the size @@ -612,7 +612,7 @@ fn addUtmpEntry(io: std.Io, entry: *Utmp, username: []const u8, pid: c_int) !voi entry.ut_id = ttyname_buf["tty".len..(@sizeOf(@TypeOf(entry.ut_id)) + "tty".len)].*; var username_buf: [@sizeOf(@TypeOf(entry.ut_user))]u8 = undefined; - _ = try std.fmt.bufPrintZ(&username_buf, "{s}", .{username}); + _ = try std.fmt.bufPrintSentinel(&username_buf, "{s}", .{username}, 0); entry.ut_user = username_buf; diff --git a/src/main.zig b/src/main.zig index 31a8519..da97f0f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -303,7 +303,7 @@ pub fn main(init: std.process.Init) !void { const username_line = reader.takeDelimiterInclusive('\n') catch break :read_save_file; - if (std.mem.containsAtLeastScalar2(u8, username_line, '-', 1)) read_username: { + if (std.mem.containsAtLeastScalar(u8, username_line, '-', 1)) read_username: { var iterator = std.mem.splitScalar(u8, username_line[0..(username_line.len - 1)], '-'); if (iterator.next() == null) break :read_username; // Would be index @@ -1202,8 +1202,8 @@ pub fn main(init: std.process.Init) !void { var iter = custom.binds.iterator(); while (iter.next()) |i| { var concat = try std.mem.concat(state.allocator, u8, &[_][]const u8{ i.key_ptr.*, " ", i.value_ptr.name }); - inline for (@typeInfo(Lang).@"struct".fields) |lang_key| { - const new = try std.mem.replaceOwned(u8, state.allocator, concat, "$" ++ lang_key.name, @field(state.lang, lang_key.name)); + inline for (@typeInfo(Lang).@"struct".field_names) |lang_key| { + const new = try std.mem.replaceOwned(u8, state.allocator, concat, "$" ++ lang_key, @field(state.lang, lang_key)); state.allocator.free(concat); concat = new; } @@ -1885,7 +1885,7 @@ fn updateBigClock(self: *BigLabel, ptr: *anyopaque) !void { const time = try interop.getTimeOfDay(); const animate_time = @divTrunc(time.microseconds, 500_000); const separator = if (state.animate and animate_time != 0) " " else ":"; - const format = try std.fmt.bufPrintZ( + const format = try std.fmt.bufPrintSentinel( &state.bigclock_format_buf, "{s}{s}{s}{s}{s}{s}", .{ @@ -1896,6 +1896,7 @@ fn updateBigClock(self: *BigLabel, ptr: *anyopaque) !void { if (state.config.bigclock_seconds) "%S" else "", if (state.config.bigclock_12hr) "%P" else "", }, + 0, ); const clock_str = interop.timeAsString(state.io, &state.bigclock_buf, format); From bcc2cdfcdc5688942aa99b18503823c5f0c8b548 Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Sun, 12 Jul 2026 12:10:47 +0200 Subject: [PATCH 03/12] Fix installation Signed-off-by: AnErrupTion --- install.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.zig b/install.zig index b5649cd..8bb4835 100644 --- a/install.zig +++ b/install.zig @@ -384,7 +384,7 @@ fn installText( destination_directory: std.Io.Dir, destination_directory_path: []const u8, destination_file: []const u8, - options: std.Io.File.CreateFlags, + options: std.Io.Dir.CreateFileOptions, ) !void { var file = try destination_directory.createFile(io, destination_file, options); defer file.close(io); From 79bb0cacf14d88d0f6c8dc9a06c4143505258b56 Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Sun, 12 Jul 2026 12:17:51 +0200 Subject: [PATCH 04/12] Remove C compilation hacks Signed-off-by: AnErrupTion --- ly-ui/build.zig | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/ly-ui/build.zig b/ly-ui/build.zig index b13633f..f3727a8 100644 --- a/ly-ui/build.zig +++ b/ly-ui/build.zig @@ -38,18 +38,7 @@ pub fn build(b: *std.Build) void { .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 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(.{ From e01fc945a2e0e9aba8f8666c816da91d414bcafb Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Sun, 12 Jul 2026 12:19:44 +0200 Subject: [PATCH 05/12] Simplify tarball script Signed-off-by: AnErrupTion --- create_vendor_tarball.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_vendor_tarball.sh b/create_vendor_tarball.sh index b42f58c..65cb233 100755 --- a/create_vendor_tarball.sh +++ b/create_vendor_tarball.sh @@ -1,2 +1,2 @@ #!/bin/sh -tar --zstd -cvf vendor.tar.zst zig-pkg ly-ui/zig-pkg ly-core/zig-pkg +tar --zstd -cvf vendor.tar.zst zig-pkg From 3dcd883c103f346be51ee508de3d651f53b4c851 Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Sun, 12 Jul 2026 12:21:27 +0200 Subject: [PATCH 06/12] termbox2: Remove Zig 0.16.0 workaround Signed-off-by: AnErrupTion --- ly-ui/build.zig.zon | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ly-ui/build.zig.zon b/ly-ui/build.zig.zon index d328567..44f24ec 100644 --- a/ly-ui/build.zig.zon +++ b/ly-ui/build.zig.zon @@ -8,8 +8,8 @@ .path = "../ly-core", }, .termbox2 = .{ - .url = "git+https://github.com/AnErrupTion/termbox2?ref=master#c7f241e8888ce243e1748b05c26a42fcfaaad936", - .hash = "N-V-__8AAAUXBQD6Fwpi9m0MBqWXFFaqW5l1lVrJC2Ynj7a-", + .url = "git+https://github.com/AnErrupTion/termbox2?ref=master#496730697c662893eec43192f48ff616c2539da6", + .hash = "N-V-__8AAOEWBQDt5tNdIzIFY6n8DdZsCP-6MyLoNS20wgpA", }, .translate_c = .{ .url = "git+https://codeberg.org/ziglang/translate-c?ref=master#2d71d6e68dd9e9ee1da2a248a1dcb5aeba683dec", From a9cbfbddbf4469163ec76f65bf1a7bebe1ee0291 Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Tue, 25 Aug 2026 23:36:43 +0200 Subject: [PATCH 07/12] Update to Zig 0.17.0-dev.1824+7988f7952 Signed-off-by: AnErrupTion --- ly-core/build.zig | 4 +-- ly-core/build.zig.zon | 8 ++--- ly-core/src/interop.zig | 2 +- ly-core/src/root.zig | 68 ++++++++++++++++++++++++++--------------- ly-ui/build.zig | 4 +-- ly-ui/build.zig.zon | 4 +-- src/auth.zig | 4 +-- src/main.zig | 4 +-- 8 files changed, 56 insertions(+), 42 deletions(-) diff --git a/ly-core/build.zig b/ly-core/build.zig index 37b8668..3290ec5 100644 --- a/ly-core/build.zig +++ b/ly-core/build.zig @@ -28,9 +28,7 @@ pub fn build(b: *std.Build) void { }); mod.addImport("zlua", zlua.module("zlua")); - const translate_c = b.dependency("translate_c", .{ - .target = target, - }); + const translate_c = b.dependency("translate_c", .{}); addCImport(b, mod, translate_c, target, optimize, "pam", "#include "); addCImport(b, mod, translate_c, target, optimize, "utmp", "#include "); diff --git a/ly-core/build.zig.zon b/ly-core/build.zig.zon index 1c20d79..48e57e6 100644 --- a/ly-core/build.zig.zon +++ b/ly-core/build.zig.zon @@ -9,12 +9,12 @@ .hash = "zigini-0.6.0-BSkB7UtYAAB6b6HEm3Pjj5WIy-uiuBYsNTNCUENVwL1t", }, .translate_c = .{ - .url = "git+https://codeberg.org/ziglang/translate-c?ref=master#2d71d6e68dd9e9ee1da2a248a1dcb5aeba683dec", - .hash = "translate_c-0.0.0-Q_BUWtI4BwDTFS7O6WTn-3qixa_s81nw_C0FHbf2aLqj", + .url = "git+https://codeberg.org/ziglang/translate-c?ref=master#3da873cacdd7e9190fe1cf40372ddf5387c970ea", + .hash = "translate_c-0.0.0-Q_BUWktLBwBO7VLbmrOKUcAMRtE5fmOxI35189X5XbJs", }, .zlua = .{ - .url = "git+https://github.com/natecraddock/ziglua?ref=zig-0.16#8f271c82baa5fc43aa02a72f6da020c2025d9436", - .hash = "zlua-0.1.0-hGRpC2aABQD4D9PBVH3wAW8k32-I4969MRQ0CpOwoley", + .url = "git+https://github.com/AnErrupTion/ziglua?ref=zig-0.17#4159a5592a8de728022e0c491a3bdde7decbdc16", + .hash = "zlua-0.1.0-hGRpCw6VBQDaOiIiYsLOcQTlcJCpqDqIrhyDVqkanTSB", }, }, .paths = .{ diff --git a/ly-core/src/interop.zig b/ly-core/src/interop.zig index 2fcf5ef..1576003 100644 --- a/ly-core/src/interop.zig +++ b/ly-core/src/interop.zig @@ -422,7 +422,7 @@ pub fn getNextUsernameEntry() ?UsernameEntry { } pub fn getUsernameEntry(allocator: std.mem.Allocator, username: []const u8) ?UsernameEntry { - const username_z = allocator.dupeZ(u8, username) catch return null; + const username_z = allocator.dupeSentinel(u8, username, 0) catch return null; defer allocator.free(username_z); const entry = pwd.getpwnam(username_z); diff --git a/ly-core/src/root.zig b/ly-core/src/root.zig index c52d7d4..15514e0 100644 --- a/ly-core/src/root.zig +++ b/ly-core/src/root.zig @@ -131,13 +131,11 @@ pub fn LuaParser(comptime Struct: type) type { var arena = std.heap.ArenaAllocator.init(allocator); const arena_alloc = arena.allocator(); - var maybe_load_error: ?anyerror = null; - errdefer |err| maybe_load_error = err; - const data = parseLua(arena_alloc, path) catch load_error: { break :load_error Struct{}; }; + var maybe_load_error: ?anyerror = null; if (global_errors.items.len != 0) { maybe_load_error = error.InvalidConfig; } @@ -176,8 +174,8 @@ pub fn LuaParser(comptime Struct: type) type { defer lua.pop(1); // pop ly table if (ly_type == .nil) return error.MissingLyTable; - inline for (struc.fields) |field| { - try setField(allocator, lua, field, &data); + inline for (struc.field_names, struc.field_types) |name, ftype| { + try setField(allocator, lua, name, ftype, &data); } }, else => @compileError("Expected a struct."), @@ -188,21 +186,47 @@ pub fn LuaParser(comptime Struct: type) type { return data; } - pub fn setField(allocator: std.mem.Allocator, lua: *Lua, comptime field: std.builtin.Type.StructField, data: *Struct) !void { - const type_info = @typeInfo(field.type); + pub fn setField( + allocator: std.mem.Allocator, + lua: *Lua, + comptime field_name: [:0]const u8, + field_type: type, + data: *Struct, + ) !void { + return setFieldInner( + allocator, + lua, + field_name, + field_type, + data, + ) catch |err| { + const value = lua.toString(-1) catch ""; + const duped = allocator.dupe(u8, value) catch ""; + errorHandler(@typeName(field_type), field_name, duped, err); + }; + } + + fn setFieldInner( + allocator: std.mem.Allocator, + lua: *Lua, + comptime field_name: [:0]const u8, + field_type: type, + data: *Struct, + ) !void { + const type_info = @typeInfo(field_type); const actual_type, const is_optional = blk: { if (type_info == .optional) { break :blk .{ type_info.optional.child, true }; } - break :blk .{ field.type, false }; + break :blk .{ field_type, false }; }; // push value to top of stack - _ = lua.getField(-1, field.name); + _ = lua.getField(-1, field_name); defer lua.pop(1); // handle null, i.e. undefined fields if (is_optional and lua.isNil(-1)) { - @field(data, field.name) = null; + @field(data, field_name) = null; return; } @@ -212,17 +236,11 @@ pub fn LuaParser(comptime Struct: type) type { } const actual_type_info = @typeInfo(actual_type); - errdefer |err| { - const value = lua.toString(-1) catch ""; - const duped = allocator.dupe(u8, value) catch ""; - errorHandler(@typeName(field.type), field.name, duped, err); - } - // dispatch depending on type if (actual_type_info == .int and is_optional) { if (lua.isNumber(-1)) { const value = try lua.toNumber(-1); - @field(data, field.name) = @trunc(value); + @field(data, field_name) = @trunc(value); } else { const str = try lua.toString(-1); @@ -233,13 +251,13 @@ pub fn LuaParser(comptime Struct: type) type { if (iter.nextCodepoint() != null) return error.ExpectedSingleCharacter; - @field(data, field.name) = if (codepoint) |cp| @intCast(cp) else null; + @field(data, field_name) = if (codepoint) |cp| @intCast(cp) else null; } // non null integer } else if (actual_type_info == .int) { if (lua.isNumber(-1)) { const value = try lua.toNumber(-1); - @field(data, field.name) = @trunc(value); + @field(data, field_name) = @trunc(value); } else { const str = try lua.toString(-1); @@ -250,27 +268,27 @@ pub fn LuaParser(comptime Struct: type) type { if (iter.nextCodepoint() != null) return error.ExpectedSingleCharacter; - @field(data, field.name) = @intCast(codepoint); + @field(data, field_name) = @intCast(codepoint); } } else if (actual_type_info == .float) { // all floats const value = try lua.toNumber(-1); - @field(data, field.name) = @floatCast(value); + @field(data, field_name) = @floatCast(value); } else if (actual_type_info == .bool) { if (!lua.isBoolean(-1)) return error.ExpectedBoolean; const value = lua.toBoolean(-1); - @field(data, field.name) = value; + @field(data, field_name) = value; } else if (actual_type == []const u8) { const value = try lua.toString(-1); const duped = try allocator.dupe(u8, value); - @field(data, field.name) = duped; + @field(data, field_name) = duped; } else if (actual_type == [:0]const u8) { const value = try lua.toString(-1); const duped = try allocator.dupeSentinel(u8, value, 0); - @field(data, field.name) = duped; + @field(data, field_name) = duped; } else if (actual_type_info == .@"enum") { const value = try lua.toString(-1); const variant = std.meta.stringToEnum(actual_type, value) orelse return error.InvalidVariant; - @field(data, field.name) = variant; + @field(data, field_name) = variant; } else unreachable; } diff --git a/ly-ui/build.zig b/ly-ui/build.zig index f3727a8..24c0a76 100644 --- a/ly-ui/build.zig +++ b/ly-ui/build.zig @@ -28,9 +28,7 @@ pub fn build(b: *std.Build) void { .optimize = optimize, }); - const translate_c_dep = b.dependency("translate_c", .{ - .target = target, - }); + const translate_c_dep = b.dependency("translate_c", .{}); const termbox2: Translator = .init(translate_c_dep, .{ .c_source_file = termbox_dep.path("termbox2.h"), diff --git a/ly-ui/build.zig.zon b/ly-ui/build.zig.zon index 96f0ca9..ee52757 100644 --- a/ly-ui/build.zig.zon +++ b/ly-ui/build.zig.zon @@ -12,8 +12,8 @@ .hash = "N-V-__8AAOEWBQDt5tNdIzIFY6n8DdZsCP-6MyLoNS20wgpA", }, .translate_c = .{ - .url = "git+https://codeberg.org/ziglang/translate-c?ref=master#2d71d6e68dd9e9ee1da2a248a1dcb5aeba683dec", - .hash = "translate_c-0.0.0-Q_BUWtI4BwDTFS7O6WTn-3qixa_s81nw_C0FHbf2aLqj", + .url = "git+https://codeberg.org/ziglang/translate-c?ref=master#3da873cacdd7e9190fe1cf40372ddf5387c970ea", + .hash = "translate_c-0.0.0-Q_BUWktLBwBO7VLbmrOKUcAMRtE5fmOxI35189X5XbJs", }, }, .paths = .{ diff --git a/src/auth.zig b/src/auth.zig index 714ddb8..14016fe 100644 --- a/src/auth.zig +++ b/src/auth.zig @@ -326,7 +326,7 @@ fn loginConv( for (0..message_count) |i| set_credentials: { switch (messages[i].?.msg_style) { interop.pam.PAM_PROMPT_ECHO_ON => { - username = allocator.dupeZ(u8, data.username) catch { + username = allocator.dupeSentinel(u8, data.username, 0) catch { status = interop.pam.PAM_BUF_ERR; break :set_credentials; }; @@ -341,7 +341,7 @@ fn loginConv( data.authreq_responded = true; } - password = allocator.dupeZ(u8, pass) catch { + password = allocator.dupeSentinel(u8, pass, 0) catch { status = interop.pam.PAM_BUF_ERR; break :set_credentials; }; diff --git a/src/main.zig b/src/main.zig index 6bd3c65..edfe800 100644 --- a/src/main.zig +++ b/src/main.zig @@ -154,8 +154,8 @@ pub fn main(init: std.process.Init) !void { } var gpa: std.heap.DebugAllocator(.{ - .never_unmap = builtin.mode == .Debug, - .retain_metadata = builtin.mode == .Debug, + .never_unmap = builtin.mode == .debug, + .retain_metadata = builtin.mode == .debug, }) = .init; defer if (gpa.deinit() == .leak) std.log.err("attention please, memory has been leaked!", .{}); From c56a607fe7bf6291850f0848c369fe7d6e12aeab Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Tue, 25 Aug 2026 23:37:43 +0200 Subject: [PATCH 08/12] Update create_vendor_tarball.sh Signed-off-by: AnErrupTion --- create_vendor_tarball.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/create_vendor_tarball.sh b/create_vendor_tarball.sh index 65cb233..b42f58c 100755 --- a/create_vendor_tarball.sh +++ b/create_vendor_tarball.sh @@ -1,2 +1,2 @@ #!/bin/sh -tar --zstd -cvf vendor.tar.zst zig-pkg +tar --zstd -cvf vendor.tar.zst zig-pkg ly-ui/zig-pkg ly-core/zig-pkg From 75001c4a33acbb51bf1dc5d342216489e03c4800 Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Tue, 25 Aug 2026 23:39:13 +0200 Subject: [PATCH 09/12] zigini: Use upstream URL Signed-off-by: AnErrupTion --- ly-core/build.zig.zon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ly-core/build.zig.zon b/ly-core/build.zig.zon index 48e57e6..91d88bc 100644 --- a/ly-core/build.zig.zon +++ b/ly-core/build.zig.zon @@ -5,7 +5,7 @@ .minimum_zig_version = "0.17.0", .dependencies = .{ .zigini = .{ - .url = "git+https://github.com/AnErrupTion/zigini?ref=zig-0.17.0#29b30ec37efccb022a57e7acda83d63fa5832dd8", + .url = "git+https://github.com/AshAmetrine/zigini?ref=main#085badbf90e25016f4609b5bde334689cdb41c5c", .hash = "zigini-0.6.0-BSkB7UtYAAB6b6HEm3Pjj5WIy-uiuBYsNTNCUENVwL1t", }, .translate_c = .{ From 35fa91058b0391735e00266c18898cac5ac57c34 Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Wed, 26 Aug 2026 09:13:39 +0200 Subject: [PATCH 10/12] migrator: One TODO is gone! Yippee! Signed-off-by: AnErrupTion --- src/config/migrator.zig | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/config/migrator.zig b/src/config/migrator.zig index 9c3fc10..fcd235a 100644 --- a/src/config/migrator.zig +++ b/src/config/migrator.zig @@ -232,9 +232,7 @@ pub fn configFieldHandler(_: std.mem.Allocator, field: ini.IniField) ?ini.IniFie // on the progress of said interface, only to find out afterwards // that you have PROCRASTINATED on the efforts meant to enhance // configuration. Thus the requirement for this reminder larger - // compared to the two reminders regarding better methods of - // X termination detection and new usernames with existing - // save files. + // compared to the one regarding better methods of X termination. // // Thus is my que to leave this TODO at thy request, // From c90aec373ed3bda7562d3555856c9126675a8bb3 Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Wed, 26 Aug 2026 09:36:02 +0200 Subject: [PATCH 11/12] auth: Implement password reset after expiration (closes #992) Signed-off-by: AnErrupTion --- res/lang/ar.ini | 2 +- res/lang/bg.ini | 2 +- res/lang/cat.ini | 2 +- res/lang/cs.ini | 2 +- res/lang/de.ini | 2 +- res/lang/en.ini | 2 +- res/lang/eo.ini | 2 +- res/lang/es.ini | 2 +- res/lang/fr.ini | 2 +- res/lang/it.ini | 2 +- res/lang/ja_JP.ini | 2 +- res/lang/ku.ini | 2 +- res/lang/lv.ini | 2 +- res/lang/pl.ini | 2 +- res/lang/pt.ini | 2 +- res/lang/pt_BR.ini | 2 +- res/lang/ro.ini | 2 +- res/lang/ru.ini | 2 +- res/lang/sr.ini | 2 +- res/lang/sr_Cyrl.ini | 2 +- res/lang/sv.ini | 2 +- res/lang/tr.ini | 2 +- res/lang/uk.ini | 2 +- res/lang/zh_CN.ini | 2 +- res/lang/zh_TW.ini | 2 +- src/auth.zig | 15 +++++++++------ src/config/Lang.zig | 4 ++-- src/main.zig | 42 +++++++++++++++++++++++++++++++++--------- 28 files changed, 69 insertions(+), 42 deletions(-) diff --git a/res/lang/ar.ini b/res/lang/ar.ini index fd51b51..009be7c 100644 --- a/res/lang/ar.ini +++ b/res/lang/ar.ini @@ -33,7 +33,6 @@ err_pam_abort = تم إلغاء معاملة PAM err_pam_acct_expired = الحساب منتهي الصلاحية err_pam_auth = خطأ في المصادقة (Authentication error) err_pam_authinfo_unavail = فشل في الحصول على معلومات المستخدم -err_pam_authok_reqd = انتهت صلاحية رمز المصادقة (Token) err_pam_buf = خطأ في ذاكرة التخزين المؤقت (Buffer) err_pam_cred_err = فشل في تعيين بيانات الاعتماد (Credentials) err_pam_cred_expired = بيانات الاعتماد منتهية الصلاحية @@ -77,6 +76,7 @@ shell = shell shutdown = ايقاف التشغيل sleep = وضع السكون toggle_password = إظهار/إخفاء كلمة المرور + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/bg.ini b/res/lang/bg.ini index 1ac9246..66cba42 100644 --- a/res/lang/bg.ini +++ b/res/lang/bg.ini @@ -33,7 +33,6 @@ err_pam_abort = прекратена транзакция err_pam_acct_expired = изтекъл профил err_pam_auth = грешка при удостоверяването err_pam_authinfo_unavail = неуспешно получаване на информация за потребителя -err_pam_authok_reqd = изтекъл жетон err_pam_buf = грешка в буфера на паметта err_pam_cred_err = неуспешно задаване на удостоверения err_pam_cred_expired = изтекли удостоверения @@ -77,6 +76,7 @@ shell = обвивка shutdown = изключване sleep = заспиване toggle_password = превключване на паролата + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/cat.ini b/res/lang/cat.ini index 3bf271b..f8684fb 100644 --- a/res/lang/cat.ini +++ b/res/lang/cat.ini @@ -33,7 +33,6 @@ err_pam_abort = transacció pam avortada err_pam_acct_expired = compte expirat err_pam_auth = error d'autenticació err_pam_authinfo_unavail = error en obtenir la informació de l'usuari -err_pam_authok_reqd = token expirat err_pam_buf = error en la memòria intermèdia err_pam_cred_err = error en establir les credencials err_pam_cred_expired = credencials expirades @@ -77,6 +76,7 @@ shell = shell shutdown = aturar sleep = suspendre toggle_password = mostrar/amagar contrasenya + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/cs.ini b/res/lang/cs.ini index 4f9456c..3e5683e 100644 --- a/res/lang/cs.ini +++ b/res/lang/cs.ini @@ -33,7 +33,6 @@ err_pam_abort = pam transakce přerušena err_pam_acct_expired = platnost účtu vypršela err_pam_auth = chyba autentizace err_pam_authinfo_unavail = nelze získat informace o uživateli -err_pam_authok_reqd = platnost tokenu vypršela err_pam_buf = chyba vyrovnávací paměti err_pam_cred_err = nelze nastavit pověření err_pam_cred_expired = platnost pověření vypršela @@ -77,6 +76,7 @@ shell = příkazový řádek shutdown = vypnout sleep = uspat toggle_password = zobrazit/skrýt heslo + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/de.ini b/res/lang/de.ini index ca3a0ef..18e8a29 100644 --- a/res/lang/de.ini +++ b/res/lang/de.ini @@ -33,7 +33,6 @@ err_pam_abort = PAM-Transaktion abgebrochen err_pam_acct_expired = Benutzerkonto abgelaufen err_pam_auth = Authentifizierungsfehler err_pam_authinfo_unavail = Abrufen der Benutzerinformationen fehlgeschlagen -err_pam_authok_reqd = Passwort abgelaufen err_pam_buf = Speicherpufferfehler err_pam_cred_err = Fehler beim Setzen der Anmeldedaten err_pam_cred_expired = Anmeldedaten abgelaufen @@ -77,6 +76,7 @@ shell = Shell shutdown = Herunterfahren sleep = Sleep toggle_password = Passwort anzeigen/verbergen + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/en.ini b/res/lang/en.ini index b3d40f2..7ad3039 100644 --- a/res/lang/en.ini +++ b/res/lang/en.ini @@ -33,7 +33,6 @@ err_pam_abort = pam transaction aborted err_pam_acct_expired = account expired err_pam_auth = authentication error err_pam_authinfo_unavail = failed to get user info -err_pam_authok_reqd = token expired err_pam_buf = memory buffer error err_pam_cred_err = failed to set credentials err_pam_cred_expired = credentials expired @@ -77,6 +76,7 @@ shell = shell shutdown = shutdown sleep = sleep toggle_password = toggle password +token_expired = password expired, please reset wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/eo.ini b/res/lang/eo.ini index c8cbb96..4e18590 100644 --- a/res/lang/eo.ini +++ b/res/lang/eo.ini @@ -33,7 +33,6 @@ err_pam_abort = PAM-a transakcio malsukcesis err_pam_acct_expired = konto eksvalidiĝis err_pam_auth = aŭtentiga eraro err_pam_authinfo_unavail = malsukcesis preni uzantajn informojn -err_pam_authok_reqd = memorsigno eksvalidiĝis err_pam_buf = bufra eraro err_pam_cred_err = malsukcesis agordi akreditaĵon err_pam_cred_expired = akreditaĵo eksvalidiĝis @@ -77,6 +76,7 @@ shell = ŝelo shutdown = malŝalti sleep = memordormi toggle_password = montri/kaŝi pasvorton + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/es.ini b/res/lang/es.ini index f587fe7..7565ec4 100644 --- a/res/lang/es.ini +++ b/res/lang/es.ini @@ -33,7 +33,6 @@ err_pam_abort = transacción pam abortada err_pam_acct_expired = cuenta expirada err_pam_auth = error de autenticación err_pam_authinfo_unavail = error al obtener información del usuario -err_pam_authok_reqd = token expirado err_pam_buf = error de la memoria intermedia err_pam_cred_err = error al establecer las credenciales err_pam_cred_expired = credenciales expiradas @@ -77,6 +76,7 @@ shell = shell shutdown = apagar sleep = suspender toggle_password = mostrar/ocultar contraseña + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/fr.ini b/res/lang/fr.ini index 472115e..167645c 100644 --- a/res/lang/fr.ini +++ b/res/lang/fr.ini @@ -33,7 +33,6 @@ err_pam_abort = transaction pam avortée err_pam_acct_expired = compte expiré err_pam_auth = erreur d'authentification err_pam_authinfo_unavail = échec de l'obtention des infos utilisateur -err_pam_authok_reqd = tiquet expiré err_pam_buf = erreur de mémoire tampon err_pam_cred_err = échec de la modification des identifiants err_pam_cred_expired = identifiants expirés @@ -77,6 +76,7 @@ shell = shell shutdown = éteindre sleep = veille toggle_password = afficher le mot de passe +token_expired = mot de passe expiré, veuillez le changer wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/it.ini b/res/lang/it.ini index 9c9679d..28889ce 100644 --- a/res/lang/it.ini +++ b/res/lang/it.ini @@ -33,7 +33,6 @@ err_pam_abort = transazione PAM interrotta err_pam_acct_expired = account scaduto err_pam_auth = errore di autenticazione err_pam_authinfo_unavail = impossibile ottenere informazioni utente -err_pam_authok_reqd = token scaduto err_pam_buf = errore buffer memoria err_pam_cred_err = impossibile impostare credenziali err_pam_cred_expired = credenziali scadute @@ -77,6 +76,7 @@ shell = shell shutdown = arresto sleep = sospendi toggle_password = mostra/nascondi password + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/ja_JP.ini b/res/lang/ja_JP.ini index a14feab..6551af7 100644 --- a/res/lang/ja_JP.ini +++ b/res/lang/ja_JP.ini @@ -33,7 +33,6 @@ err_pam_abort = PAMトランザクションが中断されました err_pam_acct_expired = アカウントの有効期限が切れています err_pam_auth = 認証エラー err_pam_authinfo_unavail = ユーザー情報の取得に失敗しました -err_pam_authok_reqd = トークンの有効期限が切れています err_pam_buf = メモリバッファエラー err_pam_cred_err = 認証情報の設定に失敗しました err_pam_cred_expired = 認証情報の有効期限が切れています @@ -77,6 +76,7 @@ shell = シェル shutdown = シャットダウン sleep = スリープ toggle_password = パスワードの表示/非表示 + wayland = Wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/ku.ini b/res/lang/ku.ini index 0c39030..7fbf777 100644 --- a/res/lang/ku.ini +++ b/res/lang/ku.ini @@ -33,7 +33,6 @@ err_pam_abort = danûstendina pam hate têkbirin err_pam_acct_expired = dema jimarê derbas bûye err_pam_auth = şaşetiya piştrastkirinê err_pam_authinfo_unavail = zanyariyên bikarhêner nehatin girtin -err_pam_authok_reqd = dema nîşandanê derbas bûye err_pam_buf = şaşetiya bîra demkî err_pam_cred_err = sazkirina rastkitinê têk çû err_pam_cred_expired = dema rastkitinê derbas bûye @@ -77,6 +76,7 @@ shell = shell shutdown = vemirîne sleep = têxîne xewê toggle_password = şîfre nîşan bide/veşêre + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/lv.ini b/res/lang/lv.ini index 8f54d89..6dc14d1 100644 --- a/res/lang/lv.ini +++ b/res/lang/lv.ini @@ -33,7 +33,6 @@ err_pam_abort = pam transakcija pārtraukta err_pam_acct_expired = konts novecojis err_pam_auth = autentifikācijas kļūda err_pam_authinfo_unavail = neizdevās iegūt lietotāja informāciju -err_pam_authok_reqd = žetons beidzies err_pam_buf = atmiņas bufera kļūda err_pam_cred_err = neizdevās iestatīt akreditācijas datus err_pam_cred_expired = akreditācijas dati novecojuši @@ -77,6 +76,7 @@ shell = terminālis shutdown = izslēgt sleep = snauda toggle_password = rādīt/slēpt paroli + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/pl.ini b/res/lang/pl.ini index 536680d..0910ee7 100644 --- a/res/lang/pl.ini +++ b/res/lang/pl.ini @@ -33,7 +33,6 @@ err_pam_abort = transakcja pam przerwana err_pam_acct_expired = konto wygasło err_pam_auth = błąd uwierzytelniania err_pam_authinfo_unavail = nie udało się zdobyć informacji o użytkowniku -err_pam_authok_reqd = token wygasł err_pam_buf = błąd bufora pamięci err_pam_cred_err = nie udało się ustawić uwierzytelnienia err_pam_cred_expired = uwierzytelnienie wygasło @@ -77,6 +76,7 @@ shell = powłoka shutdown = wyłącz sleep = uśpij toggle_password = Pokaż/ukryj hasło + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/pt.ini b/res/lang/pt.ini index 204a9f1..2b0ace4 100644 --- a/res/lang/pt.ini +++ b/res/lang/pt.ini @@ -33,7 +33,6 @@ err_pam_abort = transação pam abortada err_pam_acct_expired = conta expirada err_pam_auth = erro de autenticação err_pam_authinfo_unavail = erro ao obter informação do utilizador -err_pam_authok_reqd = token expirado err_pam_buf = erro de buffer de memória err_pam_cred_err = erro ao definir credenciais err_pam_cred_expired = credenciais expiradas @@ -77,6 +76,7 @@ shell = shell shutdown = encerrar sleep = suspender toggle_password = mostrar/ocultar palavra-passe + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/pt_BR.ini b/res/lang/pt_BR.ini index af8c970..f79bb71 100644 --- a/res/lang/pt_BR.ini +++ b/res/lang/pt_BR.ini @@ -33,7 +33,6 @@ err_pam_abort = transação pam abortada err_pam_acct_expired = conta expirada err_pam_auth = erro de autenticação err_pam_authinfo_unavail = não foi possível obter informações do usuário -err_pam_authok_reqd = token expirado err_pam_buf = erro de buffer de memória err_pam_cred_err = erro para definir credenciais err_pam_cred_expired = credenciais expiradas @@ -77,6 +76,7 @@ shell = shell shutdown = desligar sleep = suspender toggle_password = mostrar/ocultar senha + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/ro.ini b/res/lang/ro.ini index 3b771a8..318889f 100644 --- a/res/lang/ro.ini +++ b/res/lang/ro.ini @@ -33,7 +33,6 @@ err_pam_abort = tranzacţie pam anulată err_pam_acct_expired = cont expirat err_pam_auth = eroare de autentificare err_pam_authinfo_unavail = nu s-au putut obţine informaţii despre utilizator -err_pam_authok_reqd = token expirat err_pam_buf = eroare de memorie (buffer) err_pam_cred_err = nu s-au putut seta date de identificare (credentials) err_pam_cred_expired = datele de identificare (credentials) au expirat @@ -77,6 +76,7 @@ shell = shell shutdown = opreşte sistemul sleep = repaus toggle_password = afișare/ascundere parolă + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/ru.ini b/res/lang/ru.ini index 014c170..359dd95 100644 --- a/res/lang/ru.ini +++ b/res/lang/ru.ini @@ -33,7 +33,6 @@ err_pam_abort = pam транзакция прервана err_pam_acct_expired = срок действия аккаунта истёк err_pam_auth = ошибка аутентификации err_pam_authinfo_unavail = не удалось получить информацию о пользователе -err_pam_authok_reqd = токен истёк err_pam_buf = ошибка буфера памяти err_pam_cred_err = не удалось установить полномочия err_pam_cred_expired = полномочия истекли @@ -77,6 +76,7 @@ shell = оболочка shutdown = выключить sleep = сон toggle_password = показать/скрыть пароль + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/sr.ini b/res/lang/sr.ini index a2678bf..e82c4fa 100644 --- a/res/lang/sr.ini +++ b/res/lang/sr.ini @@ -33,7 +33,6 @@ err_pam_abort = pam transakcija prekinuta err_pam_acct_expired = nalog istekao err_pam_auth = greška pri autentikaciji err_pam_authinfo_unavail = neuspješno uzimanje informacija o korisniku -err_pam_authok_reqd = token istekao err_pam_buf = greška bafera memorije err_pam_cred_err = neuspješno postavljanje kredencijala err_pam_cred_expired = kredencijali istekli @@ -77,6 +76,7 @@ shell = shell shutdown = ugasi sleep = uspavaj toggle_password = prikaži/sakrij lozinku + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/sr_Cyrl.ini b/res/lang/sr_Cyrl.ini index 6222b3d..2e869bb 100644 --- a/res/lang/sr_Cyrl.ini +++ b/res/lang/sr_Cyrl.ini @@ -33,7 +33,6 @@ err_pam_abort = pam трансакција прекинута err_pam_acct_expired = налог истекао err_pam_auth = грешка при аутентикацији err_pam_authinfo_unavail = неуспешно узимање информација о кориснику -err_pam_authok_reqd = токен истекао err_pam_buf = грешка бафера меморије err_pam_cred_err = неуспешно постављање акредитива err_pam_cred_expired = акредитиви истекли @@ -77,6 +76,7 @@ shell = shell shutdown = угаси sleep = успавај toggle_password = прикажи/сакриј лозинку + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/sv.ini b/res/lang/sv.ini index 45e36d4..24fb6c9 100644 --- a/res/lang/sv.ini +++ b/res/lang/sv.ini @@ -33,7 +33,6 @@ err_pam_abort = pam-transaktion avbröts err_pam_acct_expired = kontot har löpt ut err_pam_auth = autentisering misslyckades err_pam_authinfo_unavail = hämtning av användarinformation misslyckades -err_pam_authok_reqd = token har löpt ut err_pam_buf = minnesbufferfel err_pam_cred_err = inställning av inloggningsuppgifter misslyckades err_pam_cred_expired = inloggningsuppgifterna har löpt ut @@ -77,6 +76,7 @@ shell = shell shutdown = stäng av sleep = viloläge toggle_password = visa/dölj lösenord + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/tr.ini b/res/lang/tr.ini index 414f4d5..a0d344f 100644 --- a/res/lang/tr.ini +++ b/res/lang/tr.ini @@ -33,7 +33,6 @@ err_pam_abort = pam islemi durduruldu err_pam_acct_expired = hesabin suresi dolmus err_pam_auth = kimlik dogrulama hatasi err_pam_authinfo_unavail = kullanici bilgileri getirilirken hata olustu -err_pam_authok_reqd = suresi dolmus token err_pam_buf = bellek arabellegi hatasi err_pam_cred_err = kimlik bilgileri ayarlanamadi err_pam_cred_expired = kimlik bilgilerinin suresi dolmus @@ -77,6 +76,7 @@ shell = shell shutdown = makineyi kapat sleep = uykuya al toggle_password = parolayı göster/gizle + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/uk.ini b/res/lang/uk.ini index 6baa469..b096b9a 100644 --- a/res/lang/uk.ini +++ b/res/lang/uk.ini @@ -33,7 +33,6 @@ err_pam_abort = pam транзакція перервана err_pam_acct_expired = термін дії акаунту вичерпано err_pam_auth = помилка автентифікації err_pam_authinfo_unavail = не вдалося отримати дані користувача -err_pam_authok_reqd = термін дії токена вичерпано err_pam_buf = помилка буферу пам'яті err_pam_cred_err = не вдалося змінити облікові дані err_pam_cred_expired = термін дії повноважень вичерпано @@ -77,6 +76,7 @@ shell = оболонка shutdown = вимкнути sleep = сплячий режим toggle_password = показати/приховати пароль + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/zh_CN.ini b/res/lang/zh_CN.ini index 4562624..183ec89 100644 --- a/res/lang/zh_CN.ini +++ b/res/lang/zh_CN.ini @@ -33,7 +33,6 @@ err_pam_abort = PAM事务已中止 err_pam_acct_expired = 帐户已过期 err_pam_auth = 身份验证错误 err_pam_authinfo_unavail = 获取用户信息失败 -err_pam_authok_reqd = 口令已过期 err_pam_buf = 内存缓冲区错误 err_pam_cred_err = 设置凭据失败 err_pam_cred_expired = 凭据已过期 @@ -77,6 +76,7 @@ shell = shell shutdown = 关机 sleep = 睡眠 toggle_password = 显示/隐藏密码 + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/res/lang/zh_TW.ini b/res/lang/zh_TW.ini index 50d5f68..606b2a1 100644 --- a/res/lang/zh_TW.ini +++ b/res/lang/zh_TW.ini @@ -33,7 +33,6 @@ err_pam_abort = PAM 交易已中止 err_pam_acct_expired = 帳號已過期 err_pam_auth = 驗證錯誤 err_pam_authinfo_unavail = 取得使用者資訊失敗 -err_pam_authok_reqd = 金鑰已過期 err_pam_buf = 記憶體緩衝區錯誤 err_pam_cred_err = 設定憑證失敗 err_pam_cred_expired = 憑證已過期 @@ -77,6 +76,7 @@ shell = shell shutdown = 關機 sleep = 睡眠 toggle_password = 顯示/隱藏密碼 + wayland = wayland x11 = x11 xinitrc = xinitrc diff --git a/src/auth.zig b/src/auth.zig index ef6af30..91f1cbc 100644 --- a/src/auth.zig +++ b/src/auth.zig @@ -29,7 +29,7 @@ pub const AuthOptions = struct { const PamAppdata = struct { username: []const u8, password: []const u8, - authreq_requested: bool, + new_authtok_requested: bool, authreq_responded: bool, new_password: []const u8, }; @@ -52,6 +52,7 @@ pub fn authenticate( current_environment: Environment, login: []const u8, password: []const u8, + maybe_new_password: ?[]const u8, ) !void { var tty_buffer: [3]u8 = undefined; const tty_str = try std.fmt.bufPrint(&tty_buffer, "{d}", .{options.tty}); @@ -69,7 +70,7 @@ pub fn authenticate( var credentials: PamAppdata = .{ .username = login, .password = password, - .authreq_requested = false, + .new_authtok_requested = false, .authreq_responded = false, .new_password = "", }; @@ -98,9 +99,11 @@ pub fn authenticate( try log_file.info(io, "auth/pam", "validating account", .{}); status = interop.pam.pam_acct_mgmt(handle, 0); if (status == interop.pam.PAM_NEW_AUTHTOK_REQD) { - // credentials.authreq_requested = true; - // credentials.new_password = ""; - // status = interop.pam.pam_chauthtok(handle, interop.pam.PAM_CHANGE_EXPIRED_AUTHTOK); + if (maybe_new_password) |new_passwsord| { + credentials.new_authtok_requested = true; + credentials.new_password = new_passwsord; + status = interop.pam.pam_chauthtok(handle, interop.pam.PAM_CHANGE_EXPIRED_AUTHTOK); + } } if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); @@ -334,7 +337,7 @@ fn loginConv( }, interop.pam.PAM_PROMPT_ECHO_OFF => { var pass = data.password; - if (data.authreq_requested) { + if (data.new_authtok_requested) { if (data.authreq_responded) { pass = data.new_password; } diff --git a/src/config/Lang.zig b/src/config/Lang.zig index c01cff0..d2a97e2 100644 --- a/src/config/Lang.zig +++ b/src/config/Lang.zig @@ -1,5 +1,5 @@ // -// NOTE: After editing this file, please run `/res/lang/normalize_lang_files.py` +// NOTE: After editing this file, please run `res/lang/normalize_lang_files.py` // to update all the language files accordingly. // @@ -38,7 +38,6 @@ err_pam_abort: []const u8 = "pam transaction aborted", err_pam_acct_expired: []const u8 = "account expired", err_pam_auth: []const u8 = "authentication error", err_pam_authinfo_unavail: []const u8 = "failed to get user info", -err_pam_authok_reqd: []const u8 = "token expired", err_pam_buf: []const u8 = "memory buffer error", err_pam_cred_err: []const u8 = "failed to set credentials", err_pam_cred_expired: []const u8 = "credentials expired", @@ -82,6 +81,7 @@ shell: [:0]const u8 = "shell", shutdown: []const u8 = "shutdown", sleep: []const u8 = "sleep", toggle_password: []const u8 = "toggle password", +token_expired: []const u8 = "password expired, please reset", wayland: []const u8 = "wayland", x11: []const u8 = "x11", xinitrc: [:0]const u8 = "xinitrc", diff --git a/src/main.zig b/src/main.zig index 1090d52..3ceebd2 100644 --- a/src/main.zig +++ b/src/main.zig @@ -113,6 +113,7 @@ const UiState = struct { login_text: ?*Text, password: *Text, password_widget: *Widget, + maybe_old_password: ?[]const u8, insert_mode: bool, edge_margin: Position, config: Config, @@ -889,6 +890,9 @@ pub fn main(init: std.process.Init) !void { ); defer state.password_label.deinit(); + state.maybe_old_password = null; + defer if (state.maybe_old_password) |pass| state.allocator.free(pass); + state.insert_mode = !state.config.vi_mode or state.config.vi_default_mode == .insert; state.password = try Text.init( @@ -1633,7 +1637,8 @@ fn authenticate(ptr: *anyopaque) !bool { auth_options, current_environment, if (state.login_text) |box| box.text.items else state.login.?.getCurrentUsername(), - password_text, + if (state.maybe_old_password) |pass| pass else password_text, + if (state.maybe_old_password != null) password_text else null, ) catch |err| { shared_err.writeError(err); @@ -1645,12 +1650,19 @@ fn authenticate(ptr: *anyopaque) !bool { std.process.exit(0); } - var session_status: c_int = undefined; - _ = std.posix.system.waitpid(session_pid, &session_status, 0); - // HACK: It seems like the session process is not exiting immediately after the waitpid call. - // This is a workaround to ensure the session process has exited before re-initializing the TTY. - state.io.sleep(.fromSeconds(1), .real) catch {}; - session_pid = -1; + if (state.maybe_old_password) |pass| { + state.allocator.free(pass); + state.maybe_old_password = null; + } + + if (session_pid != -1) { + var session_status: c_int = undefined; + _ = std.posix.system.waitpid(session_pid, &session_status, 0); + // HACK: It seems like the session process is not exiting immediately after the waitpid call. + // This is a workaround to ensure the session process has exited before re-initializing the TTY. + state.io.sleep(.fromSeconds(1), .real) catch {}; + session_pid = -1; + } try state.log_file.reinit(state.io); } @@ -1658,7 +1670,20 @@ fn authenticate(ptr: *anyopaque) !bool { try state.buffer.reclaim(); const auth_err = shared_err.readError(); - if (auth_err) |err| { + if (auth_err) |err| handle_error: { + if (err == error.PamNewAuthTokenRequired) { + try state.info_line.addMessage( + state.lang.token_expired, + state.config.bg, + state.config.fg, + ); + + state.maybe_old_password = try state.allocator.dupe(u8, state.password.text.items); + state.password.clear(); + state.is_autologin = false; + break :handle_error; + } + state.auth_fails += 1; state.buffer.setActiveWidget(state.password_widget); @@ -2611,7 +2636,6 @@ fn getAuthErrorMsg(err: anyerror, lang: Lang) []const u8 { error.PamCredentialsInsufficient => lang.err_pam_cred_insufficient, error.PamCredentialsUnavailable => lang.err_pam_cred_unavail, error.PamMaximumTries => lang.err_pam_maxtries, - error.PamNewAuthTokenRequired => lang.err_pam_authok_reqd, error.PamPermissionDenied => lang.err_pam_perm_denied, error.PamSessionError => lang.err_pam_session, error.PamSystemError => lang.err_pam_sys, From a6e5f87e24b1e520d29c11a5b4dc5ca7593b9ebe Mon Sep 17 00:00:00 2001 From: AnErrupTion Date: Wed, 26 Aug 2026 15:24:14 +0200 Subject: [PATCH 12/12] auth: Check faillock for locked account (closes #898) Signed-off-by: AnErrupTion --- res/config.lua | 7 +++++ res/lang/ar.ini | 1 + res/lang/bg.ini | 1 + res/lang/cat.ini | 1 + res/lang/cs.ini | 1 + res/lang/de.ini | 1 + res/lang/en.ini | 1 + res/lang/eo.ini | 1 + res/lang/es.ini | 1 + res/lang/fr.ini | 1 + res/lang/it.ini | 1 + res/lang/ja_JP.ini | 1 + res/lang/ku.ini | 1 + res/lang/lv.ini | 1 + res/lang/pl.ini | 1 + res/lang/pt.ini | 1 + res/lang/pt_BR.ini | 1 + res/lang/ro.ini | 1 + res/lang/ru.ini | 1 + res/lang/sr.ini | 1 + res/lang/sr_Cyrl.ini | 1 + res/lang/sv.ini | 1 + res/lang/tr.ini | 1 + res/lang/uk.ini | 1 + res/lang/zh_CN.ini | 1 + res/lang/zh_TW.ini | 1 + src/auth.zig | 65 +++++++++++++++++++++++++++++++++++++++++++ src/config/Config.zig | 1 + src/config/Lang.zig | 1 + src/main.zig | 2 ++ 30 files changed, 101 insertions(+) diff --git a/res/config.lua b/res/config.lua index e0fc47a..d233fbb 100644 --- a/res/config.lua +++ b/res/config.lua @@ -262,6 +262,13 @@ ly = { -- Default is red and bold error_fg = 0x01FF0000, + -- Tally directory for the pam_faillock module (if present) + -- Used for getting if an account is locked or not after too many failed + -- login attempts + -- If directory doesn't exist, lock status won't be checked when + -- authenticating + faillock_tally_dir = "/var/run/faillock", + -- Foreground color id fg = 0x00FFFFFF, diff --git a/res/lang/ar.ini b/res/lang/ar.ini index 009be7c..2c16dcc 100644 --- a/res/lang/ar.ini +++ b/res/lang/ar.ini @@ -6,6 +6,7 @@ custom = مخصص custom_info_err_output_long = الإخراج طويل جداً custom_info_err_no_output = لا يوجد إخراج custom_info_err_no_output_error = ، خطأ محتمل + err_alloc = فشل في تخصيص الذاكرة err_args = تعذر تحليل وسيطات سطر الأوامر err_autologin_session = لم يتم العثور على جلسة تسجيل الدخول التلقائي diff --git a/res/lang/bg.ini b/res/lang/bg.ini index 66cba42..f5540cb 100644 --- a/res/lang/bg.ini +++ b/res/lang/bg.ini @@ -6,6 +6,7 @@ custom = персонализирано custom_info_err_output_long = резултатът е твърде дълъг custom_info_err_no_output = няма резултат custom_info_err_no_output_error = , възможна грешка + err_alloc = неуспешно заделяне на памет err_args = неуспешен анализ на аргументите от командния ред err_autologin_session = сесията за автоматично влизане не е намерена diff --git a/res/lang/cat.ini b/res/lang/cat.ini index f8684fb..02a501f 100644 --- a/res/lang/cat.ini +++ b/res/lang/cat.ini @@ -6,6 +6,7 @@ custom = personalitzat custom_info_err_output_long = sortida massa llarga custom_info_err_no_output = sense sortida custom_info_err_no_output_error = , possible error + err_alloc = assignació de memòria fallida err_args = no s'han pogut analitzar els arguments de la línia d'ordres err_autologin_session = no s'ha trobat la sessió d'inici de sessió automàtic diff --git a/res/lang/cs.ini b/res/lang/cs.ini index 3e5683e..a8eb2bc 100644 --- a/res/lang/cs.ini +++ b/res/lang/cs.ini @@ -6,6 +6,7 @@ custom = vlastní custom_info_err_output_long = výstup je příliš dlouhý custom_info_err_no_output = žádný výstup custom_info_err_no_output_error = , možná chyba + err_alloc = alokace paměti selhala err_args = nelze analyzovat argumenty příkazového řádku err_autologin_session = relace automatického přihlášení nebyla nalezena diff --git a/res/lang/de.ini b/res/lang/de.ini index 18e8a29..fcaf6a2 100644 --- a/res/lang/de.ini +++ b/res/lang/de.ini @@ -6,6 +6,7 @@ custom = benutzerdefiniert custom_info_err_output_long = Ausgabe zu lang custom_info_err_no_output = keine Ausgabe custom_info_err_no_output_error = , möglicher Fehler + err_alloc = Speicherzuweisung fehlgeschlagen err_args = Kommandozeilenargumente konnten nicht verarbeitet werden err_autologin_session = Autologin-Sitzung nicht gefunden diff --git a/res/lang/en.ini b/res/lang/en.ini index 7ad3039..e6f166e 100644 --- a/res/lang/en.ini +++ b/res/lang/en.ini @@ -6,6 +6,7 @@ custom = custom custom_info_err_output_long = output too long custom_info_err_no_output = no output custom_info_err_no_output_error = , possible error +err_acc_locked = account locked, too many attempts err_alloc = failed memory allocation err_args = unable to parse command line arguments err_autologin_session = autologin session not found diff --git a/res/lang/eo.ini b/res/lang/eo.ini index 4e18590..ebe431c 100644 --- a/res/lang/eo.ini +++ b/res/lang/eo.ini @@ -6,6 +6,7 @@ custom = propra custom_info_err_output_long = eligo tro longa custom_info_err_no_output = neniu eligo custom_info_err_no_output_error = , ebla eraro + err_alloc = malsukcesis memorasignon err_args = ne povas analizi argumentojn de komanda linio err_autologin_session = aŭtomatan ensalutan seancon ne trovis diff --git a/res/lang/es.ini b/res/lang/es.ini index 7565ec4..bdb3615 100644 --- a/res/lang/es.ini +++ b/res/lang/es.ini @@ -6,6 +6,7 @@ custom = personalizado custom_info_err_output_long = salida demasiado larga custom_info_err_no_output = sin salida custom_info_err_no_output_error = , posible error + err_alloc = asignación de memoria fallida err_args = no se pudieron analizar los argumentos de la línea de comandos err_autologin_session = no se encontró la sesión de inicio de sesión automático diff --git a/res/lang/fr.ini b/res/lang/fr.ini index 167645c..b2f9651 100644 --- a/res/lang/fr.ini +++ b/res/lang/fr.ini @@ -6,6 +6,7 @@ custom = customisé custom_info_err_output_long = sortie trop longue custom_info_err_no_output = pas de sortie custom_info_err_no_output_error = , erreur possible +err_acc_locked = compte bloqué, trop de tentatives err_alloc = échec d'allocation mémoire err_args = échec de l'analyse des arguments en lignes de commande err_autologin_session = session de connexion automatique introuvable diff --git a/res/lang/it.ini b/res/lang/it.ini index 28889ce..580a44c 100644 --- a/res/lang/it.ini +++ b/res/lang/it.ini @@ -6,6 +6,7 @@ custom = personalizzato custom_info_err_output_long = output troppo lungo custom_info_err_no_output = nessun output custom_info_err_no_output_error = , possibile errore + err_alloc = impossibile allocare memoria err_args = impossibile analizzare gli argomenti della riga di comando err_autologin_session = sessione di accesso automatico non trovata diff --git a/res/lang/ja_JP.ini b/res/lang/ja_JP.ini index 6551af7..b64934b 100644 --- a/res/lang/ja_JP.ini +++ b/res/lang/ja_JP.ini @@ -6,6 +6,7 @@ custom = カスタム custom_info_err_output_long = 出力が長すぎます custom_info_err_no_output = 出力なし custom_info_err_no_output_error = 、エラーの可能性あり + err_alloc = メモリ割り当て失敗 err_args = コマンドライン引数を解析できません err_autologin_session = 自動ログインセッションが見つかりません diff --git a/res/lang/ku.ini b/res/lang/ku.ini index 7fbf777..74cd801 100644 --- a/res/lang/ku.ini +++ b/res/lang/ku.ini @@ -6,6 +6,7 @@ custom = kesane custom_info_err_output_long = encam pir dirêj e custom_info_err_no_output = encam tune custom_info_err_no_output_error = , xeletiya mimkun + err_alloc = veqetandina bîrê têk çû err_args = argumanên rêzika fermanê nehatin analîzkirin err_autologin_session = danişîna têketina xweber nehate dîtin diff --git a/res/lang/lv.ini b/res/lang/lv.ini index 6dc14d1..ee108c9 100644 --- a/res/lang/lv.ini +++ b/res/lang/lv.ini @@ -6,6 +6,7 @@ custom = pielāgots custom_info_err_output_long = izvade pārāk gara custom_info_err_no_output = nav izvades custom_info_err_no_output_error = , iespējama kļūda + err_alloc = neizdevās atmiņas piešķiršana err_args = nevar parsēt komandrindas argumentus err_autologin_session = automātiskās pieteikšanās sesija nav atrasta diff --git a/res/lang/pl.ini b/res/lang/pl.ini index 0910ee7..dc2770b 100644 --- a/res/lang/pl.ini +++ b/res/lang/pl.ini @@ -6,6 +6,7 @@ custom = własny custom_info_err_output_long = wyjście zbyt długie custom_info_err_no_output = brak wyjścia custom_info_err_no_output_error = , możliwy błąd + err_alloc = nieudana alokacja pamięci err_args = nie można przetworzyć argumentów wiersza poleceń err_autologin_session = nie znaleziono sesji autologowania diff --git a/res/lang/pt.ini b/res/lang/pt.ini index 2b0ace4..5ea0f9f 100644 --- a/res/lang/pt.ini +++ b/res/lang/pt.ini @@ -6,6 +6,7 @@ custom = personalizado custom_info_err_output_long = saída demasiado longa custom_info_err_no_output = sem saída custom_info_err_no_output_error = , possível erro + err_alloc = erro na atribuição de memória err_args = não foi possível analisar os argumentos da linha de comandos err_autologin_session = sessão de início de sessão automático não encontrada diff --git a/res/lang/pt_BR.ini b/res/lang/pt_BR.ini index f79bb71..1fee82e 100644 --- a/res/lang/pt_BR.ini +++ b/res/lang/pt_BR.ini @@ -6,6 +6,7 @@ custom = personalizado custom_info_err_output_long = saída muito longa custom_info_err_no_output = sem saída custom_info_err_no_output_error = , possível erro + err_alloc = alocação de memória malsucedida err_args = não foi possível analisar os argumentos da linha de comando err_autologin_session = sessão de login automático não encontrada diff --git a/res/lang/ro.ini b/res/lang/ro.ini index 318889f..a733101 100644 --- a/res/lang/ro.ini +++ b/res/lang/ro.ini @@ -6,6 +6,7 @@ custom = personalizat custom_info_err_output_long = ieșire prea lungă custom_info_err_no_output = fără ieșire custom_info_err_no_output_error = , posibil eroare + err_alloc = alocare de memorie eșuată err_args = imposibil de analizat argumentele liniei de comandă err_autologin_session = sesiunea de autentificare automată nu a fost găsită diff --git a/res/lang/ru.ini b/res/lang/ru.ini index 359dd95..1da01da 100644 --- a/res/lang/ru.ini +++ b/res/lang/ru.ini @@ -6,6 +6,7 @@ custom = пользовательский custom_info_err_output_long = вывод слишком длинный custom_info_err_no_output = нет вывода custom_info_err_no_output_error = , возможная ошибка + err_alloc = не удалось выделить память err_args = не удалось разобрать аргументы командной строки err_autologin_session = не найдена сессия с автологином diff --git a/res/lang/sr.ini b/res/lang/sr.ini index e82c4fa..fdf32a2 100644 --- a/res/lang/sr.ini +++ b/res/lang/sr.ini @@ -6,6 +6,7 @@ custom = prilagođeno custom_info_err_output_long = izlaz predugačak custom_info_err_no_output = nema izlaza custom_info_err_no_output_error = , moguća greška + err_alloc = neuspješna alokacija memorije err_args = nije moguće raščlaniti argumente komandne linije err_autologin_session = sesija automatske prijave nije pronađena diff --git a/res/lang/sr_Cyrl.ini b/res/lang/sr_Cyrl.ini index 2e869bb..0cb3944 100644 --- a/res/lang/sr_Cyrl.ini +++ b/res/lang/sr_Cyrl.ini @@ -6,6 +6,7 @@ custom = прилагођено custom_info_err_output_long = излаз предугачак custom_info_err_no_output = нема излаза custom_info_err_no_output_error = , могућа грешка + err_alloc = неуспешна алокација меморије err_args = није могуће рашчланити аргументе командне линије err_autologin_session = сесија аутоматске пријаве није пронађена diff --git a/res/lang/sv.ini b/res/lang/sv.ini index 24fb6c9..58376b8 100644 --- a/res/lang/sv.ini +++ b/res/lang/sv.ini @@ -6,6 +6,7 @@ custom = anpassad custom_info_err_output_long = utdata för lång custom_info_err_no_output = ingen utdata custom_info_err_no_output_error = , möjligt fel + err_alloc = minnesallokering misslyckades err_args = tolkning av kommandoargument misslyckades err_autologin_session = autologin-session hittades inte diff --git a/res/lang/tr.ini b/res/lang/tr.ini index a0d344f..fde531d 100644 --- a/res/lang/tr.ini +++ b/res/lang/tr.ini @@ -6,6 +6,7 @@ custom = özel custom_info_err_output_long = çıktı çok uzun custom_info_err_no_output = çıktı yok custom_info_err_no_output_error = , olası hata + err_alloc = basarisiz bellek ayirma err_args = komut satırı argümanları ayrıştırılamıyor err_autologin_session = otomatik oturum açma oturumu bulunamadı diff --git a/res/lang/uk.ini b/res/lang/uk.ini index b096b9a..fbd5b6d 100644 --- a/res/lang/uk.ini +++ b/res/lang/uk.ini @@ -6,6 +6,7 @@ custom = власний custom_info_err_output_long = вивід занадто довгий custom_info_err_no_output = немає виводу custom_info_err_no_output_error = , можлива помилка + err_alloc = невдале виділення пам'яті err_args = не вдалося розібрати аргументи командного рядка err_autologin_session = сеанс автоматичного входу не знайдено diff --git a/res/lang/zh_CN.ini b/res/lang/zh_CN.ini index 183ec89..21f31ee 100644 --- a/res/lang/zh_CN.ini +++ b/res/lang/zh_CN.ini @@ -6,6 +6,7 @@ custom = 自定义 custom_info_err_output_long = 输出过长 custom_info_err_no_output = 无输出 custom_info_err_no_output_error = ,可能有错误 + err_alloc = 内存分配失败 err_args = 无法解析命令行参数 err_autologin_session = 未找到自动登录会话 diff --git a/res/lang/zh_TW.ini b/res/lang/zh_TW.ini index 606b2a1..ee397c1 100644 --- a/res/lang/zh_TW.ini +++ b/res/lang/zh_TW.ini @@ -6,6 +6,7 @@ custom = 自訂 custom_info_err_output_long = 輸出過長 custom_info_err_no_output = 無輸出 custom_info_err_no_output_error = ,可能有錯誤 + err_alloc = 記憶體配置失敗 err_args = 無法解析命令列參數 err_autologin_session = 找不到自動登入工作階段 diff --git a/src/auth.zig b/src/auth.zig index 91f1cbc..28778b8 100644 --- a/src/auth.zig +++ b/src/auth.zig @@ -20,6 +20,7 @@ pub const AuthOptions = struct { xauth_cmd: []const u8, setup_cmd: []const u8, login_cmd: ?[]const u8, + faillock_tally_dir: []const u8, x_cmd: []const u8, x_vt: ?u8, session_pid: std.posix.pid_t, @@ -34,6 +35,18 @@ const PamAppdata = struct { new_password: []const u8, }; +//https://github.com/linux-pam/linux-pam/blob/master/modules/pam_faillock/faillock.h#L55 +const PamFaillockEntry = extern struct { + pub const STATUS_VALID: usize = 0x1; + pub const STATUS_RHOST: usize = 0x2; + pub const STATUS_TTY: usize = 0x4; + + source: [52]u8, + reserved: u16, + status: u16, + time: u64, +}; + var xorg_pid: std.posix.pid_t = 0; pub fn xorgSignalHandler(sig: std.posix.SIG) callconv(.c) void { if (xorg_pid > 0) _ = std.c.kill(xorg_pid, sig); @@ -54,6 +67,11 @@ pub fn authenticate( password: []const u8, maybe_new_password: ?[]const u8, ) !void { + var faillock_entries: usize = 0; + if (try dirExists(io, options.faillock_tally_dir)) { + faillock_entries = try getFaillockEntries(allocator, io, login, options.faillock_tally_dir); + } + var tty_buffer: [3]u8 = undefined; const tty_str = try std.fmt.bufPrint(&tty_buffer, "{d}", .{options.tty}); @@ -94,6 +112,13 @@ pub fn authenticate( // Do the PAM routine try log_file.info(io, "auth/pam", "authenticating", .{}); status = interop.pam.pam_authenticate(handle, 0); + if (status == interop.pam.PAM_AUTH_ERR and try dirExists(io, options.faillock_tally_dir)) { + const new_faillock_entries = try getFaillockEntries(allocator, io, login, options.faillock_tally_dir); + + if (faillock_entries == new_faillock_entries) { + return error.AccountLocked; + } + } if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); try log_file.info(io, "auth/pam", "validating account", .{}); @@ -188,6 +213,46 @@ pub fn authenticate( if (shared_err.readError()) |err| return err; } +fn dirExists(io: std.Io, path: []const u8) !bool { + var dir = std.Io.Dir.openDirAbsolute(io, path, .{}) catch |err| { + if (err == error.FileNotFound) return false; + return err; + }; + defer dir.close(io); + + return true; +} + +fn getFaillockEntries( + allocator: std.mem.Allocator, + io: std.Io, + username: []const u8, + tally_dir: []const u8, +) !usize { + const path = try std.fs.path.join(allocator, &.{ tally_dir, username }); + defer allocator.free(path); + + var file = try std.Io.Dir.openFileAbsolute(io, path, .{}); + defer file.close(io); + + var buffer: [1024]u8 = undefined; + var reader = file.reader(io, &buffer); + var count: usize = 0; + + while (!reader.atEnd()) { + const entry = reader.interface.takeStruct(PamFaillockEntry, .little) catch |err| { + if (err == error.EndOfStream) break; + return err; + }; + + if (entry.status & PamFaillockEntry.STATUS_VALID != 0) { + count += 1; + } + } + + return count; +} + fn startSession( log_file: *LogFile, allocator: std.mem.Allocator, diff --git a/src/config/Config.zig b/src/config/Config.zig index 18d4abe..463a9cf 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -59,6 +59,7 @@ dur_y_offset: i32 = 0, edge_margin: u8 = 0, error_bg: u32 = 0x00000000, error_fg: u32 = 0x01FF0000, +faillock_tally_dir: []const u8 = "/var/run/faillock", fg: u32 = 0x00FFFFFF, full_color: bool = true, gameoflife_fg: u32 = 0x0000FF00, diff --git a/src/config/Lang.zig b/src/config/Lang.zig index d2a97e2..85051aa 100644 --- a/src/config/Lang.zig +++ b/src/config/Lang.zig @@ -11,6 +11,7 @@ custom: []const u8 = "custom", custom_info_err_output_long: []const u8 = "output too long", custom_info_err_no_output: []const u8 = "no output", custom_info_err_no_output_error: []const u8 = ", possible error", +err_acc_locked: []const u8 = "account locked, too many attempts", err_alloc: []const u8 = "failed memory allocation", err_args: []const u8 = "unable to parse command line arguments", err_autologin_session: []const u8 = "autologin session not found", diff --git a/src/main.zig b/src/main.zig index 3ceebd2..4d6bb65 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1614,6 +1614,7 @@ fn authenticate(ptr: *anyopaque) !bool { .xauth_cmd = state.config.xauth_cmd, .setup_cmd = state.config.setup_cmd, .login_cmd = state.config.login_cmd, + .faillock_tally_dir = state.config.faillock_tally_dir, .x_cmd = state.config.x_cmd, .x_vt = state.config.x_vt, .session_pid = session_pid, @@ -2641,6 +2642,7 @@ fn getAuthErrorMsg(err: anyerror, lang: Lang) []const u8 { error.PamSystemError => lang.err_pam_sys, error.PamUserUnknown => lang.err_pam_user_unknown, error.PamAbort => lang.err_pam_abort, + error.AccountLocked => lang.err_acc_locked, else => @errorName(err), }; }