diff options
-rw-r--r-- | ChangeLog.d/feature/ZBXNEXT-7732 | 1 | ||||
-rw-r--r-- | build/win32/project/Makefile_agent | 1 | ||||
-rw-r--r-- | build/win32/project/Makefile_common.inc | 2 | ||||
-rw-r--r-- | include/sysinfo.h | 3 | ||||
-rw-r--r-- | src/go/plugins/plugins_windows.go | 1 | ||||
-rw-r--r-- | src/go/plugins/windows/registry/registry.go | 311 | ||||
-rw-r--r-- | src/libs/zbxsysinfo/win32/registry.c | 471 | ||||
-rw-r--r-- | src/libs/zbxsysinfo/win32/win32.c | 3 | ||||
-rw-r--r-- | ui/include/classes/data/CItemData.php | 12 |
9 files changed, 803 insertions, 2 deletions
diff --git a/ChangeLog.d/feature/ZBXNEXT-7732 b/ChangeLog.d/feature/ZBXNEXT-7732 new file mode 100644 index 00000000000..dd958553a63 --- /dev/null +++ b/ChangeLog.d/feature/ZBXNEXT-7732 @@ -0,0 +1 @@ +..FG...... [ZBXNEXT-7732] added metrics for Windows Registry monitoring (dgoloscapov, rdetlavs, wiper) diff --git a/build/win32/project/Makefile_agent b/build/win32/project/Makefile_agent index 2f117d25886..773fbdb88c4 100644 --- a/build/win32/project/Makefile_agent +++ b/build/win32/project/Makefile_agent @@ -85,6 +85,7 @@ OBJS = \ ..\..\..\src\libs\zbxsysinfo\win32\memory.o \ ..\..\..\src\libs\zbxsysinfo\win32\net.o \ ..\..\..\src\libs\zbxsysinfo\win32\pdhmon.o \ + ..\..\..\src\libs\zbxsysinfo\win32\registry.o \ ..\..\..\src\libs\zbxsysinfo\win32\proc.o \ ..\..\..\src\libs\zbxsysinfo\win32\services.o \ ..\..\..\src\libs\zbxsysinfo\win32\swap.o \ diff --git a/build/win32/project/Makefile_common.inc b/build/win32/project/Makefile_common.inc index 944baa9420a..f4288b2e778 100644 --- a/build/win32/project/Makefile_common.inc +++ b/build/win32/project/Makefile_common.inc @@ -13,7 +13,7 @@ RESOURCE_RES = $(PROJECTNAME).res INCS = /I $(PROJECTDIR) /I .\ /I ..\include /I ..\include\common /I ..\..\..\include /I ..\..\..\include\common !IF "$(CPU)" == "i386" || "$(CPU)" == "x86" -COMMON_FLAGS = /D _WIN32_WINNT=0x0501 +COMMON_FLAGS = /D _WIN32_WINNT=0x0502 ADD_LFLAGS = $(ADD_LFLAGS) /SUBSYSTEM:"CONSOLE,5.01" !ELSEIF "$(CPU)" == "AMD64" COMMON_FLAGS = /D _WIN32_WINNT=0x0502 diff --git a/include/sysinfo.h b/include/sysinfo.h index 87963ffccbd..bd0d06c27d5 100644 --- a/include/sysinfo.h +++ b/include/sysinfo.h @@ -314,6 +314,8 @@ int NET_IF_LIST(AGENT_REQUEST *request, AGENT_RESULT *result); int WMI_GET(AGENT_REQUEST *request, AGENT_RESULT *result); int WMI_GETALL(AGENT_REQUEST *request, AGENT_RESULT *result); int VM_VMEMORY_SIZE(AGENT_REQUEST *request, AGENT_RESULT *result); +int REGISTRY_DATA(AGENT_REQUEST *request, AGENT_RESULT *result); +int REGISTRY_GET(AGENT_REQUEST *request, AGENT_RESULT *result); #endif #ifdef _AIX @@ -412,4 +414,3 @@ void zbx_alias_list_free(void); const char *zbx_alias_get(const char *orig); #endif - diff --git a/src/go/plugins/plugins_windows.go b/src/go/plugins/plugins_windows.go index 95b9060aaae..d0a88c01c66 100644 --- a/src/go/plugins/plugins_windows.go +++ b/src/go/plugins/plugins_windows.go @@ -52,6 +52,7 @@ import ( _ "zabbix.com/plugins/windows/eventlog" _ "zabbix.com/plugins/windows/perfinstance" _ "zabbix.com/plugins/windows/perfmon" + _ "zabbix.com/plugins/windows/registry" _ "zabbix.com/plugins/windows/services" _ "zabbix.com/plugins/windows/wmi" _ "zabbix.com/plugins/zabbix/async" diff --git a/src/go/plugins/windows/registry/registry.go b/src/go/plugins/windows/registry/registry.go new file mode 100644 index 00000000000..f8b519af487 --- /dev/null +++ b/src/go/plugins/windows/registry/registry.go @@ -0,0 +1,311 @@ +//go:build windows +// +build windows + +/* +** Zabbix +** Copyright (C) 2001-2022 Zabbix SIA +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**/ + +package registry + +import ( + "encoding/base64" + "encoding/json" + "errors" + "regexp" + "strings" + + "git.zabbix.com/ap/plugin-support/plugin" + "git.zabbix.com/ap/plugin-support/zbxerr" + "golang.org/x/sys/windows/registry" +) + +// Plugin - +type Plugin struct { + plugin.Base +} + +var impl Plugin + +type registryKey struct { + Fullkey string `json:"fullkey"` + Lastsubkey string `json:"lastsubkey"` +} + +type registryValue struct { + Fullkey string `json:"fullkey"` + Lastsubkey string `json:"lastsubkey"` + Name string `json:"name"` + Data interface{} `json:"data"` + Type string `json:"type"` +} + +const ( + RegistryDiscoveryModeValues = iota + RegistryDiscoveryModeKeys +) + +func getHive(key string) (hive registry.Key, e error) { + switch key { + case "HKLM", "HKEY_LOCAL_MACHINE": + return registry.LOCAL_MACHINE, nil + case "HKCU", "HKEY_CURRENT_USER": + return registry.CURRENT_USER, nil + case "HKCR", "HKEY_CLASSES_ROOT": + return registry.CLASSES_ROOT, nil + case "HKU", "HKEY_USERS": + return registry.USERS, nil + case "HKPD", "HKEY_PERFORMANCE_DATA": + return registry.PERFORMANCE_DATA, nil + } + + return 0, errors.New("Failed to parse registry key.") +} + +func convertValue(k registry.Key, value string) (result interface{}, stype string, err error) { + _, valueType, err := k.GetValue(value, nil) + if err != nil { + return nil, "", err + } + + switch valueType { + case registry.NONE: + return "", "REG_NONE", nil + case registry.EXPAND_SZ: + stype = "REG_EXPAND_SZ" + result, _, err = k.GetStringValue(value) + case registry.SZ: + stype = "REG_SZ" + result, _, err = k.GetStringValue(value) + case registry.BINARY: + stype = "REG_BINARY" + if val, _, err := k.GetBinaryValue(value); err == nil { + result = base64.StdEncoding.EncodeToString(val) + } else { + return nil, "", err + } + case registry.QWORD: + stype = "REG_QWORD" + result, _, err = k.GetIntegerValue(value) + case registry.DWORD: + stype = "REG_DWORD" + result, _, err = k.GetIntegerValue(value) + case registry.MULTI_SZ: + stype = "REG_MULTI_SZ" + result, _, err = k.GetStringsValue(value) + default: + return nil, "", errors.New("Unsupported registry data type.") + } + + return result, stype, err +} + +func discoverValues(hive registry.Key, fullkey string, discovered_values []registryValue, current_key string, + re *regexp.Regexp, shive string) (result []registryValue, e error) { + + k, err := registry.OpenKey(hive, fullkey, registry.READ) + if err != nil { + return nil, err + } + defer k.Close() + + subkeys, err := k.ReadSubKeyNames(0) + if err != nil { + return []registryValue{}, err + } + + values, err := k.ReadValueNames(0) + if err != nil { + return []registryValue{}, err + } + + for _, v := range values { + data, valtype, err := convertValue(k, v) + + if err != nil { + continue + } + + if re != nil { + if re.MatchString(v) { + discovered_values = append(discovered_values, + registryValue{shive + "\\" + fullkey, current_key, v, data, valtype}) + } + } else { + discovered_values = append(discovered_values, + registryValue{shive + "\\" + fullkey, current_key, v, data, valtype}) + } + } + + for _, subkey := range subkeys { + new_fullkey := fullkey + "\\" + subkey + discovered_values, _ = discoverValues(hive, new_fullkey, discovered_values, subkey, re, shive) + } + + return discovered_values, nil +} + +func discoverKeys(hive registry.Key, fullkey string, subkeys []registryKey, shive string) (result []registryKey, e error) { + k, err := registry.OpenKey(hive, fullkey, registry.ENUMERATE_SUB_KEYS) + if err != nil { + return nil, err + } + + s, err := k.ReadSubKeyNames(0) + defer k.Close() + + if err != nil { + return nil, err + } + + for _, i := range s { + current_key := fullkey + "\\" + i + subkeys = append(subkeys, registryKey{shive + "\\" + current_key, i}) + subkeys, _ = discoverKeys(hive, current_key, subkeys, shive) + } + + return subkeys, nil +} + +func splitFullkey(fullkey string) (hive registry.Key, key string, shive string, e error) { + idx := strings.Index(fullkey, "\\") + + if idx == -1 { + return 0, "", "", errors.New("Failed to parse registry key.") + } + + shive = fullkey[:idx] + hive, e = getHive(shive) + key = fullkey[idx+1:] + + return +} + +func getValue(params []string) (result interface{}, err error) { + if len(params) > 2 { + return nil, zbxerr.ErrorTooManyParameters + } + + if len(params) < 1 || params[0] == "" { + return nil, errors.New("Registry key is not supplied.") + } + + fullkey := params[0] + + hive, key, _, e := splitFullkey(fullkey) + if e != nil { + return nil, e + } + + var value string + + if len(params) == 2 { + value = params[1] + } + + handle, err := registry.OpenKey(hive, key, registry.QUERY_VALUE) + if err != nil { + return nil, err + } + defer handle.Close() + + result, _, err = convertValue(handle, value) + if err != nil { + return nil, err + } + + if x, ok := result.([]string); ok { + var j []byte + j, err = json.Marshal(x) + result = string(j) + } + + return +} + +func discover(params []string) (result string, err error) { + var j []byte + var re *regexp.Regexp + + if len(params) > 3 { + return "", zbxerr.ErrorTooManyParameters + } + + if len(params) < 1 || params[0] == "" { + return "", errors.New("Registry key is not supplied.") + } + + fullkey := params[0] + + hive, key, shive, e := splitFullkey(fullkey) + if e != nil { + return "", e + } + + mode := RegistryDiscoveryModeValues + + if len(params) > 1 { + switch params[1] { + case "values", "": + // default mode - RegistryDiscoveryModeValues + case "keys": + mode = RegistryDiscoveryModeKeys + default: + return "", errors.New("Invalid 'mode' parameter.") + } + + if len(params) == 3 { + if mode != RegistryDiscoveryModeValues { + return "", zbxerr.ErrorTooManyParameters + } + if re, err = regexp.Compile(params[2]); err != nil { + return "", err + } + } + } + switch mode { + case RegistryDiscoveryModeKeys: + results := make([]registryKey, 0) + results, err = discoverKeys(hive, key, results, shive) + j, _ = json.Marshal(results) + case RegistryDiscoveryModeValues: + results := make([]registryValue, 0) + results, err = discoverValues(hive, key, results, "", re, shive) + j, _ = json.Marshal(results) + } + + return string(j), err +} + +// Export - +func (p *Plugin) Export(key string, params []string, ctx plugin.ContextProvider) (result interface{}, err error) { + switch key { + case "registry.data": + return getValue(params) + case "registry.get": + return discover(params) + default: + return nil, plugin.UnsupportedMetricError + } +} + +func init() { + plugin.RegisterMetrics(&impl, "Registry", + "registry.data", "Return value of the registry key.", + "registry.get", "Discover registry key and its subkeys.", + ) +} diff --git a/src/libs/zbxsysinfo/win32/registry.c b/src/libs/zbxsysinfo/win32/registry.c new file mode 100644 index 00000000000..c0f4396e368 --- /dev/null +++ b/src/libs/zbxsysinfo/win32/registry.c @@ -0,0 +1,471 @@ +/* +** Zabbix +** Copyright (C) 2001-2022 Zabbix SIA +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software +** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +**/ + +#include "sysinfo.h" +#include "base64.h" +#include "zbxjson.h" +#include "zbxalgo.h" +#include "zbxregexp.h" +#include <locale.h> +#include <winreg.h> + +#define ZBX_SYSINFO_REGISTRY_TAG_FULLKEY "fullkey" +#define ZBX_SYSINFO_REGISTRY_TAG_LASTKEY "lastsubkey" +#define ZBX_SYSINFO_REGISTRY_TAG_NAME "name" +#define ZBX_SYSINFO_REGISTRY_TAG_DATA "data" +#define ZBX_SYSINFO_REGISTRY_TAG_TYPE "type" + +#define MAX_KEY_LENGTH 255 +#define MAX_DATA_LENGTH 65534 +#define MAX_VALUE_NAME 16383 +#define MAX_FULLKEY_LENGTH 4096 + +#define REGISTRY_DISCOVERY_MODE_KEYS 0 +#define REGISTRY_DISCOVERY_MODE_VALUES 1 + +static HKEY get_hkey_from_fullkey(char *fullkey) +{ + if (0 == strcmp("HKEY_CLASSES_ROOT", fullkey) || 0 == strcmp("HKCR", fullkey)) + return HKEY_CLASSES_ROOT; + else if (0 == strcmp("HKEY_CURRENT_CONFIG", fullkey) || 0 == strcmp("HKCC", fullkey)) + return HKEY_CURRENT_CONFIG; + else if (0 == strcmp("HKEY_CURRENT_USER", fullkey) || 0 == strcmp("HKCU", fullkey)) + return HKEY_CURRENT_USER; + else if (0 == strcmp("HKEY_CURRENT_USER_LOCAL_SETTINGS", fullkey) || 0 == strcmp("HKCULS", fullkey)) + return HKEY_CURRENT_USER_LOCAL_SETTINGS; + else if (0 == strcmp("HKEY_LOCAL_MACHINE", fullkey) || 0 == strcmp("HKLM", fullkey)) + return HKEY_LOCAL_MACHINE; + else if (0 == strcmp("HKEY_PERFORMANCE_DATA", fullkey) || 0 == strcmp("HKPD", fullkey)) + return HKEY_PERFORMANCE_DATA; + else if (0 == strcmp("HKEY_PERFORMANCE_NLSTEXT", fullkey) || 0 == strcmp("HKPN", fullkey)) + return HKEY_PERFORMANCE_NLSTEXT; + else if (0 == strcmp("HKEY_PERFORMANCE_TEXT", fullkey) || 0 == strcmp("HKPT", fullkey)) + return HKEY_PERFORMANCE_TEXT; + else if (0 == strcmp("HKEY_USERS", fullkey) || 0 == strcmp("HKU", fullkey)) + return HKEY_USERS; + + return 0; +} + +static const char *registry_type_to_string(DWORD type) +{ + switch (type) + { + case REG_BINARY: + return "REG_BINARY"; + case REG_DWORD: + return "REG_DWORD"; + case REG_EXPAND_SZ: + return "REG_EXPAND_SZ"; + case REG_LINK: + return "REG_LINK"; + case REG_MULTI_SZ: + return "REG_MULTI_SZ"; + case REG_NONE: + return "REG_NONE"; + case REG_QWORD: + return "REG_QWORD"; + case REG_SZ: + return "REG_SZ"; + } + + return "Unknown"; +} + +static void registry_get_multistring_value(const wchar_t *wbuffer, struct zbx_json *j) +{ + char *buffer; + + while (L'\0' != *wbuffer) + { + buffer = zbx_unicode_to_utf8(wbuffer); + zbx_json_addstring(j, NULL, buffer, ZBX_JSON_TYPE_STRING); + zbx_free(buffer); + wbuffer += wcslen(wbuffer) + 1 ; + } +} + +static int convert_value(DWORD type, const char *value, DWORD value_len, char **out) +{ + struct zbx_json j; + + switch (type) { + case REG_BINARY: + str_base64_encode_dyn(value, out, (int)value_len); + return SUCCEED; + case REG_DWORD: + *out = zbx_dsprintf(NULL, "%u", *(DWORD *)value); + return SUCCEED; + case REG_QWORD: + *out = zbx_dsprintf(NULL, "%lu", *(DWORDLONG *)value); + return SUCCEED; + case REG_MULTI_SZ: + zbx_json_init(&j, ZBX_JSON_STAT_BUF_LEN); + zbx_json_initarray(&j, ZBX_JSON_STAT_BUF_LEN); + + registry_get_multistring_value((wchar_t *)value, &j); + + zbx_json_close(&j); + *out = zbx_strdup(NULL, j.buffer); + zbx_json_free(&j); + return SUCCEED; + case REG_NONE: + *out = NULL; + return SUCCEED; + case REG_SZ: + case REG_EXPAND_SZ: + *out = zbx_unicode_to_utf8((wchar_t *)value); + return SUCCEED; + default: + return FAIL; + } +} + +ZBX_PTR_VECTOR_DECL(wchar_ptr, wchar_t *) +ZBX_PTR_VECTOR_IMPL(wchar_ptr, wchar_t *) + +static void discovery_get_regkey_values(HKEY hKey, wchar_t *current_subkey, struct zbx_json *j, int mode, + wchar_t *root, const char *regexp) +{ + wchar_t achClass[MAX_PATH] = TEXT(""), achValue[MAX_VALUE_NAME]; + DWORD cchClassName = MAX_PATH, cSubKeys=0, cValues, cbName, i, retCode, + cchValue = MAX_VALUE_NAME; + char *uroot, *usubkey; + zbx_vector_wchar_ptr_t wsubkeys; + + retCode = RegQueryInfoKey(hKey, achClass, &cchClassName, NULL, &cSubKeys, NULL, NULL, &cValues, NULL, NULL, + NULL, NULL); + + zbx_vector_wchar_ptr_create(&wsubkeys); + + uroot = zbx_unicode_to_utf8(root); + usubkey = zbx_unicode_to_utf8(current_subkey); + + if (REGISTRY_DISCOVERY_MODE_KEYS == mode) + { + if (*usubkey != '\0') + { + zbx_json_addobject(j, NULL); + zbx_json_addstring(j, ZBX_SYSINFO_REGISTRY_TAG_FULLKEY, uroot, ZBX_JSON_TYPE_STRING); + zbx_json_addstring(j, ZBX_SYSINFO_REGISTRY_TAG_LASTKEY, usubkey, ZBX_JSON_TYPE_STRING); + zbx_json_close(j); + } + + zbx_free(uroot); + zbx_free(usubkey); + } + + for (i = 0; i < cSubKeys; i++) + { + cbName = MAX_KEY_LENGTH; + retCode = RegEnumKeyEx(hKey, i, achClass, &cbName, NULL, NULL, NULL, NULL); + + if (ERROR_SUCCESS == retCode) + zbx_vector_wchar_ptr_append(&wsubkeys, wcsdup(achClass)); + } + + for (i = 0; i < (DWORD)wsubkeys.values_num; i++) + { + HKEY hSubkey; + wchar_t wnew_root[MAX_FULLKEY_LENGTH]; + wchar_t *wsubkey; + + wsubkey = wsubkeys.values[i]; + + if (0 == wcscmp(wsubkey, L"")) + continue; + + _snwprintf_s(wnew_root, MAX_FULLKEY_LENGTH, MAX_FULLKEY_LENGTH, L"%s\\%s", root, wsubkey); + + if (ERROR_SUCCESS == RegOpenKeyEx(hKey, wsubkey, 0, KEY_READ, &hSubkey)) + discovery_get_regkey_values(hSubkey, wsubkey, j, mode, wnew_root, regexp); + + RegCloseKey(hSubkey); + } + + zbx_vector_wchar_ptr_clear_ext(&wsubkeys, zbx_ptr_free); + zbx_vector_wchar_ptr_destroy(&wsubkeys); + + if (REGISTRY_DISCOVERY_MODE_VALUES == mode && 0 != cValues) + { + DWORD buffer_alloc = 1024; + char *buffer; + + buffer = zbx_malloc(NULL, buffer_alloc); + + for (i = 0, retCode = ERROR_SUCCESS; i < cValues; i++) + { + DWORD valueType, value_len = buffer_alloc; + char *uvaluename, *out = NULL; + + cchValue = MAX_VALUE_NAME; + achValue[0] = L'\0'; + + if (ERROR_MORE_DATA == (retCode = RegEnumValue(hKey, i, achValue, &cchValue, NULL, &valueType, + buffer, &value_len))) + { + buffer = zbx_realloc(buffer, value_len); + buffer_alloc = value_len; + + cchValue = MAX_VALUE_NAME; + retCode = RegEnumValue(hKey, i, achValue, &cchValue, NULL, &valueType, buffer, &value_len); + } + + if (ERROR_SUCCESS != retCode) + continue; + + uvaluename = zbx_unicode_to_utf8(achValue); + + if (NULL != regexp && '\0' != *regexp) + { + if (NULL == zbx_regexp_match(uvaluename, regexp, NULL)) + { + zbx_free(uvaluename); + continue; + } + } + + if (SUCCEED != convert_value(valueType, buffer, value_len, &out)) + continue; + + zbx_json_addobject(j, NULL); + + zbx_json_addstring(j, ZBX_SYSINFO_REGISTRY_TAG_FULLKEY, uroot, + ZBX_JSON_TYPE_STRING); + + zbx_json_addstring(j, ZBX_SYSINFO_REGISTRY_TAG_LASTKEY, usubkey, + ZBX_JSON_TYPE_STRING); + + zbx_json_addstring(j, ZBX_SYSINFO_REGISTRY_TAG_NAME, uvaluename, ZBX_JSON_TYPE_STRING); + zbx_free(uvaluename); + + switch (valueType) + { + case REG_DWORD: + case REG_QWORD: + zbx_json_addstring(j, ZBX_SYSINFO_REGISTRY_TAG_DATA, out, ZBX_JSON_TYPE_INT); + break; + case REG_NONE: + zbx_json_adduint64(j, ZBX_SYSINFO_REGISTRY_TAG_DATA, 0); + break; + case REG_MULTI_SZ: + zbx_json_addraw(j, ZBX_SYSINFO_REGISTRY_TAG_DATA, out); + break; + default: + zbx_json_addstring(j, ZBX_SYSINFO_REGISTRY_TAG_DATA, out, ZBX_JSON_TYPE_STRING); + } + + zbx_free(out); + + zbx_json_addstring(j, ZBX_SYSINFO_REGISTRY_TAG_TYPE, + registry_type_to_string(valueType), ZBX_JSON_TYPE_STRING); + + zbx_json_close(j); + } + + zbx_free(buffer); + } + + zbx_free(uroot); + zbx_free(usubkey); +} + +static int split_fullkey(char **fullkey, HKEY *hive_handle, char **hive_str) +{ + char *end; + + if (NULL == (end = strchr(*fullkey, '\\'))) + return FAIL; + + *end = '\0'; + + if (NULL == (*hive_handle = get_hkey_from_fullkey(*fullkey))) + return FAIL; + + if (NULL != hive_str) + *hive_str = *fullkey; + + *fullkey = *fullkey + (end - *fullkey) + 1; + + return SUCCEED; +} + +static int registry_discover(char *key, int mode, AGENT_RESULT *result, const char *regexp) +{ + wchar_t *wkey, *wfullkey = NULL;; + HKEY hkey, hive_handle; + struct zbx_json j; + DWORD retCode; + int ret = SUCCEED; + char *hive_str, *fullkey = NULL; + + if (FAIL == split_fullkey(&key, &hive_handle, &hive_str)) + { + SET_MSG_RESULT(result, zbx_strdup(NULL, "Failed to parse registry key.")); + return FAIL; + } + + zbx_json_initarray(&j, ZBX_JSON_STAT_BUF_LEN); + + wkey = zbx_utf8_to_unicode(key); + + if (ERROR_SUCCESS == (retCode = RegOpenKeyEx(hive_handle, wkey, 0, KEY_READ, &hkey))) + { + fullkey = zbx_dsprintf(fullkey, "%s\\%s", hive_str, key); + wfullkey = zbx_utf8_to_unicode(fullkey); + discovery_get_regkey_values(hkey, L"", &j, mode, wfullkey, regexp); + } + else + { + SET_MSG_RESULT(result, zbx_strdup(NULL, strerror_from_system(retCode))); + ret = FAIL; + goto out; + } + + RegCloseKey(hkey); + + zbx_json_close(&j); + SET_STR_RESULT(result, zbx_strdup(NULL, j.buffer)); +out: + zbx_json_free(&j); + zbx_free(wkey); + zbx_free(fullkey); + zbx_free(wfullkey); + + return ret; +} + +static int registry_get_value(char *key, const char *value, AGENT_RESULT *result) +{ + wchar_t *wkey, *wvalue; + char *data = NULL, *bin_value = NULL, *value_str = NULL; + DWORD BufferSize = 0, type; + LSTATUS errCode; + HKEY hive_handle; + int ret = SUCCEED; + + if (FAIL == split_fullkey(&key, &hive_handle, NULL)) + { + SET_MSG_RESULT(result, zbx_strdup(NULL, "Failed to parse registry key.")); + return FAIL; + } + + wkey = zbx_utf8_to_unicode(key); + wvalue = (NULL != value ? zbx_utf8_to_unicode(value) : NULL); + + errCode = RegGetValue(hive_handle, wkey, wvalue, RRF_RT_ANY, &type, NULL, &BufferSize); + if (ERROR_SUCCESS == errCode) + { + data = zbx_malloc(NULL, (size_t)BufferSize); + errCode = RegGetValue(hive_handle, wkey, wvalue, RRF_RT_ANY, &type, (PVOID)data, &BufferSize); + } + + if (ERROR_SUCCESS != errCode) + { + SET_MSG_RESULT(result, zbx_strdup(NULL, strerror_from_system(errCode))); + ret = FAIL; + goto out; + } + + if (SUCCEED == (ret = convert_value(type, data, BufferSize, &value_str))) + SET_STR_RESULT(result, value_str); + else + SET_MSG_RESULT(result, zbx_strdup(NULL, "Unsupported registry data type.")); +out: + zbx_free(wkey); + zbx_free(wvalue); + zbx_free(data); + + return ret; +} + +int REGISTRY_DATA(AGENT_REQUEST *request, AGENT_RESULT *result) +{ + char *regkey, *value_name; + + if (2 < request->nparam) + { + SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters.")); + return SYSINFO_RET_FAIL; + } + + regkey = get_rparam(request, 0); + + if (NULL == regkey || '\0' == *regkey) + { + SET_MSG_RESULT(result, zbx_strdup(NULL, "Registry key is not supplied.")); + return SYSINFO_RET_FAIL; + } + + value_name = get_rparam(request, 1); + + if (FAIL == registry_get_value(regkey, value_name, result)) + return SYSINFO_RET_FAIL; + + return SYSINFO_RET_OK; +} + +int REGISTRY_GET(AGENT_REQUEST *request, AGENT_RESULT *result) +{ + char *pkey, *pmode, *regexp; + int mode; + + if (3 < request->nparam) + { + SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters.")); + return SYSINFO_RET_FAIL; + } + + pkey = get_rparam(request, 0); + + if (NULL == pkey || '\0' == *pkey) + { + SET_MSG_RESULT(result, zbx_strdup(NULL, "Registry key is not supplied.")); + return SYSINFO_RET_FAIL; + } + + pmode = get_rparam(request, 1); + + if (NULL == pmode || '\0' == *pmode || 0 == strcmp(pmode, "values")) + { + mode = REGISTRY_DISCOVERY_MODE_VALUES; + } + else if (0 == strcmp(pmode, "keys")) + { + mode = REGISTRY_DISCOVERY_MODE_KEYS; + + if (2 < request->nparam) + { + SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters.")); + return SYSINFO_RET_FAIL; + } + } + else + { + SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid 'mode' parameter.")); + return SYSINFO_RET_FAIL; + } + + regexp = get_rparam(request, 2); + + if (FAIL == registry_discover(pkey, mode, result, regexp)) + return SYSINFO_RET_FAIL; + + return SYSINFO_RET_OK; +} diff --git a/src/libs/zbxsysinfo/win32/win32.c b/src/libs/zbxsysinfo/win32/win32.c index f4fce3d7cbc..f366657c564 100644 --- a/src/libs/zbxsysinfo/win32/win32.c +++ b/src/libs/zbxsysinfo/win32/win32.c @@ -71,5 +71,8 @@ ZBX_METRIC parameters_specific[] = {"wmi.get", CF_HAVEPARAMS, WMI_GET, "root\\cimv2,select Caption from Win32_OperatingSystem"}, {"wmi.getall", CF_HAVEPARAMS, WMI_GETALL, "root\\cimv2,select * from Win32_OperatingSystem"}, + {"registry.data", CF_HAVEPARAMS, REGISTRY_DATA, NULL}, + {"registry.get", CF_HAVEPARAMS, REGISTRY_GET, NULL}, + {NULL} }; diff --git a/ui/include/classes/data/CItemData.php b/ui/include/classes/data/CItemData.php index ae67b8298e5..a516b9842e2 100644 --- a/ui/include/classes/data/CItemData.php +++ b/ui/include/classes/data/CItemData.php @@ -61,6 +61,8 @@ final class CItemData { 'proc.mem[<name>,<user>,<mode>,<cmdline>,<memtype>]', 'proc.num[<name>,<user>,<state>,<cmdline>,<zone>]', 'proc_info[process,<attribute>,<type>]', + 'registry.data[key,<value name>]', + 'registry.get[key,<mode>,<name regexp>]', 'sensor[device,sensor,<mode>]', 'service.info[service,<param>]', 'services[<type>,<state>,<exclude>]', @@ -161,6 +163,8 @@ final class CItemData { 'proc.mem[<name>,<user>,<mode>,<cmdline>,<memtype>]', 'proc.num[<name>,<user>,<state>,<cmdline>,<zone>]', 'proc_info[process,<attribute>,<type>]', + 'registry.data[key,<value name>]', + 'registry.get[key,<mode>,<name regexp>]', 'sensor[device,sensor,<mode>]', 'service.info[service,<param>]', 'services[<type>,<state>,<exclude>]', @@ -1009,6 +1013,14 @@ final class CItemData { 'description' => _('Various information about specific process(es). Returns float'), 'value_type' => ITEM_VALUE_TYPE_FLOAT ], + 'registry.data[key,<value name>]' => [ + 'description' => _('Value data for value name in Windows Registry key.'), + 'value_type' => null + ], + 'registry.get[key,<mode>,<name regexp>]' => [ + 'description' => _('List of Windows Registry values or keys located at given key. Returns JSON.'), + 'value_type' => ITEM_VALUE_TYPE_TEXT + ], 'sensor[device,sensor,<mode>]' => [ 'description' => _('Hardware sensor reading. Returns float'), 'value_type' => ITEM_VALUE_TYPE_FLOAT |