1#!/usr/bin/env python 2"""Inferior program used by process control tests.""" 3 4from __future__ import print_function 5 6import argparse 7import datetime 8import signal 9import subprocess 10import sys 11import time 12 13 14def parse_args(command_line): 15 """Parses the command line arguments given to it. 16 17 @param command_line a list of command line arguments to be parsed. 18 19 @return the argparse options dictionary. 20 """ 21 parser = argparse.ArgumentParser() 22 parser.add_argument( 23 "--ignore-signal", 24 "-i", 25 dest="ignore_signals", 26 metavar="SIGNUM", 27 action="append", 28 type=int, 29 default=[], 30 help="ignore the given signal number (if possible)") 31 parser.add_argument( 32 "--launch-child-share-handles", 33 action="store_true", 34 help=("launch a child inferior.py that shares stdout/stderr/stdio and " 35 "never returns")) 36 parser.add_argument( 37 "--never-return", 38 action="store_true", 39 help="run in an infinite loop, never return") 40 parser.add_argument( 41 "--return-code", 42 "-r", 43 type=int, 44 default=0, 45 help="specify the return code for the inferior upon exit") 46 parser.add_argument( 47 "--sleep", 48 "-s", 49 metavar="SECONDS", 50 dest="sleep_seconds", 51 type=float, 52 help="sleep for SECONDS seconds before returning") 53 parser.add_argument( 54 "--verbose", "-v", action="store_true", 55 help="log verbose operation details to stdout") 56 return parser.parse_args(command_line) 57 58 59def handle_ignore_signals(options, signals): 60 """Ignores any signals provided to it. 61 62 @param options the command line options parsed by the program. 63 General used to check flags for things like verbosity. 64 65 @param signals the list of signals to ignore. Can be None or zero-length. 66 Entries should be type int. 67 """ 68 if signals is None: 69 return 70 71 for signum in signals: 72 if options.verbose: 73 print("disabling signum {}".format(signum)) 74 signal.signal(signum, signal.SIG_IGN) 75 76 77def handle_sleep(options, sleep_seconds): 78 """Sleeps the number of seconds specified, restarting as needed. 79 80 @param options the command line options parsed by the program. 81 General used to check flags for things like verbosity. 82 83 @param sleep_seconds the number of seconds to sleep. If None 84 or <= 0, no sleeping will occur. 85 """ 86 if sleep_seconds is None: 87 return 88 89 if sleep_seconds <= 0: 90 return 91 92 end_time = datetime.datetime.now() + datetime.timedelta(0, sleep_seconds) 93 if options.verbose: 94 print("sleep end time: {}".format(end_time)) 95 96 # Do sleep in a loop: signals can interrupt. 97 while datetime.datetime.now() < end_time: 98 # We'll wrap this in a try/catch so we don't encounter 99 # a race if a signal (ignored) knocks us out of this 100 # loop and causes us to return. 101 try: 102 sleep_interval = end_time - datetime.datetime.now() 103 sleep_seconds = sleep_interval.total_seconds() 104 if sleep_seconds > 0: 105 time.sleep(sleep_seconds) 106 except: # pylint: disable=bare-except 107 pass 108 109 110def handle_launch_children(options): 111 if options.launch_child_share_handles: 112 # Launch the child, share our file handles. 113 # We won't bother reaping it since it will likely outlive us. 114 subprocess.Popen([sys.executable, __file__, "--never-return"]) 115 116 117def handle_never_return(options): 118 if not options.never_return: 119 return 120 121 # Loop forever. 122 while True: 123 try: 124 time.sleep(10) 125 except: # pylint: disable=bare-except 126 # Ignore 127 pass 128 129 130def main(command_line): 131 """Drives the main operation of the inferior test program. 132 133 @param command_line the command line options to process. 134 135 @return the exit value (program return code) for the process. 136 """ 137 options = parse_args(command_line) 138 handle_ignore_signals(options, options.ignore_signals) 139 handle_launch_children(options) 140 handle_sleep(options, options.sleep_seconds) 141 handle_never_return(options) 142 143 return options.return_code 144 145if __name__ == "__main__": 146 sys.exit(main(sys.argv[1:])) 147