1 // Copyright 2009 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <netinet/ip.h>
8 #include <signal.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <sys/select.h>
12 #include <sys/socket.h>
13 #include <sys/stat.h>
14 #include <sys/time.h>
15 #include <sys/types.h>
16 #include <sys/wait.h>
17 #include <unistd.h>
18
19 #include "include/v8-container.h"
20 #include "include/v8-template.h"
21 #include "src/base/platform/wrappers.h"
22 #include "src/d8/d8.h"
23
24 namespace v8 {
25
26 // If the buffer ends in the middle of a UTF-8 sequence then we return
27 // the length of the string up to but not including the incomplete UTF-8
28 // sequence. If the buffer ends with a valid UTF-8 sequence then we
29 // return the whole buffer.
LengthWithoutIncompleteUtf8(char* buffer, int len)30 static int LengthWithoutIncompleteUtf8(char* buffer, int len) {
31 int answer = len;
32 // 1-byte encoding.
33 static const int kUtf8SingleByteMask = 0x80;
34 static const int kUtf8SingleByteValue = 0x00;
35 // 2-byte encoding.
36 static const int kUtf8TwoByteMask = 0xE0;
37 static const int kUtf8TwoByteValue = 0xC0;
38 // 3-byte encoding.
39 static const int kUtf8ThreeByteMask = 0xF0;
40 static const int kUtf8ThreeByteValue = 0xE0;
41 // 4-byte encoding.
42 static const int kUtf8FourByteMask = 0xF8;
43 static const int kUtf8FourByteValue = 0xF0;
44 // Subsequent bytes of a multi-byte encoding.
45 static const int kMultiByteMask = 0xC0;
46 static const int kMultiByteValue = 0x80;
47 int multi_byte_bytes_seen = 0;
48 while (answer > 0) {
49 int c = buffer[answer - 1];
50 // Ends in valid single-byte sequence?
51 if ((c & kUtf8SingleByteMask) == kUtf8SingleByteValue) return answer;
52 // Ends in one or more subsequent bytes of a multi-byte value?
53 if ((c & kMultiByteMask) == kMultiByteValue) {
54 multi_byte_bytes_seen++;
55 answer--;
56 } else {
57 if ((c & kUtf8TwoByteMask) == kUtf8TwoByteValue) {
58 if (multi_byte_bytes_seen >= 1) {
59 return answer + 2;
60 }
61 return answer - 1;
62 } else if ((c & kUtf8ThreeByteMask) == kUtf8ThreeByteValue) {
63 if (multi_byte_bytes_seen >= 2) {
64 return answer + 3;
65 }
66 return answer - 1;
67 } else if ((c & kUtf8FourByteMask) == kUtf8FourByteValue) {
68 if (multi_byte_bytes_seen >= 3) {
69 return answer + 4;
70 }
71 return answer - 1;
72 } else {
73 return answer; // Malformed UTF-8.
74 }
75 }
76 }
77 return 0;
78 }
79
80 // Suspends the thread until there is data available from the child process.
81 // Returns false on timeout, true on data ready.
WaitOnFD(int fd, int read_timeout, int total_timeout, const struct timeval& start_time)82 static bool WaitOnFD(int fd, int read_timeout, int total_timeout,
83 const struct timeval& start_time) {
84 fd_set readfds, writefds, exceptfds;
85 struct timeval timeout;
86 int gone = 0;
87 if (total_timeout != -1) {
88 struct timeval time_now;
89 gettimeofday(&time_now, nullptr);
90 time_t seconds = time_now.tv_sec - start_time.tv_sec;
91 gone = static_cast<int>(seconds * 1000 +
92 (time_now.tv_usec - start_time.tv_usec) / 1000);
93 if (gone >= total_timeout) return false;
94 }
95 FD_ZERO(&readfds);
96 FD_ZERO(&writefds);
97 FD_ZERO(&exceptfds);
98 FD_SET(fd, &readfds);
99 FD_SET(fd, &exceptfds);
100 if (read_timeout == -1 ||
101 (total_timeout != -1 && total_timeout - gone < read_timeout)) {
102 read_timeout = total_timeout - gone;
103 }
104 timeout.tv_usec = (read_timeout % 1000) * 1000;
105 timeout.tv_sec = read_timeout / 1000;
106 int number_of_fds_ready = select(fd + 1, &readfds, &writefds, &exceptfds,
107 read_timeout != -1 ? &timeout : nullptr);
108 return number_of_fds_ready == 1;
109 }
110
111 // Checks whether we ran out of time on the timeout. Returns true if we ran out
112 // of time, false if we still have time.
TimeIsOut(const struct timeval& start_time, const int& total_time)113 static bool TimeIsOut(const struct timeval& start_time, const int& total_time) {
114 if (total_time == -1) return false;
115 struct timeval time_now;
116 gettimeofday(&time_now, nullptr);
117 // Careful about overflow.
118 int seconds = static_cast<int>(time_now.tv_sec - start_time.tv_sec);
119 if (seconds > 100) {
120 if (seconds * 1000 > total_time) return true;
121 return false;
122 }
123 int useconds = static_cast<int>(time_now.tv_usec - start_time.tv_usec);
124 if (seconds * 1000000 + useconds > total_time * 1000) {
125 return true;
126 }
127 return false;
128 }
129
130 // A utility class that does a non-hanging waitpid on the child process if we
131 // bail out of the System() function early. If you don't ever do a waitpid on
132 // a subprocess then it turns into one of those annoying 'zombie processes'.
133 class ZombieProtector {
134 public:
ZombieProtector(int pid)135 explicit ZombieProtector(int pid) : pid_(pid) {}
~ZombieProtector()136 ~ZombieProtector() {
137 if (pid_ != 0) waitpid(pid_, nullptr, 0);
138 }
ChildIsDeadNow()139 void ChildIsDeadNow() { pid_ = 0; }
140
141 private:
142 int pid_;
143 };
144
145 // A utility class that closes a file descriptor when it goes out of scope.
146 class OpenFDCloser {
147 public:
OpenFDCloser(int fd)148 explicit OpenFDCloser(int fd) : fd_(fd) {}
~OpenFDCloser()149 ~OpenFDCloser() { close(fd_); }
150
151 private:
152 int fd_;
153 };
154
155 // A utility class that takes the array of command arguments and puts then in an
156 // array of new[]ed UTF-8 C strings. Deallocates them again when it goes out of
157 // scope.
158 class ExecArgs {
159 public:
ExecArgs()160 ExecArgs() { exec_args_[0] = nullptr; }
Init(Isolate* isolate, Local<Value> arg0, Local<Array> command_args)161 bool Init(Isolate* isolate, Local<Value> arg0, Local<Array> command_args) {
162 String::Utf8Value prog(isolate, arg0);
163 if (*prog == nullptr) {
164 isolate->ThrowError(
165 "os.system(): String conversion of program name failed");
166 return false;
167 }
168 {
169 int len = prog.length() + 3;
170 char* c_arg = new char[len];
171 snprintf(c_arg, len, "%s", *prog);
172 exec_args_[0] = c_arg;
173 }
174 int i = 1;
175 for (unsigned j = 0; j < command_args->Length(); i++, j++) {
176 Local<Value> arg(
177 command_args
178 ->Get(isolate->GetCurrentContext(), Integer::New(isolate, j))
179 .ToLocalChecked());
180 String::Utf8Value utf8_arg(isolate, arg);
181 if (*utf8_arg == nullptr) {
182 exec_args_[i] = nullptr; // Consistent state for destructor.
183 isolate->ThrowError(
184 "os.system(): String conversion of argument failed.");
185 return false;
186 }
187 int len = utf8_arg.length() + 1;
188 char* c_arg = new char[len];
189 snprintf(c_arg, len, "%s", *utf8_arg);
190 exec_args_[i] = c_arg;
191 }
192 exec_args_[i] = nullptr;
193 return true;
194 }
~ExecArgs()195 ~ExecArgs() {
196 for (unsigned i = 0; i < kMaxArgs; i++) {
197 if (exec_args_[i] == nullptr) {
198 return;
199 }
200 delete[] exec_args_[i];
201 exec_args_[i] = nullptr;
202 }
203 }
204 static const unsigned kMaxArgs = 1000;
arg_array() const205 char* const* arg_array() const { return exec_args_; }
arg0() const206 const char* arg0() const { return exec_args_[0]; }
207
208 private:
209 char* exec_args_[kMaxArgs + 1];
210 };
211
212 // Gets the optional timeouts from the arguments to the system() call.
GetTimeouts(const v8::FunctionCallbackInfo<v8::Value>& args, int* read_timeout, int* total_timeout)213 static bool GetTimeouts(const v8::FunctionCallbackInfo<v8::Value>& args,
214 int* read_timeout, int* total_timeout) {
215 if (args.Length() > 3) {
216 if (args[3]->IsNumber()) {
217 *total_timeout = args[3]
218 ->Int32Value(args.GetIsolate()->GetCurrentContext())
219 .FromJust();
220 } else {
221 args.GetIsolate()->ThrowError("system: Argument 4 must be a number");
222 return false;
223 }
224 }
225 if (args.Length() > 2) {
226 if (args[2]->IsNumber()) {
227 *read_timeout = args[2]
228 ->Int32Value(args.GetIsolate()->GetCurrentContext())
229 .FromJust();
230 } else {
231 args.GetIsolate()->ThrowError("system: Argument 3 must be a number");
232 return false;
233 }
234 }
235 return true;
236 }
237
238 namespace {
v8_strerror(v8::Isolate* isolate, int err)239 v8::Local<v8::String> v8_strerror(v8::Isolate* isolate, int err) {
240 return v8::String::NewFromUtf8(isolate, strerror(err)).ToLocalChecked();
241 }
242 } // namespace
243
244 static const int kReadFD = 0;
245 static const int kWriteFD = 1;
246
247 // This is run in the child process after fork() but before exec(). It normally
248 // ends with the child process being replaced with the desired child program.
249 // It only returns if an error occurred.
ExecSubprocess(int* exec_error_fds, int* stdout_fds, const ExecArgs& exec_args)250 static void ExecSubprocess(int* exec_error_fds, int* stdout_fds,
251 const ExecArgs& exec_args) {
252 close(exec_error_fds[kReadFD]); // Don't need this in the child.
253 close(stdout_fds[kReadFD]); // Don't need this in the child.
254 close(1); // Close stdout.
255 dup2(stdout_fds[kWriteFD], 1); // Dup pipe fd to stdout.
256 close(stdout_fds[kWriteFD]); // Don't need the original fd now.
257 fcntl(exec_error_fds[kWriteFD], F_SETFD, FD_CLOEXEC);
258 execvp(exec_args.arg0(), exec_args.arg_array());
259 // Only get here if the exec failed. Write errno to the parent to tell
260 // them it went wrong. If it went well the pipe is closed.
261 int err = errno;
262 ssize_t bytes_written;
263 do {
264 bytes_written = write(exec_error_fds[kWriteFD], &err, sizeof(err));
265 } while (bytes_written == -1 && errno == EINTR);
266 // Return (and exit child process).
267 }
268
269 // Runs in the parent process. Checks that the child was able to exec (closing
270 // the file desriptor), or reports an error if it failed.
ChildLaunchedOK(Isolate* isolate, int* exec_error_fds)271 static bool ChildLaunchedOK(Isolate* isolate, int* exec_error_fds) {
272 ssize_t bytes_read;
273 int err;
274 do {
275 bytes_read = read(exec_error_fds[kReadFD], &err, sizeof(err));
276 } while (bytes_read == -1 && errno == EINTR);
277 if (bytes_read != 0) {
278 isolate->ThrowError(v8_strerror(isolate, err));
279 return false;
280 }
281 return true;
282 }
283
284 // Accumulates the output from the child in a string handle. Returns true if it
285 // succeeded or false if an exception was thrown.
GetStdout(Isolate* isolate, int child_fd, const struct timeval& start_time, int read_timeout, int total_timeout)286 static Local<Value> GetStdout(Isolate* isolate, int child_fd,
287 const struct timeval& start_time,
288 int read_timeout, int total_timeout) {
289 Local<String> accumulator = String::Empty(isolate);
290
291 int fullness = 0;
292 static const int kStdoutReadBufferSize = 4096;
293 char buffer[kStdoutReadBufferSize];
294
295 if (fcntl(child_fd, F_SETFL, O_NONBLOCK) != 0) {
296 return isolate->ThrowError(v8_strerror(isolate, errno));
297 }
298
299 int bytes_read;
300 do {
301 bytes_read = static_cast<int>(
302 read(child_fd, buffer + fullness, kStdoutReadBufferSize - fullness));
303 if (bytes_read == -1) {
304 if (errno == EAGAIN) {
305 if (!WaitOnFD(child_fd, read_timeout, total_timeout, start_time) ||
306 (TimeIsOut(start_time, total_timeout))) {
307 return isolate->ThrowError("Timed out waiting for output");
308 }
309 continue;
310 } else if (errno == EINTR) {
311 continue;
312 } else {
313 break;
314 }
315 }
316 if (bytes_read + fullness > 0) {
317 int length = bytes_read == 0 ? bytes_read + fullness
318 : LengthWithoutIncompleteUtf8(
319 buffer, bytes_read + fullness);
320 Local<String> addition =
321 String::NewFromUtf8(isolate, buffer, NewStringType::kNormal, length)
322 .ToLocalChecked();
323 accumulator = String::Concat(isolate, accumulator, addition);
324 fullness = bytes_read + fullness - length;
325 memcpy(buffer, buffer + length, fullness);
326 }
327 } while (bytes_read != 0);
328 return accumulator;
329 }
330
331 // Modern Linux has the waitid call, which is like waitpid, but more useful
332 // if you want a timeout. If we don't have waitid we can't limit the time
333 // waiting for the process to exit without losing the information about
334 // whether it exited normally. In the common case this doesn't matter because
335 // we don't get here before the child has closed stdout and most programs don't
336 // do that before they exit.
337 //
338 // We're disabling usage of waitid in Mac OS X because it doesn't work for us:
339 // a parent process hangs on waiting while a child process is already a zombie.
340 // See http://code.google.com/p/v8/issues/detail?id=401.
341 #if defined(WNOWAIT) && !defined(ANDROID) && !defined(__APPLE__) && \
342 !defined(__NetBSD__) && !defined(__Fuchsia__)
343 #if !defined(__FreeBSD__)
344 #define HAS_WAITID 1
345 #endif
346 #endif
347
348 // Get exit status of child.
WaitForChild(Isolate* isolate, int pid, ZombieProtector& child_waiter, const struct timeval& start_time, int read_timeout, int total_timeout)349 static bool WaitForChild(Isolate* isolate, int pid,
350 ZombieProtector& child_waiter,
351 const struct timeval& start_time, int read_timeout,
352 int total_timeout) {
353 #ifdef HAS_WAITID
354
355 siginfo_t child_info;
356 child_info.si_pid = 0;
357 int useconds = 1;
358 // Wait for child to exit.
359 while (child_info.si_pid == 0) {
360 waitid(P_PID, pid, &child_info, WEXITED | WNOHANG | WNOWAIT);
361 usleep(useconds);
362 if (useconds < 1000000) useconds <<= 1;
363 if ((read_timeout != -1 && useconds / 1000 > read_timeout) ||
364 (TimeIsOut(start_time, total_timeout))) {
365 isolate->ThrowError("Timed out waiting for process to terminate");
366 kill(pid, SIGINT);
367 return false;
368 }
369 }
370 if (child_info.si_code == CLD_KILLED) {
371 char message[999];
372 snprintf(message, sizeof(message), "Child killed by signal %d",
373 child_info.si_status);
374 isolate->ThrowError(message);
375 return false;
376 }
377 if (child_info.si_code == CLD_EXITED && child_info.si_status != 0) {
378 char message[999];
379 snprintf(message, sizeof(message), "Child exited with status %d",
380 child_info.si_status);
381 isolate->ThrowError(message);
382 return false;
383 }
384
385 #else // No waitid call.
386
387 int child_status;
388 waitpid(pid, &child_status, 0); // We hang here if the child doesn't exit.
389 child_waiter.ChildIsDeadNow();
390 if (WIFSIGNALED(child_status)) {
391 char message[999];
392 snprintf(message, sizeof(message), "Child killed by signal %d",
393 WTERMSIG(child_status));
394 isolate->ThrowError(message);
395 return false;
396 }
397 if (WEXITSTATUS(child_status) != 0) {
398 char message[999];
399 int exit_status = WEXITSTATUS(child_status);
400 snprintf(message, sizeof(message), "Child exited with status %d",
401 exit_status);
402 isolate->ThrowError(message);
403 return false;
404 }
405
406 #endif // No waitid call.
407
408 return true;
409 }
410
411 #undef HAS_WAITID
412
413 // Implementation of the system() function (see d8.h for details).
System(const v8::FunctionCallbackInfo<v8::Value>& args)414 void Shell::System(const v8::FunctionCallbackInfo<v8::Value>& args) {
415 HandleScope scope(args.GetIsolate());
416 int read_timeout = -1;
417 int total_timeout = -1;
418 if (!GetTimeouts(args, &read_timeout, &total_timeout)) return;
419 Local<Array> command_args;
420 if (args.Length() > 1) {
421 if (!args[1]->IsArray()) {
422 args.GetIsolate()->ThrowError("system: Argument 2 must be an array");
423 return;
424 }
425 command_args = args[1].As<Array>();
426 } else {
427 command_args = Array::New(args.GetIsolate(), 0);
428 }
429 if (command_args->Length() > ExecArgs::kMaxArgs) {
430 args.GetIsolate()->ThrowError("Too many arguments to system()");
431 return;
432 }
433 if (args.Length() < 1) {
434 args.GetIsolate()->ThrowError("Too few arguments to system()");
435 return;
436 }
437
438 struct timeval start_time;
439 gettimeofday(&start_time, nullptr);
440
441 ExecArgs exec_args;
442 if (!exec_args.Init(args.GetIsolate(), args[0], command_args)) {
443 return;
444 }
445 int exec_error_fds[2];
446 int stdout_fds[2];
447
448 if (pipe(exec_error_fds) != 0) {
449 args.GetIsolate()->ThrowError("pipe syscall failed.");
450 return;
451 }
452 if (pipe(stdout_fds) != 0) {
453 args.GetIsolate()->ThrowError("pipe syscall failed.");
454 return;
455 }
456
457 pid_t pid = fork();
458 if (pid == 0) { // Child process.
459 ExecSubprocess(exec_error_fds, stdout_fds, exec_args);
460 exit(1);
461 }
462
463 // Parent process. Ensure that we clean up if we exit this function early.
464 ZombieProtector child_waiter(pid);
465 close(exec_error_fds[kWriteFD]);
466 close(stdout_fds[kWriteFD]);
467 OpenFDCloser error_read_closer(exec_error_fds[kReadFD]);
468 OpenFDCloser stdout_read_closer(stdout_fds[kReadFD]);
469
470 Isolate* isolate = args.GetIsolate();
471 if (!ChildLaunchedOK(isolate, exec_error_fds)) return;
472
473 Local<Value> accumulator = GetStdout(isolate, stdout_fds[kReadFD], start_time,
474 read_timeout, total_timeout);
475 if (accumulator->IsUndefined()) {
476 kill(pid, SIGINT); // On timeout, kill the subprocess.
477 args.GetReturnValue().Set(accumulator);
478 return;
479 }
480
481 if (!WaitForChild(isolate, pid, child_waiter, start_time, read_timeout,
482 total_timeout)) {
483 return;
484 }
485
486 args.GetReturnValue().Set(accumulator);
487 }
488
ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args)489 void Shell::ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
490 if (args.Length() != 1) {
491 args.GetIsolate()->ThrowError("chdir() takes one argument");
492 return;
493 }
494 String::Utf8Value directory(args.GetIsolate(), args[0]);
495 if (*directory == nullptr) {
496 args.GetIsolate()->ThrowError(
497 "os.chdir(): String conversion of argument failed.");
498 return;
499 }
500 if (chdir(*directory) != 0) {
501 args.GetIsolate()->ThrowError(v8_strerror(args.GetIsolate(), errno));
502 return;
503 }
504 }
505
SetUMask(const v8::FunctionCallbackInfo<v8::Value>& args)506 void Shell::SetUMask(const v8::FunctionCallbackInfo<v8::Value>& args) {
507 if (args.Length() != 1) {
508 args.GetIsolate()->ThrowError("umask() takes one argument");
509 return;
510 }
511 if (args[0]->IsNumber()) {
512 int previous = umask(
513 args[0]->Int32Value(args.GetIsolate()->GetCurrentContext()).FromJust());
514 args.GetReturnValue().Set(previous);
515 return;
516 } else {
517 args.GetIsolate()->ThrowError("umask() argument must be numeric");
518 return;
519 }
520 }
521
CheckItsADirectory(Isolate* isolate, char* directory)522 static bool CheckItsADirectory(Isolate* isolate, char* directory) {
523 struct stat stat_buf;
524 int stat_result = stat(directory, &stat_buf);
525 if (stat_result != 0) {
526 isolate->ThrowError(v8_strerror(isolate, errno));
527 return false;
528 }
529 if ((stat_buf.st_mode & S_IFDIR) != 0) return true;
530 isolate->ThrowError(v8_strerror(isolate, EEXIST));
531 return false;
532 }
533
534 // Returns true for success. Creates intermediate directories as needed. No
535 // error if the directory exists already.
mkdirp(Isolate* isolate, char* directory, mode_t mask)536 static bool mkdirp(Isolate* isolate, char* directory, mode_t mask) {
537 int result = mkdir(directory, mask);
538 if (result == 0) return true;
539 if (errno == EEXIST) {
540 return CheckItsADirectory(isolate, directory);
541 } else if (errno == ENOENT) { // Intermediate path element is missing.
542 char* last_slash = strrchr(directory, '/');
543 if (last_slash == nullptr) {
544 isolate->ThrowError(v8_strerror(isolate, errno));
545 return false;
546 }
547 *last_slash = 0;
548 if (!mkdirp(isolate, directory, mask)) return false;
549 *last_slash = '/';
550 result = mkdir(directory, mask);
551 if (result == 0) return true;
552 if (errno == EEXIST) {
553 return CheckItsADirectory(isolate, directory);
554 }
555 isolate->ThrowError(v8_strerror(isolate, errno));
556 return false;
557 } else {
558 isolate->ThrowError(v8_strerror(isolate, errno));
559 return false;
560 }
561 }
562
MakeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args)563 void Shell::MakeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
564 mode_t mask = 0777;
565 if (args.Length() == 2) {
566 if (args[1]->IsNumber()) {
567 mask = args[1]
568 ->Int32Value(args.GetIsolate()->GetCurrentContext())
569 .FromJust();
570 } else {
571 args.GetIsolate()->ThrowError("mkdirp() second argument must be numeric");
572 return;
573 }
574 } else if (args.Length() != 1) {
575 args.GetIsolate()->ThrowError("mkdirp() takes one or two arguments");
576 return;
577 }
578 String::Utf8Value directory(args.GetIsolate(), args[0]);
579 if (*directory == nullptr) {
580 args.GetIsolate()->ThrowError(
581 "os.mkdirp(): String conversion of argument failed.");
582 return;
583 }
584 mkdirp(args.GetIsolate(), *directory, mask);
585 }
586
RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value>& args)587 void Shell::RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
588 if (args.Length() != 1) {
589 args.GetIsolate()->ThrowError("rmdir() takes one or two arguments");
590 return;
591 }
592 String::Utf8Value directory(args.GetIsolate(), args[0]);
593 if (*directory == nullptr) {
594 args.GetIsolate()->ThrowError(
595 "os.rmdir(): String conversion of argument failed.");
596 return;
597 }
598 rmdir(*directory);
599 }
600
SetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args)601 void Shell::SetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
602 if (args.Length() != 2) {
603 args.GetIsolate()->ThrowError("setenv() takes two arguments");
604 return;
605 }
606 String::Utf8Value var(args.GetIsolate(), args[0]);
607 String::Utf8Value value(args.GetIsolate(), args[1]);
608 if (*var == nullptr) {
609 args.GetIsolate()->ThrowError(
610 "os.setenv(): String conversion of variable name failed.");
611 return;
612 }
613 if (*value == nullptr) {
614 args.GetIsolate()->ThrowError(
615 "os.setenv(): String conversion of variable contents failed.");
616 return;
617 }
618 setenv(*var, *value, 1);
619 }
620
UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args)621 void Shell::UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
622 if (args.Length() != 1) {
623 args.GetIsolate()->ThrowError("unsetenv() takes one argument");
624 return;
625 }
626 String::Utf8Value var(args.GetIsolate(), args[0]);
627 if (*var == nullptr) {
628 args.GetIsolate()->ThrowError(
629 "os.setenv(): String conversion of variable name failed.");
630 return;
631 }
632 unsetenv(*var);
633 }
634
ReadCharsFromTcpPort(const char* name, int* size_out)635 char* Shell::ReadCharsFromTcpPort(const char* name, int* size_out) {
636 DCHECK_GE(Shell::options.read_from_tcp_port, 0);
637
638 int sockfd = socket(PF_INET, SOCK_STREAM, 0);
639 if (sockfd < 0) {
640 fprintf(stderr, "Failed to create IPv4 socket\n");
641 return nullptr;
642 }
643
644 // Create an address for localhost:PORT where PORT is specified by the shell
645 // option --read-from-tcp-port.
646 sockaddr_in serv_addr;
647 memset(&serv_addr, 0, sizeof(sockaddr_in));
648 serv_addr.sin_family = AF_INET;
649 serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
650 serv_addr.sin_port = htons(Shell::options.read_from_tcp_port);
651
652 if (connect(sockfd, reinterpret_cast<sockaddr*>(&serv_addr),
653 sizeof(serv_addr)) < 0) {
654 fprintf(stderr, "Failed to connect to localhost:%d\n",
655 Shell::options.read_from_tcp_port.get());
656 close(sockfd);
657 return nullptr;
658 }
659
660 // The file server follows the simple protocol for requesting and receiving
661 // a file with a given filename:
662 //
663 // REQUEST client -> server: {filename}"\0"
664 // RESPONSE server -> client: {4-byte file-length}{file contents}
665 //
666 // i.e. the request sends the filename with a null terminator, and response
667 // sends the file contents by sending the length (as a 4-byte big-endian
668 // value) and the contents.
669
670 // If the file length is <0, there was an error sending the file, and the
671 // rest of the response is undefined (and may, in the future, contain an error
672 // message). The socket should be closed to avoid trying to interpret the
673 // undefined data.
674
675 // REQUEST
676 // Send the filename.
677 size_t sent_len = 0;
678 size_t name_len = strlen(name) + 1; // Includes the null terminator
679 while (sent_len < name_len) {
680 ssize_t sent_now = send(sockfd, name + sent_len, name_len - sent_len, 0);
681 if (sent_now < 0) {
682 fprintf(stderr, "Failed to send %s to localhost:%d\n", name,
683 Shell::options.read_from_tcp_port.get());
684 close(sockfd);
685 return nullptr;
686 }
687 sent_len += sent_now;
688 }
689
690 // RESPONSE
691 // Receive the file.
692 ssize_t received = 0;
693
694 // First, read the (zero-terminated) file length.
695 uint32_t big_endian_file_length;
696 received = recv(sockfd, &big_endian_file_length, 4, 0);
697 // We need those 4 bytes to read off the file length.
698 if (received < 4) {
699 fprintf(stderr, "Failed to receive %s's length from localhost:%d\n", name,
700 Shell::options.read_from_tcp_port.get());
701 close(sockfd);
702 return nullptr;
703 }
704 // Reinterpretet the received file length as a signed big-endian integer.
705 int32_t file_length = bit_cast<int32_t>(htonl(big_endian_file_length));
706
707 if (file_length < 0) {
708 fprintf(stderr, "Received length %d for %s from localhost:%d\n",
709 file_length, name, Shell::options.read_from_tcp_port.get());
710 close(sockfd);
711 return nullptr;
712 }
713
714 // Allocate the output array.
715 char* chars = new char[file_length];
716
717 // Now keep receiving and copying until the whole file is received.
718 ssize_t total_received = 0;
719 while (total_received < file_length) {
720 received =
721 recv(sockfd, chars + total_received, file_length - total_received, 0);
722 if (received < 0) {
723 fprintf(stderr, "Failed to receive %s from localhost:%d\n", name,
724 Shell::options.read_from_tcp_port.get());
725 close(sockfd);
726 delete[] chars;
727 return nullptr;
728 }
729 total_received += received;
730 }
731
732 close(sockfd);
733 *size_out = file_length;
734 return chars;
735 }
736
AddOSMethods(Isolate* isolate, Local<ObjectTemplate> os_templ)737 void Shell::AddOSMethods(Isolate* isolate, Local<ObjectTemplate> os_templ) {
738 if (options.enable_os_system) {
739 os_templ->Set(isolate, "system", FunctionTemplate::New(isolate, System));
740 }
741 os_templ->Set(isolate, "chdir",
742 FunctionTemplate::New(isolate, ChangeDirectory));
743 os_templ->Set(isolate, "setenv",
744 FunctionTemplate::New(isolate, SetEnvironment));
745 os_templ->Set(isolate, "unsetenv",
746 FunctionTemplate::New(isolate, UnsetEnvironment));
747 os_templ->Set(isolate, "umask", FunctionTemplate::New(isolate, SetUMask));
748 os_templ->Set(isolate, "mkdirp",
749 FunctionTemplate::New(isolate, MakeDirectory));
750 os_templ->Set(isolate, "rmdir",
751 FunctionTemplate::New(isolate, RemoveDirectory));
752 }
753
754 } // namespace v8
755