]> git.mxchange.org Git - friendica.git/commitdiff
Move daemon to Console Command
authorPhilipp <admin@philipp.info>
Mon, 30 Dec 2024 11:40:42 +0000 (12:40 +0100)
committerPhilipp <admin@philipp.info>
Mon, 30 Dec 2024 11:40:42 +0000 (12:40 +0100)
bin/daemon.php
src/App.php
src/Console/Daemon.php [new file with mode: 0644]
src/Core/Console.php

index 06e8574cae8cd3801926d21e70387f343eabda1f..8f567edfb7d9c6d0fa81b32d522b836c4a13d0de 100755 (executable)
@@ -19,9 +19,6 @@ if (php_sapi_name() !== 'cli') {
 
 use Dice\Dice;
 
-// Get options
-$options = getopt('f', ['foreground']);
-
 // Ensure that daemon.php is executed from the base path of the installation
 chdir(dirname(__DIR__));
 
@@ -31,4 +28,7 @@ $dice = (new Dice())->addRules(require(dirname(__DIR__) . '/static/dependencies.
 
 $app = \Friendica\App::fromDice($dice);
 
-$app->processDaemon($_SERVER['argv'] ?? [], $options ?: []);
+$argv = $_SERVER['argv'] ?? [];
+array_splice($argv, 1, 0, "daemon");
+
+$app->processConsole($argv);
index 15d2cd95400c5bd7c365e7ccd6b10508f2a5c7ea..c24215e37dac25c01120b477f169720117123b6b 100644 (file)
@@ -20,14 +20,12 @@ use Friendica\Content\Nav;
 use Friendica\Core\Addon;
 use Friendica\Core\Config\Factory\Config;
 use Friendica\Core\Hook;
-use Friendica\Core\KeyValueStorage\Capability\IManageKeyValuePairs;
 use Friendica\Core\Renderer;
 use Friendica\Core\Session\Capability\IHandleUserSessions;
 use Friendica\Core\Worker\Repository\Process as ProcessRepository;
 use Friendica\Database\DBA;
 use Friendica\Database\Definition\DbaDefinition;
 use Friendica\Database\Definition\ViewDefinition;
-use Friendica\DI;
 use Friendica\Module\Maintenance;
 use Friendica\Protocol\ATProtocol\Jetstream;
 use Friendica\Security\Authentication;
@@ -222,222 +220,6 @@ class App
                (new \Friendica\Core\Console($this->container, $argv))->execute();
        }
 
-       public function processDaemon(array $argv, array $options): void
-       {
-               $this->setupContainerForAddons();
-
-               $this->setupContainerForLogger(LogChannel::DAEMON);
-
-               $this->setupLegacyServiceLocator();
-
-               $this->registerErrorHandler();
-
-               /** @var Mode */
-               $mode = $this->container->create(Mode::class);
-
-               if ($mode->isInstall()) {
-                       die("Friendica isn't properly installed yet.\n");
-               }
-
-               $mode->setExecutor(Mode::DAEMON);
-
-               /** @var IManageConfigValues */
-               $config = $this->container->create(IManageConfigValues::class);
-
-               $config->reload();
-
-               if (empty($config->get('system', 'pidfile'))) {
-                       die(<<< TXT
-                                       Please set system.pidfile in config/local.config.php. For example:
-
-                                               'system' => [
-                                                       'pidfile' => '/path/to/daemon.pid',
-                                               ],
-                                       TXT
-                       );
-               }
-
-               $pidfile = $config->get('system', 'pidfile');
-
-               if (in_array('start', $argv)) {
-                       $daemonMode = 'start';
-               }
-
-               if (in_array('stop', $argv)) {
-                       $daemonMode = 'stop';
-               }
-
-               if (in_array('status', $argv)) {
-                       $daemonMode = 'status';
-               }
-
-               $foreground = array_key_exists('f', $options) || array_key_exists('foreground', $options);
-
-               if (!isset($daemonMode)) {
-                       die("Please use either 'start', 'stop' or 'status'.\n");
-               }
-
-               if (empty($argv[0])) {
-                       die("Unexpected script behaviour. This message should never occur.\n");
-               }
-
-               $pid = null;
-
-               if (is_readable($pidfile)) {
-                       $pid = intval(file_get_contents($pidfile));
-               }
-
-               /** @var IManageKeyValuePairs */
-               $keyValue = $this->container->create(IManageKeyValuePairs::class);
-
-               if (empty($pid) && in_array($daemonMode, ['stop', 'status'])) {
-                       $keyValue->set('worker_daemon_mode', false);
-                       die("Pidfile wasn't found. Is the daemon running?\n");
-               }
-
-               if ($daemonMode == 'status') {
-                       if (posix_kill($pid, 0)) {
-                               die("Daemon process $pid is running.\n");
-                       }
-
-                       unlink($pidfile);
-
-                       $keyValue->set('worker_daemon_mode', false);
-                       die("Daemon process $pid isn't running.\n");
-               }
-
-               if ($daemonMode == 'stop') {
-                       posix_kill($pid, SIGTERM);
-
-                       unlink($pidfile);
-
-                       Logger::notice('Worker daemon process was killed', ['pid' => $pid]);
-
-                       $keyValue->set('worker_daemon_mode', false);
-                       die("Worker daemon process $pid was killed.\n");
-               }
-
-               if (!empty($pid) && posix_kill($pid, 0)) {
-                       die("Daemon process $pid is already running.\n");
-               }
-
-               Logger::notice('Starting worker daemon.', ['pid' => $pid]);
-
-               if (!$foreground) {
-                       echo "Starting worker daemon.\n";
-
-                       DBA::disconnect();
-
-                       // Fork a daemon process
-                       $pid = pcntl_fork();
-                       if ($pid == -1) {
-                               echo "Daemon couldn't be forked.\n";
-                               Logger::warning('Could not fork daemon');
-                               exit(1);
-                       } elseif ($pid) {
-                               // The parent process continues here
-                               if (!file_put_contents($pidfile, $pid)) {
-                                       echo "Pid file wasn't written.\n";
-                                       Logger::warning('Could not store pid file');
-                                       posix_kill($pid, SIGTERM);
-                                       exit(1);
-                               }
-                               echo 'Child process started with pid ' . $pid . ".\n";
-                               Logger::notice('Child process started', ['pid' => $pid]);
-                               exit(0);
-                       }
-
-                       // We now are in the child process
-                       register_shutdown_function(function () {
-                               posix_kill(posix_getpid(), SIGTERM);
-                               posix_kill(posix_getpid(), SIGHUP);
-                       });
-
-                       // Make the child the main process, detach it from the terminal
-                       if (posix_setsid() < 0) {
-                               return;
-                       }
-
-                       // Closing all existing connections with the outside
-                       fclose(STDIN);
-
-                       // And now connect the database again
-                       DBA::connect();
-               }
-
-               $keyValue->set('worker_daemon_mode', true);
-
-               // Just to be sure that this script really runs endlessly
-               set_time_limit(0);
-
-               $wait_interval = intval($config->get('system', 'cron_interval', 5)) * 60;
-
-               $do_cron = true;
-               $last_cron = 0;
-
-               /** @var BasePath */
-               $basePath = $this->container->create(BasePath::class);
-               $path = $basePath->getPath();
-
-               /** @var System */
-               $system = $this->container->create(System::class);
-
-               // Now running as a daemon.
-               while (true) {
-                       // Check the database structure and possibly fixes it
-                       Update::check($path, true);
-
-                       if (!$do_cron && ($last_cron + $wait_interval) < time()) {
-                               Logger::info('Forcing cron worker call.', ['pid' => $pid]);
-                               $do_cron = true;
-                       }
-
-                       if ($do_cron || (!$system->isMaxLoadReached() && Worker::entriesExists() && Worker::isReady())) {
-                               Worker::spawnWorker($do_cron);
-                       } else {
-                               Logger::info('Cool down for 5 seconds', ['pid' => $pid]);
-                               sleep(5);
-                       }
-
-                       if ($do_cron) {
-                               // We force a reconnect of the database connection.
-                               // This is done to ensure that the connection don't get lost over time.
-                               DBA::reconnect();
-
-                               $last_cron = time();
-                       }
-
-                       $start = time();
-                       Logger::info('Sleeping', ['pid' => $pid, 'until' => gmdate(DateTimeFormat::MYSQL, $start + $wait_interval)]);
-
-                       do {
-                               $seconds = (time() - $start);
-
-                               // logarithmic wait time calculation.
-                               // Background: After jobs had been started, they often fork many workers.
-                               // To not waste too much time, the sleep period increases.
-                               $arg = (($seconds + 1) / ($wait_interval / 9)) + 1;
-                               $sleep = min(1000000, round(log10($arg) * 1000000, 0));
-                               usleep($sleep);
-
-                               $pid = pcntl_waitpid(-1, $status, WNOHANG);
-                               if ($pid > 0) {
-                                       Logger::info('Children quit via pcntl_waitpid', ['pid' => $pid, 'status' => $status]);
-                               }
-
-                               $timeout = ($seconds >= $wait_interval);
-                       } while (!$timeout && !Worker\IPC::JobsExists());
-
-                       if ($timeout) {
-                               $do_cron = true;
-                               Logger::info('Woke up after $wait_interval seconds.', ['pid' => $pid, 'sleep' => $wait_interval]);
-                       } else {
-                               $do_cron = false;
-                               Logger::info('Worker jobs are calling to be forked.', ['pid' => $pid]);
-                       }
-               }
-       }
-
        public function processJetstream(): void
        {
                $this->setupContainerForAddons();
diff --git a/src/Console/Daemon.php b/src/Console/Daemon.php
new file mode 100644 (file)
index 0000000..5f45f81
--- /dev/null
@@ -0,0 +1,256 @@
+<?php
+
+// Copyright (C) 2010-2024, the Friendica project
+// SPDX-FileCopyrightText: 2010-2024 the Friendica project
+//
+// SPDX-License-Identifier: AGPL-3.0-or-later
+
+declare(strict_types=1);
+
+namespace Friendica\Console;
+
+use Friendica\App\Mode;
+use Friendica\Core\Config\Capability\IManageConfigValues;
+use Asika\SimpleConsole\Console;
+use Friendica\Core\KeyValueStorage\Capability\IManageKeyValuePairs;
+use Friendica\Core\System;
+use Friendica\Core\Update;
+use Friendica\Core\Worker;
+use Friendica\Database\Database;
+use Friendica\Util\BasePath;
+use Friendica\Util\DateTimeFormat;
+use Psr\Log\LoggerInterface;
+use RuntimeException;
+
+/**
+ * Console command for starting
+ */
+final class Daemon extends Console
+{
+       private Mode $mode;
+       private IManageConfigValues $config;
+       private IManageKeyValuePairs $keyValue;
+       private BasePath $basePath;
+       private System $system;
+       private LoggerInterface $logger;
+       private Database $dba;
+
+       /**
+        * @param Mode                 $mode
+        * @param IManageConfigValues  $config
+        * @param IManageKeyValuePairs $keyValue
+        * @param BasePath             $basePath
+        * @param System               $system
+        * @param LoggerInterface      $logger
+        * @param Database             $dba
+        * @param array|null           $argv
+        */
+       public function __construct(Mode $mode, IManageConfigValues $config, IManageKeyValuePairs $keyValue, BasePath $basePath, System $system, LoggerInterface $logger, Database $dba, array $argv = null)
+       {
+               parent::__construct($argv);
+
+               $this->mode     = $mode;
+               $this->config   = $config;
+               $this->keyValue = $keyValue;
+               $this->basePath = $basePath;
+               $this->system   = $system;
+               $this->logger   = $logger;
+               $this->dba       = $dba;
+       }
+
+       protected function getHelp(): string
+       {
+               return <<<HELP
+Daemon - Interacting with the Friendica daemons
+Synopsis
+       bin/console daemon [-h|--help|-?] [-v] [-a] [-f]
+
+Description
+    Interacting with the Friendica daemons
+
+Options
+    -h|--help|-?            Show help information
+    -v                      Show more debug information.
+    -f|--foreground         Runs the daemon in the forgeground
+
+Examples
+       bin/console daemon start -f
+               Starts the daemon in the foreground
+
+       bin/console daemon status
+               Gets the status of the daemon
+HELP;
+       }
+
+       protected function doExecute()
+       {
+               if ($this->mode->isInstall()) {
+                       throw new RuntimeException("Friendica isn't properly installed yet");
+               }
+
+               $this->mode->setExecutor(Mode::DAEMON);
+
+               $this->config->reload();
+
+               if (empty($this->config->get('system', 'pidfile'))) {
+                       throw new RuntimeException(<<< TXT
+                                       Please set system.pidfile in config/local.config.php. For example:
+
+                                               'system' => [
+                                                       'pidfile' => '/path/to/daemon.pid',
+                                               ],
+                                       TXT);
+               }
+
+               $pidfile = $this->config->get('system', 'pidfile');
+
+               $daemonMode = $this->getArgument(0);
+               $foreground = $this->getOption(['f', 'foreground']);
+
+               if (empty($daemonMode)) {
+                       throw new RuntimeException("Please use either 'start', 'stop' or 'status'");
+               }
+
+               $pid = null;
+               if (is_readable($pidfile)) {
+                       $pid = intval(file_get_contents($pidfile));
+               }
+
+               if (empty($pid) && in_array($daemonMode, ['stop', 'status'])) {
+                       $this->keyValue->set('worker_daemon_mode', false);
+                       throw new RuntimeException("Pidfile wasn't found. Is the daemon running?");
+               }
+
+               if ($daemonMode == 'status') {
+                       if (posix_kill($pid, 0)) {
+                               $this->out("Daemon process $pid is running");
+                               return 0;
+                       }
+
+                       unlink($pidfile);
+
+                       $this->keyValue->set('worker_daemon_mode', false);
+                       $this->out("Daemon process $pid isn't running.");
+                       return 0;
+               }
+
+               if ($daemonMode == 'stop') {
+                       posix_kill($pid, SIGTERM);
+                       unlink($pidfile);
+
+                       $this->logger->notice('Worker daemon process was killed', ['pid' => $pid]);
+
+                       $this->keyValue->set('worker_daemon_mode', false);
+                       $this->out("Daemon process $pid was killed.");
+                       return 0;
+               }
+
+               $this->logger->notice('Starting worker daemon', ['pid' => $pid]);
+
+               if (!$foreground) {
+                       $this->out("Starting worker daemon");
+                       $this->dba->disconnect();
+
+                       // Fork a daemon process
+                       $pid = pcntl_fork();
+                       if ($pid == -1) {
+                               $this->logger->warning('Could not fork daemon');
+                               throw new RuntimeException("Daemon couldn't be forked");
+                       } elseif ($pid) {
+                               // The parent process continues here
+                               if (!file_put_contents($pidfile, $pid)) {
+                                       posix_kill($pid, SIGTERM);
+                                       $this->logger->warning('Could not store pid file');
+                                       throw new RuntimeException("Pid file wasn't written");
+                               }
+                               $this->out("Child process started with pid $pid");
+                               $this->logger->notice('Child process started', ['pid' => $pid]);
+                               return 0;
+                       }
+
+                       // We now are in the child process
+                       register_shutdown_function(function () {
+                               posix_kill(posix_getpid(), SIGTERM);
+                               posix_kill(posix_getpid(), SIGHUP);
+                       });
+
+                       // Make the child the main process, detach it from the terminal
+                       if (posix_setsid() < 0) {
+                               return 0;
+                       }
+
+                       // Closing all existing connections with the outside
+                       fclose(STDIN);
+
+                       // And now connect the database again
+                       $this->dba->connect();
+               }
+
+               $this->keyValue->set('worker_daemon_mode', true);
+
+               // Just to be sure that this script really runs endlessly
+               set_time_limit(0);
+
+               $wait_interval = intval($this->config->get('system', 'cron_interval', 5)) * 60;
+
+               $do_cron = true;
+               $last_cron = 0;
+
+               $path = $this->basePath->getPath();
+
+               // Now running as a daemon.
+               while (true) {
+                       // Check the database structure and possibly fixes it
+                       Update::check($path, true);
+
+                       if (!$do_cron && ($last_cron + $wait_interval) < time()) {
+                               $this->logger->info('Forcing cron worker call.', ['pid' => $pid]);
+                               $do_cron = true;
+                       }
+
+                       if ($do_cron || (!$this->system->isMaxLoadReached() && Worker::entriesExists() && Worker::isReady())) {
+                               Worker::spawnWorker($do_cron);
+                       } else {
+                               $this->logger->info('Cool down for 5 seconds', ['pid' => $pid]);
+                               sleep(5);
+                       }
+
+                       if ($do_cron) {
+                               // We force a reconnect of the database connection.
+                               // This is done to ensure that the connection don't get lost over time.
+                               $this->dba->reconnect();
+
+                               $last_cron = time();
+                       }
+
+                       $start = time();
+                       $this->logger->info('Sleeping', ['pid' => $pid, 'until' => gmdate(DateTimeFormat::MYSQL, $start + $wait_interval)]);
+
+                       do {
+                               $seconds = (time() - $start);
+
+                               // logarithmic wait time calculation.
+                               // Background: After jobs had been started, they often fork many workers.
+                               // To not waste too much time, the sleep period increases.
+                               $arg = (($seconds + 1) / ($wait_interval / 9)) + 1;
+                               $sleep = min(1000000, round(log10($arg) * 1000000, 0));
+                               usleep((int)$sleep);
+
+                               $pid = pcntl_waitpid(-1, $status, WNOHANG);
+                               if ($pid > 0) {
+                                       $this->logger->info('Children quit via pcntl_waitpid', ['pid' => $pid, 'status' => $status]);
+                               }
+
+                               $timeout = ($seconds >= $wait_interval);
+                       } while (!$timeout && !Worker\IPC::JobsExists());
+
+                       if ($timeout) {
+                               $do_cron = true;
+                               $this->logger->info('Woke up after $wait_interval seconds.', ['pid' => $pid, 'sleep' => $wait_interval]);
+                       } else {
+                               $do_cron = false;
+                               $this->logger->info('Worker jobs are calling to be forked.', ['pid' => $pid]);
+                       }
+               }
+       }
+}
index e957e9c1a133154f61b55aa1d57f372cdbb1046f..75ff6453b2135870e5901c9e9ddad06e75a96044 100644 (file)
@@ -37,6 +37,7 @@ Commands:
        config                 Edit site config
        contact                Contact management
        createdoxygen          Generate Doxygen headers
+       daemon                 Interacting with the Friendica daemons
        dbstructure            Do database updates
        docbloxerrorchecker    Check the file tree for DocBlox errors
        extract                Generate translation string file for the Friendica project (deprecated)
@@ -75,6 +76,7 @@ HELP;
                'config'                 => Friendica\Console\Config::class,
                'contact'                => Friendica\Console\Contact::class,
                'createdoxygen'          => Friendica\Console\CreateDoxygen::class,
+               'daemon'                 => Friendica\Console\Daemon::class,
                'docbloxerrorchecker'    => Friendica\Console\DocBloxErrorChecker::class,
                'dbstructure'            => Friendica\Console\DatabaseStructure::class,
                'extract'                => Friendica\Console\Extract::class,