Skip to content

Commit

Permalink
std: ChildProcess.spawn looks at PATH env var
Browse files Browse the repository at this point in the history
closes #303
  • Loading branch information
andrewrk committed Apr 5, 2017
1 parent d65cd73 commit d15bcdc
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 26 deletions.
10 changes: 10 additions & 0 deletions std/mem.zig
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,16 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) -> bool {
return true;
}

/// Linear search for the index of a scalar value inside a slice.
pub fn indexOfScalar(comptime T: type, slice: []const T, value: T) -> ?usize {
for (slice) |item, i| {
if (item == value) {
return i;
}
}
return null;
}

/// Reads an integer from memory with size equal to bytes.len.
/// T specifies the return type, which must be large enough to store
/// the result.
Expand Down
15 changes: 2 additions & 13 deletions std/os/child_process.zig
Original file line number Diff line number Diff line change
Expand Up @@ -154,19 +154,8 @@ pub const ChildProcess = struct {
setUpChildIo(stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) %%
|err| forkChildErrReport(err_pipe[1], err);

const err = posix.getErrno(%return os.posixExecve(exe_path, args, env_map, allocator));
assert(err > 0);
forkChildErrReport(err_pipe[1], switch (err) {
errno.EFAULT => unreachable,
errno.E2BIG, errno.EMFILE, errno.ENAMETOOLONG, errno.ENFILE, errno.ENOMEM => error.SysResources,
errno.EACCES, errno.EPERM => error.AccessDenied,
errno.EINVAL, errno.ENOEXEC => error.InvalidExe,
errno.EIO, errno.ELOOP => error.FileSystem,
errno.EISDIR => error.IsDir,
errno.ENOENT, errno.ENOTDIR => error.FileNotFound,
errno.ETXTBSY => error.FileBusy,
else => error.Unexpected,
});
os.posixExecve(exe_path, args, env_map, allocator) %%
|err| forkChildErrReport(err_pipe[1], err);
}

// we are the parent
Expand Down
73 changes: 60 additions & 13 deletions std/os/index.zig
Original file line number Diff line number Diff line change
Expand Up @@ -203,15 +203,11 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) -> %void {
/// This function must allocate memory to add a null terminating bytes on path and each arg.
/// It must also convert to KEY=VALUE\0 format for environment variables, and include null
/// pointers after the args and after the environment variables.
/// Also make the first arg equal to path.
pub fn posixExecve(path: []const u8, argv: []const []const u8, env_map: &const BufMap,
allocator: &Allocator) -> %usize
/// Also make the first arg equal to exe_path.
/// This function also uses the PATH environment variable to get the full path to the executable.
pub fn posixExecve(exe_path: []const u8, argv: []const []const u8, env_map: &const BufMap,
allocator: &Allocator) -> %void
{
const path_buf = %return allocator.alloc(u8, path.len + 1);
defer allocator.free(path_buf);
@memcpy(&path_buf[0], &path[0], path.len);
path_buf[path.len] = 0;

const argv_buf = %return allocator.alloc(?&const u8, argv.len + 2);
mem.set(?&const u8, argv_buf, null);
defer {
Expand All @@ -222,10 +218,10 @@ pub fn posixExecve(path: []const u8, argv: []const []const u8, env_map: &const B
allocator.free(argv_buf);
}
{
// Add path to the first argument.
const arg_buf = %return allocator.alloc(u8, path.len + 1);
@memcpy(&arg_buf[0], path.ptr, path.len);
arg_buf[path.len] = 0;
// Add exe_path to the first argument.
const arg_buf = %return allocator.alloc(u8, exe_path.len + 1);
@memcpy(&arg_buf[0], exe_path.ptr, exe_path.len);
arg_buf[exe_path.len] = 0;

argv_buf[0] = arg_buf.ptr;
}
Expand Down Expand Up @@ -266,7 +262,58 @@ pub fn posixExecve(path: []const u8, argv: []const []const u8, env_map: &const B
}
envp_buf[envp_count] = null;

return posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr);

if (mem.indexOfScalar(u8, exe_path, '/') != null) {
// +1 for the null terminating byte
const path_buf = %return allocator.alloc(u8, exe_path.len + 1);
defer allocator.free(path_buf);
@memcpy(&path_buf[0], &exe_path[0], exe_path.len);
path_buf[exe_path.len] = 0;
return posixExecveErrnoToErr(posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)));
}

const PATH = getEnv("PATH") ?? ([]const u8)("/usr/local/bin:/bin/:/usr/bin"); // TODO issue #299
// PATH.len because it is >= the largest search_path
// +1 for the / to join the search path and exe_path
// +1 for the null terminating byte
const path_buf = %return allocator.alloc(u8, PATH.len + exe_path.len + 2);
defer allocator.free(path_buf);
var it = mem.split(PATH, ':');
var seen_eacces = false;
var err: usize = undefined;
while (true) {
const search_path = it.next() ?? break;
mem.copy(u8, path_buf, search_path);
path_buf[search_path.len] = '/';
mem.copy(u8, path_buf[search_path.len + 1 ...], exe_path);
path_buf[search_path.len + exe_path.len + 2] = 0;
err = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr));
assert(err > 0);
if (err == errno.EACCES) {
seen_eacces = true;
} else if (err != errno.ENOENT) {
return posixExecveErrnoToErr(err);
}
}
if (seen_eacces) {
err = errno.EACCES;
}
return posixExecveErrnoToErr(err);
}

fn posixExecveErrnoToErr(err: usize) -> error {
assert(err > 0);
return switch (err) {
errno.EFAULT => unreachable,
errno.E2BIG, errno.EMFILE, errno.ENAMETOOLONG, errno.ENFILE, errno.ENOMEM => error.SysResources,
errno.EACCES, errno.EPERM => error.AccessDenied,
errno.EINVAL, errno.ENOEXEC => error.InvalidExe,
errno.EIO, errno.ELOOP => error.FileSystem,
errno.EISDIR => error.IsDir,
errno.ENOENT, errno.ENOTDIR => error.FileNotFound,
errno.ETXTBSY => error.FileBusy,
else => error.Unexpected,
};
}

pub var environ_raw: []&u8 = undefined;
Expand Down

0 comments on commit d15bcdc

Please sign in to comment.