Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(14)

Side by Side Diff: remoting/tools/remoting.py

Issue 8511029: Initial check-in of Linux virtual Me2Me. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Add remoting_me2me_host, and use that instead of remoting_simple_host. Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« remoting/remoting.gyp ('K') | « remoting/remoting.gyp ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
Wez 2011/11/11 01:32:32 Add a description of what this script is for.
Wez 2011/11/11 01:32:32 I think the script name should describe what it do
Lambros 2011/11/17 01:07:05 Done.
Lambros 2011/11/17 01:07:05 Done.
6 import atexit
7 import getpass
8 import json
9 import logging
10 import os
11 import random
12 import signal
13 import socket
14 import subprocess
15 import sys
16 import time
17 import urllib2
18
19 # Local modules
20 import gaia_auth
21 import keygen
22
23 REMOTING_COMMAND = ["../../out/Debug/remoting_me2me_host"]
Sergey Ulanov 2011/11/11 05:33:02 Can we detect this path similar to how keygen modu
Lambros 2011/11/17 01:07:05 Done.
24
25 CONFIG_DIR = os.path.expanduser("~/.config/chrome-remote-desktop")
26
27 X_LOCK_FILE_TEMPLATE = "/tmp/.X%d-lock"
28 FIRST_X_DISPLAY_NUMBER = 20
29
30 X_AUTH_FILE = os.path.expanduser("~/.Xauthority")
31 os.environ["XAUTHORITY"] = X_AUTH_FILE
32
33 g_desktops = []
34
35 class Authentication:
36 """Manage authentication tokens for Chromoting/xmpp"""
37 def __init__(self, config_file):
38 self.config_file = config_file
39
40 def refresh_tokens(self):
41 print "Email:",
42 self.login = raw_input()
43 password = getpass.getpass("Password: ")
44
45 chromoting_auth = gaia_auth.GaiaAuthenticator('chromoting')
46 self.chromoting_auth_token = chromoting_auth.authenticate(self.login,
47 password)
48
49 xmpp_authenticator = gaia_auth.GaiaAuthenticator('chromiumsync')
50 self.xmpp_auth_token = xmpp_authenticator.authenticate(self.login,
51 password)
52
53 def load_config(self):
Wez 2011/11/11 01:32:32 nit: Why load/write_config rather than read/write_
Lambros 2011/11/17 01:07:05 Done (load/save).
54 try:
55 settings_file = open(self.config_file, 'r')
56 data = json.load(settings_file)
57 settings_file.close()
58 self.login = data["xmpp_login"]
59 self.chromoting_auth_token = data["chromoting_auth_token"]
60 self.xmpp_auth_token = data["xmpp_auth_token"]
61 except:
62 return False
63 return True
64
65 def write_config(self):
66 data = {
67 "xmpp_login": self.login,
68 "chromoting_auth_token": self.chromoting_auth_token,
69 "xmpp_auth_token": self.xmpp_auth_token,
70 }
71 os.umask(0066) # Set permission mask for created file.
Wez 2011/11/11 01:32:32 Does this fail with an error return code, or an ex
Lambros 2011/11/17 01:07:05 umask() cannot fail, but the open/write/close call
72 settings_file = open(self.config_file, 'w')
73 settings_file.write(json.dumps(data, indent=2))
74 settings_file.close()
75
76
77 class Host:
78 """Create a new, or manage an existing, host registration."""
Wez 2011/11/11 01:32:32 Surely this object effectively _is_ a virtual Me2M
Lambros 2011/11/17 01:07:05 Reworded comment.
79
80 server = 'www.googleapis.com'
81 url = 'https://' + server + '/chromoting/v1/@me/hosts'
82
83 def __init__(self, config_file):
84 self.config_file = config_file
85
86 @staticmethod
87 def random_uuid():
88 return ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x" %
89 tuple(map(lambda x: random.randrange(0, 0x10000), range(8))))
Wez 2011/11/11 01:32:32 Replace this with uuid.uuid1()?
Lambros 2011/11/17 01:07:05 Done.
90
91 def register_new(self, auth):
Wez 2011/11/11 01:32:32 Split this method down into generate_config(), reg
Wez 2011/11/11 01:32:32 Rename this method create_host() or create_config(
Lambros 2011/11/17 01:07:05 Done.
92 self.host_id = self.random_uuid()
93 logging.info("HostId: " + self.host_id)
94 self.host_name = socket.gethostname()
95 logging.info("HostName: " + self.host_name)
96
97 logging.info("Generating RSA key pair...")
98 (self.private_key, public_key) = keygen.generateRSAKeyPair()
99 logging.info("Done")
100
101 json_data = {
102 "data": {
103 "hostId": self.host_id,
104 "hostName": self.host_name,
105 "publicKey": public_key,
106 }
107 }
108 params = json.dumps(json_data)
109 headers = {
110 "Authorization": "GoogleLogin auth=" + auth.chromoting_auth_token,
111 "Content-Type": "application/json",
112 }
113
114 request = urllib2.Request(self.url, params, headers)
115 opener = urllib2.OpenerDirector()
116 opener.add_handler(urllib2.HTTPDefaultErrorHandler())
117
118 logging.info("Registering host with directory service...")
119 try:
120 res = urllib2.urlopen(request)
121 data = res.read()
122 except urllib2.HTTPError, err:
123 logging.error("Directory returned error: " + str(err))
124 logging.error(err.read())
125 sys.exit(1)
126 logging.info("Done")
127
128 def load_config(self):
Wez 2011/11/11 01:32:32 load vs read / save vs write
Lambros 2011/11/17 01:07:05 Done.
129 try:
130 settings_file = open(self.config_file, 'r')
131 data = json.load(settings_file)
132 settings_file.close()
133 self.host_id = data["host_id"]
134 self.host_name = data["host_name"]
135 self.private_key = data["private_key"]
136 except:
137 return False
138 return True
139
140 def write_config(self):
141 data = {
142 "host_id": self.host_id,
143 "host_name": self.host_name,
144 "private_key": self.private_key,
145 }
146 os.umask(0066) # Set permission mask for created file.
147 settings_file = open(self.config_file, 'w')
148 settings_file.write(json.dumps(data, indent=2))
149 settings_file.close()
150
151
152 def cleanup():
153 logging.info("Cleanup.")
154
155 for desktop in g_desktops:
156 if desktop.x_proc:
157 logging.info("Terminating Xvfb")
158 desktop.x_proc.terminate()
159
160 def signal_handler(signum, stackframe):
161 # Exit cleanly so the atexit handler, cleanup(), gets called.
162 raise SystemExit
163
164
165 class Desktop:
166 """Manage a single virtual desktop"""
167 def __init__(self):
168 self.x_proc = None
169 g_desktops.append(self)
170
171 @staticmethod
172 def get_unused_display_number():
173 """Return a candidate display number for which there is currently no
174 X Server lock file"""
175 display = FIRST_X_DISPLAY_NUMBER
176 while os.path.exists(X_LOCK_FILE_TEMPLATE % display):
177 display += 1
178 return display
179
180 def launch_x_server(self):
181 display = self.get_unused_display_number()
182 ret_code = subprocess.call("xauth add :%d . `mcookie`" % display,
183 shell=True)
184 if ret_code != 0:
185 logging.error("xauth failed with code %d" % ret_code)
186 return 1
187 logging.info("Starting Xvfb on display :%d" % display);
188 self.x_proc = subprocess.Popen(["Xvfb", ":%d" % display,
189 "-auth", X_AUTH_FILE,
190 "-nolisten", "tcp",
191 "-screen", "0", "1024x768x24",
192 ])
193 if not self.x_proc.pid:
194 logging.info("Could not start Xvfb.")
195 return 1
196
197 # Create clean environment for new session, so it is cleanly separated from
198 # the user's console X session.
199 self.child_env = {"DISPLAY": ":%d" % display}
200 for key in ["HOME", "PATH"]:
201 self.child_env[key] = os.environ[key]
Wez 2011/11/11 01:32:32 Are these really all we need for a valid environme
Lambros 2011/11/17 01:07:05 This is essentially what GNOME Display Manager doe
202
203 # Wait for X to be active.
204 for test in range(5):
205 proc = subprocess.Popen("xdpyinfo > /dev/null", env=self.child_env,
206 shell=True)
207 pid, retcode = os.waitpid(proc.pid, 0)
208 if retcode == 0:
209 break
210 time.sleep(0.5)
211 if retcode != 0:
212 logging.info("Could not connect to Xvfb.")
213 return 1
214 else:
215 logging.info("Xvfb is active.")
216
217 def launch_x_session(self):
218 # Start desktop session
219 session_proc = subprocess.Popen("/etc/X11/Xsession",
220 cwd=os.environ["HOME"],
221 env=self.child_env)
222 if not session_proc.pid:
223 logging.info("Could not start X session")
224 return 1
225
226 def launch_host(self):
227 # Start remoting host
228 self.host_proc = subprocess.Popen(REMOTING_COMMAND, env=self.child_env)
229 if not self.host_proc.pid:
230 logging.info("Could not start remoting host")
231 return 1
232
233
234 def main():
235 atexit.register(cleanup)
Wez 2011/11/11 01:32:32 This means at present that killing the script will
Lambros 2011/11/17 01:07:05 Future CL - a bit complex to include in here. Not
Wez 2011/11/17 02:19:28 That page is really talking about how self-daemoni
236
237 for s in (
238 signal.SIGINT,
239 signal.SIGTERM,
240 ):
Wez 2011/11/11 01:32:32 Could this be a single line? It looks strange lai
Lambros 2011/11/17 01:07:05 Done. I think that list is exhaustive. Trapping
241 signal.signal(s, signal_handler)
242
243 # Ensure full path to config directory exists.
244 if not os.path.exists(CONFIG_DIR):
245 os.makedirs(CONFIG_DIR, mode=0700)
246
247 auth = Authentication(os.path.join(CONFIG_DIR, "auth.json"))
248 if not auth.load_config():
249 auth.refresh_tokens()
250 auth.write_config()
251
252 host = Host(os.path.join(CONFIG_DIR, "host.json"))
253
254 if not host.load_config():
255 host.register_new(auth)
256 host.write_config()
Wez 2011/11/11 01:32:32 Support for multiple Host configurations, and re-s
Lambros 2011/11/17 01:07:05 I think so.
257
258 logging.info("Using host_id: " + host.host_id)
259
260 desktop = Desktop()
261 desktop.launch_x_server()
262 desktop.launch_x_session()
263 desktop.launch_host()
Wez 2011/11/11 01:32:32 If one of these fails, it looks like you'll get er
Lambros 2011/11/17 01:07:05 Turned these into Exceptions, which will kill the
264
265 while True:
266 pid, status = os.wait()
267 logging.info("wait() returned (%s,%s)" % (pid, status))
268
269 if pid == desktop.x_proc.pid:
270 logging.info("X server process terminated with code %d", status)
271 break
272
273 if pid == desktop.host_proc.pid:
274 logging.info("Host process terminated, relaunching")
275 desktop.launch_host()
276
277 if __name__ == "__main__":
278 logging.basicConfig(level=logging.DEBUG)
279 sys.exit(main())
OLDNEW
« remoting/remoting.gyp ('K') | « remoting/remoting.gyp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698