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