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