blob: 6b72b77b3ddac839d5491d5a55203420a4f4863a [file] [log] [blame]
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -08001#!/usr/bin/python2.4
2#
3#
4# Copyright 2007, The Android Open Source Project
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17
18# System imports
19import os
20import signal
21import subprocess
22import time
23import threading
24
25# local imports
26import logger
27import errors
28
29_abort_on_error = False
30
31def SetAbortOnError(abort=True):
32 """Sets behavior of RunCommand to throw AbortError if command process returns
33 a negative error code"""
34 global _abort_on_error
35 _abort_on_error = abort
36
37def RunCommand(cmd, timeout_time=None, retry_count=3, return_output=True):
38 """Spawns a subprocess to run the given shell command, and checks for
39 timeout_time. If return_output is True, the output of the command is returned
40 as a string. Otherwise, output of command directed to stdout """
41
42 result = None
43 while True:
44 try:
45 result = RunOnce(cmd, timeout_time=timeout_time,
46 return_output=return_output)
47 except errors.WaitForResponseTimedOutError:
48 if retry_count == 0:
49 raise
50 retry_count -= 1
51 logger.Log("No response for %s, retrying" % cmd)
52 else:
53 # Success
54 return result
55
56def RunOnce(cmd, timeout_time=None, return_output=True):
57 start_time = time.time()
58 so = []
59 pid = []
60 global _abort_on_error
61 error_occurred = False
62
63 def Run():
64 if return_output:
65 output_dest = subprocess.PIPE
66 else:
67 # None means direct to stdout
68 output_dest = None
69 pipe = subprocess.Popen(
70 cmd,
71 executable='/bin/bash',
72 stdout=output_dest,
73 stderr=subprocess.STDOUT,
74 shell=True)
75 pid.append(pipe.pid)
76 try:
77 output = pipe.communicate()[0]
78 if output is not None and len(output) > 0:
79 so.append(output)
80 except OSError, e:
81 logger.SilentLog("failed to retrieve stdout from: %s" % cmd)
82 logger.Log(e)
83 so.append("ERROR")
84 error_occurred = True
85 if pipe.returncode < 0:
86 logger.SilentLog("Error: %s was terminated by signal %d" %(cmd,
87 pipe.returncode))
88 error_occurred = True
89
90 t = threading.Thread(target=Run)
91 t.start()
92
93 break_loop = False
94 while not break_loop:
95 if not t.isAlive():
96 break_loop = True
97
98 # Check the timeout
99 if (not break_loop and timeout_time is not None
100 and time.time() > start_time + timeout_time):
101 try:
102 os.kill(pid[0], signal.SIGKILL)
103 except OSError:
104 # process already dead. No action required.
105 pass
106
107 logger.SilentLog("about to raise a timeout for: %s" % cmd)
108 raise errors.WaitForResponseTimedOutError
109 if not break_loop:
110 time.sleep(0.1)
111
112 t.join()
113
114 if _abort_on_error and error_occurred:
115 raise errors.AbortError
116
117 return "".join(so)