clientside_kickoff === false) { if ((time() - $system_global->package_check_ts) < DUP_PRO_Constants::PACKAGE_CHECK_TIME_IN_SEC) { return; } } if ($global->lock_mode == DUP_PRO_Thread_Lock_Mode::Flock) { $locking_file = @fopen(DUPLICATOR_PRO_LOCKING_FILE_FILENAME, 'c+'); } else { $locking_file = true; } DUP_PRO_Log::trace('Running package runner init'); if ($locking_file != false) { if ($global->lock_mode == DUP_PRO_Thread_Lock_Mode::Flock) { $acquired_lock = (flock($locking_file, LOCK_EX | LOCK_NB) != false); if ($acquired_lock) { DUP_PRO_Log::trace("File lock acquired: " . DUPLICATOR_PRO_LOCKING_FILE_FILENAME); } else { DUP_PRO_Log::trace("File lock denied " . DUPLICATOR_PRO_LOCKING_FILE_FILENAME); } } else { $acquired_lock = DUP_PRO_U::getSqlLock(); } if ($acquired_lock) { DUP_PRO_Log::trace("Acquired lock so executing package runner init core code"); $system_global->package_check_ts = time(); $system_global->save(); $pending_cancellations = DUP_PRO_Package::get_pending_cancellations(); self::cancel_long_running($pending_cancellations); if (count($pending_cancellations) > 0) { foreach ($pending_cancellations as $package_id_to_cancel) { DUP_PRO_Log::trace("looking to cancel $package_id_to_cancel"); $package_to_cancel = DUP_PRO_Package::get_by_id((int) $package_id_to_cancel); if ($package_to_cancel == false) { continue; } if ($package_to_cancel->Status == DUP_PRO_PackageStatus::STORAGE_PROCESSING) { $package_to_cancel->cancel_all_uploads(); $package_to_cancel->process_storages(); $package_to_cancel->set_status(DUP_PRO_PackageStatus::STORAGE_CANCELLED); } else { $package_to_cancel->set_status(DUP_PRO_PackageStatus::BUILD_CANCELLED); } $package_to_cancel->post_scheduled_build_failure(); } DUP_PRO_Package::clear_pending_cancellations(); } if (empty($_REQUEST['action']) || $_REQUEST['action'] != 'duplicator_pro_process_worker') { self::process_schedules(); $kick_off_worker = DUP_PRO_Package::isPackageRunning(); } if ($global->lock_mode == DUP_PRO_Thread_Lock_Mode::Flock) { if (!flock($locking_file, LOCK_UN)) { DUP_PRO_Log::trace("File lock cant release " . $locking_file); } else { DUP_PRO_Log::trace("File lock released " . $locking_file); } fclose($locking_file); } else { DUP_PRO_U::releaseSqlLock(); } } } else { DUP_PRO_Log::trace("Problem opening locking file so auto switching to SQL lock mode"); $global->lock_mode = DUP_PRO_Thread_Lock_Mode::SQL_Lock; $global->save(); exit(); } if ($kick_off_worker || self::$delayed_exit_and_kickoff) { self::kick_off_worker(); } elseif (is_admin() && (isset($_REQUEST['page']) && (strpos($_REQUEST['page'], DUP_PRO_Constants::PLUGIN_SLUG) !== false))) { DUP_PRO_Log::trace("************kicking off slug worker"); // If it's one of our pages force it to kick off the client self::kick_off_worker(true); } if (self::$delayed_exit_and_kickoff) { self::$delayed_exit_and_kickoff = false; exit(); } } /** * Add javascript for cliean side Kick off * * @return void */ public static function add_kickoff_worker_javascript() { $global = DUP_PRO_Global_Entity::getInstance(); $custom_url = strtolower($global->custom_ajax_url); $CLIENT_CALL_PERIOD_IN_MS = 20000; // How often client calls into the service if ($global->ajax_protocol == 'custom') { if (DUP_PRO_STR::startsWith($custom_url, 'http')) { $ajax_url = $custom_url; } else { // Revert to http standard if they don't have the url correct $ajax_url = admin_url('admin-ajax.php', 'http'); DUP_PRO_Log::trace("Even though custom ajax url configured, incorrect url set so reverting to $ajax_url"); } } else { $ajax_url = admin_url('admin-ajax.php', $global->ajax_protocol); } $gateway = array( 'ajaxurl' => $ajax_url, 'client_call_frequency' => $CLIENT_CALL_PERIOD_IN_MS, 'duplicator_pro_process_worker_nonce' => wp_create_nonce('duplicator_pro_process_worker'), ); wp_register_script('dup-pro-kick', DUPLICATOR_PRO_PLUGIN_URL . 'assets/js/dp-kick.js', array('jquery'), DUPLICATOR_PRO_VERSION); wp_localize_script('dup-pro-kick', 'dp_gateway', $gateway); DUP_PRO_Log::trace('KICKOFF: Client Side'); wp_enqueue_script('dup-pro-kick'); } /** * Checks active packages for being stuck or running too long and adds them for canceling * * @param int[] $pending_cancellations List of package ids to be cancelled * * @return void */ public static function cancel_long_running(&$pending_cancellations) { if (!DUP_PRO_Package::isPackageRunning()) { return; } $active_package = DUP_PRO_Package::get_next_active_package(); if ($active_package === null) { DUP_PRO_Log::trace("Active package returned null"); return; } $global = DUP_PRO_Global_Entity::getInstance(); $system_global = SystemGlobalEntity::getInstance(); $buildStarted = $active_package->timer_start > 0; $active_package->timer_start = $buildStarted ? $active_package->timer_start : DUP_PRO_U::getMicrotime(); $elapsed_sec = $buildStarted ? DUP_PRO_U::getMicrotime() - $active_package->timer_start : 0; $elapsed_minutes = $elapsed_sec / 60; $addedForCancelling = false; if ($buildStarted && $global->max_package_runtime_in_min > 0 && $elapsed_minutes > $global->max_package_runtime_in_min) { if ($active_package->build_progress->current_build_mode != DUP_PRO_Archive_Build_Mode::DupArchive) { $system_global->addQuickFix( __('Package was cancelled because it exceeded Max Build Time.', 'duplicator-pro'), sprintf( __( 'Click button to switch to the DupArchive engine. Please see this %1$sFAQ%2$s for other possible solutions.', 'duplicator-pro' ), '', '' ), array( 'global' => array( 'archive_build_mode' => DUP_PRO_Archive_Build_Mode::DupArchive, ), ) ); } elseif ($global->max_package_runtime_in_min < self::DEFAULT_MAX_BUILD_TIME_IN_MIN) { $system_global->addQuickFix( __('Package was cancelled because it exceeded Max Build Time.', 'duplicator-pro'), sprintf( __( 'Click button to increase Max Build Time. Please see this %1$sFAQ%2$s for other possible solutions.', 'duplicator-pro' ), '', '' ), array( 'global' => array( 'max_package_runtime_in_min' => self::DEFAULT_MAX_BUILD_TIME_IN_MIN, ), ) ); } DUP_PRO_Log::infoTrace("Package $active_package->ID has been going for $elapsed_minutes minutes so cancelling. ($elapsed_sec)"); array_push($pending_cancellations, $active_package->ID); $addedForCancelling = true; } if ((($active_package->Status == DUP_PRO_PackageStatus::AFTER_SCAN) || ($active_package->Status == DUP_PRO_PackageStatus::PRE_PROCESS)) && ($global->clientside_kickoff == false)) { // Traditionally package considered stuck if > 75 but that was with time % 5 so multiplying by 5 to compensate now if ($elapsed_sec > self::PACKAGE_STUCK_TIME_IN_SEC) { DUP_PRO_Log::trace("*** STUCK"); $showDefault = true; if (isset($_SERVER['AUTH_TYPE']) && $_SERVER['AUTH_TYPE'] == 'Basic' && !$global->basic_auth_enabled) { $system_global->addQuickFix( __('Set authentication username and password', 'duplicator-pro'), __('Automatically set basic auth username and password', 'duplicator-pro'), array( 'special' => array('set_basic_auth' => 1), ) ); $showDefault = false; } if (SnapURL::isCurrentUrlSSL() && $global->ajax_protocol == 'http') { $system_global->addQuickFix( __('Communication to AJAX is blocked.', 'duplicator-pro'), __('Click button to configure plugin to use HTTPS.', 'duplicator-pro'), array( 'special' => array('stuck_5percent_pending_fix' => 1), ) ); } elseif (!SnapURL::isCurrentUrlSSL() && $global->ajax_protocol == 'https') { $system_global->addQuickFix( __('Communication to AJAX is blocked.', 'duplicator-pro'), __('Click button to configure plugin to use HTTP.', 'duplicator-pro'), array( 'special' => array('stuck_5percent_pending_fix' => 1), ) ); } elseif ($global->ajax_protocol == 'custom') { $system_global->addQuickFix( __('Communication to AJAX is blocked.', 'duplicator-pro'), __('Click button to fix the admin-ajax URL setting.', 'duplicator-pro'), array( 'special' => array('stuck_5percent_pending_fix' => 1), ) ); } elseif ($showDefault) { $system_global->addTextFix( __('Communication to AJAX is blocked.', 'duplicator-pro'), sprintf( "%s %s", __('See FAQ:', 'duplicator-pro'), __('Why is the package build stuck at 5%?', 'duplicator-pro') ) ); } DUP_PRO_Log::infoTrace("Package $active_package->ID has been stuck for $elapsed_minutes minutes so cancelling. ($elapsed_sec)"); array_push($pending_cancellations, $active_package->ID); $addedForCancelling = true; } } if ($addedForCancelling) { $active_package->buildFail( 'Package was cancelled because it exceeded Max Build Time.', false ); } else { $active_package->save(); } } /** * Kick off worker * * @param bool $run_only_if_client If true then only kick off worker if the request came from the client * * @return void */ public static function kick_off_worker($run_only_if_client = false) { /* @var $global DUP_PRO_Global_Entity */ $global = DUP_PRO_Global_Entity::getInstance(); if (!$run_only_if_client || $global->clientside_kickoff) { $calling_function_name = SnapUtil::getCallingFunctionName(); DUP_PRO_Log::trace("Kicking off worker process as requested by $calling_function_name"); $custom_url = strtolower($global->custom_ajax_url); if ($global->ajax_protocol == 'custom') { if (DUP_PRO_STR::startsWith($custom_url, 'http')) { $ajax_url = $custom_url; } else { // Revert to http standard if they don't have the url correct $ajax_url = admin_url('admin-ajax.php', 'http'); DUP_PRO_Log::trace("Even though custom ajax url configured, incorrect url set so reverting to $ajax_url"); } } else { $ajax_url = admin_url('admin-ajax.php', $global->ajax_protocol); } DUP_PRO_Log::trace("Attempting to use ajax url $ajax_url"); if ($global->clientside_kickoff) { add_action('wp_enqueue_scripts', 'DUP_PRO_Package_Runner::add_kickoff_worker_javascript'); add_action('admin_enqueue_scripts', 'DUP_PRO_Package_Runner::add_kickoff_worker_javascript'); } else { // Server-side kickoff $ajax_url = SnapURL::appendQueryValue($ajax_url, 'action', 'duplicator_pro_process_worker'); $ajax_url = SnapURL::appendQueryValue($ajax_url, 'now', time()); // $duplicator_pro_process_worker_nonce = wp_create_nonce('duplicator_pro_process_worker'); //require_once(ABSPATH.'wp-includes/pluggable.php'); //$ajax_url = wp_nonce_url($ajax_url, 'duplicator_pro_process_worker', 'nonce'); DUP_PRO_Log::trace('KICKOFF: Server Side'); if ($global->basic_auth_enabled) { $sglobal = DUP_PRO_Secure_Global_Entity::getInstance(); $args = array( 'blocking' => false, 'headers' => array('Authorization' => 'Basic ' . base64_encode($global->basic_auth_user . ':' . $sglobal->basic_auth_password)), ); } else { $args = array('blocking' => false); } $args['sslverify'] = false; wp_remote_get($ajax_url, $args); } DUP_PRO_Log::trace("after sent kickoff request"); } } /** * Process schedules by cron * * @return void */ public static function process() { if (!defined('WP_MAX_MEMORY_LIMIT')) { define('WP_MAX_MEMORY_LIMIT', '512M'); } if (SnapUtil::isIniValChangeable('memory_limit')) { @ini_set('memory_limit', WP_MAX_MEMORY_LIMIT); } @set_time_limit(7200); @ignore_user_abort(true); if (SnapUtil::isIniValChangeable('pcre.backtrack_limit')) { @ini_set('pcre.backtrack_limit', (string) PHP_INT_MAX); } if (SnapUtil::isIniValChangeable('default_socket_timeout')) { @ini_set('default_socket_timeout', '7200'); // 2 Hours } /* @var $global DUP_PRO_Global_Entity */ $global = DUP_PRO_Global_Entity::getInstance(); if ($global->clientside_kickoff) { DUP_PRO_Log::trace("PROCESS: From client"); session_write_close(); } else { DUP_PRO_Log::trace("PROCESS: From server"); } // Only attempt to process schedules if manual isn't running if ($global->lock_mode == DUP_PRO_Thread_Lock_Mode::Flock) { $locking_file = fopen(DUPLICATOR_PRO_LOCKING_FILE_FILENAME, 'c+'); } else { $locking_file = true; } if ($locking_file == false) { DUP_PRO_Log::trace("Problem opening locking file so auto switching to SQL lock mode"); $global->lock_mode = DUP_PRO_Thread_Lock_Mode::SQL_Lock; $global->save(); exit(); } // Here we know that $locking_file != false if ($global->lock_mode == DUP_PRO_Thread_Lock_Mode::Flock) { $acquired_lock = (flock($locking_file, LOCK_EX | LOCK_NB) != false); if ($acquired_lock) { DUP_PRO_Log::trace("File lock acquired " . $locking_file); } else { DUP_PRO_Log::trace("File lock denied " . $locking_file); } } else { // DUP_PRO_U::getSqlLock will write details into trace log, logging is not needed here $acquired_lock = DUP_PRO_U::getSqlLock(); } if (!$acquired_lock) { // File locked so another cron already running so just skip DUP_PRO_Log::trace("File locked so skipping"); return; } // Here we know that $acquired_lock == true self::process_schedules(); $package = DUP_PRO_Package::get_next_active_package(); if ($package != null) { StoragesUtil::getDefaultStorage()->initStorageDirectory(true); $dup_tests = self::get_requirements_tests(); if ($dup_tests['Success'] == true) { $start_time = time(); DUP_PRO_Log::trace("PACKAGE $package->ID:PROCESSING"); ignore_user_abort(true); if ($package->Status < DUP_PRO_PackageStatus::AFTER_SCAN) { // Scan step built into package build - used by schedules - NOT manual build where scan is done in web service. DUP_PRO_Log::trace("PACKAGE $package->ID:SCANNING"); //After scanner runs. Save FilterInfo (unreadable, warnings, globals etc) $package->create_scan_report(); $package->update(); //del if($package->Archive->ScanStatus == DUP_PRO_Archive::ScanStatusComplete){ $dupe_package = DUP_PRO_Package::get_by_id($package->ID); $dupe_package->set_status(DUP_PRO_PackageStatus::AFTER_SCAN); //del } $end_time = time(); $scan_time = $end_time - $start_time; //del $end_time = DUP_PRO_U::getMicrotime(); // // $scan_time = $end_time - $package->Archive->ScanTimeStart; DUP_PRO_Log::trace("SCAN TIME=$scan_time seconds"); } elseif ($package->Status < DUP_PRO_PackageStatus::COPIEDPACKAGE) { DUP_PRO_Log::trace("PACKAGE $package->ID:BUILDING"); $package->run_build(); $end_time = time(); $build_time = $end_time - $start_time; DUP_PRO_Log::trace("BUILD TIME=$build_time seconds"); } elseif ($package->Status < DUP_PRO_PackageStatus::COMPLETE) { DUP_PRO_Log::trace("PACKAGE $package->ID:STORAGE PROCESSING"); $package->set_status(DUP_PRO_PackageStatus::STORAGE_PROCESSING); $package->process_storages(); $end_time = time(); $build_time = $end_time - $start_time; DUP_PRO_Log::trace("STORAGE CHUNK PROCESSING TIME=$build_time seconds"); if ($package->Status == DUP_PRO_PackageStatus::COMPLETE) { DUP_PRO_Log::trace("PACKAGE $package->ID COMPLETE"); } elseif ($package->Status == DUP_PRO_PackageStatus::ERROR) { DUP_PRO_Log::trace("PACKAGE $package->ID IN ERROR STATE"); } $packageCompleteStatuses = array( DUP_PRO_PackageStatus::COMPLETE, DUP_PRO_PackageStatus::ERROR, ); if (in_array($package->Status, $packageCompleteStatuses)) { $info = "\n"; $info .= "********************************************************************************\n"; $info .= "********************************************************************************\n"; $info .= "DUPLICATOR PRO PACKAGE CREATION OR MANUAL STORAGE TRANSFER END: " . @date("Y-m-d H:i:s") . "\n"; $info .= "NOTICE: Do NOT post to public sites or forums \n"; $info .= "********************************************************************************\n"; $info .= "********************************************************************************\n"; DUP_PRO_Log::infoTrace($info); } } ignore_user_abort(false); } else { DUP_PRO_Log::open($package->NameHash); if ($dup_tests['RES']['INSTALL'] == 'Fail') { DUP_PRO_Log::info('Installer files still present on site. Remove using Tools > Stored Data > "Remove Installer Files".'); } DUP_PRO_Log::error(__('Requirements Failed', 'duplicator-pro'), print_r($dup_tests, true), false); DUP_PRO_Log::traceError('Requirements didn\'t pass so can\'t perform backup!'); $package->post_scheduled_build_failure($dup_tests); $package->set_status(DUP_PRO_PackageStatus::REQUIREMENTS_FAILED); } } //$kick_off_worker = (DUP_PRO_Package::get_next_active_package() != null); $kick_off_worker = DUP_PRO_Package::isPackageRunning(); if ($global->lock_mode == DUP_PRO_Thread_Lock_Mode::Flock) { DUP_PRO_Log::trace("File lock released"); if (!flock($locking_file, LOCK_UN)) { DUP_PRO_Log::trace("File lock cant release " . $locking_file); } else { DUP_PRO_Log::trace("File lock released " . $locking_file); } fclose($locking_file); } else { DUP_PRO_U::releaseSqlLock(); } if ($kick_off_worker) { self::kick_off_worker(); } } /** * Gets the requirements tests * * @return array */ public static function get_requirements_tests() { $dup_tests = DUP_PRO_Server::getRequirments(); if ($dup_tests['Success'] != true) { DUP_PRO_Log::traceObject('requirements', $dup_tests); } return $dup_tests; } /** * Calculates the earliest schedule run time * * @return int */ public static function calculate_earliest_schedule_run_time() { if (!License::can(License::CAPABILITY_SCHEDULE)) { return -1; } $next_run_time = PHP_INT_MAX; $schedules = DUP_PRO_Schedule_Entity::get_active(); foreach ($schedules as $schedule) { if ($schedule->next_run_time == -1) { $schedule->updateNextRuntime(); } if ($schedule->next_run_time !== -1 && $schedule->next_run_time < $next_run_time) { $next_run_time = $schedule->next_run_time; } } if ($next_run_time == PHP_INT_MAX) { $next_run_time = -1; } return $next_run_time; } /** * Start schedule package creation * * @return void */ public static function process_schedules() { // Hack fix - observed issue on a machine where schedule process bombs $next_run_time = self::calculate_earliest_schedule_run_time(); if ($next_run_time != -1 && ($next_run_time <= time())) { $schedules = DUP_PRO_Schedule_Entity::get_active(); foreach ($schedules as $schedule) { $schedule->process(); } } } }