2012-08-09 02:48:36 +04:00
< ? php
2024-02-08 15:57:48 +03:00
declare ( strict_types = 1 );
2012-08-09 02:48:36 +04:00
/**
2024-05-24 20:43:47 +03:00
* SPDX - FileCopyrightText : 2016 - 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX - FileCopyrightText : 2016 ownCloud , Inc .
* SPDX - License - Identifier : AGPL - 3.0 - only
2013-04-20 23:26:47 +04:00
*/
2024-02-08 15:57:48 +03:00
2017-10-13 22:30:29 +03:00
require_once __DIR__ . '/lib/versioncheck.php' ;
2016-12-19 17:29:52 +03:00
2024-02-08 16:31:34 +03:00
use OCP\App\IAppManager ;
2024-02-08 15:57:48 +03:00
use OCP\BackgroundJob\IJobList ;
2024-02-08 16:16:44 +03:00
use OCP\IAppConfig ;
2024-02-08 15:57:48 +03:00
use OCP\IConfig ;
use OCP\ISession ;
use OCP\ITempManager ;
use OCP\Server ;
use OCP\Util ;
use Psr\Log\LoggerInterface ;
2013-06-10 15:45:19 +04:00
try {
2016-10-06 13:13:02 +03:00
require_once __DIR__ . '/lib/base.php' ;
2013-03-17 03:48:10 +04:00
2024-06-06 16:30:16 +03:00
if ( isset ( $argv [ 1 ]) && ( $argv [ 1 ] === '-h' || $argv [ 1 ] === '--help' )) {
2024-04-08 14:04:14 +03:00
echo ' Description :
Run the background job routine
Usage :
2024-09-15 16:47:11 +03:00
php - f cron . php -- [ - h ] [ -- verbose ] [ < job - classes >... ]
2024-04-08 14:04:14 +03:00
Arguments :
2024-04-11 11:55:39 +03:00
job - classes Optional job class list to only run those jobs
2024-04-08 14:04:14 +03:00
Options :
2024-09-15 16:47:11 +03:00
- h , -- help Display this help message
- v , -- verbose Output more information ' . PHP_EOL ;
2024-04-08 14:04:14 +03:00
exit ( 0 );
}
2024-02-08 15:57:48 +03:00
if ( Util :: needUpgrade ()) {
Server :: get ( LoggerInterface :: class ) -> debug ( 'Update required, skipping cron' , [ 'app' => 'cron' ]);
2015-03-12 23:03:26 +03:00
exit ;
}
2024-02-08 16:38:03 +03:00
$config = Server :: get ( IConfig :: class );
if ( $config -> getSystemValueBool ( 'maintenance' , false )) {
2024-02-08 15:57:48 +03:00
Server :: get ( LoggerInterface :: class ) -> debug ( 'We are in maintenance mode, skipping cron' , [ 'app' => 'cron' ]);
2015-03-12 23:03:26 +03:00
exit ;
2014-07-23 23:29:24 +04:00
}
2024-02-08 16:31:34 +03:00
// Don't do anything if Nextcloud has not been installed
if ( ! $config -> getSystemValueBool ( 'installed' , false )) {
exit ( 0 );
}
2014-02-18 20:17:08 +04:00
// load all apps to get all api routes properly setup
2024-02-08 16:31:34 +03:00
Server :: get ( IAppManager :: class ) -> loadApps ();
2024-02-08 15:57:48 +03:00
Server :: get ( ISession :: class ) -> close ();
2012-08-10 01:52:48 +04:00
2024-09-15 16:47:11 +03:00
$verbose = isset ( $argv [ 1 ]) && ( $argv [ 1 ] === '-v' || $argv [ 1 ] === '--verbose' );
2014-07-02 14:00:35 +04:00
// initialize a dummy memory session
2024-06-25 16:16:40 +03:00
$session = new \OC\Session\Memory ();
2015-07-20 13:59:04 +03:00
$cryptoWrapper = \OC :: $server -> getSessionCryptoWrapper ();
$session = $cryptoWrapper -> wrapSession ( $session );
\OC :: $server -> setSession ( $session );
2014-07-02 13:52:45 +04:00
2024-02-08 15:57:48 +03:00
$logger = Server :: get ( LoggerInterface :: class );
2024-02-08 16:16:44 +03:00
$appConfig = Server :: get ( IAppConfig :: class );
2024-02-08 15:57:48 +03:00
$tempManager = Server :: get ( ITempManager :: class );
2012-08-11 19:18:49 +04:00
2022-05-30 19:01:51 +03:00
$tempManager -> cleanOld ();
2012-08-09 02:48:36 +04:00
2013-06-10 15:45:19 +04:00
// Exit if background jobs are disabled!
2024-02-08 16:16:44 +03:00
$appMode = $appConfig -> getValueString ( 'core' , 'backgroundjobs_mode' , 'ajax' );
2018-01-26 02:14:00 +03:00
if ( $appMode === 'none' ) {
2013-06-10 15:45:19 +04:00
if ( OC :: $CLI ) {
echo 'Background Jobs are disabled!' . PHP_EOL ;
} else {
2020-03-26 11:30:18 +03:00
OC_JSON :: error ([ 'data' => [ 'message' => 'Background jobs disabled!' ]]);
2013-06-10 15:45:19 +04:00
}
2013-04-20 23:26:47 +04:00
exit ( 1 );
2012-08-09 02:48:36 +04:00
}
2012-10-27 01:16:17 +04:00
2013-06-10 15:45:19 +04:00
if ( OC :: $CLI ) {
2015-03-11 03:09:12 +03:00
// set to run indefinitely if needed
2017-03-11 19:04:21 +03:00
if ( strpos ( @ ini_get ( 'disable_functions' ), 'set_time_limit' ) === false ) {
@ set_time_limit ( 0 );
}
2015-03-11 03:09:12 +03:00
2015-04-01 11:31:48 +03:00
// the cron job must be executed with the right user
2016-07-08 16:55:17 +03:00
if ( ! function_exists ( 'posix_getuid' )) {
2020-09-17 18:23:07 +03:00
echo 'The posix extensions are required - see https://www.php.net/manual/en/book.posix.php' . PHP_EOL ;
2017-03-08 22:04:55 +03:00
exit ( 1 );
2016-07-08 16:55:17 +03:00
}
2019-08-17 18:18:17 +03:00
2020-10-03 17:32:49 +03:00
$user = posix_getuid ();
2024-05-14 13:09:15 +03:00
$configUser = fileowner ( OC :: $configDir . 'config.php' );
if ( $user !== $configUser ) {
echo 'Console has to be executed with the user that owns the file config/config.php' . PHP_EOL ;
2020-10-03 17:32:49 +03:00
echo 'Current user id: ' . $user . PHP_EOL ;
2024-05-14 13:09:15 +03:00
echo 'Owner id of config.php: ' . $configUser . PHP_EOL ;
2017-03-08 22:04:55 +03:00
exit ( 1 );
2015-04-01 11:31:48 +03:00
}
2020-10-03 17:32:49 +03:00
2018-01-26 02:14:00 +03:00
// We call Nextcloud from the CLI (aka cron)
if ( $appMode !== 'cron' ) {
2024-02-08 16:16:44 +03:00
$appConfig -> setValueString ( 'core' , 'backgroundjobs_mode' , 'cron' );
2013-06-10 15:45:19 +04:00
}
2022-01-31 19:59:09 +03:00
// Low-load hours
$onlyTimeSensitive = false ;
$startHour = $config -> getSystemValueInt ( 'maintenance_window_start' , 100 );
if ( $startHour <= 23 ) {
$date = new \DateTime ( 'now' , new \DateTimeZone ( 'UTC' ));
$currentHour = ( int ) $date -> format ( 'G' );
$endHour = $startHour + 4 ;
if ( $startHour <= 20 ) {
// Start time: 01:00
// End time: 05:00
// Only run sensitive tasks when it's before the start or after the end
$onlyTimeSensitive = $currentHour < $startHour || $currentHour > $endHour ;
} else {
// Start time: 23:00
// End time: 03:00
$endHour -= 24 ; // Correct the end time from 27:00 to 03:00
// Only run sensitive tasks when it's after the end and before the start
$onlyTimeSensitive = $currentHour > $endHour && $currentHour < $startHour ;
}
}
2013-06-10 15:45:19 +04:00
// Work
2024-02-08 15:57:48 +03:00
$jobList = Server :: get ( IJobList :: class );
2016-01-28 17:34:50 +03:00
2020-02-16 22:50:50 +03:00
// We only ask for jobs for 14 minutes, because after 5 minutes the next
// system cron task should spawn and we want to have at most three
// cron jobs running in parallel.
2016-04-21 11:33:44 +03:00
$endTime = time () + 14 * 60 ;
2016-01-28 17:34:50 +03:00
$executedJobs = [];
2024-04-11 11:55:39 +03:00
// a specific job class list can optionally be given as argument
2024-09-15 16:47:11 +03:00
$jobClasses = array_slice ( $argv , $verbose ? 2 : 1 );
2024-04-11 11:55:39 +03:00
$jobClasses = empty ( $jobClasses ) ? null : $jobClasses ;
2024-04-08 18:25:51 +03:00
while ( $job = $jobList -> getNext ( $onlyTimeSensitive , $jobClasses )) {
2016-01-28 17:34:50 +03:00
if ( isset ( $executedJobs [ $job -> getId ()])) {
2016-05-18 15:27:48 +03:00
$jobList -> unlockJob ( $job );
2016-01-28 17:34:50 +03:00
break ;
}
2024-01-10 17:51:22 +03:00
$jobDetails = get_class ( $job ) . ' (id: ' . $job -> getId () . ', arguments: ' . json_encode ( $job -> getArgument ()) . ')' ;
2023-12-20 17:35:40 +03:00
$logger -> debug ( 'CLI cron call has selected job ' . $jobDetails , [ 'app' => 'cron' ]);
2023-12-20 12:58:08 +03:00
2024-06-12 10:34:57 +03:00
$timeBefore = time ();
2023-12-20 12:58:08 +03:00
$memoryBefore = memory_get_usage ();
$memoryPeakBefore = memory_get_peak_usage ();
2024-09-15 16:47:11 +03:00
if ( $verbose ) {
echo 'Starting job ' . $jobDetails . PHP_EOL ;
}
2023-12-20 12:58:08 +03:00
2024-03-12 13:23:35 +03:00
/** @psalm-suppress DeprecatedMethod Calling execute until it is removed, then will switch to start */
2024-03-12 13:01:46 +03:00
$job -> execute ( $jobList );
2022-05-30 19:01:51 +03:00
2024-06-12 10:34:57 +03:00
$timeAfter = time ();
2023-12-20 12:58:08 +03:00
$memoryAfter = memory_get_usage ();
$memoryPeakAfter = memory_get_peak_usage ();
2024-06-12 10:34:57 +03:00
$cronInterval = 5 * 60 ;
$timeSpent = $timeAfter - $timeBefore ;
if ( $timeSpent > $cronInterval ) {
$logLevel = match ( true ) {
$timeSpent > $cronInterval * 128 => \OCP\ILogger :: FATAL ,
$timeSpent > $cronInterval * 64 => \OCP\ILogger :: ERROR ,
$timeSpent > $cronInterval * 16 => \OCP\ILogger :: WARN ,
$timeSpent > $cronInterval * 8 => \OCP\ILogger :: INFO ,
default => \OCP\ILogger :: DEBUG ,
};
$logger -> log (
$logLevel ,
'Background job ' . $jobDetails . ' ran for ' . $timeSpent . ' seconds' ,
[ 'app' => 'cron' ]
);
}
2024-05-27 16:19:48 +03:00
if ( $memoryAfter - $memoryBefore > 50_000_000 ) {
2024-09-15 16:47:11 +03:00
$message = 'Used memory grew by more than 50 MB when executing job ' . $jobDetails . ': ' . Util :: humanFileSize ( $memoryAfter ) . ' (before: ' . Util :: humanFileSize ( $memoryBefore ) . ')' ;
$logger -> warning ( $message , [ 'app' => 'cron' ]);
if ( $verbose ) {
echo $message . PHP_EOL ;
}
2023-12-20 12:58:08 +03:00
}
2024-05-27 16:19:48 +03:00
if ( $memoryPeakAfter > 300_000_000 && $memoryPeakBefore <= 300_000_000 ) {
2024-09-15 16:47:11 +03:00
$message = 'Cron job used more than 300 MB of ram after executing job ' . $jobDetails . ': ' . Util :: humanFileSize ( $memoryPeakAfter ) . ' (before: ' . Util :: humanFileSize ( $memoryPeakBefore ) . ')' ;
$logger -> warning ( $message , [ 'app' => 'cron' ]);
if ( $verbose ) {
echo $message . PHP_EOL ;
}
2023-12-20 12:58:08 +03:00
}
2016-09-30 14:30:16 +03:00
// clean up after unclean jobs
2024-02-08 16:31:34 +03:00
Server :: get ( \OC\Files\SetupManager :: class ) -> tearDown ();
2022-05-30 19:01:51 +03:00
$tempManager -> clean ();
2016-01-28 17:34:50 +03:00
2024-09-15 16:47:11 +03:00
if ( $verbose ) {
echo 'Job ' . $jobDetails . ' done in ' . ( $timeAfter - $timeBefore ) . ' seconds' . PHP_EOL ;
}
2016-01-28 17:34:50 +03:00
$jobList -> setLastJob ( $job );
$executedJobs [ $job -> getId ()] = true ;
unset ( $job );
2016-04-21 11:33:44 +03:00
2024-06-12 10:34:57 +03:00
if ( $timeAfter > $endTime ) {
2016-04-21 11:33:44 +03:00
break ;
}
2013-06-10 15:45:19 +04:00
}
} else {
// We call cron.php from some website
2017-05-30 13:10:10 +03:00
if ( $appMode === 'cron' ) {
2013-06-10 15:45:19 +04:00
// Cron is cron :-P
2020-03-26 11:30:18 +03:00
OC_JSON :: error ([ 'data' => [ 'message' => 'Backgroundjobs are using system cron!' ]]);
2013-06-10 15:45:19 +04:00
} else {
// Work and success :-)
2024-02-08 15:57:48 +03:00
$jobList = Server :: get ( IJobList :: class );
2013-06-10 15:45:19 +04:00
$job = $jobList -> getNext ();
2014-07-24 15:54:55 +04:00
if ( $job != null ) {
2022-04-23 16:42:37 +03:00
$logger -> debug ( 'WebCron call has selected job with ID ' . strval ( $job -> getId ()), [ 'app' => 'cron' ]);
2024-03-12 13:23:35 +03:00
/** @psalm-suppress DeprecatedMethod Calling execute until it is removed, then will switch to start */
2024-03-12 13:01:46 +03:00
$job -> execute ( $jobList );
2014-07-24 15:46:40 +04:00
$jobList -> setLastJob ( $job );
}
2013-06-10 15:45:19 +04:00
OC_JSON :: success ();
}
2012-08-10 01:49:20 +04:00
}
2012-10-27 01:16:17 +04:00
2014-10-17 14:08:31 +04:00
// Log the successful cron execution
2024-02-08 16:16:44 +03:00
$appConfig -> setValueInt ( 'core' , 'lastcron' , time ());
2013-06-10 15:45:19 +04:00
exit ();
} catch ( Exception $ex ) {
2024-02-08 15:57:48 +03:00
Server :: get ( LoggerInterface :: class ) -> error (
$ex -> getMessage (),
[ 'app' => 'cron' , 'exception' => $ex ]
);
2021-08-19 13:40:55 +03:00
echo $ex . PHP_EOL ;
2021-07-18 18:06:45 +03:00
exit ( 1 );
2016-04-20 19:01:47 +03:00
} catch ( Error $ex ) {
2024-02-08 15:57:48 +03:00
Server :: get ( LoggerInterface :: class ) -> error (
$ex -> getMessage (),
[ 'app' => 'cron' , 'exception' => $ex ]
);
2021-08-19 13:40:55 +03:00
echo $ex . PHP_EOL ;
2021-07-18 18:06:45 +03:00
exit ( 1 );
2013-08-18 13:02:08 +04:00
}