"Fossies" - the Fresh Open Source Software Archive

Member "atop-2.8.1/netatopif.c" (7 Jan 2023, 11673 Bytes) of package /linux/misc/atop-2.8.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "netatopif.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 2.5.0_vs_2.6.0.

    1 /*
    2 ** ATOP - System & Process Monitor
    3 **
    4 ** The program 'atop' offers the possibility to view the activity of
    5 ** the system on system-level as well as process-level.
    6 **
    7 ** This source-file contains functions to interface with the netatop
    8 ** module in the kernel. That module keeps track of network activity
    9 ** per process and thread.
   10 ** ================================================================
   11 ** Author:      Gerlof Langeveld
   12 ** E-mail:      gerlof.langeveld@atoptool.nl
   13 ** Date:        August/September 2012
   14 **
   15 ** This program is free software; you can redistribute it and/or modify it
   16 ** under the terms of the GNU General Public License as published by the
   17 ** Free Software Foundation; either version 2, or (at your option) any
   18 ** later version.
   19 **
   20 ** This program is distributed in the hope that it will be useful, but
   21 ** WITHOUT ANY WARRANTY; without even the implied warranty of
   22 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   23 ** See the GNU General Public License for more details.
   24 */
   25 
   26 #include <sys/types.h>
   27 #include <stdio.h>
   28 #include <string.h>
   29 #include <stdlib.h>
   30 #include <unistd.h>
   31 #include <errno.h>
   32 #include <sys/socket.h>
   33 #include <netinet/in.h>
   34 #include <netinet/ip.h>
   35 #include <sys/ipc.h>
   36 #include <sys/sem.h>
   37 #include <sys/stat.h>
   38 #include <fcntl.h>
   39 #include <signal.h>
   40 #include <zlib.h>
   41 #include <sys/mman.h>
   42 
   43 #include "atop.h"
   44 #include "photoproc.h"
   45 
   46 #include "netatop.h"
   47 #include "netatopd.h"
   48 
   49 static int      netsock   = -1;
   50 static int      netexitfd = -1;
   51 static struct naheader  *nahp;
   52 static int      semid     = -1;
   53 static unsigned long    lastseq;
   54 
   55 /*
   56 ** storage of last exited tasks read from exitfile
   57 ** every exitstore struct is registered in hash buckets,
   58 ** by its pid or by its begin time
   59 */
   60 struct exitstore {
   61     struct exitstore  *next;
   62     unsigned char      isused;
   63     struct netpertask  npt;
   64 };
   65 
   66 #define NHASH       1024    // must be power of two!
   67 #define HASHCALC(x) ((x)&(NHASH-1))
   68 
   69 static struct exitstore *esbucket[NHASH];
   70 
   71 static struct exitstore *exitall;
   72 static int       exitnum;
   73 static char      exithash;
   74 
   75 static void fill_networkcnt(struct tstat *, struct tstat *,
   76                             struct exitstore *);
   77 
   78 /*
   79 ** open a raw socket to the IP layer (root privs required)
   80 */
   81 void
   82 netatop_ipopen(void)
   83 {
   84     netsock = socket(PF_INET, SOCK_RAW, IPPROTO_RAW);
   85 }
   86 
   87 /*
   88 ** check if at this moment the netatop kernel module is loaded and
   89 ** the netatopd daemon is active
   90 */
   91 void
   92 netatop_probe(void)
   93 {
   94     struct sembuf       semdecr = {1, -1, SEM_UNDO};
   95     socklen_t       sl = 0;
   96     struct stat     exstat;
   97 
   98     /*
   99     ** check if IP socket is open
  100     */
  101     if (netsock == -1)
  102         return;
  103 
  104     /*
  105     ** probe if the netatop module is active
  106     */
  107     if ( getsockopt(netsock, SOL_IP, NETATOP_PROBE, NULL, &sl) != 0)
  108     {
  109         supportflags &= ~NETATOP;
  110         supportflags &= ~NETATOPD;
  111         return;
  112     }
  113 
  114     // set appropriate support flag
  115     supportflags |= NETATOP;
  116 
  117     /*
  118     ** check if the netatopd daemon is active to register exited tasks
  119     ** and decrement semaphore to indicate that we want to subscribe
  120     */
  121     if (semid == -1)
  122     {
  123         if ( (semid = semget(SEMAKEY, 0, 0))    == -1 ||
  124                           semop(semid, &semdecr, 1) == -1   )
  125         {
  126             supportflags &= ~NETATOPD;
  127             return;
  128         }
  129     }
  130 
  131     if (semctl(semid, 0, GETVAL, 0) != 1)
  132     {
  133         supportflags &= ~NETATOPD;
  134         return;
  135     }
  136 
  137     /*
  138     ** check if exitfile still open and not removed by netatopd
  139     */
  140     if (netexitfd != -1)
  141     {
  142         if ( fstat(netexitfd, &exstat) == 0 && 
  143              exstat.st_nlink > 0              ) // not removed
  144         {
  145             supportflags |= NETATOPD;
  146             return;
  147         }
  148         else
  149         {
  150             (void) close(netexitfd);
  151 
  152             if (nahp)
  153                 munmap(nahp, sizeof *nahp);
  154 
  155             netexitfd = -1;
  156             nahp = NULL;
  157         }
  158     }
  159 
  160     /*
  161     ** open file with compressed stats of exited tasks
  162     ** and (re)mmap the start record, mainly to obtain current sequence
  163     */
  164     if (netexitfd == -1)
  165     {
  166         if ( (netexitfd = open(NETEXITFILE, O_RDONLY, 0)) == -1)
  167         {
  168             supportflags &= ~NETATOPD;
  169                     return;
  170         }
  171     }
  172 
  173     if ( (nahp = mmap((void *)0, sizeof *nahp, PROT_READ, MAP_SHARED,
  174                         netexitfd, 0)) == (void *) -1)
  175     {
  176             (void) close(netexitfd);
  177         netexitfd = -1;
  178         nahp = NULL;
  179         supportflags &= ~NETATOPD;
  180         return;
  181     }
  182 
  183     /*
  184     ** if this is a new incarnation of the netatopd daemon,
  185     ** position seek pointer on first task that is relevant to us
  186     ** and remember last sequence number to know where to start
  187     */
  188     (void) lseek(netexitfd, 0, SEEK_END);
  189 
  190     lastseq     = nahp->curseq;
  191 
  192     // set appropriate support flag
  193     supportflags |= NETATOPD;
  194 }
  195 
  196 void
  197 netatop_signoff(void)
  198 {
  199     struct sembuf   semincr = {1, +1, SEM_UNDO};
  200 
  201     if (netsock == -1 || nahp == NULL)
  202         return;
  203 
  204     if (supportflags & NETATOPD)
  205     {
  206             regainrootprivs();
  207         
  208             (void) semop(semid, &semincr, 1);
  209 
  210         kill(nahp->mypid, SIGHUP);
  211 
  212             if (! droprootprivs())
  213             mcleanstop(42, "failed to drop root privs\n");
  214 
  215         (void) munmap(nahp, sizeof *nahp);
  216         (void) close(netexitfd);
  217     }
  218 }
  219 
  220 /*
  221 ** read network counters for one existing task
  222 ** (type 'g' for thread group or type 't' for thread)
  223 */
  224 void
  225 netatop_gettask(pid_t id, char type, struct tstat *tp)
  226 {
  227     struct netpertask   npt;
  228     socklen_t       socklen = sizeof npt;
  229     int             cmd = (type == 'g' ?
  230                     NETATOP_GETCNT_TGID : NETATOP_GETCNT_PID);
  231 
  232     /*
  233     ** if kernel module netatop not active on this system, skip call
  234     */
  235     if (!(supportflags & NETATOP) ) {
  236         memset(&tp->net, 0, sizeof tp->net);
  237         return;
  238     }
  239 
  240     /*
  241     ** get statistics of this process/thread
  242     */
  243     npt.id  = id;
  244 
  245         regainrootprivs();
  246 
  247     if (getsockopt(netsock, SOL_IP, cmd, &npt, &socklen) != 0) {
  248         memset(&tp->net, 0, sizeof tp->net);
  249 
  250             if (! droprootprivs())
  251             mcleanstop(42, "failed to drop root privs\n");
  252 
  253         if (errno == ENOPROTOOPT || errno == EPERM)
  254         {
  255             supportflags &= ~NETATOP;
  256             supportflags &= ~NETATOPD;
  257             close(netsock);
  258             netsock = -1;
  259         }
  260 
  261         return;
  262     }
  263 
  264         if (! droprootprivs())
  265         mcleanstop(42, "failed to drop root privs\n");
  266 
  267     /*
  268     ** statistics available: fill counters
  269     */
  270     tp->net.tcpsnd = npt.tc.tcpsndpacks;
  271     tp->net.tcprcv = npt.tc.tcprcvpacks;
  272     tp->net.tcpssz = npt.tc.tcpsndbytes;
  273     tp->net.tcprsz = npt.tc.tcprcvbytes;
  274 
  275     tp->net.udpsnd = npt.tc.udpsndpacks;
  276     tp->net.udprcv = npt.tc.udprcvpacks;
  277     tp->net.udpssz = npt.tc.udpsndbytes;
  278     tp->net.udprsz = npt.tc.udprcvbytes;
  279 }
  280 
  281 /*
  282 ** read all exited processes that have been added to the exitfile
  283 ** and store them into memory
  284 */
  285 unsigned int
  286 netatop_exitstore(void)
  287 {
  288     socklen_t       socklen = 0, nexitnet, sz, nr=0;
  289     unsigned long       uncomplen;
  290     unsigned char       nextsize;
  291     unsigned char       readbuf[nahp->ntplen+100];
  292     unsigned char       databuf[nahp->ntplen];
  293     struct netpertask   *tmp = (struct netpertask *)databuf;
  294     struct exitstore    *esp;
  295 
  296         regainrootprivs();
  297 
  298     /*
  299     ** force garbage collection:
  300     **   netatop module builds new list of exited processes that
  301     **   can be read by netatopd and written to exitfile
  302     */
  303     if (getsockopt(netsock, SOL_IP, NETATOP_FORCE_GC, NULL, &socklen)!=0) {
  304             if (! droprootprivs())
  305             mcleanstop(42, "failed to drop root privs\n");
  306 
  307         if (errno == ENOPROTOOPT || errno == EPERM)
  308         {
  309             supportflags &= ~NETATOP;
  310             supportflags &= ~NETATOPD;
  311             close(netsock);
  312             netsock = -1;
  313         }
  314 
  315         return 0;
  316     }
  317 
  318     /*
  319     ** wait until list of exited processes is read by netatopd
  320     ** and available to be read by atop
  321     */
  322     if (getsockopt(netsock, SOL_IP, NETATOP_EMPTY_EXIT, 0, &socklen) !=0) {
  323             if (! droprootprivs())
  324             mcleanstop(42, "failed to drop root privs\n");
  325 
  326         if (errno == ENOPROTOOPT || errno == EPERM)
  327         {
  328             supportflags &= ~NETATOP;
  329             supportflags &= ~NETATOPD;
  330             close(netsock);
  331             netsock = -1;
  332         }
  333 
  334         return 0;
  335     }
  336 
  337         if (! droprootprivs())
  338         mcleanstop(42, "failed to drop root privs\n");
  339 
  340     /*
  341     ** verify how many exited processes are available to be read
  342     ** from the exitfile
  343     */
  344     nexitnet = nahp->curseq - lastseq;
  345     lastseq  = nahp->curseq;
  346 
  347     if (nexitnet == 0)
  348         return 0;
  349 
  350     /*
  351     ** allocate storage for all exited processes
  352     */
  353     exitall = malloc(nexitnet * sizeof(struct exitstore));
  354 
  355     ptrverify(exitall, "Malloc failed for %d exited netprocs\n", nexitnet);
  356 
  357     memset(exitall, 0, nexitnet * sizeof(struct exitstore));
  358 
  359     esp = exitall;
  360 
  361     /*
  362     ** read next byte from exitfile that specifies the length
  363     ** of the next record
  364     */
  365     if ( read(netexitfd, &nextsize, 1) != 1) 
  366         return 0;
  367 
  368     /*
  369     ** read the next record and (if possible) the byte specifying
  370     ** the size of the next record
  371     */
  372     while ( (sz = read(netexitfd, readbuf, nextsize+1)) >= nextsize)
  373     {
  374         /*
  375         ** decompress record and store it
  376         */
  377             uncomplen = nahp->ntplen;
  378 
  379         if (nahp->ntplen <= sizeof(struct netpertask))
  380         {
  381             (void) uncompress((Byte *)&(esp->npt), &uncomplen,
  382                             readbuf, nextsize);
  383         }
  384         else
  385         {
  386             (void) uncompress((Byte *)databuf, &uncomplen,
  387                             readbuf, nextsize);
  388             esp->npt = *tmp;
  389         }
  390 
  391         esp++;
  392         nr++;
  393 
  394         /*
  395         ** check if we have read all records
  396         */
  397         if (nr == nexitnet)
  398         {
  399             /*
  400             ** if we have read one byte too many:
  401             ** reposition seek pointer 
  402             */
  403             if (sz > nextsize)
  404                 (void) lseek(netexitfd, -1, SEEK_CUR);
  405 
  406             break;
  407         }
  408 
  409         /*
  410         ** prepare reading next record
  411         */
  412         if (sz > nextsize)
  413             nextsize = readbuf[nextsize];
  414         else
  415             break;  // unexpected: more requested than available
  416     }
  417 
  418     exitnum = nr;
  419 
  420     return nr;
  421 }
  422 
  423 /*
  424 ** remove all stored exited processes from the hash bucket list
  425 */
  426 void
  427 netatop_exiterase(void)
  428 {
  429     free(exitall);
  430     memset(esbucket, 0, sizeof esbucket);
  431     exitnum = 0;
  432 }
  433 
  434 /*
  435 ** add all stored tasks to a hash bucket, either
  436 ** by pid (argument 'p') or by begin time (argument 'b')
  437 */
  438 void
  439 netatop_exithash(char hashtype)
  440 {
  441     int         i, h;
  442     struct exitstore    *esp;
  443 
  444     for (i=0, esp=exitall; i < exitnum; i++, esp++)
  445     {
  446         if (hashtype == 'p')
  447             h = HASHCALC(esp->npt.id);
  448         else
  449             h = HASHCALC(esp->npt.btime);
  450 
  451         esp->next   = esbucket[h];
  452         esbucket[h] = esp;
  453     }
  454 
  455     exithash = hashtype;
  456 }
  457 
  458 /*
  459 ** search for relevant exited network task and
  460 ** update counters in tstat struct
  461 */
  462 void
  463 netatop_exitfind(unsigned long key, struct tstat *dev, struct tstat *pre)
  464 {
  465     int             h = HASHCALC(key);
  466     struct exitstore    *esp;
  467 
  468     /*
  469     ** if bucket empty, forget about it
  470     */
  471     if ( (esp = esbucket[h]) == NULL)
  472         return;
  473 
  474     /*
  475     ** search thru hash bucket list
  476     */
  477     for (; esp; esp=esp->next)
  478     {
  479         switch (exithash)
  480         {
  481            case 'p':        // search by PID
  482             if (key != esp->npt.id)
  483                 continue;
  484 
  485             /*
  486             ** correct PID found
  487             */
  488             fill_networkcnt(dev, pre, esp);
  489             break;
  490 
  491            case 'b':        // search by begin time
  492             if (esp->isused)
  493                 continue;
  494 
  495             if (key != esp->npt.btime)
  496                 continue;
  497 
  498             /*
  499             ** btime is okay; additional checks required
  500             */
  501             if ( strcmp(esp->npt.command, pre->gen.name) != 0)
  502                 continue;
  503 
  504             if (esp->npt.tc.tcpsndpacks < pre->net.tcpsnd   ||
  505                 esp->npt.tc.tcpsndbytes < pre->net.tcpssz   ||
  506                 esp->npt.tc.tcprcvpacks < pre->net.tcprcv   ||
  507                 esp->npt.tc.tcprcvbytes < pre->net.tcprsz   ||
  508                 esp->npt.tc.udpsndpacks < pre->net.udpsnd   ||
  509                 esp->npt.tc.udpsndbytes < pre->net.udpssz   ||
  510                 esp->npt.tc.udprcvpacks < pre->net.udprcv   ||
  511                 esp->npt.tc.udprcvbytes < pre->net.udprsz     )
  512                 continue;
  513 
  514             esp->isused = 1;
  515  
  516             fill_networkcnt(dev, pre, esp);
  517             break;
  518         }
  519     }
  520 }
  521 
  522 static void
  523 fill_networkcnt(struct tstat *dev, struct tstat *pre, struct exitstore *esp)
  524 {
  525     dev->net.tcpsnd = esp->npt.tc.tcpsndpacks - pre->net.tcpsnd;
  526     dev->net.tcpssz = esp->npt.tc.tcpsndbytes - pre->net.tcpssz;
  527     dev->net.tcprcv = esp->npt.tc.tcprcvpacks - pre->net.tcprcv;
  528     dev->net.tcprsz = esp->npt.tc.tcprcvbytes - pre->net.tcprsz;
  529 
  530     dev->net.udpsnd = esp->npt.tc.udpsndpacks - pre->net.udpsnd;
  531     dev->net.udpssz = esp->npt.tc.udpsndbytes - pre->net.udpssz;
  532     dev->net.udprcv = esp->npt.tc.udprcvpacks - pre->net.udprcv;
  533     dev->net.udprsz = esp->npt.tc.udprcvbytes - pre->net.udprsz;
  534 }