Files
cacti/poller_maintenance.php
T
cigamit 9e743fdc24 Resolving Issue #544 and addtional issues
Allow Log Rotation to be other than Daily
Log rotation would not occur under certain conditions.
Purge log file always purged the cacti.log, not the selected log
2017-08-11 09:59:42 -05:00

561 lines
15 KiB
PHP

#!/usr/bin/php -q
<?php
/*
+-------------------------------------------------------------------------+
| Copyright (C) 2004-2017 The Cacti Group |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public License |
| as published by the Free Software Foundation; either version 2 |
| of the License, or (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
+-------------------------------------------------------------------------+
| Cacti: The Complete RRDTool-based Graphing Solution |
+-------------------------------------------------------------------------+
*/
/* we are not talking to the browser */
$no_http_headers = true;
/* do NOT run this script through a web browser */
if (!isset ($_SERVER['argv'][0]) || isset ($_SERVER['REQUEST_METHOD']) || isset ($_SERVER['REMOTE_ADDR'])) {
die('<br><strong>This script is only meant to run at the command line.</strong>');
}
/* let PHP run just as long as it has to */
ini_set('max_execution_time', '0');
error_reporting(E_ALL);
$dir = dirname(__FILE__);
chdir($dir);
/* record the start time */
$poller_start = microtime(true);
include ('./include/global.php');
global $config, $database_default, $archived, $purged;
/* process calling arguments */
$parms = $_SERVER['argv'];
array_shift($parms);
$debug = FALSE;
$force = FALSE;
$archived = 0;
$purged = 0;
if (sizeof($parms)) {
foreach ($parms as $parameter) {
if (strpos($parameter, '=')) {
list($arg, $value) = explode('=', $parameter);
} else {
$arg = $parameter;
$value = '';
}
switch ($arg) {
case '--version' :
case '-V' :
case '-v' :
display_version();
exit;
case '--help' :
case '-H' :
case '-h' :
display_help();
exit;
case '--force' :
$force = true;
break;
case '--debug' :
$debug = true;
break;
default :
echo 'ERROR: Invalid Parameter ' . $parameter . "\n\n";
display_help();
exit;
}
}
}
maint_debug('Checking for Purge Actions');
if ($config['poller_id'] == 1) {
/* are my tables already present? */
$purge = db_fetch_cell('SELECT count(*)
FROM data_source_purge_action');
/* if the table that holds the actions is present, work on it */
if ($purge) {
maint_debug("Purging Required - Files Found $purge");
/* take the purge in steps */
while (true) {
maint_debug('Grabbing 1000 RRDfiles to Remove');
$file_array = db_fetch_assoc('SELECT id, name, local_data_id, action
FROM data_source_purge_action
ORDER BY name
LIMIT 1000');
if (sizeof($file_array) == 0) {
break;
}
if (sizeof($file_array) || $force) {
/* there's something to do for us now */
remove_files($file_array);
if ($force) {
cleanup_ds_and_graphs();
}
}
}
/* record the start time */
$poller_end = microtime(true);
$string = sprintf('RRDMAINT STATS: Time:%4.4f Purged:%s Archived:%s', ($poller_end - $poller_start), $purged, $archived);
cacti_log($string, true, 'SYSTEM');
}
/* removing security tokens older than 90 days */
if (read_config_option('auth_cache_enabled') == 'on') {
db_execute_prepared('DELETE FROM user_auth_cache
WHERE last_update < ?',
array(date('Y-m-d H:i:s', time()-(86400*90))));
} else {
db_execute('TRUNCATE TABLE user_auth_cache');
}
// Check expired accounts
secpass_check_expired();
}
// Check the realtime cache and poller
realtime_purge_cache();
// Check whether the cacti log. Rotations takes place around midnight
if (read_config_option('logrotate_enabled') == 'on') {
$frequency = read_config_option('logrotate_frequency');
if (empty($frequency)) {
$frequency = 1;
}
$last = read_config_option('logrotate_lastrun');
$now = time();
if (empty($last)) {
set_config_option('logrotate_lastrun', time());
} elseif (($last + ($frequency * 86400)) < $now) {
logrotate_rotatenow();
}
}
exit(0);
/** realtime_purge_cache() - This function will purge files in the realtime directory
* that are older than 2 hours without changes */
function realtime_purge_cache() {
/* remove all Realtime files over than 2 hours */
if (read_config_option('realtime_cache_path') != '') {
$cache_path = read_config_option('realtime_cache_path');
if (is_dir($cache_path) && is_writeable($cache_path)) {
foreach (new DirectoryIterator($cache_path) as $fileInfo) {
if ($fileInfo->isDot()) {
continue;
}
// only remove .png and .rrd files
if (
(substr($fileInfo->getFilename(), -4, 4) == '.png') ||
(substr($fileInfo->getFilename(), -4, 4) == '.rrd')
) {
if ((time() - $fileInfo->getMTime()) >= 7200) {
unlink($fileInfo->getRealPath());
}
}
}
}
}
db_execute("DELETE FROM poller_output_realtime WHERE time<FROM_UNIXTIME(UNIX_TIMESTAMP()-300)");
}
/*
* logrotate_rotatenow
* Rotates the cacti log
*/
function logrotate_rotatenow () {
global $config;
$poller_start = microtime(true);
$log = read_config_option('path_cactilog');
if ($log == '') {
$log = $config['base_path'] . '/log/cacti.log';
}
set_config_option('logrotate_lastrun', time());
clearstatcache();
if (is_writable(dirname($log) . '/') && is_writable($log)) {
$perms = octdec(substr(decoct( fileperms($log) ), 2));
$owner = fileowner($log);
$group = filegroup($log);
if ($owner !== FALSE) {
$ext = date('Ymd');
if (file_exists($log . '-' . $ext)) {
$ext = date('YmdHis');
}
if (rename($log, $log . '-' . $ext)) {
touch($log);
chown($log, $owner);
chgrp($log, $group);
chmod($log, $perms);
cacti_log('Cacti Log Rotation - Created Log ' . basename($log) . '-' . $ext);
} else {
cacti_log('Cacti Log Rotation - ERROR: Could not rename ' . basename($log) . ' to ' . basename($log) . '-' . $ext);
}
} else {
cacti_log('Cacti Log Rotation - ERROR: Permissions issue. Please check your log directory');
}
} else {
cacti_log('Cacti Log Rotation - ERROR: Permissions issue. Directory / Log not writable.');
}
logrotate_cleanold();
/* record the start time */
$poller_end = microtime(true);
$string = sprintf('LOGMAINT STATS: Time:%4.4f', ($poller_end - $poller_start));
cacti_log($string, true, 'SYSTEM');
}
/*
* logrotate_cleanold
* Cleans up any old log files that should be removed
*/
function logrotate_cleanold () {
global $config;
$logfile = read_config_option('path_cactilog');
if ($logfile == '') {
$logfile = $config['base_path'] . '/log/cacti.log';
}
$baselogfile = basename($logfile) . '-';
$baseloglen = strlen($baselogfile);
$dir = scandir(dirname($logfile));
$r = read_config_option('logrotate_retain');
if ($r == '' || $r < 0) {
$r = 7;
}
if ($r > 365) {
$r = 365;
}
if ($r == 0) {
return;
}
if (sizeof($dir)) {
foreach ($dir as $d) {
if (substr($d, 0, $baseloglen) == $baselogfile && strlen($d) >= $baseloglen + 8) {
$e = date('Ymd', time() - ($r * 86400));
$f = substr($d, 10, 8);
if ($f < $e) {
if (is_writable(dirname($logfile) . '/' . $d)) {
@unlink(dirname($logfile) . '/' . $d);
cacti_log('Cacti Log Rotation - Purging Log : ' . $d);
} else {
cacti_log('Cacti Log Rotation - ERROR: Can not purge log : ' . $d);
}
}
}
}
}
clearstatcache();
}
/*
* secpass_check_expired
* Checks user accounts to determine if the accounts and/or their passwords should be expired
*/
function secpass_check_expired () {
maint_debug('Checking for Account / Password expiration');
// Expire Old Accounts
$e = read_config_option('secpass_expireaccount');
if ($e > 0 && is_numeric($e)) {
$t = time();
db_execute_prepared("UPDATE user_auth
SET lastlogin = ?
WHERE lastlogin = -1
AND realm = 0
AND enabled = 'on'",
array($t));
$t = $t - (intval($e) * 86400);
db_execute_prepared("UPDATE user_auth
SET enabled = ''
WHERE realm = 0
AND enabled = 'on'
AND lastlogin < ?
AND id > 1",
array($t));
}
$e = read_config_option('secpass_expirepass');
if ($e > 0 && is_numeric($e)) {
$t = time();
db_execute_prepared("UPDATE user_auth
SET lastchange = ?
WHERE lastchange = -1
AND realm = 0
AND enabled = 'on'",
array($t));
$t = $t - (intval($e) * 86400);
db_execute_prepared("UPDATE user_auth
SET must_change_password = 'on'
WHERE realm = 0
AND enabled = 'on'
AND lastchange < ?",
array($t));
}
}
/*
* remove_files
* remove all unwanted files; the list is given by table data_source_purge_action
*/
function remove_files($file_array) {
global $config, $debug, $archived, $purged;
include_once ($config['library_path'] . '/api_graph.php');
include_once ($config['library_path'] . '/api_data_source.php');
maint_debug('RRDClean is now running on ' . sizeof($file_array) . ' items');
/* determine the location of the RRA files */
if (isset ($config['rra_path'])) {
$rra_path = $config['rra_path'];
} else {
$rra_path = $config['base_path'] . '/rra';
}
/* let's prepare the archive directory */
$rrd_archive = read_config_option('rrd_archive', TRUE);
if ($rrd_archive == '') {
$rrd_archive = $rra_path . '/archive';
}
rrdclean_create_path($rrd_archive);
/* now scan the files */
foreach ($file_array as $file) {
$source_file = $rra_path . '/' . $file['name'];
switch ($file['action']) {
case '1' :
if (unlink($source_file)) {
maint_debug('Deleted: ' . $file['name']);
} else {
cacti_log($file['name'] . " ERROR: RRDfile Maintenance unable to delete from $rra_path!", true, 'MAINT');
}
$purged++;
break;
case '3' :
$target_file = $rrd_archive . '/' . $file['name'];
$target_dir = dirname($target_file);
if (!is_dir($target_dir)) {
rrdclean_create_path($target_dir);
}
if (rename($source_file, $target_file)) {
maint_debug('Moved: ' . $file['name'] . ' to: ' . $rrd_archive);
} else {
cacti_log($file['name'] . " ERROR: RRDfile Maintenance unable to move to $rrd_archive!", true, 'MAINT');
}
$archived++;
break;
}
/* drop from data_source_purge_action table */
db_execute_prepared('DELETE FROM `data_source_purge_action`
WHERE name = ?',
array($file['name']));
maint_debug('Delete from data_source_purge_action: ' . $file['name']);
//fetch all local_graph_id's according to this data source
$lgis = db_fetch_assoc_prepared('SELECT DISTINCT gl.id
FROM graph_local AS gl
INNER JOIN graph_templates_item AS gti
ON gl.id = gti.local_graph_id
INNER JOIN data_template_rrd AS dtr
ON dtr.id=gti.task_item_id
INNER JOIN data_local AS dl
ON dtr.local_data_id=dl.id
WHERE (local_data_id=?)',
array($file['local_data_id']));
if (sizeof($lgis)) {
/* anything found? */
maint_debug('Processing ' . sizeof($lgis) . ' Graphs for data source id: ' . $file['local_data_id']);
/* get them all */
foreach ($lgis as $item) {
$remove_lgis[] = $item['id'];
maint_debug('remove local_graph_id=' . $item['id']);
}
/* and remove them in a single run */
if (!empty ($remove_lgis)) {
api_graph_remove_multi($remove_lgis);
}
}
/* remove related data source if any */
if ($file['local_data_id'] > 0) {
maint_debug('Removing Data Source: ' . $file['local_data_id']);
api_data_source_remove($file['local_data_id']);
}
}
maint_debug('RRDClean has finished a purge pass of ' . sizeof($file_array) . ' items');
}
function rrdclean_create_path($path) {
global $config;
if (!is_dir($path)) {
if (mkdir($path, 0775)) {
if ($config['cacti_server_os'] != 'win32') {
$owner_id = fileowner($config['rra_path']);
$group_id = filegroup($config['rra_path']);
// NOTE: chown/chgrp fails for non-root users, checking their
// result is therefore irrevelevant
@chown($path, $owner_id);
@chgrp($path, $group_id);
}
} else {
cacti_log("ERROR: RRDfile Maintenance unable to create directory '" . $path . "'", false, 'MAINT');
}
}
// if path existed, we can return true
return is_dir($path) && is_writable($path);
}
/*
* cleanup_ds_and_graphs - courtesy John Rembo
*/
function cleanup_ds_and_graphs() {
global $config;
include_once ($config['library_path'] . '/rrd.php');
include_once ($config['library_path'] . '/utility.php');
include_once ($config['library_path'] . '/api_graph.php');
include_once ($config['library_path'] . '/api_data_source.php');
include_once ($config['library_path'] . '/functions.php');
$remove_ldis = array ();
$remove_lgis = array ();
maint_debug('RRDClean now cleans up all data sources and graphs');
//fetch all local_data_id's which have appropriate data-sources
$rrds = db_fetch_assoc("SELECT local_data_id, name_cache, data_source_path
FROM data_template_data
WHERE name_cache > ''");
//filter those whose rrd files doesn't exist
foreach ($rrds as $item) {
$ldi = $item['local_data_id'];
$name = $item['name_cache'];
$ds_pth = $item['data_source_path'];
$real_pth = str_replace('<path_rra>', $config['rra_path'], $ds_pth);
if (!file_exists($real_pth)) {
if (!in_array($ldi, $remove_ldis)) {
$remove_ldis[] = $ldi;
maint_debug("RRD file is missing for data source name: $name (local_data_id=$ldi)");
}
}
}
if (empty ($remove_ldis)) {
maint_debug('No missing rrd files found');
return 0;
}
maint_debug('Processing Graphs');
//fetch all local_graph_id's according to filtered rrds
$lgis = db_fetch_assoc('SELECT DISTINCT gl.id
FROM graph_local AS gl
INNER JOIN graph_templates_item AS gti
ON gl.id=gti.local_graph_id
INNER JOIN data_template_rrd AS dtr
ON dtr.id=gti.task_item_id
INNER JOIN data_local AS dl
ON dtr.local_data_id=dl.id
WHERE (' . array_to_sql_or($remove_ldis, 'local_data_id') . ')');
foreach ($lgis as $item) {
$remove_lgis[] = $item['id'];
maint_debug('RRD file missing for local_graph_id=' . $item['id']);
}
if (!empty ($remove_lgis)) {
maint_debug('removing graphs');
api_graph_remove_multi($remove_lgis);
}
maint_debug('removing data sources');
api_data_source_remove_multi($remove_ldis);
maint_debug('removed graphs:' . count($remove_lgis) . ' removed data-sources:' . count($remove_ldis));
}
function maint_debug($message) {
global $debug;
if ($debug) {
echo trim($message) . "\n";
}
}
/* display_version - displays version information */
function display_version() {
$version = get_cacti_version();
echo "Cacti Maintenance Poller, Version $version, " . COPYRIGHT_YEARS . "\n";
}
/*
* display_help
* displays the usage of the function
*/
function display_help() {
display_version();
echo "\nusage: poller_maintenance.php [--force] [--debug]\n\n";
echo "Cacti's maintenance poller. This poller is repsonsible for executing periodic\n";
echo "maintenance activities for Cacti including log rotation, deactivating accounts, etc.\n\n";
echo "Optional:\n";
echo " --force - Force immediate execution, e.g. for testing.\n";
echo " --debug - Display verbose output during execution.\n\n";
}