Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 # Virtual Me2Me implementation. This script runs and manages the processes | 6 # Virtual Me2Me implementation. This script runs and manages the processes |
| 7 # required for a Virtual Me2Me desktop, which are: X server, X desktop | 7 # required for a Virtual Me2Me desktop, which are: X server, X desktop |
| 8 # session, and Host process. | 8 # session, and Host process. |
| 9 # This script is intended to run continuously as a background daemon | 9 # This script is intended to run continuously as a background daemon |
| 10 # process, running under an ordinary (non-root) user account. | 10 # process, running under an ordinary (non-root) user account. |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 67 FIRST_X_DISPLAY_NUMBER = 20 | 67 FIRST_X_DISPLAY_NUMBER = 20 |
| 68 | 68 |
| 69 X_AUTH_FILE = os.path.expanduser("~/.Xauthority") | 69 X_AUTH_FILE = os.path.expanduser("~/.Xauthority") |
| 70 os.environ["XAUTHORITY"] = X_AUTH_FILE | 70 os.environ["XAUTHORITY"] = X_AUTH_FILE |
| 71 | 71 |
| 72 | 72 |
| 73 # Globals needed by the atexit cleanup() handler. | 73 # Globals needed by the atexit cleanup() handler. |
| 74 g_desktops = [] | 74 g_desktops = [] |
| 75 g_pidfile = None | 75 g_pidfile = None |
| 76 | 76 |
| 77 class Config: | |
| 78 def __init__(self, path): | |
| 79 self.path = path | |
| 80 self.data = {} | |
| 81 self.changed = False | |
| 82 | |
| 83 def load(self): | |
| 84 try: | |
| 85 settings_file = open(self.path, 'r') | |
| 86 self.data = json.load(settings_file) | |
| 87 self.changed = False | |
| 88 settings_file.close() | |
| 89 except: | |
| 90 return False | |
| 91 return True | |
| 92 | |
| 93 def save(self): | |
| 94 if not self.changed: | |
| 95 return True | |
| 96 try: | |
| 97 old_umask = os.umask(0066) | |
| 98 settings_file = open(self.path, 'w') | |
| 99 settings_file.write(json.dumps(self.data, indent=2)) | |
| 100 settings_file.close() | |
| 101 os.umask(old_umask) | |
| 102 except: | |
| 103 return False | |
| 104 self.changed = False | |
| 105 return True | |
| 106 | |
| 107 def copy_from(self, config): | |
| 108 """ Copy all parameters from |config|. | |
| 109 Args: | |
| 110 config: Config object to copy from | |
| 111 """ | |
| 112 self.data.update(config.data) | |
| 113 self.changed = True | |
| 114 | |
| 115 def get(self, key): | |
| 116 return self.data.get(key) | |
| 117 | |
| 118 def __getitem__(self, key): | |
| 119 return self.data[key] | |
| 120 | |
| 121 def __setitem__(self, key, value): | |
| 122 self.data[key] = value | |
| 123 self.changed = True | |
| 124 | |
| 125 def __delitem__(self, key): | |
| 126 del self.data[key] | |
| 127 self.changed = True | |
| 77 | 128 |
| 78 class Authentication: | 129 class Authentication: |
| 79 """Manage authentication tokens for Chromoting/xmpp""" | 130 """Manage authentication tokens for Chromoting/xmpp""" |
| 80 | 131 |
| 81 def __init__(self, config_file): | 132 def __init__(self, config): |
| 82 self.config_file = config_file | 133 self.config = config |
| 83 | 134 |
| 84 def generate_tokens(self): | 135 def generate_tokens(self): |
| 85 """Prompt for username/password and use them to generate new authentication | 136 """Prompt for username/password and use them to generate new authentication |
| 86 tokens. | 137 tokens. |
| 87 | 138 |
| 88 Raises: | 139 Raises: |
| 89 Exception: Failed to get new authentication tokens. | 140 Exception: Failed to get new authentication tokens. |
| 90 """ | 141 """ |
| 91 print "Email:", | 142 print "Email:", |
| 92 self.login = raw_input() | 143 self.login = raw_input() |
| 93 password = getpass.getpass("App-specific password: ") | 144 password = getpass.getpass("App-specific password: ") |
| 94 | 145 |
| 95 chromoting_auth = gaia_auth.GaiaAuthenticator('chromoting') | 146 chromoting_auth = gaia_auth.GaiaAuthenticator('chromoting') |
| 96 self.chromoting_auth_token = chromoting_auth.authenticate(self.login, | 147 self.chromoting_auth_token = chromoting_auth.authenticate(self.login, |
| 97 password) | 148 password) |
| 98 | 149 |
| 99 xmpp_authenticator = gaia_auth.GaiaAuthenticator('chromiumsync') | 150 xmpp_authenticator = gaia_auth.GaiaAuthenticator('chromiumsync') |
| 100 self.xmpp_auth_token = xmpp_authenticator.authenticate(self.login, | 151 self.xmpp_auth_token = xmpp_authenticator.authenticate(self.login, |
| 101 password) | 152 password) |
| 102 | 153 |
| 103 def load_config(self): | 154 def load_config(self): |
| 104 try: | 155 try: |
| 105 settings_file = open(self.config_file, 'r') | 156 self.login = self.config["xmpp_login"] |
| 106 data = json.load(settings_file) | 157 self.chromoting_auth_token = self.config["chromoting_auth_token"] |
| 107 settings_file.close() | 158 self.xmpp_auth_token = self.config["xmpp_auth_token"] |
| 108 self.login = data["xmpp_login"] | 159 except KeyError: |
| 109 self.chromoting_auth_token = data["chromoting_auth_token"] | |
| 110 self.xmpp_auth_token = data["xmpp_auth_token"] | |
| 111 except: | |
| 112 return False | 160 return False |
| 113 return True | 161 return True |
| 114 | 162 |
| 115 def save_config(self): | 163 def save_config(self): |
| 116 data = { | 164 self.config["xmpp_login"] = self.login |
| 117 "xmpp_login": self.login, | 165 self.config["chromoting_auth_token"] = self.chromoting_auth_token |
| 118 "chromoting_auth_token": self.chromoting_auth_token, | 166 self.config["xmpp_auth_token"] = self.xmpp_auth_token |
| 119 "xmpp_auth_token": self.xmpp_auth_token, | |
| 120 } | |
| 121 # File will contain private keys, so deny read/write access to others. | |
| 122 old_umask = os.umask(0066) | |
| 123 settings_file = open(self.config_file, 'w') | |
| 124 settings_file.write(json.dumps(data, indent=2)) | |
| 125 settings_file.close() | |
| 126 os.umask(old_umask) | |
| 127 | 167 |
| 168 def clear_config(self): | |
| 169 del self.config["xmpp_login"] | |
| 170 del self.config["chromoting_auth_token"] | |
| 171 del self.config["xmpp_auth_token"] | |
| 128 | 172 |
| 129 class Host: | 173 class Host: |
| 130 """This manages the configuration for a host. | 174 """This manages the configuration for a host. |
| 131 | 175 |
| 132 Callers should instantiate a Host object (passing in a filename where the | 176 Callers should instantiate a Host object (passing in a filename where the |
| 133 config will be kept), then should call either of the methods: | 177 config will be kept), then should call either of the methods: |
| 134 | 178 |
| 135 * register(auth): Create a new Host configuration and register it | 179 * register(auth): Create a new Host configuration and register it |
| 136 with the Directory Service (the "auth" parameter is used to | 180 with the Directory Service (the "auth" parameter is used to |
| 137 authenticate with the Service). | 181 authenticate with the Service). |
| 138 * load_config(): Load a config from disk, with details of an existing Host | 182 * load_config(): Load a config from disk, with details of an existing Host |
| 139 registration. | 183 registration. |
| 140 | 184 |
| 141 After calling register() (or making any config changes) the method | 185 After calling register() (or making any config changes) the method |
| 142 save_config() should be called to save the details to disk. | 186 save_config() should be called to save the details to disk. |
| 143 """ | 187 """ |
| 144 | 188 |
| 145 server = 'www.googleapis.com' | 189 server = 'www.googleapis.com' |
| 146 url = 'https://' + server + '/chromoting/v1/@me/hosts' | 190 url = 'https://' + server + '/chromoting/v1/@me/hosts' |
| 147 | 191 |
| 148 def __init__(self, config_file, auth): | 192 def __init__(self, config, auth): |
| 149 """ | 193 """ |
| 150 Args: | 194 Args: |
| 151 config_file: Host configuration file path | 195 config: Host configuration object |
| 152 auth: Authentication object with credentials for authenticating with the | 196 auth: Authentication object with credentials for authenticating with the |
| 153 Directory service. | 197 Directory service. |
| 154 """ | 198 """ |
| 155 self.config_file = config_file | 199 self.config = config |
| 156 self.auth = auth | 200 self.auth = auth |
| 157 self.host_id = str(uuid.uuid1()) | 201 self.host_id = str(uuid.uuid1()) |
| 158 self.host_name = socket.gethostname() | 202 self.host_name = socket.gethostname() |
| 159 self.host_secret_hash = None | 203 self.host_secret_hash = None |
| 160 self.private_key = None | 204 self.private_key = None |
| 161 | 205 |
| 162 def register(self): | 206 def register(self): |
| 163 """Generates a private key for the stored |host_id|, and registers it with | 207 """Generates a private key for the stored |host_id|, and registers it with |
| 164 the Directory service. | 208 the Directory service. |
| 165 | 209 |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 215 def set_pin(self, pin): | 259 def set_pin(self, pin): |
| 216 if pin == "": | 260 if pin == "": |
| 217 self.host_secret_hash = "plain:" | 261 self.host_secret_hash = "plain:" |
| 218 else: | 262 else: |
| 219 self.host_secret_hash = "hmac:" + base64.b64encode( | 263 self.host_secret_hash = "hmac:" + base64.b64encode( |
| 220 hmac.new(str(self.host_id), pin, hashlib.sha256).digest()) | 264 hmac.new(str(self.host_id), pin, hashlib.sha256).digest()) |
| 221 | 265 |
| 222 def is_pin_set(self): | 266 def is_pin_set(self): |
| 223 return self.host_secret_hash | 267 return self.host_secret_hash |
| 224 | 268 |
| 225 def load_config(self): | 269 def load_config(self): |
|
Lambros
2012/08/08 22:11:29
Now that load_config no longer pulls data from a f
Sergey Ulanov
2012/08/08 22:41:03
You can look at it as if self.config is just a ref
| |
| 226 try: | 270 try: |
| 227 settings_file = open(self.config_file, 'r') | 271 self.host_id = self.config["host_id"] |
| 228 data = json.load(settings_file) | 272 self.host_name = self.config["host_name"] |
| 229 settings_file.close() | 273 self.host_secret_hash = self.config.get("host_secret_hash") |
| 230 except: | 274 self.private_key = self.config["private_key"] |
| 231 logging.info("Failed to load: " + self.config_file) | 275 except KeyError: |
| 232 return False | 276 return False |
| 233 self.host_id = data["host_id"] | |
| 234 self.host_name = data["host_name"] | |
| 235 self.host_secret_hash = data.get("host_secret_hash") | |
| 236 self.private_key = data["private_key"] | |
| 237 return True | 277 return True |
| 238 | 278 |
| 239 def save_config(self): | 279 def save_config(self): |
|
Lambros
2012/08/08 22:11:29
Same applies as for load_config.
Sergey Ulanov
2012/08/08 22:41:03
Done.
| |
| 240 data = { | 280 self.config["host_id"] = self.host_id |
| 241 "host_id": self.host_id, | 281 self.config["host_name"] = self.host_name |
| 242 "host_name": self.host_name, | 282 self.config["host_secret_hash"] = self.host_secret_hash |
| 243 "host_secret_hash": self.host_secret_hash, | 283 self.config["private_key"] = self.private_key |
| 244 "private_key": self.private_key, | |
| 245 } | |
| 246 if self.host_secret_hash: | |
| 247 data["host_secret_hash"] = self.host_secret_hash | |
| 248 | 284 |
| 249 old_umask = os.umask(0066) | 285 def clear_config(self): |
|
Lambros
2012/08/08 22:11:29
Similar thing here. clear_config operates on self
Sergey Ulanov
2012/08/08 22:41:03
Moved this function to Config class.
| |
| 250 settings_file = open(self.config_file, 'w') | 286 del self.config["host_id"] |
| 251 settings_file.write(json.dumps(data, indent=2)) | 287 del self.config["host_name"] |
| 252 settings_file.close() | 288 del self.config["host_secret_hash"] |
| 253 os.umask(old_umask) | 289 del self.config["private_key"] |
| 254 | |
| 255 | 290 |
| 256 class Desktop: | 291 class Desktop: |
| 257 """Manage a single virtual desktop""" | 292 """Manage a single virtual desktop""" |
| 258 | 293 |
| 259 def __init__(self, sizes): | 294 def __init__(self, sizes): |
| 260 self.x_proc = None | 295 self.x_proc = None |
| 261 self.session_proc = None | 296 self.session_proc = None |
| 262 self.host_proc = None | 297 self.host_proc = None |
| 263 self.sizes = sizes | 298 self.sizes = sizes |
| 264 g_desktops.append(self) | 299 g_desktops.append(self) |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 376 self.session_proc = subprocess.Popen(XSESSION_COMMAND, | 411 self.session_proc = subprocess.Popen(XSESSION_COMMAND, |
| 377 stdin=open(os.devnull, "r"), | 412 stdin=open(os.devnull, "r"), |
| 378 cwd=HOME_DIR, | 413 cwd=HOME_DIR, |
| 379 env=self.child_env) | 414 env=self.child_env) |
| 380 if not self.session_proc.pid: | 415 if not self.session_proc.pid: |
| 381 raise Exception("Could not start X session") | 416 raise Exception("Could not start X session") |
| 382 | 417 |
| 383 def launch_host(self, host): | 418 def launch_host(self, host): |
| 384 # Start remoting host | 419 # Start remoting host |
| 385 args = [locate_executable(REMOTING_COMMAND), | 420 args = [locate_executable(REMOTING_COMMAND), |
| 386 "--host-config=%s" % (host.config_file)] | 421 "--host-config=%s" % (host.config.path)] |
| 387 if host.auth.config_file != host.config_file: | |
| 388 args.append("--auth-config=%s" % (host.auth.config_file)) | |
| 389 self.host_proc = subprocess.Popen(args, env=self.child_env) | 422 self.host_proc = subprocess.Popen(args, env=self.child_env) |
| 390 logging.info(args) | 423 logging.info(args) |
| 391 if not self.host_proc.pid: | 424 if not self.host_proc.pid: |
| 392 raise Exception("Could not start remoting host") | 425 raise Exception("Could not start remoting host") |
| 393 | 426 |
| 394 | 427 |
| 395 class PidFile: | 428 class PidFile: |
| 396 """Class to allow creating and deleting a file which holds the PID of the | 429 """Class to allow creating and deleting a file which holds the PID of the |
| 397 running process. This is used to detect if a process is already running, and | 430 running process. This is used to detect if a process is already running, and |
| 398 inform the user of the PID. On process termination, the PID file is | 431 inform the user of the PID. On process termination, the PID file is |
| (...skipping 296 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 695 | 728 |
| 696 atexit.register(cleanup) | 729 atexit.register(cleanup) |
| 697 | 730 |
| 698 for s in [signal.SIGHUP, signal.SIGINT, signal.SIGTERM, signal.SIGUSR1]: | 731 for s in [signal.SIGHUP, signal.SIGINT, signal.SIGTERM, signal.SIGUSR1]: |
| 699 signal.signal(s, signal_handler) | 732 signal.signal(s, signal_handler) |
| 700 | 733 |
| 701 # Ensure full path to config directory exists. | 734 # Ensure full path to config directory exists. |
| 702 if not os.path.exists(CONFIG_DIR): | 735 if not os.path.exists(CONFIG_DIR): |
| 703 os.makedirs(CONFIG_DIR, mode=0700) | 736 os.makedirs(CONFIG_DIR, mode=0700) |
| 704 | 737 |
| 705 host_config_file = os.path.join(CONFIG_DIR, "host#%s.json" % host_hash) | 738 host_config = Config(os.path.join(CONFIG_DIR, "host#%s.json" % host_hash)) |
| 739 host_config.load() | |
| 706 | 740 |
| 707 # --silent option is specified when we are started from WebApp UI. Don't use | 741 auth = Authentication(host_config) |
| 708 # separate auth file in that case. | 742 auth_config_loaded = auth.load_config() |
| 709 # TODO(sergeyu): Always use host config for auth parameters. | 743 if not auth_config_loaded and not options.silent: |
| 710 if options.silent: | 744 # If we failed to load authentication parameters from the host config |
| 711 auth_config_file = host_config_file | 745 # then try loading them from the legacy auth.json file. |
| 712 else: | 746 auth_config = Config(os.path.join(CONFIG_DIR, "auth.json")) |
| 713 auth_config_file = os.path.join(CONFIG_DIR, "auth.json") | 747 if auth_config.load(): |
| 748 host_config.copy_from(auth_config) | |
| 749 auth_config_loaded = auth.load_config() | |
| 714 | 750 |
| 715 auth = Authentication(auth_config_file) | 751 host = Host(host_config, auth) |
| 716 auth_config_loaded = auth.load_config() | |
| 717 | |
| 718 host = Host(host_config_file, auth) | |
| 719 host_config_loaded = host.load_config() | 752 host_config_loaded = host.load_config() |
| 720 | 753 |
| 721 if options.silent: | 754 if options.silent: |
| 722 if not host_config_loaded or not auth_config_loaded: | 755 if not host_config_loaded or not auth_config_loaded: |
| 723 logging.error("Failed to load host configuration.") | 756 logging.error("Failed to load host configuration.") |
| 724 return 1 | 757 return 1 |
| 725 else: | 758 else: |
| 726 need_auth_tokens = not auth_config_loaded | 759 need_auth_tokens = not auth_config_loaded |
| 727 need_register_host = not host_config_loaded | 760 need_register_host = not host_config_loaded |
| 728 # Outside the loop so user doesn't get asked twice. | 761 # Outside the loop so user doesn't get asked twice. |
| 729 if need_register_host: | 762 if need_register_host: |
| 730 host.ask_pin() | 763 host.ask_pin() |
| 731 elif options.new_pin or not host.is_pin_set(): | 764 elif options.new_pin or not host.is_pin_set(): |
| 732 host.ask_pin() | 765 host.ask_pin() |
| 733 host.save_config() | 766 host.save_config() |
| 734 running, pid = PidFile(pid_filename).check() | 767 running, pid = PidFile(pid_filename).check() |
| 735 if running and pid != 0: | 768 if running and pid != 0: |
| 736 os.kill(pid, signal.SIGUSR1) | 769 os.kill(pid, signal.SIGUSR1) |
| 737 print "The running instance has been updated with the new PIN." | 770 print "The running instance has been updated with the new PIN." |
| 738 return 0 | 771 return 0 |
| 739 | 772 |
| 740 # The loop is to deal with the case of registering a new Host with | 773 # The loop is to deal with the case of registering a new Host with |
| 741 # previously-saved auth tokens (from a previous run of this script), which | 774 # previously-saved auth tokens (from a previous run of this script), which |
| 742 # may require re-prompting for username & password. | 775 # may require re-prompting for username & password. |
| 743 while True: | 776 while True: |
| 744 try: | 777 if need_auth_tokens: |
| 745 if need_auth_tokens: | 778 try: |
| 746 auth.generate_tokens() | 779 auth.generate_tokens() |
| 747 auth.save_config() | 780 auth.save_config() |
|
Lambros
2012/08/08 22:11:29
Since save_config() no longer writes to disk, this
Sergey Ulanov
2012/08/08 22:41:03
Done.
| |
| 748 need_auth_tokens = False | 781 need_auth_tokens = False |
| 749 except Exception: | 782 except Exception: |
| 750 logging.error("Authentication failed") | 783 logging.error("Authentication failed") |
| 751 return 1 | 784 return 1 |
| 752 | 785 |
| 753 try: | 786 if need_register_host: |
| 754 if need_register_host: | 787 try: |
| 755 host.register() | 788 host.register() |
| 756 host.save_config() | 789 host.save_config() |
| 757 except urllib2.HTTPError, err: | 790 except urllib2.HTTPError, err: |
| 758 if err.getcode() == 401: | 791 if err.getcode() == 401: |
| 759 # Authentication failed - re-prompt for username & password. | 792 # Authentication failed - re-prompt for username & password. |
| 760 need_auth_tokens = True | 793 need_auth_tokens = True |
| 761 continue | 794 continue |
| 762 else: | 795 else: |
| 763 # Not an authentication error. | 796 # Not an authentication error. |
| 764 logging.error("Directory returned error: " + str(err)) | 797 logging.error("Directory returned error: " + str(err)) |
| 765 logging.error(err.read()) | 798 logging.error(err.read()) |
| 766 return 1 | 799 return 1 |
| 767 | 800 |
| 768 # |auth| and |host| are both set up, so break out of the loop. | 801 # |auth| and |host| are both set up, so break out of the loop. |
| 769 break | 802 break |
| 770 | 803 |
| 804 if not host_config.save(): | |
| 805 logging.error("Faled to save host configuration.") | |
| 806 return 1 | |
| 807 | |
| 771 global g_pidfile | 808 global g_pidfile |
| 772 g_pidfile = PidFile(pid_filename) | 809 g_pidfile = PidFile(pid_filename) |
| 773 running, pid = g_pidfile.check() | 810 running, pid = g_pidfile.check() |
| 774 | 811 |
| 775 if running: | 812 if running: |
| 776 print "An instance of this script is already running." | 813 print "An instance of this script is already running." |
| 777 print "Use the -k flag to terminate the running instance." | 814 print "Use the -k flag to terminate the running instance." |
| 778 print "If this isn't the case, delete '%s' and try again." % pid_filename | 815 print "If this isn't the case, delete '%s' and try again." % pid_filename |
| 779 return 1 | 816 return 1 |
| 780 | 817 |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 859 logging.info("Host process terminated") | 896 logging.info("Host process terminated") |
| 860 desktop.host_proc = None | 897 desktop.host_proc = None |
| 861 | 898 |
| 862 # These exit-codes must match the ones used by the host. | 899 # These exit-codes must match the ones used by the host. |
| 863 # See remoting/host/constants.h. | 900 # See remoting/host/constants.h. |
| 864 # Delete the host or auth configuration depending on the returned error | 901 # Delete the host or auth configuration depending on the returned error |
| 865 # code, so the next time this script is run, a new configuration | 902 # code, so the next time this script is run, a new configuration |
| 866 # will be created and registered. | 903 # will be created and registered. |
| 867 if os.WEXITSTATUS(status) == 2: | 904 if os.WEXITSTATUS(status) == 2: |
| 868 logging.info("Host configuration is invalid - exiting.") | 905 logging.info("Host configuration is invalid - exiting.") |
| 869 try: | 906 auth.clear_config() |
| 870 os.remove(host.config_file) | 907 host.clear_config() |
| 871 os.remove(auth.config_file) | 908 host_config.save() |
| 872 except: | |
| 873 pass | |
| 874 return 0 | 909 return 0 |
| 875 elif os.WEXITSTATUS(status) == 3: | 910 elif os.WEXITSTATUS(status) == 3: |
| 876 logging.info("Host ID has been deleted - exiting.") | 911 logging.info("Host ID has been deleted - exiting.") |
| 877 try: | 912 host.clear_config() |
| 878 os.remove(host.config_file) | 913 host_config.save() |
| 879 except: | |
| 880 pass | |
| 881 return 0 | 914 return 0 |
| 882 elif os.WEXITSTATUS(status) == 4: | 915 elif os.WEXITSTATUS(status) == 4: |
| 883 logging.info("OAuth credentials are invalid - exiting.") | 916 logging.info("OAuth credentials are invalid - exiting.") |
| 884 try: | 917 auth.clear_config() |
| 885 os.remove(auth.config_file) | 918 host_config.save() |
| 886 except: | |
| 887 pass | |
| 888 return 0 | 919 return 0 |
| 889 elif os.WEXITSTATUS(status) == 5: | 920 elif os.WEXITSTATUS(status) == 5: |
| 890 logging.info("Host domain is blocked by policy - exiting.") | 921 logging.info("Host domain is blocked by policy - exiting.") |
| 891 os.remove(host.config_file) | 922 os.remove(host.config_file) |
| 892 return 0 | 923 return 0 |
| 893 | 924 |
| 894 if __name__ == "__main__": | 925 if __name__ == "__main__": |
| 895 logging.basicConfig(level=logging.DEBUG) | 926 logging.basicConfig(level=logging.DEBUG) |
| 896 sys.exit(main()) | 927 sys.exit(main()) |
| OLD | NEW |