/* $Id$ */ /* Copyright (c) 2005-2022 Pierre Pronchery */ /* This file is part of DeforaOS Network Probe */ /* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* TODO: * - check ifinfo code (and thus volinfo) * - free memory allocated * - kvm_getprocs() may be more portable for *BSD */ #include #include #include #include #include #include #include #include #include "../data/Probe.h" #include "../config.h" #ifndef APPSERVER_PROBE_NAME # define APPSERVER_PROBE_NAME PACKAGE #endif #ifndef PROGNAME_PROBE # define PROGNAME_PROBE PACKAGE #endif #if defined(__linux__) # define _sysinfo_linux _sysinfo # define _userinfo_utmpx _userinfo # define _ifinfo_linux _ifinfo # define _volinfo_mtab _volinfo #elif defined(__FreeBSD__) # define _sysinfo_generic _sysinfo # define _userinfo_utmpx _userinfo # define _ifinfo_bsd _ifinfo # define _volinfo_statfs _volinfo #elif defined(__NetBSD__) # define _sysinfo_generic _sysinfo # define _userinfo_utmpx _userinfo # define _ifinfo_bsd _ifinfo # define _volinfo_statvfs _volinfo #else # define _sysinfo_generic _sysinfo # define _userinfo_utmpx _userinfo # define _ifinfo_generic _ifinfo # define _volinfo_generic _volinfo #endif #define PROBE_REFRESH 10 /* functions */ static int _probe_error(int ret); static int _probe_perror(char const * message, int ret); /* sysinfo */ #if defined(_sysinfo_linux) # include # define _sysinfo sysinfo #endif /* defined(_sysinfo_linux) */ #if defined(_sysinfo_generic) # if defined(__FreeBSD__) # define _sysinfo_uptime_generic _sysinfo_uptime # define _sysinfo_loads_generic _sysinfo_loads # define _sysinfo_ram_generic _sysinfo_ram # define _sysinfo_procs_generic _sysinfo_procs # elif defined(__NetBSD__) # define _sysinfo_uptime_sysctl _sysinfo_uptime # define _sysinfo_loads_sysctl _sysinfo_loads # define _sysinfo_ram_sysctl_uvm _sysinfo_ram # define _sysinfo_procs_sysctl _sysinfo_procs # else # define _sysinfo_uptime_generic _sysinfo_uptime # define _sysinfo_loads_generic _sysinfo_loads # define _sysinfo_ram_generic _sysinfo_ram # define _sysinfo_procs_generic _sysinfo_procs # endif struct sysinfo { long uptime; unsigned long loads[3]; unsigned long totalram; unsigned long freeram; unsigned long sharedram; unsigned long bufferram; unsigned long totalswap; unsigned long freeswap; unsigned short procs; }; static int _sysinfo_uptime(struct sysinfo * info); static int _sysinfo_loads(struct sysinfo * info); static int _sysinfo_ram(struct sysinfo * info); static int _sysinfo_procs(struct sysinfo * info); static int _sysinfo_generic(struct sysinfo * info) { int ret = 0; ret |= _sysinfo_uptime(info); ret |= _sysinfo_loads(info); ret |= _sysinfo_ram(info); ret |= _sysinfo_procs(info); return ret; } /* sysinfo sysctl */ # if defined(_sysinfo_uptime_sysctl) \ || defined(_sysinfo_loads_sysctl) \ || defined(_sysinfo_ram_sysctl_uvm) \ || defined(_sysinfo_procs_sysctl) # include # include # endif # if defined(_sysinfo_uptime_sysctl) static int _sysinfo_uptime_sysctl(struct sysinfo * info) { int mib[2]; struct timeval now; struct timeval tv; size_t len = sizeof(tv); mib[0] = CTL_KERN; mib[1] = KERN_BOOTTIME; if(gettimeofday(&now, NULL) != 0 || sysctl(mib, 2, &tv, &len, NULL, 0) == -1) { info->uptime = 0; return 1; } info->uptime = now.tv_sec - tv.tv_sec; return 0; } # endif # if defined(_sysinfo_loads_sysctl) # include static int _sysinfo_loads_sysctl(struct sysinfo * info) { int mib[2]; struct loadavg la; size_t len = sizeof(la); mib[0] = CTL_VM; mib[1] = VM_LOADAVG; if(sysctl(mib, 2, &la, &len, NULL, 0) == -1) { memset(info->loads, 0, sizeof(info->loads)); return 1; } info->loads[0] = la.ldavg[0] * la.fscale / 64; info->loads[1] = la.ldavg[1] * la.fscale / 64; info->loads[2] = la.ldavg[2] * la.fscale / 64; return 0; } # endif # if defined(_sysinfo_ram_sysctl_uvm) # include static int _sysinfo_ram_sysctl_uvm(struct sysinfo * info) { int mib[2]; struct uvmexp ue; size_t len = sizeof(ue); mib[0] = CTL_VM; mib[1] = VM_UVMEXP; if(sysctl(mib, 2, &ue, &len, NULL, 0) == -1) { memset(info, 0, sizeof(*info)); return 1; } info->totalram = ue.pagesize * ue.npages; info->freeram = ue.pagesize * ue.free; info->sharedram = ue.pagesize * ue.execpages; info->bufferram = ue.pagesize * ue.filepages; info->totalswap = ue.pagesize * ue.swpages; info->freeswap = info->totalswap - (ue.pagesize * ue.swpgonly); return 0; } # endif # if defined(_sysinfo_procs_sysctl) static int _sysinfo_procs_sysctl(struct sysinfo * info) { int mib[3]; size_t len = 0; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_ALL; if(sysctl(mib, 3, NULL, &len, NULL, 0) == -1) { info->procs = 0; return 1; } info->procs = len / sizeof(struct kinfo_proc); return 0; } # endif /* sysinfo generic */ # if defined(_sysinfo_uptime_generic) # warning Generic uptime reporting is not supported static int _sysinfo_uptime_generic(struct sysinfo * info) { info->uptime = 0; return 0; } # endif # if defined(_sysinfo_loads_generic) static int _sysinfo_loads_generic(struct sysinfo * info) { double la[3]; if(getloadavg(la, 3) != 3) { memset(info->loads, 0, sizeof(info->loads)); return 1; } info->loads[0] = la[0]; info->loads[1] = la[1]; info->loads[2] = la[2]; return 0; } # endif # if defined(_sysinfo_ram_generic) # warning Generic RAM reporting is not supported static int _sysinfo_ram_generic(struct sysinfo * info) { info->totalram = 0; info->freeram = 0; info->sharedram = 0; info->bufferram = 0; info->totalswap = 0; info->freeswap = 0; return 0; } # endif # if defined(_sysinfo_procs_generic) # include static int _sysinfo_procs_generic(struct sysinfo * info) { DIR * dir; struct dirent * de; long l; info->procs = 0; if((dir = opendir("/proc")) == NULL) return _probe_perror("/proc", 0); while((de = readdir(dir)) != NULL) { # ifdef DT_DIR if(de->d_type != DT_DIR) continue; # endif if((l = strtol(de->d_name, NULL, 10)) <= 0) continue; info->procs++; } closedir(dir); return 0; } # endif #endif /* defined(_sysinfo_generic) */ /* userinfo */ #if defined(_userinfo_none) static int _userinfo_none(unsigned int * userinfo) { *userinfo = 0; return 0; } #elif defined(_userinfo_utmpx) # include static int _userinfo_utmpx(unsigned int * userinfo) { struct utmpx * ut; for(*userinfo = 0; (ut = getutxent()) != NULL;) if(ut->ut_type == USER_PROCESS) (*userinfo)++; endutxent(); return 0; } #endif /* ifinfo */ struct ifinfo { char name[6]; unsigned int ibytes; unsigned int obytes; }; /* ifinfo linux */ #if defined(_ifinfo_linux) enum InterfaceInfo { IF_RX_BYTES = 0, IF_RX_PACKETS, IF_RX_ERRS, IF_RX_DROP, IF_RX_FIFO, IF_RX_FRAME, IF_RX_COMPRESSED, IF_RX_MULTICAST, IF_TX_BYTES, IF_TX_PACKETS, IF_TX_ERRS, IF_TX_DROP, IF_TX_FIFO, IF_TX_FRAME, IF_TX_COMPRESSED }; # define IF_LAST IF_TX_COMPRESSED static int _ifinfo_linux_append(struct ifinfo ** dev, char * buf, int nb); static int _ifinfo_linux(struct ifinfo ** dev) { int ret = 0; FILE * fp; char buf[200]; int i; int len; if((fp = fopen("/proc/net/dev", "r")) == NULL) return -1; for(i = 0; fgets(buf, sizeof(buf), fp) != NULL; i++) { len = string_get_length(buf); if(buf[len - 1] != '\n') { ret = -1; break; } if(i < 2) continue; if(_ifinfo_linux_append(dev, buf, i - 2) != 0) { ret = -1; break; } ret++; } fclose(fp); return ret; } static int _ifinfo_linux_append(struct ifinfo ** dev, char * buf, int nb) { struct ifinfo * p; size_t i; char * q; int j = 0; if((p = realloc(*dev, sizeof(*p) * (nb + 1))) == NULL) return _probe_perror(NULL, 1); *dev = p; for(i = 0; i < sizeof(p->name) && buf[i] != '\0'; i++); if(i != sizeof(p->name)) return 1; buf[sizeof(p->name)] = '\0'; for(q = buf; q[0] == ' '; q++); strcpy(p[nb].name, q); # if defined(DEBUG) fprintf(stderr, "_ifinfo_append: %s\n", p[nb].name); # endif for(i++; buf[i] != '\0'; i++) { if(j > IF_LAST) break; if(buf[i] == ' ') continue; q = &buf[i]; for(; buf[i] >= '0' && buf[i] <= '9'; i++); buf[i] = '\0'; switch(j++) { case IF_RX_BYTES: (*dev)[nb].ibytes = strtoll(q, &q, 10); break; case IF_TX_BYTES: (*dev)[nb].obytes = strtoll(q, &q, 10); break; default: continue; } if(*q != '\0') return 1; } return 0; } #endif /* defined(_ifinfo_linux) */ /* ifinfo netbsd */ #if defined(_ifinfo_bsd) # include # include # include # include static int _ifinfo_bsd_append(struct ifinfo ** dev, char * ifname, int fd, int nb); static int _ifinfo_bsd(struct ifinfo ** dev) { int ret = 0; static int fd = -1; struct ifaddrs * ifa; struct ifaddrs * p; if(fd < 0 && (fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return _probe_perror("socket", -1); if(getifaddrs(&ifa) != 0) return _probe_perror("getifaddrs", -1); for(p = ifa; p != NULL; p = p->ifa_next) { if(p->ifa_addr->sa_family != AF_LINK) continue; if(_ifinfo_bsd_append(dev, p->ifa_name, fd, ret++) == 0) continue; ret = -1; break; } freeifaddrs(ifa); return ret; } static int _ifinfo_bsd_append(struct ifinfo ** dev, char * ifname, int fd, int nb) { struct ifdatareq ifdr; struct ifinfo * p; strcpy(ifdr.ifdr_name, ifname); if(ioctl(fd, SIOCGIFDATA, &ifdr) == -1) return _probe_perror("SIOCGIFDATA", 1); if((p = realloc(*dev, sizeof(*p) * (nb + 1))) == NULL) return _probe_perror(NULL, 1); *dev = p; strcpy(p[nb].name, ifname); # if defined(DEBUG) fprintf(stderr, "_ifinfo_append: %s\n", p[nb].name); # endif p[nb].ibytes = ifdr.ifdr_data.ifi_ibytes; p[nb].obytes = ifdr.ifdr_data.ifi_obytes; return 0; } #endif /* defined(_ifinfo_bsd) */ /* ifinfo generic */ #if defined(_ifinfo_generic) # warning Generic interface reporting is not supported static int _ifinfo_generic(struct ifinfo ** dev) { *dev = NULL; return 0; } #endif /* defined(_ifinfo_generic) */ /* volinfo */ struct volinfo { char name[256]; unsigned long block_size; unsigned long total; unsigned long free; }; /* volinfo linux */ #if defined(_volinfo_mtab) # include enum VolInfo { VI_DEVICE = 0, VI_MOUNTPOINT, VI_FS, VI_OPTIONS, VI_DUMP, VI_PASS }; #define VI_LAST VI_PASS static int _volinfo_mtab_append(struct volinfo ** dev, char * buf, int nb); static int _volinfo_mtab(struct volinfo ** dev) { int ret = 0; FILE * fp; char buf[200]; int i; int len; if((fp = fopen("/etc/mtab", "r")) == NULL) return _probe_perror("/etc/mtab", -1); for(i = 0; fgets(buf, sizeof(buf), fp) != NULL; i++) { len = string_get_length(buf); if(buf[len-1] != '\n') { ret = -1; break; } if(_volinfo_mtab_append(dev, buf, i) != 0) { ret = -1; break; } ret++; } fclose(fp); return ret; } static int _volinfo_mtab_append(struct volinfo ** dev, char * buf, int nb) { unsigned int i; unsigned int j; struct volinfo * p; struct statvfs sv; for(i = 0; buf[i] != '\0' && buf[i] != ' '; i++); if(buf[i] == '\0') return 1; for(j = ++i; buf[j] != '\0' && buf[j] != ' '; j++); if(buf[j] == '\0') return 1; if((p = realloc(*dev, sizeof(struct volinfo) * (nb + 1))) == NULL) return 1; *dev = p; memset(&p[nb], 0, sizeof(struct volinfo)); if(j-i >= sizeof(p[nb].name)-1) return 1; strncpy(p[nb].name, &buf[i], j-i); p[nb].name[j-i] = '\0'; # if defined(DEBUG) fprintf(stderr, "_volinfo_append: %s\n", p[nb].name); # endif if(statvfs(p[nb].name, &sv) != 0) return 1; p[nb].block_size = sv.f_bsize; p[nb].total = sv.f_blocks; p[nb].free = sv.f_bavail; return 0; } #endif /* defined(_volinfo_mtab) */ /* volinfo_statfs */ #if defined(_volinfo_statfs) # include # include static int _volinfo_statfs_append(struct volinfo ** dev, struct statfs * buf, int nb); static int _volinfo_statfs(struct volinfo ** dev) { struct statfs buf; if(statfs("/", &buf) != 0) return _probe_perror("statfs", -1); return _volinfo_statfs_append(dev, &buf, 0); } static int _volinfo_statfs_append(struct volinfo ** dev, struct statfs * buf, int nb) { struct volinfo * p; if((p = realloc(*dev, sizeof(*p) * (nb + 1))) == NULL) return _probe_perror(NULL, 1); *dev = p; strcpy(p[nb].name, buf->f_mntonname); # if defined(DEBUG) fprintf(stderr, "_volinfo_append: %s\n", p[nb].name); # endif p[nb].block_size = buf->f_bsize; p[nb].total = buf->f_blocks * 2048 / buf->f_bsize; p[nb].free = buf->f_bavail * 2048 / buf->f_bsize; return 0; } #endif /* defined(_volinfo_statvfs) */ /* volinfo_statvfs */ #if defined(_volinfo_statvfs) # include static int _volinfo_statvfs_append(struct volinfo ** dev, struct statvfs * buf, int nb); static int _volinfo_statvfs(struct volinfo ** dev) { int ret; struct statvfs * buf; int cnt; int cnt2; if((cnt = getvfsstat(NULL, 0, ST_WAIT)) == -1) return _probe_perror("getvfsstat", -1); if((buf = malloc(sizeof(struct statvfs) * cnt)) == NULL) return _probe_perror(NULL, -1); if((cnt2 = getvfsstat(buf, sizeof(struct statvfs) * cnt, ST_WAIT)) == -1) { free(buf); return _probe_perror("getvfsstat", -1); } for(ret = 0; ret < cnt && ret < cnt2; ret++) { if(_volinfo_statvfs_append(dev, &buf[ret], ret) == 0) continue; ret = -1; break; } free(buf); return ret; } static int _volinfo_statvfs_append(struct volinfo ** dev, struct statvfs * buf, int nb) { struct volinfo * p; if((p = realloc(*dev, sizeof(*p) * (nb + 1))) == NULL) return _probe_perror(NULL, 1); *dev = p; strcpy(p[nb].name, buf->f_mntonname); # if defined(DEBUG) fprintf(stderr, "_volinfo_append: %s\n", p[nb].name); # endif p[nb].block_size = buf->f_bsize; p[nb].total = buf->f_blocks * 2048 / buf->f_bsize; p[nb].free = buf->f_bavail * 2048 / buf->f_bsize; return 0; } #endif /* defined(_volinfo_statvfs) */ /* volinfo generic */ #if defined(_volinfo_generic) # warning Generic volume information is not supported static int _volinfo_generic(struct volinfo ** dev) { *dev = NULL; return 0; } #endif /* defined(_volinfo_generic) */ /* Probe */ /* private */ /* types */ typedef struct _App { struct sysinfo sysinfo; unsigned int users; struct ifinfo * ifinfo; unsigned int ifinfo_cnt; struct volinfo * volinfo; unsigned int volinfo_cnt; } Probe; /* prototypes */ static int _probe_error(int ret); static int _probe_perror(char const * message, int ret); static int _probe_timeout(Probe * probe); /* functions */ /* probe */ static int _probe(AppServerOptions options) { Probe probe; AppServer * appserver; Event * event; struct timeval tv; memset(&probe, 0, sizeof(probe)); if(_probe_timeout(&probe) != 0) { free(probe.ifinfo); free(probe.volinfo); return 1; } if((event = event_new()) == NULL) { free(probe.ifinfo); free(probe.volinfo); return _probe_error(1); } if((appserver = appserver_new_event(&probe, options, APPSERVER_PROBE_NAME, NULL, event)) == NULL) { free(probe.ifinfo); free(probe.volinfo); event_delete(event); return _probe_error(1); } tv.tv_sec = PROBE_REFRESH; tv.tv_usec = 0; if(event_register_timeout(event, &tv, (EventTimeoutFunc)_probe_timeout, &probe) != 0) _probe_error(0); else event_loop(event); appserver_delete(appserver); event_delete(event); free(probe.ifinfo); free(probe.volinfo); return 1; } /* probe_error */ static int _probe_error(int ret) { error_print(PROGNAME_PROBE); return ret; } /* probe_perror */ static int _probe_perror(char const * message, int ret) { error_set_print(PROGNAME_PROBE, ret, "%s%s%s", message ? message : "", message ? ": " : "", strerror(errno)); return ret; } /* probe_timeout */ static int _probe_timeout(Probe * probe) { int i; #if defined(DEBUG) static unsigned int count = 0; fprintf(stderr, "%s%d%s", "_probe_timeout(", count++, ")\n"); #endif if(_sysinfo(&probe->sysinfo) != 0) return _probe_perror("sysinfo", 1); if(_userinfo(&probe->users) != 0) return _probe_perror("userinfo", 1); if((i = _ifinfo(&probe->ifinfo)) < 0) return _probe_perror("ifinfo", 1); probe->ifinfo_cnt = i; if((i = _volinfo(&probe->volinfo)) < 0) return _probe_perror("volinfo", 1); probe->volinfo_cnt = i; return 0; } /* public */ /* functions */ /* AppInterface */ /* Probe_uptime */ uint32_t Probe_uptime(Probe * probe, AppServerClient * asc) { (void) asc; #if defined(DEBUG) fprintf(stderr, "%s() %ld\n", __func__, probe->sysinfo.uptime); #endif return probe->sysinfo.uptime; } /* Probe_load */ int32_t Probe_load(Probe * probe, AppServerClient * asc, uint32_t * load1, uint32_t * load5, uint32_t * load15) { (void) asc; #if defined(DEBUG) fprintf(stderr, "%s() %lu %lu %lu\n", __func__, probe->sysinfo.loads[0], probe->sysinfo.loads[1], probe->sysinfo.loads[2]); #endif *load1 = probe->sysinfo.loads[0]; *load5 = probe->sysinfo.loads[1]; *load15 = probe->sysinfo.loads[2]; return 0; } /* Probe_ram */ int32_t Probe_ram(Probe * probe, AppServerClient * asc, uint32_t * total, uint32_t * free, uint32_t * shared, uint32_t * buffer) { (void) asc; #if defined(DEBUG) fprintf(stderr, "%s() total %lu, free %lu, shared %lu, buffered %lu\n", __func__, probe->sysinfo.totalram, probe->sysinfo.freeram, probe->sysinfo.sharedram, probe->sysinfo.bufferram); #endif *total = probe->sysinfo.totalram; *free = probe->sysinfo.freeram; *shared = probe->sysinfo.sharedram; *buffer = probe->sysinfo.bufferram; return 0; } /* Probe_swap */ int32_t Probe_swap(Probe * probe, AppServerClient * asc, uint32_t * total, uint32_t * free) { (void) asc; #if defined(DEBUG) fprintf(stderr, "%s() %lu/%lu\n", __func__, probe->sysinfo.totalswap - probe->sysinfo.freeswap, probe->sysinfo.totalswap); #endif *total = probe->sysinfo.totalswap; *free = probe->sysinfo.freeswap; return 0; } /* Probe_procs */ uint32_t Probe_procs(Probe * probe, AppServerClient * asc) { (void) asc; #if defined(DEBUG) fprintf(stderr, "%s() %u\n", __func__, probe->sysinfo.procs); #endif return probe->sysinfo.procs; } /* Probe_users */ uint32_t Probe_users(Probe * probe, AppServerClient * asc) { (void) asc; #if defined(DEBUG) fprintf(stderr, "%s() %u\n", __func__, probe->users); #endif return probe->users; } /* Probe_ifrxbytes */ uint32_t Probe_ifrxbytes(Probe * probe, AppServerClient * asc, String const * dev) { unsigned int i; (void) asc; for(i = 0; i < probe->ifinfo_cnt && string_compare(probe->ifinfo[i].name, dev) != 0; i++); if(i == probe->ifinfo_cnt) return -1; #if defined(DEBUG) fprintf(stderr, "%s() %s %u\n", __func__, probe->ifinfo[i].name, probe->ifinfo[i].ibytes); #endif return probe->ifinfo[i].ibytes; } /* Probe_iftxbytes */ uint32_t Probe_iftxbytes(Probe * probe, AppServerClient * asc, String const * dev) { unsigned int i; (void) asc; for(i = 0; i < probe->ifinfo_cnt && string_compare(probe->ifinfo[i].name, dev) != 0; i++); if(i == probe->ifinfo_cnt) return -1; #if defined(DEBUG) fprintf(stderr, "%s() %s %u\n", __func__, probe->ifinfo[i].name, probe->ifinfo[i].obytes); #endif return probe->ifinfo[i].obytes; } /* Probe_voltotal */ uint32_t Probe_voltotal(Probe * probe, AppServerClient * asc, String const * volume) { unsigned int i; (void) asc; for(i = 0; i < probe->volinfo_cnt && string_compare(probe->volinfo[i].name, volume) != 0; i++); if(i == probe->volinfo_cnt) return -1; #if defined(DEBUG) fprintf(stderr, "%s() %s %lu\n", __func__, probe->volinfo[i].name, probe->volinfo[i].total); #endif return probe->volinfo[i].total * (probe->volinfo[i].block_size / 1024); } /* Probe_volfree */ uint32_t Probe_volfree(Probe * probe, AppServerClient * asc, String const * volume) { unsigned int i; (void) asc; for(i = 0; i < probe->volinfo_cnt && string_compare(probe->volinfo[i].name, volume) != 0; i++); if(i == probe->volinfo_cnt) return -1; #if defined(DEBUG) fprintf(stderr, "%s() %s %lu\n", __func__, probe->volinfo[i].name, probe->volinfo[i].free); #endif return probe->volinfo[i].free * (probe->volinfo[i].block_size / 1024); } /* usage */ static int _usage(void) { fputs("Usage: " PROGNAME_PROBE " [-R]\n", stderr); return 1; } /* main */ int main(int argc, char * argv[]) { int o; AppServerOptions options = 0; while((o = getopt(argc, argv, "R")) != -1) switch(o) { case 'R': options = ASO_REGISTER; break; default: return _usage(); } if(optind != argc) return _usage(); return (_probe(options) == 0) ? 0 : 2; }