blob: b0e1835c5f9f9a4958d5746e27bf1040852d5549 [file] [log] [blame]
mbligh63073c92008-03-31 16:49:32 +00001#
2# Copyright 2008 Google Inc. Released under the GPL v2
3
mbligh849a0f62008-08-28 20:12:19 +00004import os, pickle, random, re, resource, select, shutil, signal, StringIO
mblighb2896192009-07-11 00:12:37 +00005import socket, struct, subprocess, sys, time, textwrap, urlparse
mbligh25284cd2009-06-08 16:17:24 +00006import warnings, smtplib, logging, urllib2
showard108d73e2009-06-22 18:14:41 +00007from autotest_lib.client.common_lib import error, barrier, logging_manager
mbligh81edd792008-08-26 16:54:02 +00008
mbligh849a0f62008-08-28 20:12:19 +00009def deprecated(func):
10 """This is a decorator which can be used to mark functions as deprecated.
11 It will result in a warning being emmitted when the function is used."""
12 def new_func(*args, **dargs):
13 warnings.warn("Call to deprecated function %s." % func.__name__,
14 category=DeprecationWarning)
15 return func(*args, **dargs)
16 new_func.__name__ = func.__name__
17 new_func.__doc__ = func.__doc__
18 new_func.__dict__.update(func.__dict__)
19 return new_func
20
21
showard108d73e2009-06-22 18:14:41 +000022class _NullStream(object):
23 def write(self, data):
24 pass
25
26
27 def flush(self):
28 pass
29
30
31TEE_TO_LOGS = object()
32_the_null_stream = _NullStream()
33
34def get_stream_tee_file(stream, level):
35 if stream is None:
36 return _the_null_stream
37 if stream is TEE_TO_LOGS:
38 return logging_manager.LoggingFile(level=level)
39 return stream
40
41
mbligh849a0f62008-08-28 20:12:19 +000042class BgJob(object):
showard170873e2009-01-07 00:22:26 +000043 def __init__(self, command, stdout_tee=None, stderr_tee=None, verbose=True,
44 stdin=None):
mbligh849a0f62008-08-28 20:12:19 +000045 self.command = command
showard108d73e2009-06-22 18:14:41 +000046 self.stdout_tee = get_stream_tee_file(stdout_tee, logging.DEBUG)
47 self.stderr_tee = get_stream_tee_file(stderr_tee, logging.ERROR)
mbligh849a0f62008-08-28 20:12:19 +000048 self.result = CmdResult(command)
mblighbd96b452008-09-03 23:14:27 +000049 if verbose:
showardb18134f2009-03-20 20:52:18 +000050 logging.debug("Running '%s'" % command)
mbligh849a0f62008-08-28 20:12:19 +000051 self.sp = subprocess.Popen(command, stdout=subprocess.PIPE,
52 stderr=subprocess.PIPE,
53 preexec_fn=self._reset_sigpipe, shell=True,
showard170873e2009-01-07 00:22:26 +000054 executable="/bin/bash",
55 stdin=stdin)
mbligh849a0f62008-08-28 20:12:19 +000056
57
58 def output_prepare(self, stdout_file=None, stderr_file=None):
59 self.stdout_file = stdout_file
60 self.stderr_file = stderr_file
61
mbligh45ffc432008-12-09 23:35:17 +000062
mbligh849a0f62008-08-28 20:12:19 +000063 def process_output(self, stdout=True, final_read=False):
64 """output_prepare must be called prior to calling this"""
65 if stdout:
66 pipe, buf, tee = self.sp.stdout, self.stdout_file, self.stdout_tee
67 else:
68 pipe, buf, tee = self.sp.stderr, self.stderr_file, self.stderr_tee
69
70 if final_read:
71 # read in all the data we can from pipe and then stop
72 data = []
73 while select.select([pipe], [], [], 0)[0]:
74 data.append(os.read(pipe.fileno(), 1024))
75 if len(data[-1]) == 0:
76 break
77 data = "".join(data)
78 else:
79 # perform a single read
80 data = os.read(pipe.fileno(), 1024)
81 buf.write(data)
showard108d73e2009-06-22 18:14:41 +000082 tee.write(data)
mbligh849a0f62008-08-28 20:12:19 +000083
84
85 def cleanup(self):
showard108d73e2009-06-22 18:14:41 +000086 self.stdout_tee.flush()
87 self.stderr_tee.flush()
mbligh849a0f62008-08-28 20:12:19 +000088 self.sp.stdout.close()
89 self.sp.stderr.close()
90 self.result.stdout = self.stdout_file.getvalue()
91 self.result.stderr = self.stderr_file.getvalue()
92
93
94 def _reset_sigpipe(self):
95 signal.signal(signal.SIGPIPE, signal.SIG_DFL)
96
mbligh81edd792008-08-26 16:54:02 +000097
98def ip_to_long(ip):
99 # !L is a long in network byte order
100 return struct.unpack('!L', socket.inet_aton(ip))[0]
101
102
103def long_to_ip(number):
104 # See above comment.
105 return socket.inet_ntoa(struct.pack('!L', number))
106
107
108def create_subnet_mask(bits):
mbligh81edd792008-08-26 16:54:02 +0000109 return (1 << 32) - (1 << 32-bits)
110
111
112def format_ip_with_mask(ip, mask_bits):
113 masked_ip = ip_to_long(ip) & create_subnet_mask(mask_bits)
114 return "%s/%s" % (long_to_ip(masked_ip), mask_bits)
mbligh6231cd62008-02-02 19:18:33 +0000115
mblighde0d47e2008-03-28 14:37:18 +0000116
jadmanskie80d4712008-10-03 16:15:59 +0000117def normalize_hostname(alias):
118 ip = socket.gethostbyname(alias)
119 return socket.gethostbyaddr(ip)[0]
120
121
mblighd6d043c2008-09-27 21:00:45 +0000122def get_ip_local_port_range():
123 match = re.match(r'\s*(\d+)\s*(\d+)\s*$',
124 read_one_line('/proc/sys/net/ipv4/ip_local_port_range'))
125 return (int(match.group(1)), int(match.group(2)))
126
127
128def set_ip_local_port_range(lower, upper):
129 write_one_line('/proc/sys/net/ipv4/ip_local_port_range',
130 '%d %d\n' % (lower, upper))
131
mbligh315b9412008-10-01 03:34:11 +0000132
mbligh45ffc432008-12-09 23:35:17 +0000133
134def send_email(mail_from, mail_to, subject, body):
135 """
136 Sends an email via smtp
137
138 mail_from: string with email address of sender
139 mail_to: string or list with email address(es) of recipients
140 subject: string with subject of email
141 body: (multi-line) string with body of email
142 """
143 if isinstance(mail_to, str):
144 mail_to = [mail_to]
145 msg = "From: %s\nTo: %s\nSubject: %s\n\n%s" % (mail_from, ','.join(mail_to),
146 subject, body)
147 try:
148 mailer = smtplib.SMTP('localhost')
149 try:
150 mailer.sendmail(mail_from, mail_to, msg)
151 finally:
152 mailer.quit()
153 except Exception, e:
154 # Emails are non-critical, not errors, but don't raise them
155 print "Sending email failed. Reason: %s" % repr(e)
156
157
jadmanski5182e162008-05-13 21:48:16 +0000158def read_one_line(filename):
mbligh6e8840c2008-07-11 18:05:49 +0000159 return open(filename, 'r').readline().rstrip('\n')
jadmanski5182e162008-05-13 21:48:16 +0000160
161
mblighb9d05512008-10-18 13:53:27 +0000162def write_one_line(filename, line):
163 open_write_close(filename, line.rstrip('\n') + '\n')
164
165
166def open_write_close(filename, data):
mbligh618ac9e2008-10-06 17:14:32 +0000167 f = open(filename, 'w')
mblighb9d05512008-10-18 13:53:27 +0000168 try:
169 f.write(data)
170 finally:
171 f.close()
jadmanski5182e162008-05-13 21:48:16 +0000172
173
mblighde0d47e2008-03-28 14:37:18 +0000174def read_keyval(path):
jadmanski0afbb632008-06-06 21:10:57 +0000175 """
176 Read a key-value pair format file into a dictionary, and return it.
177 Takes either a filename or directory name as input. If it's a
178 directory name, we assume you want the file to be called keyval.
179 """
180 if os.path.isdir(path):
181 path = os.path.join(path, 'keyval')
182 keyval = {}
jadmanski58962982009-04-21 19:54:34 +0000183 if os.path.exists(path):
184 for line in open(path):
185 line = re.sub('#.*', '', line).rstrip()
186 if not re.search(r'^[-\.\w]+=', line):
187 raise ValueError('Invalid format line: %s' % line)
188 key, value = line.split('=', 1)
189 if re.search('^\d+$', value):
190 value = int(value)
191 elif re.search('^(\d+\.)?\d+$', value):
192 value = float(value)
193 keyval[key] = value
jadmanski0afbb632008-06-06 21:10:57 +0000194 return keyval
mblighde0d47e2008-03-28 14:37:18 +0000195
196
jadmanskicc549172008-05-21 18:11:51 +0000197def write_keyval(path, dictionary, type_tag=None):
jadmanski0afbb632008-06-06 21:10:57 +0000198 """
199 Write a key-value pair format file out to a file. This uses append
200 mode to open the file, so existing text will not be overwritten or
201 reparsed.
jadmanskicc549172008-05-21 18:11:51 +0000202
jadmanski0afbb632008-06-06 21:10:57 +0000203 If type_tag is None, then the key must be composed of alphanumeric
204 characters (or dashes+underscores). However, if type-tag is not
205 null then the keys must also have "{type_tag}" as a suffix. At
206 the moment the only valid values of type_tag are "attr" and "perf".
207 """
208 if os.path.isdir(path):
209 path = os.path.join(path, 'keyval')
210 keyval = open(path, 'a')
jadmanskicc549172008-05-21 18:11:51 +0000211
jadmanski0afbb632008-06-06 21:10:57 +0000212 if type_tag is None:
mbligh97227ea2009-03-11 17:09:50 +0000213 key_regex = re.compile(r'^[-\.\w]+$')
jadmanski0afbb632008-06-06 21:10:57 +0000214 else:
215 if type_tag not in ('attr', 'perf'):
216 raise ValueError('Invalid type tag: %s' % type_tag)
217 escaped_tag = re.escape(type_tag)
mbligh97227ea2009-03-11 17:09:50 +0000218 key_regex = re.compile(r'^[-\.\w]+\{%s\}$' % escaped_tag)
jadmanski0afbb632008-06-06 21:10:57 +0000219 try:
mbligh6955e232009-07-11 00:58:47 +0000220 for key in sorted(dictionary.keys()):
jadmanski0afbb632008-06-06 21:10:57 +0000221 if not key_regex.search(key):
222 raise ValueError('Invalid key: %s' % key)
mbligh6955e232009-07-11 00:58:47 +0000223 keyval.write('%s=%s\n' % (key, dictionary[key]))
jadmanski0afbb632008-06-06 21:10:57 +0000224 finally:
225 keyval.close()
mbligh6231cd62008-02-02 19:18:33 +0000226
227
228def is_url(path):
jadmanski0afbb632008-06-06 21:10:57 +0000229 """Return true if path looks like a URL"""
230 # for now, just handle http and ftp
231 url_parts = urlparse.urlparse(path)
232 return (url_parts[0] in ('http', 'ftp'))
mbligh6231cd62008-02-02 19:18:33 +0000233
234
mblighb2896192009-07-11 00:12:37 +0000235def urlopen(url, data=None, timeout=5):
236 """Wrapper to urllib2.urlopen with timeout addition."""
mbligh02ff2d52008-06-03 15:00:21 +0000237
jadmanski0afbb632008-06-06 21:10:57 +0000238 # Save old timeout
239 old_timeout = socket.getdefaulttimeout()
240 socket.setdefaulttimeout(timeout)
241 try:
mblighb2896192009-07-11 00:12:37 +0000242 return urllib2.urlopen(url, data=data)
jadmanski0afbb632008-06-06 21:10:57 +0000243 finally:
244 socket.setdefaulttimeout(old_timeout)
mbligh02ff2d52008-06-03 15:00:21 +0000245
246
mblighb2896192009-07-11 00:12:37 +0000247def urlretrieve(url, filename, data=None, timeout=300):
248 """Retrieve a file from given url."""
249 logging.debug('Fetching %s -> %s', url, filename)
250
251 src_file = urlopen(url, data=data, timeout=timeout)
jadmanski0afbb632008-06-06 21:10:57 +0000252 try:
mblighb2896192009-07-11 00:12:37 +0000253 dest_file = open(filename, 'wb')
254 try:
255 shutil.copyfileobj(src_file, dest_file)
256 finally:
257 dest_file.close()
jadmanski0afbb632008-06-06 21:10:57 +0000258 finally:
mblighb2896192009-07-11 00:12:37 +0000259 src_file.close()
jadmanski0afbb632008-06-06 21:10:57 +0000260
mbligh02ff2d52008-06-03 15:00:21 +0000261
mbligh6231cd62008-02-02 19:18:33 +0000262def get_file(src, dest, permissions=None):
jadmanski0afbb632008-06-06 21:10:57 +0000263 """Get a file from src, which can be local or a remote URL"""
mbligh25284cd2009-06-08 16:17:24 +0000264 if src == dest:
jadmanski0afbb632008-06-06 21:10:57 +0000265 return
mbligh25284cd2009-06-08 16:17:24 +0000266
267 if is_url(src):
mblighb2896192009-07-11 00:12:37 +0000268 urlretrieve(src, dest)
jadmanski0afbb632008-06-06 21:10:57 +0000269 else:
270 shutil.copyfile(src, dest)
mbligh25284cd2009-06-08 16:17:24 +0000271
jadmanski0afbb632008-06-06 21:10:57 +0000272 if permissions:
273 os.chmod(dest, permissions)
274 return dest
mbligh6231cd62008-02-02 19:18:33 +0000275
276
277def unmap_url(srcdir, src, destdir='.'):
jadmanski0afbb632008-06-06 21:10:57 +0000278 """
279 Receives either a path to a local file or a URL.
280 returns either the path to the local file, or the fetched URL
mbligh6231cd62008-02-02 19:18:33 +0000281
jadmanski0afbb632008-06-06 21:10:57 +0000282 unmap_url('/usr/src', 'foo.tar', '/tmp')
283 = '/usr/src/foo.tar'
284 unmap_url('/usr/src', 'http://site/file', '/tmp')
285 = '/tmp/file'
286 (after retrieving it)
287 """
288 if is_url(src):
289 url_parts = urlparse.urlparse(src)
290 filename = os.path.basename(url_parts[2])
291 dest = os.path.join(destdir, filename)
292 return get_file(src, dest)
293 else:
294 return os.path.join(srcdir, src)
mbligh6231cd62008-02-02 19:18:33 +0000295
296
297def update_version(srcdir, preserve_srcdir, new_version, install,
jadmanski0afbb632008-06-06 21:10:57 +0000298 *args, **dargs):
299 """
300 Make sure srcdir is version new_version
mbligh6231cd62008-02-02 19:18:33 +0000301
jadmanski0afbb632008-06-06 21:10:57 +0000302 If not, delete it and install() the new version.
mbligh6231cd62008-02-02 19:18:33 +0000303
jadmanski0afbb632008-06-06 21:10:57 +0000304 In the preserve_srcdir case, we just check it's up to date,
305 and if not, we rerun install, without removing srcdir
306 """
307 versionfile = os.path.join(srcdir, '.version')
308 install_needed = True
mbligh6231cd62008-02-02 19:18:33 +0000309
jadmanski0afbb632008-06-06 21:10:57 +0000310 if os.path.exists(versionfile):
311 old_version = pickle.load(open(versionfile))
312 if old_version == new_version:
313 install_needed = False
mbligh6231cd62008-02-02 19:18:33 +0000314
jadmanski0afbb632008-06-06 21:10:57 +0000315 if install_needed:
316 if not preserve_srcdir and os.path.exists(srcdir):
317 shutil.rmtree(srcdir)
318 install(*args, **dargs)
319 if os.path.exists(srcdir):
320 pickle.dump(new_version, open(versionfile, 'w'))
mbligh462c0152008-03-13 15:37:10 +0000321
322
mbligh63073c92008-03-31 16:49:32 +0000323def run(command, timeout=None, ignore_status=False,
showard170873e2009-01-07 00:22:26 +0000324 stdout_tee=None, stderr_tee=None, verbose=True, stdin=None):
jadmanski0afbb632008-06-06 21:10:57 +0000325 """
326 Run a command on the host.
mbligh63073c92008-03-31 16:49:32 +0000327
jadmanski0afbb632008-06-06 21:10:57 +0000328 Args:
329 command: the command line string
330 timeout: time limit in seconds before attempting to
331 kill the running process. The run() function
332 will take a few seconds longer than 'timeout'
333 to complete if it has to kill the process.
334 ignore_status: do not raise an exception, no matter what
335 the exit code of the command is.
336 stdout_tee: optional file-like object to which stdout data
337 will be written as it is generated (data will still
338 be stored in result.stdout)
339 stderr_tee: likewise for stderr
showard108d73e2009-06-22 18:14:41 +0000340 verbose: if True, log the command being run
showard170873e2009-01-07 00:22:26 +0000341 stdin: stdin to pass to the executed process
mbligh63073c92008-03-31 16:49:32 +0000342
jadmanski0afbb632008-06-06 21:10:57 +0000343 Returns:
344 a CmdResult object
mbligh63073c92008-03-31 16:49:32 +0000345
jadmanski0afbb632008-06-06 21:10:57 +0000346 Raises:
347 CmdError: the exit code of the command
348 execution was not 0
349 """
showard170873e2009-01-07 00:22:26 +0000350 bg_job = join_bg_jobs(
351 (BgJob(command, stdout_tee, stderr_tee, verbose, stdin=stdin),),
352 timeout)[0]
mbligh849a0f62008-08-28 20:12:19 +0000353 if not ignore_status and bg_job.result.exit_status:
jadmanski9c1098b2008-09-02 14:18:48 +0000354 raise error.CmdError(command, bg_job.result,
mbligh849a0f62008-08-28 20:12:19 +0000355 "Command returned non-zero exit status")
mbligh63073c92008-03-31 16:49:32 +0000356
mbligh849a0f62008-08-28 20:12:19 +0000357 return bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000358
mbligh45ffc432008-12-09 23:35:17 +0000359
mbligha5630a52008-09-03 22:09:50 +0000360def run_parallel(commands, timeout=None, ignore_status=False,
361 stdout_tee=None, stderr_tee=None):
362 """Beahves the same as run with the following exceptions:
363
364 - commands is a list of commands to run in parallel.
365 - ignore_status toggles whether or not an exception should be raised
366 on any error.
367
368 returns a list of CmdResult objects
369 """
370 bg_jobs = []
371 for command in commands:
372 bg_jobs.append(BgJob(command, stdout_tee, stderr_tee))
373
374 # Updates objects in bg_jobs list with their process information
375 join_bg_jobs(bg_jobs, timeout)
376
377 for bg_job in bg_jobs:
378 if not ignore_status and bg_job.result.exit_status:
379 raise error.CmdError(command, bg_job.result,
380 "Command returned non-zero exit status")
381
382 return [bg_job.result for bg_job in bg_jobs]
383
384
mbligh849a0f62008-08-28 20:12:19 +0000385@deprecated
mbligh63073c92008-03-31 16:49:32 +0000386def run_bg(command):
mbligh849a0f62008-08-28 20:12:19 +0000387 """Function deprecated. Please use BgJob class instead."""
388 bg_job = BgJob(command)
389 return bg_job.sp, bg_job.result
mbligh63073c92008-03-31 16:49:32 +0000390
391
mbligh849a0f62008-08-28 20:12:19 +0000392def join_bg_jobs(bg_jobs, timeout=None):
mbligha5630a52008-09-03 22:09:50 +0000393 """Joins the bg_jobs with the current thread.
394
395 Returns the same list of bg_jobs objects that was passed in.
396 """
mblighae69f262009-04-17 20:14:56 +0000397 ret, timeout_error = 0, False
mbligh849a0f62008-08-28 20:12:19 +0000398 for bg_job in bg_jobs:
399 bg_job.output_prepare(StringIO.StringIO(), StringIO.StringIO())
mbligh63073c92008-03-31 16:49:32 +0000400
jadmanski0afbb632008-06-06 21:10:57 +0000401 try:
402 # We are holding ends to stdin, stdout pipes
403 # hence we need to be sure to close those fds no mater what
404 start_time = time.time()
mbligh849a0f62008-08-28 20:12:19 +0000405 timeout_error = _wait_for_commands(bg_jobs, start_time, timeout)
406
407 for bg_job in bg_jobs:
408 # Process stdout and stderr
409 bg_job.process_output(stdout=True,final_read=True)
410 bg_job.process_output(stdout=False,final_read=True)
jadmanski0afbb632008-06-06 21:10:57 +0000411 finally:
412 # close our ends of the pipes to the sp no matter what
mbligh849a0f62008-08-28 20:12:19 +0000413 for bg_job in bg_jobs:
414 bg_job.cleanup()
mbligh63073c92008-03-31 16:49:32 +0000415
mbligh849a0f62008-08-28 20:12:19 +0000416 if timeout_error:
417 # TODO: This needs to be fixed to better represent what happens when
418 # running in parallel. However this is backwards compatable, so it will
419 # do for the time being.
420 raise error.CmdError(bg_jobs[0].command, bg_jobs[0].result,
421 "Command(s) did not complete within %d seconds"
422 % timeout)
mbligh63073c92008-03-31 16:49:32 +0000423
mbligh63073c92008-03-31 16:49:32 +0000424
mbligh849a0f62008-08-28 20:12:19 +0000425 return bg_jobs
mbligh63073c92008-03-31 16:49:32 +0000426
mbligh849a0f62008-08-28 20:12:19 +0000427
428def _wait_for_commands(bg_jobs, start_time, timeout):
429 # This returns True if it must return due to a timeout, otherwise False.
430
mblighf0b4a0a2008-09-03 20:46:16 +0000431 # To check for processes which terminate without producing any output
432 # a 1 second timeout is used in select.
433 SELECT_TIMEOUT = 1
434
mbligh849a0f62008-08-28 20:12:19 +0000435 select_list = []
436 reverse_dict = {}
437 for bg_job in bg_jobs:
438 select_list.append(bg_job.sp.stdout)
439 select_list.append(bg_job.sp.stderr)
440 reverse_dict[bg_job.sp.stdout] = (bg_job,True)
441 reverse_dict[bg_job.sp.stderr] = (bg_job,False)
442
jadmanski0afbb632008-06-06 21:10:57 +0000443 if timeout:
444 stop_time = start_time + timeout
445 time_left = stop_time - time.time()
446 else:
447 time_left = None # so that select never times out
448 while not timeout or time_left > 0:
449 # select will return when stdout is ready (including when it is
450 # EOF, that is the process has terminated).
mblighf0b4a0a2008-09-03 20:46:16 +0000451 ready, _, _ = select.select(select_list, [], [], SELECT_TIMEOUT)
mbligh849a0f62008-08-28 20:12:19 +0000452
jadmanski0afbb632008-06-06 21:10:57 +0000453 # os.read() has to be used instead of
454 # subproc.stdout.read() which will otherwise block
mbligh849a0f62008-08-28 20:12:19 +0000455 for fileno in ready:
456 bg_job,stdout = reverse_dict[fileno]
457 bg_job.process_output(stdout)
mbligh63073c92008-03-31 16:49:32 +0000458
mbligh849a0f62008-08-28 20:12:19 +0000459 remaining_jobs = [x for x in bg_jobs if x.result.exit_status is None]
460 if len(remaining_jobs) == 0:
461 return False
462 for bg_job in remaining_jobs:
463 bg_job.result.exit_status = bg_job.sp.poll()
mbligh8ea61e22008-05-09 18:09:37 +0000464
jadmanski0afbb632008-06-06 21:10:57 +0000465 if timeout:
466 time_left = stop_time - time.time()
mbligh63073c92008-03-31 16:49:32 +0000467
mbligh849a0f62008-08-28 20:12:19 +0000468 # Kill all processes which did not complete prior to timeout
469 for bg_job in [x for x in bg_jobs if x.result.exit_status is None]:
mbligh7afc3a62008-11-27 00:35:44 +0000470 print '* Warning: run process timeout (%s) fired' % timeout
mbligh849a0f62008-08-28 20:12:19 +0000471 nuke_subprocess(bg_job.sp)
mbligh095dc642008-10-01 03:41:35 +0000472 bg_job.result.exit_status = bg_job.sp.poll()
mbligh8ea61e22008-05-09 18:09:37 +0000473
mbligh849a0f62008-08-28 20:12:19 +0000474 return True
mbligh63073c92008-03-31 16:49:32 +0000475
476
mbligh63073c92008-03-31 16:49:32 +0000477def nuke_subprocess(subproc):
jadmanski09f92032008-09-17 14:05:27 +0000478 # check if the subprocess is still alive, first
479 if subproc.poll() is not None:
480 return subproc.poll()
481
jadmanski0afbb632008-06-06 21:10:57 +0000482 # the process has not terminated within timeout,
483 # kill it via an escalating series of signals.
484 signal_queue = [signal.SIGTERM, signal.SIGKILL]
485 for sig in signal_queue:
486 try:
487 os.kill(subproc.pid, sig)
488 # The process may have died before we could kill it.
489 except OSError:
490 pass
mbligh63073c92008-03-31 16:49:32 +0000491
jadmanski0afbb632008-06-06 21:10:57 +0000492 for i in range(5):
493 rc = subproc.poll()
mblighd876f452008-12-03 15:09:17 +0000494 if rc is not None:
jadmanski0afbb632008-06-06 21:10:57 +0000495 return rc
496 time.sleep(1)
mbligh63073c92008-03-31 16:49:32 +0000497
498
499def nuke_pid(pid):
jadmanski0afbb632008-06-06 21:10:57 +0000500 # the process has not terminated within timeout,
501 # kill it via an escalating series of signals.
502 signal_queue = [signal.SIGTERM, signal.SIGKILL]
503 for sig in signal_queue:
504 try:
505 os.kill(pid, sig)
mbligh63073c92008-03-31 16:49:32 +0000506
jadmanski0afbb632008-06-06 21:10:57 +0000507 # The process may have died before we could kill it.
508 except OSError:
509 pass
mbligh63073c92008-03-31 16:49:32 +0000510
jadmanski0afbb632008-06-06 21:10:57 +0000511 try:
512 for i in range(5):
513 status = os.waitpid(pid, os.WNOHANG)[0]
514 if status == pid:
515 return
516 time.sleep(1)
mbligh63073c92008-03-31 16:49:32 +0000517
jadmanski0afbb632008-06-06 21:10:57 +0000518 if status != pid:
519 raise error.AutoservRunError('Could not kill %d'
520 % pid, None)
mbligh63073c92008-03-31 16:49:32 +0000521
jadmanski0afbb632008-06-06 21:10:57 +0000522 # the process died before we join it.
523 except OSError:
524 pass
mbligh63073c92008-03-31 16:49:32 +0000525
526
mbligh298ed592009-06-15 21:52:57 +0000527def pid_is_alive(pid):
528 """
529 True if process pid exists and is not yet stuck in Zombie state.
530 Zombies are impossible to move between cgroups, etc.
531 pid can be integer, or text of integer.
532 """
533 try:
mbligh277a0e42009-07-11 00:11:45 +0000534 stat = read_one_line('/proc/%s/stat' % pid) # pid exists
mbligh298ed592009-06-15 21:52:57 +0000535 return stat.split()[2] != 'Z' # and is not in Zombie state
536 except Exception:
537 return False # process no longer exists at all
538
539
mbligh63073c92008-03-31 16:49:32 +0000540def system(command, timeout=None, ignore_status=False):
mbligha5630a52008-09-03 22:09:50 +0000541 """This function returns the exit status of command."""
mblighf8dffb12008-10-29 16:45:26 +0000542 return run(command, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000543 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS).exit_status
mbligh63073c92008-03-31 16:49:32 +0000544
545
mbligha5630a52008-09-03 22:09:50 +0000546def system_parallel(commands, timeout=None, ignore_status=False):
547 """This function returns a list of exit statuses for the respective
548 list of commands."""
549 return [bg_jobs.exit_status for bg_jobs in
mblighf8dffb12008-10-29 16:45:26 +0000550 run_parallel(commands, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000551 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligh849a0f62008-08-28 20:12:19 +0000552
553
mbligh8ea61e22008-05-09 18:09:37 +0000554def system_output(command, timeout=None, ignore_status=False,
jadmanski0afbb632008-06-06 21:10:57 +0000555 retain_output=False):
556 if retain_output:
mblighf8dffb12008-10-29 16:45:26 +0000557 out = run(command, timeout=timeout, ignore_status=ignore_status,
showard108d73e2009-06-22 18:14:41 +0000558 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS).stdout
jadmanski0afbb632008-06-06 21:10:57 +0000559 else:
mblighf8dffb12008-10-29 16:45:26 +0000560 out = run(command, timeout=timeout, ignore_status=ignore_status).stdout
jadmanski0afbb632008-06-06 21:10:57 +0000561 if out[-1:] == '\n': out = out[:-1]
562 return out
mbligh63073c92008-03-31 16:49:32 +0000563
mbligh849a0f62008-08-28 20:12:19 +0000564
mbligha5630a52008-09-03 22:09:50 +0000565def system_output_parallel(commands, timeout=None, ignore_status=False,
566 retain_output=False):
567 if retain_output:
showard108d73e2009-06-22 18:14:41 +0000568 out = [bg_job.stdout for bg_job
569 in run_parallel(commands, timeout=timeout,
570 ignore_status=ignore_status,
571 stdout_tee=TEE_TO_LOGS, stderr_tee=TEE_TO_LOGS)]
mbligha5630a52008-09-03 22:09:50 +0000572 else:
mblighf8dffb12008-10-29 16:45:26 +0000573 out = [bg_job.stdout for bg_job in run_parallel(commands,
574 timeout=timeout, ignore_status=ignore_status)]
mbligha5630a52008-09-03 22:09:50 +0000575 for x in out:
576 if out[-1:] == '\n': out = out[:-1]
577 return out
578
579
mbligh98467952008-11-19 00:25:45 +0000580def strip_unicode(input):
581 if type(input) == list:
582 return [strip_unicode(i) for i in input]
583 elif type(input) == dict:
584 output = {}
585 for key in input.keys():
586 output[str(key)] = strip_unicode(input[key])
587 return output
588 elif type(input) == unicode:
589 return str(input)
590 else:
591 return input
592
593
mbligha5630a52008-09-03 22:09:50 +0000594def get_cpu_percentage(function, *args, **dargs):
595 """Returns a tuple containing the CPU% and return value from function call.
596
597 This function calculates the usage time by taking the difference of
598 the user and system times both before and after the function call.
599 """
600 child_pre = resource.getrusage(resource.RUSAGE_CHILDREN)
601 self_pre = resource.getrusage(resource.RUSAGE_SELF)
602 start = time.time()
603 to_return = function(*args, **dargs)
604 elapsed = time.time() - start
605 self_post = resource.getrusage(resource.RUSAGE_SELF)
606 child_post = resource.getrusage(resource.RUSAGE_CHILDREN)
607
608 # Calculate CPU Percentage
609 s_user, s_system = [a - b for a, b in zip(self_post, self_pre)[:2]]
610 c_user, c_system = [a - b for a, b in zip(child_post, child_pre)[:2]]
611 cpu_percent = (s_user + c_user + s_system + c_system) / elapsed
612
613 return cpu_percent, to_return
614
615
mblighc1cbc992008-05-27 20:01:45 +0000616"""
617This function is used when there is a need to run more than one
618job simultaneously starting exactly at the same time. It basically returns
619a modified control file (containing the synchronization code prepended)
620whenever it is ready to run the control file. The synchronization
621is done using barriers to make sure that the jobs start at the same time.
622
623Here is how the synchronization is done to make sure that the tests
624start at exactly the same time on the client.
625sc_bar is a server barrier and s_bar, c_bar are the normal barriers
626
627 Job1 Job2 ...... JobN
628 Server: | sc_bar
629 Server: | s_bar ...... s_bar
630 Server: | at.run() at.run() ...... at.run()
631 ----------|------------------------------------------------------
632 Client | sc_bar
633 Client | c_bar c_bar ...... c_bar
634 Client | <run test> <run test> ...... <run test>
635
636
637PARAMS:
638 control_file : The control file which to which the above synchronization
639 code would be prepended to
640 host_name : The host name on which the job is going to run
641 host_num (non negative) : A number to identify the machine so that we have
642 different sets of s_bar_ports for each of the machines.
643 instance : The number of the job
644 num_jobs : Total number of jobs that are going to run in parallel with
645 this job starting at the same time
646 port_base : Port number that is used to derive the actual barrier ports.
647
648RETURN VALUE:
649 The modified control file.
650
651"""
652def get_sync_control_file(control, host_name, host_num,
jadmanski0afbb632008-06-06 21:10:57 +0000653 instance, num_jobs, port_base=63100):
654 sc_bar_port = port_base
655 c_bar_port = port_base
656 if host_num < 0:
657 print "Please provide a non negative number for the host"
658 return None
659 s_bar_port = port_base + 1 + host_num # The set of s_bar_ports are
660 # the same for a given machine
mblighc1cbc992008-05-27 20:01:45 +0000661
jadmanski0afbb632008-06-06 21:10:57 +0000662 sc_bar_timeout = 180
663 s_bar_timeout = c_bar_timeout = 120
mblighc1cbc992008-05-27 20:01:45 +0000664
jadmanski0afbb632008-06-06 21:10:57 +0000665 # The barrier code snippet is prepended into the conrol file
666 # dynamically before at.run() is called finally.
667 control_new = []
mblighc1cbc992008-05-27 20:01:45 +0000668
jadmanski0afbb632008-06-06 21:10:57 +0000669 # jobid is the unique name used to identify the processes
670 # trying to reach the barriers
671 jobid = "%s#%d" % (host_name, instance)
mblighc1cbc992008-05-27 20:01:45 +0000672
jadmanski0afbb632008-06-06 21:10:57 +0000673 rendv = []
674 # rendvstr is a temp holder for the rendezvous list of the processes
675 for n in range(num_jobs):
676 rendv.append("'%s#%d'" % (host_name, n))
677 rendvstr = ",".join(rendv)
mblighc1cbc992008-05-27 20:01:45 +0000678
jadmanski0afbb632008-06-06 21:10:57 +0000679 if instance == 0:
680 # Do the setup and wait at the server barrier
681 # Clean up the tmp and the control dirs for the first instance
682 control_new.append('if os.path.exists(job.tmpdir):')
683 control_new.append("\t system('umount -f %s > /dev/null"
684 "2> /dev/null' % job.tmpdir,"
685 "ignore_status=True)")
686 control_new.append("\t system('rm -rf ' + job.tmpdir)")
687 control_new.append(
688 'b0 = job.barrier("%s", "sc_bar", %d, port=%d)'
689 % (jobid, sc_bar_timeout, sc_bar_port))
690 control_new.append(
mbligh9c12f772009-06-22 19:03:55 +0000691 'b0.rendezvous_servers("PARALLEL_MASTER", "%s")'
jadmanski0afbb632008-06-06 21:10:57 +0000692 % jobid)
mblighc1cbc992008-05-27 20:01:45 +0000693
jadmanski0afbb632008-06-06 21:10:57 +0000694 elif instance == 1:
695 # Wait at the server barrier to wait for instance=0
696 # process to complete setup
697 b0 = barrier.barrier("PARALLEL_MASTER", "sc_bar", sc_bar_timeout,
698 port=sc_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000699 b0.rendezvous_servers("PARALLEL_MASTER", jobid)
mblighc1cbc992008-05-27 20:01:45 +0000700
jadmanski0afbb632008-06-06 21:10:57 +0000701 if(num_jobs > 2):
702 b1 = barrier.barrier(jobid, "s_bar", s_bar_timeout,
703 port=s_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000704 b1.rendezvous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000705
jadmanski0afbb632008-06-06 21:10:57 +0000706 else:
707 # For the rest of the clients
708 b2 = barrier.barrier(jobid, "s_bar", s_bar_timeout, port=s_bar_port)
mbligh9c12f772009-06-22 19:03:55 +0000709 b2.rendezvous(rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000710
jadmanski0afbb632008-06-06 21:10:57 +0000711 # Client side barrier for all the tests to start at the same time
712 control_new.append('b1 = job.barrier("%s", "c_bar", %d, port=%d)'
713 % (jobid, c_bar_timeout, c_bar_port))
mbligh9c12f772009-06-22 19:03:55 +0000714 control_new.append("b1.rendezvous(%s)" % rendvstr)
mblighc1cbc992008-05-27 20:01:45 +0000715
jadmanski0afbb632008-06-06 21:10:57 +0000716 # Stick in the rest of the control file
717 control_new.append(control)
mblighc1cbc992008-05-27 20:01:45 +0000718
jadmanski0afbb632008-06-06 21:10:57 +0000719 return "\n".join(control_new)
mblighc1cbc992008-05-27 20:01:45 +0000720
mbligh63073c92008-03-31 16:49:32 +0000721
mblighc5ddfd12008-08-04 17:15:00 +0000722def get_arch(run_function=run):
723 """
724 Get the hardware architecture of the machine.
725 run_function is used to execute the commands. It defaults to
726 utils.run() but a custom method (if provided) should be of the
727 same schema as utils.run. It should return a CmdResult object and
728 throw a CmdError exception.
729 """
730 arch = run_function('/bin/uname -m').stdout.rstrip()
731 if re.match(r'i\d86$', arch):
732 arch = 'i386'
733 return arch
734
735
showard4745ecd2009-05-26 19:34:56 +0000736def get_num_logical_cpus_per_socket(run_function=run):
mbligh9fd9afe2009-04-28 18:27:25 +0000737 """
738 Get the number of cores (including hyperthreading) per cpu.
739 run_function is used to execute the commands. It defaults to
740 utils.run() but a custom method (if provided) should be of the
741 same schema as utils.run. It should return a CmdResult object and
742 throw a CmdError exception.
743 """
showard4745ecd2009-05-26 19:34:56 +0000744 siblings = run_function('grep "^siblings" /proc/cpuinfo').stdout.rstrip()
745 num_siblings = map(int,
746 re.findall(r'^siblings\s*:\s*(\d+)\s*$',
747 siblings, re.M))
748 if len(num_siblings) == 0:
749 raise error.TestError('Unable to find siblings info in /proc/cpuinfo')
750 if min(num_siblings) != max(num_siblings):
751 raise error.TestError('Number of siblings differ %r' %
752 num_siblings)
753 return num_siblings[0]
mbligh9fd9afe2009-04-28 18:27:25 +0000754
755
jadmanski4f909252008-12-01 20:47:10 +0000756def merge_trees(src, dest):
757 """
758 Merges a source directory tree at 'src' into a destination tree at
759 'dest'. If a path is a file in both trees than the file in the source
760 tree is APPENDED to the one in the destination tree. If a path is
761 a directory in both trees then the directories are recursively merged
762 with this function. In any other case, the function will skip the
763 paths that cannot be merged (instead of failing).
764 """
765 if not os.path.exists(src):
766 return # exists only in dest
767 elif not os.path.exists(dest):
768 if os.path.isfile(src):
769 shutil.copy2(src, dest) # file only in src
770 else:
771 shutil.copytree(src, dest, symlinks=True) # dir only in src
772 return
773 elif os.path.isfile(src) and os.path.isfile(dest):
774 # src & dest are files in both trees, append src to dest
775 destfile = open(dest, "a")
776 try:
777 srcfile = open(src)
778 try:
779 destfile.write(srcfile.read())
780 finally:
781 srcfile.close()
782 finally:
783 destfile.close()
784 elif os.path.isdir(src) and os.path.isdir(dest):
785 # src & dest are directories in both trees, so recursively merge
786 for name in os.listdir(src):
787 merge_trees(os.path.join(src, name), os.path.join(dest, name))
788 else:
789 # src & dest both exist, but are incompatible
790 return
791
792
mbligh63073c92008-03-31 16:49:32 +0000793class CmdResult(object):
jadmanski0afbb632008-06-06 21:10:57 +0000794 """
795 Command execution result.
mbligh63073c92008-03-31 16:49:32 +0000796
jadmanski0afbb632008-06-06 21:10:57 +0000797 command: String containing the command line itself
798 exit_status: Integer exit code of the process
799 stdout: String containing stdout of the process
800 stderr: String containing stderr of the process
801 duration: Elapsed wall clock time running the process
802 """
mbligh63073c92008-03-31 16:49:32 +0000803
804
mblighcd63a212009-05-01 23:04:38 +0000805 def __init__(self, command="", stdout="", stderr="",
jadmanski0afbb632008-06-06 21:10:57 +0000806 exit_status=None, duration=0):
807 self.command = command
808 self.exit_status = exit_status
809 self.stdout = stdout
810 self.stderr = stderr
811 self.duration = duration
mbligh63073c92008-03-31 16:49:32 +0000812
813
jadmanski0afbb632008-06-06 21:10:57 +0000814 def __repr__(self):
815 wrapper = textwrap.TextWrapper(width = 78,
816 initial_indent="\n ",
817 subsequent_indent=" ")
818
819 stdout = self.stdout.rstrip()
820 if stdout:
821 stdout = "\nstdout:\n%s" % stdout
822
823 stderr = self.stderr.rstrip()
824 if stderr:
825 stderr = "\nstderr:\n%s" % stderr
826
827 return ("* Command: %s\n"
828 "Exit status: %s\n"
829 "Duration: %s\n"
830 "%s"
831 "%s"
832 % (wrapper.fill(self.command), self.exit_status,
833 self.duration, stdout, stderr))
mbligh63073c92008-03-31 16:49:32 +0000834
835
mbligh462c0152008-03-13 15:37:10 +0000836class run_randomly:
jadmanski0afbb632008-06-06 21:10:57 +0000837 def __init__(self, run_sequentially=False):
838 # Run sequentially is for debugging control files
839 self.test_list = []
840 self.run_sequentially = run_sequentially
mbligh462c0152008-03-13 15:37:10 +0000841
842
jadmanski0afbb632008-06-06 21:10:57 +0000843 def add(self, *args, **dargs):
844 test = (args, dargs)
845 self.test_list.append(test)
mbligh462c0152008-03-13 15:37:10 +0000846
847
jadmanski0afbb632008-06-06 21:10:57 +0000848 def run(self, fn):
849 while self.test_list:
850 test_index = random.randint(0, len(self.test_list)-1)
851 if self.run_sequentially:
852 test_index = 0
853 (args, dargs) = self.test_list.pop(test_index)
854 fn(*args, **dargs)
mbligha7007722009-01-13 00:37:11 +0000855
856
mblighdd669372009-02-03 21:57:18 +0000857def import_site_symbol(path, module, name, dummy=None, modulefile=None):
858 """
859 Try to import site specific symbol from site specific file if it exists
860
861 @param path full filename of the source file calling this (ie __file__)
862 @param module full module name
863 @param name symbol name to be imported from the site file
864 @param dummy dummy value to return in case there is no symbol to import
865 @param modulefile module filename
866
867 @return site specific symbol or dummy
868
869 @exception ImportError if the site file exists but imports fails
870 """
mbligha7007722009-01-13 00:37:11 +0000871 short_module = module[module.rfind(".") + 1:]
872
873 if not modulefile:
874 modulefile = short_module + ".py"
875
876 try:
877 site_exists = os.path.getsize(os.path.join(os.path.dirname(path),
878 modulefile))
879 except os.error:
880 site_exists = False
881
mbligh61f4e442009-06-08 16:48:20 +0000882 msg = None
mbligha7007722009-01-13 00:37:11 +0000883 if site_exists:
mbligh61f4e442009-06-08 16:48:20 +0000884 # special unique value to tell us if the symbol can't be imported
885 cant_import = object()
886
mbligh062ed152009-01-13 00:57:14 +0000887 # return the object from the imported module
mbligh61f4e442009-06-08 16:48:20 +0000888 obj = getattr(__import__(module, {}, {}, [short_module]), name,
889 cant_import)
890 if obj is cant_import:
891 msg = ("unable to import site symbol '%s', using non-site "
892 "implementation") % name
mbligha7007722009-01-13 00:37:11 +0000893 else:
jadmanski0dc8ff82009-02-11 15:11:15 +0000894 msg = "unable to import site module '%s', using non-site implementation"
895 msg %= modulefile
mbligh61f4e442009-06-08 16:48:20 +0000896
897 if msg:
showardb18134f2009-03-20 20:52:18 +0000898 logging.info(msg)
mbligh062ed152009-01-13 00:57:14 +0000899 obj = dummy
900
901 return obj
902
903
904def import_site_class(path, module, classname, baseclass, modulefile=None):
905 """
906 Try to import site specific class from site specific file if it exists
907
908 Args:
909 path: full filename of the source file calling this (ie __file__)
910 module: full module name
911 classname: class name to be loaded from site file
mbligh0a8c3322009-04-28 18:32:19 +0000912 baseclass: base class object to return when no site file present or
913 to mixin when site class exists but is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +0000914 modulefile: module filename
915
mbligh0a8c3322009-04-28 18:32:19 +0000916 Returns: baseclass if site specific class does not exist, the site specific
917 class if it exists and is inherited from baseclass or a mixin of the
918 site specific class and baseclass when the site specific class exists
919 and is not inherited from baseclass
mbligh062ed152009-01-13 00:57:14 +0000920
921 Raises: ImportError if the site file exists but imports fails
922 """
923
mblighdd669372009-02-03 21:57:18 +0000924 res = import_site_symbol(path, module, classname, None, modulefile)
mbligh0a8c3322009-04-28 18:32:19 +0000925 if res:
926 if not issubclass(res, baseclass):
927 # if not a subclass of baseclass then mix in baseclass with the
928 # site specific class object and return the result
929 res = type(classname, (res, baseclass), {})
930 else:
931 res = baseclass
mbligha7007722009-01-13 00:37:11 +0000932
mbligh062ed152009-01-13 00:57:14 +0000933 return res
934
935
936def import_site_function(path, module, funcname, dummy, modulefile=None):
937 """
938 Try to import site specific function from site specific file if it exists
939
940 Args:
941 path: full filename of the source file calling this (ie __file__)
942 module: full module name
943 funcname: function name to be imported from site file
944 dummy: dummy function to return in case there is no function to import
945 modulefile: module filename
946
947 Returns: site specific function object or dummy
948
949 Raises: ImportError if the site file exists but imports fails
950 """
951
mblighdd669372009-02-03 21:57:18 +0000952 return import_site_symbol(path, module, funcname, dummy, modulefile)
mblighfb676032009-04-01 18:25:38 +0000953
954
955def write_pid(program_name):
956 """
957 Try to drop <program_name>.pid in the main autotest directory.
958
959 Args:
960 program_name: prefix for file name
961 """
962
963 my_path = os.path.dirname(__file__)
964 pid_path = os.path.abspath(os.path.join(my_path, "../.."))
965 pidf = open(os.path.join(pid_path, "%s.pid" % program_name), "w")
966 if pidf:
967 pidf.write("%s\n" % os.getpid())
968 pidf.close()
mbligh45561782009-05-11 21:14:34 +0000969
970
971def get_relative_path(path, reference):
972 """Given 2 absolute paths "path" and "reference", compute the path of
973 "path" as relative to the directory "reference".
974
975 @param path the absolute path to convert to a relative path
976 @param reference an absolute directory path to which the relative
977 path will be computed
978 """
979 # normalize the paths (remove double slashes, etc)
980 assert(os.path.isabs(path))
981 assert(os.path.isabs(reference))
982
983 path = os.path.normpath(path)
984 reference = os.path.normpath(reference)
985
986 # we could use os.path.split() but it splits from the end
987 path_list = path.split(os.path.sep)[1:]
988 ref_list = reference.split(os.path.sep)[1:]
989
990 # find the longest leading common path
991 for i in xrange(min(len(path_list), len(ref_list))):
992 if path_list[i] != ref_list[i]:
993 # decrement i so when exiting this loop either by no match or by
994 # end of range we are one step behind
995 i -= 1
996 break
997 i += 1
998 # drop the common part of the paths, not interested in that anymore
999 del path_list[:i]
1000
1001 # for each uncommon component in the reference prepend a ".."
1002 path_list[:0] = ['..'] * (len(ref_list) - i)
1003
1004 return os.path.join(*path_list)
mbligh277a0e42009-07-11 00:11:45 +00001005
1006
1007def sh_escape(command):
1008 """
1009 Escape special characters from a command so that it can be passed
1010 as a double quoted (" ") string in a (ba)sh command.
1011
1012 Args:
1013 command: the command string to escape.
1014
1015 Returns:
1016 The escaped command string. The required englobing double
1017 quotes are NOT added and so should be added at some point by
1018 the caller.
1019
1020 See also: http://www.tldp.org/LDP/abs/html/escapingsection.html
1021 """
1022 command = command.replace("\\", "\\\\")
1023 command = command.replace("$", r'\$')
1024 command = command.replace('"', r'\"')
1025 command = command.replace('`', r'\`')
1026 return command