From 73dc3fbcfd8725d6e11aa844a28bff4430a1dc42 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 8 Jun 2019 21:01:57 +0200 Subject: [PATCH] Better shutdown and signal logging --- src/danog/MadelineProto/API.php | 18 +++++++------ src/danog/MadelineProto/Exception.php | 10 +++++++ src/danog/MadelineProto/Logger.php | 4 +-- .../MadelineProto/Loop/Update/FeedLoop.php | 10 ++++--- src/danog/MadelineProto/MTProto.php | 27 ++++++++++++++++--- src/danog/MadelineProto/Magic.php | 7 +++-- .../Stream/MTProtoTools/SeqNoHandler.php | 2 +- src/danog/MadelineProto/Wrappers/Loop.php | 1 + 8 files changed, 59 insertions(+), 20 deletions(-) diff --git a/src/danog/MadelineProto/API.php b/src/danog/MadelineProto/API.php index 185bd9c0d..4e899aa0b 100644 --- a/src/danog/MadelineProto/API.php +++ b/src/danog/MadelineProto/API.php @@ -36,7 +36,6 @@ class API extends APIFactory public function __magic_construct($params = [], $settings = []) { Magic::class_exists(); - set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']); $deferred = new Deferred(); $this->asyncAPIPromise = $deferred->promise(); $this->asyncAPIPromise->onResolve(function () { @@ -131,6 +130,7 @@ class API extends APIFactory if (isset($unserialized->API)) { $this->API = $unserialized->API; $this->APIFactory(); + $unserialized->donotlog = true; $deferred->resolve(); yield $this->API->initAsync(); $this->APIFactory(); @@ -187,14 +187,16 @@ class API extends APIFactory if ($this->asyncInitPromise) { $this->init(); } - if ($this->API) { - $this->API->logger('Shutting down MadelineProto (normally or due to an exception, idk)'); - $this->API->destructing = true; - } else { - Logger::log('Shutting down MadelineProto (normally or due to an exception, idk)'); + if (!isset($this->donotlog)) { + if ($this->API) { + $this->API->logger('Shutting down MadelineProto (normally or due to an exception, idk)'); + $this->API->destructing = true; + } else { + Logger::log('Shutting down MadelineProto (normally or due to an exception, idk)'); + } + $this->destructing = true; + $this->wait($this->serialize()); } - $this->destructing = true; - $this->wait($this->serialize()); //restore_error_handler(); } diff --git a/src/danog/MadelineProto/Exception.php b/src/danog/MadelineProto/Exception.php index a62610db5..98a816165 100644 --- a/src/danog/MadelineProto/Exception.php +++ b/src/danog/MadelineProto/Exception.php @@ -86,4 +86,14 @@ class Exception extends \Exception throw new self($errstr, $errno, null, $errfile, $errline); } + + /** + * ExceptionErrorHandler. + * + * Error handler + */ + public static function ExceptionHandler($exception) + { + Logger::log($exception, Logger::FATAL_ERROR); + } } diff --git a/src/danog/MadelineProto/Logger.php b/src/danog/MadelineProto/Logger.php index a707fe7a4..333c1ac7c 100644 --- a/src/danog/MadelineProto/Logger.php +++ b/src/danog/MadelineProto/Logger.php @@ -166,8 +166,8 @@ class Logger $param = Magic::$isatty ? "\33[".$this->colors[$level].'m'.$param."\33[0m".$this->newline : $param.$this->newline; if ($this->stdout->write($param) instanceof Failure) { switch ($this->mode) { - case 3: echo "(closed) $param"; break; - case 2: file_put_contents($this->optional, "(closed) $param", FILE_APPEND); break; + case 3: echo $param; break; + case 2: file_put_contents($this->optional, $param, FILE_APPEND); break; } } break; diff --git a/src/danog/MadelineProto/Loop/Update/FeedLoop.php b/src/danog/MadelineProto/Loop/Update/FeedLoop.php index 227ab5517..bccbbc03c 100644 --- a/src/danog/MadelineProto/Loop/Update/FeedLoop.php +++ b/src/danog/MadelineProto/Loop/Update/FeedLoop.php @@ -220,11 +220,11 @@ class FeedLoop extends ResumableSignalLoop $this->API->logger->logger("Not enough data: for message update $log, getting difference...", \danog\MadelineProto\Logger::VERBOSE); $update = ['_' => 'updateChannelTooLong']; - if ($channelId && !yield $this->API->peer_isset_async($this->API->to_supergroup($channelId))) $channelId = false; + if ($channelId && $to) $channelId = false; } break; default: - if ($channelId !== false && !yield $this->API->peer_isset_async($this->API->to_supergroup($channelId))) { + if ($channelId && !yield $this->API->peer_isset_async($this->API->to_supergroup($channelId))) { $this->API->logger->logger('Skipping update, I do not have the channel id '.$channelId, \danog\MadelineProto\Logger::ERROR); return; @@ -232,7 +232,11 @@ class FeedLoop extends ResumableSignalLoop break; } if ($channelId !== $this->channelId) { - return yield $this->API->feeders[$channelId]->feedSingle($update); + if (isset($this->API->feeders[$channelId])) { + return yield $this->API->feeders[$channelId]->feedSingle($update); + } else if ($this->channelId) { + return yield $this->API->feeders[false]->feedSingle($update); + } } $this->API->logger->logger('Was fed an update of type '.$update['_']." in $this...", \danog\MadelineProto\Logger::VERBOSE); diff --git a/src/danog/MadelineProto/MTProto.php b/src/danog/MadelineProto/MTProto.php index 703974d77..c8dff2a70 100644 --- a/src/danog/MadelineProto/MTProto.php +++ b/src/danog/MadelineProto/MTProto.php @@ -233,9 +233,11 @@ class MTProto extends AsyncConstruct implements TLCallback return ['supportUser', 'referenceDatabase', 'channel_participants', 'event_handler', 'event_handler_instance', 'loop_callback', 'web_template', 'encrypted_layer', 'settings', 'config', 'authorization', 'authorized', 'rsa_keys', 'dh_config', 'chats', 'last_stored', 'qres', 'got_state', 'channels_state', 'updates', 'updates_key', 'full_chats', 'msg_ids', 'dialog_params', 'datacenter', 'v', 'constructors', 'td_constructors', 'methods', 'td_methods', 'td_descriptions', 'tl_callbacks', 'temp_requested_secret_chats', 'temp_rekeyed_secret_chats', 'secret_chats', 'hook_url', 'storage', 'authorized_dc', 'tos']; } - public function logger(...$params) + public function logger($param, $level = Logger::NOTICE, $file = null) { - return $this->logger->logger(...$params); + if ($file === null) $file = basename(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0]['file'], '.php'); + + return $this->logger->logger($param, $level, $file); } public function isAltervista() { @@ -460,9 +462,26 @@ class MTProto extends AsyncConstruct implements TLCallback if ($this->phoneConfigWatcherId) { Loop::cancel($this->phoneConfigWatcherId); } - if (\danog\MadelineProto\Magic::$has_thread && is_object(\Thread::getCurrentThread())) { - return; + $channelIds = []; + foreach ($this->channels_state->get() as $state) { + $channelIds []= $state->getChannel(); } + sort($channelIds); + foreach ($channelIds as $channelId) { + if (isset($this->feeders[$channelId])) { + $this->feeders[$channelId]->signal(true); + } + if (!isset($this->updaters[$channelId])) { + $this->updaters[$channelId]->signal(true); + } + } + if (isset($this->seqUpdater)) { + $this->seqUpdater->signal(true); + } + foreach ($this->datacenter->sockets as $datacenter) { + $datacenter->disconnect(); + } + $this->logger("Successfully destroyed MadelineProto"); } public function serialize() diff --git a/src/danog/MadelineProto/Magic.php b/src/danog/MadelineProto/Magic.php index b36fef817..a2c17f620 100644 --- a/src/danog/MadelineProto/Magic.php +++ b/src/danog/MadelineProto/Magic.php @@ -58,6 +58,8 @@ class Magic public static function class_exists() { + set_error_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionErrorHandler']); + //set_exception_handler(['\\danog\\MadelineProto\\Exception', 'ExceptionHandler']); if (!self::$inited) { self::$has_thread = class_exists('\\Thread') && method_exists('\\Thread', 'getCurrentThread'); self::$BIG_ENDIAN = pack('L', 1) === pack('N', 1); @@ -133,8 +135,9 @@ class Magic } // Even an empty handler is enough to catch ctrl+c if (defined('SIGINT')) { - Loop::onSignal(SIGINT, static function () { die(); }); - Loop::onSignal(SIGTERM, static function () { die(); }); + if (function_exists('pcntl_async_signals')) pcntl_async_signals(true); + Loop::onSignal(SIGINT, static function () { Logger::log('Got sigint', Logger::FATAL_ERROR); die(); }); + Loop::onSignal(SIGTERM, static function () { Logger::log('Got sigterm', Logger::FATAL_ERROR); die(); }); } self::$inited = true; } diff --git a/src/danog/MadelineProto/Stream/MTProtoTools/SeqNoHandler.php b/src/danog/MadelineProto/Stream/MTProtoTools/SeqNoHandler.php index 32c96f753..112069634 100644 --- a/src/danog/MadelineProto/Stream/MTProtoTools/SeqNoHandler.php +++ b/src/danog/MadelineProto/Stream/MTProtoTools/SeqNoHandler.php @@ -39,7 +39,7 @@ trait SeqNoHandler { $type = isset($this->incoming_messages[$current_msg_id]['content']['_']) ? $this->incoming_messages[$current_msg_id]['content']['_'] : '-'; if (isset($this->incoming_messages[$current_msg_id]['seq_no']) && ($seq_no = $this->generate_in_seq_no($this->content_related($this->incoming_messages[$current_msg_id]['content']))) !== $this->incoming_messages[$current_msg_id]['seq_no']) { - $this->API->logger->logger('SECURITY WARNING: Seqno mismatch (should be '.$seq_no.', is '.$this->incoming_messages[$current_msg_id]['seq_no'].', '.$type.')', \danog\MadelineProto\Logger::ERROR); + $this->API->logger->logger('SECURITY WARNING: Seqno mismatch (should be '.$seq_no.', is '.$this->incoming_messages[$current_msg_id]['seq_no'].', '.$type.')', \danog\MadelineProto\Logger::ULTRA_VERBOSE); } elseif (isset($seq_no)) { $this->API->logger->logger('Seqno OK (should be '.$seq_no.', is '.$this->incoming_messages[$current_msg_id]['seq_no'].', '.$type.')', \danog\MadelineProto\Logger::ULTRA_VERBOSE); } diff --git a/src/danog/MadelineProto/Wrappers/Loop.php b/src/danog/MadelineProto/Wrappers/Loop.php index eaf60f713..7d17975b2 100644 --- a/src/danog/MadelineProto/Wrappers/Loop.php +++ b/src/danog/MadelineProto/Wrappers/Loop.php @@ -51,6 +51,7 @@ trait Loop return false; } + $this->logger->logger('Starting event loop'); if (!is_callable($this->loop_callback) || (is_array($this->loop_callback) && $this->loop_callback[1] === 'onLoop' && !method_exists(...$this->loop_callback))) { $this->loop_callback = null; }