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) |