Skip to content

Commit

Permalink
Merge pull request friendica#14642 from nupplaphil/feat/console/daemon
Browse files Browse the repository at this point in the history
Moving the daemon to a Console class
  • Loading branch information
MrPetovan authored Dec 30, 2024
2 parents afa2969 + eebc2d9 commit acf98cc
Show file tree
Hide file tree
Showing 4 changed files with 262 additions and 222 deletions.
8 changes: 4 additions & 4 deletions bin/daemon.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@

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

Expand All @@ -31,4 +28,7 @@

$app = \Friendica\App::fromDice($dice);

$app->processDaemon($_SERVER['argv'] ?? [], $options ?: []);
$argv = $_SERVER['argv'] ?? [];
array_splice($argv, 1, 0, "daemon");

$app->processConsole($argv);
218 changes: 0 additions & 218 deletions src/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,12 @@
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;
Expand Down Expand Up @@ -222,222 +220,6 @@ public function processConsole(array $argv): void
(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();
Expand Down
Loading

0 comments on commit acf98cc

Please sign in to comment.