| ... | @@ -642,13 +642,86 @@ pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void { | ... | @@ -642,13 +642,86 @@ pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void { |
| 642 | } | 642 | } |
| 643 | } | 643 | } |
| 644 | | 644 | |
| | 645 | pub const ExecveError = error{ |
| | 646 | SystemResources, |
| | 647 | AccessDenied, |
| | 648 | InvalidExe, |
| | 649 | FileSystem, |
| | 650 | IsDir, |
| | 651 | FileNotFound, |
| | 652 | NotDir, |
| | 653 | FileBusy, |
| | 654 | ProcessFdQuotaExceeded, |
| | 655 | SystemFdQuotaExceeded, |
| | 656 | NameTooLong, |
| | 657 | } || UnexpectedError; |
| | 658 | |
| | 659 | /// Like `execve` except the parameters are null-terminated, |
| | 660 | /// matching the syscall API on all targets. This removes the need for an allocator. |
| | 661 | /// This function ignores PATH environment variable. See `execvpeC` for that. |
| | 662 | pub fn execveC(path: [*]const u8, child_argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) ExecveError { |
| | 663 | switch (errno(system.execve(path, child_argv, envp))) { |
| | 664 | 0 => unreachable, |
| | 665 | EFAULT => unreachable, |
| | 666 | E2BIG => return error.SystemResources, |
| | 667 | EMFILE => return error.ProcessFdQuotaExceeded, |
| | 668 | ENAMETOOLONG => return error.NameTooLong, |
| | 669 | ENFILE => return error.SystemFdQuotaExceeded, |
| | 670 | ENOMEM => return error.SystemResources, |
| | 671 | EACCES => return error.AccessDenied, |
| | 672 | EPERM => return error.AccessDenied, |
| | 673 | EINVAL => return error.InvalidExe, |
| | 674 | ENOEXEC => return error.InvalidExe, |
| | 675 | EIO => return error.FileSystem, |
| | 676 | ELOOP => return error.FileSystem, |
| | 677 | EISDIR => return error.IsDir, |
| | 678 | ENOENT => return error.FileNotFound, |
| | 679 | ENOTDIR => return error.NotDir, |
| | 680 | ETXTBSY => return error.FileBusy, |
| | 681 | else => |err| return unexpectedErrno(err), |
| | 682 | } |
| | 683 | } |
| | 684 | |
| | 685 | /// Like `execvpe` except the parameters are null-terminated, |
| | 686 | /// matching the syscall API on all targets. This removes the need for an allocator. |
| | 687 | /// This function also uses the PATH environment variable to get the full path to the executable. |
| | 688 | /// If `file` is an absolute path, this is the same as `execveC`. |
| | 689 | pub fn execvpeC(file: [*]const u8, child_argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) ExecveError { |
| | 690 | const file_slice = mem.toSliceConst(u8, file); |
| | 691 | if (mem.indexOfScalar(u8, file_slice, '/') != null) return execveC(file, child_argv, envp); |
| | 692 | |
| | 693 | const PATH = getenv("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; |
| | 694 | var path_buf: [MAX_PATH_BYTES]u8 = undefined; |
| | 695 | var it = mem.tokenize(PATH, ":"); |
| | 696 | var seen_eacces = false; |
| | 697 | var err: ExecveError = undefined; |
| | 698 | while (it.next()) |search_path| { |
| | 699 | if (path_buf.len < search_path.len + file_slice.len + 1) return error.NameTooLong; |
| | 700 | mem.copy(u8, &path_buf, search_path); |
| | 701 | path_buf[search_path.len] = '/'; |
| | 702 | mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice); |
| | 703 | path_buf[search_path.len + file_slice.len + 1] = 0; |
| | 704 | err = execveC(&path_buf, child_argv, envp); |
| | 705 | switch (err) { |
| | 706 | error.AccessDenied => seen_eacces = true, |
| | 707 | error.FileNotFound, error.NotDir => {}, |
| | 708 | else => |e| return e, |
| | 709 | } |
| | 710 | } |
| | 711 | if (seen_eacces) return error.AccessDenied; |
| | 712 | return err; |
| | 713 | } |
| | 714 | |
| 645 | /// This function must allocate memory to add a null terminating bytes on path and each arg. | 715 | /// This function must allocate memory to add a null terminating bytes on path and each arg. |
| 646 | /// It must also convert to KEY=VALUE\0 format for environment variables, and include null | 716 | /// It must also convert to KEY=VALUE\0 format for environment variables, and include null |
| 647 | /// pointers after the args and after the environment variables. | 717 | /// pointers after the args and after the environment variables. |
| 648 | /// `argv[0]` is the executable path. | 718 | /// `argv_slice[0]` is the executable path. |
| 649 | /// This function also uses the PATH environment variable to get the full path to the executable. | 719 | /// This function also uses the PATH environment variable to get the full path to the executable. |
| 650 | /// TODO provide execveC which does not take an allocator | 720 | pub fn execvpe( |
| 651 | pub fn execve(allocator: *mem.Allocator, argv_slice: []const []const u8, env_map: *const std.BufMap) !void { | 721 | allocator: *mem.Allocator, |
| | 722 | argv_slice: []const []const u8, |
| | 723 | env_map: *const std.BufMap, |
| | 724 | ) (ExecveError || error{OutOfMemory}) { |
| 652 | const argv_buf = try allocator.alloc(?[*]u8, argv_slice.len + 1); | 725 | const argv_buf = try allocator.alloc(?[*]u8, argv_slice.len + 1); |
| 653 | mem.set(?[*]u8, argv_buf, null); | 726 | mem.set(?[*]u8, argv_buf, null); |
| 654 | defer { | 727 | defer { |
| ... | @@ -670,37 +743,7 @@ pub fn execve(allocator: *mem.Allocator, argv_slice: []const []const u8, env_map | ... | @@ -670,37 +743,7 @@ pub fn execve(allocator: *mem.Allocator, argv_slice: []const []const u8, env_map |
| 670 | const envp_buf = try createNullDelimitedEnvMap(allocator, env_map); | 743 | const envp_buf = try createNullDelimitedEnvMap(allocator, env_map); |
| 671 | defer freeNullDelimitedEnvMap(allocator, envp_buf); | 744 | defer freeNullDelimitedEnvMap(allocator, envp_buf); |
| 672 | | 745 | |
| 673 | const exe_path = argv_slice[0]; | 746 | return execvpeC(argv_buf.ptr[0].?, argv_buf.ptr, envp_buf.ptr); |
| 674 | if (mem.indexOfScalar(u8, exe_path, '/') != null) { | | |
| 675 | return execveErrnoToErr(errno(system.execve(argv_buf[0].?, argv_buf.ptr, envp_buf.ptr))); | | |
| 676 | } | | |
| 677 | | | |
| 678 | const PATH = getenv("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; | | |
| 679 | // PATH.len because it is >= the largest search_path | | |
| 680 | // +1 for the / to join the search path and exe_path | | |
| 681 | // +1 for the null terminating byte | | |
| 682 | const path_buf = try allocator.alloc(u8, PATH.len + exe_path.len + 2); | | |
| 683 | defer allocator.free(path_buf); | | |
| 684 | var it = mem.tokenize(PATH, ":"); | | |
| 685 | var seen_eacces = false; | | |
| 686 | var err: usize = undefined; | | |
| 687 | while (it.next()) |search_path| { | | |
| 688 | mem.copy(u8, path_buf, search_path); | | |
| 689 | path_buf[search_path.len] = '/'; | | |
| 690 | mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path); | | |
| 691 | path_buf[search_path.len + exe_path.len + 1] = 0; | | |
| 692 | err = errno(system.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)); | | |
| 693 | assert(err > 0); | | |
| 694 | if (err == EACCES) { | | |
| 695 | seen_eacces = true; | | |
| 696 | } else if (err != ENOENT) { | | |
| 697 | return execveErrnoToErr(err); | | |
| 698 | } | | |
| 699 | } | | |
| 700 | if (seen_eacces) { | | |
| 701 | err = EACCES; | | |
| 702 | } | | |
| 703 | return execveErrnoToErr(err); | | |
| 704 | } | 747 | } |
| 705 | | 748 | |
| 706 | pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std.BufMap) ![]?[*]u8 { | 749 | pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std.BufMap) ![]?[*]u8 { |
| ... | @@ -734,43 +777,6 @@ pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*]u8) vo | ... | @@ -734,43 +777,6 @@ pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*]u8) vo |
| 734 | allocator.free(envp_buf); | 777 | allocator.free(envp_buf); |
| 735 | } | 778 | } |
| 736 | | 779 | |
| 737 | pub const ExecveError = error{ | | |
| 738 | SystemResources, | | |
| 739 | AccessDenied, | | |
| 740 | InvalidExe, | | |
| 741 | FileSystem, | | |
| 742 | IsDir, | | |
| 743 | FileNotFound, | | |
| 744 | NotDir, | | |
| 745 | FileBusy, | | |
| 746 | ProcessFdQuotaExceeded, | | |
| 747 | SystemFdQuotaExceeded, | | |
| 748 | NameTooLong, | | |
| 749 | } || UnexpectedError; | | |
| 750 | | | |
| 751 | fn execveErrnoToErr(err: usize) ExecveError { | | |
| 752 | assert(err > 0); | | |
| 753 | switch (err) { | | |
| 754 | EFAULT => unreachable, | | |
| 755 | E2BIG => return error.SystemResources, | | |
| 756 | EMFILE => return error.ProcessFdQuotaExceeded, | | |
| 757 | ENAMETOOLONG => return error.NameTooLong, | | |
| 758 | ENFILE => return error.SystemFdQuotaExceeded, | | |
| 759 | ENOMEM => return error.SystemResources, | | |
| 760 | EACCES => return error.AccessDenied, | | |
| 761 | EPERM => return error.AccessDenied, | | |
| 762 | EINVAL => return error.InvalidExe, | | |
| 763 | ENOEXEC => return error.InvalidExe, | | |
| 764 | EIO => return error.FileSystem, | | |
| 765 | ELOOP => return error.FileSystem, | | |
| 766 | EISDIR => return error.IsDir, | | |
| 767 | ENOENT => return error.FileNotFound, | | |
| 768 | ENOTDIR => return error.NotDir, | | |
| 769 | ETXTBSY => return error.FileBusy, | | |
| 770 | else => return unexpectedErrno(err), | | |
| 771 | } | | |
| 772 | } | | |
| 773 | | | |
| 774 | /// Get an environment variable. | 780 | /// Get an environment variable. |
| 775 | /// See also `getenvC`. | 781 | /// See also `getenvC`. |
| 776 | /// TODO make this go through libc when we have it | 782 | /// TODO make this go through libc when we have it |