11#include <netinet/ip.h>
16#include <sys/select.h>
17#include <sys/socket.h>
36 static const int kUtf8SingleByteMask = 0x80;
37 static const int kUtf8SingleByteValue = 0x00;
39 static const int kUtf8TwoByteMask = 0xE0;
40 static const int kUtf8TwoByteValue = 0xC0;
42 static const int kUtf8ThreeByteMask = 0xF0;
43 static const int kUtf8ThreeByteValue = 0xE0;
45 static const int kUtf8FourByteMask = 0xF8;
46 static const int kUtf8FourByteValue = 0xF0;
48 static const int kMultiByteMask = 0xC0;
49 static const int kMultiByteValue = 0x80;
50 int multi_byte_bytes_seen = 0;
52 int c = buffer[answer - 1];
54 if ((c & kUtf8SingleByteMask) == kUtf8SingleByteValue)
return answer;
56 if ((c & kMultiByteMask) == kMultiByteValue) {
57 multi_byte_bytes_seen++;
60 if ((c & kUtf8TwoByteMask) == kUtf8TwoByteValue) {
61 if (multi_byte_bytes_seen >= 1) {
65 }
else if ((c & kUtf8ThreeByteMask) == kUtf8ThreeByteValue) {
66 if (multi_byte_bytes_seen >= 2) {
70 }
else if ((c & kUtf8FourByteMask) == kUtf8FourByteValue) {
71 if (multi_byte_bytes_seen >= 3) {
85static bool WaitOnFD(
int fd,
int read_timeout,
int total_timeout,
86 const struct timeval& start_time) {
87 fd_set readfds, writefds, exceptfds;
88 struct timeval timeout;
90 if (total_timeout != -1) {
91 struct timeval time_now;
92 gettimeofday(&time_now,
nullptr);
93 time_t seconds = time_now.tv_sec - start_time.tv_sec;
94 gone =
static_cast<int>(seconds * 1000 +
95 (time_now.tv_usec - start_time.tv_usec) / 1000);
96 if (gone >= total_timeout)
return false;
101 FD_SET(fd, &readfds);
102 FD_SET(fd, &exceptfds);
103 if (read_timeout == -1 ||
104 (total_timeout != -1 && total_timeout - gone < read_timeout)) {
105 read_timeout = total_timeout - gone;
107 timeout.tv_usec = (read_timeout % 1000) * 1000;
108 timeout.tv_sec = read_timeout / 1000;
109 int number_of_fds_ready = select(fd + 1, &readfds, &writefds, &exceptfds,
110 read_timeout != -1 ? &timeout :
nullptr);
111 return number_of_fds_ready == 1;
116static bool TimeIsOut(
const struct timeval& start_time,
const int& total_time) {
117 if (total_time == -1)
return false;
118 struct timeval time_now;
119 gettimeofday(&time_now,
nullptr);
121 int seconds =
static_cast<int>(time_now.tv_sec - start_time.tv_sec);
123 if (seconds * 1000 > total_time)
return true;
126 int useconds =
static_cast<int>(time_now.tv_usec - start_time.tv_usec);
127 if (seconds * 1000000 + useconds > total_time * 1000) {
140 if (
pid_ != 0) waitpid(
pid_,
nullptr, 0);
166 if (*prog ==
nullptr) {
168 "os.system(): String conversion of program name failed");
172 size_t len = prog.
length() + 3;
173 char* c_arg =
new char[len];
174 snprintf(c_arg, len,
"%s", *prog);
178 for (
unsigned j = 0; j < command_args->Length();
i++, j++) {
181 ->Get(isolate->GetCurrentContext(),
Integer::New(isolate, j))
184 if (*utf8_arg ==
nullptr) {
187 "os.system(): String conversion of argument failed.");
190 size_t len = utf8_arg.
length() + 1;
191 char* c_arg =
new char[len];
192 snprintf(c_arg, len,
"%s", *utf8_arg);
217 int* read_timeout,
int* total_timeout) {
218 if (info.Length() > 3) {
219 if (info[3]->IsNumber()) {
220 *total_timeout = info[3]
221 ->Int32Value(info.GetIsolate()->GetCurrentContext())
224 info.GetIsolate()->ThrowError(
"system: Argument 4 must be a number");
228 if (info.Length() > 2) {
229 if (info[2]->IsNumber()) {
230 *read_timeout = info[2]
231 ->Int32Value(info.GetIsolate()->GetCurrentContext())
234 info.GetIsolate()->ThrowError(
"system: Argument 3 must be a number");
255 close(exec_error_fds[
kReadFD]);
260 fcntl(exec_error_fds[
kWriteFD], F_SETFD, FD_CLOEXEC);
265 ssize_t bytes_written;
267 bytes_written = write(exec_error_fds[
kWriteFD], &err,
sizeof(err));
268 }
while (bytes_written == -1 && errno == EINTR);
278 bytes_read = read(exec_error_fds[
kReadFD], &err,
sizeof(err));
279 }
while (bytes_read == -1 && errno == EINTR);
280 if (bytes_read != 0) {
281 isolate->ThrowError(v8_strerror(isolate, err));
290 const struct timeval& start_time,
291 int read_timeout,
int total_timeout) {
295 static const int kStdoutReadBufferSize = 4096;
296 char buffer[kStdoutReadBufferSize];
298 if (fcntl(child_fd, F_SETFL, O_NONBLOCK) != 0) {
299 return isolate->ThrowError(v8_strerror(isolate, errno));
304 bytes_read =
static_cast<int>(
305 read(child_fd, buffer + fullness, kStdoutReadBufferSize - fullness));
306 if (bytes_read == -1) {
307 if (errno == EAGAIN) {
308 if (!
WaitOnFD(child_fd, read_timeout, total_timeout, start_time) ||
309 (
TimeIsOut(start_time, total_timeout))) {
310 return isolate->ThrowError(
"Timed out waiting for output");
313 }
else if (errno == EINTR) {
319 if (bytes_read + fullness > 0) {
320 int length = bytes_read == 0 ? bytes_read + fullness
322 buffer, bytes_read + fullness);
327 fullness = bytes_read + fullness -
length;
328 memcpy(buffer, buffer + length, fullness);
330 }
while (bytes_read != 0);
344#if defined(WNOWAIT) && !defined(ANDROID) && !defined(__APPLE__) && \
345 !defined(__NetBSD__) && !defined(__Fuchsia__)
346#if !defined(__FreeBSD__)
354 const struct timeval& start_time,
int read_timeout,
358 siginfo_t child_info;
359 child_info.si_pid = 0;
362 while (child_info.si_pid == 0) {
363 waitid(P_PID, pid, &child_info, WEXITED | WNOHANG | WNOWAIT);
365 if (useconds < 1000000) useconds <<= 1;
366 if ((read_timeout != -1 && useconds / 1000 > read_timeout) ||
367 (
TimeIsOut(start_time, total_timeout))) {
368 isolate->ThrowError(
"Timed out waiting for process to terminate");
373 if (child_info.si_code == CLD_KILLED) {
375 snprintf(message,
sizeof(message),
"Child killed by signal %d",
376 child_info.si_status);
377 isolate->ThrowError(message);
380 if (child_info.si_code == CLD_EXITED && child_info.si_status != 0) {
382 snprintf(message,
sizeof(message),
"Child exited with status %d",
383 child_info.si_status);
384 isolate->ThrowError(message);
391 waitpid(pid, &child_status, 0);
393 if (WIFSIGNALED(child_status)) {
395 snprintf(message,
sizeof(message),
"Child killed by signal %d",
396 WTERMSIG(child_status));
397 isolate->ThrowError(message);
400 if (WEXITSTATUS(child_status) != 0) {
402 int exit_status = WEXITSTATUS(child_status);
403 snprintf(message,
sizeof(message),
"Child exited with status %d",
405 isolate->ThrowError(message);
420 int read_timeout = -1;
421 int total_timeout = -1;
422 if (!
GetTimeouts(info, &read_timeout, &total_timeout))
return;
424 if (info.Length() > 1) {
425 if (!info[1]->IsArray()) {
426 info.GetIsolate()->ThrowError(
"system: Argument 2 must be an array");
429 command_args = info[1].
As<
Array>();
431 command_args =
Array::New(info.GetIsolate(), 0);
434 info.GetIsolate()->ThrowError(
"Too many arguments to system()");
437 if (info.Length() < 1) {
438 info.GetIsolate()->ThrowError(
"Too few arguments to system()");
442 struct timeval start_time;
443 gettimeofday(&start_time,
nullptr);
446 if (!exec_args.
Init(info.GetIsolate(), info[0], command_args)) {
449 int exec_error_fds[2];
452 if (pipe(exec_error_fds) != 0) {
453 info.GetIsolate()->ThrowError(
"pipe syscall failed.");
456 if (pipe(stdout_fds) != 0) {
457 info.GetIsolate()->ThrowError(
"pipe syscall failed.");
474 Isolate* isolate = info.GetIsolate();
478 read_timeout, total_timeout);
479 if (accumulator->IsUndefined()) {
481 info.GetReturnValue().Set(accumulator);
485 if (!
WaitForChild(isolate, pid, child_waiter, start_time, read_timeout,
490 info.GetReturnValue().Set(accumulator);
495 if (info.Length() != 1) {
496 info.GetIsolate()->ThrowError(
"chdir() takes one argument");
500 if (*directory ==
nullptr) {
501 info.GetIsolate()->ThrowError(
502 "os.chdir(): String conversion of argument failed.");
505 if (chdir(*directory) != 0) {
506 info.GetIsolate()->ThrowError(v8_strerror(info.GetIsolate(), errno));
513 if (info.Length() != 1) {
514 info.GetIsolate()->ThrowError(
"umask() takes one argument");
517 if (info[0]->IsNumber()) {
519 info[0]->Int32Value(info.GetIsolate()->GetCurrentContext()).FromJust());
520 info.GetReturnValue().Set(
previous);
523 info.GetIsolate()->ThrowError(
"umask() argument must be numeric");
529 struct stat stat_buf;
530 int stat_result = stat(directory, &stat_buf);
531 if (stat_result != 0) {
532 isolate->ThrowError(v8_strerror(isolate, errno));
535 if ((stat_buf.st_mode & S_IFDIR) != 0)
return true;
536 isolate->ThrowError(v8_strerror(isolate, EEXIST));
544 if (
result == 0)
return true;
545 if (errno == EEXIST) {
547 }
else if (errno == ENOENT) {
548 char* last_slash = strrchr(directory,
'/');
549 if (last_slash ==
nullptr) {
550 isolate->ThrowError(v8_strerror(isolate, errno));
554 if (!
mkdirp(isolate, directory,
mask))
return false;
557 if (
result == 0)
return true;
558 if (errno == EEXIST) {
561 isolate->ThrowError(v8_strerror(isolate, errno));
564 isolate->ThrowError(v8_strerror(isolate, errno));
572 if (info.Length() == 2) {
573 if (info[1]->IsNumber()) {
575 ->Int32Value(info.GetIsolate()->GetCurrentContext())
578 info.GetIsolate()->ThrowError(
"mkdirp() second argument must be numeric");
581 }
else if (info.Length() != 1) {
582 info.GetIsolate()->ThrowError(
"mkdirp() takes one or two arguments");
586 if (*directory ==
nullptr) {
587 info.GetIsolate()->ThrowError(
588 "os.mkdirp(): String conversion of argument failed.");
596 if (info.Length() != 1) {
597 info.GetIsolate()->ThrowError(
"rmdir() takes one arguments");
601 if (*directory ==
nullptr) {
602 info.GetIsolate()->ThrowError(
603 "os.rmdir(): String conversion of argument failed.");
611 if (info.Length() != 2) {
612 info.GetIsolate()->ThrowError(
"setenv() takes two arguments");
617 if (*var ==
nullptr) {
618 info.GetIsolate()->ThrowError(
619 "os.setenv(): String conversion of variable name failed.");
622 if (*value ==
nullptr) {
623 info.GetIsolate()->ThrowError(
624 "os.setenv(): String conversion of variable contents failed.");
627 setenv(*var, *value, 1);
632 if (info.Length() != 1) {
633 info.GetIsolate()->ThrowError(
"unsetenv() takes one argument");
637 if (*var ==
nullptr) {
638 info.GetIsolate()->ThrowError(
639 "os.setenv(): String conversion of variable name failed.");
648 int sockfd = socket(PF_INET, SOCK_STREAM, 0);
650 fprintf(stderr,
"Failed to create IPv4 socket\n");
656 sockaddr_in serv_addr;
657 memset(&serv_addr, 0,
sizeof(sockaddr_in));
658 serv_addr.sin_family = AF_INET;
659 serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
662 if (connect(sockfd,
reinterpret_cast<sockaddr*
>(&serv_addr),
663 sizeof(serv_addr)) < 0) {
664 fprintf(stderr,
"Failed to connect to localhost:%d\n",
688 size_t name_len = strlen(name) + 1;
689 while (sent_len < name_len) {
690 ssize_t sent_now = send(sockfd, name + sent_len, name_len - sent_len, 0);
692 fprintf(stderr,
"Failed to send %s to localhost:%d\n", name,
697 sent_len += sent_now;
702 ssize_t received = 0;
705 uint32_t big_endian_file_length;
706 received = recv(sockfd, &big_endian_file_length, 4, 0);
709 fprintf(stderr,
"Failed to receive %s's length from localhost:%d\n", name,
717 if (file_length < 0) {
718 fprintf(stderr,
"Received length %d for %s from localhost:%d\n",
725 char* chars =
new char[file_length];
728 ssize_t total_received = 0;
729 while (total_received < file_length) {
731 recv(sockfd, chars + total_received, file_length - total_received, 0);
733 fprintf(stderr,
"Failed to receive %s from localhost:%d\n", name,
739 total_received += received;
743 *size_out = file_length;
748 if (options.enable_os_system) {
751 os_templ->Set(isolate,
"chdir",
753 os_templ->Set(isolate,
"setenv",
755 os_templ->Set(isolate,
"unsetenv",
758 os_templ->Set(isolate,
"mkdirp",
760 os_templ->Set(isolate,
"rmdir",
static Local< Array > New(Isolate *isolate, int length=0)
bool Init(Isolate *isolate, Local< Value > arg0, Local< Array > command_args)
char *const * arg_array() const
char * exec_args_[kMaxArgs+1]
const char * arg0() const
static const unsigned kMaxArgs
static Local< FunctionTemplate > New(Isolate *isolate, FunctionCallback callback=nullptr, Local< Value > data=Local< Value >(), Local< Signature > signature=Local< Signature >(), int length=0, ConstructorBehavior behavior=ConstructorBehavior::kAllow, SideEffectType side_effect_type=SideEffectType::kHasSideEffect, const CFunction *c_function=nullptr, uint16_t instance_type=0, uint16_t allowed_receiver_instance_type_range_start=0, uint16_t allowed_receiver_instance_type_range_end=0)
static Local< Integer > New(Isolate *isolate, int32_t value)
V8_INLINE Local< S > As() const
static void AddOSMethods(v8::Isolate *isolate, Local< ObjectTemplate > os_template)
static void System(const v8::FunctionCallbackInfo< v8::Value > &info)
static void MakeDirectory(const v8::FunctionCallbackInfo< v8::Value > &info)
static void UnsetEnvironment(const v8::FunctionCallbackInfo< v8::Value > &info)
static void SetEnvironment(const v8::FunctionCallbackInfo< v8::Value > &info)
static void SetUMask(const v8::FunctionCallbackInfo< v8::Value > &info)
static void RemoveDirectory(const v8::FunctionCallbackInfo< v8::Value > &info)
static char * ReadCharsFromTcpPort(const char *name, int *size_out)
static void ChangeDirectory(const v8::FunctionCallbackInfo< v8::Value > &info)
static ShellOptions options
static V8_WARN_UNUSED_RESULT MaybeLocal< String > NewFromUtf8(Isolate *isolate, const char *data, NewStringType type=NewStringType::kNormal, int length=-1)
static Local< String > Concat(Isolate *isolate, Local< String > left, Local< String > right)
static V8_INLINE Local< String > Empty(Isolate *isolate)
ZoneVector< RpoNumber > & result
V8_INLINE Dest bit_cast(Source const &source)
bool V8_EXPORT ValidateCallbackInfo(const FunctionCallbackInfo< void > &info)
static bool CheckItsADirectory(Isolate *isolate, char *directory)
static bool WaitForChild(Isolate *isolate, int pid, ZombieProtector &child_waiter, const struct timeval &start_time, int read_timeout, int total_timeout)
static bool mkdirp(Isolate *isolate, char *directory, mode_t mask)
static bool TimeIsOut(const struct timeval &start_time, const int &total_time)
static void ExecSubprocess(int *exec_error_fds, int *stdout_fds, const ExecArgs &exec_args)
static int LengthWithoutIncompleteUtf8(char *buffer, int len)
static bool WaitOnFD(int fd, int read_timeout, int total_timeout, const struct timeval &start_time)
static bool GetTimeouts(const v8::FunctionCallbackInfo< v8::Value > &info, int *read_timeout, int *total_timeout)
static const int kWriteFD
static bool ChildLaunchedOK(Isolate *isolate, int *exec_error_fds)
static Local< Value > GetStdout(Isolate *isolate, int child_fd, const struct timeval &start_time, int read_timeout, int total_timeout)
#define DCHECK_GE(v1, v2)
#define DCHECK(condition)
std::unique_ptr< ValueMirror > value