The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.

Please select the desired protocol below to get the URL.

This URL has Read-Only access.

Statistics
| Branch: | Revision:

main_repo / deps / v8 / src / d8-posix.cc @ f230a1cf

History | View | Annotate | Download (22.5 KB)

1
// Copyright 2009 the V8 project authors. All rights reserved.
2
// Redistribution and use in source and binary forms, with or without
3
// modification, are permitted provided that the following conditions are
4
// met:
5
//
6
//     * Redistributions of source code must retain the above copyright
7
//       notice, this list of conditions and the following disclaimer.
8
//     * Redistributions in binary form must reproduce the above
9
//       copyright notice, this list of conditions and the following
10
//       disclaimer in the documentation and/or other materials provided
11
//       with the distribution.
12
//     * Neither the name of Google Inc. nor the names of its
13
//       contributors may be used to endorse or promote products derived
14
//       from this software without specific prior written permission.
15
//
16
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27

    
28

    
29
#include <stdlib.h>
30
#include <errno.h>
31
#include <sys/types.h>
32
#include <sys/stat.h>
33
#include <sys/time.h>
34
#include <time.h>
35
#include <unistd.h>
36
#include <fcntl.h>
37
#include <sys/wait.h>
38
#include <signal.h>
39

    
40

    
41
#include "d8.h"
42
#include "d8-debug.h"
43
#include "debug.h"
44

    
45

    
46
namespace v8 {
47

    
48

    
49
// If the buffer ends in the middle of a UTF-8 sequence then we return
50
// the length of the string up to but not including the incomplete UTF-8
51
// sequence.  If the buffer ends with a valid UTF-8 sequence then we
52
// return the whole buffer.
53
static int LengthWithoutIncompleteUtf8(char* buffer, int len) {
54
  int answer = len;
55
  // 1-byte encoding.
56
  static const int kUtf8SingleByteMask = 0x80;
57
  static const int kUtf8SingleByteValue = 0x00;
58
  // 2-byte encoding.
59
  static const int kUtf8TwoByteMask = 0xe0;
60
  static const int kUtf8TwoByteValue = 0xc0;
61
  // 3-byte encoding.
62
  static const int kUtf8ThreeByteMask = 0xf0;
63
  static const int kUtf8ThreeByteValue = 0xe0;
64
  // 4-byte encoding.
65
  static const int kUtf8FourByteMask = 0xf8;
66
  static const int kUtf8FourByteValue = 0xf0;
67
  // Subsequent bytes of a multi-byte encoding.
68
  static const int kMultiByteMask = 0xc0;
69
  static const int kMultiByteValue = 0x80;
70
  int multi_byte_bytes_seen = 0;
71
  while (answer > 0) {
72
    int c = buffer[answer - 1];
73
    // Ends in valid single-byte sequence?
74
    if ((c & kUtf8SingleByteMask) == kUtf8SingleByteValue) return answer;
75
    // Ends in one or more subsequent bytes of a multi-byte value?
76
    if ((c & kMultiByteMask) == kMultiByteValue) {
77
      multi_byte_bytes_seen++;
78
      answer--;
79
    } else {
80
      if ((c & kUtf8TwoByteMask) == kUtf8TwoByteValue) {
81
        if (multi_byte_bytes_seen >= 1) {
82
          return answer + 2;
83
        }
84
        return answer - 1;
85
      } else if ((c & kUtf8ThreeByteMask) == kUtf8ThreeByteValue) {
86
        if (multi_byte_bytes_seen >= 2) {
87
          return answer + 3;
88
        }
89
        return answer - 1;
90
      } else if ((c & kUtf8FourByteMask) == kUtf8FourByteValue) {
91
        if (multi_byte_bytes_seen >= 3) {
92
          return answer + 4;
93
        }
94
        return answer - 1;
95
      } else {
96
        return answer;  // Malformed UTF-8.
97
      }
98
    }
99
  }
100
  return 0;
101
}
102

    
103

    
104
// Suspends the thread until there is data available from the child process.
105
// Returns false on timeout, true on data ready.
106
static bool WaitOnFD(int fd,
107
                     int read_timeout,
108
                     int total_timeout,
109
                     struct timeval& start_time) {
110
  fd_set readfds, writefds, exceptfds;
111
  struct timeval timeout;
112
  int gone = 0;
113
  if (total_timeout != -1) {
114
    struct timeval time_now;
115
    gettimeofday(&time_now, NULL);
116
    int seconds = time_now.tv_sec - start_time.tv_sec;
117
    gone = seconds * 1000 + (time_now.tv_usec - start_time.tv_usec) / 1000;
118
    if (gone >= total_timeout) return false;
119
  }
120
  FD_ZERO(&readfds);
121
  FD_ZERO(&writefds);
122
  FD_ZERO(&exceptfds);
123
  FD_SET(fd, &readfds);
124
  FD_SET(fd, &exceptfds);
125
  if (read_timeout == -1 ||
126
      (total_timeout != -1 && total_timeout - gone < read_timeout)) {
127
    read_timeout = total_timeout - gone;
128
  }
129
  timeout.tv_usec = (read_timeout % 1000) * 1000;
130
  timeout.tv_sec = read_timeout / 1000;
131
  int number_of_fds_ready = select(fd + 1,
132
                                   &readfds,
133
                                   &writefds,
134
                                   &exceptfds,
135
                                   read_timeout != -1 ? &timeout : NULL);
136
  return number_of_fds_ready == 1;
137
}
138

    
139

    
140
// Checks whether we ran out of time on the timeout.  Returns true if we ran out
141
// of time, false if we still have time.
142
static bool TimeIsOut(const struct timeval& start_time, const int& total_time) {
143
  if (total_time == -1) return false;
144
  struct timeval time_now;
145
  gettimeofday(&time_now, NULL);
146
  // Careful about overflow.
147
  int seconds = time_now.tv_sec - start_time.tv_sec;
148
  if (seconds > 100) {
149
    if (seconds * 1000 > total_time) return true;
150
    return false;
151
  }
152
  int useconds = time_now.tv_usec - start_time.tv_usec;
153
  if (seconds * 1000000 + useconds > total_time * 1000) {
154
    return true;
155
  }
156
  return false;
157
}
158

    
159

    
160
// A utility class that does a non-hanging waitpid on the child process if we
161
// bail out of the System() function early.  If you don't ever do a waitpid on
162
// a subprocess then it turns into one of those annoying 'zombie processes'.
163
class ZombieProtector {
164
 public:
165
  explicit ZombieProtector(int pid): pid_(pid) { }
166
  ~ZombieProtector() { if (pid_ != 0) waitpid(pid_, NULL, 0); }
167
  void ChildIsDeadNow() { pid_ = 0; }
168
 private:
169
  int pid_;
170
};
171

    
172

    
173
// A utility class that closes a file descriptor when it goes out of scope.
174
class OpenFDCloser {
175
 public:
176
  explicit OpenFDCloser(int fd): fd_(fd) { }
177
  ~OpenFDCloser() { close(fd_); }
178
 private:
179
  int fd_;
180
};
181

    
182

    
183
// A utility class that takes the array of command arguments and puts then in an
184
// array of new[]ed UTF-8 C strings.  Deallocates them again when it goes out of
185
// scope.
186
class ExecArgs {
187
 public:
188
  ExecArgs() {
189
    exec_args_[0] = NULL;
190
  }
191
  bool Init(Handle<Value> arg0, Handle<Array> command_args) {
192
    String::Utf8Value prog(arg0);
193
    if (*prog == NULL) {
194
      const char* message =
195
          "os.system(): String conversion of program name failed";
196
      ThrowException(String::New(message));
197
      return false;
198
    }
199
    int len = prog.length() + 3;
200
    char* c_arg = new char[len];
201
    snprintf(c_arg, len, "%s", *prog);
202
    exec_args_[0] = c_arg;
203
    int i = 1;
204
    for (unsigned j = 0; j < command_args->Length(); i++, j++) {
205
      Handle<Value> arg(command_args->Get(Integer::New(j)));
206
      String::Utf8Value utf8_arg(arg);
207
      if (*utf8_arg == NULL) {
208
        exec_args_[i] = NULL;  // Consistent state for destructor.
209
        const char* message =
210
            "os.system(): String conversion of argument failed.";
211
        ThrowException(String::New(message));
212
        return false;
213
      }
214
      int len = utf8_arg.length() + 1;
215
      char* c_arg = new char[len];
216
      snprintf(c_arg, len, "%s", *utf8_arg);
217
      exec_args_[i] = c_arg;
218
    }
219
    exec_args_[i] = NULL;
220
    return true;
221
  }
222
  ~ExecArgs() {
223
    for (unsigned i = 0; i < kMaxArgs; i++) {
224
      if (exec_args_[i] == NULL) {
225
        return;
226
      }
227
      delete [] exec_args_[i];
228
      exec_args_[i] = 0;
229
    }
230
  }
231
  static const unsigned kMaxArgs = 1000;
232
  char** arg_array() { return exec_args_; }
233
  char* arg0() { return exec_args_[0]; }
234

    
235
 private:
236
  char* exec_args_[kMaxArgs + 1];
237
};
238

    
239

    
240
// Gets the optional timeouts from the arguments to the system() call.
241
static bool GetTimeouts(const v8::FunctionCallbackInfo<v8::Value>& args,
242
                        int* read_timeout,
243
                        int* total_timeout) {
244
  if (args.Length() > 3) {
245
    if (args[3]->IsNumber()) {
246
      *total_timeout = args[3]->Int32Value();
247
    } else {
248
      args.GetIsolate()->ThrowException(
249
          String::New("system: Argument 4 must be a number"));
250
      return false;
251
    }
252
  }
253
  if (args.Length() > 2) {
254
    if (args[2]->IsNumber()) {
255
      *read_timeout = args[2]->Int32Value();
256
    } else {
257
      args.GetIsolate()->ThrowException(
258
          String::New("system: Argument 3 must be a number"));
259
      return false;
260
    }
261
  }
262
  return true;
263
}
264

    
265

    
266
static const int kReadFD = 0;
267
static const int kWriteFD = 1;
268

    
269

    
270
// This is run in the child process after fork() but before exec().  It normally
271
// ends with the child process being replaced with the desired child program.
272
// It only returns if an error occurred.
273
static void ExecSubprocess(int* exec_error_fds,
274
                           int* stdout_fds,
275
                           ExecArgs& exec_args) {
276
  close(exec_error_fds[kReadFD]);  // Don't need this in the child.
277
  close(stdout_fds[kReadFD]);      // Don't need this in the child.
278
  close(1);                        // Close stdout.
279
  dup2(stdout_fds[kWriteFD], 1);   // Dup pipe fd to stdout.
280
  close(stdout_fds[kWriteFD]);     // Don't need the original fd now.
281
  fcntl(exec_error_fds[kWriteFD], F_SETFD, FD_CLOEXEC);
282
  execvp(exec_args.arg0(), exec_args.arg_array());
283
  // Only get here if the exec failed.  Write errno to the parent to tell
284
  // them it went wrong.  If it went well the pipe is closed.
285
  int err = errno;
286
  int bytes_written;
287
  do {
288
    bytes_written = write(exec_error_fds[kWriteFD], &err, sizeof(err));
289
  } while (bytes_written == -1 && errno == EINTR);
290
  // Return (and exit child process).
291
}
292

    
293

    
294
// Runs in the parent process.  Checks that the child was able to exec (closing
295
// the file desriptor), or reports an error if it failed.
296
static bool ChildLaunchedOK(int* exec_error_fds) {
297
  int bytes_read;
298
  int err;
299
  do {
300
    bytes_read = read(exec_error_fds[kReadFD], &err, sizeof(err));
301
  } while (bytes_read == -1 && errno == EINTR);
302
  if (bytes_read != 0) {
303
    ThrowException(String::New(strerror(err)));
304
    return false;
305
  }
306
  return true;
307
}
308

    
309

    
310
// Accumulates the output from the child in a string handle.  Returns true if it
311
// succeeded or false if an exception was thrown.
312
static Handle<Value> GetStdout(int child_fd,
313
                               struct timeval& start_time,
314
                               int read_timeout,
315
                               int total_timeout) {
316
  Handle<String> accumulator = String::Empty();
317

    
318
  int fullness = 0;
319
  static const int kStdoutReadBufferSize = 4096;
320
  char buffer[kStdoutReadBufferSize];
321

    
322
  if (fcntl(child_fd, F_SETFL, O_NONBLOCK) != 0) {
323
    return ThrowException(String::New(strerror(errno)));
324
  }
325

    
326
  int bytes_read;
327
  do {
328
    bytes_read = read(child_fd,
329
                      buffer + fullness,
330
                      kStdoutReadBufferSize - fullness);
331
    if (bytes_read == -1) {
332
      if (errno == EAGAIN) {
333
        if (!WaitOnFD(child_fd,
334
                      read_timeout,
335
                      total_timeout,
336
                      start_time) ||
337
            (TimeIsOut(start_time, total_timeout))) {
338
          return ThrowException(String::New("Timed out waiting for output"));
339
        }
340
        continue;
341
      } else if (errno == EINTR) {
342
        continue;
343
      } else {
344
        break;
345
      }
346
    }
347
    if (bytes_read + fullness > 0) {
348
      int length = bytes_read == 0 ?
349
                   bytes_read + fullness :
350
                   LengthWithoutIncompleteUtf8(buffer, bytes_read + fullness);
351
      Handle<String> addition = String::New(buffer, length);
352
      accumulator = String::Concat(accumulator, addition);
353
      fullness = bytes_read + fullness - length;
354
      memcpy(buffer, buffer + length, fullness);
355
    }
356
  } while (bytes_read != 0);
357
  return accumulator;
358
}
359

    
360

    
361
// Modern Linux has the waitid call, which is like waitpid, but more useful
362
// if you want a timeout.  If we don't have waitid we can't limit the time
363
// waiting for the process to exit without losing the information about
364
// whether it exited normally.  In the common case this doesn't matter because
365
// we don't get here before the child has closed stdout and most programs don't
366
// do that before they exit.
367
//
368
// We're disabling usage of waitid in Mac OS X because it doens't work for us:
369
// a parent process hangs on waiting while a child process is already a zombie.
370
// See http://code.google.com/p/v8/issues/detail?id=401.
371
#if defined(WNOWAIT) && !defined(ANDROID) && !defined(__APPLE__) \
372
    && !defined(__NetBSD__)
373
#if !defined(__FreeBSD__)
374
#define HAS_WAITID 1
375
#endif
376
#endif
377

    
378

    
379
// Get exit status of child.
380
static bool WaitForChild(int pid,
381
                         ZombieProtector& child_waiter,
382
                         struct timeval& start_time,
383
                         int read_timeout,
384
                         int total_timeout) {
385
#ifdef HAS_WAITID
386

    
387
  siginfo_t child_info;
388
  child_info.si_pid = 0;
389
  int useconds = 1;
390
  // Wait for child to exit.
391
  while (child_info.si_pid == 0) {
392
    waitid(P_PID, pid, &child_info, WEXITED | WNOHANG | WNOWAIT);
393
    usleep(useconds);
394
    if (useconds < 1000000) useconds <<= 1;
395
    if ((read_timeout != -1 && useconds / 1000 > read_timeout) ||
396
        (TimeIsOut(start_time, total_timeout))) {
397
      ThrowException(String::New("Timed out waiting for process to terminate"));
398
      kill(pid, SIGINT);
399
      return false;
400
    }
401
  }
402
  if (child_info.si_code == CLD_KILLED) {
403
    char message[999];
404
    snprintf(message,
405
             sizeof(message),
406
             "Child killed by signal %d",
407
             child_info.si_status);
408
    ThrowException(String::New(message));
409
    return false;
410
  }
411
  if (child_info.si_code == CLD_EXITED && child_info.si_status != 0) {
412
    char message[999];
413
    snprintf(message,
414
             sizeof(message),
415
             "Child exited with status %d",
416
             child_info.si_status);
417
    ThrowException(String::New(message));
418
    return false;
419
  }
420

    
421
#else  // No waitid call.
422

    
423
  int child_status;
424
  waitpid(pid, &child_status, 0);  // We hang here if the child doesn't exit.
425
  child_waiter.ChildIsDeadNow();
426
  if (WIFSIGNALED(child_status)) {
427
    char message[999];
428
    snprintf(message,
429
             sizeof(message),
430
             "Child killed by signal %d",
431
             WTERMSIG(child_status));
432
    ThrowException(String::New(message));
433
    return false;
434
  }
435
  if (WEXITSTATUS(child_status) != 0) {
436
    char message[999];
437
    int exit_status = WEXITSTATUS(child_status);
438
    snprintf(message,
439
             sizeof(message),
440
             "Child exited with status %d",
441
             exit_status);
442
    ThrowException(String::New(message));
443
    return false;
444
  }
445

    
446
#endif  // No waitid call.
447

    
448
  return true;
449
}
450

    
451

    
452
// Implementation of the system() function (see d8.h for details).
453
void Shell::System(const v8::FunctionCallbackInfo<v8::Value>& args) {
454
  HandleScope scope(args.GetIsolate());
455
  int read_timeout = -1;
456
  int total_timeout = -1;
457
  if (!GetTimeouts(args, &read_timeout, &total_timeout)) return;
458
  Handle<Array> command_args;
459
  if (args.Length() > 1) {
460
    if (!args[1]->IsArray()) {
461
      args.GetIsolate()->ThrowException(
462
          String::New("system: Argument 2 must be an array"));
463
      return;
464
    }
465
    command_args = Handle<Array>::Cast(args[1]);
466
  } else {
467
    command_args = Array::New(0);
468
  }
469
  if (command_args->Length() > ExecArgs::kMaxArgs) {
470
    args.GetIsolate()->ThrowException(
471
        String::New("Too many arguments to system()"));
472
    return;
473
  }
474
  if (args.Length() < 1) {
475
    args.GetIsolate()->ThrowException(
476
        String::New("Too few arguments to system()"));
477
    return;
478
  }
479

    
480
  struct timeval start_time;
481
  gettimeofday(&start_time, NULL);
482

    
483
  ExecArgs exec_args;
484
  if (!exec_args.Init(args[0], command_args)) {
485
    return;
486
  }
487
  int exec_error_fds[2];
488
  int stdout_fds[2];
489

    
490
  if (pipe(exec_error_fds) != 0) {
491
    args.GetIsolate()->ThrowException(
492
        String::New("pipe syscall failed."));
493
    return;
494
  }
495
  if (pipe(stdout_fds) != 0) {
496
    args.GetIsolate()->ThrowException(
497
        String::New("pipe syscall failed."));
498
    return;
499
  }
500

    
501
  pid_t pid = fork();
502
  if (pid == 0) {  // Child process.
503
    ExecSubprocess(exec_error_fds, stdout_fds, exec_args);
504
    exit(1);
505
  }
506

    
507
  // Parent process.  Ensure that we clean up if we exit this function early.
508
  ZombieProtector child_waiter(pid);
509
  close(exec_error_fds[kWriteFD]);
510
  close(stdout_fds[kWriteFD]);
511
  OpenFDCloser error_read_closer(exec_error_fds[kReadFD]);
512
  OpenFDCloser stdout_read_closer(stdout_fds[kReadFD]);
513

    
514
  if (!ChildLaunchedOK(exec_error_fds)) return;
515

    
516
  Handle<Value> accumulator = GetStdout(stdout_fds[kReadFD],
517
                                        start_time,
518
                                        read_timeout,
519
                                        total_timeout);
520
  if (accumulator->IsUndefined()) {
521
    kill(pid, SIGINT);  // On timeout, kill the subprocess.
522
    args.GetReturnValue().Set(accumulator);
523
    return;
524
  }
525

    
526
  if (!WaitForChild(pid,
527
                    child_waiter,
528
                    start_time,
529
                    read_timeout,
530
                    total_timeout)) {
531
    return;
532
  }
533

    
534
  args.GetReturnValue().Set(accumulator);
535
}
536

    
537

    
538
void Shell::ChangeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
539
  if (args.Length() != 1) {
540
    const char* message = "chdir() takes one argument";
541
    args.GetIsolate()->ThrowException(String::New(message));
542
    return;
543
  }
544
  String::Utf8Value directory(args[0]);
545
  if (*directory == NULL) {
546
    const char* message = "os.chdir(): String conversion of argument failed.";
547
    args.GetIsolate()->ThrowException(String::New(message));
548
    return;
549
  }
550
  if (chdir(*directory) != 0) {
551
    args.GetIsolate()->ThrowException(String::New(strerror(errno)));
552
    return;
553
  }
554
}
555

    
556

    
557
void Shell::SetUMask(const v8::FunctionCallbackInfo<v8::Value>& args) {
558
  if (args.Length() != 1) {
559
    const char* message = "umask() takes one argument";
560
    args.GetIsolate()->ThrowException(String::New(message));
561
    return;
562
  }
563
  if (args[0]->IsNumber()) {
564
    mode_t mask = args[0]->Int32Value();
565
    int previous = umask(mask);
566
    args.GetReturnValue().Set(previous);
567
    return;
568
  } else {
569
    const char* message = "umask() argument must be numeric";
570
    args.GetIsolate()->ThrowException(String::New(message));
571
    return;
572
  }
573
}
574

    
575

    
576
static bool CheckItsADirectory(char* directory) {
577
  struct stat stat_buf;
578
  int stat_result = stat(directory, &stat_buf);
579
  if (stat_result != 0) {
580
    ThrowException(String::New(strerror(errno)));
581
    return false;
582
  }
583
  if ((stat_buf.st_mode & S_IFDIR) != 0) return true;
584
  ThrowException(String::New(strerror(EEXIST)));
585
  return false;
586
}
587

    
588

    
589
// Returns true for success.  Creates intermediate directories as needed.  No
590
// error if the directory exists already.
591
static bool mkdirp(char* directory, mode_t mask) {
592
  int result = mkdir(directory, mask);
593
  if (result == 0) return true;
594
  if (errno == EEXIST) {
595
    return CheckItsADirectory(directory);
596
  } else if (errno == ENOENT) {  // Intermediate path element is missing.
597
    char* last_slash = strrchr(directory, '/');
598
    if (last_slash == NULL) {
599
      ThrowException(String::New(strerror(errno)));
600
      return false;
601
    }
602
    *last_slash = 0;
603
    if (!mkdirp(directory, mask)) return false;
604
    *last_slash = '/';
605
    result = mkdir(directory, mask);
606
    if (result == 0) return true;
607
    if (errno == EEXIST) {
608
      return CheckItsADirectory(directory);
609
    }
610
    ThrowException(String::New(strerror(errno)));
611
    return false;
612
  } else {
613
    ThrowException(String::New(strerror(errno)));
614
    return false;
615
  }
616
}
617

    
618

    
619
void Shell::MakeDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
620
  mode_t mask = 0777;
