OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 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 getpass | 13 import getpass |
14 import hashlib | 14 import hashlib |
15 import json | 15 import json |
16 import logging | 16 import logging |
17 import optparse | |
17 import os | 18 import os |
18 import random | 19 import random |
19 import signal | 20 import signal |
20 import socket | 21 import socket |
21 import subprocess | 22 import subprocess |
22 import sys | 23 import sys |
23 import time | 24 import time |
24 import urllib2 | 25 import urllib2 |
25 import uuid | 26 import uuid |
26 | 27 |
(...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
194 logging.info("Terminating Xvfb") | 195 logging.info("Terminating Xvfb") |
195 desktop.x_proc.terminate() | 196 desktop.x_proc.terminate() |
196 | 197 |
197 def signal_handler(signum, stackframe): | 198 def signal_handler(signum, stackframe): |
198 # Exit cleanly so the atexit handler, cleanup(), gets called. | 199 # Exit cleanly so the atexit handler, cleanup(), gets called. |
199 raise SystemExit | 200 raise SystemExit |
200 | 201 |
201 | 202 |
202 class Desktop: | 203 class Desktop: |
203 """Manage a single virtual desktop""" | 204 """Manage a single virtual desktop""" |
204 def __init__(self): | 205 def __init__(self, width, height): |
205 self.x_proc = None | 206 self.x_proc = None |
207 self.width = width | |
208 self.height = height | |
206 g_desktops.append(self) | 209 g_desktops.append(self) |
207 | 210 |
208 @staticmethod | 211 @staticmethod |
209 def get_unused_display_number(): | 212 def get_unused_display_number(): |
210 """Return a candidate display number for which there is currently no | 213 """Return a candidate display number for which there is currently no |
211 X Server lock file""" | 214 X Server lock file""" |
212 display = FIRST_X_DISPLAY_NUMBER | 215 display = FIRST_X_DISPLAY_NUMBER |
213 while os.path.exists(X_LOCK_FILE_TEMPLATE % display): | 216 while os.path.exists(X_LOCK_FILE_TEMPLATE % display): |
214 display += 1 | 217 display += 1 |
215 return display | 218 return display |
216 | 219 |
217 def launch_x_server(self): | 220 def launch_x_server(self): |
218 display = self.get_unused_display_number() | 221 display = self.get_unused_display_number() |
219 ret_code = subprocess.call("xauth add :%d . `mcookie`" % display, | 222 ret_code = subprocess.call("xauth add :%d . `mcookie`" % display, |
220 shell=True) | 223 shell=True) |
221 if ret_code != 0: | 224 if ret_code != 0: |
222 raise Exception("xauth failed with code %d" % ret_code) | 225 raise Exception("xauth failed with code %d" % ret_code) |
223 | 226 |
224 logging.info("Starting Xvfb on display :%d" % display); | 227 logging.info("Starting Xvfb on display :%d" % display); |
228 screen_option = "%dx%dx24" % (self.width, self.height) | |
225 self.x_proc = subprocess.Popen(["Xvfb", ":%d" % display, | 229 self.x_proc = subprocess.Popen(["Xvfb", ":%d" % display, |
226 "-auth", X_AUTH_FILE, | 230 "-auth", X_AUTH_FILE, |
227 "-nolisten", "tcp", | 231 "-nolisten", "tcp", |
228 "-screen", "0", "1024x768x24", | 232 "-screen", "0", screen_option |
229 ]) | 233 ]) |
230 if not self.x_proc.pid: | 234 if not self.x_proc.pid: |
231 raise Exception("Could not start Xvfb.") | 235 raise Exception("Could not start Xvfb.") |
232 | 236 |
233 # Create clean environment for new session, so it is cleanly separated from | 237 # Create clean environment for new session, so it is cleanly separated from |
234 # the user's console X session. | 238 # the user's console X session. |
235 self.child_env = {"DISPLAY": ":%d" % display} | 239 self.child_env = {"DISPLAY": ":%d" % display} |
236 for key in [ | 240 for key in [ |
237 "HOME", | 241 "HOME", |
238 "LOGNAME", | 242 "LOGNAME", |
(...skipping 28 matching lines...) Expand all Loading... | |
267 def launch_host(self, host): | 271 def launch_host(self, host): |
268 # Start remoting host | 272 # Start remoting host |
269 args = [locate_executable(REMOTING_COMMAND), | 273 args = [locate_executable(REMOTING_COMMAND), |
270 "--%s=%s" % (HOST_CONFIG_SWITCH_NAME, host.config_file)] | 274 "--%s=%s" % (HOST_CONFIG_SWITCH_NAME, host.config_file)] |
271 self.host_proc = subprocess.Popen(args, env=self.child_env) | 275 self.host_proc = subprocess.Popen(args, env=self.child_env) |
272 if not self.host_proc.pid: | 276 if not self.host_proc.pid: |
273 raise Exception("Could not start remoting host") | 277 raise Exception("Could not start remoting host") |
274 | 278 |
275 | 279 |
276 def main(): | 280 def main(): |
281 parser = optparse.OptionParser() | |
282 parser.add_option("-s", "--size", dest="size", default="1280x1024", | |
283 help="dimensions of virtual desktop (default: %default)") | |
284 (options, args) = parser.parse_args() | |
285 | |
286 size_components = options.size.split("x") | |
287 if len(size_components) != 2: | |
288 parser.error("Incorrect size format, should be WIDTHxHEIGHT"); | |
289 | |
290 try: | |
291 width = int(size_components[0]) | |
292 height = int(size_components[1]) | |
293 | |
294 # Enforce minimum desktop size, as a sanity-check. The limit of 100 will | |
295 # detect typos of 2 instead of 3 digits. | |
296 if width < 100 or height < 100: | |
297 raise ValueError | |
298 except ValueError: | |
299 parser.error("Dimensions should be 100 pixels or greater") | |
Wez
2011/12/13 01:43:52
nit: I'd actually use "Width and height" rather th
| |
300 | |
277 atexit.register(cleanup) | 301 atexit.register(cleanup) |
278 | 302 |
279 for s in [signal.SIGHUP, signal.SIGINT, signal.SIGTERM]: | 303 for s in [signal.SIGHUP, signal.SIGINT, signal.SIGTERM]: |
280 signal.signal(s, signal_handler) | 304 signal.signal(s, signal_handler) |
281 | 305 |
282 # Ensure full path to config directory exists. | 306 # Ensure full path to config directory exists. |
283 if not os.path.exists(CONFIG_DIR): | 307 if not os.path.exists(CONFIG_DIR): |
284 os.makedirs(CONFIG_DIR, mode=0700) | 308 os.makedirs(CONFIG_DIR, mode=0700) |
285 | 309 |
286 auth = Authentication(os.path.join(CONFIG_DIR, "auth.json")) | 310 auth = Authentication(os.path.join(CONFIG_DIR, "auth.json")) |
287 if not auth.load_config(): | 311 if not auth.load_config(): |
288 try: | 312 try: |
289 auth.refresh_tokens() | 313 auth.refresh_tokens() |
290 except: | 314 except: |
291 logging.error("Authentication failed.") | 315 logging.error("Authentication failed.") |
292 return 1 | 316 return 1 |
293 auth.save_config() | 317 auth.save_config() |
294 | 318 |
295 host_hash = hashlib.md5(socket.gethostname()).hexdigest() | 319 host_hash = hashlib.md5(socket.gethostname()).hexdigest() |
296 host = Host(os.path.join(CONFIG_DIR, "host#%s.json" % host_hash)) | 320 host = Host(os.path.join(CONFIG_DIR, "host#%s.json" % host_hash)) |
297 | 321 |
298 if not host.load_config(): | 322 if not host.load_config(): |
299 host.create_config(auth) | 323 host.create_config(auth) |
300 host.save_config() | 324 host.save_config() |
301 | 325 |
302 logging.info("Using host_id: " + host.host_id) | 326 logging.info("Using host_id: " + host.host_id) |
303 | 327 |
304 desktop = Desktop() | 328 desktop = Desktop(width, height) |
305 desktop.launch_x_server() | 329 desktop.launch_x_server() |
306 desktop.launch_x_session() | 330 desktop.launch_x_session() |
307 desktop.launch_host(host) | 331 desktop.launch_host(host) |
308 | 332 |
309 while True: | 333 while True: |
310 pid, status = os.wait() | 334 pid, status = os.wait() |
311 logging.info("wait() returned (%s,%s)" % (pid, status)) | 335 logging.info("wait() returned (%s,%s)" % (pid, status)) |
312 | 336 |
313 if pid == desktop.x_proc.pid: | 337 if pid == desktop.x_proc.pid: |
314 logging.info("X server process terminated with code %d", status) | 338 logging.info("X server process terminated with code %d", status) |
315 break | 339 break |
316 | 340 |
317 if pid == desktop.host_proc.pid: | 341 if pid == desktop.host_proc.pid: |
318 logging.info("Host process terminated, relaunching") | 342 logging.info("Host process terminated, relaunching") |
319 desktop.launch_host(host) | 343 desktop.launch_host(host) |
320 | 344 |
321 if __name__ == "__main__": | 345 if __name__ == "__main__": |
322 logging.basicConfig(level=logging.DEBUG) | 346 logging.basicConfig(level=logging.DEBUG) |
323 sys.exit(main()) | 347 sys.exit(main()) |
OLD | NEW |