Bumping gaia.json for 1 gaia revision(s) a=gaia-bump
[gecko.git] / js / src / shell / OSObject.cpp
blob6269a31a2cdba9870188e5c4bcd1e907c405a957
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"
11 #include <errno.h>
12 #include <stdlib.h>
13 #ifdef XP_WIN
14 #include <process.h>
15 #include <string.h>
16 #else
17 #include <sys/wait.h>
18 #include <unistd.h>
19 #endif
21 // For JSFunctionSpecWithHelp
22 #include "jsfriendapi.h"
24 using namespace JS;
26 static bool
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");
32 return false;
34 RootedString key(cx, ToString(cx, args[0]));
35 if (!key)
36 return false;
37 JSAutoByteString keyBytes;
38 if (!keyBytes.encodeUtf8(cx, key))
39 return false;
41 if (const char* valueBytes = getenv(keyBytes.ptr())) {
42 RootedString value(cx, JS_NewStringCopyZ(cx, valueBytes));
43 if (!value)
44 return false;
45 args.rval().setString(value);
46 } else {
47 args.rval().setUndefined();
49 return true;
52 static bool
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");
58 return false;
60 args.rval().setInt32(getpid());
61 return true;
64 #if !defined(XP_WIN)
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.
71 inline char*
72 strerror_message(int result, char* buffer)
74 return result == 0 ? buffer : nullptr;
77 inline char*
78 strerror_message(char* result, char* buffer)
80 return result;
83 #endif
85 static void
86 ReportSysError(JSContext* cx, const char* prefix)
88 char buffer[200];
90 #if defined(XP_WIN)
91 strerror_s(buffer, sizeof(buffer), errno);
92 const char* errstr = buffer;
93 #else
94 const char* errstr = strerror_message(strerror_r(errno, buffer, sizeof(buffer)), buffer);
95 #endif
97 if (!errstr)
98 errstr = "unknown error";
100 size_t nbytes = strlen(prefix) + strlen(errstr) + 3;
101 char* final = (char*) js_malloc(nbytes);
102 if (!final) {
103 JS_ReportOutOfMemory(cx);
104 return;
107 #ifdef XP_WIN
108 _snprintf(final, nbytes, "%s: %s", prefix, errstr);
109 #else
110 snprintf(final, nbytes, "%s: %s", prefix, errstr);
111 #endif
113 JS_ReportError(cx, final);
114 js_free(final);
117 static bool
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");
124 return false;
127 JSString* str = JS::ToString(cx, args[0]);
128 if (!str)
129 return false;
131 JSAutoByteString command(cx, str);
132 if (!command)
133 return false;
135 int result = system(command.ptr());
136 if (result == -1) {
137 ReportSysError(cx, "system call failed");
138 return false;
141 args.rval().setInt32(result);
142 return true;
145 #ifndef XP_WIN
146 static bool
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");
153 return false;
156 JSString* str = JS::ToString(cx, args[0]);
157 if (!str)
158 return false;
160 JSAutoByteString command(cx, str);
161 if (!command)
162 return false;
164 int32_t childPid = fork();
165 if (childPid) {
166 args.rval().setInt32(childPid);
167 return true;
170 if (childPid == -1) {
171 ReportSysError(cx, "fork failed");
172 return false;
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);
181 exit(1);
184 static bool
185 os_kill(JSContext* cx, unsigned argc, Value* vp)
187 CallArgs args = CallArgsFromVp(argc, vp);
188 int32_t pid;
189 if (args.length() < 1) {
190 JS_ReportError(cx, "os.kill requires 1 argument");
191 return false;
193 if (!JS::ToInt32(cx, args[0], &pid))
194 return false;
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");
199 return false;
202 int signal = SIGINT;
203 if (args.length() > 1) {
204 if (!JS::ToInt32(cx, args[1], &signal))
205 return false;
208 int status = kill(pid, signal);
209 if (status == -1)
210 ReportSysError(cx, "kill failed");
211 args.rval().setUndefined();
212 return true;
215 static bool
216 os_waitpid(JSContext* cx, unsigned argc, Value* vp)
218 CallArgs args = CallArgsFromVp(argc, vp);
220 int32_t pid;
221 if (args.length() == 0) {
222 pid = -1;
223 } else {
224 if (!JS::ToInt32(cx, args[0], &pid))
225 return false;
228 bool nohang = false;
229 if (args.length() >= 2)
230 nohang = JS::ToBoolean(args[1]);
232 int status;
233 pid_t result = waitpid(pid, &status, nohang ? WNOHANG : 0);
234 if (result == -1) {
235 ReportSysError(cx, "os.waitpid failed");
236 return false;
239 RootedObject info(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
240 if (!info)
241 return false;
243 RootedValue v(cx);
244 if (result != 0) {
245 v.setInt32(result);
246 if (!JS_DefineProperty(cx, info, "pid", v, JSPROP_ENUMERATE))
247 return false;
249 if (WIFEXITED(status)) {
250 v.setInt32(WEXITSTATUS(status));
251 if (!JS_DefineProperty(cx, info, "exitStatus", v, JSPROP_ENUMERATE))
252 return false;
255 args.rval().setObject(*info);
256 return true;
258 #endif
260 static const JSFunctionSpecWithHelp os_functions[] = {
261 JS_FN_HELP("getenv", os_getenv, 1, 0,
262 "getenv(variable)",
263 " Get the value of an environment variable."),
265 JS_FN_HELP("getpid", os_getpid, 0, 0,
266 "getpid()",
267 " Return the current process id."),
269 JS_FN_HELP("system", os_system, 1, 0,
270 "system(command)",
271 " Execute command on the current host, returning result code or throwing an\n"
272 " exception on failure."),
274 #ifndef XP_WIN
275 JS_FN_HELP("spawn", os_spawn, 1, 0,
276 "spawn(command)",
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."),
289 #endif
290 JS_FS_HELP_END
293 bool
294 js::DefineOS(JSContext* cx, HandleObject global)
296 RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
297 return obj &&
298 JS_DefineFunctionsWithHelp(cx, obj, os_functions) &&
299 JS_DefineProperty(cx, global, "os", obj, 0);