1 /* libxenstat: statistics-collection library for Xen
2  * Copyright (C) International Business Machines Corp., 2005
3  * Authors: Josh Triplett <josh@kernel.org>
4  *          Judy Fischbach <jfisch@cs.pdx.edu>
5  *          David Hendricks <cro_marmot@comcast.net>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  */
17 
18 /*
19  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
20  * Use is subject to license terms.
21  */
22 
23 #define _GNU_SOURCE
24 #include <fcntl.h>
25 #include <dirent.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <regex.h>
33 #include <xen-tools/common-macros.h>
34 
35 #include "xenstat_priv.h"
36 
37 #define SYSFS_VBD_PATH "/sys/bus/xen-backend/devices"
38 #define XENSTAT_VBD_TYPE_VBD3 3
39 
40 struct priv_data {
41 	FILE *procnetdev;
42 	DIR *sysfsvbd;
43 };
44 
45 static struct priv_data *
get_priv_data(xenstat_handle * handle)46 get_priv_data(xenstat_handle *handle)
47 {
48 	if (handle->priv != NULL)
49 		return handle->priv;
50 
51 	handle->priv = malloc(sizeof(struct priv_data));
52 	if (handle->priv == NULL)
53 		return (NULL);
54 
55 	((struct priv_data *)handle->priv)->procnetdev = NULL;
56 	((struct priv_data *)handle->priv)->sysfsvbd = NULL;
57 
58 	return handle->priv;
59 }
60 
61 /* Expected format of /proc/net/dev */
62 static const char PROCNETDEV_HEADER[] =
63     "Inter-|   Receive                                                |"
64     "  Transmit\n"
65     " face |bytes    packets errs drop fifo frame compressed multicast|"
66     "bytes    packets errs drop fifo colls carrier compressed\n";
67 
68 /* We need to get the name of the bridge interface for use with bonding interfaces */
69 /* Use excludeName parameter to avoid adding bridges we don't care about, eg. virbr0 */
getBridge(const char * excludeName,char * result,size_t resultLen)70 static void getBridge(const char *excludeName, char *result, size_t resultLen)
71 {
72 	struct dirent *de;
73 	DIR *d;
74 
75 	char tmp[512] = { 0 };
76 
77 	d = opendir("/sys/class/net");
78 	while ((de = readdir(d)) != NULL) {
79 		if ((strlen(de->d_name) > 0) && (de->d_name[0] != '.')
80 			&& (strstr(de->d_name, excludeName) == NULL)) {
81 				sprintf(tmp, "/sys/class/net/%s/bridge", de->d_name);
82 
83 				if (access(tmp, F_OK) == 0) {
84 					/*
85 					 * Do not use strncpy to prevent compiler warning with
86 					 * gcc >= 10.0
87 					 * If de->d_name is longer then resultLen we truncate it
88 					 */
89 					memset(result, 0, resultLen);
90 					memcpy(result, de->d_name, MIN(strnlen(de->d_name,
91 									NAME_MAX),resultLen - 1));
92 				}
93 		}
94 	}
95 
96 	closedir(d);
97 }
98 
99 /* parseNetLine provides regular expression based parsing for lines from /proc/net/dev, all the */
100 /* information are parsed but not all are used in our case, ie. for xenstat */
parseNetDevLine(char * line,char * iface,unsigned long long * rxBytes,unsigned long long * rxPackets,unsigned long long * rxErrs,unsigned long long * rxDrops,unsigned long long * rxFifo,unsigned long long * rxFrames,unsigned long long * rxComp,unsigned long long * rxMcast,unsigned long long * txBytes,unsigned long long * txPackets,unsigned long long * txErrs,unsigned long long * txDrops,unsigned long long * txFifo,unsigned long long * txColls,unsigned long long * txCarrier,unsigned long long * txComp)101 static int parseNetDevLine(char *line, char *iface, unsigned long long *rxBytes, unsigned long long *rxPackets,
102 		unsigned long long *rxErrs, unsigned long long *rxDrops, unsigned long long *rxFifo,
103 		unsigned long long *rxFrames, unsigned long long *rxComp, unsigned long long *rxMcast,
104 		unsigned long long *txBytes, unsigned long long *txPackets, unsigned long long *txErrs,
105 		unsigned long long *txDrops, unsigned long long *txFifo, unsigned long long *txColls,
106 		unsigned long long *txCarrier, unsigned long long *txComp)
107 {
108 	/* Temporary/helper variables */
109 	int ret;
110 	char *tmp;
111 	int i = 0, x = 0, col = 0;
112 	regex_t r;
113 	regmatch_t matches[19];
114 	int num = 19;
115 
116 	/* Regular exception to parse all the information from /proc/net/dev line */
117 	const char *regex = "([^:]*):([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)"
118 			"[ ]*([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)[ ]*"
119 			"([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)[ ]*([^ ]*)";
120 
121 	/* Initialize all variables called has passed as non-NULL to zeros */
122 	if (iface != NULL)
123 		memset(iface, 0, sizeof(*iface));
124 	if (rxBytes != NULL)
125 		*rxBytes = 0;
126 	if (rxPackets != NULL)
127 		*rxPackets = 0;
128 	if (rxErrs != NULL)
129 		*rxErrs = 0;
130 	if (rxDrops != NULL)
131 		*rxDrops = 0;
132 	if (rxFifo != NULL)
133 		*rxFifo = 0;
134 	if (rxFrames != NULL)
135 		*rxFrames = 0;
136 	if (rxPackets != NULL)
137 		*rxPackets = 0;
138 	if (rxComp != NULL)
139 		*rxComp = 0;
140 	if (txBytes != NULL)
141 		*txBytes = 0;
142 	if (txPackets != NULL)
143 		*txPackets = 0;
144 	if (txErrs != NULL)
145 		*txErrs = 0;
146 	if (txDrops != NULL)
147 		*txDrops = 0;
148 	if (txFifo != NULL)
149 		*txFifo = 0;
150 	if (txColls != NULL)
151 		*txColls = 0;
152 	if (txCarrier != NULL)
153 		*txCarrier = 0;
154 	if (txComp != NULL)
155 		*txComp = 0;
156 
157 	if ((ret = regcomp(&r, regex, REG_EXTENDED))) {
158 		regfree(&r);
159 		return ret;
160 	}
161 
162 	tmp = (char *)malloc( sizeof(char) );
163 	if (regexec (&r, line, num, matches, REG_EXTENDED) == 0){
164 		for (i = 1; i < num; i++) {
165 			/* The expression matches are empty sometimes so we need to check it first */
166 			if (matches[i].rm_eo - matches[i].rm_so > 0) {
167 				/* Col variable contains current id of non-empty match */
168 				col++;
169 				tmp = (char *)realloc(tmp, (matches[i].rm_eo -
170 							matches[i].rm_so + 1) * sizeof(char));
171 				for (x = matches[i].rm_so; x < matches[i].rm_eo; x++)
172 					tmp[x - matches[i].rm_so] = line[x];
173 				tmp[x - matches[i].rm_so] = 0;
174 
175 				/* We populate all the fields from /proc/net/dev line */
176 				if (i > 1) {
177 					unsigned long long ullTmp = strtoull(tmp, NULL, 10);
178 
179 					switch (col) {
180 						case 2: if (rxBytes != NULL)
181 								*rxBytes = ullTmp;
182 							break;
183 						case 3: if (rxPackets != NULL)
184 								*rxPackets = ullTmp;
185 							break;
186 						case 4: if (rxErrs != NULL)
187 								*rxErrs = ullTmp;
188 							break;
189 						case 5: if (rxDrops != NULL)
190 								*rxDrops = ullTmp;
191 							break;
192 						case 6: if (rxFifo != NULL)
193 								*rxFifo = ullTmp;
194 							break;
195 						case 7: if (rxFrames != NULL)
196 								*rxFrames = ullTmp;
197 							break;
198 						case 8: if (rxComp != NULL)
199 								*rxComp = ullTmp;
200 							break;
201 						case 9: if (rxMcast != NULL)
202 								*rxMcast = ullTmp;
203 							break;
204 						case 10: if (txBytes != NULL)
205 								*txBytes = ullTmp;
206 							break;
207 						case 11: if (txPackets != NULL)
208 								*txPackets = ullTmp;
209 							break;
210 						case 12: if (txErrs != NULL)
211 								*txErrs = ullTmp;
212 							break;
213 						case 13: if (txDrops != NULL)
214 								*txDrops = ullTmp;
215 							break;
216 						case 14: if (txFifo != NULL)
217 								*txFifo = ullTmp;
218 							break;
219 						case 15: if (txColls != NULL)
220 								*txColls = ullTmp;
221 							break;
222 						case 16: if (txCarrier != NULL)
223 								*txCarrier = ullTmp;
224 							break;
225 						case 17: if (txComp != NULL)
226 								*txComp = ullTmp;
227 							break;
228 					}
229 				}
230 				else if (iface != NULL) {
231 					char *tmp2 = strpbrk(tmp, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
232 					if (tmp2 != NULL)
233 						strcpy(iface, tmp2);
234 				}
235 			}
236 		}
237 	}
238 
239 	free(tmp);
240 	regfree(&r);
241 
242 	return 0;
243 }
244 
245 /* Find out the domid and network number given an interface name.
246  * Return 0 if the iface cannot be recognized as a Xen VIF. */
get_iface_domid_network(const char * iface,unsigned int * domid_p,unsigned int * netid_p)247 static int get_iface_domid_network(const char *iface, unsigned int *domid_p, unsigned int *netid_p)
248 {
249 	char nodename_path[48];
250 	FILE * nodename_file;
251 	int ret;
252 
253 	snprintf(nodename_path, 48, "/sys/class/net/%s/device/nodename", iface);
254 	nodename_file = fopen(nodename_path, "r");
255 	if (nodename_file != NULL) {
256 		ret = fscanf(nodename_file, "backend/vif/%u/%u", domid_p, netid_p);
257 		fclose(nodename_file);
258 		if (ret == 2)
259 			return 1;
260 	}
261 
262 	if (sscanf(iface, "vif%u.%u", domid_p, netid_p) == 2)
263 		return 1;
264 
265 	return 0;
266 }
267 
268 /* Collect information about networks */
xenstat_collect_networks(xenstat_node * node)269 int xenstat_collect_networks(xenstat_node * node)
270 {
271 	/* Helper variables for parseNetDevLine() function defined above */
272 	int i;
273 	char line[512] = { 0 }, iface[16] = { 0 }, devBridge[16] = { 0 }, devNoBridge[17] = { 0 };
274 	unsigned long long rxBytes, rxPackets, rxErrs, rxDrops, txBytes, txPackets, txErrs, txDrops;
275 
276 	struct priv_data *priv = get_priv_data(node->handle);
277 
278 	if (priv == NULL) {
279 		perror("Allocation error");
280 		return 0;
281 	}
282 
283 	/* Open and validate /proc/net/dev if we haven't already */
284 	if (priv->procnetdev == NULL) {
285 		char header[sizeof(PROCNETDEV_HEADER)];
286 		priv->procnetdev = fopen("/proc/net/dev", "r");
287 		if (priv->procnetdev == NULL) {
288 			perror("Error opening /proc/net/dev");
289 			return 0;
290 		}
291 
292 		/* Validate the format of /proc/net/dev */
293 		if (fread(header, sizeof(PROCNETDEV_HEADER) - 1, 1,
294 			  priv->procnetdev) != 1) {
295 			perror("Error reading /proc/net/dev header");
296 			return 0;
297 		}
298 		header[sizeof(PROCNETDEV_HEADER) - 1] = '\0';
299 		if (strcmp(header, PROCNETDEV_HEADER) != 0) {
300 			fprintf(stderr,
301 				"Unexpected /proc/net/dev format\n");
302 			return 0;
303 		}
304 	}
305 
306 	/* Fill in networks */
307 	/* FIXME: optimize this */
308 	fseek(priv->procnetdev, sizeof(PROCNETDEV_HEADER) - 1,
309 	      SEEK_SET);
310 
311 	/* We get the bridge devices for use with bonding interface to get bonding interface stats */
312 	getBridge("vir", devBridge, sizeof(devBridge));
313 	snprintf(devNoBridge, sizeof(devNoBridge), "p%s", devBridge);
314 
315 	while (fgets(line, 512, priv->procnetdev)) {
316 		xenstat_domain *domain;
317 		xenstat_network net;
318 		unsigned int domid;
319 
320 		parseNetDevLine(line, iface, &rxBytes, &rxPackets, &rxErrs, &rxDrops, NULL, NULL, NULL,
321 				NULL, &txBytes, &txPackets, &txErrs, &txDrops, NULL, NULL, NULL, NULL);
322 
323 		/* If the device parsed is network bridge and both tx & rx packets are zero, we are most */
324 		/* likely using bonding so we alter the configuration for dom0 to have bridge stats */
325 		if ((strstr(iface, devBridge) != NULL) &&
326 		    (strstr(iface, devNoBridge) == NULL) &&
327 		    ((domain = xenstat_node_domain(node, 0)) != NULL)) {
328 			for (i = 0; i < domain->num_networks; i++) {
329 				if ((domain->networks[i].id != 0) ||
330 				    (domain->networks[i].tbytes != 0) ||
331 				    (domain->networks[i].rbytes != 0))
332 					continue;
333 				domain->networks[i].tbytes = txBytes;
334 				domain->networks[i].tpackets = txPackets;
335 				domain->networks[i].terrs = txErrs;
336 				domain->networks[i].tdrop = txDrops;
337 				domain->networks[i].rbytes = rxBytes;
338 				domain->networks[i].rpackets = rxPackets;
339 				domain->networks[i].rerrs = rxErrs;
340 				domain->networks[i].rdrop = rxDrops;
341 			}
342 		}
343 		else /* Otherwise we need to preserve old behaviour */
344 		if (get_iface_domid_network(iface, &domid, &net.id)) {
345 
346 			net.tbytes = txBytes;
347 			net.tpackets = txPackets;
348 			net.terrs = txErrs;
349 			net.tdrop = txDrops;
350 			net.rbytes = rxBytes;
351 			net.rpackets = rxPackets;
352 			net.rerrs = rxErrs;
353 			net.rdrop = rxDrops;
354 
355 		/* FIXME: this does a search for the domid */
356 		  domain = xenstat_node_domain(node, domid);
357 		  if (domain == NULL) {
358 			fprintf(stderr,
359 				"Found interface vif%u.%u but domain %u"
360 				" does not exist.\n", domid, net.id,
361 				domid);
362 			continue;
363 		  }
364 		  if (domain->networks == NULL) {
365 			domain->num_networks = 1;
366 			domain->networks = malloc(sizeof(xenstat_network));
367 		  } else {
368 			struct xenstat_network *tmp;
369 			domain->num_networks++;
370 			tmp = realloc(domain->networks,
371 				      domain->num_networks *
372 				      sizeof(xenstat_network));
373 			if (tmp == NULL)
374 				free(domain->networks);
375 			domain->networks = tmp;
376 		  }
377 		  if (domain->networks == NULL)
378 			return 0;
379 		  domain->networks[domain->num_networks - 1] = net;
380           }
381         }
382 
383 	return 1;
384 }
385 
386 /* Free network information in handle */
xenstat_uninit_networks(xenstat_handle * handle)387 void xenstat_uninit_networks(xenstat_handle * handle)
388 {
389 	struct priv_data *priv = get_priv_data(handle);
390 	if (priv != NULL && priv->procnetdev != NULL)
391 		fclose(priv->procnetdev);
392 }
393 
read_attributes_vbd3(const char * vbd3_path,xenstat_vbd * vbd)394 static int read_attributes_vbd3(const char *vbd3_path, xenstat_vbd *vbd)
395 {
396 	FILE *fp;
397 	struct vbd3_stats vbd3_stats;
398 
399 	fp = fopen(vbd3_path, "rb");
400 
401 	if (fp == NULL) {
402 		return -1;
403 	}
404 
405 	if (fread(&vbd3_stats, sizeof(struct vbd3_stats), 1, fp) != 1) {
406 		fclose(fp);
407 		return -1;
408 	}
409 
410 	if (vbd3_stats.version != 1) {
411 		fclose(fp);
412 		return -1;
413 	}
414 
415 	vbd->oo_reqs = vbd3_stats.oo_reqs;
416 	vbd->rd_reqs = vbd3_stats.read_reqs_submitted;
417 	vbd->rd_sects = vbd3_stats.read_sectors;
418 	vbd->wr_reqs = vbd3_stats.write_reqs_submitted;
419 	vbd->wr_sects = vbd3_stats.write_sectors;
420 
421 	fclose(fp);
422 
423 	return 0;
424 }
425 
read_attributes_vbd(const char * vbd_directory,const char * what,char * ret,int cap)426 static int read_attributes_vbd(const char *vbd_directory, const char *what, char *ret, int cap)
427 {
428 	static char file_name[80];
429 	int fd, num_read;
430 
431 	snprintf(file_name, sizeof(file_name), "%s/%s/%s",
432 		SYSFS_VBD_PATH, vbd_directory, what);
433 	fd = open(file_name, O_RDONLY, 0);
434 	if (fd==-1) return -1;
435 	num_read = read(fd, ret, cap - 1);
436 	close(fd);
437 	if (num_read<=0) return -1;
438 	ret[num_read] = '\0';
439 	return num_read;
440 }
441 
442 /* Collect information about VBDs */
xenstat_collect_vbds(xenstat_node * node)443 int xenstat_collect_vbds(xenstat_node * node)
444 {
445 	struct dirent *dp;
446 	struct priv_data *priv = get_priv_data(node->handle);
447 
448 	if (priv == NULL) {
449 		perror("Allocation error");
450 		return 0;
451 	}
452 
453 	if (priv->sysfsvbd == NULL) {
454 		priv->sysfsvbd = opendir(SYSFS_VBD_PATH);
455 		if (priv->sysfsvbd == NULL) {
456 			perror("Error opening " SYSFS_VBD_PATH);
457 			return 0;
458 		}
459 	}
460 
461 	/* Get qdisk statistics */
462 	read_attributes_qdisk(node);
463 
464 	rewinddir(priv->sysfsvbd);
465 
466 	for(dp = readdir(priv->sysfsvbd); dp != NULL ;
467 	    dp = readdir(priv->sysfsvbd)) {
468 		xenstat_domain *domain;
469 		xenstat_vbd vbd;
470 		unsigned int domid;
471 		int ret;
472 		char buf[256];
473 
474 		ret = sscanf(dp->d_name, "%255[^-]-%u-%u", buf, &domid, &vbd.dev);
475 		if (ret != 3)
476 			continue;
477 		if (!(strstr(buf, "vbd")) && !(strstr(buf, "tap")))
478 			continue;
479 
480 		if (strcmp(buf,"vbd") == 0)
481 			vbd.back_type = 1;
482 		else if (strcmp(buf,"tap") == 0)
483 			vbd.back_type = 2;
484 		else if (strcmp(buf,"vbd3") == 0)
485 			vbd.back_type = XENSTAT_VBD_TYPE_VBD3;
486 		else
487 			vbd.back_type = 0;
488 
489 		domain = xenstat_node_domain(node, domid);
490 		if (domain == NULL) {
491 			fprintf(stderr,
492 				"Found interface %s-%u-%u but domain %u"
493 				" does not exist.\n",
494 				buf, domid, vbd.dev, domid);
495 			continue;
496 		}
497 
498 		if (vbd.back_type == 1 || vbd.back_type == 2)
499 		{
500 
501 			vbd.error = 0;
502 
503 			if ((read_attributes_vbd(dp->d_name, "statistics/oo_req", buf, 256)<=0) ||
504 				((ret = sscanf(buf, "%llu", &vbd.oo_reqs)) != 1) ||
505 				(read_attributes_vbd(dp->d_name, "statistics/rd_req", buf, 256)<=0) ||
506 				((ret = sscanf(buf, "%llu", &vbd.rd_reqs)) != 1) ||
507 				(read_attributes_vbd(dp->d_name, "statistics/wr_req", buf, 256)<=0) ||
508 				((ret = sscanf(buf, "%llu", &vbd.wr_reqs)) != 1) ||
509 				(read_attributes_vbd(dp->d_name, "statistics/rd_sect", buf, 256)<=0) ||
510 				((ret = sscanf(buf, "%llu", &vbd.rd_sects)) != 1) ||
511 				(read_attributes_vbd(dp->d_name, "statistics/wr_sect", buf, 256)<=0) ||
512 				((ret = sscanf(buf, "%llu", &vbd.wr_sects)) != 1))
513 			{
514 				vbd.error = 1;
515 			}
516 		}
517 		else if (vbd.back_type == XENSTAT_VBD_TYPE_VBD3)
518 		{
519 			char *td3_pid;
520 			char *path;
521 
522 			vbd.error = 0;
523 
524 			if (asprintf(&path, "/local/domain/0/backend/vbd3/%u/%u/kthread-pid", domid, vbd.dev) < 0)
525 				continue;
526 
527 			td3_pid = xs_read(node->handle->xshandle, XBT_NULL, path, NULL);
528 
529 			free(path);
530 
531 			if (td3_pid == NULL)
532 				continue;
533 
534 			if (asprintf(&path, "/dev/shm/td3-%s/vbd-%u-%u", td3_pid, domid, vbd.dev) < 0) {
535 				free(td3_pid);
536 				continue;
537 			}
538 
539 			if (read_attributes_vbd3(path, &vbd) < 0)
540 				vbd.error = 1;
541 
542 			free(td3_pid);
543 			free(path);
544 		}
545 		else
546 		{
547 			vbd.error = 1;
548 		}
549 		if ((xenstat_save_vbd(domain, &vbd)) == NULL) {
550 			perror("Allocation error");
551 			return 0;
552 		}
553 	}
554 
555 	return 1;
556 }
557 
558 /* Free VBD information in handle */
xenstat_uninit_vbds(xenstat_handle * handle)559 void xenstat_uninit_vbds(xenstat_handle * handle)
560 {
561 	struct priv_data *priv = get_priv_data(handle);
562 	if (priv != NULL && priv->sysfsvbd != NULL)
563 		closedir(priv->sysfsvbd);
564 }
565