28 |
28 |
29 def runservice(opts, parentfn=None, initfn=None, runfn=None, logfile=None, |
29 def runservice(opts, parentfn=None, initfn=None, runfn=None, logfile=None, |
30 runargs=None, appendpid=False): |
30 runargs=None, appendpid=False): |
31 '''Run a command as a service.''' |
31 '''Run a command as a service.''' |
32 |
32 |
|
33 postexecargs = {} |
|
34 |
|
35 if opts['daemon_postexec']: |
|
36 for inst in opts['daemon_postexec']: |
|
37 if inst.startswith('unlink:'): |
|
38 postexecargs['unlink'] = inst[7:] |
|
39 elif inst.startswith('chdir:'): |
|
40 postexecargs['chdir'] = inst[6:] |
|
41 elif inst != 'none': |
|
42 raise error.Abort(_('invalid value for --daemon-postexec: %s') |
|
43 % inst) |
|
44 |
33 # When daemonized on Windows, redirect stdout/stderr to the lockfile (which |
45 # When daemonized on Windows, redirect stdout/stderr to the lockfile (which |
34 # gets cleaned up after the child is up and running), so that the parent can |
46 # gets cleaned up after the child is up and running), so that the parent can |
35 # read and print the error if this child dies early. See 594dd384803c. On |
47 # read and print the error if this child dies early. See 594dd384803c. On |
36 # other platforms, the child can write to the parent's stdio directly, until |
48 # other platforms, the child can write to the parent's stdio directly, until |
37 # it is redirected prior to runfn(). |
49 # it is redirected prior to runfn(). |
38 if pycompat.iswindows and opts['daemon_postexec']: |
50 if pycompat.iswindows and opts['daemon_postexec']: |
39 for inst in opts['daemon_postexec']: |
51 if 'unlink' in postexecargs and os.path.exists(postexecargs['unlink']): |
40 if inst.startswith('unlink:'): |
52 procutil.stdout.flush() |
41 lockpath = inst[7:] |
53 procutil.stderr.flush() |
42 if os.path.exists(lockpath): |
54 |
43 procutil.stdout.flush() |
55 fd = os.open(postexecargs['unlink'], |
44 procutil.stderr.flush() |
56 os.O_WRONLY | os.O_APPEND | os.O_BINARY) |
45 |
57 try: |
46 fd = os.open(lockpath, |
58 os.dup2(fd, 1) |
47 os.O_WRONLY | os.O_APPEND | os.O_BINARY) |
59 os.dup2(fd, 2) |
48 try: |
60 finally: |
49 os.dup2(fd, 1) |
61 os.close(fd) |
50 os.dup2(fd, 2) |
|
51 finally: |
|
52 os.close(fd) |
|
53 |
62 |
54 def writepid(pid): |
63 def writepid(pid): |
55 if opts['pid_file']: |
64 if opts['pid_file']: |
56 if appendpid: |
65 if appendpid: |
57 mode = 'ab' |
66 mode = 'ab' |
107 try: |
116 try: |
108 os.setsid() |
117 os.setsid() |
109 except AttributeError: |
118 except AttributeError: |
110 pass |
119 pass |
111 |
120 |
112 lockpath = None |
121 if 'chdir' in postexecargs: |
113 for inst in opts['daemon_postexec']: |
122 os.chdir(postexecargs['chdir']) |
114 if inst.startswith('unlink:'): |
|
115 lockpath = inst[7:] |
|
116 elif inst.startswith('chdir:'): |
|
117 os.chdir(inst[6:]) |
|
118 elif inst != 'none': |
|
119 raise error.Abort(_('invalid value for --daemon-postexec: %s') |
|
120 % inst) |
|
121 procutil.hidewindow() |
123 procutil.hidewindow() |
122 procutil.stdout.flush() |
124 procutil.stdout.flush() |
123 procutil.stderr.flush() |
125 procutil.stderr.flush() |
124 |
126 |
125 nullfd = os.open(os.devnull, os.O_RDWR) |
127 nullfd = os.open(os.devnull, os.O_RDWR) |
135 if logfile and logfilefd not in (0, 1, 2): |
137 if logfile and logfilefd not in (0, 1, 2): |
136 os.close(logfilefd) |
138 os.close(logfilefd) |
137 |
139 |
138 # Only unlink after redirecting stdout/stderr, so Windows doesn't |
140 # Only unlink after redirecting stdout/stderr, so Windows doesn't |
139 # complain about a sharing violation. |
141 # complain about a sharing violation. |
140 if lockpath: |
142 if 'unlink' in postexecargs: |
141 os.unlink(lockpath) |
143 os.unlink(postexecargs['unlink']) |
142 |
144 |
143 if runfn: |
145 if runfn: |
144 return runfn() |
146 return runfn() |
145 |
147 |
146 _cmdservicemap = { |
148 _cmdservicemap = { |