blob: c3f2544b45ff3c4d149bce17734e54bc7ef7ee2c [file] [log] [blame]
George Keishinge7e91712021-09-03 11:28:44 -05001#!/usr/bin/env python3
Michael Walsh888be1f2019-03-26 12:02:36 -05002
3r"""
4See help text for details.
5"""
6
George Keishinge635ddc2022-12-08 07:38:02 -06007import re
Patrick Williams20f38712022-12-08 06:18:26 -06008import subprocess
9import sys
Michael Walsh888be1f2019-03-26 12:02:36 -050010
Michael Walsh60d08f32019-11-25 16:51:39 -060011save_dir_path = sys.path.pop(0)
Michael Walsh888be1f2019-03-26 12:02:36 -050012
Patrick Williams20f38712022-12-08 06:18:26 -060013modules = [
14 "gen_arg",
15 "gen_print",
16 "gen_valid",
17 "gen_misc",
18 "gen_cmd",
19 "var_funcs",
20]
Michael Walsh60d08f32019-11-25 16:51:39 -060021for module in modules:
22 exec("from " + module + " import *")
Michael Walsh888be1f2019-03-26 12:02:36 -050023
Michael Walsh60d08f32019-11-25 16:51:39 -060024sys.path.insert(0, save_dir_path)
Michael Walsh888be1f2019-03-26 12:02:36 -050025
26parser = argparse.ArgumentParser(
Patrick Williams20f38712022-12-08 06:18:26 -060027 usage="%(prog)s [OPTIONS]",
Michael Walsh888be1f2019-03-26 12:02:36 -050028 description="%(prog)s will create a status file path name adhering to the"
Patrick Williams20f38712022-12-08 06:18:26 -060029 + " 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.",
Michael Walsh888be1f2019-03-26 12:02:36 -050037 formatter_class=argparse.ArgumentDefaultsHelpFormatter,
Patrick Williams20f38712022-12-08 06:18:26 -060038 prefix_chars="-+",
39)
Michael Walsh888be1f2019-03-26 12:02:36 -050040
41parser.add_argument(
Patrick Williams20f38712022-12-08 06:18:26 -060042 "--status_dir_path",
43 default="",
Michael Walsh888be1f2019-03-26 12:02:36 -050044 help="The path to the directory where the status file will be created."
Patrick Williams20f38712022-12-08 06:18:26 -060045 + "%(default)s The default value is obtained from environment"
46 + ' variable "${STATUS_DIR_PATH}", if set or from "${HOME}/'
47 + 'status/".',
48)
Michael Walsh888be1f2019-03-26 12:02:36 -050049
50parser.add_argument(
Patrick Williams20f38712022-12-08 06:18:26 -060051 "--prefix",
52 default="",
Michael Walsh888be1f2019-03-26 12:02:36 -050053 help="The prefix for the generated file name.%(default)s The default value"
Patrick Williams20f38712022-12-08 06:18:26 -060054 + " is the command portion (i.e. the first token) of the command"
55 + " string.",
56)
Michael Walsh888be1f2019-03-26 12:02:36 -050057
58parser.add_argument(
Patrick Williams20f38712022-12-08 06:18:26 -060059 "--status_file_name",
60 default="",
Michael Walsh888be1f2019-03-26 12:02:36 -050061 help="This allows the user to explicitly specify the status file name. If"
Patrick Williams20f38712022-12-08 06:18:26 -060062 + " this argument is not used, %(prog)s composes a status file name."
63 + ' If this argument is specified, the "--prefix" argument is'
64 + " ignored.",
65)
Michael Walsh888be1f2019-03-26 12:02:36 -050066
67parser.add_argument(
Patrick Williams20f38712022-12-08 06:18:26 -060068 "--stdout",
Michael Walsh888be1f2019-03-26 12:02:36 -050069 default=1,
70 type=int,
71 choices=[1, 0],
72 help="Indicates that stdout/stderr from the command string execution"
Patrick Williams20f38712022-12-08 06:18:26 -060073 + " should be written to stdout as well as to the status file.",
74)
Michael Walsh888be1f2019-03-26 12:02:36 -050075
76parser.add_argument(
Patrick Williams20f38712022-12-08 06:18:26 -060077 "--tee",
Michael Walshcc7f8e52019-12-05 16:58:45 -060078 default=1,
Michael Walsh888be1f2019-03-26 12:02:36 -050079 type=int,
80 choices=[1, 0],
Patrick Williams20f38712022-12-08 06:18:26 -060081 help='Indicates that "tee" rather than "script" should be used.',
82)
Michael Walsh888be1f2019-03-26 12:02:36 -050083
84parser.add_argument(
Patrick Williams20f38712022-12-08 06:18:26 -060085 "--show_url",
Michael Walsh888be1f2019-03-26 12:02:36 -050086 default=0,
87 type=int,
88 choices=[1, 0],
89 help="Indicates that the status file path shown should be shown in the"
Patrick Williams20f38712022-12-08 06:18:26 -060090 + " form of a url. If the output is to be viewed from a browser,"
91 + " this may well become a clickable link. Note that the"
92 + ' get_file_path_url.py program must be found in the "PATH"'
93 + " environment variable for this argument to be effective.",
94)
Michael Walsh888be1f2019-03-26 12:02:36 -050095
96parser.add_argument(
Patrick Williams20f38712022-12-08 06:18:26 -060097 "command_string",
98 default="",
99 nargs="*",
100 help="The command string to be run.%(default)s",
101)
Michael Walsh888be1f2019-03-26 12:02:36 -0500102
103# Populate stock_list with options we want.
104stock_list = [("test_mode", 0), ("quiet", 1), ("debug", 0)]
105
106
Michael Walsh888be1f2019-03-26 12:02:36 -0500107def validate_parms():
108 r"""
109 Validate program parameters, etc.
110 """
111
112 global status_dir_path
113 global command_string
114
115 # Convert command_string from list to string.
116 command_string = " ".join(command_string)
117 set_pgm_arg(command_string)
118 valid_value(command_string)
119
120 if status_dir_path == "":
Patrick Williams20f38712022-12-08 06:18:26 -0600121 status_dir_path = os.environ.get(
122 "STATUS_DIR_PATH", os.environ.get("HOME") + "/status/"
123 )
Michael Walsh888be1f2019-03-26 12:02:36 -0500124 status_dir_path = add_trailing_slash(status_dir_path)
125 set_pgm_arg(status_dir_path)
126 valid_dir_path(status_dir_path)
127
128 global prefix
129 global status_file_name
130 if status_file_name == "":
131 if prefix == "":
132 prefix = command_string.split(" ")[0]
Michael Walsh410b1782019-10-22 15:56:18 -0500133 # File extensions (e.g. ".sh", ".py", .etc), look clumsy in status file names.
Michael Walsh64fbc9c2019-04-05 16:20:26 -0500134 extension_regex = "\\.[a-zA-Z0-9]{1,3}$"
135 prefix = re.sub(extension_regex, "", prefix)
Michael Walsh888be1f2019-03-26 12:02:36 -0500136 set_pgm_arg(prefix)
137 status_file_name = prefix + "." + file_date_time_stamp() + ".status"
138 set_pgm_arg(status_file_name)
139
140 global status_file_path
141
142 status_file_path = status_dir_path + status_file_name
143 # Set environment variable for the benefit of child programs.
Patrick Williams20f38712022-12-08 06:18:26 -0600144 os.environ["AUTO_STATUS_FILE_PATH"] = status_file_path
Michael Walsh888be1f2019-03-26 12:02:36 -0500145 # Set deprecated but still used AUTOSCRIPT_STATUS_FILE_PATH value.
Patrick Williams20f38712022-12-08 06:18:26 -0600146 os.environ["AUTOSCRIPT_STATUS_FILE_PATH"] = status_file_path
Michael Walsh888be1f2019-03-26 12:02:36 -0500147
Michael Walsh888be1f2019-03-26 12:02:36 -0500148
149def script_func(command_string, status_file_path):
150 r"""
Michael Walsh410b1782019-10-22 15:56:18 -0500151 Run the command string producing both stdout and file output via the script command and return the
152 shell_rc.
Michael Walsh888be1f2019-03-26 12:02:36 -0500153
154 Description of argument(s):
155 command_string The command string to be run.
Michael Walsh410b1782019-10-22 15:56:18 -0500156 status_file_path The path to the status file which is to contain a copy of all stdout.
Michael Walsh888be1f2019-03-26 12:02:36 -0500157 """
158
Patrick Williams20f38712022-12-08 06:18:26 -0600159 cmd_buf = (
160 "script -a -q -f "
161 + status_file_path
162 + " -c '"
163 + escape_bash_quotes(command_string)
164 + ' ; printf "\\n'
165 + sprint_varx(ret_code_str, "${?}").rstrip("\n")
166 + "\\n\"'"
167 )
Michael Walsh888be1f2019-03-26 12:02:36 -0500168 qprint_issuing(cmd_buf)
169 sub_proc = subprocess.Popen(cmd_buf, shell=True)
170 sub_proc.communicate()
171 shell_rc = sub_proc.returncode
172
Michael Walsh410b1782019-10-22 15:56:18 -0500173 # Retrieve return code by examining ret_code_str output statement from status file.
Michael Walsh888be1f2019-03-26 12:02:36 -0500174 # Example text to be analyzed.
175 # auto_status_file_ret_code: 127
Patrick Williams20f38712022-12-08 06:18:26 -0600176 cmd_buf = (
177 "tail -n 10 "
178 + status_file_path
179 + ' | egrep -a "'
180 + ret_code_str
181 + ':[ ]+"'
182 )
Michael Walsh888be1f2019-03-26 12:02:36 -0500183 rc, output = shell_cmd(cmd_buf)
184 key, value = parse_key_value(output)
185 shell_rc = int(value)
186
187 return shell_rc
188
189
190def tee_func(command_string, status_file_path):
191 r"""
Michael Walsh410b1782019-10-22 15:56:18 -0500192 Run the command string producing both stdout and file output via the tee command and return the shell_rc.
Michael Walsh888be1f2019-03-26 12:02:36 -0500193
194 Description of argument(s):
195 command_string The command string to be run.
Michael Walsh410b1782019-10-22 15:56:18 -0500196 status_file_path The path to the status file which is to contain a copy of all stdout.
Michael Walsh888be1f2019-03-26 12:02:36 -0500197 """
198
Patrick Williams20f38712022-12-08 06:18:26 -0600199 cmd_buf = (
200 "set -o pipefail ; "
201 + command_string
202 + " 2>&1 | tee -a "
Michael Walsh888be1f2019-03-26 12:02:36 -0500203 + status_file_path
Patrick Williams20f38712022-12-08 06:18:26 -0600204 )
Michael Walsh888be1f2019-03-26 12:02:36 -0500205 qprint_issuing(cmd_buf)
206 sub_proc = subprocess.Popen(cmd_buf, shell=True)
207 sub_proc.communicate()
208 shell_rc = sub_proc.returncode
209
210 print
211 print_varx(ret_code_str, shell_rc)
212 with open(status_file_path, "a") as status_file:
213 # Append ret code string and status_file_path to end of status file.
214 status_file.write("\n" + sprint_varx(ret_code_str, shell_rc))
215
216 return shell_rc
217
218
219def main():
Michael Walsh60d08f32019-11-25 16:51:39 -0600220 gen_setup()
Michael Walsh888be1f2019-03-26 12:02:36 -0500221
Patrick Williams20f38712022-12-08 06:18:26 -0600222 set_term_options(
223 term_requests={"pgm_names": [command_string.split(" ")[0]]}
224 )
Michael Walshcc7f8e52019-12-05 16:58:45 -0600225
Michael Walsh888be1f2019-03-26 12:02:36 -0500226 global ret_code_str
227 ret_code_str = re.sub("\\.py$", "", pgm_name) + "_ret_code"
228
229 global show_url
230 if show_url:
231 shell_rc, output = shell_cmd("which get_file_path_url.py", show_err=0)
232 if shell_rc != 0:
233 show_url = 0
234 set_pgm_arg(show_url)
235 else:
Patrick Williams20f38712022-12-08 06:18:26 -0600236 shell_rc, status_file_url = shell_cmd(
237 "get_file_path_url.py " + status_file_path
238 )
Michael Walsh888be1f2019-03-26 12:02:36 -0500239 status_file_url = status_file_url.rstrip("\n")
240
241 # Print status file path/url to stdout and to status file.
242 with open(status_file_path, "w+") as status_file:
243 if show_url:
244 print_var(status_file_url)
245 status_file.write(sprint_var(status_file_url))
246 else:
247 print_var(status_file_path)
248 status_file.write(sprint_var(status_file_path))
249
250 if stdout:
251 if tee:
252 shell_rc = tee_func(command_string, status_file_path)
253 else:
254 shell_rc = script_func(command_string, status_file_path)
255 if show_url:
256 print_var(status_file_url)
257 else:
258 print_var(status_file_path)
259 else:
260 cmd_buf = command_string + " >> " + status_file_path + " 2>&1"
261 shell_rc, output = shell_cmd(cmd_buf, show_err=0)
262 with open(status_file_path, "a") as status_file:
263 # Append ret code string and status_file_path to end of status
264 # file.
265 status_file.write("\n" + sprint_varx(ret_code_str, shell_rc))
266
267 # Append status_file_path print statement to end of status file.
268 with open(status_file_path, "a") as status_file:
269 if show_url:
270 status_file.write(sprint_var(status_file_url))
271 else:
272 status_file.write(sprint_var(status_file_path))
273 exit(shell_rc)
274
275
276main()