1#!/usr/bin/python3 2 3from __future__ import absolute_import 4from __future__ import division 5from __future__ import print_function 6import common 7import sys, os, signal, time, subprocess, fcntl 8 9 10def _print_to_file_and_flush(msg, file): 11 """Print to the provided file, and flush after printing.""" 12 print(msg, file=file) 13 file.flush() 14 15logdir = sys.argv[1] 16stdout_start = int(sys.argv[2]) # number of bytes we can skip on stdout 17stderr_start = int(sys.argv[3]) # nubmer of bytes we can skip on stderr 18# TODO (crosbug.com/38224)- sbasi: Remove extra logging. 19stderr = open(os.path.join(logdir, 'stderr'), 'a', buffering=2) 20 21_print_to_file_and_flush('Entered autotestd_monitor.', file=stderr) 22 23# if any of our tail processes die, the monitor should die too 24def kill_self(signum, frame): 25 os.kill(os.getpid(), signal.SIGTERM) 26signal.signal(signal.SIGCHLD, kill_self) 27 28devnull = open(os.devnull, 'w') 29 30# launch some tail processes to pump the std* streams 31def launch_tail(filename, outstream, start): 32 path = os.path.join(logdir, filename) 33 argv = ['tail', '--retry', '--follow=name', '--bytes=+%d' % start, path] 34 # stdout=sys.stdout fails on pre-2.5 python (bug in subprocess module) 35 if outstream != subprocess.PIPE and outstream.fileno() == 1: 36 return subprocess.Popen(argv, stderr=devnull) 37 else: 38 return subprocess.Popen(argv, stdout=outstream, stderr=devnull) 39stdout_pump = launch_tail('stdout', sys.stdout, stdout_start) 40stderr_pump = launch_tail('stderr', sys.stderr, stderr_start) 41 42_print_to_file_and_flush('Finished launching tail subprocesses.', file=stderr) 43# wait for logdir/started to exist to be sure autotestd is started 44start_time = time.time() 45started_file_path = os.path.join(logdir, 'started') 46while not os.path.exists(started_file_path): 47 time.sleep(1) 48 if time.time() - start_time >= 30: 49 raise Exception("autotestd failed to start in %s" % logdir) 50 51_print_to_file_and_flush('Finished waiting on autotestd to start.', 52 file=stderr) 53 54# watch the exit code file for an exit 55exit_code_file = open(os.path.join(logdir, 'exit_code')) 56fcntl.flock(exit_code_file, fcntl.LOCK_EX) 57_print_to_file_and_flush('Got lock of exit_code_file.', file=stderr) 58 59try: 60 exit_code = exit_code_file.read() 61 if len(exit_code) != 4: 62 exit_code = -signal.SIGKILL # autotestd was nuked 63 else: 64 exit_code = int(exit_code) 65finally: 66 fcntl.flock(exit_code_file, fcntl.LOCK_UN) 67 exit_code_file.close() 68 _print_to_file_and_flush('Released lock of exit_code_file and closed it.', 69 file=stderr) 70 71# Give tail a tiny bit of time to finish. 72time.sleep(0.01) 73 74_print_to_file_and_flush('Killing child processes.', file=stderr) 75 76# clear the SIGCHLD handler so that killing the tails doesn't kill us 77signal.signal(signal.SIGCHLD, signal.SIG_DFL) 78os.kill(stdout_pump.pid, signal.SIGTERM) 79os.kill(stderr_pump.pid, signal.SIGTERM) 80 81stderr.close() 82# exit (with the same code as autotestd) 83sys.exit(exit_code) 84