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 "stats.h"
23 #include "diskdevices.h"
24 
25 #define ZBX_DEV_PFX	"/dev/"
26 #define ZBX_DEV_READ	0
27 #define ZBX_DEV_WRITE	1
28 
29 static struct statinfo	*si = NULL;
30 
get_diskstat(const char * devname,zbx_uint64_t * dstat)31 int	get_diskstat(const char *devname, zbx_uint64_t *dstat)
32 {
33 	int		i;
34 	struct devstat	*ds = NULL;
35 	int		ret = FAIL;
36 	char		dev[DEVSTAT_NAME_LEN + 10];
37 	const char	*pd;	/* pointer to device name without '/dev/' prefix, e.g. 'da0' */
38 
39 	assert(devname);
40 
41 	for (i = 0; i < ZBX_DSTAT_MAX; i++)
42 		dstat[i] = (zbx_uint64_t)__UINT64_C(0);
43 
44 	if (NULL == si)
45 	{
46 		si = (struct statinfo *)zbx_malloc(si, sizeof(struct statinfo));
47 		si->dinfo = (struct devinfo *)zbx_malloc(NULL, sizeof(struct devinfo));
48 		memset(si->dinfo, 0, sizeof(struct devinfo));
49 	}
50 
51 	pd = devname;
52 
53 	/* skip prefix ZBX_DEV_PFX, if present */
54 	if ('\0' != *devname && 0 == strncmp(pd, ZBX_DEV_PFX, ZBX_CONST_STRLEN(ZBX_DEV_PFX)))
55 			pd += ZBX_CONST_STRLEN(ZBX_DEV_PFX);
56 
57 #if DEVSTAT_USER_API_VER >= 5
58 	if (-1 == devstat_getdevs(NULL, si))
59 #else
60 	if (-1 == getdevs(si))
61 #endif
62 		return FAIL;
63 
64 	for (i = 0; i < si->dinfo->numdevs; i++)
65 	{
66 		ds = &si->dinfo->devices[i];
67 
68 		/* empty '*devname' string means adding statistics for all disks together */
69 		if ('\0' != *devname)
70 		{
71 			zbx_snprintf(dev, sizeof(dev), "%s%d", ds->device_name, ds->unit_number);
72 			if (0 != strcmp(dev, pd))
73 				continue;
74 		}
75 
76 #if DEVSTAT_USER_API_VER >= 5
77 		dstat[ZBX_DSTAT_R_OPER] += (zbx_uint64_t)ds->operations[DEVSTAT_READ];
78 		dstat[ZBX_DSTAT_W_OPER] += (zbx_uint64_t)ds->operations[DEVSTAT_WRITE];
79 		dstat[ZBX_DSTAT_R_BYTE] += (zbx_uint64_t)ds->bytes[DEVSTAT_READ];
80 		dstat[ZBX_DSTAT_W_BYTE] += (zbx_uint64_t)ds->bytes[DEVSTAT_WRITE];
81 #else
82 		dstat[ZBX_DSTAT_R_OPER] += (zbx_uint64_t)ds->num_reads;
83 		dstat[ZBX_DSTAT_W_OPER] += (zbx_uint64_t)ds->num_writes;
84 		dstat[ZBX_DSTAT_R_BYTE] += (zbx_uint64_t)ds->bytes_read;
85 		dstat[ZBX_DSTAT_W_BYTE] += (zbx_uint64_t)ds->bytes_written;
86 #endif
87 		ret = SUCCEED;
88 
89 		if ('\0' != *devname)
90 			break;
91 	}
92 
93 	return ret;
94 }
95 
vfs_dev_rw(AGENT_REQUEST * request,AGENT_RESULT * result,int rw)96 static int	vfs_dev_rw(AGENT_REQUEST *request, AGENT_RESULT *result, int rw)
97 {
98 	ZBX_SINGLE_DISKDEVICE_DATA *device;
99 	char		devname[32], *tmp;
100 	int		type, mode;
101 	zbx_uint64_t	dstats[ZBX_DSTAT_MAX];
102 	char		*pd;			/* pointer to device name without '/dev/' prefix, e.g. 'da0' */
103 
104 	if (3 < request->nparam)
105 	{
106 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Too many parameters."));
107 		return SYSINFO_RET_FAIL;
108 	}
109 
110 	tmp = get_rparam(request, 0);
111 
112 	if (NULL == tmp || 0 == strcmp(tmp, "all"))
113 		*devname = '\0';
114 	else
115 		strscpy(devname, tmp);
116 
117 	pd = devname;
118 
119 	if ('\0' != *pd)
120 	{
121 		/* skip prefix ZBX_DEV_PFX, if present */
122 		if (0 == strncmp(pd, ZBX_DEV_PFX, ZBX_CONST_STRLEN(ZBX_DEV_PFX)))
123 			pd += ZBX_CONST_STRLEN(ZBX_DEV_PFX);
124 	}
125 
126 	tmp = get_rparam(request, 1);
127 
128 	if (NULL == tmp || '\0' == *tmp || 0 == strcmp(tmp, "bps"))	/* default parameter */
129 		type = ZBX_DSTAT_TYPE_BPS;
130 	else if (0 == strcmp(tmp, "ops"))
131 		type = ZBX_DSTAT_TYPE_OPS;
132 	else if (0 == strcmp(tmp, "bytes"))
133 		type = ZBX_DSTAT_TYPE_BYTE;
134 	else if (0 == strcmp(tmp, "operations"))
135 		type = ZBX_DSTAT_TYPE_OPER;
136 	else
137 	{
138 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid second parameter."));
139 		return SYSINFO_RET_FAIL;
140 	}
141 
142 	if (type == ZBX_DSTAT_TYPE_BYTE || type == ZBX_DSTAT_TYPE_OPER)
143 	{
144 		if (2 < request->nparam)
145 		{
146 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid number of parameters."));
147 			return SYSINFO_RET_FAIL;
148 		}
149 
150 		if (FAIL == get_diskstat(pd, dstats))
151 		{
152 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot obtain disk information."));
153 			return SYSINFO_RET_FAIL;
154 		}
155 
156 		if (ZBX_DSTAT_TYPE_BYTE == type)
157 			SET_UI64_RESULT(result, dstats[(ZBX_DEV_READ == rw ? ZBX_DSTAT_R_BYTE : ZBX_DSTAT_W_BYTE)]);
158 		else	/* ZBX_DSTAT_TYPE_OPER */
159 			SET_UI64_RESULT(result, dstats[(ZBX_DEV_READ == rw ? ZBX_DSTAT_R_OPER : ZBX_DSTAT_W_OPER)]);
160 
161 		return SYSINFO_RET_OK;
162 	}
163 
164 	tmp = get_rparam(request, 2);
165 
166 	if (NULL == tmp || '\0' == *tmp || 0 == strcmp(tmp, "avg1"))	/* default parameter */
167 		mode = ZBX_AVG1;
168 	else if (0 == strcmp(tmp, "avg5"))
169 		mode = ZBX_AVG5;
170 	else if (0 == strcmp(tmp, "avg15"))
171 		mode = ZBX_AVG15;
172 	else
173 	{
174 		SET_MSG_RESULT(result, zbx_strdup(NULL, "Invalid third parameter."));
175 		return SYSINFO_RET_FAIL;
176 	}
177 
178 	if (NULL == collector)
179 	{
180 		/* CPU statistics collector and (optionally) disk statistics collector is started only when Zabbix */
181 		/* agentd is running as a daemon. When Zabbix agent or agentd is started with "-p" or "-t" parameter */
182 		/* the collectors are not available and keys "vfs.dev.read", "vfs.dev.write" with some parameters */
183 		/* (e.g. sps, ops) are not supported. */
184 
185 		SET_MSG_RESULT(result, zbx_strdup(NULL, "This item is available only in daemon mode when collectors are"
186 				" started."));
187 		return SYSINFO_RET_FAIL;
188 	}
189 
190 	if (NULL == (device = collector_diskdevice_get(pd)))
191 	{
192 		if (FAIL == get_diskstat(pd, dstats))	/* validate device name */
193 		{
194 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot obtain disk information."));
195 			return SYSINFO_RET_FAIL;
196 		}
197 
198 		if (NULL == (device = collector_diskdevice_add(pd)))
199 		{
200 			SET_MSG_RESULT(result, zbx_strdup(NULL, "Cannot add disk device to agent collector."));
201 			return SYSINFO_RET_FAIL;
202 		}
203 	}
204 
205 	if (ZBX_DSTAT_TYPE_BPS == type)	/* default parameter */
206 		SET_DBL_RESULT(result, (ZBX_DEV_READ == rw ? device->r_bps[mode] : device->w_bps[mode]));
207 	else if (ZBX_DSTAT_TYPE_OPS == type)
208 		SET_DBL_RESULT(result, (ZBX_DEV_READ == rw ? device->r_ops[mode] : device->w_ops[mode]));
209 
210 	return SYSINFO_RET_OK;
211 }
212 
VFS_DEV_READ(AGENT_REQUEST * request,AGENT_RESULT * result)213 int	VFS_DEV_READ(AGENT_REQUEST *request, AGENT_RESULT *result)
214 {
215 	return vfs_dev_rw(request, result, ZBX_DEV_READ);
216 }
217 
VFS_DEV_WRITE(AGENT_REQUEST * request,AGENT_RESULT * result)218 int	VFS_DEV_WRITE(AGENT_REQUEST *request, AGENT_RESULT *result)
219 {
220 	return vfs_dev_rw(request, result, ZBX_DEV_WRITE);
221 }
222