Chromium Code Reviews| 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 json | 14 import json |
| 15 import logging | 15 import logging |
| 16 import optparse | |
| 16 import os | 17 import os |
| 17 import random | 18 import random |
| 18 import signal | 19 import signal |
| 19 import socket | 20 import socket |
| 20 import subprocess | 21 import subprocess |
| 21 import sys | 22 import sys |
| 22 import time | 23 import time |
| 23 import urllib2 | 24 import urllib2 |
| 24 import uuid | 25 import uuid |
| 25 | 26 |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 190 logging.info("Terminating Xvfb") | 191 logging.info("Terminating Xvfb") |
| 191 desktop.x_proc.terminate() | 192 desktop.x_proc.terminate() |
| 192 | 193 |
| 193 def signal_handler(signum, stackframe): | 194 def signal_handler(signum, stackframe): |
| 194 # Exit cleanly so the atexit handler, cleanup(), gets called. | 195 # Exit cleanly so the atexit handler, cleanup(), gets called. |
| 195 raise SystemExit | 196 raise SystemExit |
| 196 | 197 |
| 197 | 198 |
| 198 class Desktop: | 199 class Desktop: |
| 199 """Manage a single virtual desktop""" | 200 """Manage a single virtual desktop""" |
| 200 def __init__(self): | 201 def __init__(self, width, height): |
| 201 self.x_proc = None | 202 self.x_proc = None |
| 203 self.width = width | |
| 204 self.height = height | |
| 202 g_desktops.append(self) | 205 g_desktops.append(self) |
| 203 | 206 |
| 204 @staticmethod | 207 @staticmethod |
| 205 def get_unused_display_number(): | 208 def get_unused_display_number(): |
| 206 """Return a candidate display number for which there is currently no | 209 """Return a candidate display number for which there is currently no |
| 207 X Server lock file""" | 210 X Server lock file""" |
| 208 display = FIRST_X_DISPLAY_NUMBER | 211 display = FIRST_X_DISPLAY_NUMBER |
| 209 while os.path.exists(X_LOCK_FILE_TEMPLATE % display): | 212 while os.path.exists(X_LOCK_FILE_TEMPLATE % display): |
| 210 display += 1 | 213 display += 1 |
| 211 return display | 214 return display |
| 212 | 215 |
| 213 def launch_x_server(self): | 216 def launch_x_server(self): |
| 214 display = self.get_unused_display_number() | 217 display = self.get_unused_display_number() |
| 215 ret_code = subprocess.call("xauth add :%d . `mcookie`" % display, | 218 ret_code = subprocess.call("xauth add :%d . `mcookie`" % display, |
| 216 shell=True) | 219 shell=True) |
| 217 if ret_code != 0: | 220 if ret_code != 0: |
| 218 raise Exception("xauth failed with code %d" % ret_code) | 221 raise Exception("xauth failed with code %d" % ret_code) |
| 219 | 222 |
| 220 logging.info("Starting Xvfb on display :%d" % display); | 223 logging.info("Starting Xvfb on display :%d" % display); |
| 224 screen_option = "%dx%dx24" % (self.width, self.height) | |
| 221 self.x_proc = subprocess.Popen(["Xvfb", ":%d" % display, | 225 self.x_proc = subprocess.Popen(["Xvfb", ":%d" % display, |
| 222 "-auth", X_AUTH_FILE, | 226 "-auth", X_AUTH_FILE, |
| 223 "-nolisten", "tcp", | 227 "-nolisten", "tcp", |
| 224 "-screen", "0", "1024x768x24", | 228 "-screen", "0", screen_option |
| 225 ]) | 229 ]) |
| 226 if not self.x_proc.pid: | 230 if not self.x_proc.pid: |
| 227 raise Exception("Could not start Xvfb.") | 231 raise Exception("Could not start Xvfb.") |
| 228 | 232 |
| 229 # Create clean environment for new session, so it is cleanly separated from | 233 # Create clean environment for new session, so it is cleanly separated from |
| 230 # the user's console X session. | 234 # the user's console X session. |
| 231 self.child_env = {"DISPLAY": ":%d" % display} | 235 self.child_env = {"DISPLAY": ":%d" % display} |
| 232 for key in [ | 236 for key in [ |
| 233 "HOME", | 237 "HOME", |
| 234 "LOGNAME", | 238 "LOGNAME", |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 262 | 266 |
| 263 def launch_host(self): | 267 def launch_host(self): |
| 264 # Start remoting host | 268 # Start remoting host |
| 265 command = locate_executable(REMOTING_COMMAND) | 269 command = locate_executable(REMOTING_COMMAND) |
| 266 self.host_proc = subprocess.Popen(command, env=self.child_env) | 270 self.host_proc = subprocess.Popen(command, env=self.child_env) |
| 267 if not self.host_proc.pid: | 271 if not self.host_proc.pid: |
| 268 raise Exception("Could not start remoting host") | 272 raise Exception("Could not start remoting host") |
| 269 | 273 |
| 270 | 274 |
| 271 def main(): | 275 def main(): |
| 276 parser = optparse.OptionParser() | |
| 277 parser.add_option("-s", "--size", dest="size", default="1280x1024", | |
| 278 help="size of virtual desktop (default: %default)") | |
|
Wez
2011/12/09 23:51:16
nit: Would "geometry" be the more X11-ish name?
Wez
2011/12/09 23:51:16
nit: "size of virtual desktop" -> "dimensions of v
Lambros
2011/12/13 01:32:22
Nah. xrandr uses '-s' and '--size', and calls it
| |
| 279 (options, args) = parser.parse_args() | |
| 280 | |
| 281 size_components = options.size.split("x") | |
| 282 if len(size_components) != 2: | |
| 283 parser.error("Incorrect size format, should be WIDTHxHEIGHT"); | |
| 284 | |
| 285 try: | |
| 286 width = int(size_components[0]) | |
| 287 height = int(size_components[1]) | |
| 288 except ValueError: | |
| 289 parser.error("Width/Height must be numeric") | |
| 290 | |
| 291 if width <= 0 or height <= 0: | |
| 292 parser.error("Width/Height must be positive numbers") | |
|
Wez
2011/12/09 23:51:16
nit: You could move this test inside the try and h
Wez
2011/12/09 23:51:16
nit: It may make sense to prohibit too-small deskt
Lambros
2011/12/13 01:32:22
Done.
Lambros
2011/12/13 01:32:22
Done.
| |
| 293 | |
| 272 atexit.register(cleanup) | 294 atexit.register(cleanup) |
| 273 | 295 |
| 274 for s in [signal.SIGHUP, signal.SIGINT, signal.SIGTERM]: | 296 for s in [signal.SIGHUP, signal.SIGINT, signal.SIGTERM]: |
| 275 signal.signal(s, signal_handler) | 297 signal.signal(s, signal_handler) |
| 276 | 298 |
| 277 # Ensure full path to config directory exists. | 299 # Ensure full path to config directory exists. |
| 278 if not os.path.exists(CONFIG_DIR): | 300 if not os.path.exists(CONFIG_DIR): |
| 279 os.makedirs(CONFIG_DIR, mode=0700) | 301 os.makedirs(CONFIG_DIR, mode=0700) |
| 280 | 302 |
| 281 auth = Authentication(os.path.join(CONFIG_DIR, "auth.json")) | 303 auth = Authentication(os.path.join(CONFIG_DIR, "auth.json")) |
| 282 if not auth.load_config(): | 304 if not auth.load_config(): |
| 283 try: | 305 try: |
| 284 auth.refresh_tokens() | 306 auth.refresh_tokens() |
| 285 except: | 307 except: |
| 286 logging.error("Authentication failed.") | 308 logging.error("Authentication failed.") |
| 287 return 1 | 309 return 1 |
| 288 auth.save_config() | 310 auth.save_config() |
| 289 | 311 |
| 290 host = Host(os.path.join(CONFIG_DIR, "host.json")) | 312 host = Host(os.path.join(CONFIG_DIR, "host.json")) |
| 291 | 313 |
| 292 if not host.load_config(): | 314 if not host.load_config(): |
| 293 host.create_config(auth) | 315 host.create_config(auth) |
| 294 host.save_config() | 316 host.save_config() |
| 295 | 317 |
| 296 logging.info("Using host_id: " + host.host_id) | 318 logging.info("Using host_id: " + host.host_id) |
| 297 | 319 |
| 298 desktop = Desktop() | 320 desktop = Desktop(width, height) |
| 299 desktop.launch_x_server() | 321 desktop.launch_x_server() |
| 300 desktop.launch_x_session() | 322 desktop.launch_x_session() |
| 301 desktop.launch_host() | 323 desktop.launch_host() |
| 302 | 324 |
| 303 while True: | 325 while True: |
| 304 pid, status = os.wait() | 326 pid, status = os.wait() |
| 305 logging.info("wait() returned (%s,%s)" % (pid, status)) | 327 logging.info("wait() returned (%s,%s)" % (pid, status)) |
| 306 | 328 |
| 307 if pid == desktop.x_proc.pid: | 329 if pid == desktop.x_proc.pid: |
| 308 logging.info("X server process terminated with code %d", status) | 330 logging.info("X server process terminated with code %d", status) |
| 309 break | 331 break |
| 310 | 332 |
| 311 if pid == desktop.host_proc.pid: | 333 if pid == desktop.host_proc.pid: |
| 312 logging.info("Host process terminated, relaunching") | 334 logging.info("Host process terminated, relaunching") |
| 313 desktop.launch_host() | 335 desktop.launch_host() |
| 314 | 336 |
| 315 if __name__ == "__main__": | 337 if __name__ == "__main__": |
| 316 logging.basicConfig(level=logging.DEBUG) | 338 logging.basicConfig(level=logging.DEBUG) |
| 317 sys.exit(main()) | 339 sys.exit(main()) |
| OLD | NEW |