Commit 6171338

Eric Bower  ·  2026-07-30 11:23:41 -0400 EDT
parent 0d66096
chore: ensure robust daemon

This is mostly for posterity: we setup the daemon process to chdir to "/" and
set the umask to 0. This ensures the daemon is not on a dir that could be
unmounted and then the unmount would fail (like a usb drive) and we reset the
umask so the daemon doesn't unherit an unpredictable umask.
2 files changed,  +42, -1
+14, -0
......@@ -161,6 +161,20 @@ pub fn daemonize(sesh_name: []const u8, cmd: Cmd, keep_fds_open: []i32) !PtyInfo
161161 // becomes the session leader and detaches process from its controlling terminal
162162 _ = try lib_posix.setsid();
163163
164+ // If the daemon was launched from a mounted filesystem (e.g., a USB drive
165+ // at /mnt/usb), keeping that directory as the working directory pins the
166+ // mount which means it can't be unmounted while the daemon holds it.
167+ // chdir("/") moves to the root filesystem, which is never unmounted.
168+ const dir_path_c = try lib_posix.toPosixPath("/");
169+ try lib_posix.chdirZ(&dir_path_c);
170+
171+ // The parent may have had a restrictive umask (e.g., 0077) that would
172+ // prevent the daemon from creating files with the intended permissions.
173+ // Setting umask(0) lets the daemon explicitly control permissions via
174+ // open()/creat() mode arguments, rather than inheriting an unpredictable
175+ // mask.
176+ _ = std.c.umask(0); // requires libc on linux
177+
164178 // Redirect stdin/stdout/stderr to /dev/null. The daemon
165179 // communicates via its unix socket, not stdio. Without
166180 // this, any pipe on FDs 0-2 (e.g. from bats' `run`
+28, -1
......@@ -1260,8 +1260,35 @@ const unexpected_error_tracing = builtin.mode == .Debug and switch (builtin.zig_
12601260 else => false,
12611261 };
12621262
1263+const ChangeCurDirError = error{
1264+ AccessDenied,
1265+ FileSystem,
1266+ SymLinkLoop,
1267+ NameTooLong,
1268+ FileNotFound,
1269+ SystemResources,
1270+ NotDir,
1271+ BadPathName,
1272+} || UnexpectedError;
1273+
1274+pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void {
1275+ switch (errno(system.chdir(dir_path))) {
1276+ .SUCCESS => return,
1277+ .ACCES => return error.AccessDenied,
1278+ .FAULT => unreachable,
1279+ .IO => return error.FileSystem,
1280+ .LOOP => return error.SymLinkLoop,
1281+ .NAMETOOLONG => return error.NameTooLong,
1282+ .NOENT => return error.FileNotFound,
1283+ .NOMEM => return error.SystemResources,
1284+ .NOTDIR => return error.NotDir,
1285+ .ILSEQ => |err| return unexpectedErrno(err),
1286+ else => |err| return unexpectedErrno(err),
1287+ }
1288+}
1289+
12631290 /// Used to convert a slice to a null terminated slice on the stack.
1264-fn toPosixPath(file_path: []const u8) error{NameTooLong}![PATH_MAX - 1:0]u8 {
1291+pub fn toPosixPath(file_path: []const u8) error{NameTooLong}![PATH_MAX - 1:0]u8 {
12651292 if (std.debug.runtime_safety) assert(mem.indexOfScalar(u8, file_path, 0) == null);
12661293 var path_with_null: [PATH_MAX - 1:0]u8 = undefined;
12671294 // >= rather than > to make room for the null byte