Show More
@@ -244,8 +244,23 b' class server(object):' | |||||
244 |
|
244 | |||
245 | self.client = fin |
|
245 | self.client = fin | |
246 |
|
246 | |||
|
247 | # If shutdown-on-interrupt is off, the default SIGINT handler is | |||
|
248 | # removed so that client-server communication wouldn't be interrupted. | |||
|
249 | # For example, 'runcommand' handler will issue three short read()s. | |||
|
250 | # If one of the first two read()s were interrupted, the communication | |||
|
251 | # channel would be left at dirty state and the subsequent request | |||
|
252 | # wouldn't be parsed. So catching KeyboardInterrupt isn't enough. | |||
|
253 | self._shutdown_on_interrupt = ui.configbool( | |||
|
254 | b'cmdserver', b'shutdown-on-interrupt' | |||
|
255 | ) | |||
|
256 | self._old_inthandler = None | |||
|
257 | if not self._shutdown_on_interrupt: | |||
|
258 | self._old_inthandler = signal.signal(signal.SIGINT, signal.SIG_IGN) | |||
|
259 | ||||
247 | def cleanup(self): |
|
260 | def cleanup(self): | |
248 | """release and restore resources taken during server session""" |
|
261 | """release and restore resources taken during server session""" | |
|
262 | if not self._shutdown_on_interrupt: | |||
|
263 | signal.signal(signal.SIGINT, self._old_inthandler) | |||
249 |
|
264 | |||
250 | def _read(self, size): |
|
265 | def _read(self, size): | |
251 | if not size: |
|
266 | if not size: | |
@@ -278,6 +293,32 b' class server(object):' | |||||
278 | else: |
|
293 | else: | |
279 | return [] |
|
294 | return [] | |
280 |
|
295 | |||
|
296 | def _dispatchcommand(self, req): | |||
|
297 | from . import dispatch # avoid cycle | |||
|
298 | ||||
|
299 | if self._shutdown_on_interrupt: | |||
|
300 | # no need to restore SIGINT handler as it is unmodified. | |||
|
301 | return dispatch.dispatch(req) | |||
|
302 | ||||
|
303 | try: | |||
|
304 | signal.signal(signal.SIGINT, self._old_inthandler) | |||
|
305 | return dispatch.dispatch(req) | |||
|
306 | except error.SignalInterrupt: | |||
|
307 | # propagate SIGBREAK, SIGHUP, or SIGTERM. | |||
|
308 | raise | |||
|
309 | except KeyboardInterrupt: | |||
|
310 | # SIGINT may be received out of the try-except block of dispatch(), | |||
|
311 | # so catch it as last ditch. Another KeyboardInterrupt may be | |||
|
312 | # raised while handling exceptions here, but there's no way to | |||
|
313 | # avoid that except for doing everything in C. | |||
|
314 | pass | |||
|
315 | finally: | |||
|
316 | signal.signal(signal.SIGINT, signal.SIG_IGN) | |||
|
317 | # On KeyboardInterrupt, print error message and exit *after* SIGINT | |||
|
318 | # handler removed. | |||
|
319 | req.ui.error(_(b'interrupted!\n')) | |||
|
320 | return -1 | |||
|
321 | ||||
281 | def runcommand(self): |
|
322 | def runcommand(self): | |
282 | """ reads a list of \0 terminated arguments, executes |
|
323 | """ reads a list of \0 terminated arguments, executes | |
283 | and writes the return code to the result channel """ |
|
324 | and writes the return code to the result channel """ | |
@@ -318,7 +359,10 b' class server(object):' | |||||
318 | ) |
|
359 | ) | |
319 |
|
360 | |||
320 | try: |
|
361 | try: | |
321 |
ret = |
|
362 | ret = self._dispatchcommand(req) & 255 | |
|
363 | # If shutdown-on-interrupt is off, it's important to write the | |||
|
364 | # result code *after* SIGINT handler removed. If the result code | |||
|
365 | # were lost, the client wouldn't be able to continue processing. | |||
322 | self.cresult.write(struct.pack(b'>i', int(ret))) |
|
366 | self.cresult.write(struct.pack(b'>i', int(ret))) | |
323 | finally: |
|
367 | finally: | |
324 | # restore old cwd |
|
368 | # restore old cwd |
@@ -212,6 +212,9 b' coreconfigitem(' | |||||
212 | default=lambda: [b'chgserver', b'cmdserver', b'repocache'], |
|
212 | default=lambda: [b'chgserver', b'cmdserver', b'repocache'], | |
213 | ) |
|
213 | ) | |
214 | coreconfigitem( |
|
214 | coreconfigitem( | |
|
215 | b'cmdserver', b'shutdown-on-interrupt', default=True, | |||
|
216 | ) | |||
|
217 | coreconfigitem( | |||
215 | b'color', b'.*', default=None, generic=True, |
|
218 | b'color', b'.*', default=None, generic=True, | |
216 | ) |
|
219 | ) | |
217 | coreconfigitem( |
|
220 | coreconfigitem( |
@@ -408,6 +408,18 b' Supported arguments:' | |||||
408 | If no suitable authentication entry is found, the user is prompted |
|
408 | If no suitable authentication entry is found, the user is prompted | |
409 | for credentials as usual if required by the remote. |
|
409 | for credentials as usual if required by the remote. | |
410 |
|
410 | |||
|
411 | ``cmdserver`` | |||
|
412 | ------------- | |||
|
413 | ||||
|
414 | Controls command server settings. (ADVANCED) | |||
|
415 | ||||
|
416 | ``shutdown-on-interrupt`` | |||
|
417 | If set to false, the server's main loop will continue running after | |||
|
418 | SIGINT received. ``runcommand`` requests can still be interrupted by | |||
|
419 | SIGINT. Close the write end of the pipe to shut down the server | |||
|
420 | process gracefully. | |||
|
421 | (default: True) | |||
|
422 | ||||
411 | ``color`` |
|
423 | ``color`` | |
412 | --------- |
|
424 | --------- | |
413 |
|
425 |
@@ -734,6 +734,51 b" don't fall back to cwd if invalid -R pat" | |||||
734 | $ cd .. |
|
734 | $ cd .. | |
735 |
|
735 | |||
736 |
|
736 | |||
|
737 | #if no-windows | |||
|
738 | ||||
|
739 | option to not shutdown on SIGINT: | |||
|
740 | ||||
|
741 | $ cat <<'EOF' > dbgint.py | |||
|
742 | > import os | |||
|
743 | > import signal | |||
|
744 | > import time | |||
|
745 | > from mercurial import commands, registrar | |||
|
746 | > cmdtable = {} | |||
|
747 | > command = registrar.command(cmdtable) | |||
|
748 | > @command(b"debugsleep", norepo=True) | |||
|
749 | > def debugsleep(ui): | |||
|
750 | > time.sleep(1) | |||
|
751 | > @command(b"debugsuicide", norepo=True) | |||
|
752 | > def debugsuicide(ui): | |||
|
753 | > os.kill(os.getpid(), signal.SIGINT) | |||
|
754 | > time.sleep(1) | |||
|
755 | > EOF | |||
|
756 | ||||
|
757 | >>> import signal | |||
|
758 | >>> import time | |||
|
759 | >>> from hgclient import checkwith, readchannel, runcommand | |||
|
760 | >>> @checkwith(extraargs=[b'--config', b'cmdserver.shutdown-on-interrupt=False', | |||
|
761 | ... b'--config', b'extensions.dbgint=dbgint.py']) | |||
|
762 | ... def nointr(server): | |||
|
763 | ... readchannel(server) | |||
|
764 | ... server.send_signal(signal.SIGINT) # server won't be terminated | |||
|
765 | ... time.sleep(1) | |||
|
766 | ... runcommand(server, [b'debugsleep']) | |||
|
767 | ... server.send_signal(signal.SIGINT) # server won't be terminated | |||
|
768 | ... runcommand(server, [b'debugsleep']) | |||
|
769 | ... runcommand(server, [b'debugsuicide']) # command can be interrupted | |||
|
770 | ... server.send_signal(signal.SIGTERM) # server will be terminated | |||
|
771 | ... time.sleep(1) | |||
|
772 | *** runcommand debugsleep | |||
|
773 | *** runcommand debugsleep | |||
|
774 | *** runcommand debugsuicide | |||
|
775 | interrupted! | |||
|
776 | killed! | |||
|
777 | [255] | |||
|
778 | ||||
|
779 | #endif | |||
|
780 | ||||
|
781 | ||||
737 | structured message channel: |
|
782 | structured message channel: | |
738 |
|
783 | |||
739 | $ cat <<'EOF' >> repo2/.hg/hgrc |
|
784 | $ cat <<'EOF' >> repo2/.hg/hgrc |
General Comments 0
You need to be logged in to leave comments.
Login now