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, ¬_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