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 | |
| 161 | 161 | // becomes the session leader and detaches process from its controlling terminal | |
| 162 | 162 | _ = try lib_posix.setsid(); | |
| 163 | 163 | ||
| 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 | + | ||
| 164 | 178 | // Redirect stdin/stdout/stderr to /dev/null. The daemon | |
| 165 | 179 | // communicates via its unix socket, not stdio. Without | |
| 166 | 180 | // 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_ | |
| 1260 | 1260 | else => false, | |
| 1261 | 1261 | }; | |
| 1262 | 1262 | ||
| 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 | + | ||
| 1263 | 1290 | /// 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 { | |
| 1265 | 1292 | if (std.debug.runtime_safety) assert(mem.indexOfScalar(u8, file_path, 0) == null); | |
| 1266 | 1293 | var path_with_null: [PATH_MAX - 1:0]u8 = undefined; | |
| 1267 | 1294 | // >= rather than > to make room for the null byte |