1 module arsd.exec; 2 3 static import stdlib = core.stdc.stdlib; 4 static import cstdio = core.stdc.stdio; 5 6 import std.stdio; 7 import std..string; 8 version(D_Version2) 9 static import linux = core.sys.posix.unistd; 10 else 11 static import linux = std.c.linux.linux; 12 13 extern(C) int execve(const(char)*, const(char)**, const(char)**); 14 15 16 /** 17 executes an external program, with stdin and stdout of that program optionally going to/from strings 18 19 args - an array of command line arguments to pass (may be empty or null to pass nothing) 20 input - a string that contains the data pass to standard input of the program (null is no override) 21 output - a pointer to a string that will receive standard output from the program (null is no override) 22 environment - a string of additional environment variables to send (null is none) [NOT YET IMPLEMENTED] 23 24 returns return value of program ran 25 26 throws exceptions on error 27 */ 28 int exec(string program, string[] args = null, string input = null, string* output = null, string* error = null, string[] environment = null){ 29 int[2] newStdin; 30 int[2] newStdout; 31 int[2] newStderr; 32 if(input !is null) 33 if(linux.pipe(newStdin) != 0) 34 throw new Exception("pipe stdin"); 35 if(output !is null) 36 if(linux.pipe(newStdout) != 0) 37 throw new Exception("pipe stdout"); 38 if(error !is null) 39 if(linux.pipe(newStderr) != 0) 40 throw new Exception("pipe stderr"); 41 42 int pid = linux.fork(); 43 if(pid == 0){ // child 44 if(input !is null){ 45 linux.close(newStdin[1]); 46 linux.close(0); 47 linux.dup(newStdin[0]); 48 } 49 if(output !is null){ 50 linux.close(1); 51 linux.dup(newStdout[1]); 52 } 53 if(error !is null){ 54 linux.close(2); 55 linux.dup(newStderr[1]); 56 } 57 58 const(char)*[] argv; 59 argv.length = 2 + args.length; 60 int a = 1; 61 argv[0] = toStringz(program); 62 63 if(args !is null) 64 foreach(arg; args){ 65 argv[a] = toStringz(arg); 66 a++; 67 } 68 argv[a] = null; 69 70 71 const(char)*[] envp; 72 envp.length = 1 + environment.length; 73 a = 0; 74 75 if(environment !is null) 76 foreach(arg; environment){ 77 envp[a] = toStringz(arg); 78 a++; 79 } 80 envp[a] = null; 81 82 if(execve(toStringz(program), argv.ptr, envp.ptr) == -1) 83 throw new Exception("Execute"); 84 } else { // parent 85 86 if(input !is null) 87 linux.close(newStdin[0]); 88 if(output !is null) 89 linux.close(newStdout[1]); 90 if(error !is null) 91 linux.close(newStderr[1]); 92 93 typeof(linux.write(1, null, 0)) s; 94 if(input !is null){ 95 do{ 96 s = linux.write(newStdin[1], input.ptr, input.length); 97 if(s < input.length) 98 input = input[s..$]; 99 else 100 s = 0; 101 } while (s > 0); 102 linux.close(newStdin[1]); 103 } 104 105 if(output !is null){ 106 ubyte[1024] buffer; 107 do { 108 s = linux.read(newStdout[0], buffer.ptr, 1024); 109 (*output) ~= buffer[0..s]; 110 } while (s > 0); 111 112 linux.close(newStdout[0]); 113 } 114 115 if(error !is null){ 116 ubyte[1024] buffer; 117 do { 118 s = linux.read(newStderr[0], buffer.ptr, 1024); 119 (*error) ~= buffer[0..s]; 120 } while (s > 0); 121 122 linux.close(newStderr[0]); 123 } 124 125 126 int i; 127 linux.wait(&i); 128 129 return i; 130 } 131 132 return 0; // never reached 133 } 134 135 136 void callInBackground(void delegate() functionToCall) { 137 int pid = linux.fork(); 138 if(pid == 0){ // child 139 linux.umask(0); 140 auto sid = linux.setsid(); 141 if(sid < 0) { 142 stdlib.exit(1); 143 } 144 if(linux.chdir("/") < 0) { 145 stdlib.exit(1); 146 } 147 148 freopen("/dev/null", "r", cstdio.stdin); 149 freopen("/dev/null", "w", cstdio.stdout); 150 freopen("/dev/null", "w", cstdio.stderr); 151 152 try 153 functionToCall(); 154 catch(Throwable e) { 155 stderr.writeln(e.toString()); 156 // /* 157 try { 158 import arsd.email; 159 email("me@arsdnet.net", "background exception", e.toString(), "root@fanclipper.com"); 160 } catch(Throwable e) {} 161 // */ 162 stdlib.exit(1); 163 } 164 stdlib.exit(0); 165 } 166 } 167 168