Home | History | Annotate | Download | only in cros
      1 # Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import common, logging, os, time
      6 
      7 from autotest_lib.client.common_lib import error
      8 from autotest_lib.client.common_lib import utils
      9 from autotest_lib.client.cros import constants
     10 
     11 # Log messages used to signal when we're restarting UI. Used to detect
     12 # crashes by cros_ui_test.UITest.
     13 UI_RESTART_ATTEMPT_MSG = 'cros_ui.py: Attempting StopSession...'
     14 UI_RESTART_COMPLETE_MSG = 'cros_ui.py: StopSession complete.'
     15 RESTART_UI_TIMEOUT = 90  # longer because we may be crash dumping now.
     16 
     17 
     18 def get_chrome_session_ident(host=None):
     19     """Return an identifier that changes whenever Chrome restarts.
     20 
     21     This function returns a value that is unique to the most
     22     recently started Chrome process; the returned value changes
     23     each time Chrome restarts and displays the login screen.  The
     24     change in the value can be used to detect a successful Chrome
     25     restart.
     26 
     27     Note that uniqueness is only guaranteed until the host reboots.
     28 
     29     Args:
     30         host:  If not None, a host object on which to test Chrome
     31             state, rather than running commands on the local host.
     32 
     33     """
     34     if host:
     35         return host.run(constants.LOGIN_PROMPT_STATUS_COMMAND).stdout
     36     return utils.run(constants.LOGIN_PROMPT_STATUS_COMMAND).stdout
     37 
     38 
     39 def wait_for_chrome_ready(old_session, host=None,
     40                           timeout=RESTART_UI_TIMEOUT):
     41     """Wait until a new Chrome login prompt is on screen and ready.
     42 
     43     The standard formula to check whether the prompt has appeared yet
     44     is with a pattern like the following:
     45 
     46        session = get_chrome_session_ident()
     47        logout()
     48        wait_for_chrome_ready(session)
     49 
     50     Args:
     51         old_session:  identifier for the login prompt prior to
     52             restarting Chrome.
     53         host:  If not None, a host object on which to test Chrome
     54             state, rather than running commands on the local host.
     55         timeout: float number of seconds to wait
     56 
     57     Raises:
     58         TimeoutError: Login prompt didn't get up before timeout
     59 
     60     """
     61     utils.poll_for_condition(
     62         condition=lambda: old_session != get_chrome_session_ident(host),
     63         exception=utils.TimeoutError('Timed out waiting for login prompt'),
     64         timeout=timeout, sleep_interval=1.0)
     65 
     66 
     67 def stop_and_wait_for_chrome_to_exit(timeout_secs=40):
     68     """Stops the UI and waits for chrome to exit.
     69 
     70     Stops the UI and waits for all chrome processes to exit or until
     71     timeout_secs is reached.
     72 
     73     Args:
     74         timeout_secs: float number of seconds to wait.
     75 
     76     Returns:
     77         True upon successfully stopping the UI and all chrome processes exiting.
     78         False otherwise.
     79     """
     80     status = stop(allow_fail=True)
     81     if status:
     82         logging.error('stop ui returned non-zero status: %s', status)
     83         return False
     84     start_time = time.time()
     85     while time.time() - start_time < timeout_secs:
     86         status = utils.system('pgrep chrome', ignore_status=True)
     87         if status == 1: return True
     88         time.sleep(1)
     89     logging.error('stop ui failed to stop chrome within %s seconds',
     90                   timeout_secs)
     91     return False
     92 
     93 
     94 def stop(allow_fail=False):
     95     return utils.stop_service("ui", ignore_status=allow_fail)
     96 
     97 
     98 def start(allow_fail=False, wait_for_login_prompt=True):
     99     """Start the login manager and wait for the prompt to show up."""
    100     session = get_chrome_session_ident()
    101     result = utils.start_service("ui", ignore_status=allow_fail)
    102     # If allow_fail is set, the caller might be calling us when the UI job
    103     # is already running. In that case, the above command fails.
    104     if result == 0 and wait_for_login_prompt:
    105         wait_for_chrome_ready(session)
    106     return result
    107 
    108 
    109 def restart(report_stop_failure=False):
    110     """Restart the session manager.
    111 
    112     - If the user is logged in, the session will be terminated.
    113     - If the UI is currently down, just go ahead and bring it up unless the
    114       caller has requested that a failure to stop be reported.
    115     - To ensure all processes are up and ready, this function will wait
    116       for the login prompt to show up and be marked as visible.
    117 
    118     @param report_stop_failure: False by default, set to True if you care about
    119                                 the UI being up at the time of call and
    120                                 successfully torn down by this call.
    121     """
    122     session = get_chrome_session_ident()
    123 
    124     # Log what we're about to do to /var/log/messages. Used to log crashes later
    125     # in cleanup by cros_ui_test.UITest.
    126     utils.system('logger "%s"' % UI_RESTART_ATTEMPT_MSG)
    127 
    128     try:
    129         if stop(allow_fail=not report_stop_failure) != 0:
    130             raise error.TestError('Could not stop session')
    131         start(wait_for_login_prompt=False)
    132         # Wait for login prompt to appear to indicate that all processes are
    133         # up and running again.
    134         wait_for_chrome_ready(session)
    135     finally:
    136         utils.system('logger "%s"' % UI_RESTART_COMPLETE_MSG)
    137 
    138 
    139 def nuke():
    140     """Nuke the login manager, waiting for it to restart."""
    141     restart(lambda: utils.nuke_process_by_name(constants.SESSION_MANAGER))
    142 
    143 
    144 def is_up():
    145     """Return True if the UI is up, False if not."""
    146     return utils.get_service_pid('ui')!=0
    147 
    148 
    149 def clear_respawn_state():
    150     """Removes bookkeeping related to respawning crashed UI."""
    151     for filename in [constants.UI_RESPAWN_TIMESTAMPS_FILE,
    152                      constants.UI_TOO_CRASHY_TIMESTAMPS_FILE]:
    153         try:
    154             os.unlink(filename)
    155         except OSError:
    156             pass  # It's already gone.
    157