"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 }