| ... | @@ -50,8 +50,20 @@ pub const ChildProcess = struct { | ... | @@ -50,8 +50,20 @@ pub const ChildProcess = struct { |
| 50 | err_pipe: if (builtin.os == .windows) void else [2]os.fd_t, | 50 | err_pipe: if (builtin.os == .windows) void else [2]os.fd_t, |
| 51 | llnode: if (builtin.os == .windows) void else TailQueue(*ChildProcess).Node, | 51 | llnode: if (builtin.os == .windows) void else TailQueue(*ChildProcess).Node, |
| 52 | | 52 | |
| 53 | pub const SpawnError = error{OutOfMemory} || os.ExecveError || os.SetIdError || | 53 | pub const SpawnError = error{ |
| 54 | os.ChangeCurDirError || windows.CreateProcessError; | 54 | OutOfMemory, |
| | 55 | |
| | 56 | /// POSIX-only. `StdIo.Ignore` was selected and opening `/dev/null` returned ENODEV. |
| | 57 | NoDevice, |
| | 58 | |
| | 59 | /// Windows-only. One of: |
| | 60 | /// * `cwd` was provided and it could not be re-encoded into UTF16LE, or |
| | 61 | /// * The `PATH` or `PATHEXT` environment variable contained invalid UTF-8. |
| | 62 | InvalidUtf8, |
| | 63 | |
| | 64 | /// Windows-only. `cwd` was provided, but the path did not exist when spawning the child process. |
| | 65 | CurrentWorkingDirectoryUnlinked, |
| | 66 | } || os.ExecveError || os.SetIdError || os.ChangeCurDirError || windows.CreateProcessError; |
| 55 | | 67 | |
| 56 | pub const Term = union(enum) { | 68 | pub const Term = union(enum) { |
| 57 | Exited: u32, | 69 | Exited: u32, |
| ... | @@ -102,7 +114,7 @@ pub const ChildProcess = struct { | ... | @@ -102,7 +114,7 @@ pub const ChildProcess = struct { |
| 102 | } | 114 | } |
| 103 | | 115 | |
| 104 | /// On success must call `kill` or `wait`. | 116 | /// On success must call `kill` or `wait`. |
| 105 | pub fn spawn(self: *ChildProcess) !void { | 117 | pub fn spawn(self: *ChildProcess) SpawnError!void { |
| 106 | if (builtin.os == .windows) { | 118 | if (builtin.os == .windows) { |
| 107 | return self.spawnWindows(); | 119 | return self.spawnWindows(); |
| 108 | } else { | 120 | } else { |
| ... | @@ -110,7 +122,7 @@ pub const ChildProcess = struct { | ... | @@ -110,7 +122,7 @@ pub const ChildProcess = struct { |
| 110 | } | 122 | } |
| 111 | } | 123 | } |
| 112 | | 124 | |
| 113 | pub fn spawnAndWait(self: *ChildProcess) !Term { | 125 | pub fn spawnAndWait(self: *ChildProcess) SpawnError!Term { |
| 114 | try self.spawn(); | 126 | try self.spawn(); |
| 115 | return self.wait(); | 127 | return self.wait(); |
| 116 | } | 128 | } |
| ... | @@ -162,7 +174,13 @@ pub const ChildProcess = struct { | ... | @@ -162,7 +174,13 @@ pub const ChildProcess = struct { |
| 162 | | 174 | |
| 163 | /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns. | 175 | /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns. |
| 164 | /// If it succeeds, the caller owns result.stdout and result.stderr memory. | 176 | /// If it succeeds, the caller owns result.stdout and result.stderr memory. |
| 165 | pub fn exec(allocator: *mem.Allocator, argv: []const []const u8, cwd: ?[]const u8, env_map: ?*const BufMap, max_output_size: usize) !ExecResult { | 177 | pub fn exec( |
| | 178 | allocator: *mem.Allocator, |
| | 179 | argv: []const []const u8, |
| | 180 | cwd: ?[]const u8, |
| | 181 | env_map: ?*const BufMap, |
| | 182 | max_output_size: usize, |
| | 183 | ) !ExecResult { |
| 166 | const child = try ChildProcess.init(argv, allocator); | 184 | const child = try ChildProcess.init(argv, allocator); |
| 167 | defer child.deinit(); | 185 | defer child.deinit(); |
| 168 | | 186 | |
| ... | @@ -292,7 +310,7 @@ pub const ChildProcess = struct { | ... | @@ -292,7 +310,7 @@ pub const ChildProcess = struct { |
| 292 | Term{ .Unknown = status }; | 310 | Term{ .Unknown = status }; |
| 293 | } | 311 | } |
| 294 | | 312 | |
| 295 | fn spawnPosix(self: *ChildProcess) !void { | 313 | fn spawnPosix(self: *ChildProcess) SpawnError!void { |
| 296 | const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe() else undefined; | 314 | const stdin_pipe = if (self.stdin_behavior == StdIo.Pipe) try os.pipe() else undefined; |
| 297 | errdefer if (self.stdin_behavior == StdIo.Pipe) { | 315 | errdefer if (self.stdin_behavior == StdIo.Pipe) { |
| 298 | destroyPipe(stdin_pipe); | 316 | destroyPipe(stdin_pipe); |
| ... | @@ -309,7 +327,16 @@ pub const ChildProcess = struct { | ... | @@ -309,7 +327,16 @@ pub const ChildProcess = struct { |
| 309 | }; | 327 | }; |
| 310 | | 328 | |
| 311 | const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); | 329 | const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); |
| 312 | const dev_null_fd = if (any_ignore) try os.openC(c"/dev/null", os.O_RDWR, 0) else undefined; | 330 | const dev_null_fd = if (any_ignore) |
| | 331 | os.openC(c"/dev/null", os.O_RDWR, 0) catch |err| switch (err) { |
| | 332 | error.PathAlreadyExists => unreachable, |
| | 333 | error.NoSpaceLeft => unreachable, |
| | 334 | error.FileTooBig => unreachable, |
| | 335 | error.DeviceBusy => unreachable, |
| | 336 | else => |e| return e, |
| | 337 | } |
| | 338 | else |
| | 339 | undefined; |
| 313 | defer { | 340 | defer { |
| 314 | if (any_ignore) os.close(dev_null_fd); | 341 | if (any_ignore) os.close(dev_null_fd); |
| 315 | } | 342 | } |
| ... | @@ -403,7 +430,7 @@ pub const ChildProcess = struct { | ... | @@ -403,7 +430,7 @@ pub const ChildProcess = struct { |
| 403 | } | 430 | } |
| 404 | } | 431 | } |
| 405 | | 432 | |
| 406 | fn spawnWindows(self: *ChildProcess) !void { | 433 | fn spawnWindows(self: *ChildProcess) SpawnError!void { |
| 407 | const saAttr = windows.SECURITY_ATTRIBUTES{ | 434 | const saAttr = windows.SECURITY_ATTRIBUTES{ |
| 408 | .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES), | 435 | .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES), |
| 409 | .bInheritHandle = windows.TRUE, | 436 | .bInheritHandle = windows.TRUE, |
| ... | @@ -412,11 +439,28 @@ pub const ChildProcess = struct { | ... | @@ -412,11 +439,28 @@ pub const ChildProcess = struct { |
| 412 | | 439 | |
| 413 | const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); | 440 | const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore); |
| 414 | | 441 | |
| 415 | const nul_handle = if (any_ignore) blk: { | 442 | const nul_handle = if (any_ignore) |
| 416 | break :blk try windows.CreateFile("NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ, null, windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, null); | 443 | windows.CreateFile( |
| 417 | } else blk: { | 444 | "NUL", |
| 418 | break :blk undefined; | 445 | windows.GENERIC_READ, |
| 419 | }; | 446 | windows.FILE_SHARE_READ, |
| | 447 | null, |
| | 448 | windows.OPEN_EXISTING, |
| | 449 | windows.FILE_ATTRIBUTE_NORMAL, |
| | 450 | null, |
| | 451 | ) catch |err| switch (err) { |
| | 452 | error.SharingViolation => unreachable, // not possible for "NUL" |
| | 453 | error.PathAlreadyExists => unreachable, // not possible for "NUL" |
| | 454 | error.PipeBusy => unreachable, // not possible for "NUL" |
| | 455 | error.InvalidUtf8 => unreachable, // not possible for "NUL" |
| | 456 | error.BadPathName => unreachable, // not possible for "NUL" |
| | 457 | error.FileNotFound => unreachable, // not possible for "NUL" |
| | 458 | error.AccessDenied => unreachable, // not possible for "NUL" |
| | 459 | error.NameTooLong => unreachable, // not possible for "NUL" |
| | 460 | else => |e| return e, |
| | 461 | } |
| | 462 | else |
| | 463 | undefined; |
| 420 | defer { | 464 | defer { |
| 421 | if (any_ignore) os.close(nul_handle); | 465 | if (any_ignore) os.close(nul_handle); |
| 422 | } | 466 | } |
| ... | @@ -542,10 +586,25 @@ pub const ChildProcess = struct { | ... | @@ -542,10 +586,25 @@ pub const ChildProcess = struct { |
| 542 | windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| { | 586 | windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo) catch |no_path_err| { |
| 543 | if (no_path_err != error.FileNotFound) return no_path_err; | 587 | if (no_path_err != error.FileNotFound) return no_path_err; |
| 544 | | 588 | |
| 545 | const PATH = try process.getEnvVarOwned(self.allocator, "PATH"); | 589 | var free_path = true; |
| 546 | defer self.allocator.free(PATH); | 590 | const PATH = process.getEnvVarOwned(self.allocator, "PATH") catch |err| switch (err) { |
| 547 | const PATHEXT = try process.getEnvVarOwned(self.allocator, "PATHEXT"); | 591 | error.EnvironmentVariableNotFound => blk: { |
| 548 | defer self.allocator.free(PATHEXT); | 592 | free_path = false; |
| | 593 | break :blk ""; |
| | 594 | }, |
| | 595 | else => |e| return e, |
| | 596 | }; |
| | 597 | defer if (free_path) self.allocator.free(PATH); |
| | 598 | |
| | 599 | var free_path_ext = true; |
| | 600 | const PATHEXT = process.getEnvVarOwned(self.allocator, "PATHEXT") catch |err| switch (err) { |
| | 601 | error.EnvironmentVariableNotFound => blk: { |
| | 602 | free_path_ext = false; |
| | 603 | break :blk ""; |
| | 604 | }, |
| | 605 | else => |e| return e, |
| | 606 | }; |
| | 607 | defer if (free_path_ext) self.allocator.free(PATHEXT); |
| 549 | | 608 | |
| 550 | var it = mem.tokenize(PATH, ";"); | 609 | var it = mem.tokenize(PATH, ";"); |
| 551 | retry: while (it.next()) |search_path| { | 610 | retry: while (it.next()) |search_path| { |
| ... | @@ -717,7 +776,7 @@ fn destroyPipe(pipe: [2]os.fd_t) void { | ... | @@ -717,7 +776,7 @@ fn destroyPipe(pipe: [2]os.fd_t) void { |
| 717 | // Child of fork calls this to report an error to the fork parent. | 776 | // Child of fork calls this to report an error to the fork parent. |
| 718 | // Then the child exits. | 777 | // Then the child exits. |
| 719 | fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn { | 778 | fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn { |
| 720 | writeIntFd(fd, @as(ErrInt,@errorToInt(err))) catch {}; | 779 | writeIntFd(fd, @as(ErrInt, @errorToInt(err))) catch {}; |
| 721 | os.exit(1); | 780 | os.exit(1); |
| 722 | } | 781 | } |
| 723 | | 782 | |