35 ): |
35 ): |
36 '''Run a command as a service.''' |
36 '''Run a command as a service.''' |
37 |
37 |
38 postexecargs = {} |
38 postexecargs = {} |
39 |
39 |
40 if opts['daemon_postexec']: |
40 if opts[b'daemon_postexec']: |
41 for inst in opts['daemon_postexec']: |
41 for inst in opts[b'daemon_postexec']: |
42 if inst.startswith('unlink:'): |
42 if inst.startswith(b'unlink:'): |
43 postexecargs['unlink'] = inst[7:] |
43 postexecargs[b'unlink'] = inst[7:] |
44 elif inst.startswith('chdir:'): |
44 elif inst.startswith(b'chdir:'): |
45 postexecargs['chdir'] = inst[6:] |
45 postexecargs[b'chdir'] = inst[6:] |
46 elif inst != 'none': |
46 elif inst != b'none': |
47 raise error.Abort( |
47 raise error.Abort( |
48 _('invalid value for --daemon-postexec: %s') % inst |
48 _(b'invalid value for --daemon-postexec: %s') % inst |
49 ) |
49 ) |
50 |
50 |
51 # When daemonized on Windows, redirect stdout/stderr to the lockfile (which |
51 # When daemonized on Windows, redirect stdout/stderr to the lockfile (which |
52 # gets cleaned up after the child is up and running), so that the parent can |
52 # gets cleaned up after the child is up and running), so that the parent can |
53 # read and print the error if this child dies early. See 594dd384803c. On |
53 # read and print the error if this child dies early. See 594dd384803c. On |
54 # other platforms, the child can write to the parent's stdio directly, until |
54 # other platforms, the child can write to the parent's stdio directly, until |
55 # it is redirected prior to runfn(). |
55 # it is redirected prior to runfn(). |
56 if pycompat.iswindows and opts['daemon_postexec']: |
56 if pycompat.iswindows and opts[b'daemon_postexec']: |
57 if 'unlink' in postexecargs and os.path.exists(postexecargs['unlink']): |
57 if b'unlink' in postexecargs and os.path.exists( |
|
58 postexecargs[b'unlink'] |
|
59 ): |
58 procutil.stdout.flush() |
60 procutil.stdout.flush() |
59 procutil.stderr.flush() |
61 procutil.stderr.flush() |
60 |
62 |
61 fd = os.open( |
63 fd = os.open( |
62 postexecargs['unlink'], os.O_WRONLY | os.O_APPEND | os.O_BINARY |
64 postexecargs[b'unlink'], os.O_WRONLY | os.O_APPEND | os.O_BINARY |
63 ) |
65 ) |
64 try: |
66 try: |
65 os.dup2(fd, procutil.stdout.fileno()) |
67 os.dup2(fd, procutil.stdout.fileno()) |
66 os.dup2(fd, procutil.stderr.fileno()) |
68 os.dup2(fd, procutil.stderr.fileno()) |
67 finally: |
69 finally: |
68 os.close(fd) |
70 os.close(fd) |
69 |
71 |
70 def writepid(pid): |
72 def writepid(pid): |
71 if opts['pid_file']: |
73 if opts[b'pid_file']: |
72 if appendpid: |
74 if appendpid: |
73 mode = 'ab' |
75 mode = b'ab' |
74 else: |
76 else: |
75 mode = 'wb' |
77 mode = b'wb' |
76 fp = open(opts['pid_file'], mode) |
78 fp = open(opts[b'pid_file'], mode) |
77 fp.write('%d\n' % pid) |
79 fp.write(b'%d\n' % pid) |
78 fp.close() |
80 fp.close() |
79 |
81 |
80 if opts['daemon'] and not opts['daemon_postexec']: |
82 if opts[b'daemon'] and not opts[b'daemon_postexec']: |
81 # Signal child process startup with file removal |
83 # Signal child process startup with file removal |
82 lockfd, lockpath = pycompat.mkstemp(prefix='hg-service-') |
84 lockfd, lockpath = pycompat.mkstemp(prefix=b'hg-service-') |
83 os.close(lockfd) |
85 os.close(lockfd) |
84 try: |
86 try: |
85 if not runargs: |
87 if not runargs: |
86 runargs = procutil.hgcmd() + pycompat.sysargv[1:] |
88 runargs = procutil.hgcmd() + pycompat.sysargv[1:] |
87 runargs.append('--daemon-postexec=unlink:%s' % lockpath) |
89 runargs.append(b'--daemon-postexec=unlink:%s' % lockpath) |
88 # Don't pass --cwd to the child process, because we've already |
90 # Don't pass --cwd to the child process, because we've already |
89 # changed directory. |
91 # changed directory. |
90 for i in pycompat.xrange(1, len(runargs)): |
92 for i in pycompat.xrange(1, len(runargs)): |
91 if runargs[i].startswith('--cwd='): |
93 if runargs[i].startswith(b'--cwd='): |
92 del runargs[i] |
94 del runargs[i] |
93 break |
95 break |
94 elif runargs[i].startswith('--cwd'): |
96 elif runargs[i].startswith(b'--cwd'): |
95 del runargs[i : i + 2] |
97 del runargs[i : i + 2] |
96 break |
98 break |
97 |
99 |
98 def condfn(): |
100 def condfn(): |
99 return not os.path.exists(lockpath) |
101 return not os.path.exists(lockpath) |
152 if logfile and logfilefd not in stdio: |
154 if logfile and logfilefd not in stdio: |
153 os.close(logfilefd) |
155 os.close(logfilefd) |
154 |
156 |
155 # Only unlink after redirecting stdout/stderr, so Windows doesn't |
157 # Only unlink after redirecting stdout/stderr, so Windows doesn't |
156 # complain about a sharing violation. |
158 # complain about a sharing violation. |
157 if 'unlink' in postexecargs: |
159 if b'unlink' in postexecargs: |
158 os.unlink(postexecargs['unlink']) |
160 os.unlink(postexecargs[b'unlink']) |
159 |
161 |
160 if runfn: |
162 if runfn: |
161 return runfn() |
163 return runfn() |
162 |
164 |
163 |
165 |
164 _cmdservicemap = { |
166 _cmdservicemap = { |
165 'chgunix': chgserver.chgunixservice, |
167 b'chgunix': chgserver.chgunixservice, |
166 'pipe': commandserver.pipeservice, |
168 b'pipe': commandserver.pipeservice, |
167 'unix': commandserver.unixforkingservice, |
169 b'unix': commandserver.unixforkingservice, |
168 } |
170 } |
169 |
171 |
170 |
172 |
171 def _createcmdservice(ui, repo, opts): |
173 def _createcmdservice(ui, repo, opts): |
172 mode = opts['cmdserver'] |
174 mode = opts[b'cmdserver'] |
173 try: |
175 try: |
174 servicefn = _cmdservicemap[mode] |
176 servicefn = _cmdservicemap[mode] |
175 except KeyError: |
177 except KeyError: |
176 raise error.Abort(_('unknown mode %s') % mode) |
178 raise error.Abort(_(b'unknown mode %s') % mode) |
177 commandserver.setuplogging(ui, repo) |
179 commandserver.setuplogging(ui, repo) |
178 return servicefn(ui, repo, opts) |
180 return servicefn(ui, repo, opts) |
179 |
181 |
180 |
182 |
181 def _createhgwebservice(ui, repo, opts): |
183 def _createhgwebservice(ui, repo, opts): |
182 # this way we can check if something was given in the command-line |
184 # this way we can check if something was given in the command-line |
183 if opts.get('port'): |
185 if opts.get(b'port'): |
184 opts['port'] = util.getport(opts.get('port')) |
186 opts[b'port'] = util.getport(opts.get(b'port')) |
185 |
187 |
186 alluis = {ui} |
188 alluis = {ui} |
187 if repo: |
189 if repo: |
188 baseui = repo.baseui |
190 baseui = repo.baseui |
189 alluis.update([repo.baseui, repo.ui]) |
191 alluis.update([repo.baseui, repo.ui]) |
190 else: |
192 else: |
191 baseui = ui |
193 baseui = ui |
192 webconf = opts.get('web_conf') or opts.get('webdir_conf') |
194 webconf = opts.get(b'web_conf') or opts.get(b'webdir_conf') |
193 if webconf: |
195 if webconf: |
194 if opts.get('subrepos'): |
196 if opts.get(b'subrepos'): |
195 raise error.Abort(_('--web-conf cannot be used with --subrepos')) |
197 raise error.Abort(_(b'--web-conf cannot be used with --subrepos')) |
196 |
198 |
197 # load server settings (e.g. web.port) to "copied" ui, which allows |
199 # load server settings (e.g. web.port) to "copied" ui, which allows |
198 # hgwebdir to reload webconf cleanly |
200 # hgwebdir to reload webconf cleanly |
199 servui = ui.copy() |
201 servui = ui.copy() |
200 servui.readconfig(webconf, sections=['web']) |
202 servui.readconfig(webconf, sections=[b'web']) |
201 alluis.add(servui) |
203 alluis.add(servui) |
202 elif opts.get('subrepos'): |
204 elif opts.get(b'subrepos'): |
203 servui = ui |
205 servui = ui |
204 |
206 |
205 # If repo is None, hgweb.createapp() already raises a proper abort |
207 # If repo is None, hgweb.createapp() already raises a proper abort |
206 # message as long as webconf is None. |
208 # message as long as webconf is None. |
207 if repo: |
209 if repo: |
208 webconf = dict() |
210 webconf = dict() |
209 cmdutil.addwebdirpath(repo, "", webconf) |
211 cmdutil.addwebdirpath(repo, b"", webconf) |
210 else: |
212 else: |
211 servui = ui |
213 servui = ui |
212 |
214 |
213 optlist = ( |
215 optlist = ( |
214 "name templates style address port prefix ipv6" |
216 b"name templates style address port prefix ipv6" |
215 " accesslog errorlog certificate encoding" |
217 b" accesslog errorlog certificate encoding" |
216 ) |
218 ) |
217 for o in optlist.split(): |
219 for o in optlist.split(): |
218 val = opts.get(o, '') |
220 val = opts.get(o, b'') |
219 if val in (None, ''): # should check against default options instead |
221 if val in (None, b''): # should check against default options instead |
220 continue |
222 continue |
221 for u in alluis: |
223 for u in alluis: |
222 u.setconfig("web", o, val, 'serve') |
224 u.setconfig(b"web", o, val, b'serve') |
223 |
225 |
224 app = hgweb.createapp(baseui, repo, webconf) |
226 app = hgweb.createapp(baseui, repo, webconf) |
225 return hgweb.httpservice(servui, app, opts) |
227 return hgweb.httpservice(servui, app, opts) |
226 |
228 |
227 |
229 |
228 def createservice(ui, repo, opts): |
230 def createservice(ui, repo, opts): |
229 if opts["cmdserver"]: |
231 if opts[b"cmdserver"]: |
230 return _createcmdservice(ui, repo, opts) |
232 return _createcmdservice(ui, repo, opts) |
231 else: |
233 else: |
232 return _createhgwebservice(ui, repo, opts) |
234 return _createhgwebservice(ui, repo, opts) |