mercurial/commandserver.py
changeset 45057 d6e99a446eea
parent 44374 f5c006621f07
child 45058 f43bc4ce0d69
equal deleted inserted replaced
45056:9694895749ad 45057:d6e99a446eea
   242             encname, encfn = _selectmessageencoder(ui)
   242             encname, encfn = _selectmessageencoder(ui)
   243             self.cmsg = channeledmessage(fout, b'm', encname, encfn)
   243             self.cmsg = channeledmessage(fout, b'm', encname, encfn)
   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:
   252             return b''
   267             return b''
   253 
   268 
   275         s = self._readstr()
   290         s = self._readstr()
   276         if s:
   291         if s:
   277             return s.split(b'\0')
   292             return s.split(b'\0')
   278         else:
   293         else:
   279             return []
   294             return []
       
   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
   280 
   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 """
   284         from . import dispatch  # avoid cycle
   325         from . import dispatch  # avoid cycle
   316             self.cmsg,
   357             self.cmsg,
   317             prereposetups=self._prereposetups,
   358             prereposetups=self._prereposetups,
   318         )
   359         )
   319 
   360 
   320         try:
   361         try:
   321             ret = dispatch.dispatch(req) & 255
   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
   325             if b'--cwd' in args:
   369             if b'--cwd' in args:
   326                 os.chdir(self.cwd)
   370                 os.chdir(self.cwd)