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 |