1 module upromised.process; 2 import core.stdc.signal : SIGINT; 3 import deimos.libuv.uv : uv_loop_t, uv_process_t; 4 import std.stdio : File; 5 import upromised.loop : Loop; 6 import upromised.memory : gcrelease, gcretain, getSelf; 7 import upromised.stream : Stream; 8 import upromised.pipe : Pipe; 9 import upromised.promise : DelegatePromise, Promise, PromiseIterator; 10 import upromised.uv : handle, stream, uvCheck; 11 12 shared static this() { 13 import core.stdc.signal : signal, SIG_IGN; 14 import core.sys.posix.signal : SIGPIPE; 15 signal(SIGPIPE, SIG_IGN); 16 } 17 18 class StdPipe { 19 public: 20 File file; 21 22 this(File file) { 23 this.file = file; 24 } 25 } 26 27 class Process { 28 private: 29 DelegatePromise!long wait_; 30 31 public: 32 __gshared Pipe STDIN; 33 __gshared Pipe STDOUT; 34 __gshared Pipe STDERR; 35 shared static this() { 36 import std.stdio : stderr, stdin, stdout; 37 STDIN = cast(Pipe)cast(void*)(new StdPipe(stdin)); 38 STDOUT = cast(Pipe)cast(void*)(new StdPipe(stdout)); 39 STDERR = cast(Pipe)cast(void*)(new StdPipe(stderr)); 40 } 41 42 uv_process_t self; 43 this(Loop loop, string[] argsD, Pipe stdin = null, Pipe stdout = null, Pipe stderr = null) { 44 this(cast(uv_loop_t*)loop.inner(), argsD, stdin, stdout, stderr); 45 } 46 47 this(uv_loop_t* loop, string[] argsD, Pipe stdin = null, Pipe stdout = null, Pipe stderr = null) { 48 import deimos.libuv.uv : uv_process_options_t, uv_close, uv_spawn, uv_stdio_container_t, uv_stdio_flags; 49 import std.algorithm : map; 50 import std.array : array; 51 import std.stdio : File; 52 import std..string : toStringz; 53 54 wait_ = new DelegatePromise!long; 55 uv_process_options_t options; 56 options.exit_cb = (self, exit_status, term_signal) nothrow { 57 self.getSelf!Process.wait_.resolve(exit_status); 58 }; 59 60 import core.stdc.stdlib : malloc, free; 61 uv_stdio_container_t[] io = (cast(uv_stdio_container_t*)malloc(uv_stdio_container_t.sizeof*3))[0..3]; 62 scope(exit) free(io.ptr); 63 foreach(i, pipe; [stdin, stdout, stderr]) { 64 StdPipe stdpipe = cast(StdPipe)cast(Object)cast(void*)pipe; 65 if (stdpipe !is null) { 66 io[i].flags = uv_stdio_flags.UV_INHERIT_FD; 67 io[i].data.fd = stdpipe.file.fileno; 68 } else if (pipe !is null) { 69 io[i].flags = uv_stdio_flags.UV_CREATE_PIPE; 70 if (i == 0) { 71 io[i].flags |= uv_stdio_flags.UV_WRITABLE_PIPE; 72 } else { 73 io[i].flags |= uv_stdio_flags.UV_READABLE_PIPE; 74 } 75 io[i].data.stream = pipe.self.stream; 76 } else { 77 io[i].flags = uv_stdio_flags.UV_IGNORE; 78 } 79 } 80 81 auto args = argsD.map!(x => x.toStringz).array; 82 args ~= null; 83 options.file = args[0]; 84 options.args = cast(char**)args.ptr; 85 options.stdio_count = 3; 86 options.stdio = io.ptr; 87 uv_spawn(loop, &self, &options).uvCheck(); 88 gcretain(this); 89 wait_.finall(() { 90 uv_close(self.handle, (selfSelf) nothrow { 91 gcrelease(selfSelf.getSelf!Process); 92 }); 93 }); 94 } 95 96 Promise!long wait() nothrow { 97 return wait_; 98 } 99 100 void kill(int signal = SIGINT) { 101 import deimos.libuv.uv : uv_process_kill; 102 103 uv_process_kill(&self, signal).uvCheck(); 104 } 105 106 static Stream stream(Loop loop, string[] args, Pipe stderr = Process.STDERR) { 107 return new class Stream { 108 Pipe stdin; 109 Pipe stdout; 110 Process process; 111 112 this() { 113 stdin = new Pipe(loop); 114 stdout = new Pipe(loop); 115 process = new Process(loop, args, stdin, stdout, stderr); 116 } 117 118 Promise!void shutdown() nothrow { 119 return stdin.shutdown(); 120 } 121 122 Promise!void close() nothrow { 123 return Promise!void.resolved(). 124 then(() => process.kill()) 125 .finall(() => stdin.close()) 126 .then(() => process.wait()) 127 .finall(() => stdout.close()) 128 .then((_) {}); 129 } 130 131 PromiseIterator!(const(ubyte)[]) read() nothrow { 132 return stdout.read(); 133 } 134 135 Promise!void write(immutable(ubyte)[] data) nothrow { 136 return stdin.write(data); 137 } 138 }; 139 } 140 }