root/trunk/hcf/process.d

Revision 813, 8.9 kB (checked in by Gregor, 1 year ago)

hcf/process.d, sss/conf.d: Now throws an exception if a hook command fails, rather than

exiting. This allows DSSS to clean up after itself more effectively.

Line 
1 /**
2  * Helpful process functions
3  *
4  * Authors:
5  *  Gregor Richards
6  *
7  * License:
8  *  Copyright (c) 2006, 2007  Gregor Richards
9  * 
10  *  Permission is hereby granted, free of charge, to any person obtaining a
11  *  copy of this software and associated documentation files (the "Software"),
12  *  to deal in the Software without restriction, including without limitation
13  *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  *  and/or sell copies of the Software, and to permit persons to whom the
15  *  Software is furnished to do so, subject to the following conditions:
16  * 
17  *  The above copyright notice and this permission notice shall be included in
18  *  all copies or substantial portions of the Software.
19  * 
20  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23  *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  *  DEALINGS IN THE SOFTWARE.
27  */
28
29 module hcf.process;
30
31 public import std.process;
32 alias std.process.system system;
33
34 import std.stdio;
35 import std.string;
36 import std.stream;
37
38 import std.c.stdio;
39 import std.c.stdlib;
40
41 private {
42     version (Windows) {
43         import bcd.windows.windows;
44     } else {
45         extern (C) int dup2(int, int);
46         extern (C) int fork();
47         extern (C) int pipe(int[2]);
48         extern (C) int read(int, void*, size_t);
49         extern (C) int write(int, void*, size_t);
50         extern (C) int close(int);
51         extern (C) size_t waitpid(size_t, int*, int);
52     }
53 }
54
55 class PStream : Stream {
56     /** First init step: this, then start the process based on its result */
57     void init1()
58     {
59         readable = true;
60         writeable = true;
61         seekable = false;
62        
63         version (Posix) {
64             // get our pipes
65             pipe(ip);
66             pipe(op);
67        
68             // fork
69             pid = fork();
70             if (pid == 0) {
71                 // dup2 in our stdin/out
72                 dup2(ip[0], 0);
73                 hcf.process.close(ip[1]);
74                 dup2(op[1], 1);
75                 dup2(op[1], 2);
76                 hcf.process.close(op[0]);
77             } else if (pid == -1) {
78                 // boom!
79                 throw new StreamException("Failed to fork");
80             } else {
81                 hcf.process.close(ip[0]);
82                 hcf.process.close(op[1]);
83             }
84         } else version (Windows) {
85             // get our pipes
86             _SECURITY_ATTRIBUTES sa;
87             sa.nLength = _SECURITY_ATTRIBUTES.sizeof;
88             sa.bInheritHandle = 1;
89             CreatePipe(&ipr, &ipw, &sa, 0);
90             CreatePipe(&opr, &opw, &sa, 0);
91            
92             // don't fork yet - it'll have to be done by the next step
93         }
94     }
95    
96     /** Use system */
97     this(char[] command)
98     {
99         init1();
100        
101         version (Posix) {
102             if (pid == 0) {
103                 exit(std.process.system(command));
104             }
105         } else version (Windows) {
106             _STARTUPINFOA si;
107             si.cb = _STARTUPINFOA.sizeof;
108             si.hStdInput = ipr;
109             si.hStdOutput = opw;
110             si.hStdError = opw;
111             si.dwFlags = STARTF_USESTDHANDLES;
112            
113             _PROCESS_INFORMATION pi;
114            
115             CreateProcessA(null, toStringz(command), null, null,
116                            1, 0, null, null, &si, &pi);
117             CloseHandle(ipr);
118             CloseHandle(opw);
119             CloseHandle(pi.hThread);
120             phnd = pi.hProcess;
121         }
122        
123         init2();
124     }
125    
126     /** Use execvp */
127     this(char[] pathname, char[][] argv)
128     {
129         init1();
130        
131         version (Posix) {
132             if (pid == 0) {
133                 execvp(pathname, argv);
134                 exit(1);
135             }
136         } else version(Windows) {
137             assert(0);
138         }
139        
140         init2();
141     }
142    
143     /** The second init part */
144     void init2()
145     {
146         isopen = true;
147     }
148    
149     override size_t readBlock(void* buffer, size_t size)
150     {
151         version (Posix) {
152             int rd = hcf.process.read(op[0], buffer, size);
153             if (rd == -1) {
154                 readEOF = true;
155                 return 0;
156             } else {
157                 readEOF = false;
158             }
159             return rd;
160         } else version (Windows) {
161             uint rd;
162             if (!ReadFile(opr, buffer, size, &rd, null)) {
163                 readEOF = true;
164                 return 0;
165             } else {
166                 readEOF = false;
167             }
168             return rd;
169         }
170     }
171    
172     override size_t writeBlock(void* buffer, size_t size)
173     {
174         version (Posix) {
175             int wt = hcf.process.write(ip[1], buffer, size);
176             if (wt == -1) {
177                 throw new StreamException("Process closed");
178             }
179             return wt;
180         } else version (Windows) {
181             uint wt;
182             if (!WriteFile(opr, buffer, size, &wt, null)) {
183                 readEOF = true;
184                 return 0;
185             } else {
186                 readEOF = false;
187             }
188             return wt;
189         }
190     }
191    
192     override ulong seek(long offset, SeekPos whence)
193     {
194         throw new StreamException("Cannot seek in PStreams");
195     }
196    
197     /** Close the process, return the result */
198     void close()
199     {
200         if (isopen) {
201             isopen = false;
202             version (Posix) {
203                 waitpid(pid, &eval, 0);
204                 hcf.process.close(ip[1]);
205                 hcf.process.close(op[0]);
206             } else version (Windows) {
207                 GetExitCodeProcess(phnd, &eval);
208                 CloseHandle(phnd);
209                 CloseHandle(ipw);
210                 CloseHandle(opr);
211             }
212         }
213     }
214    
215     version (Posix) {
216         /** Get the exit value */
217         int exitValue()
218         {
219             return eval;
220         }
221     } else version (Windows) {
222         /** Get the exit value */
223         uint exitValue()
224         {
225             return eval;
226         }
227     }
228    
229     private:
230    
231     version (Posix) {
232         /** The pid */
233         int pid;
234    
235         /** The exit value */
236         int eval;
237    
238         /** The input pipe */
239         int[2] ip;
240    
241         /** The output pipe */
242         int[2] op;
243     } else version (Windows) {
244         /** The process handle */
245         HANDLE phnd;
246        
247         /** The exit value */
248         uint eval;
249    
250         /** The input pipe */
251         HANDLE ipr, ipw;
252        
253         /** The output pipe */
254         HANDLE opr, opw;
255     }
256 }
257
258 /** Exception to be thrown when a command called dies */
259 class ProcessDeathException : Exception {
260     this(char[] smsg)
261     {
262         super(smsg);
263     }
264 }
265
266 /** system + guarantee success */
267 void systemOrDie(char[] cmd)
268 {
269     int res;
270     fflush(stdout); fflush(stderr);
271     res = system(cmd);
272     if (res)  // CyberShadow 2007.02.22: Display a message before exiting
273     {
274         int p = cmd.find(' ');
275         if(p!=-1) cmd=cmd[0..p];
276         writefln("Command " ~ cmd ~ " returned with code ", res, ", aborting.");
277         throw new ProcessDeathException("Command failed, aborting.");
278     }
279 }
280
281 /** system + output */
282 int sayAndSystem(char[] cmd)
283 {
284     writefln("+ %s", cmd);
285     fflush(stdout); fflush(stderr);
286     return system(cmd);
287 }
288
289 /** systemOrDie + output */
290 void saySystemDie(char[] cmd)
291 {
292     writefln("+ %s", cmd);
293     systemOrDie(cmd);
294 }
295
296 /** system + use a response file */
297 int systemResponse(char[] cmd, char[] rflag, char[] rfile, bool deleteRFile)
298 {
299     int ret;
300     char[][] elems = std.string.split(cmd, " ");
301    
302     /* the output is elems past 1 joined with \n */
303     char[] resp = std.string.join(elems[1..$], "\n");
304     std.file.write(rfile, resp);
305    
306     fflush(stdout); fflush(stderr);
307     ret = system(elems[0] ~ " " ~ rflag ~ rfile);
308    
309     if (deleteRFile) std.file.remove(rfile);
310    
311     return ret;
312 }
313
314 /** systemResponse + guarantee success */
315 void systemROrDie(char[] cmd, char[] rflag, char[] rfile, bool deleteRFile)
316 {
317     int res;
318     res = systemResponse(cmd, rflag, rfile, deleteRFile);
319     if (res)  // CyberShadow 2007.02.22: Display a message before exiting
320     {
321         int p = cmd.find(' ');
322         if(p!=-1) cmd=cmd[0..p];
323         writefln("Command " ~ cmd ~ " returned with code ", res, ", aborting.");
324         throw new ProcessDeathException("Command failed, aborting.");
325     }
326 }
327
328 /** systemResponse + output */
329 int sayAndSystemR(char[] cmd, char[] rflag, char[] rfile, bool deleteRFile)
330 {
331     writefln("+ %s", cmd);
332     return systemResponse(cmd, rflag, rfile, deleteRFile);
333 }
334
335 /** systemROrDie + output */
336 void saySystemRDie(char[] cmd, char[] rflag, char[] rfile, bool deleteRFile)
337 {
338     writefln("+ %s", cmd);
339     systemROrDie(cmd, rflag, rfile, deleteRFile);
340 }
Note: See TracBrowser for help on using the browser.