this repo has no description
13
fork

Configure Feed

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

at b58ae3a2fa16b3b7f11d2a7297c73a1e839d035b 117 lines 3.4 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 posix = std.posix; 9 10argv: []const []const u8, 11 12working_directory: ?[]const u8, 13 14// Set after spawn() 15pid: ?std.posix.pid_t = null, 16 17env_map: *const std.process.EnvMap, 18 19pty: Pty, 20 21pub fn spawn(self: *Command, allocator: std.mem.Allocator) !void { 22 var arena_allocator = std.heap.ArenaAllocator.init(allocator); 23 defer arena_allocator.deinit(); 24 25 const arena = arena_allocator.allocator(); 26 27 const argv_buf = try arena.allocSentinel(?[*:0]const u8, self.argv.len, null); 28 for (self.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr; 29 30 const envp = try createEnvironFromMap(arena, self.env_map); 31 32 const pid = try std.posix.fork(); 33 if (pid == 0) { 34 // we are the child 35 _ = std.os.linux.setsid(); 36 37 // set the controlling terminal 38 var u: c_uint = std.posix.STDIN_FILENO; 39 if (posix.system.ioctl(self.pty.tty.handle, posix.T.IOCSCTTY, @intFromPtr(&u)) != 0) return error.IoctlError; 40 41 // set up io 42 try posix.dup2(self.pty.tty.handle, std.posix.STDIN_FILENO); 43 try posix.dup2(self.pty.tty.handle, std.posix.STDOUT_FILENO); 44 try posix.dup2(self.pty.tty.handle, std.posix.STDERR_FILENO); 45 46 self.pty.tty.close(); 47 if (self.pty.pty.handle > 2) self.pty.pty.close(); 48 49 if (self.working_directory) |wd| { 50 try std.posix.chdir(wd); 51 } 52 53 // exec 54 const err = std.posix.execvpeZ(argv_buf.ptr[0].?, argv_buf.ptr, envp); 55 _ = err catch {}; 56 } 57 58 // we are the parent 59 self.pid = @intCast(pid); 60 61 if (!Terminal.global_sigchild_installed) { 62 Terminal.global_sigchild_installed = true; 63 var act = posix.Sigaction{ 64 .handler = .{ .handler = handleSigChild }, 65 .mask = switch (builtin.os.tag) { 66 .macos => 0, 67 .linux => posix.sigemptyset(), 68 else => @compileError("os not supported"), 69 }, 70 .flags = 0, 71 }; 72 posix.sigaction(posix.SIG.CHLD, &act, null); 73 } 74 75 return; 76} 77 78fn handleSigChild(_: c_int) callconv(.c) void { 79 const result = std.posix.waitpid(-1, 0); 80 81 Terminal.global_vt_mutex.lock(); 82 defer Terminal.global_vt_mutex.unlock(); 83 if (Terminal.global_vts) |vts| { 84 var vt = vts.get(result.pid) orelse return; 85 vt.event_queue.push(.exited); 86 } 87} 88 89pub fn kill(self: *Command) void { 90 if (self.pid) |pid| { 91 std.posix.kill(pid, std.posix.SIG.TERM) catch {}; 92 self.pid = null; 93 } 94} 95 96/// Creates a null-deliminated environment variable block in the format expected by POSIX, from a 97/// hash map plus options. 98fn createEnvironFromMap( 99 arena: std.mem.Allocator, 100 map: *const std.process.EnvMap, 101) ![:null]?[*:0]u8 { 102 const envp_count: usize = map.count(); 103 104 const envp_buf = try arena.allocSentinel(?[*:0]u8, envp_count, null); 105 var i: usize = 0; 106 107 { 108 var it = map.iterator(); 109 while (it.next()) |pair| { 110 envp_buf[i] = try std.fmt.allocPrintSentinel(arena, "{s}={s}", .{ pair.key_ptr.*, pair.value_ptr.* }, 0); 111 i += 1; 112 } 113 } 114 115 std.debug.assert(i == envp_count); 116 return envp_buf; 117}