1<?php
2/*
3 +-------------------------------------------------------------------------+
4 | Copyright (C) 2004-2021 The Cacti Group                                 |
5 |                                                                         |
6 | This program is free software; you can redistribute it and/or           |
7 | modify it under the terms of the GNU General Public License             |
8 | as published by the Free Software Foundation; either version 2          |
9 | of the License, or (at your option) any later version.                  |
10 |                                                                         |
11 | This program is distributed in the hope that it will be useful,         |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of          |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           |
14 | GNU General Public License for more details.                            |
15 +-------------------------------------------------------------------------+
16 | Cacti: The Complete RRDtool-based Graphing Solution                     |
17 +-------------------------------------------------------------------------+
18 | This code is designed, written, and maintained by the Cacti Group. See  |
19 | about.php and/or the AUTHORS file for specific developer information.   |
20 +-------------------------------------------------------------------------+
21 | http://www.cacti.net/                                                   |
22 +-------------------------------------------------------------------------+
23*/
24
25require('./include/auth.php');
26require_once($config['base_path'] . '/lib/poller.php');
27
28/* performing a full sync can take a lot of memory and time */
29ini_set('memory_limit', '-1');
30ini_set('max_execution_time', '900');
31
32$poller_actions = array(
33	1 => __('Delete'),
34	2 => __('Disable'),
35	3 => __('Enable'),
36	5 => __('Clear Statistics'),
37);
38
39if ($config['poller_id'] == 1) {
40	$poller_actions += array(4 =>__('Full Sync'));
41}
42
43$poller_status = array(
44	0 => '<div class="deviceUnknown">'    . __('New/Idle')     . '</div>',
45	1 => '<div class="deviceUp">'         . __('Running')      . '</div>',
46	2 => '<div class="deviceRecovering">' . __('Idle')         . '</div>',
47	3 => '<div class="deviceDown">'       . __('Down')         . '</div>',
48	4 => '<div class="deviceDisabled">'   . __('Disabled')     . '</div>',
49	5 => '<div class="deviceDown">'       . __('Recovering')   . '</div>',
50	6 => '<div class="deviceDown">'       . __('Heartbeat')    . '</div>',
51);
52
53/* file: pollers.php, action: edit */
54$fields_poller_edit = array(
55	'spacer0' => array(
56		'method' => 'spacer',
57		'friendly_name' => __('Data Collector Information'),
58	),
59	'name' => array(
60		'method' => 'textbox',
61		'friendly_name' => __('Name'),
62		'description' => __('The primary name for this Data Collector.'),
63		'value' => '|arg1:name|',
64		'size' => '50',
65		'default' => __('New Data Collector'),
66		'max_length' => '100'
67	),
68	'hostname' => array(
69		'method' => 'textbox',
70		'friendly_name' => __('Data Collector Hostname'),
71		'description' => __('The hostname for Data Collector.  It may have to be a Fully Qualified Domain name for the remote Pollers to contact it for activities such as re-indexing, Real-time graphing, etc.'),
72		'value' => '|arg1:hostname|',
73		'size' => '50',
74		'default' => '',
75		'max_length' => '100'
76	),
77	'timezone' => array(
78		'method' => 'drop_callback',
79		'friendly_name' => __('TimeZone'),
80		'description' => __('The TimeZone for the Data Collector.'),
81		'sql' => 'SELECT Name AS id, Name AS name FROM mysql.time_zone_name ORDER BY name',
82		'action' => 'ajax_tz',
83		'id' => '|arg1:timezone|',
84		'value' => '|arg1:timezone|'
85		),
86	'notes' => array(
87		'method' => 'textarea',
88		'friendly_name' => __('Notes'),
89		'description' => __('Notes for this Data Collectors Database.'),
90		'value' => '|arg1:notes|',
91		'textarea_rows' => 4,
92		'textarea_cols' => 50
93	),
94	'spacer_collection' => array(
95		'method' => 'spacer',
96		'friendly_name' => __('Collection Settings'),
97	),
98	'processes' => array(
99		'method' => 'textbox',
100		'friendly_name' => __('Processes'),
101		'description' => __('The number of Data Collector processes to use to spawn.'),
102		'value' => '|arg1:processes|',
103		'size' => '10',
104		'default' => read_config_option('concurrent_processes'),
105		'max_length' => '4'
106	),
107	'threads' => array(
108		'method' => 'textbox',
109		'friendly_name' => __('Threads'),
110		'description' => __('The number of Spine Threads to use per Data Collector process.'),
111		'value' => '|arg1:threads|',
112		'size' => '10',
113		'default' => read_config_option('max_threads'),
114		'max_length' => '4'
115	),
116	'sync_interval' => array(
117		'method' => 'drop_array',
118		'friendly_name' => __('Sync Interval'),
119		'description' => __('The polling sync interval in use.  This setting will affect how often this poller is checked and updated.'),
120		'value' => '|arg1:sync_interval|',
121		'default' => read_config_option('poller_sync_interval'),
122		'array' => $poller_sync_intervals,
123	),
124	'spacer_remotedb' => array(
125		'method' => 'spacer',
126		'friendly_name' => __('Remote Database Connection'),
127	),
128	'dbhost' => array(
129		'method' => 'textbox',
130		'friendly_name' => __('Hostname'),
131		'description' => __('The hostname for the remote database server.'),
132		'value' => '|arg1:dbhost|',
133		'size' => '50',
134		'default' => '',
135		'max_length' => '100'
136	),
137	'dbdefault' => array(
138		'method' => 'textbox',
139		'friendly_name' => __('Remote Database Name'),
140		'description' => __('The name of the remote database.'),
141		'value' => '|arg1:dbdefault|',
142		'size' => '20',
143		'default' => $database_default,
144		'max_length' => '20'
145	),
146	'dbuser' => array(
147		'method' => 'textbox',
148		'friendly_name' => __('Remote Database User'),
149		'description' => __('The user name to use to connect to the remote database.'),
150		'value' => '|arg1:dbuser|',
151		'size' => '20',
152		'default' => $database_username,
153		'max_length' => '20'
154	),
155	'dbpass' => array(
156		'method' => 'textbox_password',
157		'friendly_name' => __('Remote Database Password'),
158		'description' => __('The user password to use to connect to the remote database.'),
159		'value' => '|arg1:dbpass|',
160		'size' => '40',
161		'default' => $database_password,
162		'max_length' => '64'
163	),
164	'dbport' => array(
165		'method' => 'textbox',
166		'friendly_name' => __('Remote Database Port'),
167		'description' => __('The TCP port to use to connect to the remote database.'),
168		'value' => '|arg1:dbport|',
169		'size' => '5',
170		'default' => $database_port,
171		'max_length' => '5'
172	),
173	'dbretries' => array(
174		'method' => 'textbox',
175		'friendly_name' => __('Remote Database Retries'),
176		'description' => __('The number of times to attempt to retry to connect to the remote database.'),
177		'value' => '|arg1:dbretries|',
178		'size' => '5',
179		'default' => $database_retries,
180		'max_length' => '5'
181	),
182	'dbssl' => array(
183		'method' => 'checkbox',
184		'friendly_name' => __('Remote Database SSL'),
185		'description' => __('If the remote database uses SSL to connect, check the checkbox below.'),
186		'value' => '|arg1:dbssl|',
187		'default' => $database_ssl ? 'on':''
188	),
189	'dbsslkey' => array(
190		'method' => 'textbox',
191		'friendly_name' => __('Remote Database SSL Key'),
192		'description' => __('The file holding the SSL Key to use to connect to the remote database.'),
193		'value' => '|arg1:dbsslkey|',
194		'size' => '50',
195		'default' => $database_ssl_key,
196		'max_length' => '255'
197	),
198	'dbsslcert' => array(
199		'method' => 'textbox',
200		'friendly_name' => __('Remote Database SSL Certificate'),
201		'description' => __('The file holding the SSL Certificate to use to connect to the remote database.'),
202		'value' => '|arg1:dbsslcert|',
203		'size' => '50',
204		'default' => $database_ssl_cert,
205		'max_length' => '255'
206	),
207	'dbsslca' => array(
208		'method' => 'textbox',
209		'friendly_name' => __('Remote Database SSL Authority'),
210		'description' => __('The file holding the SSL Certificate Authority to use to connect to the remote database.  This is an optional parameter that can be required by the database provider if they have started SSL using the --ssl-mode=VERIFY_CA option.'),
211		'value' => '|arg1:dbsslca|',
212		'size' => '50',
213		'default' => $database_ssl_ca,
214		'max_length' => '255'
215	),
216	'id' => array(
217		'method' => 'hidden',
218		'value' => '|arg1:id|',
219	),
220	'save_component_poller' => array(
221		'method' => 'hidden',
222		'value' => '1'
223	)
224);
225
226/* set default action */
227set_default_action();
228
229switch (get_request_var('action')) {
230	case 'save':
231		form_save();
232
233		break;
234	case 'actions':
235		form_actions();
236
237		break;
238	case 'ajax_tz':
239		print json_encode(db_fetch_assoc_prepared('SELECT Name AS label, Name AS `value`
240			FROM mysql.time_zone_name
241			WHERE Name LIKE ?
242			ORDER BY Name
243			LIMIT ' . read_config_option('autocomplete_rows'),
244			array('%' . get_nfilter_request_var('term') . '%')));
245
246		break;
247	case 'ping':
248		test_database_connection();
249
250		break;
251	case 'edit':
252		top_header();
253
254		poller_edit();
255
256		bottom_footer();
257		break;
258	default:
259		top_header();
260
261		pollers();
262
263		bottom_footer();
264		break;
265}
266
267/* --------------------------
268    Global Form Functions
269   -------------------------- */
270
271/* --------------------------
272    The Save Function
273   -------------------------- */
274
275function form_save() {
276	if (isset_request_var('save_component_poller')) {
277
278		// Common data
279		$save['id']       = get_filter_request_var('id');
280		$save['name']     = form_input_validate(get_nfilter_request_var('name'), 'name', '', false, 3);
281		$save['hostname'] = form_input_validate(get_nfilter_request_var('hostname'), 'hostname', '', false, 3);
282		$save['timezone'] = form_input_validate(get_nfilter_request_var('timezone'), 'timezone', '', false, 3);
283		$save['notes']    = form_input_validate(get_nfilter_request_var('notes'), 'notes', '', true, 3);
284
285		// Process settings
286		$save['processes'] = form_input_validate(get_nfilter_request_var('processes'), 'processes', '^[0-9]+$', false, 3);
287		$save['threads']   = form_input_validate(get_nfilter_request_var('threads'), 'threads', '^[0-9]+$', false, 3);
288
289		if ($save['id'] != 1) {
290			$save['sync_interval'] = form_input_validate(get_nfilter_request_var('sync_interval'), 'sync_interval', '^[0-9]+$', false, 3);
291
292			// Database settings
293			$save['dbdefault']     = form_input_validate(get_nfilter_request_var('dbdefault'), 'dbdefault', '', true, 3);
294			$save['dbhost']        = form_input_validate(get_nfilter_request_var('dbhost'),    'dbhost',    '', true, 3);
295			$save['dbuser']        = form_input_validate(get_nfilter_request_var('dbuser'),    'dbuser',    '', true, 3);
296			$save['dbpass']        = form_input_validate(get_nfilter_request_var('dbpass'),    'dbpass',    '', true, 3);
297			$save['dbport']        = form_input_validate(get_nfilter_request_var('dbport'),    'dbport',    '^[0-9]+$', true, 3);
298			$save['dbretries']     = form_input_validate(get_nfilter_request_var('dbretries'), 'dbretries', '^[0-9]+$', true, 3);
299			$save['dbssl']         = isset_request_var('dbssl') ? 'on':'';
300			$save['dbsslkey']      = form_input_validate(get_nfilter_request_var('dbsslkey'),  'dbsslkey',  '', true, 3);
301			$save['dbsslcert']     = form_input_validate(get_nfilter_request_var('dbsslcert'), 'dbsslcert', '', true, 3);
302			$save['dbsslca']       = form_input_validate(get_nfilter_request_var('dbsslca'),   'dbsslca',   '', true, 3);
303		}
304
305		// Check for duplicate hostname
306		$error = false;
307		if (poller_check_duplicate_poller_id($save['id'], $save['hostname'], 'hostname')) {
308			raise_message('dupe_hostname', __esc('You have already used this hostname \'%s\'.  Please enter a non-duplicate hostname.', $save['hostname']), MESSAGE_LEVEL_ERROR);
309			$error = true;
310		}
311
312		if (isset($save['dbhost'])) {
313			if (poller_check_duplicate_poller_id($save['id'], $save['dbhost'], 'dbhost')) {
314				raise_message('dupe_dbhost', __esc('You have already used this database hostname \'%s\'.  Please enter a non-duplicate database hostname.', $save['hostname']), MESSAGE_LEVEL_ERROR);
315				$error = true;
316			}
317		}
318
319		if (isset($save['dbhost']) && $save['dbhost'] == 'localhost' && $save['id'] > 1) {
320			raise_message('poller_dbhost');
321		} elseif ($save['id'] > 1 && poller_host_duplicate($save['id'], $save['dbhost'])) {
322			raise_message('poller_nodupe');
323		} elseif (!is_error_message() && $error == false) {
324			$poller_id = sql_save($save, 'poller');
325
326			if ($poller_id) {
327				raise_message(1);
328			} else {
329				raise_message(2);
330			}
331		}
332
333		header('Location: pollers.php?header=false&action=edit&id=' . (empty($poller_id) ? get_nfilter_request_var('id') : $poller_id));
334	}
335}
336
337function poller_check_duplicate_poller_id($poller_id, $hostname, $column) {
338	$ip_addresses  = array();
339	$ip_hostnames  = array();
340
341	if (is_ipaddress($hostname)) {
342		$address = @gethostbyaddr($hostname);
343
344		if ($address != $hostname) {
345			$ip_hostnames[$address] = $address;
346		} else {
347			$ip_addresses[$address] = $address;
348		}
349
350		$ip_addresses[$hostname] = $hostname;
351	} elseif (strpos($hostname, '.') !== false) {
352		$addresses = @dns_get_record($hostname);
353		$ip        = @gethostbyname($hostname);
354
355		if ($ip != $hostname) {
356			$ip_addresses[$ip] = $ip;
357		}
358
359		$ip_hostnames[$hostname] = $hostname;
360
361		if (cacti_sizeof($addresses)) {
362			foreach($addresses as $address) {
363				if (isset($address['target'])) {
364					$ip_hostnames[$address['host']] = $address['host'];
365				}
366
367				if (isset($address['host'])) {
368					$ip_hostnames[$address['host']] = $address['host'];
369				}
370
371				if (isset($address['ip'])) {
372					$ip_addresses[$address['ip']] = $address['ip'];
373				}
374			}
375		}
376	} else {
377		$ip_hostname[$hostname] = $hostname;
378
379		$address = @gethostbyname($hostname);
380
381		if ($address != $hostname) {
382			$ip_addresses[$address] = $address;
383		}
384	}
385
386	$sql_where1 = '';
387	if (cacti_sizeof($ip_addresses)) {
388		$sql_where1 = "$column IN ('" . implode("','", $ip_addresses) . "')";
389	}
390
391	$sql_where2 = '';
392	if (cacti_sizeof($ip_hostnames)) {
393		foreach($ip_hostnames as $host) {
394			$parts = explode('.', $host);
395			$sql_where2 .= ($sql_where2 != '' ? ' OR ':' (') .
396				"($column = " . db_qstr($parts[0]) .
397				" OR $column LIKE " . db_qstr($parts[0] . '%') .
398				" OR $column = " . db_qstr($host) . ")";
399		}
400		$sql_where2 .= ')';
401	}
402
403	if ($sql_where1 != '' || $sql_where2 != '') {
404		$sql_where = ' AND ' . $sql_where1 . ($sql_where1 != '' && $sql_where2 != '' ? ' OR ':'') . $sql_where2;
405	} else {
406		$sql_where = '';
407	}
408
409	$duplicate = db_fetch_cell_prepared("SELECT id
410		FROM poller
411		WHERE id != ?
412		$sql_where",
413		array($poller_id));
414
415	if (empty($duplicate)) {
416		return false;
417	} else {
418		return true;
419	}
420}
421
422function poller_host_duplicate($poller_id, $host) {
423	if ($host == 'localhost') {
424		return true;
425	} else {
426		return db_fetch_cell_prepared('SELECT COUNT(*)
427			FROM poller
428			WHERE dbhost LIKE "' . $host . '%"
429			AND id != ?',
430			array($poller_id));
431	}
432}
433
434function form_actions() {
435	global $config, $poller_actions;
436
437	/* ================= input validation ================= */
438	get_filter_request_var('drp_action', FILTER_VALIDATE_REGEXP, array('options' => array('regexp' => '/^([a-zA-Z0-9_]+)$/')));
439	/* ==================================================== */
440
441	/* if we are to save this form, instead of display it */
442	if (isset_request_var('selected_items')) {
443		$selected_items = sanitize_unserialize_selected_items(get_nfilter_request_var('selected_items'));
444
445		if ($selected_items != false) {
446			if (get_nfilter_request_var('drp_action') == '1') { // delete
447				db_execute('DELETE FROM poller WHERE ' . array_to_sql_or($selected_items, 'id'));
448				db_execute('UPDATE host SET poller_id=1 WHERE deleted="" AND ' . array_to_sql_or($selected_items, 'poller_id'));
449				db_execute('UPDATE automation_networks SET poller_id=1 WHERE ' . array_to_sql_or($selected_items, 'poller_id'));
450				db_execute('UPDATE automation_processes SET poller_id=1 WHERE ' . array_to_sql_or($selected_items, 'poller_id'));
451				db_execute('UPDATE poller_command SET poller_id=1 WHERE ' . array_to_sql_or($selected_items, 'poller_id'));
452				db_execute('UPDATE poller_item SET poller_id=1 WHERE ' . array_to_sql_or($selected_items, 'poller_id'));
453				db_execute('UPDATE poller_output_realtime SET poller_id=1 WHERE ' . array_to_sql_or($selected_items, 'poller_id'));
454				db_execute('UPDATE poller_time SET poller_id=1 WHERE ' . array_to_sql_or($selected_items, 'poller_id'));
455
456				cacti_log('NOTE: The poller(s) with the id(s): ' . implode(',', $selected_items) . ' deleted by user ' . $_SESSION['sess_user_id'], false, 'WEBUI');
457			} elseif (get_request_var('drp_action') == '2') { // disable
458				db_execute('UPDATE poller SET disabled="on" WHERE ' . array_to_sql_or($selected_items, 'id'));
459
460				cacti_log('NOTE: The poller(s) with the id(s): ' . implode(',', $selected_items) . ' disabled by user ' . $_SESSION['sess_user_id'], false, 'WEBUI');
461			} elseif (get_request_var('drp_action') == '3') { // enable
462				db_execute('UPDATE poller SET disabled="" WHERE ' . array_to_sql_or($selected_items, 'id'));
463
464				cacti_log('NOTE: The poller(s) with the id(s): ' . implode(',', $selected_items) . ' enabled by user ' . $_SESSION['sess_user_id'], false, 'WEBUI');
465			} elseif (get_request_var('drp_action') == '4') { // full sync
466				cacti_session_close();
467
468				$success = array();
469				$failed  = array();
470				$ids     = array();
471
472				foreach($selected_items as $item) {
473					// Operation not allowed on the main poller
474					if ($item == 1) {
475						continue;
476					}
477
478					$ids[]   = $item;
479
480					$poller = db_fetch_row_prepared('SELECT *
481						FROM poller
482						WHERE id = ?',
483						array($item));
484
485					if ($poller['dbhost'] == 'localhost') {
486						raise_message('poller_dbhost');
487						continue;
488					} elseif ($item == 1) {
489						raise_message('poller_nomain');
490						continue;
491					} else {
492						if (replicate_out($item)) {
493							$success[] = $item;
494
495							db_execute_prepared('UPDATE poller
496								SET last_sync = NOW()
497								WHERE id = ?',
498								array($item));
499						} else {
500							$failed[] = $item;
501						}
502					}
503				}
504
505				cacti_session_start();
506
507				if (cacti_sizeof($failed)) {
508					cacti_log('WARNING: Some selected Remote Data Collectors in [' . implode(', ', $ids) . '] failed synchronization by user ' . get_username($_SESSION['sess_user_id']) . ', Successful/Failed[' . cacti_sizeof($success) . '/' . cacti_sizeof($failed) . '].  See log for details.', false, 'WEBUI');
509				} else {
510					cacti_log('NOTE: All selected Remote Data Collectors in [' . implode(', ', $ids) . '] synchronized correctly by user ' . get_username($_SESSION['sess_user_id']), false, 'WEBUI');
511				}
512			} elseif (get_request_var('drp_action') == '5') { // clear statistics
513				foreach($selected_items as $item) {
514					db_execute_prepared('UPDATE poller
515						SET total_time = 0, max_time = 0, min_time = 9999999, avg_time = 0, total_polls = 0
516						WHERE id = ?',
517						array($item));
518				}
519
520				raise_message('poller_clear', __('Data Collector Statistics cleared.'), MESSAGE_LEVEL_INFO);
521			}
522		}
523
524		header('Location: pollers.php?header=false');
525		exit;
526	}
527
528	/* setup some variables */
529	$pollers = ''; $i = 0;
530
531	/* loop through each of the graphs selected on the previous page and get more info about them */
532	foreach ($_POST as $var => $val) {
533		if (preg_match('/^chk_([0-9]+)$/', $var, $matches)) {
534			/* ================= input validation ================= */
535			input_validate_input_number($matches[1]);
536			/* ==================================================== */
537
538			$pollers .= '<li>' . html_escape(db_fetch_cell_prepared('SELECT name FROM poller WHERE id = ?', array($matches[1]))) . '</li>';
539			$poller_array[$i] = $matches[1];
540
541			$i++;
542		}
543	}
544
545	top_header();
546
547	form_start('pollers.php');
548
549	html_start_box($poller_actions[get_nfilter_request_var('drp_action')], '60%', '', '3', 'center', '');
550
551	if (isset($poller_array) && cacti_sizeof($poller_array)) {
552		if (get_nfilter_request_var('drp_action') == '1') { // delete
553			print "<tr>
554				<td class='textArea' class='odd'>
555					<p>" . __n('Click \'Continue\' to delete the following Data Collector.  Note, all devices will be disassociated from this Data Collector and mapped back to the Main Cacti Data Collector.', 'Click \'Continue\' to delete all following Data Collectors.  Note, all devices will be disassociated from these Data Collectors and mapped back to the Main Cacti Data Collector.', cacti_sizeof($poller_array)) . "</p>
556					<div class='itemlist'><ul>$pollers</ul></div>
557				</td>
558			</tr>\n";
559
560			$save_html = "<input type='button' class='ui-button ui-corner-all ui-widget' value='" . __esc('Cancel') . "' onClick='cactiReturnTo()'>&nbsp;<input type='submit' class='ui-button ui-corner-all ui-widget' value='" . __esc('Continue') . "' title='" . __n('Delete Data Collector', 'Delete Data Collectors', cacti_sizeof($poller_array)) . "'>";
561		} elseif (get_request_var('drp_action') == '2') { // disable
562			print "<tr>
563				<td class='textArea' class='odd'>
564					<p>" . __n('Click \'Continue\' to disable the following Data Collector.', 'Click \'Continue\' to disable the following Data Collectors.', cacti_sizeof($poller_array)) . "</p>
565					<div class='itemlist'><ul>$pollers</ul></div>
566				</td>
567			</tr>\n";
568
569			$save_html = "<input type='button' class='ui-button ui-corner-all ui-widget' value='" . __esc('Cancel') . "' onClick='cactiReturnTo()'>&nbsp;<input type='submit' class='ui-button ui-corner-all ui-widget' value='" . __esc('Continue') . "' title='" . __n('Disable Data Collector', 'Disable Data Collectors', cacti_sizeof($poller_array)) . "'>";
570		} elseif (get_request_var('drp_action') == '3') { // enable
571			print "<tr>
572				<td class='textArea' class='odd'>
573					<p>" . __n('Click \'Continue\' to enable the following Data Collector.', 'Click \'Continue\' to enable the following Data Collectors.', cacti_sizeof($poller_array)) . "</p>
574					<div class='itemlist'><ul>$pollers</ul></div>
575				</td>
576			</tr>\n";
577
578			$save_html = "<input type='button' class='ui-button ui-corner-all ui-widget' value='" . __esc('Cancel') . "' onClick='cactiReturnTo()'>&nbsp;<input type='submit' class='ui-button ui-corner-all ui-widget' value='" . __esc('Continue') . "' title='" . __n('Enable Data Collector', 'Enable Data Collectors', cacti_sizeof($poller_array)) . "'>";
579		} elseif (get_request_var('drp_action') == '4') { // full sync
580			print "<tr>
581				<td class='textArea' class='odd'>
582					<p>" . __n('Click \'Continue\' to Synchronize the Remote Data Collector for Offline Operation.', 'Click \'Continue\' to Synchronize the Remote Data Collectors for Offline Operation.', cacti_sizeof($poller_array)) . "</p>
583					<div class='itemlist'><ul>$pollers</ul></div>
584				</td>
585			</tr>\n";
586
587			$save_html = "<input type='button' class='ui-button ui-corner-all ui-widget' value='" . __esc('Cancel') . "' onClick='cactiReturnTo()'>&nbsp;<input type='submit' class='ui-button ui-corner-all ui-widget' value='" . __esc('Continue') . "' title='" . __n('Enable Data Collector', 'Synchronize Remote Data Collectors', cacti_sizeof($poller_array)) . "'>";
588		} elseif (get_request_var('drp_action') == '5') { // clear statistics
589			print "<tr>
590				<td class='textArea' class='odd'>
591					<p>" . __n('Click \'Continue\' to Clear Data Collector Statistics for the Data Collector.', 'Click \'Continue\' to Clear DAta Collector Statistics for the Data Collectors.', cacti_sizeof($poller_array)) . "</p>
592					<div class='itemlist'><ul>$pollers</ul></div>
593				</td>
594			</tr>\n";
595
596			$save_html = "<input type='button' class='ui-button ui-corner-all ui-widget' value='" . __esc('Cancel') . "' onClick='cactiReturnTo()'>&nbsp;<input type='submit' class='ui-button ui-corner-all ui-widget' value='" . __esc('Continue') . "' title='" . __n('Clear Statistics for Data Collector', 'Clear Statistics for Data Collectors', cacti_sizeof($poller_array)) . "'>";
597		}
598	} else {
599		raise_message(40);
600		header('Location: pollers.php?header=false');
601		exit;
602	}
603
604	print "<tr>
605		<td class='saveRow'>
606			<input type='hidden' name='action' value='actions'>
607			<input type='hidden' name='selected_items' value='" . (isset($poller_array) ? serialize($poller_array) : '') . "'>
608			<input type='hidden' name='drp_action' value='" . html_escape(get_nfilter_request_var('drp_action')) . "'>
609			$save_html
610		</td>
611	</tr>\n";
612
613	html_end_box();
614
615	form_end();
616
617	bottom_footer();
618}
619
620/* ---------------------
621    Site Functions
622   --------------------- */
623
624function poller_edit() {
625	global $fields_poller_edit;
626
627	/* ================= input validation ================= */
628	get_filter_request_var('id');
629	/* ==================================================== */
630
631	if (!isempty_request_var('id')) {
632		$poller = db_fetch_row_prepared('SELECT *
633			FROM poller
634			WHERE id = ?',
635			array(get_request_var('id')));
636
637		$header_label = __esc('Site [edit: %s]', $poller['name']);
638	} else {
639		$poller = array();
640
641		$header_label = __('Site [new]');
642	}
643
644	form_start('pollers.php', 'poller');
645
646	html_start_box($header_label, '100%', true, '3', 'center', '');
647
648	if (cacti_sizeof($poller)) {
649		if ($poller['id'] == 1) {
650			unset($fields_poller_edit['sync_interval']);
651			unset($fields_poller_edit['spacer_remotedb']);
652			unset($fields_poller_edit['dbdefault']);
653			unset($fields_poller_edit['dbhost']);
654			unset($fields_poller_edit['dbuser']);
655			unset($fields_poller_edit['dbpass']);
656			unset($fields_poller_edit['dbport']);
657			unset($fields_poller_edit['dbretries']);
658			unset($fields_poller_edit['dbssl']);
659			unset($fields_poller_edit['dbsslkey']);
660			unset($fields_poller_edit['dbsslcert']);
661			unset($fields_poller_edit['dbsslca']);
662		}
663
664		if ($poller['timezone'] == '') {
665			$poller['timezone'] = ini_get('date.timezone');
666		}
667	}
668
669	draw_edit_form(
670		array(
671			'config' => array('no_form_tag' => true),
672			'fields' => inject_form_variables($fields_poller_edit, (isset($poller) ? $poller : array()))
673		)
674	);
675
676	$tip_text = __('Remote Data Collectors must be able to communicate to the Main Data Collector, and vice versa.  Use this button to verify that the Main Data Collector can communicate to this Remote Data Collector.');
677
678	if (read_config_option('hide_form_description') == 'on') {
679		$tooltip = '<br><span class="formFieldDescription">' . $tip_text . '</span>';
680	} else {
681		$tooltip = '<div class="formTooltip">' . str_replace("\n", '', display_tooltip($tip_text)) . '</div>';
682	}
683
684	$row_html = '<div class="formRow odd"><div class="formColumnLeft"><div class="formFieldName">' . __('Test Database Connection') . $tooltip . '</div></div><div class="formColumnRight"><input type="button" class="ui-button ui-corner-all ui-widget" id="dbtest" value="' . __esc('Test Connection') . '"><span id="results"></span></div></div>';
685
686	$pt = read_config_option('poller_type');
687
688	if (isset($poller) && cacti_sizeof($poller)) {
689		if ($poller['id'] > 1) {
690			?>
691			<script type='text/javascript'>
692			pt = <?php print $pt;?>;
693
694			function showHideRemoteDB() {
695					var hasSSL = $('#dbssl').is(':checked');
696					if (hasSSL) {
697						$('#row_dbsslkey').show();
698						$('#row_dbsslcert').show();
699						$('#row_dbsslca').show();
700					} else {
701						$('#row_dbsslkey').hide();
702						$('#row_dbsslcert').hide();
703						$('#row_dbsslca').hide();
704					}
705			}
706
707			$(function() {
708				$('#row_dbsslca').after('<?php print $row_html;?>');
709				$('#dbssl').click(function() {
710					showHideRemoteDB();
711				});
712
713				$('#dbtest').click(function() {
714					ping_database();
715				});
716
717				showHideRemoteDB();
718
719				if (pt == 1) {
720					$('#row_threads').hide();
721				}
722			});
723
724			function ping_database() {
725				dbssl = $('#dbssl').is(':checked') ? 'on':'';
726
727				$.post('pollers.php', {
728					__csrf_magic: csrfMagicToken,
729					action:       'ping',
730					dbdefault:    $('#dbdefault').val(),
731					dbhost:       $('#dbhost').val(),
732					dbuser:       $('#dbuser').val(),
733					dbpass:       $('#dbpass').val(),
734					dbport:       $('#dbport').val(),
735					dbretries:    $('#dbretries').val(),
736					dbssl:        dbssl,
737					dbsslkey:     $('#dbsslkey').val(),
738					dbsslcert:    $('#dbsslcert').val(),
739					dbsslca:      $('#dbsslca').val()
740				}).done(function(data) {
741					$('#results').empty().show().html(data).fadeOut(2000);
742				});
743			}
744			</script>
745			<?php
746		} else {
747			?>
748			<script type='text/javascript'>
749			pt = <?php print $pt;?>;
750
751			$(function() {
752				if (pt == 1) {
753					$('#row_threads').hide();
754				}
755			});
756			</script>
757			<?php
758		}
759	}
760
761	html_end_box(true, true);
762
763	form_save_button('pollers.php', 'return');
764}
765
766function test_database_connection($poller = array()) {
767	if (!cacti_sizeof($poller)) {
768		$poller['dbtype'] = 'mysql';
769
770		$fields = array(
771			'dbhost',
772			'dbuser',
773			'dbpass',
774			'dbdefault',
775			'dbport',
776			'dbretries',
777			'dbssl',
778			'dbsslkey',
779			'dbsslcert',
780			'dbsslca'
781		);
782
783		foreach ($fields as $field) {
784			if ($field == 'dbssl') {
785				if (isset_request_var('dbssl') && get_nfilter_request_var('dbssl') == 'on') {
786					$poller['dbssl'] = 'on';
787				} else {
788					$poller['dbssl'] = '';
789				}
790			} elseif (isset_request_var($field)) {
791				$poller[$field] = get_nfilter_request_var($field);
792			} else {
793				print 'ERROR: DB Connection Column ' . $field . ' Missing';
794				return false;
795			}
796		}
797	}
798
799	$connection = db_connect_real(
800		$poller['dbhost'],
801		$poller['dbuser'],
802		$poller['dbpass'],
803		$poller['dbdefault'],
804		$poller['dbtype'],
805		$poller['dbport'],
806		$poller['dbretries'],
807		$poller['dbssl'],
808		$poller['dbsslkey'],
809		$poller['dbsslcert'],
810		$poller['dbsslca']
811	);
812
813    if (is_object($connection)) {
814        db_close($connection);
815        print __('Connection Successful');
816    } else {
817        print __('Connection Failed');
818    }
819}
820
821function pollers() {
822	global $poller_actions, $poller_status, $item_rows;
823
824	/* ================= input validation and session storage ================= */
825	$filters = array(
826		'rows' => array(
827			'filter' => FILTER_VALIDATE_INT,
828			'pageset' => true,
829			'default' => '-1'
830			),
831		'page' => array(
832			'filter' => FILTER_VALIDATE_INT,
833			'default' => '1'
834			),
835		'refresh' => array(
836			'filter' => FILTER_VALIDATE_INT,
837			'default' => '20'
838			),
839		'filter' => array(
840			'filter' => FILTER_DEFAULT,
841			'pageset' => true,
842			'default' => ''
843			),
844		'sort_column' => array(
845			'filter' => FILTER_CALLBACK,
846			'default' => 'name',
847			'options' => array('options' => 'sanitize_search_string')
848			),
849		'sort_direction' => array(
850			'filter' => FILTER_CALLBACK,
851			'default' => 'ASC',
852			'options' => array('options' => 'sanitize_search_string')
853			)
854	);
855
856	validate_store_request_vars($filters, 'sess_pollers');
857	/* ================= input validation ================= */
858
859	$refresh['page']    = 'pollers.php?header=false';
860	$refresh['seconds'] = get_request_var('refresh');
861	$refresh['logout']  = 'false';
862
863	set_page_refresh($refresh);
864
865	if (get_request_var('rows') == '-1') {
866		$rows = read_config_option('num_rows_table');
867	} else {
868		$rows = get_request_var('rows');
869	}
870
871	html_start_box( __('Data Collectors'), '100%', '', '3', 'center', '');
872
873	?>
874	<tr class='even'>
875		<td>
876			<form id='form_poller' action='pollers.php'>
877			<table class='filterTable'>
878				<tr>
879					<td>
880						<?php print __('Search');?>
881					</td>
882					<td>
883						<input type='text' class='ui-state-default ui-corner-all' id='filter' size='25' value='<?php print html_escape_request_var('filter');?>'>
884					</td>
885					<td>
886						<?php print __('Collectors');?>
887					</td>
888					<td>
889						<select id='rows' onChange='applyFilter()'>
890							<option value='-1'<?php print (get_request_var('rows') == '-1' ? ' selected>':'>') . __('Default');?></option>
891							<?php
892							if (cacti_sizeof($item_rows)) {
893								foreach ($item_rows as $key => $value) {
894									print "<option value='" . $key . "'"; if (get_request_var('rows') == $key) { print ' selected'; } print '>' . html_escape($value) . "</option>\n";
895								}
896							}
897							?>
898						</select>
899					</td>
900					<td>
901						<?php print __('Refresh');?>
902					</td>
903					<td>
904						<select id='refresh' onChange='applyFilter()'>
905							<?php
906							$frequency = array(
907								5   => __('%d Seconds', 5),
908								10  => __('%d Seconds', 10),
909								20  => __('%d Seconds', 20),
910								30  => __('%d Seconds', 30),
911								45  => __('%d Seconds', 45),
912								60  => __('%d Minute', 1),
913								120 => __('%d Minutes', 2),
914								300 => __('%d Minutes', 5)
915							);
916
917							foreach ($frequency as $r => $row) {
918								echo "<option value='" . $r . "'" . (isset_request_var('refresh') && $r == get_request_var('refresh') ? ' selected' : '') . '>' . $row . '</option>';
919							}
920							?>
921						</select>
922					</td>
923					<td>
924						<span>
925							<input type='submit' class='ui-button ui-corner-all ui-widget' id='go' value='<?php print __esc('Go');?>' title='<?php print __esc('Set/Refresh Filters');?>'>
926							<input type='button' class='ui-button ui-corner-all ui-widget' id='clear' value='<?php print __esc('Clear');?>' title='<?php print __esc('Clear Filters');?>'>
927						</span>
928					</td>
929				</tr>
930			</table>
931			</form>
932			<script type='text/javascript'>
933
934			function applyFilter() {
935				strURL  = 'pollers.php?header=false';
936				strURL += '&filter='+$('#filter').val();
937				strURL += '&refresh='+$('#refresh').val();
938				strURL += '&rows='+$('#rows').val();
939				loadPageNoHeader(strURL);
940			}
941
942			function clearFilter() {
943				strURL = 'pollers.php?clear=1&header=false';
944				loadPageNoHeader(strURL);
945			}
946
947			$(function() {
948				$('#clear').click(function() {
949					clearFilter();
950				});
951
952				$('#form_poller').submit(function(event) {
953					event.preventDefault();
954					applyFilter();
955				});
956			});
957
958			</script>
959		</td>
960	</tr>
961	<?php
962
963	html_end_box();
964
965	/* form the 'where' clause for our main sql query */
966	if (get_request_var('filter') != '') {
967		$sql_where = 'WHERE name LIKE ' . db_qstr('%' . get_request_var('filter') . '%');
968	} else {
969		$sql_where = '';
970	}
971
972	$total_rows = db_fetch_cell("SELECT COUNT(*) FROM poller $sql_where");
973
974	$sql_order = get_order_string();
975	$sql_limit = ' LIMIT ' . ($rows*(get_request_var('page')-1)) . ',' . $rows;
976
977	$pollers = db_fetch_assoc("SELECT poller.*, UNIX_TIMESTAMP() - UNIX_TIMESTAMP(poller.last_status) as heartbeat, count(h.id) AS hosts
978		FROM poller
979		LEFT JOIN host AS h
980		ON h.poller_id=poller.id
981		$sql_where
982		GROUP BY poller.id
983		$sql_order
984		$sql_limit");
985
986	$nav = html_nav_bar('pollers.php?filter=' . get_request_var('filter'), MAX_DISPLAY_PAGES, get_request_var('page'), $rows, $total_rows, 5, __('Pollers'), 'page', 'main');
987
988	form_start('pollers.php', 'chk');
989
990	print $nav;
991
992	html_start_box('', '100%', '', '3', 'center', '');
993
994	$display_text = array(
995		'name'        => array('display' => __('Collector Name'), 'align' => 'left',   'sort' => 'ASC',  'tip' => __('The Name of this Data Collector.')),
996		'id'          => array('display' => __('ID'),             'align' => 'right',  'sort' => 'ASC',  'tip' => __('The unique id associated with this Data Collector.')),
997		'hostname'    => array('display' => __('Hostname'),       'align' => 'right',  'sort' => 'ASC',  'tip' => __('The Hostname where the Data Collector is running.')),
998		'status'      => array('display' => __('Status'),         'align' => 'center', 'sort' => 'DESC', 'tip' => __('The Status of this Data Collector.')),
999		'nosort0'   => array('display' => __('Proc/Threads'),      'align' => 'right',  'sort' => 'DESC', 'tip' => __('The Number of Poller Processes and Threads for this Data Collector.')),
1000		'total_time'  => array('display' => __('Polling Time'),   'align' => 'right',  'sort' => 'DESC', 'tip' => __('The last data collection time for this Data Collector.')),
1001		'nosort1'     => array('display' => __('Avg/Max'),        'align' => 'right',  'sort' => 'DESC', 'tip' => __('The Average and Maximum Collector timings for this Data Collector.')),
1002		'hosts'       => array('display' => __('Devices'),        'align' => 'right',  'sort' => 'DESC', 'tip' => __('The number of Devices associated with this Data Collector.')),
1003		'snmp'        => array('display' => __('SNMP Gets'),      'align' => 'right',  'sort' => 'DESC', 'tip' => __('The number of SNMP gets associated with this Collector.')),
1004		'script'      => array('display' => __('Scripts'),        'align' => 'right',  'sort' => 'DESC', 'tip' => __('The number of script calls associated with this Data Collector.')),
1005		'server'      => array('display' => __('Servers'),        'align' => 'right',  'sort' => 'DESC', 'tip' => __('The number of script server calls associated with this Data Collector.')),
1006		'last_update' => array('display' => __('Last Finished'),  'align' => 'right',  'sort' => 'DESC', 'tip' => __('The last time this Data Collector completed.')),
1007		'last_status' => array('display' => __('Last Update'),    'align' => 'right',  'sort' => 'DESC', 'tip' => __('The last time this Data Collector checked in with the main Cacti site.')),
1008		'last_sync' => array('display' => __('Last Sync'),        'align' => 'right',  'sort' => 'DESC', 'tip' => __('The last time this Data Collector was full synced with main Cacti site.')));
1009
1010	html_header_sort_checkbox($display_text, get_request_var('sort_column'), get_request_var('sort_direction'), false);
1011
1012	$i = 0;
1013	if (cacti_sizeof($pollers)) {
1014		foreach ($pollers as $poller) {
1015			if ($poller['id'] == 1) {
1016				$disabled = true;
1017			} else {
1018				$disabled = false;
1019			}
1020
1021			if ($poller['disabled'] == 'on') {
1022				$poller['status'] = 4;
1023			}else if ($poller['heartbeat'] > 310) {
1024				$poller['status'] = 6;
1025			}
1026
1027			$mma = round($poller['avg_time'], 2) . '/' .  round($poller['max_time'], 2);
1028
1029			if (empty($poller['name'])) {
1030				$poller['name'] = '&lt;no name&gt;';
1031			}
1032
1033			$pt = read_config_option('poller_type');
1034
1035			form_alternate_row('line' . $poller['id'], true, $disabled);
1036			form_selectable_cell(filter_value($poller['name'], get_request_var('filter'), 'pollers.php?action=edit&id=' . $poller['id']), $poller['id']);
1037			form_selectable_cell($poller['id'], $poller['id'], '', 'right');
1038			form_selectable_ecell($poller['hostname'], $poller['id'], '', 'right');
1039			form_selectable_cell($poller_status[$poller['status']], $poller['id'], '', 'center');
1040			form_selectable_cell($poller['processes'] . '/' . ($pt == 2 ? $poller['threads']:'-'), $poller['id'], '', 'right');
1041			form_selectable_cell(number_format_i18n($poller['total_time'], 2), $poller['id'], '', 'right');
1042			form_selectable_cell($mma, $poller['id'], '', 'right');
1043			form_selectable_cell(number_format_i18n($poller['hosts'], '-1'), $poller['id'], '', 'right');
1044			form_selectable_cell(number_format_i18n($poller['snmp'], '-1'), $poller['id'], '', 'right');
1045			form_selectable_cell(number_format_i18n($poller['script'], '-1'), $poller['id'], '', 'right');
1046			form_selectable_cell(number_format_i18n($poller['server'], '-1'), $poller['id'], '', 'right');
1047			form_selectable_cell(substr($poller['last_update'], 5), $poller['id'], '', 'right');
1048			form_selectable_cell(substr($poller['last_status'], 5), $poller['id'], '', 'right');
1049
1050			if ($poller['id'] == 1) {
1051				form_selectable_cell(__('N/A'), $poller['id'], '', 'right');
1052			} else {
1053				form_selectable_cell(substr($poller['last_sync'], 5), $poller['id'], '', 'right');
1054			}
1055
1056			form_checkbox_cell($poller['name'], $poller['id'], $disabled);
1057			form_end_row();
1058		}
1059	} else {
1060		print "<tr class='tableRow'><td colspan='" . (cacti_sizeof($display_text)+1) . "'><em>" . __('No Data Collectors Found') . "</em></td></tr>\n";
1061	}
1062
1063	html_end_box(false);
1064
1065	if (cacti_sizeof($pollers)) {
1066		print $nav;
1067	}
1068
1069	/* draw the dropdown containing a list of available actions for this form */
1070	draw_actions_dropdown($poller_actions);
1071
1072	form_end();
1073}
1074
1075