cb15178b4eb92737cfc24fb7866da20778e6956e
[obnox/samba/samba-obnox.git] / buildtools / wafadmin / pproc.py
1 # borrowed from python 2.5.2c1
2 # Copyright (c) 2003-2005 by Peter Astrand <astrand@lysator.liu.se>
3 # Licensed to PSF under a Contributor Agreement.
4
5 import sys
6 mswindows = (sys.platform == "win32")
7
8 import os
9 import types
10 import traceback
11 import gc
12
13 class CalledProcessError(Exception):
14     def __init__(self, returncode, cmd):
15         self.returncode = returncode
16         self.cmd = cmd
17     def __str__(self):
18         return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
19
20 if mswindows:
21     import threading
22     import msvcrt
23     if 0:
24         import pywintypes
25         from win32api import GetStdHandle, STD_INPUT_HANDLE, \
26                              STD_OUTPUT_HANDLE, STD_ERROR_HANDLE
27         from win32api import GetCurrentProcess, DuplicateHandle, \
28                              GetModuleFileName, GetVersion
29         from win32con import DUPLICATE_SAME_ACCESS, SW_HIDE
30         from win32pipe import CreatePipe
31         from win32process import CreateProcess, STARTUPINFO, \
32                                  GetExitCodeProcess, STARTF_USESTDHANDLES, \
33                                  STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE
34         from win32event import WaitForSingleObject, INFINITE, WAIT_OBJECT_0
35     else:
36         from _subprocess import *
37         class STARTUPINFO:
38             dwFlags = 0
39             hStdInput = None
40             hStdOutput = None
41             hStdError = None
42             wShowWindow = 0
43         class pywintypes:
44             error = IOError
45 else:
46     import select
47     import errno
48     import fcntl
49     import pickle
50
51 __all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "CalledProcessError"]
52
53 try:
54     MAXFD = os.sysconf("SC_OPEN_MAX")
55 except:
56     MAXFD = 256
57
58 try:
59     False
60 except NameError:
61     False = 0
62     True = 1
63
64 _active = []
65
66 def _cleanup():
67     for inst in _active[:]:
68         if inst.poll(_deadstate=sys.maxint) >= 0:
69             try:
70                 _active.remove(inst)
71             except ValueError:
72                 pass
73
74 PIPE = -1
75 STDOUT = -2
76
77
78 def call(*popenargs, **kwargs):
79     return Popen(*popenargs, **kwargs).wait()
80
81 def check_call(*popenargs, **kwargs):
82     retcode = call(*popenargs, **kwargs)
83     cmd = kwargs.get("args")
84     if cmd is None:
85         cmd = popenargs[0]
86     if retcode:
87         raise CalledProcessError(retcode, cmd)
88     return retcode
89
90
91 def list2cmdline(seq):
92     result = []
93     needquote = False
94     for arg in seq:
95         bs_buf = []
96
97         if result:
98             result.append(' ')
99
100         needquote = (" " in arg) or ("\t" in arg) or arg == ""
101         if needquote:
102             result.append('"')
103
104         for c in arg:
105             if c == '\\':
106                 bs_buf.append(c)
107             elif c == '"':
108                 result.append('\\' * len(bs_buf)*2)
109                 bs_buf = []
110                 result.append('\\"')
111             else:
112                 if bs_buf:
113                     result.extend(bs_buf)
114                     bs_buf = []
115                 result.append(c)
116
117         if bs_buf:
118             result.extend(bs_buf)
119
120         if needquote:
121             result.extend(bs_buf)
122             result.append('"')
123
124     return ''.join(result)
125
126 class Popen(object):
127     def __init__(self, args, bufsize=0, executable=None,
128                  stdin=None, stdout=None, stderr=None,
129                  preexec_fn=None, close_fds=False, shell=False,
130                  cwd=None, env=None, universal_newlines=False,
131                  startupinfo=None, creationflags=0):
132         _cleanup()
133
134         self._child_created = False
135         if not isinstance(bufsize, (int, long)):
136             raise TypeError("bufsize must be an integer")
137
138         if mswindows:
139             if preexec_fn is not None:
140                 raise ValueError("preexec_fn is not supported on Windows platforms")
141             if close_fds:
142                 raise ValueError("close_fds is not supported on Windows platforms")
143         else:
144             if startupinfo is not None:
145                 raise ValueError("startupinfo is only supported on Windows platforms")
146             if creationflags != 0:
147                 raise ValueError("creationflags is only supported on Windows platforms")
148
149         self.stdin = None
150         self.stdout = None
151         self.stderr = None
152         self.pid = None
153         self.returncode = None
154         self.universal_newlines = universal_newlines
155
156         (p2cread, p2cwrite,
157          c2pread, c2pwrite,
158          errread, errwrite) = self._get_handles(stdin, stdout, stderr)
159
160         self._execute_child(args, executable, preexec_fn, close_fds,
161                             cwd, env, universal_newlines,
162                             startupinfo, creationflags, shell,
163                             p2cread, p2cwrite,
164                             c2pread, c2pwrite,
165                             errread, errwrite)
166
167         if mswindows:
168             if stdin is None and p2cwrite is not None:
169                 os.close(p2cwrite)
170                 p2cwrite = None
171             if stdout is None and c2pread is not None:
172                 os.close(c2pread)
173                 c2pread = None
174             if stderr is None and errread is not None:
175                 os.close(errread)
176                 errread = None
177
178         if p2cwrite:
179             self.stdin = os.fdopen(p2cwrite, 'wb', bufsize)
180         if c2pread:
181             if universal_newlines:
182                 self.stdout = os.fdopen(c2pread, 'rU', bufsize)
183             else:
184                 self.stdout = os.fdopen(c2pread, 'rb', bufsize)
185         if errread:
186             if universal_newlines:
187                 self.stderr = os.fdopen(errread, 'rU', bufsize)
188             else:
189                 self.stderr = os.fdopen(errread, 'rb', bufsize)
190
191
192     def _translate_newlines(self, data):
193         data = data.replace("\r\n", "\n")
194         data = data.replace("\r", "\n")
195         return data
196
197
198     def __del__(self, sys=sys):
199         if not self._child_created:
200             return
201         self.poll(_deadstate=sys.maxint)
202         if self.returncode is None and _active is not None:
203             _active.append(self)
204
205
206     def communicate(self, input=None):
207         if [self.stdin, self.stdout, self.stderr].count(None) >= 2:
208             stdout = None
209             stderr = None
210             if self.stdin:
211                 if input:
212                     self.stdin.write(input)
213                 self.stdin.close()
214             elif self.stdout:
215                 stdout = self.stdout.read()
216             elif self.stderr:
217                 stderr = self.stderr.read()
218             self.wait()
219             return (stdout, stderr)
220
221         return self._communicate(input)
222
223
224     if mswindows:
225         def _get_handles(self, stdin, stdout, stderr):
226             if stdin is None and stdout is None and stderr is None:
227                 return (None, None, None, None, None, None)
228
229             p2cread, p2cwrite = None, None
230             c2pread, c2pwrite = None, None
231             errread, errwrite = None, None
232
233             if stdin is None:
234                 p2cread = GetStdHandle(STD_INPUT_HANDLE)
235             if p2cread is not None:
236                 pass
237             elif stdin is None or stdin == PIPE:
238                 p2cread, p2cwrite = CreatePipe(None, 0)
239                 p2cwrite = p2cwrite.Detach()
240                 p2cwrite = msvcrt.open_osfhandle(p2cwrite, 0)
241             elif isinstance(stdin, int):
242                 p2cread = msvcrt.get_osfhandle(stdin)
243             else:
244                 p2cread = msvcrt.get_osfhandle(stdin.fileno())
245             p2cread = self._make_inheritable(p2cread)
246
247             if stdout is None:
248                 c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE)
249             if c2pwrite is not None:
250                 pass
251             elif stdout is None or stdout == PIPE:
252                 c2pread, c2pwrite = CreatePipe(None, 0)
253                 c2pread = c2pread.Detach()
254                 c2pread = msvcrt.open_osfhandle(c2pread, 0)
255             elif isinstance(stdout, int):
256                 c2pwrite = msvcrt.get_osfhandle(stdout)
257             else:
258                 c2pwrite = msvcrt.get_osfhandle(stdout.fileno())
259             c2pwrite = self._make_inheritable(c2pwrite)
260
261             if stderr is None:
262                 errwrite = GetStdHandle(STD_ERROR_HANDLE)
263             if errwrite is not None:
264                 pass
265             elif stderr is None or stderr == PIPE:
266                 errread, errwrite = CreatePipe(None, 0)
267                 errread = errread.Detach()
268                 errread = msvcrt.open_osfhandle(errread, 0)
269             elif stderr == STDOUT:
270                 errwrite = c2pwrite
271             elif isinstance(stderr, int):
272                 errwrite = msvcrt.get_osfhandle(stderr)
273             else:
274                 errwrite = msvcrt.get_osfhandle(stderr.fileno())
275             errwrite = self._make_inheritable(errwrite)
276
277             return (p2cread, p2cwrite,
278                     c2pread, c2pwrite,
279                     errread, errwrite)
280         def _make_inheritable(self, handle):
281             return DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), 0, 1, DUPLICATE_SAME_ACCESS)
282
283         def _find_w9xpopen(self):
284             w9xpopen = os.path.join(os.path.dirname(GetModuleFileName(0)), "w9xpopen.exe")
285             if not os.path.exists(w9xpopen):
286                 w9xpopen = os.path.join(os.path.dirname(sys.exec_prefix), "w9xpopen.exe")
287                 if not os.path.exists(w9xpopen):
288                     raise RuntimeError("Cannot locate w9xpopen.exe, which is needed for Popen to work with your shell or platform.")
289             return w9xpopen
290
291         def _execute_child(self, args, executable, preexec_fn, close_fds,
292                            cwd, env, universal_newlines,
293                            startupinfo, creationflags, shell,
294                            p2cread, p2cwrite,
295                            c2pread, c2pwrite,
296                            errread, errwrite):
297
298             if not isinstance(args, types.StringTypes):
299                 args = list2cmdline(args)
300
301             if startupinfo is None:
302                 startupinfo = STARTUPINFO()
303             if None not in (p2cread, c2pwrite, errwrite):
304                 startupinfo.dwFlags |= STARTF_USESTDHANDLES
305                 startupinfo.hStdInput = p2cread
306                 startupinfo.hStdOutput = c2pwrite
307                 startupinfo.hStdError = errwrite
308
309             if shell:
310                 startupinfo.dwFlags |= STARTF_USESHOWWINDOW
311                 startupinfo.wShowWindow = SW_HIDE
312                 comspec = os.environ.get("COMSPEC", "cmd.exe")
313                 args = comspec + " /c " + args
314                 if (GetVersion() >= 0x80000000L or
315                         os.path.basename(comspec).lower() == "command.com"):
316                     w9xpopen = self._find_w9xpopen()
317                     args = '"%s" %s' % (w9xpopen, args)
318                     creationflags |= CREATE_NEW_CONSOLE
319
320             try:
321                 hp, ht, pid, tid = CreateProcess(executable, args, None, None, 1, creationflags, env, cwd, startupinfo)
322             except pywintypes.error, e:
323                 raise WindowsError(*e.args)
324
325             self._child_created = True
326             self._handle = hp
327             self.pid = pid
328             ht.Close()
329
330             if p2cread is not None:
331                 p2cread.Close()
332             if c2pwrite is not None:
333                 c2pwrite.Close()
334             if errwrite is not None:
335                 errwrite.Close()
336
337
338         def poll(self, _deadstate=None):
339             if self.returncode is None:
340                 if WaitForSingleObject(self._handle, 0) == WAIT_OBJECT_0:
341                     self.returncode = GetExitCodeProcess(self._handle)
342             return self.returncode
343
344
345         def wait(self):
346             if self.returncode is None:
347                 obj = WaitForSingleObject(self._handle, INFINITE)
348                 self.returncode = GetExitCodeProcess(self._handle)
349             return self.returncode
350
351         def _readerthread(self, fh, buffer):
352             buffer.append(fh.read())
353
354         def _communicate(self, input):
355             stdout = None
356             stderr = None
357
358             if self.stdout:
359                 stdout = []
360                 stdout_thread = threading.Thread(target=self._readerthread, args=(self.stdout, stdout))
361                 stdout_thread.setDaemon(True)
362                 stdout_thread.start()
363             if self.stderr:
364                 stderr = []
365                 stderr_thread = threading.Thread(target=self._readerthread, args=(self.stderr, stderr))
366                 stderr_thread.setDaemon(True)
367                 stderr_thread.start()
368
369             if self.stdin:
370                 if input is not None:
371                     self.stdin.write(input)
372                 self.stdin.close()
373
374             if self.stdout:
375                 stdout_thread.join()
376             if self.stderr:
377                 stderr_thread.join()
378
379             if stdout is not None:
380                 stdout = stdout[0]
381             if stderr is not None:
382                 stderr = stderr[0]
383
384             if self.universal_newlines and hasattr(file, 'newlines'):
385                 if stdout:
386                     stdout = self._translate_newlines(stdout)
387                 if stderr:
388                     stderr = self._translate_newlines(stderr)
389
390             self.wait()
391             return (stdout, stderr)
392
393     else:
394         def _get_handles(self, stdin, stdout, stderr):
395             p2cread, p2cwrite = None, None
396             c2pread, c2pwrite = None, None
397             errread, errwrite = None, None
398
399             if stdin is None:
400                 pass
401             elif stdin == PIPE:
402                 p2cread, p2cwrite = os.pipe()
403             elif isinstance(stdin, int):
404                 p2cread = stdin
405             else:
406                 p2cread = stdin.fileno()
407
408             if stdout is None:
409                 pass
410             elif stdout == PIPE:
411                 c2pread, c2pwrite = os.pipe()
412             elif isinstance(stdout, int):
413                 c2pwrite = stdout
414             else:
415                 c2pwrite = stdout.fileno()
416
417             if stderr is None:
418                 pass
419             elif stderr == PIPE:
420                 errread, errwrite = os.pipe()
421             elif stderr == STDOUT:
422                 errwrite = c2pwrite
423             elif isinstance(stderr, int):
424                 errwrite = stderr
425             else:
426                 errwrite = stderr.fileno()
427
428             return (p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite)
429
430         def _set_cloexec_flag(self, fd):
431             try:
432                 cloexec_flag = fcntl.FD_CLOEXEC
433             except AttributeError:
434                 cloexec_flag = 1
435
436             old = fcntl.fcntl(fd, fcntl.F_GETFD)
437             fcntl.fcntl(fd, fcntl.F_SETFD, old | cloexec_flag)
438
439         def _close_fds(self, but):
440             for i in xrange(3, MAXFD):
441                 if i == but:
442                     continue
443                 try:
444                     os.close(i)
445                 except:
446                     pass
447
448         def _execute_child(self, args, executable, preexec_fn, close_fds,
449                            cwd, env, universal_newlines, startupinfo, creationflags, shell,
450                            p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite):
451
452             if isinstance(args, types.StringTypes):
453                 args = [args]
454             else:
455                 args = list(args)
456
457             if shell:
458                 args = ["/bin/sh", "-c"] + args
459
460             if executable is None:
461                 executable = args[0]
462
463             errpipe_read, errpipe_write = os.pipe()
464             self._set_cloexec_flag(errpipe_write)
465
466             gc_was_enabled = gc.isenabled()
467             gc.disable()
468             try:
469                 self.pid = os.fork()
470             except:
471                 if gc_was_enabled:
472                     gc.enable()
473                 raise
474             self._child_created = True
475             if self.pid == 0:
476                 try:
477                     if p2cwrite:
478                         os.close(p2cwrite)
479                     if c2pread:
480                         os.close(c2pread)
481                     if errread:
482                         os.close(errread)
483                     os.close(errpipe_read)
484
485                     if p2cread:
486                         os.dup2(p2cread, 0)
487                     if c2pwrite:
488                         os.dup2(c2pwrite, 1)
489                     if errwrite:
490                         os.dup2(errwrite, 2)
491
492                     if p2cread and p2cread not in (0,):
493                         os.close(p2cread)
494                     if c2pwrite and c2pwrite not in (p2cread, 1):
495                         os.close(c2pwrite)
496                     if errwrite and errwrite not in (p2cread, c2pwrite, 2):
497                         os.close(errwrite)
498
499                     if close_fds:
500                         self._close_fds(but=errpipe_write)
501
502                     if cwd is not None:
503                         os.chdir(cwd)
504
505                     if preexec_fn:
506                         apply(preexec_fn)
507
508                     if env is None:
509                         os.execvp(executable, args)
510                     else:
511                         os.execvpe(executable, args, env)
512
513                 except:
514                     exc_type, exc_value, tb = sys.exc_info()
515                     exc_lines = traceback.format_exception(exc_type, exc_value, tb)
516                     exc_value.child_traceback = ''.join(exc_lines)
517                     os.write(errpipe_write, pickle.dumps(exc_value))
518
519                 os._exit(255)
520
521             if gc_was_enabled:
522                 gc.enable()
523             os.close(errpipe_write)
524             if p2cread and p2cwrite:
525                 os.close(p2cread)
526             if c2pwrite and c2pread:
527                 os.close(c2pwrite)
528             if errwrite and errread:
529                 os.close(errwrite)
530
531             data = os.read(errpipe_read, 1048576)
532             os.close(errpipe_read)
533             if data != "":
534                 os.waitpid(self.pid, 0)
535                 child_exception = pickle.loads(data)
536                 raise child_exception
537
538         def _handle_exitstatus(self, sts):
539             if os.WIFSIGNALED(sts):
540                 self.returncode = -os.WTERMSIG(sts)
541             elif os.WIFEXITED(sts):
542                 self.returncode = os.WEXITSTATUS(sts)
543             else:
544                 raise RuntimeError("Unknown child exit status!")
545
546         def poll(self, _deadstate=None):
547             if self.returncode is None:
548                 try:
549                     pid, sts = os.waitpid(self.pid, os.WNOHANG)
550                     if pid == self.pid:
551                         self._handle_exitstatus(sts)
552                 except os.error:
553                     if _deadstate is not None:
554                         self.returncode = _deadstate
555             return self.returncode
556
557         def wait(self):
558             if self.returncode is None:
559                 pid, sts = os.waitpid(self.pid, 0)
560                 self._handle_exitstatus(sts)
561             return self.returncode
562
563         def _communicate(self, input):
564             read_set = []
565             write_set = []
566             stdout = None
567             stderr = None
568
569             if self.stdin:
570                 self.stdin.flush()
571                 if input:
572                     write_set.append(self.stdin)
573                 else:
574                     self.stdin.close()
575             if self.stdout:
576                 read_set.append(self.stdout)
577                 stdout = []
578             if self.stderr:
579                 read_set.append(self.stderr)
580                 stderr = []
581
582             input_offset = 0
583             while read_set or write_set:
584                 rlist, wlist, xlist = select.select(read_set, write_set, [])
585
586                 if self.stdin in wlist:
587                     bytes_written = os.write(self.stdin.fileno(), buffer(input, input_offset, 512))
588                     input_offset += bytes_written
589                     if input_offset >= len(input):
590                         self.stdin.close()
591                         write_set.remove(self.stdin)
592
593                 if self.stdout in rlist:
594                     data = os.read(self.stdout.fileno(), 1024)
595                     if data == "":
596                         self.stdout.close()
597                         read_set.remove(self.stdout)
598                     stdout.append(data)
599
600                 if self.stderr in rlist:
601                     data = os.read(self.stderr.fileno(), 1024)
602                     if data == "":
603                         self.stderr.close()
604                         read_set.remove(self.stderr)
605                     stderr.append(data)
606
607             if stdout is not None:
608                 stdout = ''.join(stdout)
609             if stderr is not None:
610                 stderr = ''.join(stderr)
611
612             if self.universal_newlines and hasattr(file, 'newlines'):
613                 if stdout:
614                     stdout = self._translate_newlines(stdout)
615                 if stderr:
616                     stderr = self._translate_newlines(stderr)
617
618             self.wait()
619             return (stdout, stderr)
620