1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 **/
19 
20 #include "common.h"
21 #include "sysinfo.h"
22 #include "zbxjson.h"
23 #include "zbxalgo.h"
24 #include "log.h"
25 
26 typedef struct
27 {
28 	char		*fsname;
29 	char		*fstype;
30 	char		*fsdrivetype;
31 	zbx_uint64_t	total;
32 	zbx_uint64_t	not_used;
33 	zbx_uint64_t	used;
34 	double		pfree;
35 	double		pused;
36 }
37 zbx_wmpoint_t;
38 
39 #define zbx_wcsdup(old, str)		zbx_wcsdup2(__FILE__, __LINE__, old, str)
40 
zbx_wcsdup2(const char * filename,int line,wchar_t * old,const wchar_t * str)41 static wchar_t	*zbx_wcsdup2(const char *filename, int line, wchar_t *old, const wchar_t *str)
42 {
43 	int	retry;
44 	wchar_t	*ptr = NULL;
45 
46 	zbx_free(old);
47 
48 	for (retry = 10; 0 < retry && NULL == ptr; ptr = wcsdup(str), retry--)
49 		;
50 
51 	if (NULL != ptr)
52 		return ptr;
53 
54 	zabbix_log(LOG_LEVEL_CRIT, "[file:%s,line:%d] zbx_wcsdup: out of memory. Requested " ZBX_FS_SIZE_T " bytes.",
55 			filename, line, (zbx_fs_size_t)((wcslen(str) + 1) * sizeof(wchar_t)));
56 
57 	exit(EXIT_FAILURE);
58 }
59 
wmpoint_compare_func(const void * d1,const void * d2)60 static int	wmpoint_compare_func(const void *d1, const void *d2)
61 {
62 	const zbx_wmpoint_t	*m1 = *(const zbx_wmpoint_t **)d1;
63 	const zbx_wmpoint_t	*m2 = *(const zbx_wmpoint_t **)d2;
64 
65 	return strcmp(m1->fsname, m2->fsname);
66 }
67 
get_fs_size_stat(const char * fs,zbx_uint64_t * total,zbx_uint64_t * not_used,zbx_uint64_t * used,double * pfree,double * pused,char ** error)68 static int	get_fs_size_stat(const char *fs, zbx_uint64_t *total, zbx_uint64_t *not_used,
69 		zbx_uint64_t *used, double *pfree, double *pused, char **error)
70 {
71 	wchar_t 	*wpath;
72 	ULARGE_INTEGER	freeBytes, totalBytes;
73 
74 	wpath = zbx_utf8_to_unicode(fs);
75 	if (0 == GetDiskFreeSpaceEx(wpath, &freeBytes, &totalBytes, NULL))
76 	{
77 		zbx_free(wpath);
78 		*error = zbx_dsprintf(NULL, "Cannot obtain filesystem information: %s",
79 				strerror_from_system(GetLastError()));
80 		zabbix_log(LOG_LEVEL_DEBUG,"%s failed with error: %s",__func__, *error);
81 		return SYSINFO_RET_FAIL;
82 	}
83 	zbx_free(wpath);
84 
85 	*total = totalBytes.QuadPart;
86 	*not_used = freeBytes.QuadPart;
87 	*used = totalBytes.QuadPart - freeBytes.QuadPart;
88 	*pfree = (double)(__int64)freeBytes.QuadPart * 100. / (double)(__int64)totalBytes.QuadPart;
89 	*pused = (double)((__int64)totalBytes.QuadPart - (__int64)freeBytes.QuadPart) * 100. /
90 			(double)(__int64)totalBytes.QuadPart;
91 
92 	return SYSINFO_RET_OK;
93 
94 }
95 
vfs_fs_size(AGENT_REQUEST * request,AGENT_RESULT * result,HANDLE timeout_event)96 static int	vfs_fs_size(AGENT_REQUEST *request, AGENT_RESULT *result, HANDLE timeout_event)
97 {
98 	char		*path, *mode;
99 	char		*error;
100 	zbx_uint64_t	total, used, free;
101 	double		pused,pfree;
102 
103 	/* 'timeout_event' argument is here to make the vfs_fs_size() prototype as required by */
104 	/* zbx_execute_threaded_metric() on MS Windows */
105 	ZBX_UNUSED(timeout_event);
106 
107 	if (2 < request->nparam)
108 	{
109 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
110 		return SYSINFO_RET_FAIL;
111 	}
112 
113 	path = get_rparam(request, 0);
114 	mode = get_rparam(request, 1);
115 
116 	if (NULL == path || '\0' == *path)
117 	{
118 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid first parameter."));
119 		return SYSINFO_RET_FAIL;
120 	}
121 
122 	if (SYSINFO_RET_OK != get_fs_size_stat(path, &total, &free, &used, &pfree, &pused, &error))
123 	{
124 		SET_MSG_RESULT(result, error);
125 		return SYSINFO_RET_FAIL;
126 	}
127 
128 	if (NULL == mode || '\0' == *mode || 0 == strcmp(mode, "total"))
129 		SET_UI64_RESULT(result, total);
130 	else if (0 == strcmp(mode, "free"))
131 		SET_UI64_RESULT(result, free);
132 	else if (0 == strcmp(mode, "used"))
133 		SET_UI64_RESULT(result, used);
134 	else if (0 == strcmp(mode, "pfree"))
135 		SET_DBL_RESULT(result, pfree);
136 	else if (0 == strcmp(mode, "pused"))
137 		SET_DBL_RESULT(result, pused);
138 	else
139 	{
140 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid second parameter."));
141 		return SYSINFO_RET_FAIL;
142 	}
143 
144 	return SYSINFO_RET_OK;
145 }
146 
VFS_FS_SIZE(AGENT_REQUEST * request,AGENT_RESULT * result)147 int	VFS_FS_SIZE(AGENT_REQUEST *request, AGENT_RESULT *result)
148 {
149 	return zbx_execute_threaded_metric(vfs_fs_size, request, result);
150 }
151 
get_drive_type_string(UINT type)152 static const char	*get_drive_type_string(UINT type)
153 {
154 	switch (type)
155 	{
156 		case DRIVE_UNKNOWN:
157 			return "unknown";
158 		case DRIVE_NO_ROOT_DIR:
159 			return "norootdir";
160 		case DRIVE_REMOVABLE:
161 			return "removable";
162 		case DRIVE_FIXED:
163 			return "fixed";
164 		case DRIVE_REMOTE:
165 			return "remote";
166 		case DRIVE_CDROM:
167 			return "cdrom";
168 		case DRIVE_RAMDISK:
169 			return "ramdisk";
170 		default:
171 			THIS_SHOULD_NEVER_HAPPEN;
172 			return "unknown";
173 	}
174 }
175 
get_fs_data(const wchar_t * path,char ** fsname,char ** fstype,char ** fsdrivetype)176 static void	get_fs_data(const wchar_t* path, char **fsname, char **fstype, char **fsdrivetype)
177 {
178 	wchar_t	fs_name[MAX_PATH + 1], *long_path = NULL;
179 	size_t	sz;
180 
181 	*fsname = zbx_unicode_to_utf8(path);
182 	if (0 < (sz = strlen(*fsname)) && '\\' == (*fsname)[--sz])
183 		(*fsname)[sz] = '\0';
184 
185 	/* add \\?\ prefix if path exceeds MAX_PATH */
186 	if (MAX_PATH < (sz = wcslen(path) + 1) && 0 != wcsncmp(path, L"\\\\?\\", 4))
187 	{
188 		/* allocate memory buffer enough to hold null-terminated path and prefix */
189 		long_path = (wchar_t*)zbx_malloc(long_path, (sz + 4) * sizeof(wchar_t));
190 
191 		long_path[0] = L'\\';
192 		long_path[1] = L'\\';
193 		long_path[2] = L'?';
194 		long_path[3] = L'\\';
195 
196 		memcpy(long_path + 4, path, sz * sizeof(wchar_t));
197 		path = long_path;
198 	}
199 
200 	if (FALSE != GetVolumeInformation(path, NULL, 0, NULL, NULL, NULL, fs_name, ARRSIZE(fs_name)))
201 		*fstype = zbx_unicode_to_utf8(fs_name);
202 	else
203 		*fstype = zbx_strdup(NULL, "UNKNOWN");
204 
205 	*fsdrivetype = zbx_strdup(NULL, get_drive_type_string(GetDriveType(path)));
206 
207 	zbx_free(long_path);
208 }
209 
add_fs_to_vector(zbx_vector_ptr_t * mntpoints,wchar_t * path,char ** error)210 static int	add_fs_to_vector(zbx_vector_ptr_t *mntpoints, wchar_t *path, char **error)
211 {
212 	zbx_wmpoint_t	*mntpoint;
213 	zbx_uint64_t	total, not_used, used;
214 	double		pfree, pused;
215 	char 		*fsname = NULL, *fstype = NULL, *fsdrivetype = NULL;
216 
217 	get_fs_data(path, &fsname, &fstype, &fsdrivetype);
218 
219 	if (SYSINFO_RET_OK != get_fs_size_stat(fsname, &total, &not_used, &used, &pfree, &pused, error))
220 	{
221 		zbx_free(fsname);
222 		zbx_free(fstype);
223 		zbx_free(fsdrivetype);
224 		return FAIL;
225 	}
226 
227 	mntpoint = (zbx_wmpoint_t *)zbx_malloc(NULL, sizeof(zbx_wmpoint_t));
228 	mntpoint->fsname = fsname;
229 	mntpoint->fstype = fstype;
230 	mntpoint->fsdrivetype = fsdrivetype;
231 	mntpoint->total = total;
232 	mntpoint->not_used = not_used;
233 	mntpoint->used = used;
234 	mntpoint->pfree = pfree;
235 	mntpoint->pused = pused;
236 	zbx_vector_ptr_append(mntpoints, mntpoint);
237 
238 	return SUCCEED;
239 }
240 
zbx_wmpoints_free(zbx_wmpoint_t * mpoint)241 static void	zbx_wmpoints_free(zbx_wmpoint_t *mpoint)
242 {
243 	zbx_free(mpoint->fsname);
244 	zbx_free(mpoint->fstype);
245 	zbx_free(mpoint->fsdrivetype);
246 	zbx_free(mpoint);
247 }
248 
get_mount_paths(zbx_vector_ptr_t * mount_paths,char ** error)249 static int	get_mount_paths(zbx_vector_ptr_t *mount_paths, char **error)
250 {
251 	wchar_t	*buffer = NULL, volume_name[MAX_PATH + 1], *p;
252 	DWORD	size_dw, last_error;
253 	HANDLE	volume = INVALID_HANDLE_VALUE;
254 	size_t	sz;
255 	int	ret = FAIL;
256 
257 	/* make an initial call to GetLogicalDriveStrings() to get the necessary size into the dwSize variable */
258 	if (0 == (size_dw = GetLogicalDriveStrings(0, buffer)))
259 	{
260 		*error = zbx_strdup(*error, "Cannot obtain necessary buffer size from system.");
261 		return FAIL;
262 	}
263 
264 	buffer = (wchar_t *)zbx_malloc(buffer, (size_dw + 1) * sizeof(wchar_t));
265 
266 	/* make a second call to GetLogicalDriveStrings() to get the actual data we require */
267 	if (0 == (size_dw = GetLogicalDriveStrings(size_dw, buffer)))
268 	{
269 		*error = zbx_strdup(*error, "Cannot obtain necessary buffer size from system.");
270 		goto out;
271 	}
272 
273 	/* add drive letters */
274 	for (p = buffer, sz = wcslen(p); sz > 0; p += sz + 1, sz = wcslen(p))
275 		zbx_vector_ptr_append(mount_paths, zbx_wcsdup(NULL, p));
276 
277 	if (INVALID_HANDLE_VALUE == (volume = FindFirstVolume(volume_name, ARRSIZE(volume_name))))
278 	{
279 		*error = zbx_strdup(*error, "Cannot find a volume.");
280 		goto out;
281 	}
282 
283 	/* search volumes for mount point folder paths */
284 	do
285 	{
286 		while (FALSE == GetVolumePathNamesForVolumeName(volume_name, buffer, size_dw, &size_dw))
287 		{
288 			if (ERROR_MORE_DATA != (last_error = GetLastError()))
289 			{
290 				*error = zbx_dsprintf(*error, "Cannot obtain a list of filesystems: %s",
291 						strerror_from_system(last_error));
292 				goto out;
293 			}
294 
295 			buffer = (wchar_t*)zbx_realloc(buffer, size_dw * sizeof(wchar_t));
296 		}
297 
298 		for (p = buffer, sz = wcslen(p); sz > 0; p += sz + 1, sz = wcslen(p))
299 		{
300 			/* add mount point folder paths but skip drive letters */
301 			if (3 < sz)
302 				zbx_vector_ptr_append(mount_paths, zbx_wcsdup(NULL, p));
303 		}
304 
305 	} while (FALSE != FindNextVolume(volume, volume_name, ARRSIZE(volume_name)));
306 
307 	if (ERROR_NO_MORE_FILES != (last_error = GetLastError()))
308 	{
309 		*error = zbx_dsprintf(*error, "Cannot obtain complete list of filesystems.",
310 				strerror_from_system(last_error));
311 		goto out;
312 	}
313 
314 	ret = SUCCEED;
315 out:
316 	if (INVALID_HANDLE_VALUE != volume)
317 		FindVolumeClose(volume);
318 
319 	zbx_free(buffer);
320 	return ret;
321 }
322 
VFS_FS_DISCOVERY(AGENT_REQUEST * request,AGENT_RESULT * result)323 int	VFS_FS_DISCOVERY(AGENT_REQUEST *request, AGENT_RESULT *result)
324 {
325 	struct zbx_json		j;
326 	int 			i, ret = SYSINFO_RET_FAIL;
327 	zbx_vector_ptr_t	mount_paths;
328 	char			*error = NULL, *fsname, *fstype, *fsdrivetype;
329 
330 	zbx_vector_ptr_create(&mount_paths);
331 	zbx_json_initarray(&j, ZBX_JSON_STAT_BUF_LEN);
332 
333 	if (FAIL == get_mount_paths(&mount_paths, &error))
334 	{
335 		SET_MSG_RESULT(result, error);
336 		goto out;
337 	}
338 
339 
340 	for (i = 0; i < mount_paths.values_num; i++)
341 	{
342 		get_fs_data(mount_paths.values[i], &fsname, &fstype, &fsdrivetype);
343 
344 		zbx_json_addobject(&j, NULL);
345 		zbx_json_addstring(&j, ZBX_LLD_MACRO_FSNAME, fsname, ZBX_JSON_TYPE_STRING);
346 		zbx_json_addstring(&j, ZBX_LLD_MACRO_FSTYPE, fstype, ZBX_JSON_TYPE_STRING);
347 		zbx_json_addstring(&j, ZBX_LLD_MACRO_FSDRIVETYPE, fsdrivetype, ZBX_JSON_TYPE_STRING);
348 		zbx_json_close(&j);
349 
350 		zbx_free(fsname);
351 		zbx_free(fstype);
352 		zbx_free(fsdrivetype);
353 	}
354 
355 	zbx_json_close(&j);
356 
357 	SET_STR_RESULT(result, zbx_strdup(NULL, j.buffer));
358 	ret = SYSINFO_RET_OK;
359 out:
360 	zbx_vector_ptr_clear_ext(&mount_paths, (zbx_clean_func_t)zbx_ptr_free);
361 	zbx_vector_ptr_destroy(&mount_paths);
362 
363 	zbx_json_free(&j);
364 
365 	return ret;
366 }
367 
vfs_fs_get(AGENT_REQUEST * request,AGENT_RESULT * result,HANDLE timeout_event)368 static int	vfs_fs_get(AGENT_REQUEST *request, AGENT_RESULT *result,  HANDLE timeout_event)
369 {
370 	size_t			sz;
371 	struct zbx_json		j;
372 	zbx_vector_ptr_t	mntpoints;
373 	zbx_wmpoint_t		*mpoint;
374 	int			i, ret = SYSINFO_RET_FAIL;
375 	char 			*error = NULL;
376 	zbx_vector_ptr_t	mount_paths;
377 
378 	zbx_vector_ptr_create(&mount_paths);
379 	zbx_json_initarray(&j, ZBX_JSON_STAT_BUF_LEN);
380 
381 	if (FAIL == get_mount_paths(&mount_paths, &error))
382 	{
383 		SET_MSG_RESULT(result, error);
384 		goto out;
385 	}
386 
387 	/* 'timeout_event' argument is here to make the vfs_fs_size() prototype as required by */
388 	/* zbx_execute_threaded_metric() on MS Windows */
389 	ZBX_UNUSED(timeout_event);
390 	zbx_vector_ptr_create(&mntpoints);
391 
392 	for (i = 0; i < mount_paths.values_num; i++)
393 	{
394 		if (FAIL == add_fs_to_vector(&mntpoints, mount_paths.values[i], &error))
395 		{
396 			zabbix_log(LOG_LEVEL_DEBUG, "%s", error);
397 			zbx_free(error);
398 			continue;
399 		}
400 	}
401 
402 	zbx_vector_ptr_clear_ext(&mount_paths, (zbx_clean_func_t)zbx_ptr_free);
403 	if (FAIL == get_mount_paths(&mount_paths, &error))
404 	{
405 		SET_MSG_RESULT(result, error);
406 		goto out;
407 	}
408 
409 	for (i = 0; i < mount_paths.values_num; i++)
410 	{
411 		zbx_wmpoint_t	mpoint_local;
412 		int		idx;
413 
414 		mpoint_local.fsname = zbx_unicode_to_utf8(mount_paths.values[i]);
415 		if (0 < (sz = strlen(mpoint_local.fsname)) && '\\' == mpoint_local.fsname[--sz])
416 			mpoint_local.fsname[sz] = '\0';
417 
418 		if (FAIL != (idx = zbx_vector_ptr_search(&mntpoints, &mpoint_local, wmpoint_compare_func)))
419 		{
420 			mpoint = (zbx_wmpoint_t *)mntpoints.values[idx];
421 			zbx_json_addobject(&j, NULL);
422 			zbx_json_addstring(&j, ZBX_SYSINFO_TAG_FSNAME, mpoint->fsname, ZBX_JSON_TYPE_STRING);
423 			zbx_json_addstring(&j, ZBX_SYSINFO_TAG_FSTYPE, mpoint->fstype, ZBX_JSON_TYPE_STRING);
424 			zbx_json_addstring(&j, ZBX_SYSINFO_TAG_FSDRIVETYPE, mpoint->fsdrivetype, ZBX_JSON_TYPE_STRING);
425 			zbx_json_addobject(&j, ZBX_SYSINFO_TAG_BYTES);
426 			zbx_json_adduint64(&j, ZBX_SYSINFO_TAG_TOTAL, mpoint->total);
427 			zbx_json_adduint64(&j, ZBX_SYSINFO_TAG_FREE, mpoint->not_used);
428 			zbx_json_adduint64(&j, ZBX_SYSINFO_TAG_USED, mpoint->used);
429 			zbx_json_addfloat(&j, ZBX_SYSINFO_TAG_PFREE, mpoint->pfree);
430 			zbx_json_addfloat(&j, ZBX_SYSINFO_TAG_PUSED, mpoint->pused);
431 			zbx_json_close(&j);
432 			zbx_json_close(&j);
433 		}
434 		zbx_free(mpoint_local.fsname);
435 	}
436 
437 	zbx_json_close(&j);
438 
439 	SET_STR_RESULT(result, zbx_strdup(NULL, j.buffer));
440 	ret = SYSINFO_RET_OK;
441 out:
442 	zbx_vector_ptr_clear_ext(&mount_paths, (zbx_clean_func_t)zbx_ptr_free);
443 	zbx_vector_ptr_destroy(&mount_paths);
444 
445 	zbx_json_free(&j);
446 	zbx_vector_ptr_clear_ext(&mntpoints, (zbx_clean_func_t)zbx_wmpoints_free);
447 	zbx_vector_ptr_destroy(&mntpoints);
448 
449 	return ret;
450 }
451 
VFS_FS_GET(AGENT_REQUEST * request,AGENT_RESULT * result)452 int	VFS_FS_GET(AGENT_REQUEST *request, AGENT_RESULT *result)
453 {
454 	return zbx_execute_threaded_metric(vfs_fs_get, request, result);
455 }
456