diff --git a/quickjs-libc.c b/quickjs-libc.c index d08ff7f74..d8c48bfd8 100644 --- a/quickjs-libc.c +++ b/quickjs-libc.c @@ -3101,20 +3101,36 @@ static JSValue js_os_readlink(JSContext *ctx, JSValueConst this_val, JS_FreeCString(ctx, path); return make_string_error(ctx, buf, err); } +#endif +#if !defined(__wasi__) +#if defined(_WIN32) +static char *build_envp(JSContext *ctx, JSValue obj) +{ +#define MS_ENVP_BUFFSIZE 4096 // this does automatically double as needed + size_t key_len, str_len, buff_len = MS_ENVP_BUFFSIZE; + char *envp, *pair; +#else static char **build_envp(JSContext *ctx, JSValue obj) { - uint32_t len, i; - JSPropertyEnum *tab; + size_t key_len, str_len; char **envp, *pair; +#endif // WIN32 const char *key, *str; + uint32_t len, i; + JSPropertyEnum *tab; JSValue val; - size_t key_len, str_len; + if (JS_GetOwnPropertyNames(ctx, &tab, &len, obj, JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0) return NULL; +#ifdef _WIN32 + envp = js_mallocz(ctx,buff_len); + pair = envp; +#else envp = js_mallocz(ctx, sizeof(envp[0]) * ((size_t)len + 1)); +#endif if (!envp) goto fail; for(i = 0; i < len; i++) { @@ -3132,17 +3148,33 @@ static char **build_envp(JSContext *ctx, JSValue obj) } key_len = strlen(key); str_len = strlen(str); +#if defined(_WIN32) + /* syntax highither AND MSVC AND ClangCl would not handle char* + size_t + size_t */ + if (&pair[key_len + str_len + 2] > &envp[buff_len]) { + size_t buff_len2 = buff_len; + buff_len *= 2; + char *envp2 = envp; + envp = js_mallocz(ctx,buff_len); + memcpy(envp, envp2, buff_len2); + js_free(ctx, envp2); + }; +#else pair = js_malloc(ctx, key_len + str_len + 2); if (!pair) { JS_FreeCString(ctx, key); JS_FreeCString(ctx, str); goto fail; } +#endif // _WIN32 memcpy(pair, key, key_len); pair[key_len] = '='; memcpy(pair + key_len + 1, str, str_len); pair[key_len + 1 + str_len] = '\0'; +#if defined(_WIN32) + pair += key_len + str_len + 2; +#else envp[i] = pair; +#endif JS_FreeCString(ctx, key); JS_FreeCString(ctx, str); } @@ -3153,14 +3185,18 @@ static char **build_envp(JSContext *ctx, JSValue obj) return envp; fail: if (envp) { +#if !defined(_WIN32) for(i = 0; i < len; i++) js_free(ctx, envp[i]); +#endif // !_WIN32 js_free(ctx, envp); envp = NULL; } goto done; } +#endif // !(__wasi__) +#if !defined(_WIN32) && !defined(__wasi__) /* execvpe is not available on non GNU systems */ static int my_execvpe(const char *filename, char **argv, char **envp) { @@ -3228,7 +3264,7 @@ static void js_os_exec_once_init(void) *(void **) (&js_os_exec_closefrom) = dlsym(RTLD_DEFAULT, "closefrom"); } -#endif +#endif // !defined(EMSCRIPTEN) && !defined(__wasi__) /* exec(args[, options]) -> exitcode */ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, @@ -3236,13 +3272,15 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, { JSValueConst options, args = argv[0]; JSValue val, ret_val; - const char **exec_argv, *file = NULL, *str, *cwd = NULL; - char **envp = environ; + const char *file = NULL, *str, *cwd = NULL; uint32_t exec_argc, i; - int ret, pid, status; - bool block_flag = true, use_path = true; + int ret, pid; + bool block_flag = true, use_path = true, inherit = false; static const char *std_name[3] = { "stdin", "stdout", "stderr" }; int std_fds[3]; + char **envp = environ; + const char **exec_argv; + int status; uint32_t uid = -1, gid = -1; int ngroups = -1; gid_t groups[64]; @@ -3284,6 +3322,8 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, goto exception; if (get_bool_option(ctx, &use_path, options, "usePath")) goto exception; + if (get_bool_option(ctx, &inherit, options, "inherit")) + goto exception; val = JS_GetPropertyStr(ctx, options, "file"); if (JS_IsException(val)) @@ -3311,6 +3351,7 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, if (JS_IsException(val)) goto exception; if (!JS_IsUndefined(val)) { + inherit = true; int fd; ret = JS_ToInt32(ctx, &fd, val); JS_FreeValue(ctx, val); @@ -3329,7 +3370,7 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, if (!envp) goto exception; } - + val = JS_GetPropertyStr(ctx, options, "uid"); if (JS_IsException(val)) goto exception; @@ -3339,7 +3380,7 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, if (ret) goto exception; } - + val = JS_GetPropertyStr(ctx, options, "gid"); if (JS_IsException(val)) goto exception; @@ -3382,10 +3423,10 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, if (idx < len) goto exception; } - + } -#if !defined(EMSCRIPTEN) && !defined(__wasi__) +#if !defined(EMSCRIPTEN) && !defined(__wasi__) // should happen pre-fork because it calls dlsym() // and that's not an async-signal-safe function js_once(&js_os_exec_once, js_os_exec_once_init); @@ -3401,9 +3442,15 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, /* 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) + if ( dup2(std_fds[i], i) < 0) _exit(127); - } + close (std_fds[i]); + } else if (!inherit) { + int fd = open("/dev/null", O_WRONLY); + if ( dup2(fd, i) < 0) + _exit(127); + close(fd); + }; } if (js_os_exec_closefrom) { @@ -3477,6 +3524,303 @@ static JSValue js_os_exec(JSContext *ctx, JSValueConst this_val, ret_val = JS_EXCEPTION; goto done; } +#endif + +#if defined(_WIN32) +/* exec(args[, options]) -> exitcode */ +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 *file = NULL, *str, *cwd = NULL; + uint32_t exec_argc, i, besafe; + int32_t excode, dwait; + int ret, pid; + bool block_flag = true, use_path = true, inherit = false; + static const char *std_name[3] = { "stdin", "stdout", "stderr" }; + int std_fds[3]; + int cmd_ind, cmd_strlen; + char cmd_buff [JS__PATH_MAX]; + char *envp = 0; + + 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; + if (get_bool_option(ctx, &inherit, options, "inherit")) + 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; + inherit = true; + } + } + + 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; + } + } + + cmd_ind = 0; + if (file) + cmd_strlen = strlen(file); + else + cmd_strlen = 0; + if (cmd_strlen) { + strncpy(&cmd_buff[cmd_ind], file, cmd_strlen); + cmd_ind += cmd_strlen; + }; + + val = JS_GetPropertyStr(ctx, args, "length"); + if (JS_IsException(val)) + goto exception; + ret = JS_ToUint32(ctx, &exec_argc, val); + JS_FreeValue(ctx, val); + if (ret) + goto exception; + /* arbitrary limit to avoid overflow */ + if (exec_argc < 1 || exec_argc > 65535) { + JS_ThrowTypeError(ctx, "invalid number of arguments"); + goto 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; + cmd_strlen = strlen(str); + if (cmd_strlen) { + if ((i != 0) || (file != 0)) { + cmd_buff[cmd_ind] = ' '; + cmd_ind++; + }; + if ( (cmd_ind + cmd_strlen + 1) > JS__PATH_MAX ) { + JS_ThrowRangeError(ctx, "exec command line too long."); + goto exception; + }; + strncpy(&cmd_buff[cmd_ind], str, cmd_strlen); + cmd_ind += cmd_strlen; + }; + } + cmd_buff[cmd_ind] = 0; + + int cflags = NORMAL_PRIORITY_CLASS; + STARTUPINFO istart; + PROCESS_INFORMATION iproc; + memset(&iproc, 0, sizeof(iproc)); + memset(&istart, 0, sizeof(istart) ); + istart.cb = sizeof(istart); + + if (inherit) + { + istart.dwFlags = STARTF_USESTDHANDLES; + + istart.hStdInput = (HANDLE) _get_osfhandle( std_fds[0] ); + if (istart.hStdInput == INVALID_HANDLE_VALUE) { + JS_ThrowInternalError(ctx, "failed to associate stdin of process"); + goto exception; + } + + istart.hStdOutput = (HANDLE) _get_osfhandle( std_fds[1] ); + if (istart.hStdOutput == INVALID_HANDLE_VALUE) { + JS_ThrowInternalError(ctx, "failed to associate stdout of process"); + goto exception; + } + + istart.hStdError = (HANDLE) _get_osfhandle( std_fds[2] ); + if (istart.hStdError == INVALID_HANDLE_VALUE) { + JS_ThrowInternalError(ctx, "failed to associate stderr of process"); + goto exception; + } + } else cflags |= CREATE_NO_WINDOW; + + + besafe = excode = 0; + if (!CreateProcessA(file, cmd_buff, 0, 0, true, cflags, envp, cwd, &istart, &iproc) ) { + excode = GetLastError(); + ret_val = JS_NewInt32(ctx, excode); + } else if (block_flag) { + while (block_flag) { + dwait = WaitForSingleObject(iproc.hProcess, INFINITE); + if (dwait == WAIT_OBJECT_0) { + if (!GetExitCodeProcess (iproc.hProcess, (DWORD*) &excode)) + excode = GetLastError(); + ret_val = JS_NewInt32(ctx, (int32_t) excode); + block_flag = 0; + } else { + besafe++; + if (besafe==20) { + JS_ThrowPlainError(ctx, "exec process did not complete after %d iterations.", besafe); + goto exception; + }; + }; + }; + CloseHandle(iproc.hProcess); + CloseHandle(iproc.hThread); + } else { + pid = iproc.dwProcessId; + ret_val = JS_NewInt32(ctx, pid); + }; + + done: + JS_FreeCString(ctx, file); + JS_FreeCString(ctx, cwd); + if (envp) js_free(ctx, envp); + return ret_val; + exception: + ret_val = JS_EXCEPTION; + goto done; +} + +/* pipe() -> [read_fd, write_fd] or null if error */ +static JSValue js_os_pipe(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int pipe_fds[2], ret; + JSValue obj; + HANDLE pipe_handles[2]; + struct _SECURITY_ATTRIBUTES secur; + secur.nLength = sizeof(secur); + secur.lpSecurityDescriptor = 0; + secur.bInheritHandle = true; +#define WIN32_PIPE_BUFFSIZE 4096 + ret = CreatePipe(&pipe_handles[0], &pipe_handles[1], &secur, WIN32_PIPE_BUFFSIZE); + if (ret == 0) + return JS_NULL; + pipe_fds[0] = _open_osfhandle( (intptr_t) pipe_handles[0], 0 ); + if (pipe_fds[0] == -1 ) { + CloseHandle(pipe_handles[0]); + return JS_NULL; + } + pipe_fds[1] = _open_osfhandle( (intptr_t) pipe_handles[1], 0 ); + if (pipe_fds[1] == -1 ) { + CloseHandle(pipe_handles[1]); + return JS_NULL; + } + obj = JS_NewArray(ctx); + if (JS_IsException(obj)) + return obj; + JS_DefinePropertyValueUint32(ctx, obj, 0, JS_NewInt32(ctx, pipe_fds[0]), + JS_PROP_C_W_E); + JS_DefinePropertyValueUint32(ctx, obj, 1, JS_NewInt32(ctx, pipe_fds[1]), + JS_PROP_C_W_E); + return obj; +} + +/* kill(pid) -> (NULL | err) : works like standard c/linux kill except signal is ignored */ +static JSValue js_os_kill(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int pid; + BOOL ret; + HANDLE ph; + DWORD flags = PROCESS_TERMINATE; + if (JS_ToInt32(ctx, &pid, argv[0])) + return JS_EXCEPTION; + ph = OpenProcess(flags, false, (DWORD) pid); + if (!ph) + return JS_NewInt32(ctx, GetLastError()); + ret = TerminateProcess(ph, 0); + CloseHandle(ph); + if (ret) + JS_NewInt32(ctx, 0); + int err = GetLastError(); + return JS_NewInt32(ctx,err); +} + + +/* watchpid(pid, blocking) -> ret: < 0 = -error | ret: = 0 still waiting | ret: = pid complete + hybrid clone of waitpid that will work with Win32 + GNU systems except there's no status */ +static JSValue js_os_watchpid(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int pid; + HANDLE ph; + int block = 0, ret; + DWORD options = 0, flags = PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE; + if (JS_ToInt32(ctx, &pid, argv[0])) + return JS_EXCEPTION; + if ( (argc > 1) && (JS_ToInt32(ctx, &block, argv[1]))) + return JS_EXCEPTION; + if (block==1) + options = INFINITE; + ph = OpenProcess(flags, false, (DWORD) pid); + if (!ph) + return JS_NewInt32(ctx, -GetLastError()); + ret = WaitForSingleObject((HANDLE) ph, options); + CloseHandle(ph); + if (ret == WAIT_TIMEOUT) + return JS_NewInt32(ctx, 0); // timed out + if (ret != 0) + return JS_NewInt32(ctx, -GetLastError()); + return JS_NewInt32(ctx, pid); +} +#endif // _WIN32 + +#if !defined(_WIN32) && !defined(__wasi__) +static JSValue js_os_watchpid(JSContext *ctx, JSValueConst this_val, + int argc, JSValueConst *argv) +{ + int pid, status, block = 0, options = 0, ret; + if (JS_ToInt32(ctx, &pid, argv[0])) + return JS_EXCEPTION; + if ((argc > 1) && (JS_ToInt32(ctx, &block, argv[1]))) + return JS_EXCEPTION; + if (!block) options = WNOHANG; + ret = waitpid(pid, &status, options); + if (ret == 0) + return JS_NewInt32(ctx, 0); + if (ret==pid) + return JS_NewInt32(ctx, pid); + return JS_NewInt32(ctx, -errno); +} /* getpid() -> pid */ static JSValue js_os_getpid(JSContext *ctx, JSValueConst this_val, @@ -4113,17 +4457,18 @@ static const JSCFunctionListEntry js_os_funcs[] = { JS_CFUNC_DEF("sleep", 1, js_os_sleep ), #if !defined(__wasi__) JS_CFUNC_DEF("realpath", 1, js_os_realpath ), + JS_CFUNC_DEF("exec", 1, js_os_exec ), + JS_CFUNC_DEF("watchpid", 2, js_os_watchpid ), + JS_CFUNC_DEF("pipe", 0, js_os_pipe ), + JS_CFUNC_DEF("kill", 2, js_os_kill ), #endif #if !defined(_WIN32) && !defined(__wasi__) JS_CFUNC_MAGIC_DEF("lstat", 1, js_os_stat, 1 ), JS_CFUNC_DEF("symlink", 2, js_os_symlink ), JS_CFUNC_DEF("readlink", 1, js_os_readlink ), - JS_CFUNC_DEF("exec", 1, js_os_exec ), JS_CFUNC_DEF("getpid", 0, js_os_getpid ), JS_CFUNC_DEF("waitpid", 2, js_os_waitpid ), OS_FLAG(WNOHANG), - JS_CFUNC_DEF("pipe", 0, js_os_pipe ), - JS_CFUNC_DEF("kill", 2, js_os_kill ), JS_CFUNC_DEF("dup", 1, js_os_dup ), JS_CFUNC_DEF("dup2", 2, js_os_dup2 ), #endif diff --git a/tests.conf b/tests.conf index b006e1666..9faea62d5 100644 --- a/tests.conf +++ b/tests.conf @@ -8,3 +8,4 @@ tests/empty.js tests/fixture_cyclic_import.js tests/microbench.js tests/test_worker_module.js +tests/test_os_exec_child.js diff --git a/tests/test_os_exec.js b/tests/test_os_exec.js new file mode 100644 index 000000000..e53918ec8 --- /dev/null +++ b/tests/test_os_exec.js @@ -0,0 +1,107 @@ +import * as std from "qjs:std"; +import * as os from "qjs:os"; +import { assert } from "./assert.js"; + +const isWin = os.platform === 'win32'; +const isCygwin = os.platform === 'cygwin'; + +function test_os_exec() { + var ret, fdout, fderr, pid, f, status, amend, exe, dir; + + /* cmd.exe does leave a \r that getline doesn't trim */ + const osTrimLine = ( isWin ? "\r" : "" ); + const osShellCmd = ( isWin ? "cmd" : "/bin/sh" ); + const osShellFlag = ( isWin ? "/c" : "-c" ); + const osPathSeparator = ( isWin ? "\\" : "/" ); + const osStallProgram = ( isWin ? "cmd /k" : "cat" ); + + pid = os.exec( [osShellCmd, osShellFlag, ":"], { block: false} ); + /* watchpid is similar waitpid on WIN32 + GNU but lacks a status indicator + watchpid(p,0) == waitpid(p, WNOHANG), watchpid(p,1) == waitpid(p,0) */ + ret = os.watchpid(pid, 1); + assert(ret, pid); + + fdout = os.pipe(); + const osShellEchoParam = ( isWin ? 'echo %FOO%' : 'echo $FOO' ); + pid = os.exec( [osShellCmd, osShellFlag, osShellEchoParam ], { + stdout: fdout[1], + block: false, + env: { FOO: "hello" }, + } ); + assert(pid >= 0); + os.close(fdout[1]); + f = std.fdopen(fdout[0], "r"); + assert(f.getline(), "hello" + osTrimLine); + assert(f.getline(), null); + f.close(); + if (!isWin) { + [ret, status] = os.waitpid(pid, 0); + assert(ret, pid); + assert(status & 0x7f, 0); /* exited */ + assert(status >> 8, 0); /* exit code */ + } + + pid = os.exec([osStallProgram], { block: false } ); + assert(pid >= 0); + ret = os.watchpid(pid, 0); + assert(ret, 0); + /* os.kill in WIN32 Just does TerminateProcess & signal control is ignored. */ + os.kill(pid, os.SIGTERM); + os.sleep(0); // watcpid is too fast! you have to sleep or check it again. + ret = os.watchpid(pid, 1); + assert(ret, pid); + + if (!isWin) { + pid = os.exec([osStallProgram], { block: false } ); + assert(pid >= 0); + [ret, status] = os.waitpid(pid, os.WNOHANG); + assert(ret, 0); + os.kill(pid, os.SIGTERM); + [ret, status] = os.waitpid(pid, 0); + assert(ret, pid); + // Flaky on cygwin for unclear reasons, see + // https://github.com/quickjs-ng/quickjs/issues/184 + if (!isCygwin) { + assert(status & 0x7f, os.SIGTERM); + } + } + + amend = os.exePath().split(osPathSeparator); + amend.pop(); + amend.push("qjs"); + exe = amend.join(osPathSeparator); + [dir, ret] = os.getcwd(); + amend = dir.split(osPathSeparator); + if (amend[amend.length - 1] == "build") + amend.pop(); + if (amend[amend.length - 1] != "tests") { + amend.push("tests"); + dir = amend.join(osPathSeparator); + }; + + pid = os.exec( [ exe,"-q"], { block: true } ); + assert(pid, 0); + + fdout = os.pipe(); + fderr = os.pipe(); + pid = os.exec( [exe, "test_os_exec_child.js"], { + block: true, + stdout: fdout[1], + stderr: fderr[1], + cwd: dir, + } ); + assert(pid, 0); + os.close(fdout[1]); + os.close(fderr[1]); + f = std.fdopen(fdout[0], "r"); + assert(f.getline(), "shell text passes through inherited stdio" + osTrimLine); + assert(f.getline(), null); + f.close(); + f = std.fdopen(fderr[0], "r"); + ret = f.getline().indexOf('bad'); + assert(ret > 0); + f.close(); + +}; + +test_os_exec(); diff --git a/tests/test_os_exec_child.js b/tests/test_os_exec_child.js new file mode 100644 index 000000000..af7b33cbe --- /dev/null +++ b/tests/test_os_exec_child.js @@ -0,0 +1,45 @@ +import * as std from "qjs:std"; +import * as os from "qjs:os"; +import { assert } from "./assert.js"; + +const isWin = os.platform === 'win32'; + +function test_os_processes() { + var ret; + + const osShellCmd = ( isWin ? "cmd" : "/bin/sh" ); + const osShellFlag = ( isWin ? "/c" : "-c" ); + + ret = os.exec( [osShellCmd, osShellFlag, "exit 2"], { usePath: false } ); + assert(ret, 2 ); + + ret = os.exec( [osShellCmd, osShellFlag, ": good commands return 0"] ); + assert(ret, 0); + + ret = os.exec( [osShellCmd, osShellFlag, "errors return respective error codes"], ); + assert( ret, ( isWin ? 1 : 127 ) ); + + ret = os.exec( [osShellCmd, osShellFlag, "echo stdio not inherited by default"] ); + assert(ret, 0); + + ret = os.exec( [osShellCmd, osShellFlag, "bad command or filename @@ stdio inherited"], + { inherit: true } + ); + assert( ret, ( isWin ? 1 : 127 ) ); + + ret = os.exec( ["echo invalid process names fail and do not report to stdio"], + { inherit: true } + ); + assert( ret, ( isWin ? 2 : 127 ) ); + + ret = os.exec( ["exit 1"], { usePath: false } ); + assert(ret, ( isWin ? 2 : 127 ) ); + + ret = os.exec( [osShellCmd, osShellFlag, "echo shell text passes through inherited stdio"], + { inherit: true } + ); + assert(ret, 0); + +}; + +test_os_processes(); diff --git a/tests/test_std.js b/tests/test_std.js index 32648ef57..79018de7d 100644 --- a/tests/test_std.js +++ b/tests/test_std.js @@ -201,45 +201,6 @@ function test_os() assert(os.remove(fdir) === 0); } -function test_os_exec() -{ - var ret, fds, pid, f, status; - - ret = os.exec(["true"]); - assert(ret, 0); - - ret = os.exec(["/bin/sh", "-c", "exit 1"], { usePath: false }); - assert(ret, 1); - - fds = os.pipe(); - pid = os.exec(["sh", "-c", "echo $FOO"], { - stdout: fds[1], - block: false, - env: { FOO: "hello" }, - } ); - assert(pid >= 0); - os.close(fds[1]); /* close the write end (as it is only in the child) */ - f = std.fdopen(fds[0], "r"); - assert(f.getline(), "hello"); - assert(f.getline(), null); - f.close(); - [ret, status] = os.waitpid(pid, 0); - assert(ret, pid); - assert(status & 0x7f, 0); /* exited */ - assert(status >> 8, 0); /* exit code */ - - pid = os.exec(["cat"], { block: false } ); - assert(pid >= 0); - os.kill(pid, os.SIGTERM); - [ret, status] = os.waitpid(pid, 0); - assert(ret, pid); - // Flaky on cygwin for unclear reasons, see - // https://github.com/quickjs-ng/quickjs/issues/184 - if (!isCygwin) { - assert(status & 0x7f, os.SIGTERM); - } -} - function test_interval() { var t = os.setInterval(f, 1); @@ -293,7 +254,6 @@ test_file2(); test_getline(); test_popen(); test_os(); -!isWin && test_os_exec(); test_interval(); test_timeout(); test_timeout_order();