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 5import common, logging, os, time 6 7from autotest_lib.client.common_lib import error 8from autotest_lib.client.common_lib import utils 9from 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. 13UI_RESTART_ATTEMPT_MSG = 'cros_ui.py: Attempting StopSession...' 14UI_RESTART_COMPLETE_MSG = 'cros_ui.py: StopSession complete.' 15RESTART_UI_TIMEOUT = 90 # longer because we may be crash dumping now. 16 17 18def 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 39def 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 67def 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 94def stop(allow_fail=False): 95 return utils.stop_service("ui", ignore_status=allow_fail) 96 97 98def 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 109def 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 139def nuke(): 140 """Nuke the login manager, waiting for it to restart.""" 141 restart(lambda: utils.nuke_process_by_name(constants.SESSION_MANAGER)) 142 143 144def is_up(): 145 """Return True if the UI is up, False if not.""" 146 return utils.get_service_pid('ui')!=0 147 148 149def 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