621
  if (args.Length() == 2) {
622
    if (args[1]->IsNumber()) {
623
      mask = args[1]->Int32Value();
624
    } else {
625
      const char* message = "mkdirp() second argument must be numeric";
626
      args.GetIsolate()->ThrowException(String::New(message));
627
      return;
628
    }
629
  } else if (args.Length() != 1) {
630
    const char* message = "mkdirp() takes one or two arguments";
631
    args.GetIsolate()->ThrowException(String::New(message));
632
    return;
633
  }
634
  String::Utf8Value directory(args[0]);
635
  if (*directory == NULL) {
636
    const char* message = "os.mkdirp(): String conversion of argument failed.";
637
    args.GetIsolate()->ThrowException(String::New(message));
638
    return;
639
  }
640
  mkdirp(*directory, mask);
641
}
642

    
643

    
644
void Shell::RemoveDirectory(const v8::FunctionCallbackInfo<v8::Value>& args) {
645
  if (args.Length() != 1) {
646
    const char* message = "rmdir() takes one or two arguments";
647
    args.GetIsolate()->ThrowException(String::New(message));
648
    return;
649
  }
650
  String::Utf8Value directory(args[0]);
651
  if (*directory == NULL) {
652
    const char* message = "os.rmdir(): String conversion of argument failed.";
653
    args.GetIsolate()->ThrowException(String::New(message));
654
    return;
655
  }
656
  rmdir(*directory);
657
}
658

    
659

    
660
void Shell::SetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
661
  if (args.Length() != 2) {
662
    const char* message = "setenv() takes two arguments";
663
    args.GetIsolate()->ThrowException(String::New(message));
664
    return;
665
  }
