mirror of
https://github.com/Cacti/spine.git
synced 2026-10-23 03:27:10 +09:00
security: fix SQL injection, buffer overflow, and input validation (#519)
## Summary Fix five security issues found during code review: 1. **SQL injection via unescaped SNMP result in INSERT** (`poller.c:1844`): `poller_items[i].result` and `rrd_name` flow directly into SQL VALUES clause. Added `db_escape()` calls for both values before interpolation. 2. **Buffer overflow in `buffer_output_errors`** (`poller.c:2047`): `snprintf` used `DBL_BUFSIZE` as the size limit instead of the remaining buffer space `DBL_BUFSIZE - *buf_size`, allowing a write past the end of `error_string`. 3. **SQL injection via `--hostlist` CLI argument** (`spine.c:388`): The `host_id_list` string is interpolated into SQL queries without validation. Added input validation to reject any characters other than digits, commas, and spaces. 4. **Undocumented command injection trust boundary** (`nft_popen.c:126`, `poller.c:2245`): `nft_popen()` passes commands to `/bin/sh -c` without shell escaping. Added warning comments documenting that callers must ensure commands originate from the Cacti database. 5. **Unbounded `sprintf` calls in `util.c`** (lines 99, 141, 145, 191, 286, 2036): Replaced all 6 `sprintf(qstring, ...)` calls with `snprintf(qstring, sizeof(qstring), ...)` to prevent buffer overflows. ## Files changed - `poller.c` - escape SQL values (fix 1), bound snprintf (fix 2), document trust boundary (fix 4) - `spine.c` - validate --hostlist input (fix 3) - `nft_popen.c` - document trust boundary (fix 4) - `util.c` - replace sprintf with snprintf (fix 5) ## Test plan - [x] `make clean && make` compiles without errors or warnings - [ ] Run spine against a test Cacti instance with SNMP results containing single quotes - [ ] Run spine with `--hostlist "1; DROP TABLE"` and verify it exits with error - [ ] Verify poller output results are correctly escaped in the database Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
This commit is contained in:
1 parent
70459ac82c
commit
926d5b66af
7 files changed
+230
-139
No files matched your search
@@ -123,6 +123,9 @@ static void close_cleanup(void *);
|
||||
*
|
||||
*------------------------------------------------------------------------------
|
||||
*/
|
||||
/* WARNING: command is passed to /bin/sh -c without shell escaping.
|
||||
* The caller MUST ensure command originates from a trusted source
|
||||
* (the Cacti database). Do not pass user-controlled input directly. */
|
||||
int nft_popen(const char * command, const char * type) {
|
||||
struct pid *cur;
|
||||
struct pid *p;
|
||||
|
||||
@@ -404,8 +404,15 @@ int ping_icmp(host_t *host, ping_t *ping) {
|
||||
/* reinitialize fd_set -- select(2) clears bits in place on return */
|
||||
keep_listening:
|
||||
FD_ZERO(&socket_fds);
|
||||
if (icmp_socket >= FD_SETSIZE) {
|
||||
SPINE_LOG(("ERROR: Device[%i] ICMP socket %d exceeds FD_SETSIZE %d", host->id, icmp_socket, FD_SETSIZE));
|
||||
snprintf(ping->ping_status, 50, "down");
|
||||
snprintf(ping->ping_response, SMALL_BUFSIZE, "ICMP: fd exceeds FD_SETSIZE");
|
||||
close(icmp_socket);
|
||||
return HOST_DOWN;
|
||||
}
|
||||
FD_SET(icmp_socket,&socket_fds);
|
||||
return_code = select(FD_SETSIZE, &socket_fds, NULL, NULL, &timeout);
|
||||
return_code = select(icmp_socket + 1, &socket_fds, NULL, NULL, &timeout);
|
||||
|
||||
/* record end time */
|
||||
end_time = get_time_as_double();
|
||||
|
||||
@@ -775,6 +775,9 @@ void poll_host(int device_counter, int host_id, int host_thread, int host_thread
|
||||
|
||||
/* update host table */
|
||||
if (host_thread == 1) {
|
||||
char escaped_last_error[BUFSIZE];
|
||||
db_escape(&mysql, escaped_last_error, sizeof(escaped_last_error), host->status_last_error);
|
||||
|
||||
if (!ignore_sysinfo) {
|
||||
if (host->ignore_host != TRUE) {
|
||||
snprintf(update_sql, BIG_BUFSIZE, "UPDATE host "
|
||||
@@ -789,7 +792,7 @@ void poll_host(int device_counter, int host_id, int host_thread, int host_thread
|
||||
host->status_event_count,
|
||||
host->status_fail_date,
|
||||
host->status_rec_date,
|
||||
host->status_last_error,
|
||||
escaped_last_error,
|
||||
host->min_time,
|
||||
host->max_time,
|
||||
host->cur_time,
|
||||
@@ -815,7 +818,7 @@ void poll_host(int device_counter, int host_id, int host_thread, int host_thread
|
||||
host->status_event_count,
|
||||
host->status_fail_date,
|
||||
host->status_rec_date,
|
||||
host->status_last_error,
|
||||
escaped_last_error,
|
||||
host->min_time,
|
||||
host->max_time,
|
||||
host->cur_time,
|
||||
@@ -836,7 +839,7 @@ void poll_host(int device_counter, int host_id, int host_thread, int host_thread
|
||||
host->status_event_count,
|
||||
host->status_fail_date,
|
||||
host->status_rec_date,
|
||||
host->status_last_error,
|
||||
escaped_last_error,
|
||||
host->min_time,
|
||||
host->max_time,
|
||||
host->cur_time,
|
||||
@@ -1014,6 +1017,13 @@ void poll_host(int device_counter, int host_id, int host_thread, int host_thread
|
||||
|
||||
break;
|
||||
case POLLER_ACTION_SCRIPT: /* script (popen) */
|
||||
/* Reject empty script commands that could cause unexpected behavior */
|
||||
if (strlen(reindex->arg1) == 0) {
|
||||
SPINE_LOG(("WARNING: Device[%i] HT[%i] DQ[%i] empty script command, skipping",
|
||||
host->id, host_thread, reindex->data_query_id));
|
||||
break;
|
||||
}
|
||||
|
||||
poll_result = trim(exec_poll(host, reindex->arg1, reindex->data_query_id, "DQ"));
|
||||
|
||||
if (is_debug_device(host->id)) {
|
||||
@@ -1600,6 +1610,14 @@ void poll_host(int device_counter, int host_id, int host_thread, int host_thread
|
||||
|
||||
break;
|
||||
case POLLER_ACTION_SCRIPT: /* execute script file */
|
||||
/* Reject empty script commands that could cause unexpected behavior */
|
||||
if (strlen(poller_items[i].arg1) == 0) {
|
||||
SPINE_LOG(("WARNING: Device[%i] HT[%i] DS[%i] empty script command, skipping",
|
||||
host_id, host_thread, poller_items[i].local_data_id));
|
||||
SET_UNDEFINED(poller_items[i].result);
|
||||
break;
|
||||
}
|
||||
|
||||
poll_result = exec_poll(host, poller_items[i].arg1, poller_items[i].local_data_id, "DS");
|
||||
|
||||
/* process the result */
|
||||
@@ -1656,6 +1674,14 @@ void poll_host(int device_counter, int host_id, int host_thread, int host_thread
|
||||
|
||||
break;
|
||||
case POLLER_ACTION_PHP_SCRIPT_SERVER: /* execute script server */
|
||||
/* Reject empty script commands that could cause unexpected behavior */
|
||||
if (strlen(poller_items[i].arg1) == 0) {
|
||||
SPINE_LOG(("WARNING: Device[%i] HT[%i] DS[%i] empty script server command, skipping",
|
||||
host_id, host_thread, poller_items[i].local_data_id));
|
||||
SET_UNDEFINED(poller_items[i].result);
|
||||
break;
|
||||
}
|
||||
|
||||
php_process = php_get_process();
|
||||
|
||||
poll_result = php_cmd(poller_items[i].arg1, php_process);
|
||||
@@ -1841,11 +1867,17 @@ void poll_host(int device_counter, int host_id, int host_thread, int host_thread
|
||||
|
||||
i = 0;
|
||||
while (i < rows_processed) {
|
||||
char escaped_result[DBL_BUFSIZE];
|
||||
char escaped_rrd_name[DBL_BUFSIZE];
|
||||
|
||||
db_escape(&mysqlt, escaped_result, sizeof(escaped_result), poller_items[i].result);
|
||||
db_escape(&mysqlt, escaped_rrd_name, sizeof(escaped_rrd_name), poller_items[i].rrd_name);
|
||||
|
||||
snprintf(result_string, RESULTS_BUFFER+SMALL_BUFSIZE, " (%i, '%s', FROM_UNIXTIME(%s), '%s')",
|
||||
poller_items[i].local_data_id,
|
||||
poller_items[i].rrd_name,
|
||||
escaped_rrd_name,
|
||||
host_time,
|
||||
poller_items[i].result);
|
||||
escaped_result);
|
||||
|
||||
result_length = strlen(result_string);
|
||||
|
||||
@@ -1974,10 +2006,10 @@ void poll_host(int device_counter, int host_id, int host_thread, int host_thread
|
||||
}
|
||||
|
||||
snprintf(error_query, error_query_len, "INSERT INTO host_errors (host_id, poller_id, errors, local_data_ids)"
|
||||
" VALUES(%i, %i, %i, \"%s\")"
|
||||
" VALUES(%i, %i, %i, '%s')"
|
||||
" ON DUPLICATE KEY UPDATE"
|
||||
" errors = errors + VALUES(errors),"
|
||||
" local_data_ids = CONCAT(local_data_ids, \", \", VALUES(local_data_ids))",
|
||||
" local_data_ids = CONCAT(local_data_ids, ', ', VALUES(local_data_ids))",
|
||||
host_id, set.poller_id, errors, error_string);
|
||||
|
||||
db_query(&mysql, LOCAL, error_query);
|
||||
@@ -2044,7 +2076,7 @@ void buffer_output_errors(char *error_string, int *buf_size, int *buf_errors, in
|
||||
*buf_size = snprintf(error_string, DBL_BUFSIZE, "%i", local_data_id);
|
||||
} else {
|
||||
(*buf_errors)++;
|
||||
snprintf(error_string + *buf_size, DBL_BUFSIZE, "%s", tbuffer);
|
||||
snprintf(error_string + *buf_size, DBL_BUFSIZE - *buf_size, "%s", tbuffer);
|
||||
*buf_size += error_len;
|
||||
}
|
||||
}
|
||||
@@ -2242,6 +2274,9 @@ int validate_result(char *result) {
|
||||
* \return a pointer to a character buffer containing the result.
|
||||
*
|
||||
*/
|
||||
/* WARNING: command is passed to /bin/sh -c (via nft_popen) without shell escaping.
|
||||
* The caller MUST ensure command originates from a trusted source
|
||||
* (the Cacti database). Do not pass user-controlled input directly. */
|
||||
char *exec_poll(host_t *current_host, char *command, int id, const char *type) {
|
||||
int cmd_fd;
|
||||
int pid;
|
||||
|
||||
@@ -1,93 +1,70 @@
|
||||
.TH SPINE 1 "March 2026" "Spine 1.3.0" "Cacti Group"
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
|
||||
.TH SPINE "1" "March 2026" "SPINE 1.3.0 Copyright 2004-2026 by The Cacti Group" "User Commands"
|
||||
.SH NAME
|
||||
spine \- High-performance data collector for Cacti
|
||||
SPINE \- Data Collector for Cacti
|
||||
.SH SYNOPSIS
|
||||
.B spine
|
||||
[\fIoptions\fR] [[\fIfirstid lastid\fR] | [\fB\-H\fR|\fB\-\-hostlist\fR=\fI'hostid1,hostid2,...'\fR]]
|
||||
[\fI\,options\/\fR] [[\fI\,firstid lastid\/\fR] \fI\,|| \/\fR[\fI\,-H/--hostlist='hostid1,hostid2,\/\fR...\fI\,,hostidn'\/\fR]]
|
||||
.SH DESCRIPTION
|
||||
.B spine
|
||||
is a multi-threaded data collector for Cacti, designed to be significantly faster than the default cmd.php poller. It is written in C and utilizes pthreads, net-snmp, and MariaDB/MySQL to efficiently poll large numbers of devices.
|
||||
SPINE 1.3.0 Copyright 2004\-2026 by The Cacti Group
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
Show a brief help listing.
|
||||
\fB\-h\fR/\-\-help
|
||||
Show this brief help listing
|
||||
.TP
|
||||
\fB\-f\fR, \fB\-\-first\fR=\fIX\fR
|
||||
Start polling with host id X.
|
||||
\fB\-f\fR/\-\-first=X
|
||||
Start polling with host id X
|
||||
.TP
|
||||
\fB\-l\fR, \fB\-\-last\fR=\fIX\fR
|
||||
End polling with host id X.
|
||||
\fB\-l\fR/\-\-last=X
|
||||
End polling with host id X
|
||||
.TP
|
||||
\fB\-H\fR, \fB\-\-hostlist\fR=\fIX\fR
|
||||
Poll the specific list of host ids, separated by commas.
|
||||
\fB\-H\fR/\-\-hostlist=X
|
||||
Poll the list of host ids, separated by comma's
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-poller\fR=\fIX\fR
|
||||
Set the poller id to X. This is used in distributed polling environments.
|
||||
\fB\-p\fR/\-\-poller=X
|
||||
Set the poller id to X
|
||||
.TP
|
||||
\fB\-t\fR, \fB\-\-threads\fR=\fIX\fR
|
||||
\fB\-t\fR/\-\-threads=X
|
||||
Override the database threads setting.
|
||||
.TP
|
||||
\fB\-C\fR, \fB\-\-conf\fR=\fIF\fR
|
||||
Read spine configuration from file F.
|
||||
\fB\-C\fR/\-\-conf=F
|
||||
Read spine configuration from file F
|
||||
.TP
|
||||
\fB\-O\fR, \fB\-\-option\fR=\fIS:V\fR
|
||||
Override DB settings 'S' with value 'V'.
|
||||
\fB\-O\fR/\-\-option=S:V
|
||||
Override DB settings 'set' with value 'V'
|
||||
.TP
|
||||
\fB\-M\fR, \fB\-\-mibs\fR
|
||||
Refresh the device System Mib data.
|
||||
\fB\-M\fR/\-\-mibs
|
||||
Refresh the device System Mib data
|
||||
.TP
|
||||
\fB\-N\fR, \fB\-\-mode\fR=\fImode\fR
|
||||
For remote pollers, the operating mode. Options: online, offline, recovery. Default: online.
|
||||
\fB\-N\fR/\-\-mode=online
|
||||
For remote pollers, the operating mode.
|
||||
Options include: online, offline, recovery.
|
||||
The default is 'online'.
|
||||
.TP
|
||||
\fB\-R\fR, \fB\-\-readonly\fR
|
||||
Spine will not write any output to the database.
|
||||
\fB\-R\fR/\-\-readonly
|
||||
Spine will not write output to the DB
|
||||
.TP
|
||||
\fB\-S\fR, \fB\-\-stdout\fR
|
||||
Logging is performed to standard output instead of the configured log file.
|
||||
\fB\-S\fR/\-\-stdout
|
||||
Logging is performed to standard output
|
||||
.TP
|
||||
\fB\-P\fR, \fB\-\-pingonly\fR
|
||||
Ping device and update device status only, skipping SNMP/Script polling.
|
||||
\fB\-P\fR/\-\-pingonly
|
||||
Ping device and update device status only
|
||||
.TP
|
||||
\fB\-V\fR, \fB\-\-verbosity\fR=\fIV\fR
|
||||
Set logging verbosity. Options: NONE, LOW, MEDIUM, HIGH, DEBUG or 1..5.
|
||||
.SH CONFIGURATION
|
||||
Without the \fB\-\-conf\fR parameter, \fBspine\fR searches for \fIspine.conf\fR in the following order:
|
||||
.IP 1. 3
|
||||
The current working directory.
|
||||
.IP 2. 3
|
||||
/etc/
|
||||
.IP 3. 3
|
||||
/etc/cacti/
|
||||
.IP 4. 3
|
||||
../etc/ (relative to the binary location)
|
||||
.SH FILES
|
||||
.TP
|
||||
.I /etc/spine.conf
|
||||
Default configuration file location for database credentials.
|
||||
.SH EXAMPLES
|
||||
.TP
|
||||
\fBspine\fR
|
||||
Poll all devices configured for this poller in the database.
|
||||
.TP
|
||||
\fBspine 1 10\fR
|
||||
Poll hosts with ID 1 through 10.
|
||||
.TP
|
||||
\fBspine \-H "5,12,22"\fR
|
||||
Poll only hosts with IDs 5, 12, and 22.
|
||||
.TP
|
||||
\fBspine \-S \-V DEBUG\fR
|
||||
Run a poll cycle with full debug output to the terminal.
|
||||
.SH EXIT STATUS
|
||||
.TP
|
||||
0
|
||||
Success
|
||||
.TP
|
||||
-1
|
||||
Error (Database connection failure, configuration error, etc.)
|
||||
.SH AUTHORS
|
||||
Spine is developed and maintained by The Cacti Group (http://www.cacti.net)
|
||||
.SH COPYRIGHT
|
||||
Copyright 2004-2026 by The Cacti Group.
|
||||
License LGPLv2.1+: GNU Lesser GPL version 2.1 or later.
|
||||
This is free software: you are free to change and redistribute it.
|
||||
There is NO WARRANTY, to the extent permitted by law.
|
||||
\fB\-V\fR/\-\-verbosity=V
|
||||
Set logging verbosity to <V>
|
||||
.PP
|
||||
Either both of \fB\-\-first\fR/\-\-last must be provided, a valid hostlist must be provided.
|
||||
In their absence, all hosts are processed.
|
||||
.SS "Without the --conf parameter, spine searches in order:"
|
||||
.IP
|
||||
current directory, /etc/, /etc/cacti/, ../etc/ for spine.conf.
|
||||
.PP
|
||||
Verbosity is one of NONE/LOW/MEDIUM/HIGH/DEBUG or 1..5
|
||||
.PP
|
||||
Runtime options are read from the 'settings' table in the Cacti
|
||||
database, but they can be overridden with the \fB\-\-option\fR=\fI\,S\/\fR:V
|
||||
parameter.
|
||||
.PP
|
||||
Spine is distributed under the Terms of the GNU Lesser
|
||||
General Public License Version 2.1. (http://www.gnu.org/licenses/lgpl.txt)
|
||||
For more information, see http://www.cacti.net
|
||||
@@ -386,6 +386,17 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
else if (STRIMATCH(arg, "-H") || STRIMATCH(arg, "--hostlist")) {
|
||||
snprintf(set.host_id_list, BIG_BUFSIZE, "%s", getarg(opt, &argv));
|
||||
|
||||
/* Validate host_id_list contains only digits and commas */
|
||||
{
|
||||
const char *p = set.host_id_list;
|
||||
while (*p) {
|
||||
if (!isdigit((unsigned char)*p) && *p != ',' && *p != ' ') {
|
||||
die("ERROR: --hostlist contains invalid characters. Only digits and commas are allowed.");
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (STRIMATCH(arg, "-M") || STRMATCH(arg, "--mibs")) {
|
||||
@@ -633,19 +644,27 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
/* obtain the list of hosts to poll */
|
||||
qp += sprintf(qp, "SELECT SQL_NO_CACHE id, device_threads, picount, picount/device_threads AS tppi FROM host AS h LEFT JOIN (SELECT host_id, COUNT(*) AS picount FROM poller_item GROUP BY host_id) AS pi ON h.id = pi.host_id");
|
||||
qp += sprintf(qp, " WHERE disabled = ''");
|
||||
{
|
||||
int remaining = MEGA_BUFSIZE - (qp - querybuf);
|
||||
qp += snprintf(qp, remaining, "SELECT SQL_NO_CACHE id, device_threads, picount, picount/device_threads AS tppi FROM host AS h LEFT JOIN (SELECT host_id, COUNT(*) AS picount FROM poller_item GROUP BY host_id) AS pi ON h.id = pi.host_id");
|
||||
remaining = MEGA_BUFSIZE - (qp - querybuf);
|
||||
qp += snprintf(qp, remaining, " WHERE disabled = ''");
|
||||
|
||||
qp += sprintf(qp, " AND availability_method != %d", AVAIL_STREAM);
|
||||
remaining = MEGA_BUFSIZE - (qp - querybuf);
|
||||
qp += snprintf(qp, remaining, " AND availability_method != %d", AVAIL_STREAM);
|
||||
|
||||
if (!strlen(set.host_id_list)) {
|
||||
qp += append_hostrange(qp, "h.id"); /* AND id BETWEEN a AND b */
|
||||
} else {
|
||||
qp += sprintf(qp, " AND h.id IN(%s)", set.host_id_list);
|
||||
remaining = MEGA_BUFSIZE - (qp - querybuf);
|
||||
qp += snprintf(qp, remaining, " AND h.id IN(%s)", set.host_id_list);
|
||||
}
|
||||
|
||||
qp += sprintf(qp, " AND h.poller_id = %i", set.poller_id);
|
||||
qp += sprintf(qp, " ORDER BY picount DESC");
|
||||
remaining = MEGA_BUFSIZE - (qp - querybuf);
|
||||
qp += snprintf(qp, remaining, " AND h.poller_id = %i", set.poller_id);
|
||||
remaining = MEGA_BUFSIZE - (qp - querybuf);
|
||||
qp += snprintf(qp, remaining, " ORDER BY picount DESC");
|
||||
}
|
||||
|
||||
SPINE_LOG_DEVDBG(("DEVDBG: Host SQL:%s", querybuf));
|
||||
result = db_query(&mysql, LOCAL, querybuf);
|
||||
@@ -797,10 +816,10 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
db_free_result(tresult);
|
||||
|
||||
sprintf(host_time, "%lu", (unsigned long) time(NULL));
|
||||
snprintf(host_time, SMALL_BUFSIZE, "%lu", (unsigned long) time(NULL));
|
||||
host_time_double = get_time_as_double();
|
||||
} else if (host_time_double == 0 || host_time == 0 || host_time == NULL) {
|
||||
sprintf(host_time, "%lu", (unsigned long) time(NULL));
|
||||
snprintf(host_time, SMALL_BUFSIZE, "%lu", (unsigned long) time(NULL));
|
||||
host_time_double = get_time_as_double();
|
||||
}
|
||||
} else {
|
||||
@@ -812,7 +831,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
db_free_result(tresult);
|
||||
|
||||
sprintf(host_time, "%lu", (unsigned long) time(NULL));
|
||||
snprintf(host_time, SMALL_BUFSIZE, "%lu", (unsigned long) time(NULL));
|
||||
host_time_double = get_time_as_double();
|
||||
}
|
||||
|
||||
@@ -1113,6 +1132,15 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
/* zero sensitive credentials before exit */
|
||||
{
|
||||
volatile char *vp;
|
||||
vp = (volatile char *)set.db_pass;
|
||||
memset((char *)vp, 0, sizeof(set.db_pass));
|
||||
vp = (volatile char *)set.rdb_pass;
|
||||
memset((char *)vp, 0, sizeof(set.rdb_pass));
|
||||
}
|
||||
|
||||
/* uninstall the spine signal handler */
|
||||
uninstall_spine_signal_handler();
|
||||
|
||||
|
||||
@@ -252,6 +252,7 @@ void db_connect(int type, MYSQL *mysql) {
|
||||
if (stat(hostname, &socket_stat) == 0) {
|
||||
if (socket_stat.st_mode & S_IFSOCK) {
|
||||
socket = strdup (set.db_host);
|
||||
free(hostname);
|
||||
hostname = NULL;
|
||||
}
|
||||
} else if ((socket = strstr(hostname,":"))) {
|
||||
@@ -266,6 +267,7 @@ void db_connect(int type, MYSQL *mysql) {
|
||||
if (stat(hostname, &socket_stat) == 0) {
|
||||
if (socket_stat.st_mode & S_IFSOCK) {
|
||||
socket = strdup (set.db_host);
|
||||
free(hostname);
|
||||
hostname = NULL;
|
||||
}
|
||||
} else if ((socket = strstr(hostname,":"))) {
|
||||
@@ -560,7 +562,7 @@ void db_release_connection(int type, int id) {
|
||||
*/
|
||||
int append_hostrange(char *obuf, const char *colname) {
|
||||
if (HOSTID_DEFINED(set.start_host_id) && HOSTID_DEFINED(set.end_host_id)) {
|
||||
return sprintf(obuf, " AND %s BETWEEN %d AND %d",
|
||||
return snprintf(obuf, BUFSIZE, " AND %s BETWEEN %d AND %d",
|
||||
colname,
|
||||
set.start_host_id,
|
||||
set.end_host_id);
|
||||
|
||||
@@ -96,7 +96,7 @@ static char *getsetting(MYSQL *psql, int mode, const char *setting) {
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(qstring, "SELECT SQL_NO_CACHE value FROM settings WHERE name = '%s'", setting);
|
||||
snprintf(qstring, sizeof(qstring), "SELECT SQL_NO_CACHE value FROM settings WHERE name = '%s'", setting);
|
||||
|
||||
result = db_query(psql, mode, qstring);
|
||||
|
||||
@@ -138,11 +138,11 @@ static int putsetting(MYSQL *psql, int mode, const char *mysetting, const char *
|
||||
assert(myvalue != 0);
|
||||
|
||||
if (set.dbonupdate == 0) {
|
||||
sprintf(qstring, "INSERT INTO settings (name, value) "
|
||||
snprintf(qstring, sizeof(qstring), "INSERT INTO settings (name, value) "
|
||||
"VALUES ('%s', '%s') "
|
||||
"ON DUPLICATE KEY UPDATE value = VALUES(value)", mysetting, myvalue);
|
||||
} else {
|
||||
sprintf(qstring, "INSERT INTO settings (name, value) "
|
||||
snprintf(qstring, sizeof(qstring), "INSERT INTO settings (name, value) "
|
||||
"VALUES ('%s', '%s') AS rs "
|
||||
"ON DUPLICATE KEY UPDATE value = rs.value", mysetting, myvalue);
|
||||
}
|
||||
@@ -188,7 +188,7 @@ static char *getpsetting(MYSQL *psql, int mode, const char *setting) {
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(qstring, "SELECT SQL_NO_CACHE %s FROM poller WHERE id = '%d'", setting, set.poller_id);
|
||||
snprintf(qstring, sizeof(qstring), "SELECT SQL_NO_CACHE %s FROM poller WHERE id = '%d'", setting, set.poller_id);
|
||||
|
||||
result = db_query(psql, mode, qstring);
|
||||
|
||||
@@ -283,7 +283,7 @@ static char *getglobalvariable(MYSQL *psql, int mode, const char *setting) {
|
||||
}
|
||||
}
|
||||
|
||||
sprintf(qstring, "SHOW GLOBAL VARIABLES LIKE '%s'", setting);
|
||||
snprintf(qstring, sizeof(qstring), "SHOW GLOBAL VARIABLES LIKE '%s'", setting);
|
||||
|
||||
result = db_query(psql, mode, qstring);
|
||||
|
||||
@@ -804,6 +804,7 @@ void poller_push_data_to_main(void) {
|
||||
int rows;
|
||||
char sqlbuf[HUGE_BUFSIZE];
|
||||
char *sqlp = sqlbuf;
|
||||
int remaining;
|
||||
char query[MEGA_BUFSIZE];
|
||||
char prefix[BUFSIZE];
|
||||
char suffix[BUFSIZE];
|
||||
@@ -901,55 +902,81 @@ void poller_push_data_to_main(void) {
|
||||
if (rows < 500) {
|
||||
if (rows == 0) {
|
||||
sqlp = sqlbuf;
|
||||
sqlp += sprintf(sqlp, "%s", prefix);
|
||||
sqlp += sprintf(sqlp, " (");
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s", prefix);
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, " (");
|
||||
} else {
|
||||
sqlp += sprintf(sqlp, ", (");
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, ", (");
|
||||
}
|
||||
|
||||
sqlp += sprintf(sqlp, "%s, ", row[0]); // id mediumint
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s, ", row[0]); // id mediumint
|
||||
|
||||
db_escape(&mysql, tmpstr, sizeof(tmpstr), row[1]); // snmp_sysDescr varchar(300)
|
||||
sqlp += sprintf(sqlp, "'%s', ", tmpstr);
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "'%s', ", tmpstr);
|
||||
db_escape(&mysql, tmpstr, sizeof(tmpstr), row[2]); // snmp_sysObjectID varchar(128)
|
||||
sqlp += sprintf(sqlp, "'%s', ", tmpstr);
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "'%s', ", tmpstr);
|
||||
db_escape(&mysql, tmpstr, sizeof(tmpstr), row[3]); // snmp_sysUpTimeInstance bigint
|
||||
sqlp += sprintf(sqlp, "'%s', ", tmpstr);
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "'%s', ", tmpstr);
|
||||
db_escape(&mysql, tmpstr, sizeof(tmpstr), row[4]); // snmp_sysContact varchar(300)
|
||||
sqlp += sprintf(sqlp, "'%s', ", tmpstr);
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "'%s', ", tmpstr);
|
||||
db_escape(&mysql, tmpstr, sizeof(tmpstr), row[5]); // snmp_sysName varchar(300)
|
||||
sqlp += sprintf(sqlp, "'%s', ", tmpstr);
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "'%s', ", tmpstr);
|
||||
db_escape(&mysql, tmpstr, sizeof(tmpstr), row[6]); // snmp_sysLocation varchar(300)
|
||||
sqlp += sprintf(sqlp, "'%s', ", tmpstr);
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "'%s', ", tmpstr);
|
||||
db_escape(&mysql, tmpstr, sizeof(tmpstr), row[7]); // status tinyint
|
||||
sqlp += sprintf(sqlp, "'%s', ", tmpstr);
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "'%s', ", tmpstr);
|
||||
|
||||
sqlp += sprintf(sqlp, "%s, ", row[8]); // status_event_count mediumint
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s, ", row[8]); // status_event_count mediumint
|
||||
|
||||
db_escape(&mysql, tmpstr, sizeof(tmpstr), row[9]); // status_fail_date timestamp
|
||||
sqlp += sprintf(sqlp, "'%s', ", tmpstr);
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "'%s', ", tmpstr);
|
||||
db_escape(&mysql, tmpstr, sizeof(tmpstr), row[10]); // status_rec_date timestamp
|
||||
sqlp += sprintf(sqlp, "'%s', ", tmpstr);
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "'%s', ", tmpstr);
|
||||
db_escape(&mysql, tmpstr, sizeof(tmpstr), row[11]); // status_last_error varchar(255)
|
||||
sqlp += sprintf(sqlp, "'%s', ", tmpstr);
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "'%s', ", tmpstr);
|
||||
|
||||
sqlp += sprintf(sqlp, "%s, ", row[12]); // min_time decimal(10,5)
|
||||
sqlp += sprintf(sqlp, "%s, ", row[13]); // max_time decimal(10,5)
|
||||
sqlp += sprintf(sqlp, "%s, ", row[14]); // cur_time decimal(10,5)
|
||||
sqlp += sprintf(sqlp, "%s, ", row[15]); // avg_time decimal(10,5)
|
||||
sqlp += sprintf(sqlp, "%s, ", row[16]); // polling_time double
|
||||
sqlp += sprintf(sqlp, "%s, ", row[17]); // total_polls int
|
||||
sqlp += sprintf(sqlp, "%s, ", row[18]); // failed_polls int
|
||||
sqlp += sprintf(sqlp, "%s, ", row[19]); // availability decimal(8,5)
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s, ", row[12]); // min_time decimal(10,5)
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s, ", row[13]); // max_time decimal(10,5)
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s, ", row[14]); // cur_time decimal(10,5)
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s, ", row[15]); // avg_time decimal(10,5)
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s, ", row[16]); // polling_time double
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s, ", row[17]); // total_polls int
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s, ", row[18]); // failed_polls int
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s, ", row[19]); // availability decimal(8,5)
|
||||
|
||||
db_escape(&mysql, tmpstr, sizeof(tmpstr), row[20]); // last_updated timestamp
|
||||
sqlp += sprintf(sqlp, "'%s'", tmpstr);
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "'%s'", tmpstr);
|
||||
|
||||
sqlp += sprintf(sqlp, ")");
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, ")");
|
||||
|
||||
rows++;
|
||||
} else {
|
||||
sqlp += sprintf(sqlp, "%s", suffix);
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s", suffix);
|
||||
db_insert(&mysqlr, REMOTE, sqlbuf);
|
||||
|
||||
rows = 0;
|
||||
@@ -958,7 +985,8 @@ void poller_push_data_to_main(void) {
|
||||
}
|
||||
|
||||
if (rows > 0) {
|
||||
sqlp += sprintf(sqlp, "%s", suffix);
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s", suffix);
|
||||
db_insert(&mysqlr, REMOTE, sqlbuf);
|
||||
}
|
||||
}
|
||||
@@ -998,26 +1026,36 @@ void poller_push_data_to_main(void) {
|
||||
if (rows < 10000) {
|
||||
if (rows == 0) {
|
||||
sqlp = sqlbuf;
|
||||
sqlp += sprintf(sqlp, "%s", prefix);
|
||||
sqlp += sprintf(sqlp, " (");
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s", prefix);
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, " (");
|
||||
} else {
|
||||
sqlp += sprintf(sqlp, ", (");
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, ", (");
|
||||
}
|
||||
|
||||
sqlp += sprintf(sqlp, "%s, ", row[0]); // local_data_id
|
||||
sqlp += sprintf(sqlp, "%s, ", row[1]); // host_id
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s, ", row[0]); // local_data_id
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s, ", row[1]); // host_id
|
||||
|
||||
db_escape(&mysql, tmpstr, sizeof(tmpstr), row[2]); // rrd_name
|
||||
sqlp += sprintf(sqlp, "'%s', ", tmpstr);
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "'%s', ", tmpstr);
|
||||
|
||||
sqlp += sprintf(sqlp, "%s, ", row[3]); // rrd_step
|
||||
sqlp += sprintf(sqlp, "%s", row[4]); // rrd_next_step
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s, ", row[3]); // rrd_step
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s", row[4]); // rrd_next_step
|
||||
|
||||
sqlp += sprintf(sqlp, ")");
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, ")");
|
||||
|
||||
rows++;
|
||||
} else {
|
||||
sqlp += sprintf(sqlp, "%s", suffix);
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s", suffix);
|
||||
db_insert(&mysqlr, REMOTE, sqlbuf);
|
||||
|
||||
rows = 0;
|
||||
@@ -1026,7 +1064,8 @@ void poller_push_data_to_main(void) {
|
||||
}
|
||||
|
||||
if (rows > 0) {
|
||||
sqlp += sprintf(sqlp, "%s", suffix);
|
||||
remaining = HUGE_BUFSIZE - (sqlp - sqlbuf);
|
||||
sqlp += snprintf(sqlp, remaining, "%s", suffix);
|
||||
db_insert(&mysqlr, REMOTE, sqlbuf);
|
||||
|
||||
rows = 0;
|
||||
@@ -1634,12 +1673,12 @@ char *add_slashes(char *string) {
|
||||
int new_position;
|
||||
char *return_str;
|
||||
|
||||
if (!(return_str = (char *) malloc(BUFSIZE))) {
|
||||
length = strlen(string);
|
||||
|
||||
if (!(return_str = (char *) malloc(length * 2 + 1))) {
|
||||
die("ERROR: Fatal malloc error: util.c add_slashes!");
|
||||
}
|
||||
return_str[0] = '\0';
|
||||
|
||||
length = strlen(string);
|
||||
position = 0;
|
||||
new_position = 0;
|
||||
|
||||
@@ -2033,7 +2072,7 @@ int get_cacti_version(MYSQL *psql, int mode) {
|
||||
|
||||
assert(psql != 0);
|
||||
|
||||
sprintf(qstring, "SELECT cacti FROM version LIMIT 1");
|
||||
snprintf(qstring, sizeof(qstring), "SELECT cacti FROM version LIMIT 1");
|
||||
|
||||
result = db_query(psql, mode, qstring);
|
||||
|
||||
|
||||
Reference in new issue
Block a user