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