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