/*
** Copyright (C) 2001-2024 Zabbix SIA
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** 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 Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see .
**/
/* strptime() on newer and older GNU/Linux systems */
#define _GNU_SOURCE
#include "../sysinfo.h"
#include "software.h"
#include "zbxalgo.h"
#include "zbxexec.h"
#include "zbxregexp.h"
#include "zbxstr.h"
#include "zbxjson.h"
#include "zbxnum.h"
#ifdef HAVE_SYS_UTSNAME_H
# include
#endif
#define SW_OS_FULL "/proc/version"
#define SW_OS_SHORT "/proc/version_signature"
#define SW_OS_NAME "/etc/issue.net"
#define SW_OS_NAME_RELEASE "/etc/os-release"
#define SW_OS_OPTION_PRETTY_NAME "PRETTY_NAME"
#define TIME_FMT "%a %b %e %H:%M:%S %Y"
#define DETAIL_BUF 128
int system_sw_arch(AGENT_REQUEST *request, AGENT_RESULT *result)
{
struct utsname name;
ZBX_UNUSED(request);
if (-1 == uname(&name))
{
SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain system information: %s", zbx_strerror(errno)));
return SYSINFO_RET_FAIL;
}
SET_STR_RESULT(result, zbx_strdup(NULL, name.machine));
return SYSINFO_RET_OK;
}
static int get_line_from_file(char **line, int size, FILE *f)
{
if (NULL == fgets(*line, size, f))
{
*line = zbx_strdup(*line, "Cannot read from file.");
return FAIL;
}
zbx_rtrim(*line, ZBX_WHITESPACE);
return SUCCEED;
}
static int get_os_name(char **line)
{
char tmp_line[MAX_STRING_LEN];
FILE *f = NULL;
*line = zbx_malloc(NULL, sizeof(char) * MAX_STRING_LEN);
/* firstly need to check option PRETTY_NAME in /etc/os-release */
/* if cannot find it, get value from /etc/issue.net */
if (NULL != (f = fopen(SW_OS_NAME_RELEASE, "r")))
{
char line2[MAX_STRING_LEN];
int line_read = FAIL;
while (NULL != fgets(tmp_line, sizeof(tmp_line), f))
{
if (0 != strncmp(tmp_line, SW_OS_OPTION_PRETTY_NAME,
ZBX_CONST_STRLEN(SW_OS_OPTION_PRETTY_NAME)))
continue;
if (1 == sscanf(tmp_line, SW_OS_OPTION_PRETTY_NAME "=\"%[^\"]", *line) ||
1 == sscanf(tmp_line, SW_OS_OPTION_PRETTY_NAME "=%[^ \t\n] %s", *line, line2))
{
line_read = SUCCEED;
break;
}
}
zbx_fclose(f);
if (SUCCEED == line_read)
{
zbx_rtrim(*line, ZBX_WHITESPACE);
goto out;
}
}
if (NULL == (f = fopen(SW_OS_NAME, "r")))
{
*line = zbx_dsprintf(*line, "Cannot open " SW_OS_NAME ": %s", zbx_strerror(errno));
goto error;
}
else
{
if (FAIL == get_line_from_file(line, MAX_STRING_LEN, f))
goto error;
zbx_fclose(f);
}
out:
return SUCCEED;
error:
if (NULL != f)
zbx_fclose(f);
return FAIL;
}
static int get_os_info_file(char **line, const char *filename)
{
FILE *f = NULL;
int ret = FAIL;
*line = zbx_malloc(NULL, sizeof(char) * MAX_STRING_LEN);
if (NULL == (f = fopen(filename, "r")))
{
*line = zbx_dsprintf(*line, "Cannot open %s: %s", filename, zbx_strerror(errno));
return FAIL;
}
ret = get_line_from_file(line, MAX_STRING_LEN, f);
zbx_fclose(f);
return ret;
}
int system_sw_os(AGENT_REQUEST *request, AGENT_RESULT *result)
{
char *type, *str;
int ret = SYSINFO_RET_FAIL;
if (1 < request->nparam)
{
SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
return ret;
}
type = get_rparam(request, 0);
if (NULL == type || '\0' == *type || 0 == strcmp(type, "full"))
{
ret = get_os_info_file(&str, SW_OS_FULL);
}
else if (0 == strcmp(type, "short"))
{
ret = get_os_info_file(&str, SW_OS_SHORT);
}
else if (0 == strcmp(type, "name"))
{
ret = get_os_name(&str);
}
else
{
SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid first parameter."));
return ret;
}
if (SUCCEED == ret)
{
SET_STR_RESULT(result, zbx_strdup(NULL, str));
ret = SYSINFO_RET_OK;
}
else
{
SET_MSG_RESULT(result, zbx_strdup(NULL, str));
ret = SYSINFO_RET_FAIL;
}
zbx_free(str);
return ret;
}
static int dpkg_list(const char *line, char *package, size_t max_package_len)
{
char fmt[32], tmp[32];
zbx_snprintf(fmt, sizeof(fmt), "%%" ZBX_FS_SIZE_T "s %%" ZBX_FS_SIZE_T "s",
(zbx_fs_size_t)(max_package_len - 1), (zbx_fs_size_t)(sizeof(tmp) - 1));
if (2 != sscanf(line, fmt, package, tmp) || 0 != strcmp(tmp, "install"))
return FAIL;
return SUCCEED;
}
static void add_package_to_json(struct zbx_json *json, const char *name, const char *manager, const char *version,
zbx_uint64_t size, const char *arch, time_t buildtime_timestamp, const char *buildtime_value,
time_t installtime_timestamp, const char *installtime_value)
{
zbx_json_addobject(json, NULL);
zbx_json_addstring(json, "name", name, ZBX_JSON_TYPE_STRING);
zbx_json_addstring(json, "manager", manager, ZBX_JSON_TYPE_STRING);
zbx_json_addstring(json, "version", version, ZBX_JSON_TYPE_STRING);
zbx_json_adduint64(json, "size", size);
zbx_json_addstring(json, "arch", arch, ZBX_JSON_TYPE_STRING);
zbx_json_addobject(json, "buildtime");
zbx_json_addint64(json, "timestamp", buildtime_timestamp);
zbx_json_addstring(json, "value", buildtime_value, ZBX_JSON_TYPE_STRING);
zbx_json_close(json);
zbx_json_addobject(json, "installtime");
zbx_json_addint64(json, "timestamp", installtime_timestamp);
zbx_json_addstring(json, "value", installtime_value, ZBX_JSON_TYPE_STRING);
zbx_json_close(json);
zbx_json_close(json);
}
static void dpkg_details(const char *manager, const char *line, const char *regex, struct zbx_json *json)
{
static char fmt[64] = "";
char status[DETAIL_BUF] = "", name[DETAIL_BUF] = "", version[DETAIL_BUF] = "", arch[DETAIL_BUF] = "";
zbx_uint64_t size;
int rv;
if ('\0' == fmt[0])
{
zbx_snprintf(fmt, sizeof(fmt),
"%%" ZBX_FS_SIZE_T "[^,],"
"%%" ZBX_FS_SIZE_T "[^,],"
"%%" ZBX_FS_SIZE_T "[^,],"
"%%" ZBX_FS_SIZE_T "[^,],"
"%" ZBX_FS_UI64,
(zbx_fs_size_t)(sizeof(status) - 1),
(zbx_fs_size_t)(sizeof(name) - 1),
(zbx_fs_size_t)(sizeof(version) - 1),
(zbx_fs_size_t)(sizeof(arch) - 1));
}
#define NUM_FIELDS 5
if (NUM_FIELDS != (rv = sscanf(line, fmt, status, name, version, arch, &size)))
{
zabbix_log(LOG_LEVEL_DEBUG, "%s: could only collect %d (expected %d) values, ignoring",
line, rv, NUM_FIELDS);
return;
}
#undef NUM_FIELDS
if (0 != strcmp(status, "install ok installed"))
return;
if (NULL != regex && NULL == zbx_regexp_match(name, regex, NULL))
return;
/* the reported size is in kB, we want bytes */
size *= ZBX_KIBIBYTE;
add_package_to_json(json, name, manager, version, size, arch, 0, "", 0, "");
}
static void rpm_details(const char *manager, const char *line, const char *regex, struct zbx_json *json)
{
static char fmt[64] = "";
char name[DETAIL_BUF] = "", version[DETAIL_BUF] = "", arch[DETAIL_BUF] = "",
buildtime_value[DETAIL_BUF], installtime_value[DETAIL_BUF];
zbx_uint64_t size;
time_t buildtime_timestamp, installtime_timestamp;
int rv;
if ('\0' == fmt[0])
{
zbx_snprintf(fmt, sizeof(fmt),
"%%" ZBX_FS_SIZE_T "[^,],"
"%%" ZBX_FS_SIZE_T "[^,],"
"%%" ZBX_FS_SIZE_T "[^,],"
"%" ZBX_FS_TIME_T ","
"%" ZBX_FS_TIME_T ","
"%" ZBX_FS_UI64,
(zbx_fs_size_t)(sizeof(name) - 1),
(zbx_fs_size_t)(sizeof(version) - 1),
(zbx_fs_size_t)(sizeof(arch) - 1));
}
#define NUM_FIELDS 6
if (NUM_FIELDS != (rv = sscanf(line, fmt, name, version, arch, &size, &buildtime_timestamp,
&installtime_timestamp)))
{
zabbix_log(LOG_LEVEL_DEBUG, "%s: could only collect %d (expected %d) values, ignoring",
line, rv, NUM_FIELDS);
return;
}
#undef NUM_FIELDS
if (NULL != regex && NULL == zbx_regexp_match(name, regex, NULL))
return;
strftime(buildtime_value, sizeof(buildtime_value), TIME_FMT, localtime(&buildtime_timestamp));
strftime(installtime_value, sizeof(installtime_value), TIME_FMT, localtime(&installtime_timestamp));
add_package_to_json(json, name, manager, version, size, arch, buildtime_timestamp, buildtime_value,
installtime_timestamp, installtime_value);
}
static void pacman_details(const char *manager, const char *line, const char *regex, struct zbx_json *json)
{
static char fmt[64] = "";
char name[DETAIL_BUF] = "", version[DETAIL_BUF] = "", arch[DETAIL_BUF] = "",
size_str[DETAIL_BUF] = "", buildtime_value[DETAIL_BUF] = "", installtime_value[DETAIL_BUF],
*suffix;
const char *p;
zbx_uint64_t size;
time_t buildtime_timestamp, installtime_timestamp;
struct tm tm;
double size_double;
int rv;
/* e. g. " tpm2-tss, 3.2.0-3, x86_64, 2.86 MiB, Tue Nov 1 20:46:18 2022, Sun Nov 6 00:04:09 2022" */
if ('\0' == fmt[0])
{
zbx_snprintf(fmt, sizeof(fmt),
" %%" ZBX_FS_SIZE_T "[^,],"
" %%" ZBX_FS_SIZE_T "[^,],"
" %%" ZBX_FS_SIZE_T "[^,],"
" %%" ZBX_FS_SIZE_T "[^,],"
" %%" ZBX_FS_SIZE_T "[^,],"
" %%" ZBX_FS_SIZE_T "[^,]",
(zbx_fs_size_t)(sizeof(name) - 1),
(zbx_fs_size_t)(sizeof(version) - 1),
(zbx_fs_size_t)(sizeof(arch) - 1),
(zbx_fs_size_t)(sizeof(size_str) - 1),
(zbx_fs_size_t)(sizeof(buildtime_value) - 1),
(zbx_fs_size_t)(sizeof(installtime_value) - 1));
}
#define NUM_FIELDS 6
if (NUM_FIELDS != (rv = sscanf(line, fmt, name, version, arch, size_str, buildtime_value, installtime_value)))
{
zabbix_log(LOG_LEVEL_DEBUG, "%s: could only collect %d (expected %d) values, ignoring",
line, rv, NUM_FIELDS);
return;
}
#undef NUM_FIELDS
if (NULL != regex && NULL == zbx_regexp_match(name, regex, NULL))
return;
if (NULL == (suffix = strchr(size_str, ' ')))
{
zabbix_log(LOG_LEVEL_DEBUG, "%s: unexpected Installed Size \"%s\" (expected whitespace), ignoring",
line, size_str);
return;
}
*suffix++ = '\0';
if (SUCCEED != zbx_is_double(size_str, &size_double))
{
zabbix_log(LOG_LEVEL_DEBUG, "%s: unexpected Installed Size \"%s\" (expected type double), ignoring",
line, size_str);
return;
}
/* pacman supports the following labels: */
/* "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB" */
if (0 == strcmp(suffix, "B"))
{
size = (zbx_uint64_t)size_double;
}
else if (0 == strcmp(suffix, "KiB"))
{
size = (zbx_uint64_t)(size_double * ZBX_KIBIBYTE);
}
else if (0 == strcmp(suffix, "MiB"))
{
size = (zbx_uint64_t)(size_double * ZBX_MEBIBYTE);
}
else if (0 == strcmp(suffix, "GiB"))
{
size = (zbx_uint64_t)(size_double * ZBX_GIBIBYTE);
}
else if (0 == strcmp(suffix, "TiB"))
{
size = (zbx_uint64_t)(size_double * ZBX_TEBIBYTE);
}
else
{
zabbix_log(LOG_LEVEL_DEBUG, "%s: unrecognized Installed Size suffix \"%s %s\", ignoring",
line, size_str, suffix);
return;
}
memset(&tm, 0, sizeof(tm));
tm.tm_isdst = -1; /* tell mktime() to determine whether daylight saving time is in effect */
if (NULL == (p = strptime(buildtime_value, TIME_FMT, &tm)))
{
zabbix_log(LOG_LEVEL_DEBUG, "%s: unexpected Build Date \"%s\", ignoring", line, buildtime_value);
return;
}
if ('\0' != *p)
{
zabbix_log(LOG_LEVEL_DEBUG, "%s: unexpected Build Date format at \"%s\" (expected %s), ignoring",
line, p, TIME_FMT);
return;
}
buildtime_timestamp = mktime(&tm);
memset(&tm, 0, sizeof(tm));
tm.tm_isdst = -1; /* tell mktime() to determine whether daylight saving time is in effect */
if (NULL == strptime(installtime_value, TIME_FMT, &tm))
{
zabbix_log(LOG_LEVEL_DEBUG, "%s: unexpected Install Date \"%s\", ignoring", line, installtime_value);
return;
}
if ('\0' != *p)
{
zabbix_log(LOG_LEVEL_DEBUG, "%s: unexpected Install Date format at \"%s\" (expected %s), ignoring",
line, p, TIME_FMT);
return;
}
installtime_timestamp = mktime(&tm);
add_package_to_json(json, name, manager, version, size, arch, buildtime_timestamp, buildtime_value,
installtime_timestamp, installtime_value);
}
static void pkgtools_details(const char *manager, const char *line, const char *regex, struct zbx_json *json)
{
static char fmt[64] = "";
char name[DETAIL_BUF] = "", version[DETAIL_BUF] = "", arch[DETAIL_BUF] = "",
size_str[DETAIL_BUF] = "", *out = NULL, *suffix;
zbx_uint64_t size, multiplier;
double size_double;
int rv;
/* Since can contain dashes we cannot use sscanf() here so regex is the only way. */
/* /var/log/packages/util-linux-2.27.1-x86_64-1:UNCOMPRESSED PACKAGE SIZE: 1.9M */
/* "version" and "build" must be combined: ---...: */
if (SUCCEED != zbx_regexp_sub(
line,
"^/var/log/packages/(.*)-([^-]+)-([^-]+)-([^:]+):UNCOMPRESSED PACKAGE SIZE:\\s+(.*)$",
"\\1,\\2-\\4,\\3,\\5",
&out))
{
zabbix_log(LOG_LEVEL_DEBUG, "internal error: could not compile regex");
goto out;
}
if (NULL == out)
{
zabbix_log(LOG_LEVEL_DEBUG, "unexpected line \"%s\", ignoring", line);
goto out;
}
if ('\0' == fmt[0])
{
zbx_snprintf(fmt, sizeof(fmt),
" %%" ZBX_FS_SIZE_T "[^,],"
" %%" ZBX_FS_SIZE_T "[^,],"
" %%" ZBX_FS_SIZE_T "[^,],"
" %%" ZBX_FS_SIZE_T "[^,],",
(zbx_fs_size_t)(sizeof(name) - 1),
(zbx_fs_size_t)(sizeof(version) - 1),
(zbx_fs_size_t)(sizeof(arch) - 1),
(zbx_fs_size_t)(sizeof(size_str) - 1));
}
#define NUM_FIELDS 4
rv = sscanf(out, fmt, name, version, arch, size_str);
if (NUM_FIELDS != rv)
{
zabbix_log(LOG_LEVEL_DEBUG, "%s: could only collect %d (expected %d) values, ignoring",
line, rv, NUM_FIELDS);
goto out;
}
#undef NUM_FIELDS
if (NULL != regex && NULL == zbx_regexp_match(name, regex, NULL))
goto out;
/* according to pkgtools source code the size suffix is */
/* either 'K' or 'M' and it may be specified in 3 formats: */
/* K */
/* .M */
/* M */
if (NULL != (suffix = strchr(size_str, 'K')))
{
multiplier = ZBX_KIBIBYTE;
}
else if (NULL != (suffix = strchr(size_str, 'M')))
{
multiplier = ZBX_MEBIBYTE;
}
else
{
zabbix_log(LOG_LEVEL_DEBUG, "%s: unexpected size suffix in \"%s\": expected 'K' or 'M', ignoring",
line, size_str);
goto out;
}
*suffix = '\0';
if (SUCCEED != zbx_is_double(size_str, &size_double))
{
zabbix_log(LOG_LEVEL_DEBUG, "%s: unexpected size in \"%s\"", line, size_str);
goto out;
}
size = (zbx_uint64_t)(size_double * multiplier);
add_package_to_json(json, name, manager, version, size, arch, 0, "", 0, "");
out:
zbx_free(out);
}
static void portage_details(const char *manager, const char *line, const char *regex, struct zbx_json *json)
{
int rv;
static char pkginfo_fmt[128] = "";
char sizeinfo_fmt[128] = "", *pkginfo, *sizeinfo, *saveptr, *l;
char category[DETAIL_BUF] = "", name[DETAIL_BUF] = "", version[DETAIL_BUF] = "",
revision[DETAIL_BUF] = "", repo[DETAIL_BUF] = "";
size_t files, nonfiles, size;
l = zbx_strdup(NULL, line);
pkginfo = strtok_r(l, ":", &saveptr);
sizeinfo = strtok_r(NULL, ":", &saveptr);
if (NULL == pkginfo || NULL == sizeinfo)
{
zabbix_log(LOG_LEVEL_DEBUG, "failed to find package version or size information (%s)",
line);
goto out;
}
/*
* e.g. "dev-lang,tcl,8.6.12,r1,gentoo: 1104 files, 25 non-files, 10871914 bytes"
* or "dev-lang,perl,5.36.0,r1,gentoo: 1920 files (1919 unique), 326 non-files, 58056025 bytes"
*/
if ('\0' == *pkginfo_fmt)
{
zbx_snprintf(pkginfo_fmt, sizeof(pkginfo_fmt),
"%%" ZBX_FS_SIZE_T "[^,]," /* category */
"%%" ZBX_FS_SIZE_T "[^,]," /* name */
"%%" ZBX_FS_SIZE_T "[^,]," /* version */
"%%" ZBX_FS_SIZE_T "[^,]," /* revision */
"%%" ZBX_FS_SIZE_T "[^ ]" /* repo */
,
(zbx_fs_size_t)(sizeof(category) - 1),
(zbx_fs_size_t)(sizeof(name) - 1),
(zbx_fs_size_t)(sizeof(version) - 1),
(zbx_fs_size_t)(sizeof(revision) - 1),
(zbx_fs_size_t)(sizeof(repo) - 1));
}
/* NOTE: as sizeinfo may differ in format, we can't make sizeinfo_fmt static */
zbx_snprintf(sizeinfo_fmt, sizeof(sizeinfo_fmt),
"%" ZBX_FS_UI64 " files%s, "
"%" ZBX_FS_UI64 " non-files, "
"%" ZBX_FS_SIZE_T " bytes"
,
(NULL != strchr(sizeinfo, '('))
? " (%*lu unique)"
: "");
#define PKGINFO_NUM_FIELDS 5
if (PKGINFO_NUM_FIELDS != (rv = sscanf(pkginfo, pkginfo_fmt, category, name, version, revision, repo)))
{
zabbix_log(LOG_LEVEL_DEBUG, "%s: could only collect %d (expected %d) values, ignoring",
pkginfo, rv, PKGINFO_NUM_FIELDS);
goto out;
}
#undef PKGINFO_NUM_FIELDS
#define SIZEINFO_NUM_FIELDS 3
if (SIZEINFO_NUM_FIELDS != (rv = sscanf(sizeinfo, sizeinfo_fmt, &files, &nonfiles, &size)))
{
zabbix_log(LOG_LEVEL_DEBUG, "%s: could only collect %d (expected %d) values, ignoring",
sizeinfo, rv, SIZEINFO_NUM_FIELDS);
goto out;
}
#undef SIZEINFO_NUM_FIELDS
if (NULL != regex && NULL == zbx_regexp_match(name, regex, NULL))
goto out;
/*
* XXX: due to the rigidity of the add_package_to_json() interface, we can't report file counts.
* We should make it easier to add arbitrary data items to the package object.
*/
add_package_to_json(json, name, manager, version, size, "", 0, "", 0, "");
out:
zbx_free(l);
}
static void print_packages(char **buffer, size_t *alloc_len, size_t *offset, zbx_vector_str_t *packages,
const char *manager)
{
int i;
if (NULL != manager)
zbx_snprintf_alloc(buffer, alloc_len, offset, "[%s]", manager);
if (0 < packages->values_num)
{
if (NULL != manager)
zbx_chrcpy_alloc(buffer, alloc_len, offset, ' ');
zbx_vector_str_sort(packages, ZBX_DEFAULT_STR_COMPARE_FUNC);
for (i = 0; i < packages->values_num; i++)
zbx_snprintf_alloc(buffer, alloc_len, offset, "%s, ", packages->values[i]);
*offset -= 2;
}
if (NULL != *buffer)
(*buffer)[*offset] = '\0';
}
/**
* NAME
* TEST_CMD
* LIST_CMD
* DETAILS_CMD
* LIST_PARSER
* DETAILS_PARSER
*/
static ZBX_PACKAGE_MANAGER package_managers[] =
{
{
"dpkg",
"dpkg --version 2> /dev/null",
"dpkg --get-selections",
"LC_ALL=C dpkg-query -W -f='${Status},${Package},${Version},${Architecture},${Installed-Size}\n'",
dpkg_list,
dpkg_details
},
{
"pkgtools",
"[ -d /var/log/packages ] && echo true",
"ls /var/log/packages",
"grep -r '^UNCOMPRESSED PACKAGE SIZE' /var/log/packages",
NULL,
pkgtools_details
},
{
"rpm",
"rpm --version 2> /dev/null",
"rpm -qa",
"LC_ALL=C rpm -qa --queryformat '%{NAME},%{VERSION}-%{RELEASE},%{ARCH},%{SIZE},%{BUILDTIME},%{INSTALLTIME}\n'",
NULL,
rpm_details
},
{
"pacman",
"pacman --version 2> /dev/null",
"pacman -Q",
"LC_ALL=C pacman -Qi 2>/dev/null | grep -E '^(Name|Installed Size|Version|Architecture|(Install|Build) Date)' | cut -f2- -d: | paste -d, - - - - - -",
NULL,
pacman_details
},
{
"portage",
"qsize --version 2> /dev/null",
"qlist -C -I -F '%{PN},%{PV},%{PR}'",
"qsize -C --bytes -F '%{CATEGORY},%{PN},%{PV},%{PR},%{REPO}'",
NULL,
portage_details
},
{0}
};
int system_sw_packages(AGENT_REQUEST *request, AGENT_RESULT *result)
{
size_t output_alloc = 0, output_offset = 0;
int ret = SYSINFO_RET_FAIL, show_pm, check_regex, check_manager;
char *output = NULL, *regex, *manager, *mode, tmp[MAX_STRING_LEN], *buf = NULL,
*package, *saveptr;
zbx_vector_str_t packages;
ZBX_PACKAGE_MANAGER *mng;
if (3 < request->nparam)
{
SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
return ret;
}
regex = get_rparam(request, 0);
manager = get_rparam(request, 1);
mode = get_rparam(request, 2);
check_regex = (NULL != regex && '\0' != *regex && 0 != strcmp(regex, "all"));
check_manager = (NULL != manager && '\0' != *manager && 0 != strcmp(manager, "all"));
if (NULL == mode || '\0' == *mode || 0 == strcmp(mode, "full"))
show_pm = 1; /* show package managers' names */
else if (0 == strcmp(mode, "short"))
show_pm = 0;
else
{
SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
return ret;
}
zbx_vector_str_create(&packages);
for (int i = 0; NULL != package_managers[i].name; i++)
{
mng = &package_managers[i];
saveptr = NULL;
if (1 == check_manager && 0 != strcmp(manager, mng->name))
continue;
if (SUCCEED == zbx_execute(mng->test_cmd, &buf, tmp, sizeof(tmp), request->timeout,
ZBX_EXIT_CODE_CHECKS_DISABLED, NULL) &&
'\0' != *buf) /* consider this manager if test_cmd outputs anything to stdout */
{
if (SUCCEED != zbx_execute(mng->list_cmd, &buf, tmp, sizeof(tmp), request->timeout,
ZBX_EXIT_CODE_CHECKS_DISABLED, NULL))
{
continue;
}
ret = SYSINFO_RET_OK;
package = strtok_r(buf, "\n", &saveptr);
while (NULL != package)
{
if (NULL != mng->list_parser) /* check if the package name needs to be parsed */
{
if (SUCCEED == mng->list_parser(package, tmp, sizeof(tmp)))
package = tmp;
else
goto next;
}
if (1 == check_regex && NULL == zbx_regexp_match(package, regex, NULL))
goto next;
zbx_vector_str_append(&packages, zbx_strdup(NULL, package));
next:
package = strtok_r(NULL, "\n", &saveptr);
}
if (1 == show_pm)
{
print_packages(&output, &output_alloc, &output_offset, &packages, mng->name);
zbx_chrcpy_alloc(&output, &output_alloc, &output_offset, '\n');
zbx_vector_str_clear_ext(&packages, zbx_str_free);
}
}
}
zbx_free(buf);
if (0 == show_pm)
{
print_packages(&output, &output_alloc, &output_offset, &packages, NULL);
zbx_vector_str_clear_ext(&packages, zbx_str_free);
}
else if (0 != output_offset)
{
output[--output_offset] = '\0';
}
zbx_vector_str_destroy(&packages);
if (SYSINFO_RET_OK == ret)
{
if (NULL == output)
output = zbx_strdup(NULL, "");
SET_TEXT_RESULT(result, output);
}
else
{
SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot obtain package information."));
zbx_free(output);
}
return ret;
}
static void append_to_pretty_ver(char **pretty, const char *str)
{
size_t prt_alloc = 0, prt_offset = 0;
if (NULL == *pretty)
zbx_strcpy_alloc(pretty, &prt_alloc, &prt_offset, str);
else
*pretty = zbx_dsprintf(*pretty, "%s %s", *pretty, str);
return;
}
int system_sw_os_get(AGENT_REQUEST *request, AGENT_RESULT *result)
{
#define SW_OS_GET_TYPE "os_type"
#define SW_OS_GET_PROD_NAME "product_name"
#define SW_OS_GET_ARCH "architecture"
#define SW_OS_GET_KRNL_MAJOR "kernel_major"
#define SW_OS_GET_KRNL_MINOR "kernel_minor"
#define SW_OS_GET_KRNL_PATCH "kernel_patch"
#define SW_OS_GET_KRNL "kernel"
#define SW_OS_GET_VER_PRETTY "version_pretty"
#define SW_OS_GET_VER_FULL "version_full"
struct zbx_json j;
struct utsname info;
int read;
char *str, *prt_version = NULL;
char major[sizeof(info.release)], minor[sizeof(info.release)], patch[sizeof(info.release)];
ZBX_UNUSED(request);
if (0 < request->nparam)
{
SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
return SYSINFO_RET_FAIL;
}
zbx_json_init(&j, ZBX_JSON_STAT_BUF_LEN);
zbx_json_addstring(&j, SW_OS_GET_TYPE, "linux", ZBX_JSON_TYPE_STRING);
if (SUCCEED == get_os_name(&str))
{
zbx_json_addstring(&j, SW_OS_GET_PROD_NAME, str, ZBX_JSON_TYPE_STRING);
append_to_pretty_ver(&prt_version, str);
}
zbx_free(str);
if (0 != uname(&info))
goto out;
if (0 != strlen(info.machine))
{
zbx_json_addstring(&j, SW_OS_GET_ARCH, info.machine, ZBX_JSON_TYPE_STRING);
append_to_pretty_ver(&prt_version, info.machine);
}
if (0 != strlen(info.release))
{
read = sscanf(info.release, "%[0-9].%[0-9].%[0-9]", major, minor, patch);
if (0 < read)
zbx_json_addstring(&j, SW_OS_GET_KRNL_MAJOR, major, ZBX_JSON_TYPE_STRING);
if (1 < read)
zbx_json_addstring(&j, SW_OS_GET_KRNL_MINOR, minor, ZBX_JSON_TYPE_STRING);
if (2 < read)
zbx_json_addstring(&j, SW_OS_GET_KRNL_PATCH, patch, ZBX_JSON_TYPE_STRING);
zbx_json_addstring(&j, SW_OS_GET_KRNL, info.release, ZBX_JSON_TYPE_STRING);
append_to_pretty_ver(&prt_version, info.release);
}
out:
if (NULL != prt_version && 0 != strlen(prt_version))
zbx_json_addstring(&j, SW_OS_GET_VER_PRETTY, prt_version, ZBX_JSON_TYPE_STRING);
if (SUCCEED == get_os_info_file(&str, SW_OS_FULL))
zbx_json_addstring(&j, SW_OS_GET_VER_FULL, str, ZBX_JSON_TYPE_STRING);
else
zbx_json_addstring(&j, SW_OS_GET_VER_FULL, "", ZBX_JSON_TYPE_STRING);
zbx_free(str);
zbx_free(prt_version);
SET_STR_RESULT(result, strdup(j.buffer));
zbx_json_free(&j);
return SYSINFO_RET_OK;
#undef SW_OS_GET_TYPE
#undef SW_OS_GET_PROD_NAME
#undef SW_OS_GET_ARCH
#undef SW_OS_GET_KRNL_MAJOR
#undef SW_OS_GET_KRNL_MINOR
#undef SW_OS_GET_KRNL_PATCH
#undef SW_OS_GET_KRNL
#undef SW_OS_GET_VER_PRETTY
#undef SW_OS_GET_VER_FULL
}
int system_sw_packages_get(AGENT_REQUEST *request, AGENT_RESULT *result)
{
int ret = SYSINFO_RET_FAIL, check_regex, check_manager;
char *regex, *manager, *line, *saveptr, *buf = NULL, error[MAX_STRING_LEN];
ZBX_PACKAGE_MANAGER *mng;
struct zbx_json json;
int timeout;
if (2 < request->nparam)
{
SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
return ret;
}
regex = get_rparam(request, 0);
manager = get_rparam(request, 1);
check_regex = (NULL != regex && '\0' != *regex && 0 != strcmp(regex, "all"));
check_manager = (NULL != manager && '\0' != *manager && 0 != strcmp(manager, "all"));
zbx_json_initarray(&json, 10 * ZBX_KIBIBYTE);
timeout = request->timeout;
for (int i = 0; NULL != package_managers[i].name; i++)
{
time_t tm_start;
mng = &package_managers[i];
saveptr = NULL;
if (1 == check_manager && 0 != strcmp(manager, mng->name))
continue;
tm_start = time(NULL);
if (SUCCEED == zbx_execute(mng->test_cmd, &buf, error, sizeof(error), timeout,
ZBX_EXIT_CODE_CHECKS_DISABLED, NULL) &&
'\0' != *buf) /* consider this manager if test_cmd outputs anything to stdout */
{
timeout = timeout - (int)(time(NULL) - tm_start);
tm_start = time(NULL);
if (SUCCEED != zbx_execute(mng->details_cmd, &buf, error, sizeof(error), timeout,
ZBX_EXIT_CODE_CHECKS_DISABLED, NULL))
{
continue;
}
timeout = timeout - (int)(time(NULL) - tm_start);
ret = SYSINFO_RET_OK;
line = strtok_r(buf, "\n", &saveptr);
while (NULL != line)
{
mng->details_parser(mng->name, line, (1 == check_regex ? regex : NULL), &json);
line = strtok_r(NULL, "\n", &saveptr);
}
}
}
zbx_free(buf);
if (SYSINFO_RET_OK == ret)
SET_TEXT_RESULT(result, zbx_strdup(NULL, json.buffer));
else
SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot obtain package information."));
zbx_json_free(&json);
return ret;
}