1#!/usr/bin/env python 2 3r""" 4See help text for details. 5""" 6 7import sys 8import subprocess 9 10save_path_0 = sys.path[0] 11del sys.path[0] 12 13from gen_arg import * 14from gen_print import * 15from gen_valid import * 16from gen_misc import * 17from gen_cmd import * 18from var_funcs import * 19 20# Restore sys.path[0]. 21sys.path.insert(0, save_path_0) 22 23# Set exit_on_error for gen_valid functions. 24set_exit_on_error(True) 25 26parser = argparse.ArgumentParser( 27 usage='%(prog)s [OPTIONS]', 28 description="%(prog)s will create a status file path name adhering to the" 29 + " following pattern: <status dir path>/<prefix>.yymmdd." 30 + "hhmmss.status. It will then run the command string and" 31 + " direct its stdout/stderr to the status file and optionally" 32 + " to stdout. This dual output streaming will be" 33 + " accomplished using either the \"script\" or the \"tee\"" 34 + " program. %(prog)s will also set and export environment" 35 + " variable \"AUTO_STATUS_FILE_PATH\" for the benefit of" 36 + " child programs.", 37 formatter_class=argparse.ArgumentDefaultsHelpFormatter, 38 prefix_chars='-+') 39 40parser.add_argument( 41 '--status_dir_path', 42 default='', 43 help="The path to the directory where the status file will be created." 44 + "%(default)s The default value is obtained from environment" 45 + " variable \"${STATUS_DIR_PATH}\", if set or from \"${HOME}/" 46 + "status/\".") 47 48parser.add_argument( 49 '--prefix', 50 default='', 51 help="The prefix for the generated file name.%(default)s The default value" 52 + " is the command portion (i.e. the first token) of the command" 53 + " string.") 54 55parser.add_argument( 56 '--status_file_name', 57 default='', 58 help="This allows the user to explicitly specify the status file name. If" 59 + " this argument is not used, %(prog)s composes a status file name." 60 + " If this argument is specified, the \"--prefix\" argument is" 61 + " ignored.") 62 63parser.add_argument( 64 '--stdout', 65 default=1, 66 type=int, 67 choices=[1, 0], 68 help="Indicates that stdout/stderr from the command string execution" 69 + " should be written to stdout as well as to the status file.") 70 71parser.add_argument( 72 '--tee', 73 default=0, 74 type=int, 75 choices=[1, 0], 76 help="Indicates that \"tee\" rather than \"script\" should be used.") 77 78parser.add_argument( 79 '--show_url', 80 default=0, 81 type=int, 82 choices=[1, 0], 83 help="Indicates that the status file path shown should be shown in the" 84 + " form of a url. If the output is to be viewed from a browser," 85 + " this may well become a clickable link. Note that the" 86 + " get_file_path_url.py program must be found in the \"PATH\"" 87 + " environment variable for this argument to be effective.") 88 89parser.add_argument( 90 'command_string', 91 default='', 92 nargs='*', 93 help="The command string to be run.%(default)s") 94 95# Populate stock_list with options we want. 96stock_list = [("test_mode", 0), ("quiet", 1), ("debug", 0)] 97 98 99def exit_function(signal_number=0, 100 frame=None): 101 r""" 102 Execute whenever the program ends normally or with the signals that we 103 catch (i.e. TERM, INT). 104 """ 105 106 dprint_executing() 107 dprint_var(signal_number) 108 109 qprint_pgm_footer() 110 111 112def signal_handler(signal_number, 113 frame): 114 r""" 115 Handle signals. Without a function to catch a SIGTERM or SIGINT, our 116 program would terminate immediately with return code 143 and without 117 calling our exit_function. 118 """ 119 120 # Our convention is to set up exit_function with atexit.register() so 121 # there is no need to explicitly call exit_function from here. 122 123 dprint_executing() 124 125 # Calling exit prevents us from returning to the code that was running 126 # when we received the signal. 127 exit(0) 128 129 130def validate_parms(): 131 r""" 132 Validate program parameters, etc. 133 """ 134 135 global status_dir_path 136 global command_string 137 138 # Convert command_string from list to string. 139 command_string = " ".join(command_string) 140 set_pgm_arg(command_string) 141 valid_value(command_string) 142 143 if status_dir_path == "": 144 status_dir_path = \ 145 os.environ.get("STATUS_DIR_PATH", 146 os.environ.get("HOME") + "/status/") 147 status_dir_path = add_trailing_slash(status_dir_path) 148 set_pgm_arg(status_dir_path) 149 valid_dir_path(status_dir_path) 150 151 global prefix 152 global status_file_name 153 if status_file_name == "": 154 if prefix == "": 155 prefix = command_string.split(" ")[0] 156 set_pgm_arg(prefix) 157 status_file_name = prefix + "." + file_date_time_stamp() + ".status" 158 set_pgm_arg(status_file_name) 159 160 global status_file_path 161 162 status_file_path = status_dir_path + status_file_name 163 # Set environment variable for the benefit of child programs. 164 os.environ['AUTO_STATUS_FILE_PATH'] = status_file_path 165 # Set deprecated but still used AUTOSCRIPT_STATUS_FILE_PATH value. 166 os.environ['AUTOSCRIPT_STATUS_FILE_PATH'] = status_file_path 167 168 gen_post_validation(exit_function, signal_handler) 169 170 171def script_func(command_string, status_file_path): 172 r""" 173 Run the command string producing both stdout and file output via the 174 script command and return the shell_rc. 175 176 Description of argument(s): 177 command_string The command string to be run. 178 status_file_path The path to the status file which is to 179 contain a copy of all stdout. 180 """ 181 182 cmd_buf = "script -a -q -f " + status_file_path + " -c '" \ 183 + escape_bash_quotes(command_string) + " ; printf \"\\n" \ 184 + sprint_varx(ret_code_str, "${?}").rstrip("\n") + "\\n\"'" 185 qprint_issuing(cmd_buf) 186 sub_proc = subprocess.Popen(cmd_buf, shell=True) 187 sub_proc.communicate() 188 shell_rc = sub_proc.returncode 189 190 # Retrieve return code by examining ret_code_str output statement from 191 # status file. 192 # Example text to be analyzed. 193 # auto_status_file_ret_code: 127 194 cmd_buf = "tail -n 10 " + status_file_path + " | egrep -a \"" \ 195 + ret_code_str + ":[ ]+\"" 196 rc, output = shell_cmd(cmd_buf) 197 key, value = parse_key_value(output) 198 shell_rc = int(value) 199 200 return shell_rc 201 202 203def tee_func(command_string, status_file_path): 204 r""" 205 Run the command string producing both stdout and file output via the tee 206 command and return the shell_rc. 207 208 Description of argument(s): 209 command_string The command string to be run. 210 status_file_path The path to the status file which is to 211 contain a copy of all stdout. 212 """ 213 214 cmd_buf = "set -o pipefail ; " + command_string + " 2>&1 | tee -a " \ 215 + status_file_path 216 qprint_issuing(cmd_buf) 217 sub_proc = subprocess.Popen(cmd_buf, shell=True) 218 sub_proc.communicate() 219 shell_rc = sub_proc.returncode 220 221 print 222 print_varx(ret_code_str, shell_rc) 223 with open(status_file_path, "a") as status_file: 224 # Append ret code string and status_file_path to end of status file. 225 status_file.write("\n" + sprint_varx(ret_code_str, shell_rc)) 226 227 return shell_rc 228 229 230def main(): 231 232 gen_get_options(parser, stock_list) 233 234 validate_parms() 235 236 qprint_pgm_header() 237 238 global ret_code_str 239 ret_code_str = re.sub("\\.py$", "", pgm_name) + "_ret_code" 240 241 global show_url 242 if show_url: 243 shell_rc, output = shell_cmd("which get_file_path_url.py", show_err=0) 244 if shell_rc != 0: 245 show_url = 0 246 set_pgm_arg(show_url) 247 else: 248 shell_rc, status_file_url = shell_cmd("get_file_path_url.py " 249 + status_file_path) 250 status_file_url = status_file_url.rstrip("\n") 251 252 # Print status file path/url to stdout and to status file. 253 with open(status_file_path, "w+") as status_file: 254 if show_url: 255 print_var(status_file_url) 256 status_file.write(sprint_var(status_file_url)) 257 else: 258 print_var(status_file_path) 259 status_file.write(sprint_var(status_file_path)) 260 261 if stdout: 262 if tee: 263 shell_rc = tee_func(command_string, status_file_path) 264 else: 265 shell_rc = script_func(command_string, status_file_path) 266 if show_url: 267 print_var(status_file_url) 268 else: 269 print_var(status_file_path) 270 else: 271 cmd_buf = command_string + " >> " + status_file_path + " 2>&1" 272 shell_rc, output = shell_cmd(cmd_buf, show_err=0) 273 with open(status_file_path, "a") as status_file: 274 # Append ret code string and status_file_path to end of status 275 # file. 276 status_file.write("\n" + sprint_varx(ret_code_str, shell_rc)) 277 278 # Append status_file_path print statement to end of status file. 279 with open(status_file_path, "a") as status_file: 280 if show_url: 281 status_file.write(sprint_var(status_file_url)) 282 else: 283 status_file.write(sprint_var(status_file_path)) 284 exit(shell_rc) 285 286 287main() 288