blob: fb14f95f4269b0b2efb11350e8d5599c93307bf8 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2009 the V8 project authors. All rights reserved.
Ben Murdochb8a8cc12014-11-26 15:28:44 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
Steve Blocka7e24c12009-10-30 11:49:00 +00004
Steve Blocka7e24c12009-10-30 11:49:00 +00005#include <errno.h>
Ben Murdochb8a8cc12014-11-26 15:28:44 +00006#include <fcntl.h>
7#include <signal.h>
8#include <stdlib.h>
9#include <string.h>
Steve Blocka7e24c12009-10-30 11:49:00 +000010#include <sys/stat.h>
11#include <sys/time.h>
Ben Murdochb8a8cc12014-11-26 15:28:44 +000012#include <sys/types.h>
Steve Blocka7e24c12009-10-30 11:49:00 +000013#include <sys/wait.h>
Ben Murdochb8a8cc12014-11-26 15:28:44 +000014#include <unistd.h>
Steve Blocka7e24c12009-10-30 11:49:00 +000015
Ben Murdochb8a8cc12014-11-26 15:28:44 +000016#include "src/d8.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000017
Ben Murdochb8a8cc12014-11-26 15:28:44 +000018#if !V8_OS_NACL
19#include <sys/select.h>
20#endif
Steve Blocka7e24c12009-10-30 11:49:00 +000021
22namespace v8 {
23
24
25// If the buffer ends in the middle of a UTF-8 sequence then we return
26// the length of the string up to but not including the incomplete UTF-8
27// sequence. If the buffer ends with a valid UTF-8 sequence then we
28// return the whole buffer.
29static int LengthWithoutIncompleteUtf8(char* buffer, int len) {
30 int answer = len;
31 // 1-byte encoding.
32 static const int kUtf8SingleByteMask = 0x80;
33 static const int kUtf8SingleByteValue = 0x00;
34 // 2-byte encoding.
35 static const int kUtf8TwoByteMask = 0xe0;
36 static const int kUtf8TwoByteValue = 0xc0;
37 // 3-byte encoding.
38 static const int kUtf8ThreeByteMask = 0xf0;
39 static const int kUtf8ThreeByteValue = 0xe0;
40 // 4-byte encoding.
41 static const int kUtf8FourByteMask = 0xf8;
42 static const int kUtf8FourByteValue = 0xf0;
43 // Subsequent bytes of a multi-byte encoding.
44 static const int kMultiByteMask = 0xc0;
45 static const int kMultiByteValue = 0x80;
46 int multi_byte_bytes_seen = 0;
47 while (answer > 0) {
48 int c = buffer[answer - 1];
49 // Ends in valid single-byte sequence?
50 if ((c & kUtf8SingleByteMask) == kUtf8SingleByteValue) return answer;
51 // Ends in one or more subsequent bytes of a multi-byte value?
52 if ((c & kMultiByteMask) == kMultiByteValue) {
53 multi_byte_bytes_seen++;
54 answer--;
55 } else {
56 if ((c & kUtf8TwoByteMask) == kUtf8TwoByteValue) {
57 if (multi_byte_bytes_seen >= 1) {
58 return answer + 2;
59 }
60 return answer - 1;
61 } else if ((c & kUtf8ThreeByteMask) == kUtf8ThreeByteValue) {
62 if (multi_byte_bytes_seen >= 2) {
63 return answer + 3;
64 }
65 return answer - 1;
66 } else if ((c & kUtf8FourByteMask) == kUtf8FourByteValue) {
67 if (multi_byte_bytes_seen >= 3) {
68 return answer + 4;
69 }
70 return answer - 1;
71 } else {
72 return answer; // Malformed UTF-8.
73 }
74 }
75 }
76 return 0;
77}
78
79
80// Suspends the thread until there is data available from the child process.
81// Returns false on timeout, true on data ready.
82static bool WaitOnFD(int fd,
83 int read_timeout,
84 int total_timeout,
Ben Murdochb8a8cc12014-11-26 15:28:44 +000085 const struct timeval& start_time) {
Steve Blocka7e24c12009-10-30 11:49:00 +000086 fd_set readfds, writefds, exceptfds;
87 struct timeval timeout;
88 int gone = 0;
89 if (total_timeout != -1) {
90 struct timeval time_now;
91 gettimeofday(&time_now, NULL);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000092 time_t seconds = time_now.tv_sec - start_time.tv_sec;
93 gone = static_cast<int>(seconds * 1000 +
94 (time_now.tv_usec - start_time.tv_usec) / 1000);
Steve Blocka7e24c12009-10-30 11:49:00 +000095 if (gone >= total_timeout) return false;
96 }
97 FD_ZERO(&readfds);
98 FD_ZERO(&writefds);
99 FD_ZERO(&exceptfds);
100 FD_SET(fd, &readfds);
101 FD_SET(fd, &exceptfds);
102 if (read_timeout == -1 ||
103 (total_timeout != -1 && total_timeout - gone < read_timeout)) {
104 read_timeout = total_timeout - gone;
105 }
106 timeout.tv_usec = (read_timeout % 1000) * 1000;
107 timeout.tv_sec = read_timeout / 1000;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000108#if V8_OS_NACL
109 // PNaCL has no support for select.
110 int number_of_fds_ready = -1;
111#else
Steve Blocka7e24c12009-10-30 11:49:00 +0000112 int number_of_fds_ready = select(fd + 1,
113 &readfds,
114 &writefds,
115 &exceptfds,
116 read_timeout != -1 ? &timeout : NULL);
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000117#endif
Steve Blocka7e24c12009-10-30 11:49:00 +0000118 return number_of_fds_ready == 1;
119}
120
121
122// Checks whether we ran out of time on the timeout. Returns true if we ran out
123// of time, false if we still have time.
124static bool TimeIsOut(const struct timeval& start_time, const int& total_time) {
125 if (total_time == -1) return false;
126 struct timeval time_now;
127 gettimeofday(&time_now, NULL);
128 // Careful about overflow.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000129 int seconds = static_cast<int>(time_now.tv_sec - start_time.tv_sec);
Steve Blocka7e24c12009-10-30 11:49:00 +0000130 if (seconds > 100) {
131 if (seconds * 1000 > total_time) return true;
132 return false;
133 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000134 int useconds = static_cast<int>(time_now.tv_usec - start_time.tv_usec);
Steve Blocka7e24c12009-10-30 11:49:00 +0000135 if (seconds * 1000000 + useconds > total_time * 1000) {
136 return true;
137 }
138 return false;
139}
140
141
142// A utility class that does a non-hanging waitpid on the child process if we
143// bail out of the System() function early. If you don't ever do a waitpid on
144// a subprocess then it turns into one of those annoying 'zombie processes'.
145class ZombieProtector {
146 public:
147 explicit ZombieProtector(int pid): pid_(pid) { }
148 ~ZombieProtector() { if (pid_ != 0) waitpid(pid_, NULL, 0); }
149 void ChildIsDeadNow() { pid_ = 0; }
150 private:
151 int pid_;
152};
153
154
155// A utility class that closes a file descriptor when it goes out of scope.
156class OpenFDCloser {
157 public:
158 explicit OpenFDCloser(int fd): fd_(fd) { }
159 ~OpenFDCloser() { close(fd_); }
160 private:
161 int fd_;
162};
163
164
165// A utility class that takes the array of command arguments and puts then in an
166// array of new[]ed UTF-8 C strings. Deallocates them again when it goes out of
167// scope.
168class ExecArgs {
169 public:
170 ExecArgs() {
171 exec_args_[0] = NULL;
172 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000173 bool Init(Isolate* isolate, Local<Value> arg0, Local<Array> command_args) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000174 String::Utf8Value prog(arg0);
175 if (*prog == NULL) {
176 const char* message =
177 "os.system(): String conversion of program name failed";
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000178 isolate->ThrowException(
179 String::NewFromUtf8(isolate, message, NewStringType::kNormal)
180 .ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000181 return false;
182 }
183 int len = prog.length() + 3;
184 char* c_arg = new char[len];
185 snprintf(c_arg, len, "%s", *prog);
186 exec_args_[0] = c_arg;
187 int i = 1;
188 for (unsigned j = 0; j < command_args->Length(); i++, j++) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000189 Local<Value> arg(
190 command_args->Get(isolate->GetCurrentContext(),
191 Integer::New(isolate, j)).ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000192 String::Utf8Value utf8_arg(arg);
193 if (*utf8_arg == NULL) {
194 exec_args_[i] = NULL; // Consistent state for destructor.
195 const char* message =
196 "os.system(): String conversion of argument failed.";
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000197 isolate->ThrowException(
198 String::NewFromUtf8(isolate, message, NewStringType::kNormal)
199 .ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000200 return false;
201 }
202 int len = utf8_arg.length() + 1;
203 char* c_arg = new char[len];
204 snprintf(c_arg, len, "%s", *utf8_arg);
205 exec_args_[i] = c_arg;
206 }
207 exec_args_[i] = NULL;
208 return true;
209 }
210 ~ExecArgs() {
211 for (unsigned i = 0; i < kMaxArgs; i++) {
212 if (exec_args_[i] == NULL) {
213 return;
214 }
215 delete [] exec_args_[i];
216 exec_args_[i] = 0;
217 }
218 }
219 static const unsigned kMaxArgs = 1000;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000220 char* const* arg_array() const { return exec_args_; }
221 const char* arg0() const { return exec_args_[0]; }
Ben Murdoch589d6972011-11-30 16:04:58 +0000222
Steve Blocka7e24c12009-10-30 11:49:00 +0000223 private:
224 char* exec_args_[kMaxArgs + 1];
225};
226
227
228// Gets the optional timeouts from the arguments to the system() call.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000229static bool GetTimeouts(const v8::FunctionCallbackInfo<v8::Value>& args,
Steve Blocka7e24c12009-10-30 11:49:00 +0000230 int* read_timeout,
231 int* total_timeout) {
232 if (args.Length() > 3) {
233 if (args[3]->IsNumber()) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000234 *total_timeout = args[3]
235 ->Int32Value(args.GetIsolate()->GetCurrentContext())
236 .FromJust();
Steve Blocka7e24c12009-10-30 11:49:00 +0000237 } else {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000238 args.GetIsolate()->ThrowException(
239 String::NewFromUtf8(args.GetIsolate(),
240 "system: Argument 4 must be a number",
241 NewStringType::kNormal).ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000242 return false;
243 }
244 }
245 if (args.Length() > 2) {
246 if (args[2]->IsNumber()) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000247 *read_timeout = args[2]
248 ->Int32Value(args.GetIsolate()->GetCurrentContext())
249 .FromJust();
Steve Blocka7e24c12009-10-30 11:49:00 +0000250 } else {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000251 args.GetIsolate()->ThrowException(
252 String::NewFromUtf8(args.GetIsolate(),
253 "system: Argument 3 must be a number",
254 NewStringType::kNormal).ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000255 return false;
256 }
257 }
258 return true;
259}
260
261
262static const int kReadFD = 0;
263static const int kWriteFD = 1;
264
265
266// This is run in the child process after fork() but before exec(). It normally
267// ends with the child process being replaced with the desired child program.
268// It only returns if an error occurred.
269static void ExecSubprocess(int* exec_error_fds,
270 int* stdout_fds,
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000271 const ExecArgs& exec_args) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000272 close(exec_error_fds[kReadFD]); // Don't need this in the child.
273 close(stdout_fds[kReadFD]); // Don't need this in the child.
274 close(1); // Close stdout.
275 dup2(stdout_fds[kWriteFD], 1); // Dup pipe fd to stdout.
276 close(stdout_fds[kWriteFD]); // Don't need the original fd now.
277 fcntl(exec_error_fds[kWriteFD], F_SETFD, FD_CLOEXEC);
278 execvp(exec_args.arg0(), exec_args.arg_array());
279 // Only get here if the exec failed. Write errno to the parent to tell
280 // them it went wrong. If it went well the pipe is closed.
281 int err = errno;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000282 ssize_t bytes_written;
Steve Blocka7e24c12009-10-30 11:49:00 +0000283 do {
284 bytes_written = write(exec_error_fds[kWriteFD], &err, sizeof(err));
285 } while (bytes_written == -1 && errno == EINTR);
286 // Return (and exit child process).
287}
288
289
290// Runs in the parent process. Checks that the child was able to exec (closing
291// the file desriptor), or reports an error if it failed.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000292static bool ChildLaunchedOK(Isolate* isolate, int* exec_error_fds) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000293 ssize_t bytes_read;
Steve Blocka7e24c12009-10-30 11:49:00 +0000294 int err;
295 do {
296 bytes_read = read(exec_error_fds[kReadFD], &err, sizeof(err));
297 } while (bytes_read == -1 && errno == EINTR);
298 if (bytes_read != 0) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000299 isolate->ThrowException(
300 String::NewFromUtf8(isolate, strerror(err), NewStringType::kNormal)
301 .ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000302 return false;
303 }
304 return true;
305}
306
307
308// Accumulates the output from the child in a string handle. Returns true if it
309// succeeded or false if an exception was thrown.
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000310static Local<Value> GetStdout(Isolate* isolate, int child_fd,
311 const struct timeval& start_time,
312 int read_timeout, int total_timeout) {
313 Local<String> accumulator = String::Empty(isolate);
Steve Blocka7e24c12009-10-30 11:49:00 +0000314
315 int fullness = 0;
316 static const int kStdoutReadBufferSize = 4096;
317 char buffer[kStdoutReadBufferSize];
318
319 if (fcntl(child_fd, F_SETFL, O_NONBLOCK) != 0) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000320 return isolate->ThrowException(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000321 String::NewFromUtf8(isolate, strerror(errno), NewStringType::kNormal)
322 .ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000323 }
324
325 int bytes_read;
326 do {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000327 bytes_read = static_cast<int>(
328 read(child_fd, buffer + fullness, kStdoutReadBufferSize - fullness));
Steve Blocka7e24c12009-10-30 11:49:00 +0000329 if (bytes_read == -1) {
330 if (errno == EAGAIN) {
331 if (!WaitOnFD(child_fd,
332 read_timeout,
333 total_timeout,
334 start_time) ||
335 (TimeIsOut(start_time, total_timeout))) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000336 return isolate->ThrowException(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000337 String::NewFromUtf8(isolate, "Timed out waiting for output",
338 NewStringType::kNormal).ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000339 }
340 continue;
341 } else if (errno == EINTR) {
342 continue;
343 } else {
344 break;
345 }
346 }
347 if (bytes_read + fullness > 0) {
348 int length = bytes_read == 0 ?
349 bytes_read + fullness :
350 LengthWithoutIncompleteUtf8(buffer, bytes_read + fullness);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000351 Local<String> addition =
352 String::NewFromUtf8(isolate, buffer, NewStringType::kNormal, length)
353 .ToLocalChecked();
Ben Murdoch3fb3ca82011-12-02 17:19:32 +0000354 accumulator = String::Concat(accumulator, addition);
Steve Blocka7e24c12009-10-30 11:49:00 +0000355 fullness = bytes_read + fullness - length;
356 memcpy(buffer, buffer + length, fullness);
357 }
358 } while (bytes_read != 0);
359 return accumulator;
360}
361
362
363// Modern Linux has the waitid call, which is like waitpid, but more useful
364// if you want a timeout. If we don't have waitid we can't limit the time
365// waiting for the process to exit without losing the information about
366// whether it exited normally. In the common case this doesn't matter because
367// we don't get here before the child has closed stdout and most programs don't
368// do that before they exit.
369//
370// We're disabling usage of waitid in Mac OS X because it doens't work for us:
371// a parent process hangs on waiting while a child process is already a zombie.
372// See http://code.google.com/p/v8/issues/detail?id=401.
Ben Murdoch3ef787d2012-04-12 10:51:47 +0100373#if defined(WNOWAIT) && !defined(ANDROID) && !defined(__APPLE__) \
374 && !defined(__NetBSD__)
Steve Block44f0eee2011-05-26 01:26:41 +0100375#if !defined(__FreeBSD__)
Steve Blocka7e24c12009-10-30 11:49:00 +0000376#define HAS_WAITID 1
377#endif
Steve Block44f0eee2011-05-26 01:26:41 +0100378#endif
Steve Blocka7e24c12009-10-30 11:49:00 +0000379
380
381// Get exit status of child.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000382static bool WaitForChild(Isolate* isolate,
383 int pid,
384 ZombieProtector& child_waiter, // NOLINT
385 const struct timeval& start_time,
Steve Blocka7e24c12009-10-30 11:49:00 +0000386 int read_timeout,
387 int total_timeout) {
388#ifdef HAS_WAITID
389
390 siginfo_t child_info;
391 child_info.si_pid = 0;
392 int useconds = 1;
393 // Wait for child to exit.
394 while (child_info.si_pid == 0) {
395 waitid(P_PID, pid, &child_info, WEXITED | WNOHANG | WNOWAIT);
396 usleep(useconds);
397 if (useconds < 1000000) useconds <<= 1;
398 if ((read_timeout != -1 && useconds / 1000 > read_timeout) ||
399 (TimeIsOut(start_time, total_timeout))) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000400 isolate->ThrowException(
401 String::NewFromUtf8(isolate,
402 "Timed out waiting for process to terminate",
403 NewStringType::kNormal).ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000404 kill(pid, SIGINT);
405 return false;
406 }
407 }
408 if (child_info.si_code == CLD_KILLED) {
409 char message[999];
410 snprintf(message,
411 sizeof(message),
412 "Child killed by signal %d",
413 child_info.si_status);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000414 isolate->ThrowException(
415 String::NewFromUtf8(isolate, message, NewStringType::kNormal)
416 .ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000417 return false;
418 }
419 if (child_info.si_code == CLD_EXITED && child_info.si_status != 0) {
420 char message[999];
421 snprintf(message,
422 sizeof(message),
423 "Child exited with status %d",
424 child_info.si_status);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000425 isolate->ThrowException(
426 String::NewFromUtf8(isolate, message, NewStringType::kNormal)
427 .ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000428 return false;
429 }
430
431#else // No waitid call.
432
433 int child_status;
434 waitpid(pid, &child_status, 0); // We hang here if the child doesn't exit.
435 child_waiter.ChildIsDeadNow();
436 if (WIFSIGNALED(child_status)) {
437 char message[999];
438 snprintf(message,
439 sizeof(message),
440 "Child killed by signal %d",
441 WTERMSIG(child_status));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000442 isolate->ThrowException(
443 String::NewFromUtf8(isolate, message, NewStringType::kNormal)
444 .ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000445 return false;
446 }
447 if (WEXITSTATUS(child_status) != 0) {
448 char message[999];
449 int exit_status = WEXITSTATUS(child_status);
450 snprintf(message,
451 sizeof(message),
452 "Child exited with status %d",
453 exit_status);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000454 isolate->ThrowException(
455 String::NewFromUtf8(isolate, message, NewStringType::kNormal)
456 .ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000457 return false;
458 }
459
460#endif // No waitid call.
461
462 return true;
463}
464
465
466// Implementation of the system() function (see d8.h for details).
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000467void Shell::System(const v8::FunctionCallbackInfo<v8::Value>& args) {
468 HandleScope scope(args.GetIsolate());
Steve Blocka7e24c12009-10-30 11:49:00 +0000469 int read_timeout = -1;
470 int total_timeout = -1;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000471 if (!GetTimeouts(args, &read_timeout, &total_timeout)) return;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000472 Local<Array> command_args;
Steve Blocka7e24c12009-10-30 11:49:00 +0000473 if (args.Length() > 1) {
474 if (!args[1]->IsArray()) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000475 args.GetIsolate()->ThrowException(
476 String::NewFromUtf8(args.GetIsolate(),
477 "system: Argument 2 must be an array",
478 NewStringType::kNormal).ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000479 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000480 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000481 command_args = Local<Array>::Cast(args[1]);
Steve Blocka7e24c12009-10-30 11:49:00 +0000482 } else {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000483 command_args = Array::New(args.GetIsolate(), 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000484 }
485 if (command_args->Length() > ExecArgs::kMaxArgs) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000486 args.GetIsolate()->ThrowException(
487 String::NewFromUtf8(args.GetIsolate(), "Too many arguments to system()",
488 NewStringType::kNormal).ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000489 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000490 }
491 if (args.Length() < 1) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000492 args.GetIsolate()->ThrowException(
493 String::NewFromUtf8(args.GetIsolate(), "Too few arguments to system()",
494 NewStringType::kNormal).ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000495 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000496 }
497
498 struct timeval start_time;
499 gettimeofday(&start_time, NULL);
500
501 ExecArgs exec_args;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000502 if (!exec_args.Init(args.GetIsolate(), args[0], command_args)) {
503 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000504 }
505 int exec_error_fds[2];
506 int stdout_fds[2];
507
508 if (pipe(exec_error_fds) != 0) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000509 args.GetIsolate()->ThrowException(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000510 String::NewFromUtf8(args.GetIsolate(), "pipe syscall failed.",
511 NewStringType::kNormal).ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000512 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000513 }
514 if (pipe(stdout_fds) != 0) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000515 args.GetIsolate()->ThrowException(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000516 String::NewFromUtf8(args.GetIsolate(), "pipe syscall failed.",
517 NewStringType::kNormal).ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000518 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000519 }
520
521 pid_t pid = fork();
522 if (pid == 0) { // Child process.
523 ExecSubprocess(exec_error_fds, stdout_fds, exec_args);
524 exit(1);
525 }
526
527 // Parent process. Ensure that we clean up if we exit this function early.
528 ZombieProtector child_waiter(pid);
529 close(exec_error_fds[kWriteFD]);
530 close(stdout_fds[kWriteFD]);
531 OpenFDCloser error_read_closer(exec_error_fds[kReadFD]);
532 OpenFDCloser stdout_read_closer(stdout_fds[kReadFD]);
533
Ben Murdoch61f157c2016-09-16 13:49:30 +0100534 Isolate* isolate = args.GetIsolate();
535 if (!ChildLaunchedOK(isolate, exec_error_fds)) return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000536
Ben Murdoch61f157c2016-09-16 13:49:30 +0100537 Local<Value> accumulator = GetStdout(isolate, stdout_fds[kReadFD], start_time,
538 read_timeout, total_timeout);
Steve Blocka7e24c12009-10-30 11:49:00 +0000539 if (accumulator->IsUndefined()) {
540 kill(pid, SIGINT); // On timeout, kill the subprocess.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000541 args.GetReturnValue().Set(accumulator);
542 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000543 }
544
Ben Murdoch61f157c2016-09-16 13:49:30 +0100545 if (!WaitForChild(isolate, pid, child_waiter, start_time, read_timeout,
Steve Blocka7e24c12009-10-30 11:49:00 +0000546 total_timeout)) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000547 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000548 }
549
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000550 args.GetReturnValue().Set(accumulator);
Steve Blocka7e24c12009-10-30 11:49:00 +0000551}
552
553
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000554void Shell::ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000555 if (args.Length() != 1) {
556 const char* message = "chdir() takes one argument";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000557 args.GetIsolate()->ThrowException(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000558 String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
559 .ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000560 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000561 }
562 String::Utf8Value directory(args[0]);
563 if (*directory == NULL) {
564 const char* message = "os.chdir(): String conversion of argument failed.";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000565 args.GetIsolate()->ThrowException(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000566 String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
567 .ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000568 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000569 }
570 if (chdir(*directory) != 0) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000571 args.GetIsolate()->ThrowException(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000572 String::NewFromUtf8(args.GetIsolate(), strerror(errno),
573 NewStringType::kNormal).ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000574 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000575 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000576}
577
578
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000579void Shell::SetUMask(const v8::FunctionCallbackInfo<v8::Value>& args) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000580 if (args.Length() != 1) {
581 const char* message = "umask() takes one argument";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000582 args.GetIsolate()->ThrowException(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000583 String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
584 .ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000585 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000586 }
587 if (args[0]->IsNumber()) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000588#if V8_OS_NACL
589 // PNaCL has no support for umask.
590 int previous = 0;
591#else
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000592 int previous = umask(
593 args[0]->Int32Value(args.GetIsolate()->GetCurrentContext()).FromJust());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000594#endif
595 args.GetReturnValue().Set(previous);
596 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000597 } else {
598 const char* message = "umask() argument must be numeric";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000599 args.GetIsolate()->ThrowException(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000600 String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
601 .ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000602 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000603 }
604}
605
606
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000607static bool CheckItsADirectory(Isolate* isolate, char* directory) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000608 struct stat stat_buf;
609 int stat_result = stat(directory, &stat_buf);
610 if (stat_result != 0) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000611 isolate->ThrowException(
612 String::NewFromUtf8(isolate, strerror(errno), NewStringType::kNormal)
613 .ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000614 return false;
615 }
616 if ((stat_buf.st_mode & S_IFDIR) != 0) return true;
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000617 isolate->ThrowException(
618 String::NewFromUtf8(isolate, strerror(EEXIST), NewStringType::kNormal)
619 .ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000620 return false;
621}
622
623
624// Returns true for success. Creates intermediate directories as needed. No
625// error if the directory exists already.
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000626static bool mkdirp(Isolate* isolate, char* directory, mode_t mask) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000627 int result = mkdir(directory, mask);
628 if (result == 0) return true;
629 if (errno == EEXIST) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000630 return CheckItsADirectory(isolate, directory);
Steve Blocka7e24c12009-10-30 11:49:00 +0000631 } else if (errno == ENOENT) { // Intermediate path element is missing.
632 char* last_slash = strrchr(directory, '/');
633 if (last_slash == NULL) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000634 isolate->ThrowException(
635 String::NewFromUtf8(isolate, strerror(errno), NewStringType::kNormal)
636 .ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000637 return false;
638 }
639 *last_slash = 0;
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000640 if (!mkdirp(isolate, directory, mask)) return false;
Steve Blocka7e24c12009-10-30 11:49:00 +0000641 *last_slash = '/';
642 result = mkdir(directory, mask);
643 if (result == 0) return true;
644 if (errno == EEXIST) {
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000645 return CheckItsADirectory(isolate, directory);
Steve Blocka7e24c12009-10-30 11:49:00 +0000646 }
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000647 isolate->ThrowException(
648 String::NewFromUtf8(isolate, strerror(errno), NewStringType::kNormal)
649 .ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000650 return false;
651 } else {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000652 isolate->ThrowException(
653 String::NewFromUtf8(isolate, strerror(errno), NewStringType::kNormal)
654 .ToLocalChecked());
Steve Blocka7e24c12009-10-30 11:49:00 +0000655 return false;
656 }
657}
658
659
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000660void Shell::MakeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000661 mode_t mask = 0777;
662 if (args.Length() == 2) {
663 if (args[1]->IsNumber()) {
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000664 mask = args[1]
665 ->Int32Value(args.GetIsolate()->GetCurrentContext())
666 .FromJust();
Steve Blocka7e24c12009-10-30 11:49:00 +0000667 } else {
668 const char* message = "mkdirp() second argument must be numeric";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000669 args.GetIsolate()->ThrowException(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000670 String::NewFromUtf8(args.GetIsolate(), message,
671 NewStringType::kNormal).ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000672 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000673 }
674 } else if (args.Length() != 1) {
675 const char* message = "mkdirp() takes one or two arguments";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000676 args.GetIsolate()->ThrowException(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000677 String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
678 .ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000679 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000680 }
681 String::Utf8Value directory(args[0]);
682 if (*directory == NULL) {
683 const char* message = "os.mkdirp(): String conversion of argument failed.";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000684 args.GetIsolate()->ThrowException(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000685 String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
686 .ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000687 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000688 }
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000689 mkdirp(args.GetIsolate(), *directory, mask);
Steve Blocka7e24c12009-10-30 11:49:00 +0000690}
691
692
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000693void Shell::RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000694 if (args.Length() != 1) {
695 const char* message = "rmdir() takes one or two arguments";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000696 args.GetIsolate()->ThrowException(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000697 String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
698 .ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000699 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000700 }
701 String::Utf8Value directory(args[0]);
702 if (*directory == NULL) {
703 const char* message = "os.rmdir(): String conversion of argument failed.";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000704 args.GetIsolate()->ThrowException(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000705 String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
706 .ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000707 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000708 }
709 rmdir(*directory);
Steve Blocka7e24c12009-10-30 11:49:00 +0000710}
711
712
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000713void Shell::SetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000714 if (args.Length() != 2) {
715 const char* message = "setenv() takes two arguments";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000716 args.GetIsolate()->ThrowException(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000717 String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
718 .ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000719 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000720 }
721 String::Utf8Value var(args[0]);
722 String::Utf8Value value(args[1]);
723 if (*var == NULL) {
724 const char* message =
725 "os.setenv(): String conversion of variable name failed.";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000726 args.GetIsolate()->ThrowException(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000727 String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
728 .ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000729 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000730 }
731 if (*value == NULL) {
732 const char* message =
733 "os.setenv(): String conversion of variable contents failed.";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000734 args.GetIsolate()->ThrowException(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000735 String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
736 .ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000737 return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000738 }
739 setenv(*var, *value, 1);
Steve Blocka7e24c12009-10-30 11:49:00 +0000740}
741
742
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000743void Shell::UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
Steve Block6ded16b2010-05-10 14:33:55 +0100744 if (args.Length() != 1) {
745 const char* message = "unsetenv() takes one argument";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000746 args.GetIsolate()->ThrowException(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000747 String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
748 .ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000749 return;
Steve Block6ded16b2010-05-10 14:33:55 +0100750 }
751 String::Utf8Value var(args[0]);
752 if (*var == NULL) {
753 const char* message =
754 "os.setenv(): String conversion of variable name failed.";
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000755 args.GetIsolate()->ThrowException(
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000756 String::NewFromUtf8(args.GetIsolate(), message, NewStringType::kNormal)
757 .ToLocalChecked());
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000758 return;
Steve Block6ded16b2010-05-10 14:33:55 +0100759 }
760 unsetenv(*var);
Steve Block6ded16b2010-05-10 14:33:55 +0100761}
762
763
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000764void Shell::AddOSMethods(Isolate* isolate, Local<ObjectTemplate> os_templ) {
765 os_templ->Set(String::NewFromUtf8(isolate, "system", NewStringType::kNormal)
766 .ToLocalChecked(),
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000767 FunctionTemplate::New(isolate, System));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000768 os_templ->Set(String::NewFromUtf8(isolate, "chdir", NewStringType::kNormal)
769 .ToLocalChecked(),
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000770 FunctionTemplate::New(isolate, ChangeDirectory));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000771 os_templ->Set(String::NewFromUtf8(isolate, "setenv", NewStringType::kNormal)
772 .ToLocalChecked(),
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000773 FunctionTemplate::New(isolate, SetEnvironment));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000774 os_templ->Set(String::NewFromUtf8(isolate, "unsetenv", NewStringType::kNormal)
775 .ToLocalChecked(),
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000776 FunctionTemplate::New(isolate, UnsetEnvironment));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000777 os_templ->Set(String::NewFromUtf8(isolate, "umask", NewStringType::kNormal)
778 .ToLocalChecked(),
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000779 FunctionTemplate::New(isolate, SetUMask));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000780 os_templ->Set(String::NewFromUtf8(isolate, "mkdirp", NewStringType::kNormal)
781 .ToLocalChecked(),
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000782 FunctionTemplate::New(isolate, MakeDirectory));
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000783 os_templ->Set(String::NewFromUtf8(isolate, "rmdir", NewStringType::kNormal)
784 .ToLocalChecked(),
Ben Murdochb8a8cc12014-11-26 15:28:44 +0000785 FunctionTemplate::New(isolate, RemoveDirectory));
Steve Blocka7e24c12009-10-30 11:49:00 +0000786}
787
788} // namespace v8