Open new log file after every fork() where necessary

Signed-off-by: AnErrupTion <anerruption@disroot.org>
This commit is contained in:
AnErrupTion 2025-10-18 15:29:47 +02:00
parent bfb3f925d9
commit 1fbcb10110
No known key found for this signature in database
3 changed files with 96 additions and 70 deletions

51
src/LogFile.zig Normal file
View File

@ -0,0 +1,51 @@
const std = @import("std");
const LogFile = @This();
path: []const u8,
could_open_log_file: bool = undefined,
file: std.fs.File = undefined,
buffer: []u8,
file_writer: std.fs.File.Writer = undefined,
pub fn init(path: []const u8, buffer: []u8) !LogFile {
var log_file = LogFile{ .path = path, .buffer = buffer };
log_file.could_open_log_file = try openLogFile(path, &log_file);
return log_file;
}
pub fn reinit(self: *LogFile) !void {
self.could_open_log_file = try openLogFile(self.path, self);
}
pub fn deinit(self: *LogFile) void {
self.file_writer.interface.flush() catch {};
self.file.close();
}
fn openLogFile(path: []const u8, log_file: *LogFile) !bool {
var could_open_log_file = true;
open_log_file: {
log_file.file = std.fs.cwd().openFile(path, .{ .mode = .write_only }) catch std.fs.cwd().createFile(path, .{ .mode = 0o666 }) catch {
// If we could neither open an existing log file nor create a new
// one, abort.
could_open_log_file = false;
break :open_log_file;
};
}
if (!could_open_log_file) {
log_file.file = try std.fs.openFileAbsolute("/dev/null", .{ .mode = .write_only });
}
var log_file_writer = log_file.file.writer(log_file.buffer);
// Seek to the end of the log file
if (could_open_log_file) {
const stat = try log_file.file.stat();
try log_file_writer.seekTo(stat.size);
}
log_file.file_writer = log_file_writer;
return could_open_log_file;
}

View File