666
  String::Utf8Value var(args[0]);
667
  String::Utf8Value value(args[1]);
668
  if (*var == NULL) {
669
    const char* message =
670
        "os.setenv(): String conversion of variable name failed.";
671
    args.GetIsolate()->ThrowException(String::New(message));
672
    return;
673
  }
674
  if (*value == NULL) {
675
    const char* message =
676
        "os.setenv(): String conversion of variable contents failed.";
677
    args.GetIsolate()->ThrowException(String::New(message));
678
    return;
679
  }
680
  setenv(*var, *value, 1);
681
}
682

    
683

    
684
void Shell::UnsetEnvironment(const v8::FunctionCallbackInfo<v8::Value>& args) {
685
  if (args.Length() != 1) {
686
    const char* message = "unsetenv() takes one argument";
687
    args.GetIsolate()->ThrowException(String::New(message));
688
    return;
689
  }
690
  String::Utf8Value var(args[0]);
691
  if (*var == NULL) {
692
    const char* message =
693
        "os.setenv(): String conversion of variable name failed.";
694
    args.GetIsolate()->ThrowException(String::New(message));
695
    return;
696
  }
697
  unsetenv(*var);
698
}
699

    
700

    
701
void Shell::AddOSMethods(Handle<ObjectTemplate> os_templ) {
702
  os_templ->Set(String::New("system"), FunctionTemplate::New(System));
703
  os_templ->Set(String::New("chdir"), FunctionTemplate::New(ChangeDirectory));
704
  os_templ->Set(String::New("setenv"), FunctionTemplate::New(SetEnvironment));
705
  os_templ->Set(String::New("unsetenv"),
706
                FunctionTemplate::New(UnsetEnvironment));
707
  os_templ->Set(String::New("umask"), FunctionTemplate::New(SetUMask));
708
  os_templ->Set(String::New("mkdirp"), FunctionTemplate::New(MakeDirectory));
709
  os_templ->Set(String::New("rmdir"), FunctionTemplate::New(RemoveDirectory));
710
}
711

    
712
}  // namespace v8