Package qm :: Module executable
[hide private]
[frames] | no frames]

Source Code for Module qm.executable

   1  ######################################################################## 
   2  # 
   3  # File:   executable.py 
   4  # Author: Mark Mitchell 
   5  # Date:   11/14/2002 
   6  # 
   7  # Contents: 
   8  #   Executable, RedirectedExecutable 
   9  # 
  10  # Copyright (c) 2002, 2003 by CodeSourcery, LLC.  All rights reserved.  
  11  # 
  12  ######################################################################## 
  13   
  14  ######################################################################## 
  15  # Imports 
  16  ####################################################################### 
  17   
  18  import os 
  19  import qm.common 
  20  import signal 
  21  import string 
  22  import sys 
  23  import time 
  24   
  25  # The classes in this module are implemented differently depending on 
  26  # the operating system in use. 
  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  # Classes 
  45  ####################################################################### 
  46   
47 -class Executable(object):
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
60 - def Spawn(self, arguments=[], environment = None, dir = None, 61 path = None, exception_pipe = None):
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 # Remember the directory in which the execution will occur. 103 self.__dir = dir 104 105 # The path to the executable is the first argument, if not 106 # explicitly specified. 107 if not path: 108 path = arguments[0] 109 110 # Normalize the path name. At the conclusion of this 111 # processing, the path is either an absolute path, or contains 112 # no directory seperators. 113 if os.path.isabs(path): 114 # An absolute path. 115 pass 116 elif (os.sep in path or (os.altsep and os.altsep in path)): 117 # A relative path name, like "./program". 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 # A path with no directory separators. The program to 126 # execute will be found by searching the PATH environment 127 # variable. 128 pass 129 130 # Initialize the parent. 131 startupinfo = self._InitializeParent() 132 133 # Initialize self.__child so that if "fork" or "CreateProcess" 134 # throws an exception our caller can tell that there is no 135 # child process to kill. 136 self.__child = None 137 138 if sys.platform == "win32": 139 # Compute the command line. The Windows API uses a single 140 # string as the command line, rather than an array of 141 # arguments. 142 command_line = self._CreateCommandLine(arguments) 143 144 # If the path is not absolute, then we need to search the 145 # PATH. Since CreateProcess only searches the PATH if its 146 # first argument is None, we clear path here. 147 if not os.path.isabs(path): 148 path = None 149 150 # Windows supports wide-characters in the environment, but 151 # the Win32 extensions to Python require that all of the 152 # entries in the environment be of the same type, 153 # i.e,. that either all of them be of type StringType or 154 # of type UnicodeType. Therefore, if we find any elements 155 # that are Unicode strings, convert all of them to Unicode 156 # strings. 157 if environment is not None: 158 # See if there any Unicode strings in the environment. 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 # If there are Unicode strings in the environment, 166 # convert all of the key-value pairs to Unicode. 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 # Create the child process. 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 # Fork. 186 self.__child = os.fork() 187 188 if self.__child == 0: 189 try: 190 # Close the read end of the pipe. 191 if exception_pipe: 192 os.close(exception_pipe[0]) 193 # Initialize the child. 194 self._InitializeChild() 195 # Exec the program. 196 if environment: 197 os.execvpe(path, arguments, environment) 198 else: 199 os.execvp(path, arguments) 200 except: 201 if exception_pipe: 202 # Get the exception information. 203 exc_info = sys.exc_info() 204 # Write it to the pipe. The traceback object 205 # cannot be pickled, unfortunately, so we 206 # cannot communicate that information. 207 cPickle.dump(exc_info[:2], 208 os.fdopen(exception_pipe[1], "w"), 209 1) 210 # Exit without running cleanups. 211 os._exit(1) 212 213 # This code should never be reached. 214 assert None 215 216 # Nothing will be written to the exception pipe in the parent. 217 if exception_pipe: 218 os.close(exception_pipe[1]) 219 220 # Let the parent take any actions required after creating the 221 # child. 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 # If fork succeeds, but the exec fails, we want information 256 # about *why* it failed. The exit code from the subprocess is 257 # not nearly as illuminating as the exception raised by exec. 258 # Therefore, we create a pipe between the parent and child; 259 # the child writes the exception into the pipe to communicate 260 # it to the parent. 261 if sys.platform != "win32": 262 exception_pipe = os.pipe() 263 # Mark the write end as close-on-exec so that the file 264 # descriptor is not passed on to the child. 265 qm.common.close_file_on_exec(exception_pipe[1]) 266 else: 267 exception_pipe = None 268 269 # Start the program. 270 child = self.Spawn(arguments, environment, dir, path, exception_pipe) 271 272 # Give the parent a chance to do whatever it needs to do. 273 self._DoParent() 274 275 # Wait for the child to exit. 276 if sys.platform == "win32": 277 win32event.WaitForSingleObject(child, win32event.INFINITE) 278 # Get its exit code. 279 return win32process.GetExitCodeProcess(child) 280 else: 281 status = os.waitpid(child, 0)[1] 282 self.__child = None 283 284 # See if an exception was pushed back up the pipe. 285 data = os.fdopen(exception_pipe[0]).read() 286 # If any data was read, then it is data corresponding to 287 # the exception thrown by exec. 288 if data: 289 # Unpickle the data. 290 exc_info = cPickle.loads(data) 291 # And raise it here. 292 raise exc_info[0], exc_info[1] 293 294 return status
295 296
297 - def _InitializeParent(self):
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
311 - def Kill(self):
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
325 - def _HandleChild(self):
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
337 - def _InitializeChild(self):
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 # The way Python's threading support works, every thread except 349 # the main thread always has all signals blocked. This is fine 350 # for the threads themselves, but it causes problems if we 351 # 'fork' from a child thread; the new process starts with all 352 # signals blocked, which is probably not what you want! 353 # Arguably this is a bug in Python, but for the meantime, work 354 # around this by setting the new process's signal mask to match 355 # the signal mask that QMTest was started with. 356 qm.sigmask.restore_mask() 357 358 if self.__dir: 359 os.chdir(self.__dir)
360 361
362 - def _DoParent(self):
363 """Perform actions required in the parent after 'Spawn'.""" 364 365 pass
366 367
368 - def _GetChildPID(self):
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
380 - def _CreateCommandLine(self, arguments):
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 # Add a space between arguments. 393 if need_space: 394 command += " " 395 else: 396 need_space = 1 397 # If the argument contains whitespace characters, enclose 398 # it in quotes. Similarly, an empty argument must be 399 # enclosed in quotes. 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
417 -class TimeoutExecutable(Executable):
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
445 - def __init__(self, timeout = -1):
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
464 - def _InitializeChild(self):
465 466 # Put the child into its own process group. This step is 467 # performed in both the parent and the child; therefore both 468 # processes can safely assume that the creation of the process 469 # group has taken place. 470 if self.__UseSeparateProcessGroupForChild(): 471 os.setpgid(0, 0) 472 473 super(TimeoutExecutable, self)._InitializeChild()
474 475
476 - def _HandleChild(self):
477 478 super(TimeoutExecutable, self)._HandleChild() 479 480 if self.__UseSeparateProcessGroupForChild(): 481 # Put the child into its own process group. This step is 482 # performed in both the parent and the child; therefore both 483 # processes can safely assume that the creation of the process 484 # group has taken place. 485 child_pid = self._GetChildPID() 486 try: 487 os.setpgid(child_pid, child_pid) 488 except: 489 # The call to setpgid may fail if the child has exited, 490 # or has already called 'exec'. In that case, we are 491 # guaranteed that the child has already put itself in the 492 # desired process group. 493 pass 494 495 # Create the monitoring process. 496 # 497 # If the monitoring process is in parent's process group and 498 # kills the child after waitpid has returned in the parent, we 499 # may end up trying to kill a process group other than the one 500 # that we intend to kill. Therefore, we put the monitoring 501 # process in the same process group as the child; that ensures 502 # that the process group will persist until the monitoring 503 # process kills it. 504 self.__monitor_pid = os.fork() 505 if self.__monitor_pid != 0: 506 # Make sure that the monitoring process is placed into the 507 # child's process group before the parent process calls 508 # 'waitpid'. In this way, we are guaranteed that the process 509 # group as the child 510 os.setpgid(self.__monitor_pid, child_pid) 511 else: 512 # Put the monitoring process into the child's process 513 # group. We know the process group still exists at 514 # this point because either (a) we are in the process 515 # group, or (b) the parent has not yet called waitpid. 516 os.setpgid(0, child_pid) 517 518 # Allow the monitor to exit normally if woken up by SIGHUP. 519 def terminate(signum, frame): 520 os._exit(0)
521 signal.signal(signal.SIGHUP, terminate) 522 523 524 # Close all open file descriptors. They are not needed 525 # in the monitor process. Furthermore, when the parent 526 # closes the write end of the stdin pipe to the child, 527 # we do not want the pipe to remain open; leaving the 528 # pipe open in the monitor process might cause the child 529 # to block waiting for additional input. 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 # Give the child time to run. 543 time.sleep (self.__timeout) 544 # Kill all processes in the child process group. 545 os.kill(0, signal.SIGKILL) 546 else: 547 # This call to select will never terminate. 548 select.select ([], [], []) 549 finally: 550 # Exit. This code is in a finally clause so that 551 # we are guaranteed to get here no matter what. 552 os._exit(0) 553 elif self.__timeout >= 0 and sys.platform == "win32": 554 # Create a monitoring thread. 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):
561 562 if self.__UseSeparateProcessGroupForChild(): 563 self.__monitor_pid = None 564 elif self.__timeout >= 0 and sys.platform == "win32": 565 self.__monitor_thread = None 566 567 # Run the process. 568 try: 569 status = super(TimeoutExecutable, self).Run(arguments, 570 environment, 571 dir, 572 path) 573 # At this point we tell the monitor to hang up, as we no longer need it. 574 if self.__UseSeparateProcessGroupForChild(): 575 os.kill(self.__monitor_pid, signal.SIGHUP) 576 finally: 577 if self.__UseSeparateProcessGroupForChild(): 578 # Clean up the monitoring program; it is no longer needed. 579 child_pid = self._GetChildPID() 580 if child_pid is not None: 581 os.kill(-child_pid, signal.SIGKILL) 582 if self.__monitor_pid is not None: 583 monitor_status = os.waitpid(self.__monitor_pid, 0)[1] 584 if monitor_status != 0 and self.__timeout != -2: 585 raise qm.common.Timeout(self._CreateCommandLine(arguments)) 586 elif self.__timeout >= 0 and sys.platform == "win32": 587 # Join the monitoring thread. 588 if self.__monitor_thread is not None: 589 self.__monitor_thread.join() 590 591 return status
592 593
594 - def __UseSeparateProcessGroupForChild(self):
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 # In Windows 2000 (or later), we should use "jobs" by 603 # analogy with UNIX process groups. However, that 604 # functionality is not (yet) provided by the Python Win32 605 # extensions. 606 return 0 607 608 return self.__timeout >= 0 or self.__timeout == -2
609 610 611 if sys.platform == "win32": 612
613 - def __Monitor(self):
614 """Kill the child if the timeout expires. 615 616 This function is run in the monitoring thread.""" 617 618 # The timeout may be expressed as a floating-point value 619 # on UNIX, but it must be an integer number of 620 # milliseconds when passed to WaitForSingleObject. 621 timeout = int(self.__timeout * 1000) 622 # Wait for the child process to terminate or for the 623 # timer to expire. 624 result = win32event.WaitForSingleObject(self._GetChildPID(), 625 timeout) 626 # If the timeout occurred, kill the child process. 627 if result == win32con.WAIT_TIMEOUT: 628 self.Kill()
629 630 631
632 -class RedirectedExecutable(TimeoutExecutable):
633 """A 'RedirectedExecutable' redirects the standard I/O streams.""" 634
635 - def _InitializeParent(self):
636 637 super(RedirectedExecutable, self)._InitializeParent() 638 639 # Create a pipe for each of the streams. 640 self._stdin_pipe = self._StdinPipe() 641 self._stdout_pipe = self._StdoutPipe() 642 self._stderr_pipe = self._StderrPipe() 643 644 # There has been no output yet. 645 self.stdout = "" 646 self.stderr = "" 647 648 # Under Windows, create a startupinfo structure that explains 649 # where the streams connected to the child should go. 650 if sys.platform == "win32": 651 # Create a startupinfo structure. 652 startupinfo = win32process.STARTUPINFO() 653 # Indicate that the child process should use the standard 654 # handles in startupinfo. 655 startupinfo.dwFlags = win32con.STARTF_USESTDHANDLES 656 657 # Attach each of the pipes to the appropriate entries in 658 # startupinfo. Also create a non-inheritable duplicate of the 659 # pipe end we will be using, and close the inheritable 660 # version. 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 # If there's no stderr pipe -- but there is a stdout 679 # pipe -- redirect both stdout and stderr to the same 680 # pipe. 681 startupinfo.hStdError = self._stdout_pipe[1] 682 else: 683 startupinfo.hStdError = win32file.INVALID_HANDLE_VALUE 684 685 return startupinfo
686 687
688 - def _InitializeChild(self):
689 690 # Let the base class do any initialization required. 691 super(RedirectedExecutable, self)._InitializeChild() 692 693 # Redirect the standard I/O streams to the pipes. Python does 694 # not provide STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO, 695 # so we must use the file descriptor numbers directly. 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 # If there's no stderr pipe -- but there is a stdout 710 # pipe -- redirect both stdout and stderr to the same 711 # pipe. 712 os.dup2(self._stdout_pipe[1], 2) 713 else: 714 os.close(2) 715 716 # Close the pipe fds. This should happen automatically when we 717 # exec the new process anyway, but it is polite to close fds as 718 # soon as possible. 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
730 - def _HandleChild(self):
731 732 # Close the pipe ends that we do not need. 733 if self._stdin_pipe: 734 self._ClosePipeEnd(self._stdin_pipe[0]) 735 if self._stdout_pipe: 736 self._ClosePipeEnd(self._stdout_pipe[1]) 737 if self._stderr_pipe: 738 self._ClosePipeEnd(self._stderr_pipe[1]) 739 740 # The pipes created by 'RedirectedExecutable' must be closed 741 # before the monitor process (created by 'TimeoutExecutable') 742 # is created. Otherwise, if the child process dies, 'select' 743 # in the parent will not return if the monitor process may 744 # still have one of the file descriptors open. 745 super(RedirectedExecutable, self)._HandleChild()
746 747
748 - def _DoParent(self):
749 750 super(RedirectedExecutable, self)._DoParent() 751 752 # Process the various redirected streams until none of the 753 # streams remain open. 754 if sys.platform != "win32": 755 while 1: 756 # Prepare the lists of interesting descriptors. 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 # If there are no longer any interesting descriptors, we are 767 # done. 768 if not read_fds and not write_fds: 769 return 770 771 # See which descriptors are ready for processing. 772 read_ready, write_ready \ 773 = select.select(read_fds, write_fds, [])[:2] 774 775 # Process them. 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 # Under Windows, neither select, nor 784 # WaitForMultipleObjects, works on pipes. The only 785 # approach that is reliable under all versions of Windows 786 # is to use a separate thread for each handle. By 787 # converting the pipe ends from OS handles to file 788 # descriptors at this point, _ReadStdout, _ReadStderr, and 789 # _WriteStdin can use the same implementations under 790 # Windows that they do under UNIX. 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 # Start the threads. 823 for t in stdin_thread, stdout_thread, stderr_thread: 824 if t: 825 t.start() 826 # Wait for them to finish. 827 for t in stdin_thread, stdout_thread, stderr_thread: 828 if t: 829 t.join()
830 831
832 - def _ReadStdout(self):
833 """Read from the standard output pipe.""" 834 835 # Read some data. 836 data = os.read(self._stdout_pipe[0], 64 * 1024) 837 838 if not data: 839 # If there is no new data, end-of-file has been reached. 840 os.close(self._stdout_pipe[0]) 841 self._stdout_pipe = None 842 else: 843 # Otherwise, add the data to the output we have already 844 # collected. 845 self.stdout += data
846 847
848 - def _ReadStderr(self):
849 """Read from the standard error pipe.""" 850 851 # Read some data. 852 data = os.read(self._stderr_pipe[0], 64 * 1024) 853 854 if not data: 855 # If there is no new data, end-of-file has been reached. 856 os.close(self._stderr_pipe[0]) 857 self._stderr_pipe = None 858 else: 859 # Otherwise, add the data to the output we have already 860 # collected. 861 self.stderr += data
862 863
864 - def _WriteStdin(self):
865 """Write to the standard input pipe. 866 867 This implementation writes no data and closes the pipe.""" 868 869 # Close the pipe. 870 os.close(self._stdin_pipe[1]) 871 self._stdin_pipe = None
872 873
874 - def _StdinPipe(self):
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 # Make sure that writing to the pipe will never result in 883 # deadlock. 884 fcntl.fcntl(pipe[1], fcntl.F_SETFL, 885 fcntl.fcntl(pipe[1], fcntl.F_GETFL) | os.O_NONBLOCK) 886 return pipe
887 888
889 - def _StdoutPipe(self):
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
898 - def _StderrPipe(self):
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
910 - def _ClosePipeEnd(self, fd):
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
922 - def _CreatePipe(self):
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 # Create a security descriptor so that we can mark the handles 933 # as inheritable. (A call to os.pipe under Windows 934 # returns handles that are not inheritable.) 935 sa = pywintypes.SECURITY_ATTRIBUTES() 936 sa.bInheritHandle = 1 937 # Transform the tuple returned into a list so that the 938 # individual elements can be altered. 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
948 - def __CallUntilNone(self, f, attribute):
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
959 - def __UninheritableHandle(self, handle):
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
1001 - def _WriteStdin(self):
1002 1003 # If there's nothing more to write, stop. 1004 if self.__next == len(self.__input): 1005 super(Filter, self)._WriteStdin() 1006 else: 1007 # Write some data. 1008 self.__next += os.write(self._stdin_pipe[1], 1009 self.__input[self.__next 1010 : self.__next + 64 * 1024])
1011 1012 1013 ######################################################################## 1014 # Variables 1015 ####################################################################### 1016 1017 __all__ = ["Executable", 1018 "TimeoutExecutable", 1019 "RedirectedExecutable", 1020 "Filter"] 1021