@ -5,6 +5,7 @@ const enums = @import("enums.zig");
const Environment = @import("Environment.zig");
const interop = @import("interop.zig");
const SharedError = @import("SharedError.zig");
const LogFile = @import("LogFile.zig");
const Md5 = std.crypto.hash.Md5;
const utmp = interop.utmp;
@ -32,7 +33,7 @@ pub fn sessionSignalHandler(i: c_int) callconv(.c) void {
if (child_pid > 0) _ = std.c.kill(child_pid, i);
}
pub fn authenticate(allocator: std.mem.Allocator, log_writer: *std.Io.Writer, options: AuthOptions, current_environment: Environment, login: []const u8, password: []const u8) !void {
pub fn authenticate(allocator: std.mem.Allocator, log_file: *LogFile, options: AuthOptions, current_environment: Environment, login: []const u8, password: []const u8) !void {
var tty_buffer: [3]u8 = undefined;
const tty_str = try std.fmt.bufPrint(&tty_buffer, "{d}", .{options.tty});
@ -57,6 +58,8 @@ pub fn authenticate(allocator: std.mem.Allocator, log_writer: *std.Io.Writer, op
};
var handle: ?*interop.pam.pam_handle = undefined;
var log_writer = &log_file.file_writer.interface;
try log_writer.writeAll("[pam] starting session\n");
var status = interop.pam.pam_start(options.service_name, null, &conv, &handle);
if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status);
@ -100,15 +103,23 @@ pub fn authenticate(allocator: std.mem.Allocator, log_writer: *std.Io.Writer, op
var shared_err = try SharedError.init();
defer shared_err.deinit();
log_file.deinit();
child_pid = try std.posix.fork();
if (child_pid == 0) {
try log_writer.writeAll("starting session\n");
try log_writer.flush();
try log_file.reinit();
log_writer = &log_file.file_writer.interface;
startSession(log_writer, allocator, options, tty_str, user_entry, handle, current_environment) catch |e| {
try log_writer.writeAll("starting session\n");
startSession(log_file, allocator, options, tty_str, user_entry, handle, current_environment) catch |e| {
shared_err.writeError(e);
log_file.deinit();
std.process.exit(1);
};
log_file.deinit();
std.process.exit(0);
}
@ -134,13 +145,15 @@ pub fn authenticate(allocator: std.mem.Allocator, log_writer: *std.Io.Writer, op
// Wait for the session to stop
_ = std.posix.waitpid(child_pid, 0);
try log_file.reinit();
removeUtmpEntry(&entry);
if (shared_err.readError()) |err| return err;
}
fn startSession(
log_writer: *std.Io.Writer,
log_file: *LogFile,
allocator: std.mem.Allocator,
options: AuthOptions,
tty_str: []u8,
@ -172,11 +185,11 @@ fn startSession(
// Execute what the user requested
switch (current_environment.display_server) {
.wayland, .shell, .custom => try executeCmd(log_writer, allocator, user_entry.shell.?, options, current_environment.is_terminal, current_environment.cmd),
.wayland, .shell, .custom => try executeCmd(log_file, allocator, user_entry.shell.?, options, current_environment.is_terminal, current_environment.cmd),
.xinitrc, .x11 => if (build_options.enable_x11_support) {
var vt_buf: [5]u8 = undefined;
const vt = try std.fmt.bufPrint(&vt_buf, "vt{d}", .{options.tty});
try executeX11Cmd(log_writer, allocator, user_entry.shell.?, user_entry.home.?, options, current_environment.cmd orelse "", vt);
try executeX11Cmd(log_file, allocator, user_entry.shell.?, user_entry.home.?, options, current_environment.cmd orelse "", vt);
},
}
}
@ -369,7 +382,7 @@ fn mcookie() [Md5.digest_length * 2]u8 {
return std.fmt.bytesToHex(&out, .lower);
}
fn xauth(log_writer: *std.Io.Writer, allocator: std.mem.Allocator, display_name: []u8, shell: [*:0]const u8, home: []const u8, options: AuthOptions) !void {
fn xauth(log_file: *LogFile, allocator: std.mem.Allocator, display_name: []u8, shell: [*:0]const u8, home: []const u8, options: AuthOptions) !void {
const xauthority = try createXauthFile(home);
try interop.setEnvironmentVariable(allocator, "XAUTHORITY", xauthority, true);
try interop.setEnvironmentVariable(allocator, "DISPLAY", display_name, true);
@ -388,15 +401,15 @@ fn xauth(log_writer: *std.Io.Writer, allocator: std.mem.Allocator, display_name:
const status = std.posix.waitpid(pid, 0);
if (status.status != 0) {
try log_writer.print("xauth command failed with status {d}\n", .{status.status});
try log_file.file_writer.interface.print("xauth command failed with status {d}\n", .{status.status});
return error.XauthFailed;
}
}
fn executeX11Cmd(log_writer: *std.Io.Writer, allocator: std.mem.Allocator, shell: []const u8, home: []const u8, options: AuthOptions, desktop_cmd: []const u8, vt: []const u8) !void {
try log_writer.writeAll("[x11] getting free display\n");
try log_writer.flush();
fn executeX11Cmd(log_file: *LogFile, allocator: std.mem.Allocator, shell: []const u8, home: []const u8, options: AuthOptions, desktop_cmd: []const u8, vt: []const u8) !void {
var log_writer = &log_file.file_writer.interface;
try log_writer.writeAll("[x11] getting free display\n");
const display_num = try getFreeDisplay();
var buf: [4]u8 = undefined;
const display_name = try std.fmt.bufPrint(&buf, ":{d}", .{display_num});
@ -405,13 +418,9 @@ fn executeX11Cmd(log_writer: *std.Io.Writer, allocator: std.mem.Allocator, shell
defer allocator.free(shell_z);
try log_writer.writeAll("[x11] creating xauth file\n");
try log_writer.flush();
try xauth(log_writer, allocator, display_name, shell_z, home, options);
try xauth(log_file, allocator, display_name, shell_z, home, options);
try log_writer.writeAll("[x11] starting x server\n");
try log_writer.flush();
const pid = try std.posix.fork();
if (pid == 0) {
var cmd_buffer: [1024]u8 = undefined;
@ -432,16 +441,12 @@ fn executeX11Cmd(log_writer: *std.Io.Writer, allocator: std.mem.Allocator, shell
};
}
try log_writer.writeAll("[x11] getting x server pid\n");
try log_writer.flush();
// X Server detaches from the process.
// PID can be fetched from /tmp/X{d}.lock
try log_writer.writeAll("[x11] getting x server pid\n");
const x_pid = try getXPid(display_num);
try log_writer.writeAll("[x11] launching environment\n");
try log_writer.flush();
xorg_pid = try std.posix.fork();
if (xorg_pid == 0) {
var cmd_buffer: [1024]u8 = undefined;
@ -471,14 +476,14 @@ fn executeX11Cmd(log_writer: *std.Io.Writer, allocator: std.mem.Allocator, shell
_ = std.posix.waitpid(x_pid, 0);
}
fn executeCmd(log_writer: *std.Io.Writer, allocator: std.mem.Allocator, shell: []const u8, options: AuthOptions, is_terminal: bool, exec_cmd: ?[]const u8) !void {
fn executeCmd(global_log_file: *LogFile, allocator: std.mem.Allocator, shell: []const u8, options: AuthOptions, is_terminal: bool, exec_cmd: ?[]const u8) !void {
var maybe_log_file: ?std.fs.File = null;
if (!is_terminal) {
// For custom desktop entries, the "Terminal" value here determines if
// we redirect standard output & error or not. That is, we redirect only
// if it's equal to false (so if it's not running in a TTY).
if (options.session_log) |log_path| {
maybe_log_file = try redirectStandardStreams(log_writer, log_path, true);
maybe_log_file = try redirectStandardStreams(global_log_file, log_path, true);
}
}
defer if (maybe_log_file) |log_file| log_file.close();
@ -493,12 +498,12 @@ fn executeCmd(log_writer: *std.Io.Writer, allocator: std.mem.Allocator, shell: [
return std.posix.execveZ(shell_z, &args, std.c.environ);
}
fn redirectStandardStreams(log_writer: *std.Io.Writer, session_log: []const u8, create: bool) !std.fs.File {
fn redirectStandardStreams(global_log_file: *LogFile, session_log: []const u8, create: bool) !std.fs.File {
const log_file = if (create) (std.fs.cwd().createFile(session_log, .{ .mode = 0o666 }) catch |err| {
try log_writer.print("failed to create new session log file: {s}\n", .{@errorName(err)});
try global_log_file.file_writer.interface.print("failed to create new session log file: {s}\n", .{@errorName(err)});
return err;
}) else (std.fs.cwd().openFile(session_log, .{ .mode = .read_write }) catch |err| {
try log_writer.print("failed to open existing session log file: {s}\n", .{@errorName(err)});
try global_log_file.file_writer.interface.print("failed to open existing session log file: {s}\n", .{@errorName(err)});
return err;
});

View File

@ -25,6 +25,7 @@ const OldSave = @import("config/OldSave.zig");
const SavedUsers = @import("config/SavedUsers.zig");
const migrator = @import("config/migrator.zig");
const SharedError = @import("SharedError.zig");
const LogFile = @import("LogFile.zig");
const StringList = std.ArrayListUnmanaged([]const u8);
const Ini = ini.Ini;
@ -261,13 +262,12 @@ pub fn main() !void {
}
}
var log_file: std.fs.File = undefined;
defer log_file.close();
var log_file_buffer: [1024]u8 = undefined;
var log_buffer: [1024]u8 = undefined;
var log_file_writer: std.fs.File.Writer = undefined;
var could_open_log_file = try openLogFile(config.ly_log, &log_file, &log_buffer, &log_file_writer);
var log_writer = &log_file_writer.interface;
var log_file = try LogFile.init(config.ly_log, &log_file_buffer);
defer log_file.deinit();
var log_writer = &log_file.file_writer.interface;
// These strings only end up getting freed if the user quits Ly using Ctrl+C, which is fine since in the other cases
// we end up shutting down or restarting the system
@ -352,7 +352,7 @@ pub fn main() !void {
}
}
if (!could_open_log_file) {
if (!log_file.could_open_log_file) {
try info_line.addMessage(lang.err_log, config.error_bg, config.error_fg);
try log_writer.writeAll("failed to open log file\n");
}
@ -917,7 +917,7 @@ pub fn main() !void {
defer shared_err.deinit();
{
log_file.close();
log_file.deinit();
session_pid = try std.posix.fork();
if (session_pid == 0) {
@ -942,18 +942,16 @@ pub fn main() !void {
};
std.posix.sigaction(std.posix.SIG.CHLD, &tty_control_transfer_act, null);
could_open_log_file = try openLogFile(config.ly_log, &log_file, &log_buffer, &log_file_writer);
log_writer = &log_file_writer.interface;
defer log_file.close();
try log_file.reinit();
auth.authenticate(allocator, log_writer, auth_options, current_environment, login.getCurrentUsername(), password.text.items) catch |err| {
auth.authenticate(allocator, &log_file, auth_options, current_environment, login.getCurrentUsername(), password.text.items) catch |err| {
shared_err.writeError(err);
try log_writer.flush();
log_file.deinit();
std.process.exit(1);
};
try log_writer.flush();
log_file.deinit();
std.process.exit(0);
}
@ -963,8 +961,7 @@ pub fn main() !void {
std.Thread.sleep(std.time.ns_per_s * 1);
session_pid = -1;
could_open_log_file = try openLogFile(config.ly_log, &log_file, &log_buffer, &log_file_writer);
log_writer = &log_file_writer.interface;
try log_file.reinit();
}
// Take back control of the TTY
@ -1053,33 +1050,6 @@ pub fn main() !void {
}
}
fn openLogFile(path: []const u8, log_file: *std.fs.File, buffer: []u8, writer: *std.fs.File.Writer) !bool {
var could_open_log_file = true;
open_log_file: {
log_file.* = std.fs.cwd().openFile(path, .{ .mode = .write_only }) catch std.fs.cwd().createFile(path, .{ .mode = 0o666 }) catch {
// If we could neither open an existing log file nor create a new
// one, abort.
could_open_log_file = false;
break :open_log_file;
};
}
if (!could_open_log_file) {
log_file.* = try std.fs.openFileAbsolute("/dev/null", .{ .mode = .write_only });
}
var log_file_writer = log_file.writer(buffer);
// Seek to the end of the log file
if (could_open_log_file) {
const stat = try log_file.stat();
try log_file_writer.seekTo(stat.size);
}
writer.* = log_file_writer;
return could_open_log_file;
}
fn configErrorHandler(type_name: []const u8, key: []const u8, value: []const u8, err: anyerror) void {
config_errors.append(temporary_allocator, .{
.type_name = temporary_allocator.dupe(u8, type_name) catch return,