2012-08-08 18:48:36 -04:00
< ? php
2024-02-08 07:57:48 -05:00
declare ( strict_types = 1 );
2012-08-08 18:48:36 -04:00
/**
2024-05-24 13:43:47 -04: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 15:26:47 -04:00
*/
2024-02-08 07:57:48 -05:00
2017-10-13 15:30:29 -04:00
require_once __DIR__ . '/lib/versioncheck.php' ;
2016-12-19 09:29:52 -05:00
2024-02-08 08:31:34 -05:00
use OCP\App\IAppManager ;
2024-02-08 07:57:48 -05:00
use OCP\BackgroundJob\IJobList ;
2024-02-08 08:16:44 -05:00
use OCP\IAppConfig ;
2024-02-08 07:57:48 -05:00
use OCP\IConfig ;
use OCP\ISession ;
use OCP\ITempManager ;
use OCP\Server ;
use OCP\Util ;
use Psr\Log\LoggerInterface ;
2013-06-10 07:45:19 -04:00
try {
2016-10-06 06:13:02 -04:00
require_once __DIR__ . '/lib/base.php' ;
2013-03-16 19:48:10 -04:00
2024-06-06 09:30:16 -04:00
if ( isset ( $argv [ 1 ]) && ( $argv [ 1 ] === '-h' || $argv [ 1 ] === '--help' )) {
2024-04-08 07:04:14 -04:00
echo ' Description :
Run the background job routine
Usage :
2024-09-15 09:47:11 -04:00
php - f cron . php -- [ - h ] [ -- verbose ] [ < job - classes >... ]
2024-04-08 07:04:14 -04:00
Arguments :
2024-04-11 04:55:39 -04:00
job - classes Optional job class list to only run those jobs
2024-04-08 07:04:14 -04:00
Options :
2024-09-15 09:47:11 -04:00
- h , -- help Display this help message
- v , -- verbose Output more information ' . PHP_EOL ;
2024-04-08 07:04:14 -04:00
exit ( 0 );
}
2024-02-08 07:57:48 -05:00
if ( Util :: needUpgrade ()) {
Server :: get ( LoggerInterface :: class ) -> debug ( 'Update required, skipping cron' , [ 'app' => 'cron' ]);
2015-03-12 16:03:26 -04:00
exit ;
}
2024-02-08 08:38:03 -05:00
$config = Server :: get ( IConfig :: class );
if ( $config -> getSystemValueBool ( 'maintenance' , false )) {
2024-02-08 07:57:48 -05:00
Server :: get ( LoggerInterface :: class ) -> debug ( 'We are in maintenance mode, skipping cron' , [ 'app' => 'cron' ]);
2015-03-12 16:03:26 -04:00
exit ;
2014-07-23 15:29:24 -04:00
}
2024-02-08 08:31:34 -05:00
// Don't do anything if Nextcloud has not been installed
if ( ! $config -> getSystemValueBool ( 'installed' , false )) {
exit ( 0 );
}
2014-02-18 11:17:08 -05:00
// load all apps to get all api routes properly setup
2024-02-08 08:31:34 -05:00
Server :: get ( IAppManager :: class ) -> loadApps ();
2024-02-08 07:57:48 -05:00
Server :: get ( ISession :: class ) -> close ();
2012-08-09 17:52:48 -04:00
2024-09-15 09:47:11 -04:00
$verbose = isset ( $argv [ 1 ]) && ( $argv [ 1 ] === '-v' || $argv [ 1 ] === '--verbose' );
2014-07-02 06:00:35 -04:00
// initialize a dummy memory session
2024-06-25 09:16:40 -04:00
$session = new \OC\Session\Memory ();
2015-07-20 06:59:04 -04:00
$cryptoWrapper = \OC :: $server -> getSessionCryptoWrapper ();
$session = $cryptoWrapper -> wrapSession ( $session );
\OC :: $server -> setSession ( $session );
2014-07-02 05:52:45 -04:00
2024-02-08 07:57:48 -05:00
$logger = Server :: get ( LoggerInterface :: class );
2024-02-08 08:16:44 -05:00
$appConfig = Server :: get ( IAppConfig :: class );
2024-02-08 07:57:48 -05:00
$tempManager = Server :: get ( ITempManager :: class );
2012-08-11 11:18:49 -04:00
2022-05-30 12:01:51 -04:00
$tempManager -> cleanOld ();
2012-08-08 18:48:36 -04:00
2013-06-10 07:45:19 -04:00
// Exit if background jobs are disabled!
2024-02-08 08:16:44 -05:00
$appMode = $appConfig -> getValueString ( 'core' , 'backgroundjobs_mode' , 'ajax' );
2018-01-25 18:14:00 -05:00
if ( $appMode === 'none' ) {
2013-06-10 07:45:19 -04:00
if ( OC :: $CLI ) {
echo 'Background Jobs are disabled!' . PHP_EOL ;
} else {
2020-03-26 04:30:18 -04:00
OC_JSON :: error ([ 'data' => [ 'message' => 'Background jobs disabled!' ]]);
2013-06-10 07:45:19 -04:00
}
2013-04-20 15:26:47 -04:00
exit ( 1 );
2012-08-08 18:48:36 -04:00
}
2012-10-26 17:16:17 -04:00
2013-06-10 07:45:19 -04:00
if ( OC :: $CLI ) {
2015-03-10 20:09:12 -04:00
// set to run indefinitely if needed
2017-03-11 11:04:21 -05:00
if ( strpos ( @ ini_get ( 'disable_functions' ), 'set_time_limit' ) === false ) {
@ set_time_limit ( 0 );
}
2015-03-10 20:09:12 -04:00
2015-04-01 04:31:48 -04:00
// the cron job must be executed with the right user
2016-07-08 09:55:17 -04:00
if ( ! function_exists ( 'posix_getuid' )) {
2020-09-17 11:23:07 -04:00
echo 'The posix extensions are required - see https://www.php.net/manual/en/book.posix.php' . PHP_EOL ;
2017-03-08 14:04:55 -05:00
exit ( 1 );
2016-07-08 09:55:17 -04:00
}
2019-08-17 11:18:17 -04:00
2020-10-03 10:32:49 -04:00
$user = posix_getuid ();
2024-05-14 06:09:15 -04: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 10:32:49 -04:00
echo 'Current user id: ' . $user . PHP_EOL ;
2024-05-14 06:09:15 -04:00
echo 'Owner id of config.php: ' . $configUser . PHP_EOL ;
2017-03-08 14:04:55 -05:00
exit ( 1 );
2015-04-01 04:31:48 -04:00
}
2020-10-03 10:32:49 -04:00
2018-01-25 18:14:00 -05:00
// We call Nextcloud from the CLI (aka cron)
if ( $appMode !== 'cron' ) {
2024-02-08 08:16:44 -05:00
$appConfig -> setValueString ( 'core' , 'backgroundjobs_mode' , 'cron' );
2013-06-10 07:45:19 -04:00
}
2022-01-31 11:59:09 -05: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 07:45:19 -04:00
// Work
2024-02-08 07:57:48 -05:00
$jobList = Server :: get ( IJobList :: class );
2016-01-28 09:34:50 -05:00
2020-02-16 14:50:50 -05: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 04:33:44 -04:00
$endTime = time () + 14 * 60 ;
2016-01-28 09:34:50 -05:00
$executedJobs = [];
2024-04-11 04:55:39 -04:00
// a specific job class list can optionally be given as argument
2024-09-15 09:47:11 -04:00
$jobClasses = array_slice ( $argv , $verbose ? 2 : 1 );
2024-04-11 04:55:39 -04:00
$jobClasses = empty ( $jobClasses ) ? null : $jobClasses ;
2024-04-08 11:25:51 -04:00
while ( $job = $jobList -> getNext ( $onlyTimeSensitive , $jobClasses )) {
2016-01-28 09:34:50 -05:00
if ( isset ( $executedJobs [ $job -> getId ()])) {
2016-05-18 08:27:48 -04:00
$jobList -> unlockJob ( $job );
2016-01-28 09:34:50 -05:00
break ;
}
2024-01-10 09:51:22 -05:00
$jobDetails = get_class ( $job ) . ' (id: ' . $job -> getId () . ', arguments: ' . json_encode ( $job -> getArgument ()) . ')' ;
2023-12-20 09:35:40 -05:00
$logger -> debug ( 'CLI cron call has selected job ' . $jobDetails , [ 'app' => 'cron' ]);
2023-12-20 04:58:08 -05:00
2024-06-12 03:34:57 -04:00
$timeBefore = time ();
2023-12-20 04:58:08 -05:00
$memoryBefore = memory_get_usage ();
$memoryPeakBefore = memory_get_peak_usage ();
2024-09-15 09:47:11 -04:00
if ( $verbose ) {
echo 'Starting job ' . $jobDetails . PHP_EOL ;
}
2023-12-20 04:58:08 -05:00
2024-03-12 06:23:35 -04:00
/** @psalm-suppress DeprecatedMethod Calling execute until it is removed, then will switch to start */
2024-03-12 06:01:46 -04:00
$job -> execute ( $jobList );
2022-05-30 12:01:51 -04:00
2024-06-12 03:34:57 -04:00
$timeAfter = time ();
2023-12-20 04:58:08 -05:00
$memoryAfter = memory_get_usage ();
$memoryPeakAfter = memory_get_peak_usage ();
2024-06-12 03:34:57 -04: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 09:19:48 -04:00
if ( $memoryAfter - $memoryBefore > 50_000_000 ) {
2024-09-15 09:47:11 -04: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 04:58:08 -05:00
}
2024-05-27 09:19:48 -04:00
if ( $memoryPeakAfter > 300_000_000 && $memoryPeakBefore <= 300_000_000 ) {
2024-09-15 09:47:11 -04: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 04:58:08 -05:00
}
2016-09-30 07:30:16 -04:00
// clean up after unclean jobs
2024-02-08 08:31:34 -05:00
Server :: get ( \OC\Files\SetupManager :: class ) -> tearDown ();
2022-05-30 12:01:51 -04:00
$tempManager -> clean ();
2016-01-28 09:34:50 -05:00
2024-09-15 09:47:11 -04:00
if ( $verbose ) {
echo 'Job ' . $jobDetails . ' done in ' . ( $timeAfter - $timeBefore ) . ' seconds' . PHP_EOL ;
}
2016-01-28 09:34:50 -05:00
$jobList -> setLastJob ( $job );
$executedJobs [ $job -> getId ()] = true ;
unset ( $job );
2016-04-21 04:33:44 -04:00
2024-06-12 03:34:57 -04:00
if ( $timeAfter > $endTime ) {
2016-04-21 04:33:44 -04:00
break ;
}
2013-06-10 07:45:19 -04:00
}
} else {
// We call cron.php from some website
2017-05-30 06:10:10 -04:00
if ( $appMode === 'cron' ) {
2013-06-10 07:45:19 -04:00
// Cron is cron :-P
2020-03-26 04:30:18 -04:00
OC_JSON :: error ([ 'data' => [ 'message' => 'Backgroundjobs are using system cron!' ]]);
2013-06-10 07:45:19 -04:00
} else {
// Work and success :-)
2024-02-08 07:57:48 -05:00
$jobList = Server :: get ( IJobList :: class );
2013-06-10 07:45:19 -04:00
$job = $jobList -> getNext ();
2014-07-24 07:54:55 -04:00
if ( $job != null ) {
2022-04-23 09:42:37 -04:00
$logger -> debug ( 'WebCron call has selected job with ID ' . strval ( $job -> getId ()), [ 'app' => 'cron' ]);
2024-03-12 06:23:35 -04:00
/** @psalm-suppress DeprecatedMethod Calling execute until it is removed, then will switch to start */
2024-03-12 06:01:46 -04:00
$job -> execute ( $jobList );
2014-07-24 07:46:40 -04:00
$jobList -> setLastJob ( $job );
}
2013-06-10 07:45:19 -04:00
OC_JSON :: success ();
}
2012-08-09 17:49:20 -04:00
}
2012-10-26 17:16:17 -04:00
2014-10-17 06:08:31 -04:00
// Log the successful cron execution
2024-02-08 08:16:44 -05:00
$appConfig -> setValueInt ( 'core' , 'lastcron' , time ());
2013-06-10 07:45:19 -04:00
exit ();
} catch ( Exception $ex ) {
2024-02-08 07:57:48 -05:00
Server :: get ( LoggerInterface :: class ) -> error (
$ex -> getMessage (),
[ 'app' => 'cron' , 'exception' => $ex ]
);
2021-08-19 06:40:55 -04:00
echo $ex . PHP_EOL ;
2021-07-18 11:06:45 -04:00
exit ( 1 );
2016-04-20 12:01:47 -04:00
} catch ( Error $ex ) {
2024-02-08 07:57:48 -05:00
Server :: get ( LoggerInterface :: class ) -> error (
$ex -> getMessage (),
[ 'app' => 'cron' , 'exception' => $ex ]
);
2021-08-19 06:40:55 -04:00
echo $ex . PHP_EOL ;
2021-07-18 11:06:45 -04:00
exit ( 1 );
2013-08-18 05:02:08 -04:00
}