1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 // OSObject.h - os object for exposing posix system calls in the JS shell
9 #include "shell/OSObject.h"
21 // For JSFunctionSpecWithHelp
22 #include "jsfriendapi.h"
27 os_getenv(JSContext
* cx
, unsigned argc
, Value
* vp
)
29 CallArgs args
= CallArgsFromVp(argc
, vp
);
30 if (args
.length() < 1) {
31 JS_ReportError(cx
, "os.getenv requires 1 argument");
34 RootedString
key(cx
, ToString(cx
, args
[0]));
37 JSAutoByteString keyBytes
;
38 if (!keyBytes
.encodeUtf8(cx
, key
))
41 if (const char* valueBytes
= getenv(keyBytes
.ptr())) {
42 RootedString
value(cx
, JS_NewStringCopyZ(cx
, valueBytes
));
45 args
.rval().setString(value
);
47 args
.rval().setUndefined();
53 os_getpid(JSContext
* cx
, unsigned argc
, Value
* vp
)
55 CallArgs args
= CallArgsFromVp(argc
, vp
);
56 if (args
.length() != 0) {
57 JS_ReportError(cx
, "os.getpid takes no arguments");
60 args
.rval().setInt32(getpid());
66 // There are two possible definitions of strerror_r floating around. The GNU
67 // one returns a char* which may or may not be the buffer you passed in. The
68 // other one returns an integer status code, and always writes the result into
69 // the provided buffer.
72 strerror_message(int result
, char* buffer
)
74 return result
== 0 ? buffer
: nullptr;
78 strerror_message(char* result
, char* buffer
)
86 ReportSysError(JSContext
* cx
, const char* prefix
)
91 strerror_s(buffer
, sizeof(buffer
), errno
);
92 const char* errstr
= buffer
;
94 const char* errstr
= strerror_message(strerror_r(errno
, buffer
, sizeof(buffer
)), buffer
);
98 errstr
= "unknown error";
100 size_t nbytes
= strlen(prefix
) + strlen(errstr
) + 3;
101 char* final
= (char*) js_malloc(nbytes
);
103 JS_ReportOutOfMemory(cx
);
108 _snprintf(final
, nbytes
, "%s: %s", prefix
, errstr
);
110 snprintf(final
, nbytes
, "%s: %s", prefix
, errstr
);
113 JS_ReportError(cx
, final
);
118 os_system(JSContext
* cx
, unsigned argc
, jsval
* vp
)
120 CallArgs args
= CallArgsFromVp(argc
, vp
);
122 if (args
.length() == 0) {
123 JS_ReportError(cx
, "os.system requires 1 argument");
127 JSString
* str
= JS::ToString(cx
, args
[0]);
131 JSAutoByteString
command(cx
, str
);
135 int result
= system(command
.ptr());
137 ReportSysError(cx
, "system call failed");
141 args
.rval().setInt32(result
);
147 os_spawn(JSContext
* cx
, unsigned argc
, jsval
* vp
)
149 CallArgs args
= CallArgsFromVp(argc
, vp
);
151 if (args
.length() == 0) {
152 JS_ReportError(cx
, "os.spawn requires 1 argument");
156 JSString
* str
= JS::ToString(cx
, args
[0]);
160 JSAutoByteString
command(cx
, str
);
164 int32_t childPid
= fork();
166 args
.rval().setInt32(childPid
);
170 if (childPid
== -1) {
171 ReportSysError(cx
, "fork failed");
175 // We are in the child
177 const char* cmd
[] = {"sh", "-c", nullptr, nullptr};
178 cmd
[2] = command
.ptr();
180 execvp("sh", (char * const*)cmd
);
185 os_kill(JSContext
* cx
, unsigned argc
, Value
* vp
)
187 CallArgs args
= CallArgsFromVp(argc
, vp
);
189 if (args
.length() < 1) {
190 JS_ReportError(cx
, "os.kill requires 1 argument");
193 if (!JS::ToInt32(cx
, args
[0], &pid
))
196 // It is too easy to kill yourself accidentally with os.kill("goose").
197 if (pid
== 0 && !args
[0].isInt32()) {
198 JS_ReportError(cx
, "os.kill requires numeric pid");
203 if (args
.length() > 1) {
204 if (!JS::ToInt32(cx
, args
[1], &signal
))
208 int status
= kill(pid
, signal
);
210 ReportSysError(cx
, "kill failed");
211 args
.rval().setUndefined();
216 os_waitpid(JSContext
* cx
, unsigned argc
, Value
* vp
)
218 CallArgs args
= CallArgsFromVp(argc
, vp
);
221 if (args
.length() == 0) {
224 if (!JS::ToInt32(cx
, args
[0], &pid
))
229 if (args
.length() >= 2)
230 nohang
= JS::ToBoolean(args
[1]);
233 pid_t result
= waitpid(pid
, &status
, nohang
? WNOHANG
: 0);
235 ReportSysError(cx
, "os.waitpid failed");
239 RootedObject
info(cx
, JS_NewObject(cx
, nullptr, JS::NullPtr(), JS::NullPtr()));
246 if (!JS_DefineProperty(cx
, info
, "pid", v
, JSPROP_ENUMERATE
))
249 if (WIFEXITED(status
)) {
250 v
.setInt32(WEXITSTATUS(status
));
251 if (!JS_DefineProperty(cx
, info
, "exitStatus", v
, JSPROP_ENUMERATE
))
255 args
.rval().setObject(*info
);
260 static const JSFunctionSpecWithHelp os_functions
[] = {
261 JS_FN_HELP("getenv", os_getenv
, 1, 0,
263 " Get the value of an environment variable."),
265 JS_FN_HELP("getpid", os_getpid
, 0, 0,
267 " Return the current process id."),
269 JS_FN_HELP("system", os_system
, 1, 0,
271 " Execute command on the current host, returning result code or throwing an\n"
272 " exception on failure."),
275 JS_FN_HELP("spawn", os_spawn
, 1, 0,
277 " Start up a separate process running the given command. Returns the pid."),
279 JS_FN_HELP("kill", os_kill
, 1, 0,
280 "kill(pid[, signal])",
281 " Send a signal to the given pid. The default signal is SIGINT. The signal\n"
282 " passed in must be numeric, if given."),
284 JS_FN_HELP("waitpid", os_waitpid
, 1, 0,
285 "waitpid(pid[, nohang])",
286 " Calls waitpid(). 'nohang' is a boolean indicating whether to pass WNOHANG.\n"
287 " The return value is an object containing a 'pid' field, if a process was waitable\n"
288 " and an 'exitStatus' field if a pid exited."),
294 js::DefineOS(JSContext
* cx
, HandleObject global
)
296 RootedObject
obj(cx
, JS_NewObject(cx
, nullptr, JS::NullPtr(), JS::NullPtr()));
298 JS_DefineFunctionsWithHelp(cx
, obj
, os_functions
) &&
299 JS_DefineProperty(cx
, global
, "os", obj
, 0);