blob: 41b9d80ca9672e20b7d59834e19643b447135ecd [file] [log] [blame]
lmrbbc9dd52009-07-22 20:33:47 +00001#!/usr/bin/python
2import sys, subprocess, pty, select, os, time, signal, re, termios, fcntl
3import threading, logging, commands
4import common, kvm_utils
5
6"""
7A class and functions used for running and controlling child processes.
8
9@copyright: 2008-2009 Red Hat Inc.
10"""
11
12
13def run_bg(command, termination_func=None, output_func=None, output_prefix="",
14 timeout=1.0):
15 """
16 Run command as a subprocess. Call output_func with each line of output
17 from the subprocess (prefixed by output_prefix). Call termination_func
18 when the subprocess terminates. Return when timeout expires or when the
19 subprocess exits -- whichever occurs first.
20
21 @brief: Run a subprocess in the background and collect its output and
22 exit status.
23
24 @param command: The shell command to execute
25 @param termination_func: A function to call when the process terminates
26 (should take an integer exit status parameter)
27 @param output_func: A function to call with each line of output from
28 the subprocess (should take a string parameter)
29 @param output_prefix: A string to pre-pend to each line of the output,
30 before passing it to stdout_func
31 @param timeout: Time duration (in seconds) to wait for the subprocess to
32 terminate before returning
33
34 @return: A kvm_tail object.
35 """
36 process = kvm_tail(command=command,
37 termination_func=termination_func,
38 output_func=output_func,
39 output_prefix=output_prefix)
40
41 end_time = time.time() + timeout
42 while time.time() < end_time and process.is_alive():
43 time.sleep(0.1)
44
45 return process
46
47
48def run_fg(command, output_func=None, output_prefix="", timeout=1.0):
49 """
50 Run command as a subprocess. Call output_func with each line of output
51 from the subprocess (prefixed by prefix). Return when timeout expires or
52 when the subprocess exits -- whichever occurs first. If timeout expires
53 and the subprocess is still running, kill it before returning.
54
55 @brief: Run a subprocess in the foreground and collect its output and
56 exit status.
57
58 @param command: The shell command to execute
59 @param output_func: A function to call with each line of output from
60 the subprocess (should take a string parameter)
61 @param output_prefix: A string to pre-pend to each line of the output,
62 before passing it to stdout_func
63 @param timeout: Time duration (in seconds) to wait for the subprocess to
64 terminate before killing it and returning
65
66 @return: A 2-tuple containing the exit status of the process and its
67 STDOUT/STDERR output. If timeout expires before the process
68 terminates, the returned status is None.
69 """
70 process = run_bg(command, None, output_func, output_prefix, timeout)
71 output = process.get_output()
72 if process.is_alive():
73 status = None
74 else:
75 status = process.get_status()
76 process.close()
77 return (status, output)
78
79
80def _lock(filename):
81 if not os.path.exists(filename):
82 open(filename, "w").close()
83 fd = os.open(filename, os.O_RDWR)
84 fcntl.lockf(fd, fcntl.LOCK_EX)
85 return fd
86
87
88def _unlock(fd):
89 fcntl.lockf(fd, fcntl.LOCK_UN)
90 os.close(fd)
91
92
93def _locked(filename):
94 try:
95 fd = os.open(filename, os.O_RDWR)
96 except:
97 return False
98 try:
99 fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
100 except:
101 os.close(fd)
102 return True
103 fcntl.lockf(fd, fcntl.LOCK_UN)
104 os.close(fd)
105 return False
106
107
108def _wait(filename):
109 fd = _lock(filename)
110 _unlock(fd)
111
112
113def _get_filenames(base_dir, id):
114 return [os.path.join(base_dir, s + id) for s in
115 "shell-pid-", "status-", "output-", "inpipe-",
116 "lock-server-running-", "lock-client-starting-"]
117
118
119def _get_reader_filename(base_dir, id, reader):
120 return os.path.join(base_dir, "outpipe-%s-%s" % (reader, id))
121
122
123class kvm_spawn:
124 """
125 This class is used for spawning and controlling a child process.
126
127 A new instance of this class can either run a new server (a small Python
128 program that reads output from the child process and reports it to the
129 client and to a text file) or attach to an already running server.
130 When a server is started it runs the child process.
131 The server writes output from the child's STDOUT and STDERR to a text file.
132 The text file can be accessed at any time using get_output().
133 In addition, the server opens as many pipes as requested by the client and
134 writes the output to them.
135 The pipes are requested and accessed by classes derived from kvm_spawn.
136 These pipes are referred to as "readers".
137 The server also receives input from the client and sends it to the child
138 process.
139 An instance of this class can be pickled. Every derived class is
140 responsible for restoring its own state by properly defining
141 __getinitargs__().
142
143 The first named pipe is used by _tail(), a function that runs in the
144 background and reports new output from the child as it is produced.
145 The second named pipe is used by a set of functions that read and parse
146 output as requested by the user in an interactive manner, similar to
147 pexpect.
148 When unpickled it automatically
149 resumes _tail() if needed.
150 """
151
152 def __init__(self, command=None, id=None, echo=False, linesep="\n"):
153 """
154 Initialize the class and run command as a child process.
155
156 @param command: Command to run, or None if accessing an already running
157 server.
158 @param id: ID of an already running server, if accessing a running
159 server, or None if starting a new one.
160 @param echo: Boolean indicating whether echo should be initially
161 enabled for the pseudo terminal running the subprocess. This
162 parameter has an effect only when starting a new server.
163 @param linesep: Line separator to be appended to strings sent to the
164 child process by sendline().
165 """
166 self.id = id or kvm_utils.generate_random_string(8)
167
168 # Define filenames for communication with server
169 base_dir = "/tmp/kvm_spawn"
170 try:
171 os.makedirs(base_dir)
172 except:
173 pass
174 (self.shell_pid_filename,
175 self.status_filename,
176 self.output_filename,
177 self.inpipe_filename,
178 self.lock_server_running_filename,
179 self.lock_client_starting_filename) = _get_filenames(base_dir,
180 self.id)
181
182 # Remember some attributes
183 self.echo = echo
184 self.linesep = linesep
185
186 # Make sure the 'readers' and 'close_hooks' attributes exist
187 if not hasattr(self, "readers"):
188 self.readers = []
189 if not hasattr(self, "close_hooks"):
190 self.close_hooks = []
191
192 # Define the reader filenames
193 self.reader_filenames = dict(
194 (reader, _get_reader_filename(base_dir, self.id, reader))
195 for reader in self.readers)
196
197 # Let the server know a client intends to open some pipes;
198 # if the executed command terminates quickly, the server will wait for
199 # the client to release the lock before exiting
200 lock_client_starting = _lock(self.lock_client_starting_filename)
201
202 # Start the server (which runs the command)
203 if command:
204 sub = subprocess.Popen("python %s" % __file__,
205 shell=True,
206 stdin=subprocess.PIPE,
207 stdout=subprocess.PIPE,
208 stderr=subprocess.STDOUT)
209 # Send parameters to the server
210 sub.stdin.write("%s\n" % self.id)
211 sub.stdin.write("%s\n" % echo)
212 sub.stdin.write("%s\n" % ",".join(self.readers))
213 sub.stdin.write("%s\n" % command)
214 # Wait for the server to complete its initialization
lmrb8f53d62009-07-27 13:29:17 +0000215 while not "Server %s ready" % self.id in sub.stdout.readline():
216 pass
lmre7a732b2009-08-07 20:13:37 +0000217 # Remember the start time for is_alive()
218 self.start_time = time.time()
lmrbbc9dd52009-07-22 20:33:47 +0000219
220 # Open the reading pipes
221 self.reader_fds = {}
222 try:
223 assert(_locked(self.lock_server_running_filename))
224 for reader, filename in self.reader_filenames.items():
225 self.reader_fds[reader] = os.open(filename, os.O_RDONLY)
226 except:
227 pass
228
229 # Allow the server to continue
230 _unlock(lock_client_starting)
231
232
233 # The following two functions are defined to make sure the state is set
234 # exclusively by the constructor call as specified in __getinitargs__().
235
236 def __getstate__(self):
237 pass
238
239
240 def __setstate__(self, state):
241 pass
242
243
244 def __getinitargs__(self):
245 # Save some information when pickling -- will be passed to the
246 # constructor upon unpickling
247 return (None, self.id, self.echo, self.linesep)
248
249
250 def _add_reader(self, reader):
251 """
252 Add a reader whose file descriptor can be obtained with _get_fd().
253 Should be called before __init__(). Intended for use by derived
254 classes.
255
256 @param reader: The name of the reader.
257 """
258 if not hasattr(self, "readers"):
259 self.readers = []
260 self.readers.append(reader)
261
262
263 def _add_close_hook(self, hook):
264 """
265 Add a close hook function to be called when close() is called.
266 The function will be called after the process terminates but before
267 final cleanup. Intended for use by derived classes.
268
269 @param hook: The hook function.
270 """
271 if not hasattr(self, "close_hooks"):
272 self.close_hooks = []
273 self.close_hooks.append(hook)
274
275
276 def _get_fd(self, reader):
277 """
278 Return an open file descriptor corresponding to the specified reader
279 pipe. If no such reader exists, or the pipe could not be opened,
280 return None. Intended for use by derived classes.
281
282 @param reader: The name of the reader.
283 """
284 return self.reader_fds.get(reader)
285
286
287 def get_id(self):
288 """
289 Return the instance's id attribute, which may be used to access the
290 process in the future.
291 """
292 return self.id
293
294
295 def get_shell_pid(self):
296 """
297 Return the PID of the subshell process, or None if not available.
298 The subshell is the shell that runs the command.
299 """
300 try:
301 file = open(self.shell_pid_filename, "r")
302 pid = int(file.read())
303 file.close()
304 return pid
305 except:
306 return None
307
308
309 def get_pid(self, index=0):
310 """
311 Try to get and return the PID of a child process of the subshell.
312 This is usually the PID of the process executed in the subshell.
313 There are 3 exceptions:
314 - If the subshell couldn't start the process for some reason, no
315 PID can be returned.
316 - If the subshell is running several processes in parallel,
317 multiple PIDs can be returned. Use the index parameter in this
318 case.
319 - Before starting the process, after the process has terminated,
320 or while running shell code that doesn't start any processes --
321 no PID can be returned.
322
323 @param index: The index of the child process whose PID is requested.
324 Normally this should remain 0.
325 @return: The PID of the child process, or None if none could be found.
326 """
327 parent_pid = self.get_shell_pid()
328 if not parent_pid:
329 return None
330 pids = commands.getoutput("ps --ppid %d -o pid=" % parent_pid).split()
331 try:
332 return int(pids[index])
333 except:
334 return None
335
336
337 def get_status(self):
338 """
339 Wait for the process to exit and return its exit status, or None
340 if the exit status is not available.
341 """
342 _wait(self.lock_server_running_filename)
343 try:
344 file = open(self.status_filename, "r")
345 status = int(file.read())
346 file.close()
347 return status
348 except:
349 return None
350
351
352 def get_output(self):
353 """
354 Return the STDOUT and STDERR output of the process so far.
355 """
356 try:
357 file = open(self.output_filename, "r")
358 output = file.read()
359 file.close()
360 return output
361 except:
362 return ""
363
364
365 def is_alive(self):
366 """
367 Return True if the process is running.
368 """
369 pid = self.get_shell_pid()
370 # See if the PID exists
371 try:
372 os.kill(pid, 0)
373 except:
374 return False
375 # Make sure the PID belongs to the original process
376 filename = "/proc/%d/cmdline" % pid
377 try:
378 file = open(filename, "r")
379 cmdline = file.read()
380 file.close()
381 except:
382 # If we couldn't find the file for some reason, skip the check
383 return True
lmre7a732b2009-08-07 20:13:37 +0000384 # If this process is new (less than 10 secs old) skip the check
385 if hasattr(self, "start_time") and time.time() < self.start_time + 10:
386 return True
387 # Perform the check
lmrbbc9dd52009-07-22 20:33:47 +0000388 if self.id in cmdline:
389 return True
390 return False
391
392
393 def close(self, sig=signal.SIGTERM):
394 """
395 Kill the child process if it's alive and remove temporary files.
396
397 @param sig: The signal to send the process when attempting to kill it.
398 """
399 # Kill it if it's alive
400 if self.is_alive():
401 try:
402 os.kill(self.get_shell_pid(), sig)
403 except:
404 pass
405 # Wait for the server to exit
406 _wait(self.lock_server_running_filename)
407 # Call all cleanup routines
408 for hook in self.close_hooks:
409 hook()
410 # Close reader file descriptors
411 for fd in self.reader_fds.values():
412 try:
413 os.close(fd)
414 except:
415 pass
416 # Remove all used files
417 for filename in (_get_filenames("/tmp/kvm_spawn", self.id) +
418 self.reader_filenames.values()):
419 try:
420 os.unlink(filename)
421 except OSError:
422 pass
423
424
425 def set_linesep(self, linesep):
426 """
427 Sets the line separator string (usually "\\n").
428
429 @param linesep: Line separator string.
430 """
431 self.linesep = linesep
432
433
434 def send(self, str=""):
435 """
436 Send a string to the child process.
437
438 @param str: String to send to the child process.
439 """
440 try:
441 fd = os.open(self.inpipe_filename, os.O_RDWR)
442 os.write(fd, str)
443 os.close(fd)
444 except:
445 pass
446
447
448 def sendline(self, str=""):
449 """
450 Send a string followed by a line separator to the child process.
451
452 @param str: String to send to the child process.
453 """
454 self.send(str + self.linesep)
455
456
457class kvm_tail(kvm_spawn):
458 """
459 This class runs a child process in the background and sends its output in
460 real time, line-by-line, to a callback function.
461
462 See kvm_spawn's docstring.
463
464 This class uses a single pipe reader to read data in real time from the
465 child process and report it to a given callback function.
466 When the child process exits, its exit status is reported to an additional
467 callback function.
468
469 When this class is unpickled, it automatically resumes reporting output.
470 """
471
472 def __init__(self, command=None, id=None, echo=False, linesep="\n",
lmr4fe15ba2009-08-13 04:11:26 +0000473 termination_func=None, termination_params=(),
474 output_func=None, output_params=(),
475 output_prefix=""):
lmrbbc9dd52009-07-22 20:33:47 +0000476 """
477 Initialize the class and run command as a child process.
478
479 @param command: Command to run, or None if accessing an already running
480 server.
481 @param id: ID of an already running server, if accessing a running
482 server, or None if starting a new one.
483 @param echo: Boolean indicating whether echo should be initially
484 enabled for the pseudo terminal running the subprocess. This
485 parameter has an effect only when starting a new server.
486 @param linesep: Line separator to be appended to strings sent to the
487 child process by sendline().
488 @param termination_func: Function to call when the process exits. The
489 function must accept a single exit status parameter.
lmr4fe15ba2009-08-13 04:11:26 +0000490 @param termination_params: Parameters to send to termination_func
491 before the exit status.
lmrbbc9dd52009-07-22 20:33:47 +0000492 @param output_func: Function to call whenever a line of output is
493 available from the STDOUT or STDERR streams of the process.
494 The function must accept a single string parameter. The string
495 does not include the final newline.
lmr4fe15ba2009-08-13 04:11:26 +0000496 @param output_params: Parameters to send to output_func before the
497 output line.
lmrbbc9dd52009-07-22 20:33:47 +0000498 @param output_prefix: String to prepend to lines sent to output_func.
499 """
500 # Add a reader and a close hook
501 self._add_reader("tail")
502 self._add_close_hook(self._join_thread)
503
504 # Init the superclass
505 kvm_spawn.__init__(self, command, id, echo, linesep)
506
507 # Remember some attributes
508 self.termination_func = termination_func
lmr4fe15ba2009-08-13 04:11:26 +0000509 self.termination_params = termination_params
lmrbbc9dd52009-07-22 20:33:47 +0000510 self.output_func = output_func
lmr4fe15ba2009-08-13 04:11:26 +0000511 self.output_params = output_params
lmrbbc9dd52009-07-22 20:33:47 +0000512 self.output_prefix = output_prefix
513
514 # Start the thread in the background
515 self.tail_thread = threading.Thread(None, self._tail)
516 self.tail_thread.start()
517
518
519 def __getinitargs__(self):
520 return kvm_spawn.__getinitargs__(self) + (self.termination_func,
lmr4fe15ba2009-08-13 04:11:26 +0000521 self.termination_params,
lmrbbc9dd52009-07-22 20:33:47 +0000522 self.output_func,
lmr4fe15ba2009-08-13 04:11:26 +0000523 self.output_params,
lmrbbc9dd52009-07-22 20:33:47 +0000524 self.output_prefix)
525
526
527 def set_termination_func(self, termination_func):
528 """
529 Set the termination_func attribute. See __init__() for details.
530
531 @param termination_func: Function to call when the process terminates.
532 Must take a single parameter -- the exit status.
533 """
534 self.termination_func = termination_func
535
536
lmr4fe15ba2009-08-13 04:11:26 +0000537 def set_termination_params(self, termination_params):
538 """
539 Set the termination_params attribute. See __init__() for details.
540
541 @param termination_params: Parameters to send to termination_func
542 before the exit status.
543 """
544 self.termination_params = termination_params
545
546
lmrbbc9dd52009-07-22 20:33:47 +0000547 def set_output_func(self, output_func):
548 """
549 Set the output_func attribute. See __init__() for details.
550
551 @param output_func: Function to call for each line of STDOUT/STDERR
552 output from the process. Must take a single string parameter.
553 """
554 self.output_func = output_func
555
556
lmr4fe15ba2009-08-13 04:11:26 +0000557 def set_output_params(self, output_params):
558 """
559 Set the output_params attribute. See __init__() for details.
560
561 @param output_params: Parameters to send to output_func before the
562 output line.
563 """
564 self.output_params = output_params
565
566
lmrbbc9dd52009-07-22 20:33:47 +0000567 def set_output_prefix(self, output_prefix):
568 """
569 Set the output_prefix attribute. See __init__() for details.
570
571 @param output_prefix: String to pre-pend to each line sent to
572 output_func (see set_output_callback()).
573 """
574 self.output_prefix = output_prefix
575
576
577 def _tail(self):
578 def print_line(text):
579 # Pre-pend prefix and remove trailing whitespace
580 text = self.output_prefix + text.rstrip()
581 # Sanitize text
582 text = text.decode("utf-8", "replace")
583 # Pass it to output_func
584 try:
lmr4fe15ba2009-08-13 04:11:26 +0000585 params = self.output_params + (text,)
586 self.output_func(*params)
lmrbbc9dd52009-07-22 20:33:47 +0000587 except TypeError:
588 pass
589
590 fd = self._get_fd("tail")
591 buffer = ""
592 while True:
593 try:
594 # See if there's any data to read from the pipe
595 r, w, x = select.select([fd], [], [], 0.05)
596 except:
597 break
598 if fd in r:
599 # Some data is available; read it
600 new_data = os.read(fd, 1024)
601 if not new_data:
602 break
603 buffer += new_data
604 # Send the output to output_func line by line
605 # (except for the last line)
606 if self.output_func:
607 lines = buffer.split("\n")
608 for line in lines[:-1]:
609 print_line(line)
610 # Leave only the last line
611 last_newline_index = buffer.rfind("\n")
612 buffer = buffer[last_newline_index+1:]
613 else:
614 # No output is available right now; flush the buffer
615 if buffer:
616 print_line(buffer)
617 buffer = ""
618 # The process terminated; print any remaining output
619 if buffer:
620 print_line(buffer)
621 # Get the exit status, print it and send it to termination_func
622 status = self.get_status()
623 if status is None:
624 return
625 print_line("(Process terminated with status %s)" % status)
626 try:
lmr4fe15ba2009-08-13 04:11:26 +0000627 params = self.termination_params + (status,)
628 self.termination_func(*params)
lmrbbc9dd52009-07-22 20:33:47 +0000629 except TypeError:
630 pass
631
632
633 def _join_thread(self):
634 # Wait for the tail thread to exit
635 if self.tail_thread:
636 self.tail_thread.join()
637
638
639class kvm_expect(kvm_tail):
640 """
641 This class runs a child process in the background and provides expect-like
642 services.
643
644 It also provides all of kvm_tail's functionality.
645 """
646
647 def __init__(self, command=None, id=None, echo=False, linesep="\n",
lmr4fe15ba2009-08-13 04:11:26 +0000648 termination_func=None, termination_params=(),
649 output_func=None, output_params=(),
650 output_prefix=""):
lmrbbc9dd52009-07-22 20:33:47 +0000651 """
652 Initialize the class and run command as a child process.
653
654 @param command: Command to run, or None if accessing an already running
655 server.
656 @param id: ID of an already running server, if accessing a running
657 server, or None if starting a new one.
658 @param echo: Boolean indicating whether echo should be initially
659 enabled for the pseudo terminal running the subprocess. This
660 parameter has an effect only when starting a new server.
661 @param linesep: Line separator to be appended to strings sent to the
662 child process by sendline().
663 @param termination_func: Function to call when the process exits. The
664 function must accept a single exit status parameter.
lmr4fe15ba2009-08-13 04:11:26 +0000665 @param termination_params: Parameters to send to termination_func
666 before the exit status.
lmrbbc9dd52009-07-22 20:33:47 +0000667 @param output_func: Function to call whenever a line of output is
668 available from the STDOUT or STDERR streams of the process.
669 The function must accept a single string parameter. The string
670 does not include the final newline.
lmr4fe15ba2009-08-13 04:11:26 +0000671 @param output_params: Parameters to send to output_func before the
672 output line.
lmrbbc9dd52009-07-22 20:33:47 +0000673 @param output_prefix: String to prepend to lines sent to output_func.
674 """
675 # Add a reader
676 self._add_reader("expect")
677
678 # Init the superclass
679 kvm_tail.__init__(self, command, id, echo, linesep,
lmr4fe15ba2009-08-13 04:11:26 +0000680 termination_func, termination_params,
681 output_func, output_params, output_prefix)
lmrbbc9dd52009-07-22 20:33:47 +0000682
683
684 def __getinitargs__(self):
685 return kvm_tail.__getinitargs__(self)
686
687
688 def read_nonblocking(self, timeout=None):
689 """
690 Read from child until there is nothing to read for timeout seconds.
691
692 @param timeout: Time (seconds) to wait before we give up reading from
693 the child process, or None to use the default value.
694 """
695 if timeout is None:
696 timeout = 0.1
697 fd = self._get_fd("expect")
698 data = ""
699 while True:
700 try:
701 r, w, x = select.select([fd], [], [], timeout)
702 except:
703 return data
704 if fd in r:
705 new_data = os.read(fd, 1024)
706 if not new_data:
707 return data
708 data += new_data
709 else:
710 return data
711
712
713 def match_patterns(self, str, patterns):
714 """
715 Match str against a list of patterns.
716
717 Return the index of the first pattern that matches a substring of str.
718 None and empty strings in patterns are ignored.
719 If no match is found, return None.
720
721 @param patterns: List of strings (regular expression patterns).
722 """
723 for i in range(len(patterns)):
724 if not patterns[i]:
725 continue
726 if re.search(patterns[i], str):
727 return i
728
729
730 def read_until_output_matches(self, patterns, filter=lambda x: x,
731 timeout=30.0, internal_timeout=None,
732 print_func=None):
733 """
734 Read using read_nonblocking until a match is found using match_patterns,
735 or until timeout expires. Before attempting to search for a match, the
736 data is filtered using the filter function provided.
737
738 @brief: Read from child using read_nonblocking until a pattern
739 matches.
740 @param patterns: List of strings (regular expression patterns)
741 @param filter: Function to apply to the data read from the child before
742 attempting to match it against the patterns (should take and
743 return a string)
744 @param timeout: The duration (in seconds) to wait until a match is
745 found
746 @param internal_timeout: The timeout to pass to read_nonblocking
747 @param print_func: A function to be used to print the data being read
748 (should take a string parameter)
749 @return: Tuple containing the match index (or None if no match was
750 found) and the data read so far.
751 """
752 match = None
753 data = ""
754
755 end_time = time.time() + timeout
756 while time.time() < end_time:
757 # Read data from child
758 newdata = self.read_nonblocking(internal_timeout)
759 # Print it if necessary
760 if print_func and newdata:
761 str = newdata
762 if str.endswith("\n"):
763 str = str[:-1]
764 for line in str.split("\n"):
765 print_func(line.decode("utf-8", "replace"))
766 data += newdata
767
768 done = False
769 # Look for patterns
770 match = self.match_patterns(filter(data), patterns)
771 if match is not None:
772 done = True
773 # Check if child has died
774 if not self.is_alive():
775 logging.debug("Process terminated with status %s" % self.get_status())
776 done = True
777 # Are we done?
778 if done: break
779
780 # Print some debugging info
781 if match is None and (self.is_alive() or self.get_status() != 0):
782 logging.debug("Timeout elapsed or process terminated. Output:" +
783 kvm_utils.format_str_for_message(data.strip()))
784
785 return (match, data)
786
787
788 def read_until_last_word_matches(self, patterns, timeout=30.0,
789 internal_timeout=None, print_func=None):
790 """
791 Read using read_nonblocking until the last word of the output matches
792 one of the patterns (using match_patterns), or until timeout expires.
793
794 @param patterns: A list of strings (regular expression patterns)
795 @param timeout: The duration (in seconds) to wait until a match is
796 found
797 @param internal_timeout: The timeout to pass to read_nonblocking
798 @param print_func: A function to be used to print the data being read
799 (should take a string parameter)
800 @return: A tuple containing the match index (or None if no match was
801 found) and the data read so far.
802 """
803 def get_last_word(str):
804 if str:
805 return str.split()[-1]
806 else:
807 return ""
808
809 return self.read_until_output_matches(patterns, get_last_word,
810 timeout, internal_timeout,
811 print_func)
812
813
814 def read_until_last_line_matches(self, patterns, timeout=30.0,
815 internal_timeout=None, print_func=None):
816 """
817 Read using read_nonblocking until the last non-empty line of the output
818 matches one of the patterns (using match_patterns), or until timeout
819 expires. Return a tuple containing the match index (or None if no match
820 was found) and the data read so far.
821
822 @brief: Read using read_nonblocking until the last non-empty line
823 matches a pattern.
824
825 @param patterns: A list of strings (regular expression patterns)
826 @param timeout: The duration (in seconds) to wait until a match is
827 found
828 @param internal_timeout: The timeout to pass to read_nonblocking
829 @param print_func: A function to be used to print the data being read
830 (should take a string parameter)
831 """
832 def get_last_nonempty_line(str):
833 nonempty_lines = [l for l in str.splitlines() if l.strip()]
834 if nonempty_lines:
835 return nonempty_lines[-1]
836 else:
837 return ""
838
839 return self.read_until_output_matches(patterns, get_last_nonempty_line,
840 timeout, internal_timeout,
841 print_func)
842
843
844class kvm_shell_session(kvm_expect):
845 """
846 This class runs a child process in the background. It it suited for
847 processes that provide an interactive shell, such as SSH and Telnet.
848
849 It provides all services of kvm_expect and kvm_tail. In addition, it
850 provides command running services, and a utility function to test the
851 process for responsiveness.
852 """
853
854 def __init__(self, command=None, id=None, echo=False, linesep="\n",
lmr4fe15ba2009-08-13 04:11:26 +0000855 termination_func=None, termination_params=(),
856 output_func=None, output_params=(),
857 output_prefix="",
lmrbbc9dd52009-07-22 20:33:47 +0000858 prompt=r"[\#\$]\s*$", status_test_command="echo $?"):
859 """
860 Initialize the class and run command as a child process.
861
862 @param command: Command to run, or None if accessing an already running
863 server.
864 @param id: ID of an already running server, if accessing a running
865 server, or None if starting a new one.
866 @param echo: Boolean indicating whether echo should be initially
867 enabled for the pseudo terminal running the subprocess. This
868 parameter has an effect only when starting a new server.
869 @param linesep: Line separator to be appended to strings sent to the
870 child process by sendline().
871 @param termination_func: Function to call when the process exits. The
872 function must accept a single exit status parameter.
lmr4fe15ba2009-08-13 04:11:26 +0000873 @param termination_params: Parameters to send to termination_func
874 before the exit status.
lmrbbc9dd52009-07-22 20:33:47 +0000875 @param output_func: Function to call whenever a line of output is
876 available from the STDOUT or STDERR streams of the process.
877 The function must accept a single string parameter. The string
878 does not include the final newline.
lmr4fe15ba2009-08-13 04:11:26 +0000879 @param output_params: Parameters to send to output_func before the
880 output line.
lmrbbc9dd52009-07-22 20:33:47 +0000881 @param output_prefix: String to prepend to lines sent to output_func.
882 @param prompt: Regular expression describing the shell's prompt line.
883 @param status_test_command: Command to be used for getting the last
884 exit status of commands run inside the shell (used by
885 get_command_status_output() and friends).
886 """
887 # Init the superclass
888 kvm_expect.__init__(self, command, id, echo, linesep,
lmr4fe15ba2009-08-13 04:11:26 +0000889 termination_func, termination_params,
890 output_func, output_params, output_prefix)
lmrbbc9dd52009-07-22 20:33:47 +0000891
892 # Remember some attributes
893 self.prompt = prompt
894 self.status_test_command = status_test_command
895
896
897 def __getinitargs__(self):
898 return kvm_expect.__getinitargs__(self) + (self.prompt,
899 self.status_test_command)
900
901
902 def set_prompt(self, prompt):
903 """
904 Set the prompt attribute for later use by read_up_to_prompt.
905
906 @param: String that describes the prompt contents.
907 """
908 self.prompt = prompt
909
910
911 def set_status_test_command(self, status_test_command):
912 """
913 Set the command to be sent in order to get the last exit status.
914
915 @param status_test_command: Command that will be sent to get the last
916 exit status.
917 """
918 self.status_test_command = status_test_command
919
920
921 def is_responsive(self, timeout=5.0):
922 """
923 Return True if the process responds to STDIN/terminal input.
924
925 Send a newline to the child process (e.g. SSH or Telnet) and read some
926 output using read_nonblocking().
927 If all is OK, some output should be available (e.g. the shell prompt).
928 In that case return True. Otherwise return False.
929
930 @param timeout: Time duration to wait before the process is considered
931 unresponsive.
932 """
933 # Read all output that's waiting to be read, to make sure the output
934 # we read next is in response to the newline sent
935 self.read_nonblocking(timeout=0.1)
936 # Send a newline
937 self.sendline()
938 # Wait up to timeout seconds for some output from the child
939 end_time = time.time() + timeout
940 while time.time() < end_time:
941 time.sleep(0.5)
942 if self.read_nonblocking(timeout=0).strip():
943 return True
944 # No output -- report unresponsive
945 return False
946
947
948 def read_up_to_prompt(self, timeout=30.0, internal_timeout=None,
949 print_func=None):
950 """
951 Read using read_nonblocking until the last non-empty line of the output
952 matches the prompt regular expression set by set_prompt, or until
953 timeout expires.
954
955 @brief: Read using read_nonblocking until the last non-empty line
956 matches the prompt.
957
958 @param timeout: The duration (in seconds) to wait until a match is
959 found
960 @param internal_timeout: The timeout to pass to read_nonblocking
961 @param print_func: A function to be used to print the data being
962 read (should take a string parameter)
963
964 @return: A tuple containing True/False indicating whether the prompt
965 was found, and the data read so far.
966 """
967 (match, output) = self.read_until_last_line_matches([self.prompt],
968 timeout,
969 internal_timeout,
970 print_func)
971 return (match is not None, output)
972
973
974 def get_command_status_output(self, command, timeout=30.0,
975 internal_timeout=None, print_func=None):
976 """
977 Send a command and return its exit status and output.
978
979 @param command: Command to send (must not contain newline characters)
980 @param timeout: The duration (in seconds) to wait until a match is
981 found
982 @param internal_timeout: The timeout to pass to read_nonblocking
983 @param print_func: A function to be used to print the data being read
984 (should take a string parameter)
985
986 @return: A tuple (status, output) where status is the exit status or
987 None if no exit status is available (e.g. timeout elapsed), and
988 output is the output of command.
989 """
990 def remove_command_echo(str, cmd):
991 if str and str.splitlines()[0] == cmd:
992 str = "".join(str.splitlines(True)[1:])
993 return str
994
995 def remove_last_nonempty_line(str):
996 return "".join(str.rstrip().splitlines(True)[:-1])
997
998 # Print some debugging info
999 logging.debug("Sending command: %s" % command)
1000
1001 # Read everything that's waiting to be read
1002 self.read_nonblocking(0.1)
1003
1004 # Send the command and get its output
1005 self.sendline(command)
1006 (match, output) = self.read_up_to_prompt(timeout, internal_timeout,
1007 print_func)
1008 # Remove the echoed command from the output
1009 output = remove_command_echo(output, command)
1010 # If the prompt was not found, return the output so far
1011 if not match:
1012 return (None, output)
1013 # Remove the final shell prompt from the output
1014 output = remove_last_nonempty_line(output)
1015
1016 # Send the 'echo ...' command to get the last exit status
1017 self.sendline(self.status_test_command)
1018 (match, status) = self.read_up_to_prompt(10.0, internal_timeout)
1019 if not match:
1020 return (None, output)
1021 status = remove_command_echo(status, self.status_test_command)
1022 status = remove_last_nonempty_line(status)
1023 # Get the first line consisting of digits only
1024 digit_lines = [l for l in status.splitlines() if l.strip().isdigit()]
1025 if not digit_lines:
1026 return (None, output)
1027 status = int(digit_lines[0].strip())
1028
1029 # Print some debugging info
1030 if status != 0:
1031 logging.debug("Command failed; status: %d, output:%s", status,
1032 kvm_utils.format_str_for_message(output.strip()))
1033
1034 return (status, output)
1035
1036
1037 def get_command_status(self, command, timeout=30.0, internal_timeout=None,
1038 print_func=None):
1039 """
1040 Send a command and return its exit status.
1041
1042 @param command: Command to send
1043 @param timeout: The duration (in seconds) to wait until a match is
1044 found
1045 @param internal_timeout: The timeout to pass to read_nonblocking
1046 @param print_func: A function to be used to print the data being read
1047 (should take a string parameter)
1048
1049 @return: Exit status or None if no exit status is available (e.g.
1050 timeout elapsed).
1051 """
1052 (status, output) = self.get_command_status_output(command, timeout,
1053 internal_timeout,
1054 print_func)
1055 return status
1056
1057
1058 def get_command_output(self, command, timeout=30.0, internal_timeout=None,
1059 print_func=None):
1060 """
1061 Send a command and return its output.
1062
1063 @param command: Command to send
1064 @param timeout: The duration (in seconds) to wait until a match is
1065 found
1066 @param internal_timeout: The timeout to pass to read_nonblocking
1067 @param print_func: A function to be used to print the data being read
1068 (should take a string parameter)
1069 """
1070 (status, output) = self.get_command_status_output(command, timeout,
1071 internal_timeout,
1072 print_func)
1073 return output
1074
1075
1076# The following is the server part of the module.
1077
1078def _server_main():
1079 id = sys.stdin.readline().strip()
1080 echo = sys.stdin.readline().strip() == "True"
1081 readers = sys.stdin.readline().strip().split(",")
1082 command = sys.stdin.readline().strip() + " && echo %s > /dev/null" % id
1083
1084 # Define filenames to be used for communication
1085 base_dir = "/tmp/kvm_spawn"
1086 (shell_pid_filename,
1087 status_filename,
1088 output_filename,
1089 inpipe_filename,
1090 lock_server_running_filename,
1091 lock_client_starting_filename) = _get_filenames(base_dir, id)
1092
1093 # Populate the reader filenames list
1094 reader_filenames = [_get_reader_filename(base_dir, id, reader)
1095 for reader in readers]
1096
1097 # Set $TERM = dumb
1098 os.putenv("TERM", "dumb")
1099
1100 (shell_pid, shell_fd) = pty.fork()
1101 if shell_pid == 0:
1102 # Child process: run the command in a subshell
1103 os.execv("/bin/sh", ["/bin/sh", "-c", command])
1104 else:
1105 # Parent process
1106 lock_server_running = _lock(lock_server_running_filename)
1107
1108 # Set terminal echo on/off and disable pre- and post-processing
1109 attr = termios.tcgetattr(shell_fd)
1110 attr[0] &= ~termios.INLCR
1111 attr[0] &= ~termios.ICRNL
1112 attr[0] &= ~termios.IGNCR
1113 attr[1] &= ~termios.OPOST
1114 if echo:
1115 attr[3] |= termios.ECHO
1116 else:
1117 attr[3] &= ~termios.ECHO
1118 termios.tcsetattr(shell_fd, termios.TCSANOW, attr)
1119
1120 # Open output file
1121 output_file = open(output_filename, "w")
1122 # Open input pipe
1123 os.mkfifo(inpipe_filename)
1124 inpipe_fd = os.open(inpipe_filename, os.O_RDWR)
1125 # Open output pipes (readers)
1126 reader_fds = []
1127 for filename in reader_filenames:
1128 os.mkfifo(filename)
1129 reader_fds.append(os.open(filename, os.O_RDWR))
1130
1131 # Write shell PID to file
1132 file = open(shell_pid_filename, "w")
1133 file.write(str(shell_pid))
1134 file.close()
1135
1136 # Print something to stdout so the client can start working
lmrb8f53d62009-07-27 13:29:17 +00001137 print "Server %s ready" % id
lmrbbc9dd52009-07-22 20:33:47 +00001138 sys.stdout.flush()
1139
1140 # Initialize buffers
1141 buffers = ["" for reader in readers]
1142
1143 # Read from child and write to files/pipes
1144 while True:
1145 # Make a list of reader pipes whose buffers are not empty
1146 fds = [fd for (i, fd) in enumerate(reader_fds) if buffers[i]]
1147 # Wait until there's something to do
1148 r, w, x = select.select([shell_fd, inpipe_fd], fds, [])
1149 # If a reader pipe is ready for writing --
1150 for (i, fd) in enumerate(reader_fds):
1151 if fd in w:
1152 bytes_written = os.write(fd, buffers[i])
1153 buffers[i] = buffers[i][bytes_written:]
1154 # If there's data to read from the child process --
1155 if shell_fd in r:
1156 try:
1157 data = os.read(shell_fd, 16384)
1158 except OSError:
1159 break
1160 # Remove carriage returns from the data -- they often cause
1161 # trouble and are normally not needed
1162 data = data.replace("\r", "")
1163 output_file.write(data)
1164 output_file.flush()
1165 for i in range(len(readers)):
1166 buffers[i] += data
1167 # If there's data to read from the client --
1168 if inpipe_fd in r:
1169 data = os.read(inpipe_fd, 1024)
1170 os.write(shell_fd, data)
1171
1172 # Wait for the shell process to exit and get its exit status
1173 status = os.waitpid(shell_pid, 0)[1]
1174 status = os.WEXITSTATUS(status)
1175 file = open(status_filename, "w")
1176 file.write(str(status))
1177 file.close()
1178
1179 # Wait for the client to finish initializing
1180 _wait(lock_client_starting_filename)
1181
1182 # Delete FIFOs
1183 for filename in reader_filenames + [inpipe_filename]:
1184 try:
1185 os.unlink(filename)
1186 except OSError:
1187 pass
1188
1189 # Close all files and pipes
1190 output_file.close()
1191 os.close(inpipe_fd)
1192 for fd in reader_fds:
1193 os.close(fd)
1194
1195 _unlock(lock_server_running)
1196
1197
1198if __name__ == "__main__":
1199 _server_main()