Show More
@@ -21,6 +21,8 b' import traceback' | |||||
21 |
|
21 | |||
22 | from .i18n import _ |
|
22 | from .i18n import _ | |
23 |
|
23 | |||
|
24 | from hgdemandimport import tracing | |||
|
25 | ||||
24 | from . import ( |
|
26 | from . import ( | |
25 | cmdutil, |
|
27 | cmdutil, | |
26 | color, |
|
28 | color, | |
@@ -84,7 +86,8 b' class request(object):' | |||||
84 | def run(): |
|
86 | def run(): | |
85 | "run the command in sys.argv" |
|
87 | "run the command in sys.argv" | |
86 | initstdio() |
|
88 | initstdio() | |
87 | req = request(pycompat.sysargv[1:]) |
|
89 | with tracing.log('parse args into request'): | |
|
90 | req = request(pycompat.sysargv[1:]) | |||
88 | err = None |
|
91 | err = None | |
89 | try: |
|
92 | try: | |
90 | status = dispatch(req) |
|
93 | status = dispatch(req) | |
@@ -176,182 +179,184 b' def _formatargs(args):' | |||||
176 |
|
179 | |||
177 | def dispatch(req): |
|
180 | def dispatch(req): | |
178 | """run the command specified in req.args; returns an integer status code""" |
|
181 | """run the command specified in req.args; returns an integer status code""" | |
179 | if req.ferr: |
|
182 | with tracing.log('dispatch.dispatch'): | |
180 |
f |
|
183 | if req.ferr: | |
181 | elif req.ui: |
|
184 | ferr = req.ferr | |
182 |
|
|
185 | elif req.ui: | |
183 | else: |
|
186 | ferr = req.ui.ferr | |
184 | ferr = procutil.stderr |
|
187 | else: | |
|
188 | ferr = procutil.stderr | |||
185 |
|
189 | |||
186 | try: |
|
190 | try: | |
187 | if not req.ui: |
|
191 | if not req.ui: | |
188 | req.ui = uimod.ui.load() |
|
192 | req.ui = uimod.ui.load() | |
189 | req.earlyoptions.update(_earlyparseopts(req.ui, req.args)) |
|
193 | req.earlyoptions.update(_earlyparseopts(req.ui, req.args)) | |
190 | if req.earlyoptions['traceback']: |
|
194 | if req.earlyoptions['traceback']: | |
191 | req.ui.setconfig('ui', 'traceback', 'on', '--traceback') |
|
195 | req.ui.setconfig('ui', 'traceback', 'on', '--traceback') | |
192 |
|
196 | |||
193 | # set ui streams from the request |
|
197 | # set ui streams from the request | |
194 | if req.fin: |
|
198 | if req.fin: | |
195 | req.ui.fin = req.fin |
|
199 | req.ui.fin = req.fin | |
196 | if req.fout: |
|
200 | if req.fout: | |
197 | req.ui.fout = req.fout |
|
201 | req.ui.fout = req.fout | |
198 | if req.ferr: |
|
202 | if req.ferr: | |
199 | req.ui.ferr = req.ferr |
|
203 | req.ui.ferr = req.ferr | |
200 | except error.Abort as inst: |
|
204 | except error.Abort as inst: | |
201 | ferr.write(_("abort: %s\n") % inst) |
|
205 | ferr.write(_("abort: %s\n") % inst) | |
202 | if inst.hint: |
|
206 | if inst.hint: | |
203 | ferr.write(_("(%s)\n") % inst.hint) |
|
207 | ferr.write(_("(%s)\n") % inst.hint) | |
204 | return -1 |
|
208 | return -1 | |
205 | except error.ParseError as inst: |
|
209 | except error.ParseError as inst: | |
206 | _formatparse(ferr.write, inst) |
|
210 | _formatparse(ferr.write, inst) | |
207 | return -1 |
|
211 | return -1 | |
208 |
|
212 | |||
209 | msg = _formatargs(req.args) |
|
213 | msg = _formatargs(req.args) | |
210 | starttime = util.timer() |
|
214 | starttime = util.timer() | |
211 | ret = 1 # default of Python exit code on unhandled exception |
|
215 | ret = 1 # default of Python exit code on unhandled exception | |
212 | try: |
|
|||
213 | ret = _runcatch(req) or 0 |
|
|||
214 | except error.ProgrammingError as inst: |
|
|||
215 | req.ui.error(_('** ProgrammingError: %s\n') % inst) |
|
|||
216 | if inst.hint: |
|
|||
217 | req.ui.error(_('** (%s)\n') % inst.hint) |
|
|||
218 | raise |
|
|||
219 | except KeyboardInterrupt as inst: |
|
|||
220 | try: |
|
216 | try: | |
221 | if isinstance(inst, error.SignalInterrupt): |
|
217 | ret = _runcatch(req) or 0 | |
222 | msg = _("killed!\n") |
|
218 | except error.ProgrammingError as inst: | |
223 | else: |
|
219 | req.ui.error(_('** ProgrammingError: %s\n') % inst) | |
224 | msg = _("interrupted!\n") |
|
220 | if inst.hint: | |
225 |
req.ui.error( |
|
221 | req.ui.error(_('** (%s)\n') % inst.hint) | |
226 | except error.SignalInterrupt: |
|
222 | raise | |
227 | # maybe pager would quit without consuming all the output, and |
|
223 | except KeyboardInterrupt as inst: | |
228 | # SIGPIPE was raised. we cannot print anything in this case. |
|
224 | try: | |
229 | pass |
|
225 | if isinstance(inst, error.SignalInterrupt): | |
230 | except IOError as inst: |
|
226 | msg = _("killed!\n") | |
231 | if inst.errno != errno.EPIPE: |
|
227 | else: | |
232 | raise |
|
228 | msg = _("interrupted!\n") | |
233 | ret = -1 |
|
229 | req.ui.error(msg) | |
234 | finally: |
|
230 | except error.SignalInterrupt: | |
235 | duration = util.timer() - starttime |
|
231 | # maybe pager would quit without consuming all the output, and | |
236 | req.ui.flush() |
|
232 | # SIGPIPE was raised. we cannot print anything in this case. | |
237 | if req.ui.logblockedtimes: |
|
233 | pass | |
238 | req.ui._blockedtimes['command_duration'] = duration * 1000 |
|
234 | except IOError as inst: | |
239 | req.ui.log('uiblocked', 'ui blocked ms', |
|
235 | if inst.errno != errno.EPIPE: | |
240 | **pycompat.strkwargs(req.ui._blockedtimes)) |
|
236 | raise | |
241 | req.ui.log("commandfinish", "%s exited %d after %0.2f seconds\n", |
|
237 | ret = -1 | |
242 | msg, ret & 255, duration) |
|
238 | finally: | |
243 | try: |
|
239 | duration = util.timer() - starttime | |
244 |
req. |
|
240 | req.ui.flush() | |
245 | except: # exiting, so no re-raises |
|
241 | if req.ui.logblockedtimes: | |
246 | ret = ret or -1 |
|
242 | req.ui._blockedtimes['command_duration'] = duration * 1000 | |
247 | return ret |
|
243 | req.ui.log('uiblocked', 'ui blocked ms', | |
|
244 | **pycompat.strkwargs(req.ui._blockedtimes)) | |||
|
245 | req.ui.log("commandfinish", "%s exited %d after %0.2f seconds\n", | |||
|
246 | msg, ret & 255, duration) | |||
|
247 | try: | |||
|
248 | req._runexithandlers() | |||
|
249 | except: # exiting, so no re-raises | |||
|
250 | ret = ret or -1 | |||
|
251 | return ret | |||
248 |
|
252 | |||
249 | def _runcatch(req): |
|
253 | def _runcatch(req): | |
250 | def catchterm(*args): |
|
254 | with tracing.log('dispatch._runcatch'): | |
251 | raise error.SignalInterrupt |
|
255 | def catchterm(*args): | |
|
256 | raise error.SignalInterrupt | |||
252 |
|
257 | |||
253 | ui = req.ui |
|
258 | ui = req.ui | |
254 | try: |
|
|||
255 | for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM': |
|
|||
256 | num = getattr(signal, name, None) |
|
|||
257 | if num: |
|
|||
258 | signal.signal(num, catchterm) |
|
|||
259 | except ValueError: |
|
|||
260 | pass # happens if called in a thread |
|
|||
261 |
|
||||
262 | def _runcatchfunc(): |
|
|||
263 | realcmd = None |
|
|||
264 | try: |
|
259 | try: | |
265 | cmdargs = fancyopts.fancyopts(req.args[:], commands.globalopts, {}) |
|
260 | for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM': | |
266 | cmd = cmdargs[0] |
|
261 | num = getattr(signal, name, None) | |
267 | aliases, entry = cmdutil.findcmd(cmd, commands.table, False) |
|
262 | if num: | |
268 | realcmd = aliases[0] |
|
263 | signal.signal(num, catchterm) | |
269 | except (error.UnknownCommand, error.AmbiguousCommand, |
|
264 | except ValueError: | |
270 | IndexError, getopt.GetoptError): |
|
265 | pass # happens if called in a thread | |
271 | # Don't handle this here. We know the command is |
|
|||
272 | # invalid, but all we're worried about for now is that |
|
|||
273 | # it's not a command that server operators expect to |
|
|||
274 | # be safe to offer to users in a sandbox. |
|
|||
275 | pass |
|
|||
276 | if realcmd == 'serve' and '--stdio' in cmdargs: |
|
|||
277 | # We want to constrain 'hg serve --stdio' instances pretty |
|
|||
278 | # closely, as many shared-ssh access tools want to grant |
|
|||
279 | # access to run *only* 'hg -R $repo serve --stdio'. We |
|
|||
280 | # restrict to exactly that set of arguments, and prohibit |
|
|||
281 | # any repo name that starts with '--' to prevent |
|
|||
282 | # shenanigans wherein a user does something like pass |
|
|||
283 | # --debugger or --config=ui.debugger=1 as a repo |
|
|||
284 | # name. This used to actually run the debugger. |
|
|||
285 | if (len(req.args) != 4 or |
|
|||
286 | req.args[0] != '-R' or |
|
|||
287 | req.args[1].startswith('--') or |
|
|||
288 | req.args[2] != 'serve' or |
|
|||
289 | req.args[3] != '--stdio'): |
|
|||
290 | raise error.Abort( |
|
|||
291 | _('potentially unsafe serve --stdio invocation: %s') % |
|
|||
292 | (stringutil.pprint(req.args),)) |
|
|||
293 |
|
266 | |||
294 | try: |
|
267 | def _runcatchfunc(): | |
295 | debugger = 'pdb' |
|
268 | realcmd = None | |
296 |
|
|
269 | try: | |
297 | 'pdb': pdb.set_trace |
|
270 | cmdargs = fancyopts.fancyopts( | |
298 | } |
|
271 | req.args[:], commands.globalopts, {}) | |
299 | debugmortem = { |
|
272 | cmd = cmdargs[0] | |
300 | 'pdb': pdb.post_mortem |
|
273 | aliases, entry = cmdutil.findcmd(cmd, commands.table, False) | |
301 | } |
|
274 | realcmd = aliases[0] | |
|
275 | except (error.UnknownCommand, error.AmbiguousCommand, | |||
|
276 | IndexError, getopt.GetoptError): | |||
|
277 | # Don't handle this here. We know the command is | |||
|
278 | # invalid, but all we're worried about for now is that | |||
|
279 | # it's not a command that server operators expect to | |||
|
280 | # be safe to offer to users in a sandbox. | |||
|
281 | pass | |||
|
282 | if realcmd == 'serve' and '--stdio' in cmdargs: | |||
|
283 | # We want to constrain 'hg serve --stdio' instances pretty | |||
|
284 | # closely, as many shared-ssh access tools want to grant | |||
|
285 | # access to run *only* 'hg -R $repo serve --stdio'. We | |||
|
286 | # restrict to exactly that set of arguments, and prohibit | |||
|
287 | # any repo name that starts with '--' to prevent | |||
|
288 | # shenanigans wherein a user does something like pass | |||
|
289 | # --debugger or --config=ui.debugger=1 as a repo | |||
|
290 | # name. This used to actually run the debugger. | |||
|
291 | if (len(req.args) != 4 or | |||
|
292 | req.args[0] != '-R' or | |||
|
293 | req.args[1].startswith('--') or | |||
|
294 | req.args[2] != 'serve' or | |||
|
295 | req.args[3] != '--stdio'): | |||
|
296 | raise error.Abort( | |||
|
297 | _('potentially unsafe serve --stdio invocation: %s') % | |||
|
298 | (stringutil.pprint(req.args),)) | |||
302 |
|
299 | |||
303 | # read --config before doing anything else |
|
300 | try: | |
304 | # (e.g. to change trust settings for reading .hg/hgrc) |
|
301 | debugger = 'pdb' | |
305 | cfgs = _parseconfig(req.ui, req.earlyoptions['config']) |
|
302 | debugtrace = { | |
306 |
|
303 | 'pdb': pdb.set_trace | ||
307 |
|
|
304 | } | |
308 | # copy configs that were passed on the cmdline (--config) to |
|
305 | debugmortem = { | |
309 | # the repo ui |
|
306 | 'pdb': pdb.post_mortem | |
310 | for sec, name, val in cfgs: |
|
307 | } | |
311 | req.repo.ui.setconfig(sec, name, val, source='--config') |
|
|||
312 |
|
308 | |||
313 | # developer config: ui.debugger |
|
309 | # read --config before doing anything else | |
314 | debugger = ui.config("ui", "debugger") |
|
310 | # (e.g. to change trust settings for reading .hg/hgrc) | |
315 | debugmod = pdb |
|
311 | cfgs = _parseconfig(req.ui, req.earlyoptions['config']) | |
316 | if not debugger or ui.plain(): |
|
312 | ||
317 | # if we are in HGPLAIN mode, then disable custom debugging |
|
313 | if req.repo: | |
318 | debugger = 'pdb' |
|
314 | # copy configs that were passed on the cmdline (--config) to | |
319 | elif req.earlyoptions['debugger']: |
|
315 | # the repo ui | |
320 | # This import can be slow for fancy debuggers, so only |
|
316 | for sec, name, val in cfgs: | |
321 | # do it when absolutely necessary, i.e. when actual |
|
317 | req.repo.ui.setconfig(sec, name, val, source='--config') | |
322 | # debugging has been requested |
|
|||
323 | with demandimport.deactivated(): |
|
|||
324 | try: |
|
|||
325 | debugmod = __import__(debugger) |
|
|||
326 | except ImportError: |
|
|||
327 | pass # Leave debugmod = pdb |
|
|||
328 |
|
318 | |||
329 | debugtrace[debugger] = debugmod.set_trace |
|
319 | # developer config: ui.debugger | |
330 | debugmortem[debugger] = debugmod.post_mortem |
|
320 | debugger = ui.config("ui", "debugger") | |
|
321 | debugmod = pdb | |||
|
322 | if not debugger or ui.plain(): | |||
|
323 | # if we are in HGPLAIN mode, then disable custom debugging | |||
|
324 | debugger = 'pdb' | |||
|
325 | elif req.earlyoptions['debugger']: | |||
|
326 | # This import can be slow for fancy debuggers, so only | |||
|
327 | # do it when absolutely necessary, i.e. when actual | |||
|
328 | # debugging has been requested | |||
|
329 | with demandimport.deactivated(): | |||
|
330 | try: | |||
|
331 | debugmod = __import__(debugger) | |||
|
332 | except ImportError: | |||
|
333 | pass # Leave debugmod = pdb | |||
331 |
|
334 | |||
332 | # enter the debugger before command execution |
|
335 | debugtrace[debugger] = debugmod.set_trace | |
333 | if req.earlyoptions['debugger']: |
|
336 | debugmortem[debugger] = debugmod.post_mortem | |
334 | ui.warn(_("entering debugger - " |
|
|||
335 | "type c to continue starting hg or h for help\n")) |
|
|||
336 |
|
337 | |||
337 | if (debugger != 'pdb' and |
|
338 | # enter the debugger before command execution | |
338 | debugtrace[debugger] == debugtrace['pdb']): |
|
339 | if req.earlyoptions['debugger']: | |
339 |
ui.warn(_(" |
|
340 | ui.warn(_("entering debugger - " | |
340 | "but its module was not found\n") % debugger) |
|
341 | "type c to continue starting hg or h for help\n")) | |
341 | with demandimport.deactivated(): |
|
|||
342 | debugtrace[debugger]() |
|
|||
343 | try: |
|
|||
344 | return _dispatch(req) |
|
|||
345 | finally: |
|
|||
346 | ui.flush() |
|
|||
347 | except: # re-raises |
|
|||
348 | # enter the debugger when we hit an exception |
|
|||
349 | if req.earlyoptions['debugger']: |
|
|||
350 | traceback.print_exc() |
|
|||
351 | debugmortem[debugger](sys.exc_info()[2]) |
|
|||
352 | raise |
|
|||
353 |
|
342 | |||
354 | return _callcatch(ui, _runcatchfunc) |
|
343 | if (debugger != 'pdb' and | |
|
344 | debugtrace[debugger] == debugtrace['pdb']): | |||
|
345 | ui.warn(_("%s debugger specified " | |||
|
346 | "but its module was not found\n") % debugger) | |||
|
347 | with demandimport.deactivated(): | |||
|
348 | debugtrace[debugger]() | |||
|
349 | try: | |||
|
350 | return _dispatch(req) | |||
|
351 | finally: | |||
|
352 | ui.flush() | |||
|
353 | except: # re-raises | |||
|
354 | # enter the debugger when we hit an exception | |||
|
355 | if req.earlyoptions['debugger']: | |||
|
356 | traceback.print_exc() | |||
|
357 | debugmortem[debugger](sys.exc_info()[2]) | |||
|
358 | raise | |||
|
359 | return _callcatch(ui, _runcatchfunc) | |||
355 |
|
360 | |||
356 | def _callcatch(ui, func): |
|
361 | def _callcatch(ui, func): | |
357 | """like scmutil.callcatch but handles more high-level exceptions about |
|
362 | """like scmutil.callcatch but handles more high-level exceptions about |
General Comments 0
You need to be logged in to leave comments.
Login now