diff --git a/src/infrastructure/daemon/bot/PhabricatorBot.php b/src/infrastructure/daemon/bot/PhabricatorBot.php --- a/src/infrastructure/daemon/bot/PhabricatorBot.php +++ b/src/infrastructure/daemon/bot/PhabricatorBot.php @@ -83,6 +83,8 @@ ->connect(); $this->runLoop(); + + $this->protocolAdapter->disconnect(); } public function getConfig($key, $default = null) { @@ -104,6 +106,7 @@ $handler->runBackgroundTasks(); } } while (!$this->shouldExit()); + } public function writeMessage(PhabricatorBotMessage $message) { diff --git a/src/infrastructure/daemon/bot/adapter/PhabricatorBaseProtocolAdapter.php b/src/infrastructure/daemon/bot/adapter/PhabricatorBaseProtocolAdapter.php --- a/src/infrastructure/daemon/bot/adapter/PhabricatorBaseProtocolAdapter.php +++ b/src/infrastructure/daemon/bot/adapter/PhabricatorBaseProtocolAdapter.php @@ -22,6 +22,13 @@ abstract public function connect(); /** + * Disconnect from the service. + */ + public function disconnect() { + return; + } + + /** * This is the spout for messages coming in from the protocol. * This will be called in the main event loop of the bot daemon * So if if doesn't implement some sort of blocking timeout diff --git a/src/infrastructure/daemon/bot/adapter/PhabricatorIRCProtocolAdapter.php b/src/infrastructure/daemon/bot/adapter/PhabricatorIRCProtocolAdapter.php --- a/src/infrastructure/daemon/bot/adapter/PhabricatorIRCProtocolAdapter.php +++ b/src/infrastructure/daemon/bot/adapter/PhabricatorIRCProtocolAdapter.php @@ -71,8 +71,13 @@ $ok = @stream_select($read, $write, $except, $timeout_sec = 1); if ($ok === false) { - throw new Exception( - 'socket_select() failed: '.socket_strerror(socket_last_error())); + // We may have been interrupted by a signal, like a SIGINT. Try + // selecting again. If the second select works, conclude that the failure + // was most likely because we were signaled. + $ok = @stream_select($read, $write, $except, $timeout_sec = 0); + if ($ok === false) { + throw new Exception('stream_select() failed!'); + } } if ($read) { @@ -102,6 +107,8 @@ $len = fwrite($this->socket, $this->writeBuffer); if ($len === false) { throw new Exception('fwrite() failed!'); + } else if ($len === 0) { + break; } else { $messages[] = id(new PhabricatorBotMessage()) ->setCommand('LOG') @@ -250,8 +257,22 @@ $data); } - public function __destruct() { - $this->write('QUIT Goodbye.'); - fclose($this->socket); + public function disconnect() { + // NOTE: FreeNode doesn't show quit messages if you've recently joined a + // channel, presumably to prevent some kind of abuse. If you're testing + // this, you may need to stay connected to the network for a few minutes + // before it works. If you disconnect too quickly, the server will replace + // your message with a "Client Quit" message. + + $quit = $this->getConfig('quit', pht('Shutting down.')); + $this->write("QUIT :{$quit}"); + + // Flush the write buffer. + while (strlen($this->writeBuffer)) { + $this->getNextMessages(0); + } + + @fclose($this->socket); + $this->socket = null; } }