this repo has no description
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 161 lines 5.3 kB view raw
1const Command = @This(); 2 3const std = @import("std"); 4const builtin = @import("builtin"); 5const Pty = @import("Pty.zig"); 6const Terminal = @import("Terminal.zig"); 7 8const linux = std.os.linux; 9const posix = std.posix; 10 11argv: []const []const u8, 12 13working_directory: ?[]const u8, 14 15// Set after spawn() 16pid: ?std.posix.pid_t = null, 17 18env_map: *const std.process.Environ.Map, 19 20pty: Pty, 21 22pub fn spawn(self: *Command, io: std.Io, allocator: std.mem.Allocator) !void { 23 var arena_allocator = std.heap.ArenaAllocator.init(allocator); 24 defer arena_allocator.deinit(); 25 const arena = arena_allocator.allocator(); 26 27 // Keep fork->exec child path allocation-free, following std/Io/Threaded.zig:posixExecv 28 const argv_block = try arena.allocSentinel(?[*:0]const u8, self.argv.len, null); 29 for (self.argv, 0..) |arg, i| argv_block[i] = (try arena.dupeZ(u8, arg)).ptr; 30 const env_block = try self.env_map.createPosixBlock(arena, .{}); 31 const path = self.env_map.get("PATH") orelse std.Io.Threaded.default_PATH; 32 33 const pid = pid: { 34 const rc = linux.fork(); 35 break :pid switch (linux.errno(rc)) { 36 .SUCCESS => rc, 37 else => return error.ForkError, 38 }; 39 }; 40 if (pid == 0) { 41 // we are the child 42 _ = std.os.linux.setsid(); 43 44 // set the controlling terminal 45 var u: c_uint = std.posix.STDIN_FILENO; 46 if (posix.system.ioctl(self.pty.tty.handle, posix.T.IOCSCTTY, @intFromPtr(&u)) != 0) return error.IoctlError; 47 48 // set up io 49 { 50 const rc = linux.dup2(self.pty.tty.handle, std.posix.STDIN_FILENO); 51 switch (linux.errno(rc)) { 52 .SUCCESS => {}, 53 else => return error.Dup2Failed, 54 } 55 } 56 { 57 const rc = linux.dup2(self.pty.tty.handle, std.posix.STDOUT_FILENO); 58 switch (linux.errno(rc)) { 59 .SUCCESS => {}, 60 else => return error.Dup2Failed, 61 } 62 } 63 { 64 const rc = linux.dup2(self.pty.tty.handle, std.posix.STDERR_FILENO); 65 switch (linux.errno(rc)) { 66 .SUCCESS => {}, 67 else => return error.Dup2Failed, 68 } 69 } 70 self.pty.tty.close(io); 71 if (self.pty.pty.handle > 2) self.pty.pty.close(io); 72 73 if (self.working_directory) |wd| { 74 const wd_z = try posix.toPosixPath(wd); 75 if (linux.errno(linux.chdir(&wd_z)) != .SUCCESS) return error.ChdirFailed; 76 } 77 78 // exec 79 execvpeLinux(argv_block.ptr, env_block, self.argv[0], path) catch {}; 80 linux.exit(127); 81 } 82 83 // we are the parent 84 self.pid = @intCast(pid); 85 86 if (!Terminal.global_sigchild_installed) { 87 Terminal.global_sigchild_installed = true; 88 var act = posix.Sigaction{ 89 .handler = .{ .handler = handleSigChild }, 90 .mask = switch (builtin.os.tag) { 91 .macos => 0, 92 .linux => posix.sigemptyset(), 93 else => @compileError("os not supported"), 94 }, 95 .flags = 0, 96 }; 97 posix.sigaction(posix.SIG.CHLD, &act, null); 98 } 99 100 return; 101} 102 103fn handleSigChild(_: posix.SIG) callconv(.c) void { 104 var status: u32 = undefined; 105 const rc = linux.waitpid(-1, &status, 0); 106 const pid: i32 = switch (linux.errno(rc)) { 107 .SUCCESS => @intCast(rc), 108 else => return, 109 }; 110 111 Terminal.global_vt_mutex.lock(Terminal.global_io) catch return; 112 defer Terminal.global_vt_mutex.unlock(Terminal.global_io); 113 var vt = Terminal.global_vts.get(pid) orelse return; 114 vt.event_queue.push(.exited) catch {}; 115} 116 117pub fn kill(self: *Command) void { 118 if (self.pid) |pid| { 119 posix.kill(pid, posix.SIG.TERM) catch {}; 120 self.pid = null; 121 } 122} 123 124// Keep fork->exec child path allocation-free, following std/Io/Threaded.zig:posixExecv 125fn execvpeLinux( 126 argv: [*:null]const ?[*:0]const u8, 127 env_block: std.process.Environ.PosixBlock, 128 arg0: []const u8, 129 path: []const u8, 130) !noreturn { 131 // This implementation is largely copied from std/Io/Threaded.zig 132 // (`spawnPosix` + `posixExecv`/`posixExecvPath`) and adapted for this PTY fork path. 133 if (std.mem.indexOfScalar(u8, arg0, '/') != null) { 134 const path_z = try posix.toPosixPath(arg0); 135 return std.Io.Threaded.posixExecvPath(&path_z, argv, env_block); 136 } 137 138 var it = std.mem.tokenizeScalar(u8, path, std.fs.path.delimiter); 139 var path_buf: [posix.PATH_MAX]u8 = undefined; 140 var err: std.process.ReplaceError = error.FileNotFound; 141 var seen_eacces = false; 142 143 while (it.next()) |dir| { 144 const path_len = dir.len + arg0.len + 1; 145 if (path_buf.len < path_len + 1) return error.NameTooLong; 146 @memcpy(path_buf[0..dir.len], dir); 147 path_buf[dir.len] = '/'; 148 @memcpy(path_buf[dir.len + 1 ..][0..arg0.len], arg0); 149 path_buf[path_len] = 0; 150 const full_path = path_buf[0..path_len :0].ptr; 151 err = std.Io.Threaded.posixExecvPath(full_path, argv, env_block); 152 switch (err) { 153 error.AccessDenied => seen_eacces = true, 154 error.FileNotFound, error.NotDir => {}, 155 else => |e| return e, 156 } 157 } 158 159 if (seen_eacces) return error.AccessDenied; 160 return err; 161}