| 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 26 matching lines...) Expand all Loading... |
| 37 # By default this script will try to determine the most appropriate X session | 37 # By default this script will try to determine the most appropriate X session |
| 38 # command for the system. To use a specific session instead, set this variable | 38 # command for the system. To use a specific session instead, set this variable |
| 39 # to the executable filename, or a list containing the executable and any | 39 # to the executable filename, or a list containing the executable and any |
| 40 # arguments, for example: | 40 # arguments, for example: |
| 41 # XSESSION_COMMAND = "/usr/bin/gnome-session-fallback" | 41 # XSESSION_COMMAND = "/usr/bin/gnome-session-fallback" |
| 42 # XSESSION_COMMAND = ["/usr/bin/gnome-session", "--session=ubuntu-2d"] | 42 # XSESSION_COMMAND = ["/usr/bin/gnome-session", "--session=ubuntu-2d"] |
| 43 XSESSION_COMMAND = None | 43 XSESSION_COMMAND = None |
| 44 | 44 |
| 45 REMOTING_COMMAND = "remoting_me2me_host" | 45 REMOTING_COMMAND = "remoting_me2me_host" |
| 46 | 46 |
| 47 # Command-line switch for passing the config path to remoting_me2me_host. | |
| 48 HOST_CONFIG_SWITCH_NAME = "host-config" | |
| 49 | |
| 50 # Needs to be an absolute path, since the current working directory is changed | 47 # Needs to be an absolute path, since the current working directory is changed |
| 51 # when this process self-daemonizes. | 48 # when this process self-daemonizes. |
| 52 SCRIPT_PATH = os.path.dirname(sys.argv[0]) | 49 SCRIPT_PATH = os.path.dirname(sys.argv[0]) |
| 53 if SCRIPT_PATH: | 50 if SCRIPT_PATH: |
| 54 SCRIPT_PATH = os.path.abspath(SCRIPT_PATH) | 51 SCRIPT_PATH = os.path.abspath(SCRIPT_PATH) |
| 55 else: | 52 else: |
| 56 SCRIPT_PATH = os.getcwd() | 53 SCRIPT_PATH = os.getcwd() |
| 57 | 54 |
| 58 # These are relative to SCRIPT_PATH. | 55 # These are relative to SCRIPT_PATH. |
| 59 EXE_PATHS_TO_TRY = [ | 56 EXE_PATHS_TO_TRY = [ |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 141 * load_config(): Load a config from disk, with details of an existing Host | 138 * load_config(): Load a config from disk, with details of an existing Host |
| 142 registration. | 139 registration. |
| 143 | 140 |
| 144 After calling register() (or making any config changes) the method | 141 After calling register() (or making any config changes) the method |
| 145 save_config() should be called to save the details to disk. | 142 save_config() should be called to save the details to disk. |
| 146 """ | 143 """ |
| 147 | 144 |
| 148 server = 'www.googleapis.com' | 145 server = 'www.googleapis.com' |
| 149 url = 'https://' + server + '/chromoting/v1/@me/hosts' | 146 url = 'https://' + server + '/chromoting/v1/@me/hosts' |
| 150 | 147 |
| 151 def __init__(self, config_file): | 148 def __init__(self, config_file, auth): |
| 149 """ |
| 150 Args: |
| 151 config_file: Host configuration file path |
| 152 auth: Authentication object with credentials for authenticating with the |
| 153 Directory service. |
| 154 """ |
| 152 self.config_file = config_file | 155 self.config_file = config_file |
| 156 self.auth = auth |
| 153 self.host_id = str(uuid.uuid1()) | 157 self.host_id = str(uuid.uuid1()) |
| 154 self.host_name = socket.gethostname() | 158 self.host_name = socket.gethostname() |
| 155 self.host_secret_hash = None | 159 self.host_secret_hash = None |
| 156 self.private_key = None | 160 self.private_key = None |
| 157 | 161 |
| 158 def register(self, auth): | 162 def register(self): |
| 159 """Generates a private key for the stored |host_id|, and registers it with | 163 """Generates a private key for the stored |host_id|, and registers it with |
| 160 the Directory service. | 164 the Directory service. |
| 161 | 165 |
| 162 Args: | |
| 163 auth: Authentication object with credentials for authenticating with the | |
| 164 Directory service. | |
| 165 | |
| 166 Raises: | 166 Raises: |
| 167 urllib2.HTTPError: An error occurred talking to the Directory server | 167 urllib2.HTTPError: An error occurred talking to the Directory server |
| 168 (for example, if the |auth| credentials were rejected). | 168 (for example, if the |auth| credentials were rejected). |
| 169 """ | 169 """ |
| 170 | 170 |
| 171 logging.info("HostId: " + self.host_id) | 171 logging.info("HostId: " + self.host_id) |
| 172 logging.info("HostName: " + self.host_name) | 172 logging.info("HostName: " + self.host_name) |
| 173 | 173 |
| 174 logging.info("Generating RSA key pair...") | 174 logging.info("Generating RSA key pair...") |
| 175 (self.private_key, public_key) = keygen.generateRSAKeyPair() | 175 (self.private_key, public_key) = keygen.generateRSAKeyPair() |
| 176 logging.info("Done") | 176 logging.info("Done") |
| 177 | 177 |
| 178 json_data = { | 178 json_data = { |
| 179 "data": { | 179 "data": { |
| 180 "hostId": self.host_id, | 180 "hostId": self.host_id, |
| 181 "hostName": self.host_name, | 181 "hostName": self.host_name, |
| 182 "publicKey": public_key, | 182 "publicKey": public_key, |
| 183 } | 183 } |
| 184 } | 184 } |
| 185 params = json.dumps(json_data) | 185 params = json.dumps(json_data) |
| 186 headers = { | 186 headers = { |
| 187 "Authorization": "GoogleLogin auth=" + auth.chromoting_auth_token, | 187 "Authorization": "GoogleLogin auth=" + self.auth.chromoting_auth_token, |
| 188 "Content-Type": "application/json", | 188 "Content-Type": "application/json", |
| 189 } | 189 } |
| 190 | 190 |
| 191 request = urllib2.Request(self.url, params, headers) | 191 request = urllib2.Request(self.url, params, headers) |
| 192 opener = urllib2.OpenerDirector() | 192 opener = urllib2.OpenerDirector() |
| 193 opener.add_handler(urllib2.HTTPDefaultErrorHandler()) | 193 opener.add_handler(urllib2.HTTPDefaultErrorHandler()) |
| 194 | 194 |
| 195 logging.info("Registering host with directory service...") | 195 logging.info("Registering host with directory service...") |
| 196 | 196 |
| 197 res = urllib2.urlopen(request) | 197 res = urllib2.urlopen(request) |
| (...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 376 self.session_proc = subprocess.Popen(XSESSION_COMMAND, | 376 self.session_proc = subprocess.Popen(XSESSION_COMMAND, |
| 377 stdin=open(os.devnull, "r"), | 377 stdin=open(os.devnull, "r"), |
| 378 cwd=HOME_DIR, | 378 cwd=HOME_DIR, |
| 379 env=self.child_env) | 379 env=self.child_env) |
| 380 if not self.session_proc.pid: | 380 if not self.session_proc.pid: |
| 381 raise Exception("Could not start X session") | 381 raise Exception("Could not start X session") |
| 382 | 382 |
| 383 def launch_host(self, host): | 383 def launch_host(self, host): |
| 384 # Start remoting host | 384 # Start remoting host |
| 385 args = [locate_executable(REMOTING_COMMAND), | 385 args = [locate_executable(REMOTING_COMMAND), |
| 386 "--%s=%s" % (HOST_CONFIG_SWITCH_NAME, host.config_file)] | 386 "--host_config=%s" % (host.config_file), |
| 387 "--auth_config=%s" % (host.auth.config_file)] |
| 387 self.host_proc = subprocess.Popen(args, env=self.child_env) | 388 self.host_proc = subprocess.Popen(args, env=self.child_env) |
| 388 if not self.host_proc.pid: | 389 if not self.host_proc.pid: |
| 389 raise Exception("Could not start remoting host") | 390 raise Exception("Could not start remoting host") |
| 390 | 391 |
| 391 | 392 |
| 392 class PidFile: | 393 class PidFile: |
| 393 """Class to allow creating and deleting a file which holds the PID of the | 394 """Class to allow creating and deleting a file which holds the PID of the |
| 394 running process. This is used to detect if a process is already running, and | 395 running process. This is used to detect if a process is already running, and |
| 395 inform the user of the PID. On process termination, the PID file is | 396 inform the user of the PID. On process termination, the PID file is |
| 396 deleted. | 397 deleted. |
| (...skipping 288 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 685 # Ensure full path to config directory exists. | 686 # Ensure full path to config directory exists. |
| 686 if not os.path.exists(CONFIG_DIR): | 687 if not os.path.exists(CONFIG_DIR): |
| 687 os.makedirs(CONFIG_DIR, mode=0700) | 688 os.makedirs(CONFIG_DIR, mode=0700) |
| 688 | 689 |
| 689 if options.explicit_config: | 690 if options.explicit_config: |
| 690 for file_name in ["auth.json", "host#%s.json" % host_hash]: | 691 for file_name in ["auth.json", "host#%s.json" % host_hash]: |
| 691 settings_file = open(os.path.join(CONFIG_DIR, file_name), 'w') | 692 settings_file = open(os.path.join(CONFIG_DIR, file_name), 'w') |
| 692 settings_file.write(options.explicit_config) | 693 settings_file.write(options.explicit_config) |
| 693 settings_file.close() | 694 settings_file.close() |
| 694 | 695 |
| 696 # TODO(sergeyu): Move auth parameters to the host config. |
| 695 auth = Authentication(os.path.join(CONFIG_DIR, "auth.json")) | 697 auth = Authentication(os.path.join(CONFIG_DIR, "auth.json")) |
| 696 need_auth_tokens = not auth.load_config() | 698 need_auth_tokens = not auth.load_config() |
| 697 | 699 |
| 698 host = Host(os.path.join(CONFIG_DIR, "host#%s.json" % host_hash)) | 700 host = Host(os.path.join(CONFIG_DIR, "host#%s.json" % host_hash), auth) |
| 699 register_host = not host.load_config() | 701 register_host = not host.load_config() |
| 700 | 702 |
| 701 # Outside the loop so user doesn't get asked twice. | 703 # Outside the loop so user doesn't get asked twice. |
| 702 if register_host: | 704 if register_host: |
| 703 host.ask_pin() | 705 host.ask_pin() |
| 704 elif options.new_pin or not host.is_pin_set(): | 706 elif options.new_pin or not host.is_pin_set(): |
| 705 host.ask_pin() | 707 host.ask_pin() |
| 706 host.save_config() | 708 host.save_config() |
| 707 running, pid = PidFile(pid_filename).check() | 709 running, pid = PidFile(pid_filename).check() |
| 708 if running and pid != 0: | 710 if running and pid != 0: |
| (...skipping 10 matching lines...) Expand all Loading... |
| 719 if need_auth_tokens: | 721 if need_auth_tokens: |
| 720 auth.generate_tokens() | 722 auth.generate_tokens() |
| 721 auth.save_config() | 723 auth.save_config() |
| 722 need_auth_tokens = False | 724 need_auth_tokens = False |
| 723 except Exception: | 725 except Exception: |
| 724 logging.error("Authentication failed") | 726 logging.error("Authentication failed") |
| 725 return 1 | 727 return 1 |
| 726 | 728 |
| 727 try: | 729 try: |
| 728 if register_host: | 730 if register_host: |
| 729 host.register(auth) | 731 host.register() |
| 730 host.save_config() | 732 host.save_config() |
| 731 except urllib2.HTTPError, err: | 733 except urllib2.HTTPError, err: |
| 732 if err.getcode() == 401: | 734 if err.getcode() == 401: |
| 733 # Authentication failed - re-prompt for username & password. | 735 # Authentication failed - re-prompt for username & password. |
| 734 need_auth_tokens = True | 736 need_auth_tokens = True |
| 735 continue | 737 continue |
| 736 else: | 738 else: |
| 737 # Not an authentication error. | 739 # Not an authentication error. |
| 738 logging.error("Directory returned error: " + str(err)) | 740 logging.error("Directory returned error: " + str(err)) |
| 739 logging.error(err.read()) | 741 logging.error(err.read()) |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 848 os.remove(host.config_file) | 850 os.remove(host.config_file) |
| 849 return 0 | 851 return 0 |
| 850 elif os.WEXITSTATUS(status) == 4: | 852 elif os.WEXITSTATUS(status) == 4: |
| 851 logging.info("OAuth credentials are invalid - exiting.") | 853 logging.info("OAuth credentials are invalid - exiting.") |
| 852 os.remove(auth.config_file) | 854 os.remove(auth.config_file) |
| 853 return 0 | 855 return 0 |
| 854 | 856 |
| 855 if __name__ == "__main__": | 857 if __name__ == "__main__": |
| 856 logging.basicConfig(level=logging.DEBUG) | 858 logging.basicConfig(level=logging.DEBUG) |
| 857 sys.exit(main()) | 859 sys.exit(main()) |
| OLD | NEW |