daemon/netatopd.c | 369 ++++++++++++++++++++++++++---------------------------- module/netatop.c | 119 ++++++++---------- netatop.init | 131 +++++++++++++------ netatopd.h | 16 +-- 4 files changed, 330 insertions(+), 305 deletions(-) diff --git a/daemon/netatopd.c b/daemon/netatopd.c index b456dda..7fda130 100644 --- a/daemon/netatopd.c +++ b/daemon/netatopd.c @@ -54,14 +54,9 @@ #include "../netatopd.h" #include "../netatopversion.h" -#define EVER ;; - #include #include -static int histopen(struct naheader **); -static void recstore(int, struct netpertask *, socklen_t); - /* ** Semaphore-handling ** @@ -73,46 +68,145 @@ static void recstore(int, struct netpertask *, socklen_t); ** decremented by every analysis process (like atop) that uses the log-file ** and incremented as soon as such analysis process stops again. */ -#define SEMTOTAL 100 -#define NUMCLIENTS (SEMTOTAL - semctl(semid, 1, GETVAL, 0)) +#define SEMTOTAL 100 +static int numclients(int semid) +{ + return SEMTOTAL - semctl(semid, 1, GETVAL, 0); +} + +#define err_log(s) syslog(LOG_ERR, s) +#define info_log(s) syslog(LOG_INFO, s) -int -main(int argc, char *argv[]) +/* +** open history file +*/ +static int histopen(struct naheader **nahp) { - int i, netsock, histfd = -1, semid; - struct netpertask npt; - socklen_t len = sizeof npt; - struct naheader *nap; - struct sigaction sigact; - void gethup(int); - struct sembuf semincr = {0, +1, SEM_UNDO}; + int fd; + struct naheader nahdr; + + /* + ** remove the old file; this way atop can detect that a + ** new file must be opened + */ + unlink(NETEXITFILE); + + /* + ** open new file + */ + fd = open(NETEXITFILE, O_RDWR|O_CREAT|O_TRUNC, 0644); + if (fd == -1) { + err_log("cannot open " NETEXITFILE " for write\n"); + return -1; + } + + /* + ** write new header and mmap + */ + nahdr.magic = MYMAGIC; + nahdr.curseq = 0; + nahdr.hdrlen = sizeof(struct naheader); + nahdr.ntplen = sizeof(struct netpertask); + nahdr.mypid = getpid(); + if (write(fd, &nahdr, sizeof(nahdr)) != sizeof(nahdr)) { + err_log("cannot write to " NETEXITFILE "\n"); + return -1; + } + + *nahp = mmap(NULL, sizeof(*nahp), PROT_WRITE, MAP_SHARED, fd, 0); + + if (*nahp == (void *)-1) { + err_log("mmap of " NETEXITFILE " failed\n"); + return -1; + } + + return fd; +} + +static int recstore(int fd, struct netpertask *np, socklen_t len) +{ + Byte compbuf[sizeof(*np) + 128]; + unsigned long complen; + struct statvfs statvfs; + + /* + ** check if the filesystem is not filled for more than 95% + */ + if (fstatvfs(fd, &statvfs) != -1 && + statvfs.f_bfree * 100 / statvfs.f_blocks < 5) { + err_log("Filesystem > 95%% full; write skipped\n"); + return 0; + } + + /* + ** filesystem space sufficient + ** compress netpertask struct + */ + complen = sizeof(compbuf) - 1; + switch (compress(compbuf + 1, &complen, (Byte *)np, (unsigned long)sizeof(*np))) { + case Z_OK: + case Z_STREAM_END: + case Z_NEED_DICT: + break; + default: + err_log("compression failure\n"); + return 5; + } + + compbuf[0] = (Byte)complen; + + /* + ** write compressed netpertask struct, headed by one byte + ** with the size of the compressed struct + */ + if (write(fd, compbuf, complen + 1) < complen) { + err_log("write failure\n"); + return 5; + } + + return 0; +} + + +/* +** dummy handler for SIGHUP +*/ +void gethup(int sig) +{ +} + +int main(int argc, char *argv[]) +{ + int i, netsock, histfd, semid; + struct netpertask npt; + socklen_t len; + struct naheader *nap; + struct sigaction sigact; + struct sembuf semincr; /* ** version number required? */ - if (argc == 2 && *argv[1] == '-' && *(argv[1]+1) == 'v') - { - printf("%s - %s \n", - NETATOPVERSION, NETATOPDATE); + if (argc == 2 && *argv[1] == '-' && *(argv[1] + 1) == 'v') { + puts(NETATOPVERSION " - " NETATOPDATE " "); return 0; } /* - ** verify if we are running with the right privileges + ** verify if we are running with the right privileges */ - if (geteuid() != 0) - { - fprintf(stderr, "Root privileges are needed!\n"); - exit(1); + if (geteuid() != 0) { + fputs("Root privileges are needed!\n", stderr); + return 1; } /* - ** open socket to IP layer + ** open socket to IP layer */ - if ( (netsock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW)) == -1) - { + netsock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW); + if (netsock == -1) { perror("open raw socket"); - exit(2); + return 2; } /* @@ -120,40 +214,35 @@ main(int argc, char *argv[]) ** if it already exists, verify if a netatopd daemon ** is already running */ - if ( (semid = semget(SEMAKEY, 0, 0)) >= 0) // exists? - { - if ( semctl(semid, 0, GETVAL, 0) == 1) - { - fprintf(stderr, "Another netatopd is already running!"); - exit(3); + semid = semget(SEMAKEY, 0, 0); + if (semid >= 0) { // exists? + if (semctl(semid, 0, GETVAL, 0) == 1) { + fputs("Another netatopd is already running!", stderr); + return 3; } - } - else - { - if ( (semid = semget(SEMAKEY, 2, 0600|IPC_CREAT|IPC_EXCL)) >= 0) - { - (void) semctl(semid, 0, SETVAL, 0); - (void) semctl(semid, 1, SETVAL, SEMTOTAL); - } - else - { + } else { + semid = semget(SEMAKEY, 2, 0600|IPC_CREAT|IPC_EXCL); + if (semid >= 0) { + semctl(semid, 0, SETVAL, 0); + semctl(semid, 1, SETVAL, SEMTOTAL); + } else { perror("cannot create semaphore"); - exit(3); + return 3; } } /* ** daemonize this process */ - if ( fork() ) - exit(0); // implicitly switch to background + if (fork()) + return 0; // implicitly switch to background setsid(); if ( fork() ) - exit(0); + return 0; - for (i=0; i < 1024; i++) + for (i = 0; i < 1024; i++) if (i != netsock) close(i); @@ -165,15 +254,17 @@ main(int argc, char *argv[]) ** open syslog interface for failure messages */ openlog("netatopd", LOG_PID, LOG_DAEMON); - syslog(LOG_INFO, "version %s actived\n", NETATOPVERSION); + info_log("version " NETATOPVERSION " actived\n"); /* ** raise semaphore to define a busy netatopd */ - if ( semop(semid, &semincr, 1) == -1) - { - syslog(LOG_ERR, "cannot increment semaphore\n"); - exit(3); + semincr.sem_num = 0; + semincr.sem_op = +1; + semincr.sem_flg = SEM_UNDO; + if (semop(semid, &semincr, 1) == -1) { + err_log("cannot increment semaphore\n"); + return 3; } /* @@ -181,9 +272,9 @@ main(int argc, char *argv[]) ** the sighup signal to verify if there are no clients any more ** (truncate exitfile) */ - memset(&sigact, 0, sizeof sigact); - sigact.sa_handler = gethup; - sigaction(SIGHUP, &sigact, (struct sigaction *)0); + memset(&sigact, 0, sizeof(sigact)); + sigact.sa_handler = gethup; + sigaction(SIGHUP, &sigact, NULL); /* ** raise priority @@ -194,174 +285,64 @@ main(int argc, char *argv[]) ** open history file */ histfd = histopen(&nap); + if (histfd == -1) + return 3; /* ** continuously obtain the info about exited processes */ - for (EVER) + for (len = sizeof(npt);;) { /* ** check if anybody is interested in the exitfile ** if not, close and truncate it */ - if (NUMCLIENTS == 0 && nap->curseq != 0) + if (!numclients(semid) && nap->curseq) { /* ** destroy and reopen history file */ munmap(nap, sizeof(struct naheader)); close(histfd); - syslog(LOG_INFO, "reopen history file\n"); + info_log("reopen history file\n"); histfd = histopen(&nap); + if (histfd == -1) + return 3; } /* - ** get next exited process (call blocks till available) + ** get next exited process (call blocks till available) */ - switch (getsockopt(netsock, SOL_IP, NETATOP_GETCNT_EXIT, - &npt, &len)) - { - case 0: - // skip if nobody is using it - if (NUMCLIENTS == 0) + if (getsockopt(netsock, SOL_IP, NETATOP_GETCNT_EXIT, &npt, &len)) + // getsockopt failed + switch (errno) { + // no netatop module loaded? + case ENOPROTOOPT: + sleep(10); continue; - + // signal received? + case EINTR: + continue; + default: + err_log("getsockopt failed\n"); + return 0; + } + else if (numclients(semid)) { + // skip if nobody is using it /* ** at least one listener is active, so ** store record for exited process */ - recstore(histfd, &npt, len); - + i = recstore(histfd, &npt, len); + if (i) + return i; /* ** increment sequence number in file header */ nap->curseq++; - break; - - default: // getsockopt failed - switch (errno) - { - // no netatop module loaded? - case ENOPROTOOPT: - sleep(10); - continue; - - // signal received? - case EINTR: - continue; - - default: - syslog(LOG_ERR, "getsockopt failed\n"); - return 0; - } } } return 0; } -/* -** open history file -*/ -static int -histopen(struct naheader **nahp) -{ - int fd; - struct naheader nahdr = {MYMAGIC, 0, - sizeof(struct naheader), - sizeof(struct netpertask), - getpid()}; - /* - ** remove the old file; this way atop can detect that a - ** new file must be opened - */ - (void) unlink(NETEXITFILE); - - /* - ** open new file - */ - if ( (fd = open(NETEXITFILE, O_RDWR|O_CREAT|O_TRUNC, 0644)) == -1) - { - syslog(LOG_ERR, "cannot open %s for write\n", NETEXITFILE); - exit(3); - } - - /* - ** write new header and mmap - */ - if ( write(fd, &nahdr, sizeof nahdr) != sizeof nahdr) - { - syslog(LOG_ERR, "cannot write to %s\n", NETEXITFILE); - exit(3); - } - - *nahp = mmap((void *)0, sizeof *nahp, PROT_WRITE, MAP_SHARED, fd, 0); - - if (*nahp == (void *) -1) - { - syslog(LOG_ERR, "mmap of %s failed\n", NETEXITFILE); - exit(3); - } - - return fd; -} - -static void -recstore(int fd, struct netpertask *np, socklen_t len) -{ - Byte compbuf[sizeof *np + 128]; - unsigned long complen = sizeof compbuf -1; - struct statvfs statvfs; - int rv; - - /* - ** check if the filesystem is not filled for more than 95% - */ - if ( fstatvfs(fd, &statvfs) != -1) - { - if (statvfs.f_bfree * 100 / statvfs.f_blocks < 5) - { - syslog(LOG_ERR, "Filesystem > 95%% full; " - "write skipped\n"); - return; - } - } - - /* - ** filesystem space sufficient - ** compress netpertask struct - */ - rv = compress(compbuf+1, &complen, (Byte *)np, - (unsigned long)sizeof *np); - switch (rv) - { - case Z_OK: - case Z_STREAM_END: - case Z_NEED_DICT: - break; - - default: - syslog(LOG_ERR, "compression failure\n"); - exit(5); - } - - compbuf[0] = (Byte)complen; - - /* - ** write compressed netpertask struct, headed by one byte - ** with the size of the compressed struct - */ - if ( write(fd, compbuf, complen+1) < complen) - { - syslog(LOG_ERR, "write failure\n"); - exit(5); - } -} - -/* -** dummy handler for SIGHUP -*/ -void -gethup(int sig) -{ -} diff --git a/module/netatop.c b/module/netatop.c index 9ebf563..ac187ad 100644 --- a/module/netatop.c +++ b/module/netatop.c @@ -307,9 +307,9 @@ static void get_tcpv4_ident(struct iphdr *, void *, static struct sockinfo *find_sockinfo(int, union keydef *, int, int); static struct sockinfo *make_sockinfo(int, union keydef *, int, int); -static void wipesockinfo(void); -static void wipetaskinfo(void); -static void wipetaskexit(void); +static void __exit wipesockinfo(void); +static void __exit wipetaskinfo(void); +static void __exit wipetaskexit(void); static void garbage_collector(void); static void gctaskexit(void); @@ -1018,6 +1018,16 @@ gctaskexit() spin_unlock_irqrestore(&exitlock, flags); } +static struct pid *find_vpid_lock(struct taskinfo *ti) +{ + struct pid *pid; + + rcu_read_lock(); + pid = find_vpid(ti->id); + rcu_read_unlock(); + return pid; +} + /* ** cleanup sockinfo structures that are connected to finished processes */ @@ -1027,7 +1037,6 @@ gcsockinfo() int i; struct sockinfo *sip, *sipsave; unsigned long sflags, tflags; - struct pid *pid; /* ** go through all sockinfo hash buckets @@ -1116,11 +1125,7 @@ gcsockinfo() ** if the thread group exists, just mark ** it as 'checked' for this cycle */ - rcu_read_lock(); - pid = find_vpid(sip->tgp->id); - rcu_read_unlock(); - - if (pid == NULL) { + if (find_vpid_lock(sip->tgp) == NULL) { sip->tgp->state = INDELETE; spin_unlock_irqrestore( &thash[sip->tgh].lock, tflags); @@ -1177,11 +1182,7 @@ gcsockinfo() ** if not, mark it as 'indelete' and break connection ** if thread exists, mark it 'checked' */ - rcu_read_lock(); - pid = find_vpid(sip->thp->id); - rcu_read_unlock(); - - if (pid == NULL) { + if (find_vpid_lock(sip->thp) == NULL) { sip->thp->state = INDELETE; sip->thp = NULL; } else { @@ -1237,7 +1238,6 @@ gctaskinfo() int i; struct taskinfo *tip, *tipsave; unsigned long tflags; - struct pid *pid; /* ** go through all taskinfo hash buckets @@ -1255,6 +1255,11 @@ gctaskinfo() */ while (tip != (void *)&thash[i].ch) { switch (tip->state) { + default: // not checked yet + if (find_vpid_lock(tip) != NULL) { + tip = tip->ch.next; + break; + } /* ** remove INDELETE tasks from the hash buckets ** -- move thread group to exitlist @@ -1275,24 +1280,6 @@ gctaskinfo() tip->state = 0; tip = tip->ch.next; break; - - default: // not checked yet - rcu_read_lock(); - pid = find_vpid(tip->id); - rcu_read_unlock(); - - if (pid == NULL) { - tipsave = tip->ch.next; - - if (tip->type == 'g') - move_taskinfo(tip); - else - delete_taskinfo(tip); - - tip = tipsave; - } else { - tip = tip->ch.next; - } } } @@ -1304,7 +1291,7 @@ gctaskinfo() /* ** remove all sockinfo structs */ -static void +static void __exit wipesockinfo() { struct sockinfo *sip, *sipsave; @@ -1332,7 +1319,7 @@ wipesockinfo() /* ** remove all taskinfo structs from hash list */ -static void +static void __exit wipetaskinfo() { struct taskinfo *tip, *tipsave; @@ -1360,12 +1347,24 @@ wipetaskinfo() /* ** remove all taskinfo structs from exit list */ -static void +static void __exit wipetaskexit() { gctaskexit(); } +static void dec_nrt(struct taskinfo *tip) +{ + unsigned long flags; + + ((struct taskinfo *)tip->ch.next)->ch.prev = tip->ch.prev; + ((struct taskinfo *)tip->ch.prev)->ch.next = tip->ch.next; + + spin_lock_irqsave(&nrtlock, flags); + nrt--; + spin_unlock_irqrestore(&nrtlock, flags); +} + /* ** move one taskinfo struct from hash bucket to exitlist */ @@ -1377,12 +1376,7 @@ move_taskinfo(struct taskinfo *tip) /* ** remove from hash list */ - ((struct taskinfo *)tip->ch.next)->ch.prev = tip->ch.prev; - ((struct taskinfo *)tip->ch.prev)->ch.next = tip->ch.next; - - spin_lock_irqsave(&nrtlock, flags); - nrt--; - spin_unlock_irqrestore(&nrtlock, flags); + dec_nrt(tip); /* ** add to exitlist @@ -1393,12 +1387,11 @@ move_taskinfo(struct taskinfo *tip) spin_lock_irqsave(&exitlock, flags); - if (exittail) { // list filled? + if (exittail) // list filled? exittail->ch.next = tip; - exittail = tip; - } else { // list empty - exithead = exittail = tip; - } + else // list empty + exithead = tip; + exittail = tip; nre++; @@ -1413,16 +1406,9 @@ move_taskinfo(struct taskinfo *tip) static void delete_taskinfo(struct taskinfo *tip) { - unsigned long flags; - - ((struct taskinfo *)tip->ch.next)->ch.prev = tip->ch.prev; - ((struct taskinfo *)tip->ch.prev)->ch.next = tip->ch.next; + dec_nrt(tip); kmem_cache_free(ticache, tip); - - spin_lock_irqsave(&nrtlock, flags); - nrt--; - spin_unlock_irqrestore(&nrtlock, flags); } /* @@ -1629,7 +1615,7 @@ static int netatop_thread(void *dummy) { while (!kthread_should_stop()) { /* - ** do garbage collection + ** do garbage collection */ garbage_collector(); @@ -1642,10 +1628,16 @@ static int netatop_thread(void *dummy) return 0; } +static void kmem_cache_destroy2(void) +{ + kmem_cache_destroy(ticache); + kmem_cache_destroy(sicache); +} + /* ** called when module loaded */ -int +int __init init_module() { int i; @@ -1686,8 +1678,7 @@ init_module() ** register getsockopt for user space communication */ if (nf_register_sockopt(&sockopts) < 0) { - kmem_cache_destroy(ticache); - kmem_cache_destroy(sicache); + kmem_cache_destroy2(); return -1; } @@ -1699,8 +1690,7 @@ init_module() if (IS_ERR(knetatop_task)) { nf_unregister_sockopt(&sockopts); - kmem_cache_destroy(ticache); - kmem_cache_destroy(sicache); + kmem_cache_destroy2(); return -1; } @@ -1736,7 +1726,7 @@ init_module() /* ** called when module unloaded */ -void +void __exit cleanup_module() { /* @@ -1762,8 +1752,7 @@ cleanup_module() wipetaskexit(); /* - ** destroy caches + ** destroy caches */ - kmem_cache_destroy(ticache); - kmem_cache_destroy(sicache); + kmem_cache_destroy2(); } diff --git a/netatop.init b/netatop.init index e6a368b..c1e076d 100755 --- a/netatop.init +++ b/netatop.init @@ -2,65 +2,120 @@ # # netatop Startup script for the netatop kernel module and daemon # -# chkconfig: 2345 84 16 +# chkconfig: - 11 89 # description: Gather per-process statistics about network utilization # ### BEGIN INIT INFO # Provides: netatop # Required-Start: $local_fs # Required-Stop: $local_fs -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 +# Default-Start: 3 5 +# Default-Stop: 0 1 2 6 # Short-Description: Gather per-process statistics about network utilization # Description: Gather per-process statistics about network utilization ### END INIT INFO -RETVAL=0 +# Do not load RH compatibility interface. +WITHOUT_RC_COMPAT=1 + +# Source function library. +. /etc/init.d/functions -start() { - modprobe netatop +PIDFILE=/var/run/netatop.pid +LOCKFILE=/var/lock/subsys/netatop +RETVAL=0 - if [ -f /usr/sbin/netatopd ]; then - /usr/sbin/netatopd - fi +load_mod() +{ + action "Loading netatop module:" modprobe -q netatop } -stop() { - PID=$(ps -e | grep netatopd | sed -e 's/^ *//' -e 's/ .*//') +unload_mod() +{ + action "Unloading netatop module:" modprobe -qr netatop +} - if [ "$PID" ] - then - kill "$PID" - fi +startd() +{ + start_daemon --pidfile "$PIDFILE" --lockfile "$LOCKFILE" --expect-user root -- netatopd + RETVAL=$? + [ $RETVAL -eq 0 ] && pidof netatopd > "$PIDFILE" + return $RETVAL +} - modprobe -r netatop +stopd() +{ + stop_daemon --pidfile "$PIDFILE" --lockfile "$LOCKFILE" --expect-user root -- netatopd + RETVAL=$? + return $RETVAL } -# See how we were called. -case "$1" in - start) - start - ;; +start() +{ + RETVAL=1 + load_mod && startd + return $RETVAL +} - stop) - stop - ;; +stop() +{ + stopd && unload_mod + return $RETVAL +} - status) - cat /proc/netatop - ;; +restart() +{ + stopd && startd +} - reload) - stop - start - ;; +reload() +{ + stop && start +} - restart) - stop - start - ;; +Status() +{ + grep -qs '^netatop' /proc/modules && echo "netatop module is loaded" + status --pidfile "$PIDFILE" --expect-user root -- netatopd + RETVAL=$? + return $RETVAL +} - *) - echo "Usage: $0 [start|stop|status|reload|restart]" - exit 1 +# See how we were called. +case "$1" in + start) + start + ;; + stop) + stop + ;; + reload) + reload + ;; + restart) + restart + ;; + condstop) + if [ -e "$LOCKFILE" ]; then + stop + fi + ;; + condrestart) + if [ -e "$LOCKFILE" ]; then + restart + fi + ;; + condreload) + if [ -e "$LOCKFILE" ]; then + reload + fi + ;; + status) + Status + ;; + *) + msg_usage "${0##*/} {start|stop|reload|restart|condstop|condrestart|condreload|status}" + RETVAL=1 esac + +exit $RETVAL diff --git a/netatopd.h b/netatopd.h index c1ba8e0..cb838fd 100644 --- a/netatopd.h +++ b/netatopd.h @@ -1,12 +1,12 @@ -#define SEMAKEY 1541961 +#define SEMAKEY 1541961 -#define NETEXITFILE "/var/run/netatop.log" -#define MYMAGIC (unsigned int) 0xfeedb0b0 +#define NETEXITFILE "/var/run/netatop.log" +#define MYMAGIC 0xfeedb0b0U struct naheader { - u_int32_t magic; // magic number MYMAGIC - u_int32_t curseq; // sequence number of last netpertask - u_int16_t hdrlen; // length of this header - u_int16_t ntplen; // length of netpertask structure - pid_t mypid; // PID of netatopd itself + u_int32_t magic; // magic number MYMAGIC + u_int32_t curseq; // sequence number of last netpertask + u_int16_t hdrlen; // length of this header + u_int16_t ntplen; // length of netpertask structure + pid_t mypid; // PID of netatopd itself };