1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 import os
19 import qm.common
20 import signal
21 import string
22 import sys
23 import time
24
25
26
27 if sys.platform == "win32":
28 import msvcrt
29 import pywintypes
30 from threading import *
31 import win32api
32 import win32con
33 import win32event
34 import win32file
35 import win32pipe
36 import win32process
37 else:
38 import cPickle
39 import fcntl
40 import select
41 import qm.sigmask
42
43
44
45
46
48 """An 'Executable' is a program that the operating system can run.
49
50 'Exectuable' (and classes derived from it) create child processes.
51 The 'Spawn' function creates child processes that execute
52 asynchronously. The 'Run' function creates child processes that
53 execute synchrounously, i.e,. the 'Run' function does not return
54 until the child process has completed its execution.
55
56 It is safe to reuse a particular 'Executable' instance (by calling
57 'Spawn' or 'Run' more than once), so long as the uses are not
58 interleaved."""
59
62 """Spawn the program.
63
64 'arguments' -- The sequence of arguments that should be passed
65 to the executable. The first argument provided in this
66 sequence will be 'argv[0]'; that is also the value used for
67 the path to the executable.
68
69 'environment' -- If not 'None', a dictionary giving the
70 environment that should be provided to the child.
71
72 'dir' -- If not 'None', the directory in which the child
73 should begin execution. If 'None', the child will execute in
74 the same directory as the parent.
75
76 'path' -- If not 'None', the path to the program to run. If
77 'None', 'arguments[0]' is used.
78
79 'exception_pipe' -- If not 'None', a pipe that the child can
80 use to communicate an exception to the parent. This pipe is
81 only used on UNIX systems. The write end of the pipe will be
82 closed by this function.
83
84 returns -- The PID of the child.
85
86 Before creating the child, the parent will call
87 'self._InitializeParent'. On UNIX systems, the child will
88 call 'self._InitializeChild' after 'fork', but before 'exec'.
89 On non-UNIX systems, 'self._InitializeChild' will never be
90 called.
91
92 After creating the child, 'self._HandleChild' is called in the
93 parent. This hook should be used to handle tasks that must be
94 performed after the child is running.
95
96 If the path to the program is absolute, or contains no
97 separator characters, it is not modified. Otherwise the path
98 to the program is relative, it is transformed into an absolute
99 path using 'dir' as the base, or the current directory if
100 'dir' is not set."""
101
102
103 self.__dir = dir
104
105
106
107 if not path:
108 path = arguments[0]
109
110
111
112
113 if os.path.isabs(path):
114
115 pass
116 elif (os.sep in path or (os.altsep and os.altsep in path)):
117
118 if dir:
119 path = os.path.normpath(os.path.join(dir, path))
120 if not os.path.isabs(path):
121 path = os.path.abspath(path)
122 else:
123 path = os.path.abspath(path)
124 else:
125
126
127
128 pass
129
130
131 startupinfo = self._InitializeParent()
132
133
134
135
136 self.__child = None
137
138 if sys.platform == "win32":
139
140
141
142 command_line = self._CreateCommandLine(arguments)
143
144
145
146
147 if not os.path.isabs(path):
148 path = None
149
150
151
152
153
154
155
156
157 if environment is not None:
158
159 uses_unicode = 0
160 for (k, v) in environment.iteritems():
161 if (isinstance(k, unicode)
162 or isinstance(v, unicode)):
163 uses_unicode = 1
164 break
165
166
167 if uses_unicode:
168 new_environment = {}
169 for (k, v) in environment.iteritems():
170 new_environment[unicode(k)] = unicode(v)
171 environment = new_environment
172
173
174 self.__child \
175 = win32process.CreateProcess(path,
176 command_line,
177 None,
178 None,
179 1,
180 0,
181 environment,
182 self.__dir,
183 startupinfo)[0]
184 else:
185
186 self.__child = os.fork()
187
188 if self.__child == 0:
189 try:
190
191 if exception_pipe:
192 os.close(exception_pipe[0])
193
194 self._InitializeChild()
195
196 if environment:
197 os.execvpe(path, arguments, environment)
198 else:
199 os.execvp(path, arguments)
200 except:
201 if exception_pipe:
202
203 exc_info = sys.exc_info()
204
205
206
207 cPickle.dump(exc_info[:2],
208 os.fdopen(exception_pipe[1], "w"),
209 1)
210
211 os._exit(1)
212
213
214 assert None
215
216
217 if exception_pipe:
218 os.close(exception_pipe[1])
219
220
221
222 self._HandleChild()
223
224 return self.__child
225
226
227 - def Run(self, arguments=[], environment = None, dir = None,
228 path = None):
229 """Spawn the program and wait for it to finish.
230
231 'arguments' -- The sequence of arguments that should be passed
232 to the executable. The first argument provided in this
233 sequence will be 'argv[0]'.
234
235 'environment' -- If not 'None', a dictionary giving the
236 environment that should be provided to the child. If 'None',
237 the child will inherit the parents environment.
238
239 'dir' -- If not 'None', the directory in which the child
240 should begin execution. If 'None', the child will execute in
241 the same directory as the parent.
242
243 'path' -- If not 'None', the path to the program to run. If
244 'None', 'arguments[0]' is used.
245
246 returns -- The status returned by the program. Under UNIX,
247 this is the value returned by 'waitpid'; under Windows, it is
248 the value returned by 'GetExitCodeProcess'.
249
250 After invoking 'Spawn', this function invokes '_DoParent' to
251 allow the parent process to perform whatever actions are
252 required. After that function returns, the parent waits for
253 the child process to exit."""
254
255
256
257
258
259
260
261 if sys.platform != "win32":
262 exception_pipe = os.pipe()
263
264
265 qm.common.close_file_on_exec(exception_pipe[1])
266 else:
267 exception_pipe = None
268
269
270 child = self.Spawn(arguments, environment, dir, path, exception_pipe)
271
272
273 self._DoParent()
274
275
276 if sys.platform == "win32":
277 win32event.WaitForSingleObject(child, win32event.INFINITE)
278
279 return win32process.GetExitCodeProcess(child)
280 else:
281 status = os.waitpid(child, 0)[1]
282 self.__child = None
283
284
285 data = os.fdopen(exception_pipe[0]).read()
286
287
288 if data:
289
290 exc_info = cPickle.loads(data)
291
292 raise exc_info[0], exc_info[1]
293
294 return status
295
296
298 """Initialize the parent process.
299
300 Before spawning the child, this method is invoked to give the
301 parent a chance to initialize itself.
302
303 returns -- Under Windows, a 'PySTARTUPINFO' structure
304 explaining how the child should be initialized. On other
305 systems, the return value is ignored."""
306
307 if sys.platform == "win32":
308 return win32process.STARTUPINFO()
309
310
312 """Kill the child process.
313
314 The child process is killed in a way that does not permit an
315 orderly shutdown. In other words, 'SIGKILL' is used under
316 UNIX, not 'SIGTERM'. On Windows, 'TerminateProcess' is used,
317 and the exit code from the child process will be '1'."""
318
319 if sys.platform == "win32":
320 win32process.TerminateProcess(self._GetChildPID(), 1)
321 else:
322 os.kill(self._GetChildPID(), signal.SIGKILL)
323
324
326 """Run in the parent process after the child has been created.
327
328 The child process has been spawned; its PID is avialable via
329 '_GetChildPID'. Take any actions in the parent that are
330 required now that the child exists.
331
332 Derived class versions must call this method."""
333
334 pass
335
336
338 """Initialize the child process.
339
340 After 'fork' is called this method is invoked to give the
341 child a chance to initialize itself. '_InitializeParent' will
342 already have been called in the parent process.
343
344 This method is not used under Windows."""
345
346 assert sys.platform != "win32"
347
348
349
350
351
352
353
354
355
356 qm.sigmask.restore_mask()
357
358 if self.__dir:
359 os.chdir(self.__dir)
360
361
363 """Perform actions required in the parent after 'Spawn'."""
364
365 pass
366
367
369 """Return the process ID for the child process.
370
371 returns -- The process ID for the child process. (On Windows,
372 the value returned is the process handle.) Returns 'None' if
373 the child has not yet been created, or if something went awry
374 when creating it. For example, if 'os.fork' throws an
375 exception, this value will return 'None'."""
376
377 return self.__child
378
379
381 """Return a string giving the process command line.
382
383 arguments -- A sequence of arguments (including argv[0])
384 indicating the command to be run.
385
386 returns -- A string that could be provided to the shell in
387 order to run the command."""
388
389 command = ""
390 need_space = 0
391 for a in arguments:
392
393 if need_space:
394 command += " "
395 else:
396 need_space = 1
397
398
399
400 if not a:
401 command += '""'
402 continue
403 whitespace = 0
404 for c in string.whitespace:
405 if c in a:
406 whitespace = 1
407 break
408 if whitespace:
409 command += '"' + a + '"'
410 else:
411 command += a
412
413 return command
414
415
416
418 """A 'TimeoutExecutable' runs for a limited time.
419
420 If the timer expires, the child process is killed and a Timeout
421 exception is raised.
422
423 In order to implement this functionality under UNIX, the child
424 process is placed into its own process group. An additional
425 monitoring process is created whose sole job is to kill the
426 primary child's process group if the timeout expires. Process
427 groups are used so that if the child process spawns additional
428 processes they are killed too. A separate monitoring process is
429 used so as not to block the parent.
430
431 Under Windows, a monitoring thread is created. When the timer
432 expires, the child process is terminated. However, the child
433 process is not placed into a separate process group, so
434 granchildren kare not terminated. In the future, when Python
435 provides access to 'CreateJobObject' and related functions, jobs
436 will be used to provide functionality similar to UNIX process
437 groups.
438
439 The 'Run' method will automatically start the monitoring process.
440 The 'Spawn' method does not start the monitoring process. User's
441 of 'Spawn' should invoke '_DoParent' in order to start the
442 monitoring process. Derived class '_DoParent' functions should
443 call the version defined in this class."""
444
446 """Construct a new 'TimeoutExecutable'.
447
448 'timeout' -- The number of seconds that the child is permitted
449 to run. This value may be a floating-point value. However,
450 the value may be rounded to an integral value on some systems.
451 Once the timeout expires, the child and its entire process
452 group is killed. (The processes in the process group are sent
453 the 'SIGKILL' signal.) If the 'timeout' is -2, the child is
454 allowed to run forever, but when it terminates the child's
455 process group is killed.
456
457 If the 'timeout' is -1, this class behaves exactly like
458 'Executable'."""
459
460 super(TimeoutExecutable, self).__init__()
461 self.__timeout = float(timeout)
462
463
474
475
477
478 super(TimeoutExecutable, self)._HandleChild()
479
480 if self.__UseSeparateProcessGroupForChild():
481
482
483
484
485 child_pid = self._GetChildPID()
486 try:
487 os.setpgid(child_pid, child_pid)
488 except:
489
490
491
492
493 pass
494
495
496
497
498
499
500
501
502
503
504 self.__monitor_pid = os.fork()
505 if self.__monitor_pid != 0:
506
507
508
509
510 os.setpgid(self.__monitor_pid, child_pid)
511 else:
512
513
514
515
516 os.setpgid(0, child_pid)
517
518
519 def terminate(signum, frame):
520 os._exit(0)
521 signal.signal(signal.SIGHUP, terminate)
522
523
524
525
526
527
528
529
530 try:
531 max_fds = os.sysconf("SC_OPEN_MAX")
532 except:
533 max_fds = 256
534 for fd in xrange(max_fds):
535 try:
536 os.close(fd)
537 except:
538 pass
539
540 try:
541 if self.__timeout >= 0:
542
543 time.sleep (self.__timeout)
544
545 os.kill(0, signal.SIGKILL)
546 else:
547
548 select.select ([], [], [])
549 finally:
550
551
552 os._exit(0)
553 elif self.__timeout >= 0 and sys.platform == "win32":
554
555 self.__monitor_thread = Thread(target = self.__Monitor)
556 self.__monitor_thread.start()
557
558
559 - def Run(self, arguments=[], environment = None, dir = None,
560 path = None):
592
593
595 """Returns true if the child wil be placed in its own process group.
596
597 returns -- True if the child will be placed in its own process
598 group. In that case, a separate monitoring process will also
599 be created."""
600
601 if sys.platform == "win32":
602
603
604
605
606 return 0
607
608 return self.__timeout >= 0 or self.__timeout == -2
609
610
611 if sys.platform == "win32":
612
614 """Kill the child if the timeout expires.
615
616 This function is run in the monitoring thread."""
617
618
619
620
621 timeout = int(self.__timeout * 1000)
622
623
624 result = win32event.WaitForSingleObject(self._GetChildPID(),
625 timeout)
626
627 if result == win32con.WAIT_TIMEOUT:
628 self.Kill()
629
630
631
633 """A 'RedirectedExecutable' redirects the standard I/O streams."""
634
636
637 super(RedirectedExecutable, self)._InitializeParent()
638
639
640 self._stdin_pipe = self._StdinPipe()
641 self._stdout_pipe = self._StdoutPipe()
642 self._stderr_pipe = self._StderrPipe()
643
644
645 self.stdout = ""
646 self.stderr = ""
647
648
649
650 if sys.platform == "win32":
651
652 startupinfo = win32process.STARTUPINFO()
653
654
655 startupinfo.dwFlags = win32con.STARTF_USESTDHANDLES
656
657
658
659
660
661 if self._stdin_pipe:
662 startupinfo.hStdInput = self._stdin_pipe[0]
663 self._stdin_pipe[1] \
664 = self.__UninheritableHandle(self._stdin_pipe[1])
665 else:
666 startupinfo.hStdInput = win32file.INVALID_HANDLE_VALUE
667 if self._stdout_pipe:
668 startupinfo.hStdOutput = self._stdout_pipe[1]
669 self._stdout_pipe[0] \
670 = self.__UninheritableHandle(self._stdout_pipe[0])
671 else:
672 startupinfo.hStdOutput = win32file.INVALID_HANDLE_VALUE
673 if self._stderr_pipe:
674 startupinfo.hStdError = self._stderr_pipe[1]
675 self._stderr_pipe[0] \
676 = self.__UninheritableHandle(self._stderr_pipe[0])
677 elif self._stdout_pipe:
678
679
680
681 startupinfo.hStdError = self._stdout_pipe[1]
682 else:
683 startupinfo.hStdError = win32file.INVALID_HANDLE_VALUE
684
685 return startupinfo
686
687
689
690
691 super(RedirectedExecutable, self)._InitializeChild()
692
693
694
695
696 if self._stdin_pipe:
697 os.dup2(self._stdin_pipe[0], 0)
698 else:
699 os.close(0)
700
701 if self._stdout_pipe:
702 os.dup2(self._stdout_pipe[1], 1)
703 else:
704 os.close(1)
705
706 if self._stderr_pipe:
707 os.dup2(self._stderr_pipe[1], 2)
708 elif self._stdout_pipe:
709
710
711
712 os.dup2(self._stdout_pipe[1], 2)
713 else:
714 os.close(2)
715
716
717
718
719 if self._stdin_pipe:
720 os.close(self._stdin_pipe[0])
721 os.close(self._stdin_pipe[1])
722 if self._stdout_pipe:
723 os.close(self._stdout_pipe[0])
724 os.close(self._stdout_pipe[1])
725 if self._stderr_pipe:
726 os.close(self._stderr_pipe[0])
727 os.close(self._stderr_pipe[1])
728
729
746
747
749
750 super(RedirectedExecutable, self)._DoParent()
751
752
753
754 if sys.platform != "win32":
755 while 1:
756
757 read_fds = []
758 write_fds = []
759 if self._stdout_pipe:
760 read_fds.append(self._stdout_pipe[0])
761 if self._stderr_pipe:
762 read_fds.append(self._stderr_pipe[0])
763 if self._stdin_pipe:
764 write_fds.append(self._stdin_pipe[1])
765
766
767
768 if not read_fds and not write_fds:
769 return
770
771
772 read_ready, write_ready \
773 = select.select(read_fds, write_fds, [])[:2]
774
775
776 if self._stdout_pipe and self._stdout_pipe[0] in read_ready:
777 self._ReadStdout()
778 if self._stderr_pipe and self._stderr_pipe[0] in read_ready:
779 self._ReadStderr()
780 if self._stdin_pipe and self._stdin_pipe[1] in write_ready:
781 self._WriteStdin()
782 else:
783
784
785
786
787
788
789
790
791
792 if self._stdin_pipe:
793 h = self._stdin_pipe[1]
794 self._stdin_pipe[1] = msvcrt.open_osfhandle(h, 0)
795 h.Detach()
796 stdin_thread = Thread(target = self.__CallUntilNone,
797 args = (self._WriteStdin,
798 "_stdin_pipe"))
799 else:
800 stdin_thread = None
801
802 if self._stdout_pipe:
803 h = self._stdout_pipe[0]
804 self._stdout_pipe[0] = msvcrt.open_osfhandle(h, 0)
805 h.Detach()
806 stdout_thread = Thread(target = self.__CallUntilNone,
807 args = (self._ReadStdout,
808 "_stdout_pipe"))
809 else:
810 stdout_thread = None
811
812 if self._stderr_pipe:
813 h = self._stderr_pipe[0]
814 self._stderr_pipe[0] = msvcrt.open_osfhandle(h, 0)
815 h.Detach()
816 stderr_thread = Thread(target = self.__CallUntilNone,
817 args = (self._ReadStderr,
818 "_stderr_pipe"))
819 else:
820 stderr_thread = None
821
822
823 for t in stdin_thread, stdout_thread, stderr_thread:
824 if t:
825 t.start()
826
827 for t in stdin_thread, stdout_thread, stderr_thread:
828 if t:
829 t.join()
830
831
833 """Read from the standard output pipe."""
834
835
836 data = os.read(self._stdout_pipe[0], 64 * 1024)
837
838 if not data:
839
840 os.close(self._stdout_pipe[0])
841 self._stdout_pipe = None
842 else:
843
844
845 self.stdout += data
846
847
849 """Read from the standard error pipe."""
850
851
852 data = os.read(self._stderr_pipe[0], 64 * 1024)
853
854 if not data:
855
856 os.close(self._stderr_pipe[0])
857 self._stderr_pipe = None
858 else:
859
860
861 self.stderr += data
862
863
865 """Write to the standard input pipe.
866
867 This implementation writes no data and closes the pipe."""
868
869
870 os.close(self._stdin_pipe[1])
871 self._stdin_pipe = None
872
873
875 """Return a pipe to which to redirect the standard input.
876
877 returns -- A pipe, or 'None' if the standard input should be
878 closed in the child."""
879
880 pipe = self._CreatePipe()
881 if sys.platform != "win32":
882
883
884 fcntl.fcntl(pipe[1], fcntl.F_SETFL,
885 fcntl.fcntl(pipe[1], fcntl.F_GETFL) | os.O_NONBLOCK)
886 return pipe
887
888
890 """Return a pipe to which to redirect the standard output.
891
892 returns -- A pipe, or 'None' if the standard output should be
893 closed in the child."""
894
895 return self._CreatePipe()
896
897
899 """Return a pipe to which to redirect the standard input.
900
901 returns -- A pipe, or 'None'. If 'None' is returned, but
902 '_StdoutPipe' returns a pipe, then the standard error and
903 standard input will both be redirected to that pipe. However,
904 if '_StdoutPipe' also returns 'None', then the standard error
905 will be closed in the child."""
906
907 return self._CreatePipe()
908
909
911 """Close the file descriptor 'fd', which is one end of a pipe.
912
913 'fd' -- Under UNIX, a file descriptor. Under Windows, a
914 handle."""
915
916 if sys.platform == "win32":
917 fd.Close()
918 else:
919 os.close(fd)
920
921
923 """Return a new pipe.
924
925 returns -- A tuple (under UNIX) or list (under Windows)
926 consisting of the file descriptors (UNIX) or handles (Windows)
927 for the read end and write end of a new pipe. The pipe is
928 inheritable by child processes. On UNIX the fds will not be
929 inherited across 'exec'."""
930
931 if sys.platform == "win32":
932
933
934
935 sa = pywintypes.SECURITY_ATTRIBUTES()
936 sa.bInheritHandle = 1
937
938
939 r, w = win32pipe.CreatePipe(sa, 0)
940 return [r, w]
941 else:
942 pipe = os.pipe()
943 for fd in pipe:
944 qm.common.close_file_on_exec(fd)
945 return pipe
946
947
949 """Call 'f' until 'self.attribute' is 'None'.
950
951 'f' -- A callable.
952
953 'attribute' -- A string giving the name of an attribute."""
954
955 while getattr(self, attribute) is not None:
956 f()
957
958
960 """Return a duplicate of a file handle that is not inheritable.
961
962 'handle' -- A file handle.
963
964 returns -- A new handle that is a non-inheritable duplicate of
965 the 'handle'.
966
967 This method should only be used under Windows."""
968
969 assert sys.platform == "win32"
970
971 current_process = win32api.GetCurrentProcess()
972 return win32api.DuplicateHandle(current_process,
973 handle,
974 current_process,
975 0,
976 0,
977 win32con.DUPLICATE_SAME_ACCESS)
978
979
980
981 -class Filter(RedirectedExecutable):
982 """A 'FilterExecutable' feeds an input string to another proces.
983
984 The input string is provided to a child process via a pipe; the
985 standard output and standard error streams from the child process
986 are collected in the 'Filter'."""
987
988 - def __init__(self, input, timeout = -1):
989 """Create a new 'Filter'.
990
991 'input' -- The string containing the input to provide to the
992 child process.
993
994 'timeout' -- As for 'TimeoutExecutable.__init__'."""
995
996 super(Filter, self).__init__(timeout)
997 self.__input = input
998 self.__next = 0
999
1000
1002
1003
1004 if self.__next == len(self.__input):
1005 super(Filter, self)._WriteStdin()
1006 else:
1007
1008 self.__next += os.write(self._stdin_pipe[1],
1009 self.__input[self.__next
1010 : self.__next + 64 * 1024])
1011
1012
1013
1014
1015
1016
1017 __all__ = ["Executable",
1018 "TimeoutExecutable",
1019 "RedirectedExecutable",
1020 "Filter"]
1021