| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/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. |
| (...skipping 341 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 352 | 352 |
| 353 @staticmethod | 353 @staticmethod |
| 354 def get_unused_display_number(): | 354 def get_unused_display_number(): |
| 355 """Return a candidate display number for which there is currently no | 355 """Return a candidate display number for which there is currently no |
| 356 X Server lock file""" | 356 X Server lock file""" |
| 357 display = FIRST_X_DISPLAY_NUMBER | 357 display = FIRST_X_DISPLAY_NUMBER |
| 358 while os.path.exists(X_LOCK_FILE_TEMPLATE % display): | 358 while os.path.exists(X_LOCK_FILE_TEMPLATE % display): |
| 359 display += 1 | 359 display += 1 |
| 360 return display | 360 return display |
| 361 | 361 |
| 362 def _init_child_env(self, keep_env): | 362 def _init_child_env(self): |
| 363 if keep_env: | 363 # Create clean environment for new session, so it is cleanly separated from |
| 364 self.child_env = dict(os.environ) | 364 # the user's console X session. |
| 365 else: | 365 self.child_env = {} |
| 366 # Create clean environment for new session, so it is cleanly separated | |
| 367 # from the user's console X session. | |
| 368 self.child_env = {} | |
| 369 | 366 |
| 370 for key in [ | 367 for key in [ |
| 371 "HOME", | 368 "HOME", |
| 372 "LANG", | 369 "LANG", |
| 373 "LOGNAME", | 370 "LOGNAME", |
| 374 "PATH", | 371 "PATH", |
| 375 "SHELL", | 372 "SHELL", |
| 376 "USER", | 373 "USER", |
| 377 "USERNAME", | 374 "USERNAME", |
| 378 LOG_FILE_ENV_VAR]: | 375 LOG_FILE_ENV_VAR]: |
| 379 if key in os.environ: | 376 if key in os.environ: |
| 380 self.child_env[key] = os.environ[key] | 377 self.child_env[key] = os.environ[key] |
| 381 | |
| 382 # Initialize the environment from files that would normally be read in a | |
| 383 # PAM-authenticated session. | |
| 384 for env_filename in [ | |
| 385 "/etc/environment", | |
| 386 "/etc/default/locale", | |
| 387 os.path.expanduser("~/.pam_environment")]: | |
| 388 if not os.path.exists(env_filename): | |
| 389 continue | |
| 390 try: | |
| 391 with open(env_filename, "r") as env_file: | |
| 392 for line in env_file: | |
| 393 line = line.rstrip("\n") | |
| 394 # Split at the first "=", leaving any further instances in the | |
| 395 # value. | |
| 396 key_value_pair = line.split("=", 1) | |
| 397 if len(key_value_pair) == 2: | |
| 398 key, value = tuple(key_value_pair) | |
| 399 # The file stores key=value assignments, but the value may be | |
| 400 # quoted, so strip leading & trailing quotes from it. | |
| 401 value = value.strip("'\"") | |
| 402 self.child_env[key] = value | |
| 403 except IOError: | |
| 404 logging.error("Failed to read file: %s" % env_filename) | |
| 405 | 378 |
| 406 # Ensure that the software-rendering GL drivers are loaded by the desktop | 379 # Ensure that the software-rendering GL drivers are loaded by the desktop |
| 407 # session, instead of any hardware GL drivers installed on the system. | 380 # session, instead of any hardware GL drivers installed on the system. |
| 408 library_path = ( | 381 self.child_env["LD_LIBRARY_PATH"] = ( |
| 409 "/usr/lib/%(arch)s-linux-gnu/mesa:" | 382 "/usr/lib/%(arch)s-linux-gnu/mesa:" |
| 410 "/usr/lib/%(arch)s-linux-gnu/dri:" | 383 "/usr/lib/%(arch)s-linux-gnu/dri:" |
| 411 "/usr/lib/%(arch)s-linux-gnu/gallium-pipe" % | 384 "/usr/lib/%(arch)s-linux-gnu/gallium-pipe" % |
| 412 { "arch": platform.machine() }) | 385 { "arch": platform.machine() }) |
| 413 | 386 |
| 414 if "LD_LIBRARY_PATH" in self.child_env: | 387 # Initialize the environment from files that would normally be read in a |
| 415 library_path += ":" + self.child_env["LD_LIBRARY_PATH"] | 388 # PAM-authenticated session. |
| 416 | 389 for env_filename in [ |
| 417 self.child_env["LD_LIBRARY_PATH"] = library_path | 390 "/etc/environment", |
| 391 "/etc/default/locale", |
| 392 os.path.expanduser("~/.pam_environment")]: |
| 393 if not os.path.exists(env_filename): |
| 394 continue |
| 395 try: |
| 396 with open(env_filename, "r") as env_file: |
| 397 for line in env_file: |
| 398 line = line.rstrip("\n") |
| 399 # Split at the first "=", leaving any further instances in the |
| 400 # value. |
| 401 key_value_pair = line.split("=", 1) |
| 402 if len(key_value_pair) == 2: |
| 403 key, value = tuple(key_value_pair) |
| 404 # The file stores key=value assignments, but the value may be |
| 405 # quoted, so strip leading & trailing quotes from it. |
| 406 value = value.strip("'\"") |
| 407 self.child_env[key] = value |
| 408 except IOError: |
| 409 logging.error("Failed to read file: %s" % env_filename) |
| 418 | 410 |
| 419 def _setup_pulseaudio(self): | 411 def _setup_pulseaudio(self): |
| 420 self.pulseaudio_pipe = None | 412 self.pulseaudio_pipe = None |
| 421 | 413 |
| 422 # pulseaudio uses UNIX sockets for communication. Length of UNIX socket | 414 # pulseaudio uses UNIX sockets for communication. Length of UNIX socket |
| 423 # name is limited to 108 characters, so audio will not work properly if | 415 # name is limited to 108 characters, so audio will not work properly if |
| 424 # the path is too long. To workaround this problem we use only first 10 | 416 # the path is too long. To workaround this problem we use only first 10 |
| 425 # symbols of the host hash. | 417 # symbols of the host hash. |
| 426 pulse_path = os.path.join(CONFIG_DIR, | 418 pulse_path = os.path.join(CONFIG_DIR, |
| 427 "pulseaudio#%s" % g_host_hash[0:10]) | 419 "pulseaudio#%s" % g_host_hash[0:10]) |
| (...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 650 raise Exception("Unable to choose suitable X session command.") | 642 raise Exception("Unable to choose suitable X session command.") |
| 651 | 643 |
| 652 logging.info("Launching X session: %s" % xsession_command) | 644 logging.info("Launching X session: %s" % xsession_command) |
| 653 self.session_proc = subprocess.Popen(xsession_command, | 645 self.session_proc = subprocess.Popen(xsession_command, |
| 654 stdin=open(os.devnull, "r"), | 646 stdin=open(os.devnull, "r"), |
| 655 cwd=HOME_DIR, | 647 cwd=HOME_DIR, |
| 656 env=self.child_env) | 648 env=self.child_env) |
| 657 if not self.session_proc.pid: | 649 if not self.session_proc.pid: |
| 658 raise Exception("Could not start X session") | 650 raise Exception("Could not start X session") |
| 659 | 651 |
| 660 def launch_session(self, keep_env, x_args): | 652 def launch_session(self, x_args): |
| 661 self._init_child_env(keep_env) | 653 self._init_child_env() |
| 662 self._setup_pulseaudio() | 654 self._setup_pulseaudio() |
| 663 self._setup_gnubby() | 655 self._setup_gnubby() |
| 664 self._launch_x_server(x_args) | 656 self._launch_x_server(x_args) |
| 665 self._launch_x_session() | 657 self._launch_x_session() |
| 666 | 658 |
| 667 def launch_host(self, host_config): | 659 def launch_host(self, host_config): |
| 668 # Start remoting host | 660 # Start remoting host |
| 669 args = [HOST_BINARY_PATH, "--host-config=-"] | 661 args = [HOST_BINARY_PATH, "--host-config=-"] |
| 670 if self.pulseaudio_pipe: | 662 if self.pulseaudio_pipe: |
| 671 args.append("--audio-pipe-name=%s" % self.pulseaudio_pipe) | 663 args.append("--audio-pipe-name=%s" % self.pulseaudio_pipe) |
| (...skipping 565 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1237 parser.add_option("", "--reload", dest="reload", default=False, | 1229 parser.add_option("", "--reload", dest="reload", default=False, |
| 1238 action="store_true", | 1230 action="store_true", |
| 1239 help="Signal currently running host to reload the config.") | 1231 help="Signal currently running host to reload the config.") |
| 1240 parser.add_option("", "--add-user", dest="add_user", default=False, | 1232 parser.add_option("", "--add-user", dest="add_user", default=False, |
| 1241 action="store_true", | 1233 action="store_true", |
| 1242 help="Add current user to the chrome-remote-desktop group.") | 1234 help="Add current user to the chrome-remote-desktop group.") |
| 1243 parser.add_option("", "--add-user-as-root", dest="add_user_as_root", | 1235 parser.add_option("", "--add-user-as-root", dest="add_user_as_root", |
| 1244 action="store", metavar="USER", | 1236 action="store", metavar="USER", |
| 1245 help="Adds the specified user to the chrome-remote-desktop " | 1237 help="Adds the specified user to the chrome-remote-desktop " |
| 1246 "group (must be run as root).") | 1238 "group (must be run as root).") |
| 1247 parser.add_option("", "--keep-parent-env", dest="keep_env", default=False, | |
| 1248 action="store_true", | |
| 1249 help=optparse.SUPPRESS_HELP) | |
| 1250 parser.add_option("", "--watch-resolution", dest="watch_resolution", | 1239 parser.add_option("", "--watch-resolution", dest="watch_resolution", |
| 1251 type="int", nargs=2, default=False, action="store", | 1240 type="int", nargs=2, default=False, action="store", |
| 1252 help=optparse.SUPPRESS_HELP) | 1241 help=optparse.SUPPRESS_HELP) |
| 1253 (options, args) = parser.parse_args() | 1242 (options, args) = parser.parse_args() |
| 1254 | 1243 |
| 1255 # Determine the filename of the host configuration and PID files. | 1244 # Determine the filename of the host configuration and PID files. |
| 1256 if not options.config: | 1245 if not options.config: |
| 1257 options.config = os.path.join(CONFIG_DIR, "host#%s.json" % g_host_hash) | 1246 options.config = os.path.join(CONFIG_DIR, "host#%s.json" % g_host_hash) |
| 1258 | 1247 |
| 1259 # Check for a modal command-line option (start, stop, etc.) | 1248 # Check for a modal command-line option (start, stop, etc.) |
| (...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1473 logging.info("Relaunching self") | 1462 logging.info("Relaunching self") |
| 1474 relaunch_self() | 1463 relaunch_self() |
| 1475 else: | 1464 else: |
| 1476 # If there is a non-zero |failures| count, restarting the whole script | 1465 # If there is a non-zero |failures| count, restarting the whole script |
| 1477 # would lose this information, so just launch the session as normal. | 1466 # would lose this information, so just launch the session as normal. |
| 1478 if x_server_inhibitor.is_inhibited(): | 1467 if x_server_inhibitor.is_inhibited(): |
| 1479 logging.info("Waiting before launching X server") | 1468 logging.info("Waiting before launching X server") |
| 1480 relaunch_times.append(x_server_inhibitor.earliest_relaunch_time) | 1469 relaunch_times.append(x_server_inhibitor.earliest_relaunch_time) |
| 1481 else: | 1470 else: |
| 1482 logging.info("Launching X server and X session.") | 1471 logging.info("Launching X server and X session.") |
| 1483 desktop.launch_session(options.keep_env, args) | 1472 desktop.launch_session(args) |
| 1484 x_server_inhibitor.record_started(MINIMUM_PROCESS_LIFETIME, | 1473 x_server_inhibitor.record_started(MINIMUM_PROCESS_LIFETIME, |
| 1485 backoff_time) | 1474 backoff_time) |
| 1486 allow_relaunch_self = True | 1475 allow_relaunch_self = True |
| 1487 | 1476 |
| 1488 if desktop.host_proc is None: | 1477 if desktop.host_proc is None: |
| 1489 if host_inhibitor.is_inhibited(): | 1478 if host_inhibitor.is_inhibited(): |
| 1490 logging.info("Waiting before launching host process") | 1479 logging.info("Waiting before launching host process") |
| 1491 relaunch_times.append(host_inhibitor.earliest_relaunch_time) | 1480 relaunch_times.append(host_inhibitor.earliest_relaunch_time) |
| 1492 else: | 1481 else: |
| 1493 logging.info("Launching host process") | 1482 logging.info("Launching host process") |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1547 else: | 1536 else: |
| 1548 logging.info("Host exited with status %s." % os.WEXITSTATUS(status)) | 1537 logging.info("Host exited with status %s." % os.WEXITSTATUS(status)) |
| 1549 elif os.WIFSIGNALED(status): | 1538 elif os.WIFSIGNALED(status): |
| 1550 logging.info("Host terminated by signal %s." % os.WTERMSIG(status)) | 1539 logging.info("Host terminated by signal %s." % os.WTERMSIG(status)) |
| 1551 | 1540 |
| 1552 | 1541 |
| 1553 if __name__ == "__main__": | 1542 if __name__ == "__main__": |
| 1554 logging.basicConfig(level=logging.DEBUG, | 1543 logging.basicConfig(level=logging.DEBUG, |
| 1555 format="%(asctime)s:%(levelname)s:%(message)s") | 1544 format="%(asctime)s:%(levelname)s:%(message)s") |
| 1556 sys.exit(main()) | 1545 sys.exit(main()) |
| OLD | NEW |