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. |
| 11 | 11 |
| 12 import atexit | 12 import atexit |
| 13 import base64 | |
| 13 import getpass | 14 import getpass |
| 14 import hashlib | 15 import hashlib |
| 16 import hmac | |
| 15 import json | 17 import json |
| 16 import logging | 18 import logging |
| 17 import optparse | 19 import optparse |
| 18 import os | 20 import os |
| 19 import random | 21 import random |
| 20 import signal | 22 import signal |
| 21 import socket | 23 import socket |
| 22 import subprocess | 24 import subprocess |
| 23 import sys | 25 import sys |
| 24 import time | 26 import time |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 108 settings_file.close() | 110 settings_file.close() |
| 109 os.umask(old_umask) | 111 os.umask(old_umask) |
| 110 | 112 |
| 111 | 113 |
| 112 class Host: | 114 class Host: |
| 113 """This manages the configuration for a host. | 115 """This manages the configuration for a host. |
| 114 | 116 |
| 115 Callers should instantiate a Host object (passing in a filename where the | 117 Callers should instantiate a Host object (passing in a filename where the |
| 116 config will be kept), then should call either of the methods: | 118 config will be kept), then should call either of the methods: |
| 117 | 119 |
| 118 * create_config(auth): Create a new Host configuration and register it with | 120 * register(auth): Create a new Host configuration and register it |
| 119 the Directory Service (the "auth" parameter is used to authenticate with the | 121 with the Directory Service (the "auth" parameter is used to |
| 120 Service). | 122 authenticate with the Service). |
| 121 * load_config(): Load a config from disk, with details of an existing Host | 123 * load_config(): Load a config from disk, with details of an existing Host |
| 122 registration. | 124 registration. |
| 123 | 125 |
| 124 After calling create_config() (or making any config changes) the method | 126 After calling register() (or making any config changes) the method |
| 125 save_config() should be called to save the details to disk. | 127 save_config() should be called to save the details to disk. |
| 126 """ | 128 """ |
| 127 | 129 |
| 128 server = 'www.googleapis.com' | 130 server = 'www.googleapis.com' |
| 129 url = 'https://' + server + '/chromoting/v1/@me/hosts' | 131 url = 'https://' + server + '/chromoting/v1/@me/hosts' |
| 130 | 132 |
| 131 def __init__(self, config_file): | 133 def __init__(self, config_file): |
| 132 self.config_file = config_file | 134 self.config_file = config_file |
| 135 self.host_id = str(uuid.uuid1()) | |
| 136 self.host_name = socket.gethostname() | |
| 137 self.host_secret_hash = None | |
| 138 self.private_key = None | |
| 133 | 139 |
| 134 def create_config(self, auth): | 140 def register(self, auth): |
| 135 self.host_id = str(uuid.uuid1()) | |
| 136 logging.info("HostId: " + self.host_id) | 141 logging.info("HostId: " + self.host_id) |
| 137 self.host_name = socket.gethostname() | |
| 138 logging.info("HostName: " + self.host_name) | 142 logging.info("HostName: " + self.host_name) |
| 139 | 143 |
| 140 logging.info("Generating RSA key pair...") | 144 logging.info("Generating RSA key pair...") |
| 141 (self.private_key, public_key) = keygen.generateRSAKeyPair() | 145 (self.private_key, public_key) = keygen.generateRSAKeyPair() |
| 142 logging.info("Done") | 146 logging.info("Done") |
| 143 | 147 |
| 144 json_data = { | 148 json_data = { |
| 145 "data": { | 149 "data": { |
| 146 "hostId": self.host_id, | 150 "hostId": self.host_id, |
| 147 "hostName": self.host_name, | 151 "hostName": self.host_name, |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 161 logging.info("Registering host with directory service...") | 165 logging.info("Registering host with directory service...") |
| 162 try: | 166 try: |
| 163 res = urllib2.urlopen(request) | 167 res = urllib2.urlopen(request) |
| 164 data = res.read() | 168 data = res.read() |
| 165 except urllib2.HTTPError, err: | 169 except urllib2.HTTPError, err: |
| 166 logging.error("Directory returned error: " + str(err)) | 170 logging.error("Directory returned error: " + str(err)) |
| 167 logging.error(err.read()) | 171 logging.error(err.read()) |
| 168 sys.exit(1) | 172 sys.exit(1) |
| 169 logging.info("Done") | 173 logging.info("Done") |
| 170 | 174 |
| 175 def ask_pin(self): | |
| 176 pin = getpass.getpass("Host PIN (can be empty): ") | |
|
Wez
2012/01/23 23:53:49
nit: Verify that the PIN is either empty or at lea
Sergey Ulanov
2012/01/24 06:32:22
Done.
| |
| 177 self.host_secret_hash = "hmac;" + base64.b64encode( | |
| 178 hmac.new(str(self.host_id), pin, hashlib.sha256).digest()) | |
|
Wez
2012/01/23 23:53:49
nit: It will make changes to inform the client of
Sergey Ulanov
2012/01/24 06:32:22
Done.
| |
| 179 | |
| 180 def ask_pin_if_unknown(self): | |
| 181 if not self.host_secret_hash: | |
| 182 self.ask_pin() | |
| 183 return True | |
| 184 return False | |
| 185 | |
| 171 def load_config(self): | 186 def load_config(self): |
| 172 try: | 187 try: |
| 173 settings_file = open(self.config_file, 'r') | 188 settings_file = open(self.config_file, 'r') |
| 174 data = json.load(settings_file) | 189 data = json.load(settings_file) |
| 175 settings_file.close() | 190 settings_file.close() |
| 176 self.host_id = data["host_id"] | |
| 177 self.host_name = data["host_name"] | |
| 178 self.private_key = data["private_key"] | |
| 179 except: | 191 except: |
| 192 logging.info("Failed to load: " + self.config_file) | |
| 180 return False | 193 return False |
| 194 self.host_id = data["host_id"] | |
| 195 self.host_name = data["host_name"] | |
| 196 self.host_secret_hash = data.get("host_secret_hash") | |
| 197 self.private_key = data["private_key"] | |
| 181 return True | 198 return True |
| 182 | 199 |
| 183 def save_config(self): | 200 def save_config(self): |
| 184 data = { | 201 data = { |
| 185 "host_id": self.host_id, | 202 "host_id": self.host_id, |
| 186 "host_name": self.host_name, | 203 "host_name": self.host_name, |
| 204 "host_secret_hash": self.host_secret_hash, | |
| 187 "private_key": self.private_key, | 205 "private_key": self.private_key, |
| 188 } | 206 } |
| 189 old_umask = os.umask(0066) | 207 old_umask = os.umask(0066) |
| 190 settings_file = open(self.config_file, 'w') | 208 settings_file = open(self.config_file, 'w') |
| 191 settings_file.write(json.dumps(data, indent=2)) | 209 settings_file.write(json.dumps(data, indent=2)) |
| 192 settings_file.close() | 210 settings_file.close() |
| 193 os.umask(old_umask) | 211 os.umask(old_umask) |
| 194 | 212 |
| 195 | 213 |
| 196 class Desktop: | 214 class Desktop: |
| (...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 496 try: | 514 try: |
| 497 auth.refresh_tokens() | 515 auth.refresh_tokens() |
| 498 except: | 516 except: |
| 499 logging.error("Authentication failed.") | 517 logging.error("Authentication failed.") |
| 500 return 1 | 518 return 1 |
| 501 auth.save_config() | 519 auth.save_config() |
| 502 | 520 |
| 503 host = Host(os.path.join(CONFIG_DIR, "host#%s.json" % host_hash)) | 521 host = Host(os.path.join(CONFIG_DIR, "host#%s.json" % host_hash)) |
| 504 | 522 |
| 505 if not host.load_config(): | 523 if not host.load_config(): |
| 506 host.create_config(auth) | 524 host.ask_pin() |
| 525 host.register(auth) | |
| 526 host.save_config() | |
| 527 | |
| 528 if host.ask_pin_if_unknown(): | |
| 507 host.save_config() | 529 host.save_config() |
| 508 | 530 |
| 509 global g_pidfile | 531 global g_pidfile |
| 510 g_pidfile = PidFile(pid_filename) | 532 g_pidfile = PidFile(pid_filename) |
| 511 running, pid = g_pidfile.check() | 533 running, pid = g_pidfile.check() |
| 512 | 534 |
| 513 if running: | 535 if running: |
| 514 if pid == 0: | 536 if pid == 0: |
| 515 pid = 'unknown' | 537 pid = 'unknown' |
| 516 | 538 |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 588 desktop.session_proc = None | 610 desktop.session_proc = None |
| 589 | 611 |
| 590 if desktop.host_proc is not None and pid == desktop.host_proc.pid: | 612 if desktop.host_proc is not None and pid == desktop.host_proc.pid: |
| 591 logging.info("Host process terminated") | 613 logging.info("Host process terminated") |
| 592 desktop.host_proc = None | 614 desktop.host_proc = None |
| 593 | 615 |
| 594 | 616 |
| 595 if __name__ == "__main__": | 617 if __name__ == "__main__": |
| 596 logging.basicConfig(level=logging.DEBUG) | 618 logging.basicConfig(level=logging.DEBUG) |
| 597 sys.exit(main()) | 619 sys.exit(main()) |
| OLD | NEW |