##// END OF EJS Templates
server: refactor 'daemon_postexec' instructions into a dictionary
Matt Harbison -
r37232:73a60281 default
parent child Browse files
Show More
@@ -1,207 +1,209 b''
1 1 # server.py - utility and factory of server
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import os
11 11 import tempfile
12 12
13 13 from .i18n import _
14 14
15 15 from . import (
16 16 chgserver,
17 17 cmdutil,
18 18 commandserver,
19 19 error,
20 20 hgweb,
21 21 pycompat,
22 22 util,
23 23 )
24 24
25 25 from .utils import (
26 26 procutil,
27 27 )
28 28
29 29 def runservice(opts, parentfn=None, initfn=None, runfn=None, logfile=None,
30 30 runargs=None, appendpid=False):
31 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 45 # When daemonized on Windows, redirect stdout/stderr to the lockfile (which
34 46 # gets cleaned up after the child is up and running), so that the parent can
35 47 # read and print the error if this child dies early. See 594dd384803c. On
36 48 # other platforms, the child can write to the parent's stdio directly, until
37 49 # it is redirected prior to runfn().
38 50 if pycompat.iswindows and opts['daemon_postexec']:
39 for inst in opts['daemon_postexec']:
40 if inst.startswith('unlink:'):
41 lockpath = inst[7:]
42 if os.path.exists(lockpath):
43 procutil.stdout.flush()
44 procutil.stderr.flush()
51 if 'unlink' in postexecargs and os.path.exists(postexecargs['unlink']):
52 procutil.stdout.flush()
53 procutil.stderr.flush()
45 54
46 fd = os.open(lockpath,
47 os.O_WRONLY | os.O_APPEND | os.O_BINARY)
48 try:
49 os.dup2(fd, 1)
50 os.dup2(fd, 2)
51 finally:
52 os.close(fd)
55 fd = os.open(postexecargs['unlink'],
56 os.O_WRONLY | os.O_APPEND | os.O_BINARY)
57 try:
58 os.dup2(fd, 1)
59 os.dup2(fd, 2)
60 finally:
61 os.close(fd)
53 62
54 63 def writepid(pid):
55 64 if opts['pid_file']:
56 65 if appendpid:
57 66 mode = 'ab'
58 67 else:
59 68 mode = 'wb'
60 69 fp = open(opts['pid_file'], mode)
61 70 fp.write('%d\n' % pid)
62 71 fp.close()
63 72
64 73 if opts['daemon'] and not opts['daemon_postexec']:
65 74 # Signal child process startup with file removal
66 75 lockfd, lockpath = tempfile.mkstemp(prefix='hg-service-')
67 76 os.close(lockfd)
68 77 try:
69 78 if not runargs:
70 79 runargs = procutil.hgcmd() + pycompat.sysargv[1:]
71 80 runargs.append('--daemon-postexec=unlink:%s' % lockpath)
72 81 # Don't pass --cwd to the child process, because we've already
73 82 # changed directory.
74 83 for i in xrange(1, len(runargs)):
75 84 if runargs[i].startswith('--cwd='):
76 85 del runargs[i]
77 86 break
78 87 elif runargs[i].startswith('--cwd'):
79 88 del runargs[i:i + 2]
80 89 break
81 90 def condfn():
82 91 return not os.path.exists(lockpath)
83 92 pid = procutil.rundetached(runargs, condfn)
84 93 if pid < 0:
85 94 # If the daemonized process managed to write out an error msg,
86 95 # report it.
87 96 if pycompat.iswindows and os.path.exists(lockpath):
88 97 with open(lockpath) as log:
89 98 for line in log:
90 99 procutil.stderr.write(line)
91 100 raise error.Abort(_('child process failed to start'))
92 101 writepid(pid)
93 102 finally:
94 103 util.tryunlink(lockpath)
95 104 if parentfn:
96 105 return parentfn(pid)
97 106 else:
98 107 return
99 108
100 109 if initfn:
101 110 initfn()
102 111
103 112 if not opts['daemon']:
104 113 writepid(procutil.getpid())
105 114
106 115 if opts['daemon_postexec']:
107 116 try:
108 117 os.setsid()
109 118 except AttributeError:
110 119 pass
111 120
112 lockpath = None
113 for inst in opts['daemon_postexec']:
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 if 'chdir' in postexecargs:
122 os.chdir(postexecargs['chdir'])
121 123 procutil.hidewindow()
122 124 procutil.stdout.flush()
123 125 procutil.stderr.flush()
124 126
125 127 nullfd = os.open(os.devnull, os.O_RDWR)
126 128 logfilefd = nullfd
127 129 if logfile:
128 130 logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND,
129 131 0o666)
130 132 os.dup2(nullfd, 0)
131 133 os.dup2(logfilefd, 1)
132 134 os.dup2(logfilefd, 2)
133 135 if nullfd not in (0, 1, 2):
134 136 os.close(nullfd)
135 137 if logfile and logfilefd not in (0, 1, 2):
136 138 os.close(logfilefd)
137 139
138 140 # Only unlink after redirecting stdout/stderr, so Windows doesn't
139 141 # complain about a sharing violation.
140 if lockpath:
141 os.unlink(lockpath)
142 if 'unlink' in postexecargs:
143 os.unlink(postexecargs['unlink'])
142 144
143 145 if runfn:
144 146 return runfn()
145 147
146 148 _cmdservicemap = {
147 149 'chgunix': chgserver.chgunixservice,
148 150 'pipe': commandserver.pipeservice,
149 151 'unix': commandserver.unixforkingservice,
150 152 }
151 153
152 154 def _createcmdservice(ui, repo, opts):
153 155 mode = opts['cmdserver']
154 156 try:
155 157 return _cmdservicemap[mode](ui, repo, opts)
156 158 except KeyError:
157 159 raise error.Abort(_('unknown mode %s') % mode)
158 160
159 161 def _createhgwebservice(ui, repo, opts):
160 162 # this way we can check if something was given in the command-line
161 163 if opts.get('port'):
162 164 opts['port'] = util.getport(opts.get('port'))
163 165
164 166 alluis = {ui}
165 167 if repo:
166 168 baseui = repo.baseui
167 169 alluis.update([repo.baseui, repo.ui])
168 170 else:
169 171 baseui = ui
170 172 webconf = opts.get('web_conf') or opts.get('webdir_conf')
171 173 if webconf:
172 174 if opts.get('subrepos'):
173 175 raise error.Abort(_('--web-conf cannot be used with --subrepos'))
174 176
175 177 # load server settings (e.g. web.port) to "copied" ui, which allows
176 178 # hgwebdir to reload webconf cleanly
177 179 servui = ui.copy()
178 180 servui.readconfig(webconf, sections=['web'])
179 181 alluis.add(servui)
180 182 elif opts.get('subrepos'):
181 183 servui = ui
182 184
183 185 # If repo is None, hgweb.createapp() already raises a proper abort
184 186 # message as long as webconf is None.
185 187 if repo:
186 188 webconf = dict()
187 189 cmdutil.addwebdirpath(repo, "", webconf)
188 190 else:
189 191 servui = ui
190 192
191 193 optlist = ("name templates style address port prefix ipv6"
192 194 " accesslog errorlog certificate encoding")
193 195 for o in optlist.split():
194 196 val = opts.get(o, '')
195 197 if val in (None, ''): # should check against default options instead
196 198 continue
197 199 for u in alluis:
198 200 u.setconfig("web", o, val, 'serve')
199 201
200 202 app = hgweb.createapp(baseui, repo, webconf)
201 203 return hgweb.httpservice(servui, app, opts)
202 204
203 205 def createservice(ui, repo, opts):
204 206 if opts["cmdserver"]:
205 207 return _createcmdservice(ui, repo, opts)
206 208 else:
207 209 return _createhgwebservice(ui, repo, opts)
General Comments 0
You need to be logged in to leave comments. Login now