/*- * Copyright (c) 2001, 2003 Lev Walkin . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: ifst_linux.c,v 1.62 2005/06/05 21:40:59 vlm Exp $ */ #include "ipcad.h" #include "disp.h" #include "sf_lite.h" #include "opt.h" #include #include #include #include #include #include #include #include #include #include #include #include static int skfd = -1; struct l_ifstat { /* Receive */ unsigned long ibytes; unsigned long ipackets; unsigned long ierrs; unsigned long idrop; unsigned long ififo; unsigned long iframe; unsigned long icompr; unsigned long imcast; /* Transmit */ unsigned long obytes; unsigned long opackets; unsigned long oerrs; unsigned long odrop; unsigned long ofifo; unsigned long ocolls; unsigned long ocarrier; unsigned long ocompr; }; static struct l_ifstat rt_pos; /* Field positions for interface counters */ static int rt_pos_initialized; /* Field positions initialized */ static FILE *pnd = NULL; /* Pre-opened /proc/net/dev */ static int uptime_fd = -1; static pthread_mutex_t uptime_mutex; int status(FILE *f, char *ifname); char * get_encaps(int encaps) { static char buf[32]; switch(encaps) { case ARPHRD_ETHER: return "Ethernet"; case ARPHRD_PPP: return "PPP"; case ARPHRD_SLIP: return "SLIP"; case ARPHRD_CSLIP: return "CSLIP"; case ARPHRD_LOOPBACK: return "Loopback"; case ARPHRD_HDLC: return "HDLC"; } snprintf(buf, sizeof(buf), "Unknown/%d", encaps); return buf; } void ether_status(FILE *f, struct sockaddr *sa) { fprintf(f, " Hardware is %s, ", get_encaps(sa->sa_family)); #define EA(foo) ((unsigned char)*((unsigned char *)sa->sa_data + (foo))) fprintf(f, "address is %02x%02x.%02x%02x.%02x%02x", EA(0), EA(1), EA(2), EA(3), EA(4), EA(5) ); #ifdef ENABLE_BIA_OUTPUT fprintf(f, " (bia %02x%02x.%02x%02x.%02x%02x", EA(0), EA(1), EA(2), EA(3), EA(4), EA(5) ); #endif fprintf(f, "\n"); } int if_stat(FILE *f, char *ifname) { struct ifreq ifr; int encaps = -1; if(skfd == -1) { skfd = socket(AF_INET, SOCK_DGRAM, 0); if(skfd == -1) { fprintf(f, "System error\n"); return -1; } } if(strlen(ifname) >= sizeof(ifr.ifr_name)) { fprintf(f, "No such interface %s\n", ifname); return -1; } strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); if(ioctl(skfd, SIOCGIFNAME, &ifr) == 0) { fprintf(f, "No such interface %s\n", ifname); return -1; } else { ifname = ifr.ifr_name; } if(ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) { fprintf(f, "No such interface %s\n", ifname); return -1; } fprintf(f, "%s is %s, line protocol is %s\n", ifname, ((ifr.ifr_flags & IFF_RUNNING) ? "up" : "down"), ((ifr.ifr_flags & IFF_UP) ? "up" : "down") ); if(ioctl(skfd, SIOCGIFHWADDR, &ifr) == 0) { encaps = ifr.ifr_hwaddr.sa_family; if(encaps == ARPHRD_ETHER) { ether_status(f, &ifr.ifr_hwaddr); }; } ifr.ifr_addr.sa_family = AF_INET; if(ioctl(skfd, SIOCGIFADDR, &ifr) == 0) { fprintf(f, " Internet address is "); print_ip(f, ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr); if(ioctl(skfd, SIOCGIFNETMASK, &ifr) == 0) { fprintf(f, " "); print_ip(f, ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr); } fprintf(f, "\n"); if(ioctl(skfd, SIOCGIFDSTADDR, &ifr) == 0) { fprintf(f, " Peer IP address is "); print_ip(f, ((struct sockaddr_in *)&ifr.ifr_dstaddr)->sin_addr); fprintf(f, "\n"); } if(ioctl(skfd, SIOCGIFBRDADDR, &ifr) == 0) { fprintf(f, " IP broadcast address is "); print_ip(f, ((struct sockaddr_in *)&ifr.ifr_broadaddr)->sin_addr); fprintf(f, "\n"); } } fprintf(f, " Encapsulation %s, looback %s\n", get_encaps(encaps), ((ifr.ifr_flags & IFF_LOOPBACK) ? "set": "not set") ); if(ioctl(skfd, SIOCGIFMTU, &ifr) == 0) fprintf(f, " MTU %u\n", ifr.ifr_mtu); display_internal_averages(f, ifname); status(f, ifname); return 0; } static int fill_rt_pos(char *buf) { svect *inio; /* iface[0]|receive[1]|transmit[2] */ svect *inpsl; /* bytes packets ... */ svect *outsl; /* bytes packets ... */ int inindex; /* shift in words from the start */ int outindex; /* shift in words from the start */ /* Split iface|receive|transmit */ inio = split(buf, "|", 0); if(!inio || inio->count != 3) { if(inio) sfree(inio); return -1; } /* Count initial iface[0] shifting */ inpsl = split(inio->list[0], "|: \t\n", 0); if(!inpsl || !inpsl->count) { sfree(inio); if(inpsl) sfree(inpsl); return -1; } /* Shift */ inindex = outindex = inpsl->count; sfree(inpsl); /* Receive counters */ inpsl = split(inio->list[1], "|: \t\n", 0); if(!inpsl || !inpsl->count) { sfree(inio); if(inpsl) sfree(inpsl); return -1; } /* Transmit counters */ outsl = split(inio->list[2], "|: \t\n", 0); if(!outsl || !outsl->count) { sfree(inio); sfree(inpsl); if(outsl) sfree(outsl); return -1; } /* Adjust transmit counters index */ outindex += outsl->count; /* Receive */ rt_pos.ibytes = inindex + sfind(inpsl, "bytes"); if(rt_pos.ibytes == (unsigned int)-1) { return -1; } rt_pos.ipackets = inindex + sfind(inpsl, "packets"); rt_pos.ierrs = inindex + sfind(inpsl, "errs"); rt_pos.idrop = inindex + sfind(inpsl, "drop"); rt_pos.ififo = inindex + sfind(inpsl, "fifo"); rt_pos.iframe = inindex + sfind(inpsl, "frame"); rt_pos.icompr = inindex + sfind(inpsl, "compressed"); rt_pos.imcast = inindex + sfind(inpsl, "multicast"); /* Transmit */ rt_pos.obytes = outindex + sfind(outsl, "bytes"); rt_pos.opackets = outindex + sfind(outsl, "packets"); rt_pos.oerrs = outindex + sfind(outsl, "errs"); rt_pos.odrop = outindex + sfind(outsl, "drop"); rt_pos.ofifo = outindex + sfind(outsl, "fifo"); rt_pos.ocolls = outindex + sfind(outsl, "colls"); rt_pos.ocarrier = outindex + sfind(outsl, "carrier"); rt_pos.ocompr = outindex + sfind(outsl, "compressed"); sfree(inio); sfree(inpsl); sfree(outsl); return 0; } /* Get status from the string */ struct l_ifstat * get_ifstat(svect *sl) { struct l_ifstat *nif; #define FIF(foo) nif->foo = (((signed long)rt_pos.foo) > 0)?strtoul(sl->list[rt_pos.foo], NULL, 10):0L nif = (struct l_ifstat *)calloc(1, sizeof(struct l_ifstat)); if(!nif) return NULL; /* Receive */ FIF(ibytes); FIF(ipackets); FIF(ierrs); FIF(idrop); FIF(ififo); FIF(iframe); FIF(icompr); FIF(imcast); /* Transmit */ FIF(obytes); FIF(opackets); FIF(oerrs); FIF(odrop); FIF(ofifo); FIF(ocolls); FIF(ocarrier); FIF(ocompr); return nif; }; void display_ifstat(FILE *f, struct l_ifstat *ifs) { fprintf(f, " %lu packets input, %lu bytes, %lu no buffer\n", ifs->ipackets, ifs->ibytes, ifs->idrop); fprintf(f, " %lu input errors, %lu CRC, %lu frame, %lu overrun, %lu ignored\n", ifs->ierrs, 0L, ifs->iframe, 0L, ifs->ierrs); fprintf(f, " %lu packets output, %lu bytes, %lu underruns\n", ifs->opackets, ifs->obytes, 0L); fprintf(f, " %lu output errors, %lu collisions, %lu interface resets\n", ifs->oerrs, ifs->ocolls, ifs->ocarrier); fprintf(f, " %lu output drops\n", ifs->odrop); return; }; int status(FILE *f, char *ifname) { char buf[1024]; svect *sl; struct l_ifstat *lfs; /* Lock all */ pthread_mutex_lock(&pndev_lock); if(!pnd) { if(ifst_preopen() == -1) { fprintf(f, " Verbose statistics unavailable: indefined source\n"); goto finish; } } else { fseek(pnd, 0L, 0); } /* * Eat two header lines. */ fgets(buf, sizeof(buf), pnd); /* Eat first header line */ if(fgets(buf, sizeof(buf), pnd) == NULL) { fprintf(f, " Verbose statistics unavailable: " "invalid format\n"); goto finish; } if(!rt_pos_initialized) { if(fill_rt_pos(buf)) { fprintf(f, " Verbose statistics unavailable: invalid format\n"); goto finish; } else { rt_pos_initialized = 1; } } sl = sinit(); if(!sl) { fprintf(f, " Verbose statistics unavailable: resource shortage\n"); goto finish; } while(fgets(buf, sizeof(buf), pnd) != NULL) { sclear(sl); if(splitf(sl, buf, "|: \t", 0) < 2) continue; if(strcmp(sl->list[0], ifname)) continue; lfs = get_ifstat(sl); sfree(sl); if(lfs) { display_ifstat(f, lfs); } else { fprintf(f, " Verbose statistics unavailable: resource shortage\n"); goto finish; } pthread_mutex_unlock(&pndev_lock); return 0; } sfree(sl); finish: pthread_mutex_unlock(&pndev_lock); return -1; } #ifndef IPCAD_IFLIST_USE_GETIFADDRS svect * get_interface_names() { char buf[1024]; svect *sl; if(!pnd) { errno = ESRCH; return NULL; } pthread_mutex_lock(&pndev_lock); fseek(pnd, 0L, 0); fgets(buf, sizeof(buf), pnd); /* Eat first header line */ if(fgets(buf, sizeof(buf), pnd) == NULL) { pthread_mutex_unlock(&pndev_lock); return NULL; } sl = sinit(); while(fgets(buf, sizeof(buf), pnd)) { char *ifname, *p; for(ifname = buf; *ifname && *ifname == ' '; ifname++); for(p = ifname; *p; p++) { switch(*p) { default: continue; case ':': case ' ': case '\t': case '|': if(sadd2(sl, ifname, p - ifname) == -1) { sfree(sl); sl = NULL; break; } } break; } } pthread_mutex_unlock(&pndev_lock); return sl; } #endif /* IPCAD_IFLIST_USE_GETIFADDRS */ #ifndef _PATH_PROCNET_DEV #define _PATH_PROCNET_DEV "/proc/net/dev" #endif #ifndef _PATH_PROC_UPTIME #define _PATH_PROC_UPTIME "/proc/uptime" #endif int ifst_preopen() { uptime_fd = open(_PATH_PROC_UPTIME, O_RDONLY); pnd = fopen(_PATH_PROCNET_DEV, "r"); if(pnd) setbuf(pnd, NULL); else return -1; return 0; } static double system_uptime_d() { char buf[256]; double uptime; if(uptime_fd == -1) return 0.0; pthread_mutex_lock(&uptime_mutex); if(lseek(uptime_fd, 0L, SEEK_SET) == 0 && read(uptime_fd, buf, sizeof(buf)) > 0) { buf[sizeof(buf) - 1] = '\0'; /* Just in case */ errno = 0; uptime = (time_t)strtod(buf, NULL); if(errno) uptime = 0.0; } else { uptime = 0.0; } pthread_mutex_unlock(&uptime_mutex); return uptime; } #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 256 #endif void system_uptime(FILE *f) { char buf[MAXHOSTNAMELEN]; double uptime; uptime = system_uptime_d(); if(uptime <= 0.0) return; gethostname(buf, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; fprintf(f, "%s uptime is", buf); display_uptime(f, uptime); }