1"""Launch `dvc daemon` command in a separate detached process.""" 2 3from __future__ import unicode_literals 4 5import os 6import sys 7import inspect 8from subprocess import Popen 9 10import dvc.logger as logger 11from dvc.utils import is_binary, fix_env 12from dvc.utils.compat import cast_bytes_py2 13 14 15CREATE_NEW_PROCESS_GROUP = 0x00000200 16DETACHED_PROCESS = 0x00000008 17 18 19def _spawn_windows(cmd, env): 20 from subprocess import STARTUPINFO, STARTF_USESHOWWINDOW 21 22 creationflags = CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS 23 24 startupinfo = STARTUPINFO() 25 startupinfo.dwFlags |= STARTF_USESHOWWINDOW 26 27 Popen( 28 cmd, 29 env=env, 30 close_fds=True, 31 shell=False, 32 creationflags=creationflags, 33 startupinfo=startupinfo, 34 ).communicate() 35 36 37def _spawn_posix(cmd, env): 38 # NOTE: using os._exit instead of sys.exit, because dvc built 39 # with PyInstaller has trouble with SystemExit exeption and throws 40 # errors such as "[26338] Failed to execute script __main__" 41 try: 42 pid = os.fork() 43 if pid > 0: 44 return 45 except OSError: 46 logger.error("failed at first fork") 47 os._exit(1) # pylint: disable=protected-access 48 49 os.setsid() 50 os.umask(0) 51 52 try: 53 pid = os.fork() 54 if pid > 0: 55 os._exit(0) # pylint: disable=protected-access 56 except OSError: 57 logger.error("failed at second fork") 58 os._exit(1) # pylint: disable=protected-access 59 60 sys.stdin.close() 61 sys.stdout.close() 62 sys.stderr.close() 63 64 Popen(cmd, env=env, close_fds=True, shell=False).communicate() 65 66 os._exit(0) # pylint: disable=protected-access 67 68 69def daemon(args): 70 """Launch a `dvc daemon` command in a detached process. 71 72 Args: 73 args (list): list of arguments to append to `dvc daemon` command. 74 """ 75 cmd = [sys.executable] 76 if not is_binary(): 77 cmd += ["-m", "dvc"] 78 cmd += ["daemon", "-q"] + args 79 80 env = fix_env() 81 file_path = os.path.abspath(inspect.stack()[0][1]) 82 env[cast_bytes_py2("PYTHONPATH")] = cast_bytes_py2( 83 os.path.dirname(os.path.dirname(file_path)) 84 ) 85 86 logger.debug("Trying to spawn '{}' with env '{}'".format(cmd, env)) 87 88 if os.name == "nt": 89 _spawn_windows(cmd, env) 90 elif os.name == "posix": 91 _spawn_posix(cmd, env) 92 else: 93 raise NotImplementedError 94 95 logger.debug("Spawned '{}'".format(cmd)) 96