Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / v8 / src / d8-posix.cc
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 "d8.h"
19 #include "d8-debug.h"
20 #include "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.
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.
83 static bool WaitOnFD(int fd,
84                      int read_timeout,
85                      int total_timeout,
86                      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.
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:
142   explicit ZombieProtector(int pid): pid_(pid) { }
143   ~ZombieProtector() { if (pid_ != 0) waitpid(pid_, NULL, 0); }
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:
153   explicit OpenFDCloser(int fd): fd_(fd) { }
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:
165   ExecArgs() {
166     exec_args_[0] = NULL;
167   }
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   }
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;
209   char** arg_array() { return exec_args_; }
210   char* arg0() { 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.
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.
250 static void ExecSubprocess(int* exec_error_fds,
251                            int* stdout_fds,
252                            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.
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.
289 static Handle<Value> GetStdout(Isolate* isolate,
290                                int child_fd,
291                                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.
361 static bool WaitForChild(Isolate* isolate,
362                          int pid,
363                          ZombieProtector& child_waiter,
364                          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).
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
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
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
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.
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
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
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
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
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
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