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