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 "log.h"
24 #include "zbxalgo.h"
25 #include "inodes.h"
26 
get_fs_size_stat(const char * fs,zbx_uint64_t * total,zbx_uint64_t * free,zbx_uint64_t * used,double * pfree,double * pused,char ** error)27 static int	get_fs_size_stat(const char *fs, zbx_uint64_t *total, zbx_uint64_t *free,
28 		zbx_uint64_t *used, double *pfree, double *pused, char **error)
29 {
30 #ifdef HAVE_SYS_STATVFS_H
31 #	ifdef HAVE_SYS_STATVFS64
32 #		define ZBX_STATFS	statvfs64
33 #	else
34 #		define ZBX_STATFS	statvfs
35 #	endif
36 #	define ZBX_BSIZE	f_frsize
37 #else
38 #	ifdef HAVE_SYS_STATFS64
39 #		define ZBX_STATFS	statfs64
40 #	else
41 #		define ZBX_STATFS	statfs
42 #	endif
43 #	define ZBX_BSIZE	f_bsize
44 #endif
45 	struct ZBX_STATFS	s;
46 
47 	if (0 != ZBX_STATFS(fs, &s))
48 	{
49 		*error = zbx_dsprintf(NULL, "Cannot obtain filesystem information: %s", zbx_strerror(errno));
50 		zabbix_log(LOG_LEVEL_DEBUG,"%s failed with error: %s",__func__, *error);
51 		return SYSINFO_RET_FAIL;
52 	}
53 
54 	/* Available space could be negative (top bit set) if we hit disk space */
55 	/* reserved for non-privileged users. Treat it as 0.                    */
56 	if (0 != ZBX_IS_TOP_BIT_SET(s.f_bavail))
57 		s.f_bavail = 0;
58 
59 	if (NULL != total)
60 		*total = (zbx_uint64_t)s.f_blocks * s.ZBX_BSIZE;
61 
62 	if (NULL != free)
63 		*free = (zbx_uint64_t)s.f_bavail * s.ZBX_BSIZE;
64 
65 	if (NULL != used)
66 		*used = (zbx_uint64_t)(s.f_blocks - s.f_bfree) * s.ZBX_BSIZE;
67 
68 	if (NULL != pfree)
69 	{
70 		if (0 != s.f_blocks - s.f_bfree + s.f_bavail)
71 			*pfree = (double)(100.0 * s.f_bavail) / (s.f_blocks - s.f_bfree + s.f_bavail);
72 		else
73 			*pfree = 0;
74 	}
75 
76 	if (NULL != pused)
77 	{
78 		if (0 != s.f_blocks - s.f_bfree + s.f_bavail)
79 			*pused = 100.0 - (double)(100.0 * s.f_bavail) / (s.f_blocks - s.f_bfree + s.f_bavail);
80 		else
81 			*pused = 0;
82 	}
83 
84 	return SYSINFO_RET_OK;
85 }
86 
VFS_FS_USED(const char * fs,AGENT_RESULT * result)87 static int	VFS_FS_USED(const char *fs, AGENT_RESULT *result)
88 {
89 	zbx_uint64_t	value;
90 	char		*error;
91 
92 	if (SYSINFO_RET_OK != get_fs_size_stat(fs, NULL, NULL, &value, NULL, NULL, &error))
93 	{
94 		SET_MSG_RESULT(result, error);
95 		return SYSINFO_RET_FAIL;
96 	}
97 
98 	SET_UI64_RESULT(result, value);
99 
100 	return SYSINFO_RET_OK;
101 }
102 
VFS_FS_FREE(const char * fs,AGENT_RESULT * result)103 static int	VFS_FS_FREE(const char *fs, AGENT_RESULT *result)
104 {
105 	zbx_uint64_t	value;
106 	char		*error;
107 
108 	if (SYSINFO_RET_OK != get_fs_size_stat(fs, NULL, &value, NULL, NULL, NULL, &error))
109 	{
110 		SET_MSG_RESULT(result, error);
111 		return SYSINFO_RET_FAIL;
112 	}
113 
114 	SET_UI64_RESULT(result, value);
115 
116 	return SYSINFO_RET_OK;
117 }
118 
VFS_FS_TOTAL(const char * fs,AGENT_RESULT * result)119 static int	VFS_FS_TOTAL(const char *fs, AGENT_RESULT *result)
120 {
121 	zbx_uint64_t	value;
122 	char		*error;
123 
124 	if (SYSINFO_RET_OK != get_fs_size_stat(fs, &value, NULL, NULL, NULL, NULL, &error))
125 	{
126 		SET_MSG_RESULT(result, error);
127 		return SYSINFO_RET_FAIL;
128 	}
129 
130 	SET_UI64_RESULT(result, value);
131 
132 	return SYSINFO_RET_OK;
133 }
134 
VFS_FS_PFREE(const char * fs,AGENT_RESULT * result)135 static int	VFS_FS_PFREE(const char *fs, AGENT_RESULT *result)
136 {
137 	double	value;
138 	char	*error;
139 
140 	if (SYSINFO_RET_OK != get_fs_size_stat(fs, NULL, NULL, NULL, &value, NULL, &error))
141 	{
142 		SET_MSG_RESULT(result, error);
143 		return SYSINFO_RET_FAIL;
144 	}
145 
146 	SET_DBL_RESULT(result, value);
147 
148 	return SYSINFO_RET_OK;
149 }
150 
VFS_FS_PUSED(const char * fs,AGENT_RESULT * result)151 static int	VFS_FS_PUSED(const char *fs, AGENT_RESULT *result)
152 {
153 	double	value;
154 	char	*error;
155 
156 	if (SYSINFO_RET_OK != get_fs_size_stat(fs, NULL, NULL, NULL, NULL, &value, &error))
157 	{
158 		SET_MSG_RESULT(result, error);
159 		return SYSINFO_RET_FAIL;
160 	}
161 
162 	SET_DBL_RESULT(result, value);
163 
164 	return SYSINFO_RET_OK;
165 }
166 
vfs_fs_size(AGENT_REQUEST * request,AGENT_RESULT * result)167 static int	vfs_fs_size(AGENT_REQUEST *request, AGENT_RESULT *result)
168 {
169 	char	*fsname, *mode;
170 
171 	if (2 < request->nparam)
172 	{
173 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
174 		return SYSINFO_RET_FAIL;
175 	}
176 
177 	fsname = get_rparam(request, 0);
178 	mode = get_rparam(request, 1);
179 
180 	if (NULL == fsname || '\0' == *fsname)
181 	{
182 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid first parameter."));
183 		return SYSINFO_RET_FAIL;
184 	}
185 
186 	if (NULL == mode || '\0' == *mode || 0 == strcmp(mode, "total"))	/* default parameter */
187 		return VFS_FS_TOTAL(fsname, result);
188 	if (0 == strcmp(mode, "free"))
189 		return VFS_FS_FREE(fsname, result);
190 	if (0 == strcmp(mode, "pfree"))
191 		return VFS_FS_PFREE(fsname, result);
192 	if (0 == strcmp(mode, "used"))
193 		return VFS_FS_USED(fsname, result);
194 	if (0 == strcmp(mode, "pused"))
195 		return VFS_FS_PUSED(fsname, result);
196 
197 	SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid second parameter."));
198 
199 	return SYSINFO_RET_FAIL;
200 }
201 
VFS_FS_SIZE(AGENT_REQUEST * request,AGENT_RESULT * result)202 int	VFS_FS_SIZE(AGENT_REQUEST *request, AGENT_RESULT *result)
203 {
204 	return zbx_execute_threaded_metric(vfs_fs_size, request, result);
205 }
206 
zbx_get_vfs_name_by_type(int type)207 static const char	*zbx_get_vfs_name_by_type(int type)
208 {
209 	extern struct vfs_ent	*getvfsbytype(int type);
210 
211 	struct vfs_ent		*vfs;
212 	static char		**vfs_names = NULL;
213 	static size_t		vfs_names_alloc = 0;
214 
215 	if (type + 1 > vfs_names_alloc)
216 	{
217 		size_t	num = type + 1;
218 
219 		vfs_names = zbx_realloc(vfs_names, sizeof(char *) * num);
220 		memset(vfs_names + vfs_names_alloc, 0, sizeof(char *) * (num - vfs_names_alloc));
221 		vfs_names_alloc = num;
222 	}
223 
224 	if (NULL == vfs_names)
225 		return "unknown";
226 
227 	if (NULL == vfs_names[type] && NULL != (vfs = getvfsbytype(type)))
228 		vfs_names[type] = zbx_strdup(vfs_names[type], vfs->vfsent_name);
229 
230 	return NULL != vfs_names[type] ? vfs_names[type] : "unknown";
231 }
232 
VFS_FS_DISCOVERY(AGENT_REQUEST * request,AGENT_RESULT * result)233 int	VFS_FS_DISCOVERY(AGENT_REQUEST *request, AGENT_RESULT *result)
234 {
235 	int		rc, sz, i, ret = SYSINFO_RET_FAIL;
236 	struct vmount	*vms = NULL, *vm;
237 	struct zbx_json	j;
238 
239 	/* check how many bytes to allocate for the mounted filesystems */
240 	if (-1 == (rc = mntctl(MCTL_QUERY, sizeof(sz), (char *)&sz)))
241 	{
242 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain system information: %s", zbx_strerror(errno)));
243 		return SYSINFO_RET_FAIL;
244 	}
245 
246 	sz *= 2;
247 	vms = zbx_malloc(vms, (size_t)sz);
248 
249 	/* get the list of mounted filesystems */
250 	/* return code is number of filesystems returned */
251 	if (-1 == (rc = mntctl(MCTL_QUERY, sz, (char *)vms)))
252 	{
253 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain system information: %s", zbx_strerror(errno)));
254 		goto error;
255 	}
256 
257 	zbx_json_initarray(&j, ZBX_JSON_STAT_BUF_LEN);
258 
259 	for (i = 0, vm = vms; i < rc; i++)
260 	{
261 		zbx_json_addobject(&j, NULL);
262 		zbx_json_addstring(&j, ZBX_LLD_MACRO_FSNAME, (char *)vm + vm->vmt_data[VMT_STUB].vmt_off,
263 				ZBX_JSON_TYPE_STRING);
264 		zbx_json_addstring(&j, ZBX_LLD_MACRO_FSTYPE, zbx_get_vfs_name_by_type(vm->vmt_gfstype),
265 				ZBX_JSON_TYPE_STRING);
266 		zbx_json_close(&j);
267 
268 		/* go to the next vmount structure */
269 		vm = (struct vmount *)((char *)vm + vm->vmt_length);
270 	}
271 
272 	zbx_json_close(&j);
273 
274 	SET_STR_RESULT(result, zbx_strdup(NULL, j.buffer));
275 
276 	zbx_json_free(&j);
277 
278 	ret = SYSINFO_RET_OK;
279 error:
280 	zbx_free(vms);
281 
282 	return ret;
283 }
284 
vfs_fs_get(AGENT_REQUEST * request,AGENT_RESULT * result)285 static int	vfs_fs_get(AGENT_REQUEST *request, AGENT_RESULT *result)
286 {
287 	int			rc, sz, i, ret = SYSINFO_RET_FAIL;
288 	struct vmount		*vms = NULL, *vm;
289 	struct zbx_json		j;
290 	zbx_uint64_t		total, not_used, used;
291 	zbx_uint64_t		itotal, inot_used, iused;
292 	double			pfree, pused;
293 	double			ipfree, ipused;
294 	char 			*error;
295 	zbx_vector_ptr_t	mntpoints;
296 	zbx_mpoint_t		*mntpoint;
297 	char 			*mpoint;
298 
299 	/* check how many bytes to allocate for the mounted filesystems */
300 	if (-1 == (rc = mntctl(MCTL_QUERY, sizeof(sz), (char *)&sz)))
301 	{
302 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain system information: %s", zbx_strerror(errno)));
303 		return SYSINFO_RET_FAIL;
304 	}
305 
306 	zbx_vector_ptr_create(&mntpoints);
307 
308 	sz *= 2;
309 	vms = zbx_malloc(vms, (size_t)sz);
310 
311 	/* get the list of mounted filesystems */
312 	/* return code is number of filesystems returned */
313 	if (-1 == (rc = mntctl(MCTL_QUERY, sz, (char *)vms)))
314 	{
315 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain system information: %s", zbx_strerror(errno)));
316 		goto out;
317 	}
318 	for (i = 0, vm = vms; i < rc; i++)
319 	{
320 		mpoint = (char *)vm + vm->vmt_data[VMT_STUB].vmt_off;
321 
322 		if (SYSINFO_RET_OK != get_fs_size_stat(mpoint, &total, &not_used, &used, &pfree, &pused, &error))
323 		{
324 			zbx_free(error);
325 			continue;
326 		}
327 		if (SYSINFO_RET_OK != get_fs_inode_stat(mpoint, &itotal, &inot_used, &iused, &ipfree, &ipused, "pused",
328 				&error))
329 		{
330 			zbx_free(error);
331 			continue;
332 		}
333 
334 		mntpoint = (zbx_mpoint_t *)zbx_malloc(NULL, sizeof(zbx_mpoint_t));
335 		zbx_strlcpy(mntpoint->fsname, mpoint, MAX_STRING_LEN);
336 		zbx_strlcpy(mntpoint->fstype, zbx_get_vfs_name_by_type(vm->vmt_gfstype), MAX_STRING_LEN);
337 		mntpoint->bytes.total = total;
338 		mntpoint->bytes.used = used;
339 		mntpoint->bytes.not_used = not_used;
340 		mntpoint->bytes.pfree = pfree;
341 		mntpoint->bytes.pused = pused;
342 		mntpoint->inodes.total = itotal;
343 		mntpoint->inodes.used = iused;
344 		mntpoint->inodes.not_used = inot_used;
345 		mntpoint->inodes.pfree = ipfree;
346 		mntpoint->inodes.pused = ipused;
347 
348 		zbx_vector_ptr_append(&mntpoints, mntpoint);
349 
350 		/* go to the next vmount structure */
351 		vm = (struct vmount *)((char *)vm + vm->vmt_length);
352 	}
353 
354 	if (-1 == (rc = mntctl(MCTL_QUERY, sz, (char *)vms)))
355 	{
356 		SET_MSG_RESULT(result, zbx_dsprintf(NULL, "Cannot obtain system information: %s", zbx_strerror(errno)));
357 		goto out;
358 	}
359 
360 	zbx_json_initarray(&j, ZBX_JSON_STAT_BUF_LEN);
361 
362 	for (i = 0, vm = vms; i < rc; i++)
363 	{
364 		int idx;
365 
366 		mpoint = (char *)vm + vm->vmt_data[VMT_STUB].vmt_off;
367 
368 		if (FAIL != (idx = zbx_vector_ptr_search(&mntpoints, mpoint, ZBX_DEFAULT_STR_COMPARE_FUNC)))
369 		{
370 			mntpoint = (zbx_mpoint_t *)mntpoints.values[idx];
371 			zbx_json_addobject(&j, NULL);
372 			zbx_json_addstring(&j, ZBX_SYSINFO_TAG_FSNAME, mntpoint->fsname, ZBX_JSON_TYPE_STRING);
373 			zbx_json_addstring(&j, ZBX_SYSINFO_TAG_FSTYPE, mntpoint->fstype, ZBX_JSON_TYPE_STRING);
374 			zbx_json_addobject(&j, ZBX_SYSINFO_TAG_BYTES);
375 			zbx_json_adduint64(&j, ZBX_SYSINFO_TAG_TOTAL, mntpoint->bytes.total);
376 			zbx_json_adduint64(&j, ZBX_SYSINFO_TAG_FREE, mntpoint->bytes.not_used);
377 			zbx_json_adduint64(&j, ZBX_SYSINFO_TAG_USED, mntpoint->bytes.used);
378 			zbx_json_addfloat(&j, ZBX_SYSINFO_TAG_PFREE, mntpoint->bytes.pfree);
379 			zbx_json_addfloat(&j, ZBX_SYSINFO_TAG_PUSED, mntpoint->bytes.pused);
380 			zbx_json_close(&j);
381 			zbx_json_addobject(&j, ZBX_SYSINFO_TAG_INODES);
382 			zbx_json_adduint64(&j, ZBX_SYSINFO_TAG_TOTAL, mntpoint->inodes.total);
383 			zbx_json_adduint64(&j, ZBX_SYSINFO_TAG_FREE, mntpoint->inodes.not_used);
384 			zbx_json_adduint64(&j, ZBX_SYSINFO_TAG_USED, mntpoint->inodes.used);
385 			zbx_json_addfloat(&j, ZBX_SYSINFO_TAG_PFREE, mntpoint->inodes.pfree);
386 			zbx_json_addfloat(&j, ZBX_SYSINFO_TAG_PUSED, mntpoint->inodes.pused);
387 			zbx_json_close(&j);
388 			zbx_json_close(&j);
389 		}
390 
391 		/* go to the next vmount structure */
392 		vm = (struct vmount *)((char *)vm + vm->vmt_length);
393 	}
394 
395 	zbx_json_close(&j);
396 
397 	SET_STR_RESULT(result, zbx_strdup(NULL, j.buffer));
398 
399 	zbx_json_free(&j);
400 
401 	ret = SYSINFO_RET_OK;
402 out:
403 	zbx_free(vms);
404 	zbx_vector_ptr_clear_ext(&mntpoints, (zbx_clean_func_t)zbx_mpoints_free);
405 	zbx_vector_ptr_destroy(&mntpoints);
406 
407 	return ret;
408 }
409 
VFS_FS_GET(AGENT_REQUEST * request,AGENT_RESULT * result)410 int	VFS_FS_GET(AGENT_REQUEST *request, AGENT_RESULT *result)
411 {
412 	return zbx_execute_threaded_metric(vfs_fs_get, request, result);
413 
414 }
415