mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1098641 - Add os.{getpid,waitpid,spawn,system}, r=jorendorff
This commit is contained in:
parent
6f708df182
commit
12b2fc0bcf
@ -421,7 +421,7 @@ function unsafeVariableAddressTaken(suppressed, variable)
|
||||
|
||||
function computePrintedLines(functionName)
|
||||
{
|
||||
assert(!system("xdbfind src_body.xdb '" + functionName + "' > " + tmpfile));
|
||||
assert(!os.system("xdbfind src_body.xdb '" + functionName + "' > " + tmpfile));
|
||||
var lines = snarf(tmpfile).split('\n');
|
||||
|
||||
for (var body of functionBodies)
|
||||
|
@ -8,7 +8,18 @@
|
||||
|
||||
#include "shell/OSObject.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef XP_WIN
|
||||
#include <process.h>
|
||||
#include <string.h>
|
||||
#else
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
// For JSFunctionSpecWithHelp
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
using namespace JS;
|
||||
|
||||
@ -38,9 +49,239 @@ os_getenv(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static const JSFunctionSpec os_functions[] = {
|
||||
JS_FS("getenv", os_getenv, 1, 0),
|
||||
JS_FS_END
|
||||
static bool
|
||||
os_getpid(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
if (args.length() != 0) {
|
||||
JS_ReportError(cx, "os.getpid takes no arguments");
|
||||
return false;
|
||||
}
|
||||
args.rval().setInt32(getpid());
|
||||
return true;
|
||||
}
|
||||
|
||||
// There are two possible definitions of strerror_r floating around. The GNU
|
||||
// one returns a char* which may or may not be the buffer you passed in. The
|
||||
// other one returns an integer status code, and always writes the result into
|
||||
// the provided buffer.
|
||||
|
||||
static inline char *
|
||||
strerror_message(int result, char *buffer)
|
||||
{
|
||||
return result == 0 ? buffer : nullptr;
|
||||
}
|
||||
|
||||
static inline char *
|
||||
strerror_message(char *result, char *buffer)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
ReportSysError(JSContext *cx, const char *prefix)
|
||||
{
|
||||
static char buffer[200];
|
||||
#if defined(XP_WIN)
|
||||
strerror_s(buffer, sizeof(buffer), errno);
|
||||
const char *errstr = buffer;
|
||||
#else
|
||||
const char *errstr = strerror_message(strerror_r(errno, buffer, sizeof(buffer)), buffer);
|
||||
#endif
|
||||
|
||||
if (!errstr)
|
||||
errstr = "unknown error";
|
||||
|
||||
size_t nbytes = strlen(prefix) + strlen(errstr) + 3;
|
||||
char *final = (char*) js_malloc(nbytes);
|
||||
if (!final) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
_snprintf(final, nbytes, "%s: %s", prefix, errstr);
|
||||
#else
|
||||
snprintf(final, nbytes, "%s: %s", prefix, errstr);
|
||||
#endif
|
||||
JS_ReportError(cx, final);
|
||||
js_free(final);
|
||||
}
|
||||
|
||||
static bool
|
||||
os_system(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (args.length() == 0) {
|
||||
JS_ReportError(cx, "os.system requires 1 argument");
|
||||
return false;
|
||||
}
|
||||
|
||||
JSString *str = JS::ToString(cx, args[0]);
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
JSAutoByteString command(cx, str);
|
||||
if (!command)
|
||||
return false;
|
||||
|
||||
int result = system(command.ptr());
|
||||
if (result == -1) {
|
||||
ReportSysError(cx, "system call failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setInt32(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef XP_WIN
|
||||
static bool
|
||||
os_spawn(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (args.length() == 0) {
|
||||
JS_ReportError(cx, "os.spawn requires 1 argument");
|
||||
return false;
|
||||
}
|
||||
|
||||
JSString *str = JS::ToString(cx, args[0]);
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
JSAutoByteString command(cx, str);
|
||||
if (!command)
|
||||
return false;
|
||||
|
||||
int32_t childPid = fork();
|
||||
if (childPid) {
|
||||
args.rval().setInt32(childPid);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (childPid == -1) {
|
||||
ReportSysError(cx, "fork failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// We are in the child
|
||||
|
||||
const char *cmd[] = {"sh", "-c", nullptr, nullptr};
|
||||
cmd[2] = command.ptr();
|
||||
|
||||
execvp("sh", (char * const*)cmd);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static bool
|
||||
os_kill(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
int32_t pid;
|
||||
if (args.length() < 1) {
|
||||
JS_ReportError(cx, "os.kill requires 1 argument");
|
||||
return false;
|
||||
}
|
||||
if (!JS::ToInt32(cx, args[0], &pid))
|
||||
return false;
|
||||
|
||||
// It is too easy to kill yourself accidentally with os.kill("goose").
|
||||
if (pid == 0 && !args[0].isInt32()) {
|
||||
JS_ReportError(cx, "os.kill requires numeric pid");
|
||||
return false;
|
||||
}
|
||||
|
||||
int signal = SIGINT;
|
||||
if (args.length() > 1) {
|
||||
if (!JS::ToInt32(cx, args[1], &signal))
|
||||
return false;
|
||||
}
|
||||
|
||||
int status = kill(pid, signal);
|
||||
if (status == -1)
|
||||
ReportSysError(cx, "kill failed");
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
os_waitpid(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
int32_t pid;
|
||||
if (args.length() == 0) {
|
||||
pid = -1;
|
||||
} else {
|
||||
if (!JS::ToInt32(cx, args[0], &pid))
|
||||
return false;
|
||||
}
|
||||
|
||||
bool nohang = false;
|
||||
if (args.length() >= 2)
|
||||
nohang = JS::ToBoolean(args[1]);
|
||||
|
||||
int status;
|
||||
pid_t result = waitpid(pid, &status, nohang ? WNOHANG : 0);
|
||||
if (result == -1) {
|
||||
ReportSysError(cx, "os.waitpid failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject info(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
|
||||
if (!info)
|
||||
return false;
|
||||
|
||||
RootedValue v(cx);
|
||||
if (result != 0) {
|
||||
v.setInt32(result);
|
||||
if (!JS_DefineProperty(cx, info, "pid", v, JSPROP_ENUMERATE))
|
||||
return false;
|
||||
}
|
||||
if (WIFEXITED(status)) {
|
||||
v.setInt32(WEXITSTATUS(status));
|
||||
if (!JS_DefineProperty(cx, info, "exitStatus", v, JSPROP_ENUMERATE))
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setObject(*info);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const JSFunctionSpecWithHelp os_functions[] = {
|
||||
JS_FN_HELP("getenv", os_getenv, 1, 0,
|
||||
"getenv(variable)",
|
||||
" Get the value of an environment variable."),
|
||||
|
||||
JS_FN_HELP("getpid", os_getpid, 0, 0,
|
||||
"getpid()",
|
||||
" Return the current process id."),
|
||||
|
||||
JS_FN_HELP("system", os_system, 1, 0,
|
||||
"system(command)",
|
||||
" Execute command on the current host, returning result code or throwing an\n"
|
||||
" exception on failure."),
|
||||
|
||||
#ifndef XP_WIN
|
||||
JS_FN_HELP("spawn", os_spawn, 1, 0,
|
||||
"spawn(command)",
|
||||
" Start up a separate process running the given command. Returns the pid."),
|
||||
|
||||
JS_FN_HELP("kill", os_kill, 1, 0,
|
||||
"kill(pid[, signal])",
|
||||
" Send a signal to the given pid. The default signal is SIGINT. The signal\n"
|
||||
" passed in must be numeric, if given."),
|
||||
|
||||
JS_FN_HELP("waitpid", os_waitpid, 1, 0,
|
||||
"waitpid(pid[, nohang])",
|
||||
" Calls waitpid(). 'nohang' is a boolean indicating whether to pass WNOHANG.\n"
|
||||
" The return value is an object containing a 'pid' field, if a process was waitable\n"
|
||||
" and an 'exitStatus' field if a pid exited."),
|
||||
#endif
|
||||
JS_FS_HELP_END
|
||||
};
|
||||
|
||||
bool
|
||||
@ -48,6 +289,6 @@ js::DefineOS(JSContext *cx, HandleObject global)
|
||||
{
|
||||
RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
|
||||
return obj &&
|
||||
JS_DefineFunctions(cx, obj, os_functions) &&
|
||||
JS_DefineFunctionsWithHelp(cx, obj, os_functions) &&
|
||||
JS_DefineProperty(cx, global, "os", obj, 0);
|
||||
}
|
||||
|
@ -3575,30 +3575,6 @@ RedirectOutput(JSContext *cx, unsigned argc, jsval *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
System(JSContext *cx, unsigned argc, jsval *vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (args.length() == 0) {
|
||||
JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
|
||||
"system");
|
||||
return false;
|
||||
}
|
||||
|
||||
JSString *str = JS::ToString(cx, args[0]);
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
JSAutoByteString command(cx, str);
|
||||
if (!command)
|
||||
return false;
|
||||
|
||||
int result = system(command.ptr());
|
||||
args.rval().setInt32(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int sArgc;
|
||||
static char **sArgv;
|
||||
|
||||
@ -4565,10 +4541,6 @@ static const JSFunctionSpecWithHelp fuzzing_unsafe_functions[] = {
|
||||
" Redirect stdout and/or stderr to the named file. Pass undefined to avoid\n"
|
||||
" redirecting. Filenames are relative to the current working directory."),
|
||||
|
||||
JS_FN_HELP("system", System, 1, 0,
|
||||
"system(command)",
|
||||
" Execute command on the current host, returning result code."),
|
||||
|
||||
JS_FN_HELP("nestedShell", NestedShell, 0, 0,
|
||||
"nestedShell(shellArgs...)",
|
||||
" Execute the given code in a new JS shell process, passing this nested shell\n"
|
||||
|
1
js/src/tests/shell/README
Normal file
1
js/src/tests/shell/README
Normal file
@ -0,0 +1 @@
|
||||
Tests for JS shell-only functions
|
39
js/src/tests/shell/os.js
Normal file
39
js/src/tests/shell/os.js
Normal file
@ -0,0 +1,39 @@
|
||||
// |reftest| skip-if(!xulRuntime.shell||xulRuntime.OS=="WINNT")
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
*/
|
||||
|
||||
var pid = os.getpid();
|
||||
assertEq(pid > 0, true);
|
||||
|
||||
var PATH = os.getenv("PATH");
|
||||
assertEq(PATH.indexOf("bin") > 0, true);
|
||||
assertEq(os.getenv("SQUAMMISH_HILLBILLY_GOAT_SQUEEZERS"), undefined);
|
||||
|
||||
assertEq(os.system("true"), 0, "/bin/true should exit 0");
|
||||
assertEq(os.system("false") != 0, true, "/bin/false should exit nonzero");
|
||||
|
||||
var kidpid = os.spawn("sleep 3600");
|
||||
assertEq(kidpid > 0, true, "spawning sleep");
|
||||
var info = os.waitpid(kidpid, true);
|
||||
assertEq(info.hasOwnProperty("pid"), false);
|
||||
assertEq(info.hasOwnProperty("exitStatus"), false);
|
||||
|
||||
os.kill(kidpid);
|
||||
|
||||
info = os.waitpid(kidpid);
|
||||
assertEq(info.hasOwnProperty("pid"), true, "waiting on dead process should return pid");
|
||||
assertEq(info.pid, kidpid);
|
||||
assertEq(info.hasOwnProperty("exitStatus"), false, "killed process should not have exitStatus");
|
||||
|
||||
kidpid = os.spawn("false");
|
||||
assertEq(kidpid > 0, true, "spawning /bin/false");
|
||||
info = os.waitpid(kidpid);
|
||||
assertEq(info.hasOwnProperty("pid"), true, "waiting on dead process should return pid");
|
||||
assertEq(info.pid, kidpid);
|
||||
assertEq(info.hasOwnProperty("exitStatus"), true, "process should have exitStatus");
|
||||
assertEq(info.exitStatus, 1, "/bin/false should exit 1");
|
||||
|
||||
reportCompare(true, true);
|
0
js/src/tests/shell/shell.js
Normal file
0
js/src/tests/shell/shell.js
Normal file
Loading…
Reference in New Issue
Block a user