in src/couch_quickjs/quickjs/quickjs-libc.c [3005:3218]
static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValueConst options, args = argv[0];
JSValue val, ret_val;
const char **exec_argv, *file = NULL, *str, *cwd = NULL;
char **envp = environ;
uint32_t exec_argc, i;
int ret, pid, status;
BOOL block_flag = TRUE, use_path = TRUE;
static const char *std_name[3] = { "stdin", "stdout", "stderr" };
int std_fds[3];
uint32_t uid = -1, gid = -1;
val = JS_GetPropertyStr(ctx, args, "length");
if (JS_IsException(val))
return JS_EXCEPTION;
ret = JS_ToUint32(ctx, &exec_argc, val);
JS_FreeValue(ctx, val);
if (ret)
return JS_EXCEPTION;
/* arbitrary limit to avoid overflow */
if (exec_argc < 1 || exec_argc > 65535) {
return JS_ThrowTypeError(ctx, "invalid number of arguments");
}
exec_argv = js_mallocz(ctx, sizeof(exec_argv[0]) * (exec_argc + 1));
if (!exec_argv)
return JS_EXCEPTION;
for(i = 0; i < exec_argc; i++) {
val = JS_GetPropertyUint32(ctx, args, i);
if (JS_IsException(val))
goto exception;
str = JS_ToCString(ctx, val);
JS_FreeValue(ctx, val);
if (!str)
goto exception;
exec_argv[i] = str;
}
exec_argv[exec_argc] = NULL;
for(i = 0; i < 3; i++)
std_fds[i] = i;
/* get the options, if any */
if (argc >= 2) {
options = argv[1];
if (get_bool_option(ctx, &block_flag, options, "block"))
goto exception;
if (get_bool_option(ctx, &use_path, options, "usePath"))
goto exception;
val = JS_GetPropertyStr(ctx, options, "file");
if (JS_IsException(val))
goto exception;
if (!JS_IsUndefined(val)) {
file = JS_ToCString(ctx, val);
JS_FreeValue(ctx, val);
if (!file)
goto exception;
}
val = JS_GetPropertyStr(ctx, options, "cwd");
if (JS_IsException(val))
goto exception;
if (!JS_IsUndefined(val)) {
cwd = JS_ToCString(ctx, val);
JS_FreeValue(ctx, val);
if (!cwd)
goto exception;
}
/* stdin/stdout/stderr handles */
for(i = 0; i < 3; i++) {
val = JS_GetPropertyStr(ctx, options, std_name[i]);
if (JS_IsException(val))
goto exception;
if (!JS_IsUndefined(val)) {
int fd;
ret = JS_ToInt32(ctx, &fd, val);
JS_FreeValue(ctx, val);
if (ret)
goto exception;
std_fds[i] = fd;
}
}
val = JS_GetPropertyStr(ctx, options, "env");
if (JS_IsException(val))
goto exception;
if (!JS_IsUndefined(val)) {
envp = build_envp(ctx, val);
JS_FreeValue(ctx, val);
if (!envp)
goto exception;
}
val = JS_GetPropertyStr(ctx, options, "uid");
if (JS_IsException(val))
goto exception;
if (!JS_IsUndefined(val)) {
ret = JS_ToUint32(ctx, &uid, val);
JS_FreeValue(ctx, val);
if (ret)
goto exception;
}
val = JS_GetPropertyStr(ctx, options, "gid");
if (JS_IsException(val))
goto exception;
if (!JS_IsUndefined(val)) {
ret = JS_ToUint32(ctx, &gid, val);
JS_FreeValue(ctx, val);
if (ret)
goto exception;
}
}
pid = fork();
if (pid < 0) {
JS_ThrowTypeError(ctx, "fork error");
goto exception;
}
if (pid == 0) {
/* child */
/* remap the stdin/stdout/stderr handles if necessary */
for(i = 0; i < 3; i++) {
if (std_fds[i] != i) {
if (dup2(std_fds[i], i) < 0)
_exit(127);
}
}
#if defined(HAVE_CLOSEFROM)
/* closefrom() is available on many recent unix systems:
Linux with glibc 2.34+, Solaris 9+, FreeBSD 7.3+,
NetBSD 3.0+, OpenBSD 3.5+.
Linux with the musl libc and macOS don't have it.
*/
closefrom(3);
#else
{
/* Close the file handles manually, limit to 1024 to avoid
costly loop on linux Alpine where sysconf(_SC_OPEN_MAX)
returns a huge value 1048576.
Patch inspired by nicolas-duteil-nova. See also:
https://stackoverflow.com/questions/73229353/
https://stackoverflow.com/questions/899038/#918469
*/
int fd_max = min_int(sysconf(_SC_OPEN_MAX), 1024);
for(i = 3; i < fd_max; i++)
close(i);
}
#endif
if (cwd) {
if (chdir(cwd) < 0)
_exit(127);
}
if (uid != -1) {
if (setuid(uid) < 0)
_exit(127);
}
if (gid != -1) {
if (setgid(gid) < 0)
_exit(127);
}
if (!file)
file = exec_argv[0];
if (use_path)
ret = my_execvpe(file, (char **)exec_argv, envp);
else
ret = execve(file, (char **)exec_argv, envp);
_exit(127);
}
/* parent */
if (block_flag) {
for(;;) {
ret = waitpid(pid, &status, 0);
if (ret == pid) {
if (WIFEXITED(status)) {
ret = WEXITSTATUS(status);
break;
} else if (WIFSIGNALED(status)) {
ret = -WTERMSIG(status);
break;
}
}
}
} else {
ret = pid;
}
ret_val = JS_NewInt32(ctx, ret);
done:
JS_FreeCString(ctx, file);
JS_FreeCString(ctx, cwd);
for(i = 0; i < exec_argc; i++)
JS_FreeCString(ctx, exec_argv[i]);
js_free(ctx, exec_argv);
if (envp != environ) {
char **p;
p = envp;
while (*p != NULL) {
js_free(ctx, *p);
p++;
}
js_free(ctx, envp);
}
return ret_val;
exception:
ret_val = JS_EXCEPTION;
goto done;
}