15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/python
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2011 The Chromium Authors. All rights reserved.
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import signal
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import subprocess
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import time
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class BrowserProcessBase(object):
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, handle):
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.handle = handle
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print 'PID', self.handle.pid
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def GetReturnCode(self):
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self.handle.returncode
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def IsRunning(self):
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self.handle.poll() is None
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Wait(self, wait_steps, sleep_time):
243551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    try:
253551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      self.term()
263551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    except Exception:
273551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      # Terminating the handle can raise an exception. There is likely no point
283551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      # in waiting if the termination didn't succeed.
293551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      return
303551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    i = 0
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # subprocess.wait() doesn't have a timeout, unfortunately.
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while self.IsRunning() and i < wait_steps:
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      time.sleep(sleep_time)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      i += 1
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Kill(self):
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if self.IsRunning():
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print 'KILLING the browser'
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      try:
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.kill()
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # If it doesn't die, we hang.  Oh well.
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        self.handle.wait()
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      except Exception:
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # If it is already dead, then it's ok.
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # This may happen if the browser dies after the first poll, but
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # before the kill.
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if self.IsRunning():
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          raise
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class BrowserProcess(BrowserProcessBase):
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def term(self):
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.handle.terminate()
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def kill(self):
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self.handle.kill()
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class BrowserProcessPosix(BrowserProcessBase):
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """ This variant of BrowserProcess uses process groups to manage browser
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  life time. """
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def term(self):
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    os.killpg(self.handle.pid, signal.SIGTERM)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def kill(self):
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    os.killpg(self.handle.pid, signal.SIGKILL)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def RunCommandWithSubprocess(cmd, env=None):
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  handle = subprocess.Popen(cmd, env=env)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return BrowserProcess(handle)
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def RunCommandInProcessGroup(cmd, env=None):
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def SetPGrp():
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    os.setpgrp()
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print 'I\'M THE SESSION LEADER!'
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  handle = subprocess.Popen(cmd, env=env, preexec_fn=SetPGrp)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return BrowserProcessPosix(handle)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
83