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
6 #include <stdlib.h>
7 #include <errno.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <sys/time.h>
11 #include <time.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <sys/wait.h>
15 #include <signal.h>
16
17
18 #include "src/d8.h"
19 #include "src/d8-debug.h"
20 #include "src/debug.h"
21
22
23 namespace v8 {
24
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
81 // Suspends the thread until there is data available from the child process.
82 // Returns false on timeout, true on data ready.
WaitOnFD(int fd,int read_timeout,int total_timeout,const struct timeval & start_time)83 static bool WaitOnFD(int fd,
84 int read_timeout,
85 int total_timeout,
86 const struct timeval& start_time) {
87 fd_set readfds, writefds, exceptfds;
88 struct timeval timeout;
89 int gone = 0;
90 if (total_timeout != -1) {
91 struct timeval time_now;
92 gettimeofday(&time_now, NULL);
93 int seconds = time_now.tv_sec - start_time.tv_sec;
94 gone = seconds * 1000 + (time_now.tv_usec - start_time.tv_usec) / 1000;
95 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;
108 int number_of_fds_ready = select(fd + 1,
109 &readfds,
110 &writefds,
111 &exceptfds,
112 read_timeout != -1 ? &timeout : NULL);
113 return number_of_fds_ready == 1;
114 }
115
116
117 // Checks whether we ran out of time on the timeout. Returns true if we ran out
118 // of time, false if we still have time.
TimeIsOut(const struct timeval & start_time,const int & total_time)119 static bool TimeIsOut(const struct timeval& start_time, const int& total_time) {
120 if (total_time == -1) return false;
121 struct timeval time_now;
122 gettimeofday(&time_now, NULL);
123 // Careful about overflow.
124 int seconds = time_now.tv_sec - start_time.tv_sec;
125 if (seconds > 100) {
126 if (seconds * 1000 > total_time) return true;
127 return false;
128 }
129 int useconds = time_now.tv_usec - start_time.tv_usec;
130 if (seconds * 1000000 + useconds > total_time * 1000) {
131 return true;
132 }
133 return false;
134 }
135
136
137 // A utility class that does a non-hanging waitpid on the child process if we
138 // bail out of the System() function early. If you don't ever do a waitpid on
139 // a subprocess then it turns into one of those annoying 'zombie processes'.
140 class ZombieProtector {
141 public:
ZombieProtector(int pid)142 explicit ZombieProtector(int pid): pid_(pid) { }
~ZombieProtector()143 ~ZombieProtector() { if (pid_ != 0) waitpid(pid_, NULL, 0); }
ChildIsDeadNow()144 void ChildIsDeadNow() { pid_ = 0; }
145 private:
146 int pid_;
147 };
148
149
150 // A utility class that closes a file descriptor when it goes out of scope.
151 class OpenFDCloser {
152 public:
OpenFDCloser(int fd)153 explicit OpenFDCloser(int fd): fd_(fd) { }
~OpenFDCloser()154 ~OpenFDCloser() { close(fd_); }
155 private:
156 int fd_;
157 };
158
159
160 // A utility class that takes the array of command arguments and puts then in an
161 // array of new[]ed UTF-8 C strings. Deallocates them again when it goes out of
162 // scope.
163 class ExecArgs {
164 public:
ExecArgs()165 ExecArgs() {
166 exec_args_[0] = NULL;
167 }
Init(Isolate * isolate,Handle<Value> arg0,Handle<Array> command_args)168 bool Init(Isolate* isolate, Handle<Value> arg0, Handle<Array> command_args) {
169 String::Utf8Value prog(arg0);
170 if (*prog == NULL) {
171 const char* message =
172 "os.system(): String conversion of program name failed";
173 isolate->ThrowException(String::NewFromUtf8(isolate, message));
174 return false;
175 }
176 int len = prog.length() + 3;
177 char* c_arg = new char[len];
178 snprintf(c_arg, len, "%s", *prog);
179 exec_args_[0] = c_arg;
180 int i = 1;
181 for (unsigned j = 0; j < command_args->Length(); i++, j++) {
182 Handle<Value> arg(command_args->Get(Integer::New(isolate, j)));
183 String::Utf8Value utf8_arg(arg);
184 if (*utf8_arg == NULL) {
185 exec_args_[i] = NULL; // Consistent state for destructor.
186 const char* message =
187 "os.system(): String conversion of argument failed.";
188 isolate->ThrowException(String::NewFromUtf8(isolate, message));
189 return false;
190 }
191 int len = utf8_arg.length() + 1;
192 char* c_arg = new char[len];
193 snprintf(c_arg, len, "%s", *utf8_arg);
194 exec_args_[i] = c_arg;
195 }
196 exec_args_[i] = NULL;
197 return true;
198 }
~ExecArgs()199 ~ExecArgs() {
200 for (unsigned i = 0; i < kMaxArgs; i++) {
201 if (exec_args_[i] == NULL) {
202 return;
203 }
204 delete [] exec_args_[i];
205 exec_args_[i] = 0;
206 }
207 }
208 static const unsigned kMaxArgs = 1000;
arg_array() const209 char* const* arg_array() const { return exec_args_; }
arg0() const210 const char* arg0() const { return exec_args_[0]; }
211
212 private:
213 char* exec_args_[kMaxArgs + 1];
214 };
215
216
217 // Gets the optional timeouts from the arguments to the system() call.
GetTimeouts(const v8::FunctionCallbackInfo<v8::Value> & args,int * read_timeout,int * total_timeout)218 static bool GetTimeouts(const v8::FunctionCallbackInfo<v8::Value>& args,
219 int* read_timeout,
220 int* total_timeout) {
221 if (args.Length() > 3) {
222 if (args[3]->IsNumber()) {
223 *total_timeout = args[3]->Int32Value();
224 } else {
225 args.GetIsolate()->ThrowException(String::NewFromUtf8(
226 args.GetIsolate(), "system: Argument 4 must be a number"));
227 return false;
228 }
229 }
230 if (args.Length() > 2) {
231 if (args[2]->IsNumber()) {
232 *read_timeout = args[2]->Int32Value();
233 } else {
234 args.GetIsolate()->ThrowException(String::NewFromUtf8(
235 args.GetIsolate(), "system: Argument 3 must be a number"));
236 return false;
237 }
238 }
239 return true;
240 }
241
242
243 static const int kReadFD = 0;
244 static const int kWriteFD = 1;
245
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,
251 int* stdout_fds,
252 const ExecArgs& exec_args) {
253 close(exec_error_fds[kReadFD]); // Don't need this in the child.
254 close(stdout_fds[kReadFD]); // Don't need this in the child.
255 close(1); // Close stdout.
256 dup2(stdout_fds[kWriteFD], 1); // Dup pipe fd to stdout.
257 close(stdout_fds[kWriteFD]); // Don't need the original fd now.
258 fcntl(exec_error_fds[kWriteFD], F_SETFD, FD_CLOEXEC);
259 execvp(exec_args.arg0(), exec_args.arg_array());
260 // Only get here if the exec failed. Write errno to the parent to tell
261 // them it went wrong. If it went well the pipe is closed.
262 int err = errno;
263 int bytes_written;
264 do {
265 bytes_written = write(exec_error_fds[kWriteFD], &err, sizeof(err));
266 } while (bytes_written == -1 && errno == EINTR);
267 // Return (and exit child process).
268 }
269
270
271 // Runs in the parent process. Checks that the child was able to exec (closing
272 // the file desriptor), or reports an error if it failed.
ChildLaunchedOK(Isolate * isolate,int * exec_error_fds)273 static bool ChildLaunchedOK(Isolate* isolate, int* exec_error_fds) {
274 int bytes_read;
275 int err;
276 do {
277 bytes_read = read(exec_error_fds[kReadFD], &err, sizeof(err));
278 } while (bytes_read == -1 && errno == EINTR);
279 if (bytes_read != 0) {
280 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(err)));
281 return false;
282 }
283 return true;
284 }
285
286
287 // Accumulates the output from the child in a string handle. Returns true if it
288 // 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)289 static Handle<Value> GetStdout(Isolate* isolate,
290 int child_fd,
291 const struct timeval& start_time,
292 int read_timeout,
293 int total_timeout) {
294 Handle<String> accumulator = String::Empty(isolate);
295
296 int fullness = 0;
297 static const int kStdoutReadBufferSize = 4096;
298 char buffer[kStdoutReadBufferSize];
299
300 if (fcntl(child_fd, F_SETFL, O_NONBLOCK) != 0) {
301 return isolate->ThrowException(
302 String::NewFromUtf8(isolate, strerror(errno)));
303 }
304
305 int bytes_read;
306 do {
307 bytes_read = read(child_fd,
308 buffer + fullness,
309 kStdoutReadBufferSize - fullness);
310 if (bytes_read == -1) {
311 if (errno == EAGAIN) {
312 if (!WaitOnFD(child_fd,
313 read_timeout,
314 total_timeout,
315 start_time) ||
316 (TimeIsOut(start_time, total_timeout))) {
317 return isolate->ThrowException(
318 String::NewFromUtf8(isolate, "Timed out waiting for output"));
319 }
320 continue;
321 } else if (errno == EINTR) {
322 continue;
323 } else {
324 break;
325 }
326 }
327 if (bytes_read + fullness > 0) {
328 int length = bytes_read == 0 ?
329 bytes_read + fullness :
330 LengthWithoutIncompleteUtf8(buffer, bytes_read + fullness);
331 Handle<String> addition =
332 String::NewFromUtf8(isolate, buffer, String::kNormalString, length);
333 accumulator = String::Concat(accumulator, addition);
334 fullness = bytes_read + fullness - length;
335 memcpy(buffer, buffer + length, fullness);
336 }
337 } while (bytes_read != 0);
338 return accumulator;
339 }
340
341
342 // Modern Linux has the waitid call, which is like waitpid, but more useful
343 // if you want a timeout. If we don't have waitid we can't limit the time
344 // waiting for the process to exit without losing the information about
345 // whether it exited normally. In the common case this doesn't matter because
346 // we don't get here before the child has closed stdout and most programs don't
347 // do that before they exit.
348 //
349 // We're disabling usage of waitid in Mac OS X because it doens't work for us:
350 // a parent process hangs on waiting while a child process is already a zombie.
351 // See http://code.google.com/p/v8/issues/detail?id=401.
352 #if defined(WNOWAIT) && !defined(ANDROID) && !defined(__APPLE__) \
353 && !defined(__NetBSD__)
354 #if !defined(__FreeBSD__)
355 #define HAS_WAITID 1
356 #endif
357 #endif
358
359
360 // Get exit status of child.
WaitForChild(Isolate * isolate,int pid,ZombieProtector & child_waiter,const struct timeval & start_time,int read_timeout,int total_timeout)361 static bool WaitForChild(Isolate* isolate,
362 int pid,
363 ZombieProtector& child_waiter, // NOLINT
364 const struct timeval& start_time,
365 int read_timeout,
366 int total_timeout) {
367 #ifdef HAS_WAITID
368
369 siginfo_t child_info;
370 child_info.si_pid = 0;
371 int useconds = 1;
372 // Wait for child to exit.
373 while (child_info.si_pid == 0) {
374 waitid(P_PID, pid, &child_info, WEXITED | WNOHANG | WNOWAIT);
375 usleep(useconds);
376 if (useconds < 1000000) useconds <<= 1;
377 if ((read_timeout != -1 && useconds / 1000 > read_timeout) ||
378 (TimeIsOut(start_time, total_timeout))) {
379 isolate->ThrowException(String::NewFromUtf8(
380 isolate, "Timed out waiting for process to terminate"));
381 kill(pid, SIGINT);
382 return false;
383 }
384 }
385 if (child_info.si_code == CLD_KILLED) {
386 char message[999];
387 snprintf(message,
388 sizeof(message),
389 "Child killed by signal %d",
390 child_info.si_status);
391 isolate->ThrowException(String::NewFromUtf8(isolate, message));
392 return false;
393 }
394 if (child_info.si_code == CLD_EXITED && child_info.si_status != 0) {
395 char message[999];
396 snprintf(message,
397 sizeof(message),
398 "Child exited with status %d",
399 child_info.si_status);
400 isolate->ThrowException(String::NewFromUtf8(isolate, message));
401 return false;
402 }
403
404 #else // No waitid call.
405
406 int child_status;
407 waitpid(pid, &child_status, 0); // We hang here if the child doesn't exit.
408 child_waiter.ChildIsDeadNow();
409 if (WIFSIGNALED(child_status)) {
410 char message[999];
411 snprintf(message,
412 sizeof(message),
413 "Child killed by signal %d",
414 WTERMSIG(child_status));
415 isolate->ThrowException(String::NewFromUtf8(isolate, message));
416 return false;
417 }
418 if (WEXITSTATUS(child_status) != 0) {
419 char message[999];
420 int exit_status = WEXITSTATUS(child_status);
421 snprintf(message,
422 sizeof(message),
423 "Child exited with status %d",
424 exit_status);
425 isolate->ThrowException(String::NewFromUtf8(isolate, message));
426 return false;
427 }
428
429 #endif // No waitid call.
430
431 return true;
432 }
433
434
435 // Implementation of the system() function (see d8.h for details).
System(const v8::FunctionCallbackInfo<v8::Value> & args)436 void Shell::System(const v8::FunctionCallbackInfo<v8::Value>& args) {
437 HandleScope scope(args.GetIsolate());
438 int read_timeout = -1;
439 int total_timeout = -1;
440 if (!GetTimeouts(args, &read_timeout, &total_timeout)) return;
441 Handle<Array> command_args;
442 if (args.Length() > 1) {
443 if (!args[1]->IsArray()) {
444 args.GetIsolate()->ThrowException(String::NewFromUtf8(
445 args.GetIsolate(), "system: Argument 2 must be an array"));
446 return;
447 }
448 command_args = Handle<Array>::Cast(args[1]);
449 } else {
450 command_args = Array::New(args.GetIsolate(), 0);
451 }
452 if (command_args->Length() > ExecArgs::kMaxArgs) {
453 args.GetIsolate()->ThrowException(String::NewFromUtf8(
454 args.GetIsolate(), "Too many arguments to system()"));
455 return;
456 }
457 if (args.Length() < 1) {
458 args.GetIsolate()->ThrowException(String::NewFromUtf8(
459 args.GetIsolate(), "Too few arguments to system()"));
460 return;
461 }
462
463 struct timeval start_time;
464 gettimeofday(&start_time, NULL);
465
466 ExecArgs exec_args;
467 if (!exec_args.Init(args.GetIsolate(), args[0], command_args)) {
468 return;
469 }
470 int exec_error_fds[2];
471 int stdout_fds[2];
472
473 if (pipe(exec_error_fds) != 0) {
474 args.GetIsolate()->ThrowException(
475 String::NewFromUtf8(args.GetIsolate(), "pipe syscall failed."));
476 return;
477 }
478 if (pipe(stdout_fds) != 0) {
479 args.GetIsolate()->ThrowException(
480 String::NewFromUtf8(args.GetIsolate(), "pipe syscall failed."));
481 return;
482 }
483
484 pid_t pid = fork();
485 if (pid == 0) { // Child process.
486 ExecSubprocess(exec_error_fds, stdout_fds, exec_args);
487 exit(1);
488 }
489
490 // Parent process. Ensure that we clean up if we exit this function early.
491 ZombieProtector child_waiter(pid);
492 close(exec_error_fds[kWriteFD]);
493 close(stdout_fds[kWriteFD]);
494 OpenFDCloser error_read_closer(exec_error_fds[kReadFD]);
495 OpenFDCloser stdout_read_closer(stdout_fds[kReadFD]);
496
497 if (!ChildLaunchedOK(args.GetIsolate(), exec_error_fds)) return;
498
499 Handle<Value> accumulator = GetStdout(args.GetIsolate(),
500 stdout_fds[kReadFD],
501 start_time,
502 read_timeout,
503 total_timeout);
504 if (accumulator->IsUndefined()) {
505 kill(pid, SIGINT); // On timeout, kill the subprocess.
506 args.GetReturnValue().Set(accumulator);
507 return;
508 }
509
510 if (!WaitForChild(args.GetIsolate(),
511 pid,
512 child_waiter,
513 start_time,
514 read_timeout,
515 total_timeout)) {
516 return;
517 }
518
519 args.GetReturnValue().Set(accumulator);
520 }
521
522
ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value> & args)523 void Shell::ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
524 if (args.Length() != 1) {
525 const char* message = "chdir() takes one argument";
526 args.GetIsolate()->ThrowException(
527 String::NewFromUtf8(args.GetIsolate(), message));
528 return;
529 }
530 String::Utf8Value directory(args[0]);
531 if (*directory == NULL) {
532 const char* message = "os.chdir(): String conversion of argument failed.";
533 args.GetIsolate()->ThrowException(
534 String::NewFromUtf8(args.GetIsolate(), message));
535 return;
536 }
537 if (chdir(*directory) != 0) {
538 args.GetIsolate()->ThrowException(
539 String::NewFromUtf8(args.GetIsolate(), strerror(errno)));
540 return;
541 }
542 }
543
544
SetUMask(const v8::FunctionCallbackInfo<v8::Value> & args)545 void Shell::SetUMask(const v8::FunctionCallbackInfo<v8::Value>& args) {
546 if (args.Length() != 1) {
547 const char* message = "umask() takes one argument";
548 args.GetIsolate()->ThrowException(
549 String::NewFromUtf8(args.GetIsolate(), message));
550 return;
551 }
552 if (args[0]->IsNumber()) {
553 mode_t mask = args[0]->Int32Value();
554 int previous = umask(mask);
555 args.GetReturnValue().Set(previous);
556 return;
557 } else {
558 const char* message = "umask() argument must be numeric";
559 args.GetIsolate()->ThrowException(
560 String::NewFromUtf8(args.GetIsolate(), message));
561 return;
562 }
563 }
564
565
CheckItsADirectory(Isolate * isolate,char * directory)566 static bool CheckItsADirectory(Isolate* isolate, char* directory) {
567 struct stat stat_buf;
568 int stat_result = stat(directory, &stat_buf);
569 if (stat_result != 0) {
570 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
571 return false;
572 }
573 if ((stat_buf.st_mode & S_IFDIR) != 0) return true;
574 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(EEXIST)));
575 return false;
576 }
577
578
579 // Returns true for success. Creates intermediate directories as needed. No
580 // error if the directory exists already.
mkdirp(Isolate * isolate,char * directory,mode_t mask)581 static bool mkdirp(Isolate* isolate, char* directory, mode_t mask) {
582 int result = mkdir(directory, mask);
583 if (result == 0) return true;
584 if (errno == EEXIST) {
585 return CheckItsADirectory(isolate, directory);
586 } else if (errno == ENOENT) { // Intermediate path element is missing.
587 char* last_slash = strrchr(directory, '/');
588 if (last_slash == NULL) {
589 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
590 return false;
591 }
592 *last_slash = 0;
593 if (!mkdirp(isolate, directory, mask)) return false;
594 *last_slash = '/';
595 result = mkdir(directory, mask);
596 if (result == 0) return true;
597 if (errno == EEXIST) {
598 return CheckItsADirectory(isolate, directory);
599 }
600 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
601 return false;
602 } else {
603 isolate->ThrowException(String::NewFromUtf8(isolate, strerror(errno)));
604 return false;
605 }
606 }
607
608
MakeDirectory(const v8::FunctionCallbackInfo<v8::Value> & args)609 void Shell::MakeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
610 mode_t mask = 0777;
611 if (args.Length() == 2) {
612 if (args[1]->IsNumber()) {
613 mask = args[1]->Int32Value();
614 } else {
615 const char* message = "mkdirp() second argument must be numeric";
616 args.GetIsolate()->ThrowException(
617 String::NewFromUtf8(args.GetIsolate(), message));
618 return;
619 }
620 } else if (args.Length() != 1) {
621 const char* message = "mkdirp() takes one or two arguments";
622 args.GetIsolate()->ThrowException(
623 String::NewFromUtf8(args.GetIsolate(), message));
624 return;
625 }
626 String::Utf8Value directory(args[0]);
627 if (*directory == NULL) {
628 const char* message = "os.mkdirp(): String conversion of argument failed.";
629 args.GetIsolate()->ThrowException(
630 String::NewFromUtf8(args.GetIsolate(), message));
631 return;
632 }
633 mkdirp(args.GetIsolate(), *directory, mask);
634 }
635
636
RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value> & args)637 void Shell::RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
638 if (args.Length() != 1) {
639 const char* message = "rmdir() takes one or two arguments";
640 args.GetIsolate()->ThrowException(
641 String::NewFromUtf8(args.GetIsolate(), message));
642 return;
643 }
644 String::Utf8Value directory(args[0]);
645 if (*directory == NULL) {
646 const char* message = "os.rmdir(): String conversion of argument failed.";
647 args.GetIsolate()->ThrowException(
648 String::NewFromUtf8(args.GetIsolate(), message));
649 return;
650 }
651 rmdir(*directory);
652 }
653
654
SetEnvironment(const v8::FunctionCallbackInfo<v8::Value> & args)655 void Shell::SetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
656 if (args.Length() != 2) {
657 const char* message = "setenv() takes two arguments";
658 args.GetIsolate()->ThrowException(
659 String::NewFromUtf8(args.GetIsolate(), message));
660 return;
661 }
662 String::Utf8Value var(args[0]);
663 String::Utf8Value value(args[1]);
664 if (*var == NULL) {
665 const char* message =
666 "os.setenv(): String conversion of variable name failed.";
667 args.GetIsolate()->ThrowException(
668 String::NewFromUtf8(args.GetIsolate(), message));
669 return;
670 }
671 if (*value == NULL) {
672 const char* message =
673 "os.setenv(): String conversion of variable contents failed.";
674 args.GetIsolate()->ThrowException(
675 String::NewFromUtf8(args.GetIsolate(), message));
676 return;
677 }
678 setenv(*var, *value, 1);
679 }
680
681
UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value> & args)682 void Shell::UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
683 if (args.Length() != 1) {
684 const char* message = "unsetenv() takes one argument";
685 args.GetIsolate()->ThrowException(
686 String::NewFromUtf8(args.GetIsolate(), message));
687 return;
688 }
689 String::Utf8Value var(args[0]);
690 if (*var == NULL) {
691 const char* message =
692 "os.setenv(): String conversion of variable name failed.";
693 args.GetIsolate()->ThrowException(
694 String::NewFromUtf8(args.GetIsolate(), message));
695 return;
696 }
697 unsetenv(*var);
698 }
699
700
AddOSMethods(Isolate * isolate,Handle<ObjectTemplate> os_templ)701 void Shell::AddOSMethods(Isolate* isolate, Handle<ObjectTemplate> os_templ) {
702 os_templ->Set(String::NewFromUtf8(isolate, "system"),
703 FunctionTemplate::New(isolate, System));
704 os_templ->Set(String::NewFromUtf8(isolate, "chdir"),
705 FunctionTemplate::New(isolate, ChangeDirectory));
706 os_templ->Set(String::NewFromUtf8(isolate, "setenv"),
707 FunctionTemplate::New(isolate, SetEnvironment));
708 os_templ->Set(String::NewFromUtf8(isolate, "unsetenv"),
709 FunctionTemplate::New(isolate, UnsetEnvironment));
710 os_templ->Set(String::NewFromUtf8(isolate, "umask"),
711 FunctionTemplate::New(isolate, SetUMask));
712 os_templ->Set(String::NewFromUtf8(isolate, "mkdirp"),
713 FunctionTemplate::New(isolate, MakeDirectory));
714 os_templ->Set(String::NewFromUtf8(isolate, "rmdir"),
715 FunctionTemplate::New(isolate, RemoveDirectory));
716 }
717
718 } // namespace v8
719