1 #include "common.h"
2 #include "ifinfo.h"
3 #include "iflist.h"
4 #include "dbsql.h"
5 #include "dbaccess.h"
6 #include "datacache.h"
7 #include "misc.h"
8 #include "cfg.h"
9 #include "ibw.h"
10 #include "fs.h"
11 #include "id.h"
12 #include "daemon.h"
13
daemonize(void)14 void daemonize(void)
15 {
16 int i;
17 char str[10];
18
19 i = (int)fork();
20
21 if (i < 0) { /* fork error */
22 perror("Error: fork");
23 exit(EXIT_FAILURE);
24 }
25 if (i > 0) { /* parent exits */
26 exit(EXIT_SUCCESS);
27 }
28 /* child (daemon) continues */
29
30 setsid(); /* obtain a new process group */
31
32 if (!verifylogaccess()) {
33 printf("Error: Unable to use logfile. Exiting.\n");
34 exit(EXIT_FAILURE);
35 }
36
37 /* lock / pid file */
38 pidfile = open(cfg.pidfile, O_RDWR | O_CREAT, 0644);
39 if (pidfile < 0) {
40 perror("Error: pidfile");
41 snprintf(errorstring, 1024, "opening pidfile \"%s\" failed (%s), exiting.", cfg.pidfile, strerror(errno));
42 printe(PT_Error);
43 exit(EXIT_FAILURE); /* can't open */
44 }
45 if (lockf(pidfile, F_TLOCK, 0) < 0) {
46 perror("Error: pidfile lock");
47 snprintf(errorstring, 1024, "pidfile \"%s\" lock failed (%s), exiting.", cfg.pidfile, strerror(errno));
48 printe(PT_Error);
49 exit(EXIT_FAILURE); /* can't lock */
50 }
51
52 /* close all descriptors except lock file */
53 for (i = getdtablesize(); i >= 0; --i) {
54 if (i != pidfile) {
55 close(i);
56 }
57 }
58
59 /* redirect standard i/o to null */
60 i = open("/dev/null", O_RDWR); /* stdin */
61
62 if (i < 0) {
63 perror("Error: open() /dev/null");
64 snprintf(errorstring, 1024, "open() /dev/null failed, exiting.");
65 printe(PT_Error);
66 exit(EXIT_FAILURE);
67 }
68
69 /* stdout */
70 if (dup(i) < 0) {
71 perror("Error: dup(stdout)");
72 snprintf(errorstring, 1024, "dup(stdout) failed, exiting.");
73 printe(PT_Error);
74 exit(EXIT_FAILURE);
75 }
76 /* stderr */
77 if (dup(i) < 0) {
78 perror("Error: dup(stderr)");
79 snprintf(errorstring, 1024, "dup(stderr) failed, exiting.");
80 printe(PT_Error);
81 exit(EXIT_FAILURE);
82 }
83
84 close(i);
85
86 umask(027); /* set newly created file permissions */
87
88 /* change running directory */
89 if (chdir("/") < 0) {
90 perror("Error: chdir(/)");
91 snprintf(errorstring, 1024, "directory change to / failed, exiting.");
92 printe(PT_Error);
93 exit(EXIT_FAILURE);
94 }
95
96 /* first instance continues */
97 snprintf(str, 10, "%d\n", (int)getpid());
98
99 /* record pid to pidfile */
100 if (write(pidfile, str, strlen(str)) < 0) {
101 perror("Error: write(pidfile)");
102 snprintf(errorstring, 1024, "writing to pidfile \"%s\" failed (%s), exiting.", cfg.pidfile, strerror(errno));
103 printe(PT_Error);
104 exit(EXIT_FAILURE);
105 }
106
107 signal(SIGCHLD, SIG_IGN); /* ignore child */
108 signal(SIGTSTP, SIG_IGN); /* ignore tty signals */
109 signal(SIGTTOU, SIG_IGN);
110 signal(SIGTTIN, SIG_IGN);
111 }
112
addinterfaces(DSTATE * s)113 unsigned int addinterfaces(DSTATE *s)
114 {
115 iflist *ifl = NULL, *ifl_iterator = NULL;
116 unsigned int count = 0;
117 uint32_t bwlimit = 0;
118
119 timeused_debug(__func__, 1);
120
121 /* get list of currently visible interfaces */
122 if (getiflist(&ifl, 0, 1) == 0) {
123 iflistfree(&ifl);
124 return 0;
125 }
126
127 if (ifl == NULL) {
128 return 0;
129 }
130
131 if (debug) {
132 printf("Interface list:");
133 ifl_iterator = ifl;
134 while (ifl_iterator != NULL) {
135 printf(" \"%s\"", ifl_iterator->interface);
136 ifl_iterator = ifl_iterator->next;
137 }
138 printf("\n");
139 }
140
141 ifl_iterator = ifl;
142 while (ifl_iterator != NULL) {
143 if (debug)
144 printf("Processing: \"%s\"\n", ifl_iterator->interface);
145
146 /* skip already known interfaces */
147 if (db_getinterfacecountbyname(ifl_iterator->interface)) {
148 if (debug)
149 printf("already known\n");
150 ifl_iterator = ifl_iterator->next;
151 continue;
152 }
153
154 /* create database for interface */
155 if (!db_addinterface(ifl_iterator->interface)) {
156 if (debug)
157 printf("add failed, skip\n");
158 ifl_iterator = ifl_iterator->next;
159 continue;
160 }
161
162 if (!getifinfo(ifl_iterator->interface)) {
163 if (debug)
164 printf("getifinfo failed, skip\n");
165 /* remove empty entry from database since the interface can't provide data */
166 db_removeinterface(ifl_iterator->interface);
167 ifl_iterator = ifl_iterator->next;
168 continue;
169 }
170
171 db_setcounters(ifl_iterator->interface, ifinfo.rx, ifinfo.tx);
172
173 count++;
174 ibwget(ifl_iterator->interface, &bwlimit);
175 if (bwlimit > 0) {
176 snprintf(errorstring, 1024, "Interface \"%s\" added with %" PRIu32 " Mbit bandwidth limit.", ifl_iterator->interface, bwlimit);
177 } else {
178 snprintf(errorstring, 1024, "Interface \"%s\" added. Warning: no bandwidth limit has been set.", ifl_iterator->interface);
179 }
180 printe(PT_Infoless);
181 if (s->running) {
182 datacache_add(&s->dcache, ifl_iterator->interface, 1);
183 }
184 ifl_iterator = ifl_iterator->next;
185 }
186
187 if (count && !s->running) {
188 if (count == 1) {
189 printf("-> %u new interface found.\n", count);
190 } else {
191 printf("-> %u new interfaces found.\n", count);
192 }
193
194 printf("Limits can be modified using the configuration file. See \"man vnstat.conf\".\n");
195 printf("Unwanted interfaces can be removed from monitoring with \"vnstat --remove\".\n");
196 }
197
198 iflistfree(&ifl);
199 timeused_debug(__func__, 0);
200 return count;
201 }
202
detectboot(DSTATE * s)203 void detectboot(DSTATE *s)
204 {
205 char buffer[32];
206 char *btime_buffer;
207 uint64_t current_btime, db_btime;
208
209 current_btime = getbtime();
210 btime_buffer = db_getinfo("btime");
211
212 if (current_btime == 0) {
213 return;
214 } else if (strlen(btime_buffer) == 0) {
215 snprintf(buffer, 32, "%" PRIu64 "", current_btime);
216 db_setinfo("btime", buffer, 1);
217 return;
218 }
219 db_btime = strtoull(btime_buffer, (char **)NULL, 0);
220
221 if (db_btime < (current_btime - (uint32_t)cfg.bvar)) {
222 s->bootdetected = 1;
223 if (debug)
224 printf("System has been booted, %" PRIu64 " < %" PRIu64 " - %d\n", db_btime, current_btime, cfg.bvar);
225 }
226
227 snprintf(buffer, 32, "%" PRIu64 "", current_btime);
228 db_setinfo("btime", buffer, 1);
229 }
230
debugtimestamp(void)231 void debugtimestamp(void)
232 {
233 time_t now;
234 char timestamp[22];
235
236 now = time(NULL);
237 strftime(timestamp, 22, DATETIMEFORMAT, localtime(&now));
238 printf("%s\n", timestamp);
239 }
240
initdstate(DSTATE * s)241 void initdstate(DSTATE *s)
242 {
243 db = NULL;
244 noexit = 1; /* disable exits in functions */
245 debug = 0; /* debug disabled by default */
246 disableprints = 0; /* let prints be visible */
247 s->rundaemon = 0; /* daemon disabled by default */
248
249 s->running = 0;
250 s->dbsaved = 1;
251 s->showhelp = 1;
252 s->sync = 0;
253 s->forcesave = 0;
254 s->noadd = 0;
255 s->initdb = 0;
256 s->iflisthash = 0;
257 s->cfgfile[0] = '\0';
258 s->user[0] = '\0';
259 s->group[0] = '\0';
260 s->prevdbupdate = 0;
261 s->prevdbsave = 0;
262 s->dbifcount = 0;
263 s->dodbsave = 0;
264 s->bootdetected = 0;
265 s->cleanuphour = getcurrenthour();
266 s->dbretrycount = 0;
267 s->dcache = NULL;
268 s->prevwaldbcheckpoint = time(NULL);
269 }
270
preparedatabase(DSTATE * s)271 void preparedatabase(DSTATE *s)
272 {
273 s->dbifcount = db_getinterfacecount();
274
275 if (s->dbifcount > 0 && !cfg.alwaysadd) {
276 s->dbifcount = 0;
277 return;
278 }
279
280 if (debug) {
281 printf("db if count: %" PRIu64 "\n", s->dbifcount);
282 }
283
284 if (s->noadd) {
285 printf("No interfaces found in database, exiting.\n");
286 exit(EXIT_FAILURE);
287 }
288
289 if (!spacecheck(cfg.dbdir)) {
290 printf("Error: Not enough free diskspace available, exiting.\n");
291 exit(EXIT_FAILURE);
292 }
293
294 if (s->dbifcount == 0) {
295 if (importlegacydbs(s) && !cfg.alwaysadd) {
296 s->dbifcount = 0;
297 return;
298 }
299 printf("No interfaces found in database, adding available interfaces...\n");
300 }
301
302 if (!addinterfaces(s) && s->dbifcount == 0) {
303 printf("Nothing to do, exiting.\n");
304 exit(EXIT_FAILURE);
305 }
306
307 /* set counter back to zero so that dbs will be cached later */
308 s->dbifcount = 0;
309 }
310
importlegacydbs(DSTATE * s)311 unsigned int importlegacydbs(DSTATE *s)
312 {
313 DIR *dir;
314 struct dirent *di;
315 unsigned int importcount = 0;
316
317 if ((dir = opendir(cfg.dbdir)) == NULL) {
318 printf("Error: Unable to open database directory \"%s\": %s\n", cfg.dbdir, strerror(errno));
319 printf("Make sure it exists and is at least read enabled for current user.\n");
320 printf("Exiting...\n");
321 exit(EXIT_FAILURE);
322 }
323
324 s->dbifcount = 0;
325 while ((di = readdir(dir))) {
326 if ((di->d_name[0] != '.') && (strncmp(di->d_name, DATABASEFILE, strlen(DATABASEFILE)) != 0)) {
327 /* ignore already known interfaces */
328 if (db_getinterfacecountbyname(di->d_name)) {
329 continue;
330 }
331 if (importlegacydb(di->d_name, cfg.dbdir)) {
332 importcount++;
333 }
334 }
335 }
336 closedir(dir);
337
338 s->dbifcount += importcount;
339 return importcount;
340 }
341
setsignaltraps(void)342 void setsignaltraps(void)
343 {
344 intsignal = 0;
345 if (signal(SIGINT, sighandler) == SIG_ERR) {
346 perror("Error: signal SIGINT");
347 exit(EXIT_FAILURE);
348 }
349 if (signal(SIGHUP, sighandler) == SIG_ERR) {
350 perror("Error: signal SIGHUP");
351 exit(EXIT_FAILURE);
352 }
353 if (signal(SIGTERM, sighandler) == SIG_ERR) {
354 perror("Error: signal SIGTERM");
355 exit(EXIT_FAILURE);
356 }
357 }
358
filldatabaselist(DSTATE * s)359 void filldatabaselist(DSTATE *s)
360 {
361 iflist *dbifl = NULL, *dbifl_iterator = NULL;
362
363 timeused_debug(__func__, 1);
364
365 if (db_getiflist(&dbifl) < 0) {
366 errorexitdaemon(s, 1);
367 }
368
369 dbifl_iterator = dbifl;
370
371 while (dbifl_iterator != NULL) {
372 if (debug) {
373 printf("\nProcessing interface \"%s\"...\n", dbifl_iterator->interface);
374 }
375 if (!datacache_add(&s->dcache, dbifl_iterator->interface, s->sync)) {
376 snprintf(errorstring, 1024, "Cache memory allocation failed (%s), exiting.", strerror(errno));
377 printe(PT_Error);
378 errorexitdaemon(s, 1);
379 }
380 s->dbifcount++;
381 dbifl_iterator = dbifl_iterator->next;
382 }
383
384 iflistfree(&dbifl);
385 s->sync = 0;
386
387 /* disable update interval check for one loop if database list was refreshed */
388 /* otherwise increase default update interval since there's nothing else to do */
389 if (s->dbifcount) {
390 s->updateinterval = 0;
391 intsignal = 42;
392 s->prevdbsave = s->current;
393 /* list monitored interfaces to log */
394 datacache_status(&s->dcache);
395 } else {
396 s->updateinterval = 120;
397 }
398 timeused_debug(__func__, 0);
399 }
400
adjustsaveinterval(DSTATE * s)401 void adjustsaveinterval(DSTATE *s)
402 {
403 /* modify active save interval if all interfaces are unavailable */
404 if (datacache_activecount(&s->dcache) > 0) {
405 s->saveinterval = cfg.saveinterval * 60;
406 } else {
407 s->saveinterval = cfg.offsaveinterval * 60;
408 }
409 }
410
checkdbsaveneed(DSTATE * s)411 void checkdbsaveneed(DSTATE *s)
412 {
413 if ((s->current - s->prevdbsave) >= (s->saveinterval) || s->forcesave) {
414 s->dodbsave = 1;
415 s->forcesave = 0;
416 s->prevdbsave = s->current - (s->current % s->saveinterval);
417 } else {
418 s->dodbsave = 0;
419 }
420 }
421
processdatacache(DSTATE * s)422 void processdatacache(DSTATE *s)
423 {
424 datacache *iterator = s->dcache;
425
426 timeused_debug(__func__, 1);
427
428 while (iterator != NULL) {
429
430 if (debug) {
431 printf("dc: processing %s (%d)...\n", iterator->interface, s->dodbsave);
432 }
433
434 if (!iterator->filled) {
435 if (!initcachevalues(s, &iterator)) {
436 iterator = iterator->next;
437 continue;
438 }
439 s->iflisthash = 0;
440 }
441
442 if (iterator->active) {
443 if (!getifinfo(iterator->interface)) {
444 /* disable interface since we can't access its data */
445 iterator->active = 0;
446 snprintf(errorstring, 1024, "Interface \"%s\" not available, disabling.", iterator->interface);
447 printe(PT_Info);
448 } else {
449 if (!processifinfo(s, &iterator)) {
450 iterator = iterator->next;
451 continue;
452 }
453 }
454 } else {
455 if (debug)
456 printf("dc: interface is disabled\n");
457 }
458
459 iterator = iterator->next;
460 }
461
462 if (s->bootdetected) {
463 s->bootdetected = 0;
464 }
465 timeused_debug(__func__, 0);
466
467 if (s->dodbsave) {
468 flushcachetodisk(s);
469 cleanremovedinterfaces(s);
470 if (s->cleanuphour != getcurrenthour()) {
471 db_removeoldentries();
472 s->cleanuphour = getcurrenthour();
473 }
474 if (cfg.rescanonsave) {
475 rescandatabaseforinterfaces(s);
476 }
477 s->dodbsave = 0;
478 }
479 }
480
initcachevalues(DSTATE * s,datacache ** dc)481 int initcachevalues(DSTATE *s, datacache **dc)
482 {
483 interfaceinfo info;
484
485 if (!db_getinterfaceinfo((*dc)->interface, &info)) {
486 return 0;
487 }
488
489 if (s->bootdetected) {
490 (*dc)->currx = 0;
491 (*dc)->curtx = 0;
492 } else {
493 (*dc)->currx = info.rxcounter;
494 (*dc)->curtx = info.txcounter;
495 }
496 (*dc)->updated = info.updated;
497 (*dc)->filled = 1;
498
499 return 1;
500 }
501
processifinfo(DSTATE * s,datacache ** dc)502 int processifinfo(DSTATE *s, datacache **dc)
503 {
504 uint64_t rxchange, txchange;
505 uint64_t maxtransfer;
506 uint32_t maxbw;
507 time_t interval;
508 short detected64bit = 0;
509
510 if ((*dc)->syncneeded) { /* if --sync was used during startup */
511 (*dc)->currx = ifinfo.rx;
512 (*dc)->curtx = ifinfo.tx;
513 (*dc)->syncneeded = 0;
514 return 1;
515 }
516
517 if ((*dc)->updated > ifinfo.timestamp) {
518 /* skip update if previous update is less than a day in the future */
519 /* otherwise exit with error message since the clock is probably messed */
520 if ((*dc)->updated > (ifinfo.timestamp + 86400)) {
521 snprintf(errorstring, 1024, "Interface \"%s\" has previous update date too much in the future, exiting. (%u / %u)", (*dc)->interface, (unsigned int)(*dc)->updated, (unsigned int)ifinfo.timestamp);
522 printe(PT_Error);
523 errorexitdaemon(s, 1);
524 }
525 return 0;
526 }
527
528 interval = ifinfo.timestamp - (*dc)->updated;
529 /* maximum configurable update interval is 5 minutes, limit here is set to 6 minutes (360 seconds) */
530 /* in order to be on the safe side and avoid discarding data in case there's some random extra delay */
531 if ((interval >= 1) && (interval <= 360)) {
532
533 if ((*dc)->currx > MAX32 || (*dc)->curtx > MAX32 || ifinfo.rx > MAX32 || ifinfo.tx > MAX32) {
534 ifinfo.is64bit = 1;
535 detected64bit = 1;
536 }
537
538 rxchange = countercalc(&(*dc)->currx, &ifinfo.rx, ifinfo.is64bit);
539 txchange = countercalc(&(*dc)->curtx, &ifinfo.tx, ifinfo.is64bit);
540
541 /* workaround for interface drivers using only 32-bit range with 64-bit interface counters, */
542 /* active only when automatic detection is enabled and all values are within 32-bit range */
543 if (cfg.is64bit == -2 || !detected64bit) {
544 if ((rxchange / (uint64_t)interval / 1024 / 1024 * 8) > BWMAX || (txchange / (uint64_t)interval / 1024 / 1024 * 8) > BWMAX) {
545 ifinfo.is64bit = 0;
546 rxchange = countercalc(&(*dc)->currx, &ifinfo.rx, 0);
547 txchange = countercalc(&(*dc)->curtx, &ifinfo.tx, 0);
548 }
549 }
550
551 /* get bandwidth limit for current interface */
552 ibwget((*dc)->interface, &maxbw);
553
554 if (maxbw > 0) {
555
556 /* calculate maximum possible transfer since last update based on set maximum rate */
557 /* and add 2% in order to be on the safe side */
558 maxtransfer = (uint64_t)(ceilf(((float)maxbw / (float)8) * (float)interval * (float)1.02)) * 1024 * 1024;
559
560 if (debug)
561 printf("interval: %" PRIu64 " maxbw: %" PRIu32 " maxrate: %" PRIu64 " rxc: %" PRIu64 " txc: %" PRIu64 "\n", (uint64_t)interval, maxbw, maxtransfer, rxchange, txchange);
562
563 /* sync counters if traffic is greater than set maximum */
564 if ((rxchange > maxtransfer) || (txchange > maxtransfer)) {
565 snprintf(errorstring, 1024, "Traffic rate for \"%s\" higher than set maximum %" PRIu32 " Mbit (%" PRIu64 "s->%" PRIu64 ", r%" PRIu64 " t%" PRIu64 ", 64bit:%d), syncing.", (*dc)->interface, maxbw, (uint64_t)interval, maxtransfer, rxchange, txchange, ifinfo.is64bit);
566 printe(PT_Info);
567 rxchange = txchange = 0;
568 }
569 }
570
571 if (rxchange || txchange || cfg.trafficlessentries) {
572 xferlog_add(&(*dc)->log, (*dc)->updated - ((*dc)->updated % 300), rxchange, txchange);
573 }
574 }
575 (*dc)->currx = ifinfo.rx;
576 (*dc)->curtx = ifinfo.tx;
577 (*dc)->updated = ifinfo.timestamp;
578
579 return 1;
580 }
581
flushcachetodisk(DSTATE * s)582 void flushcachetodisk(DSTATE *s)
583 {
584 int ret;
585 double used_secs = 0.0;
586 uint32_t logcount = 0;
587 datacache *iterator = s->dcache;
588 xferlog *logiterator;
589 interfaceinfo info;
590
591 timeused(__func__, 1);
592
593 if (!db_begintransaction()) {
594 handledatabaseerror(s);
595 return;
596 }
597
598 db_errcode = 0;
599 while (iterator != NULL) {
600 /* ignore interface no longer in database */
601 if (!db_getinterfacecountbyname(iterator->interface)) {
602 if (db_errcode) {
603 handledatabaseerror(s);
604 break;
605 } else {
606 iterator = iterator->next;
607 continue;
608 }
609 }
610
611 /* flush interface specific log to database */
612 logcount = 0;
613 logiterator = iterator->log;
614 while (logiterator != NULL) {
615 if (!db_addtraffic_dated(iterator->interface, logiterator->rx, logiterator->tx, (uint64_t)logiterator->timestamp)) {
616 handledatabaseerror(s);
617 break;
618 }
619 logiterator = logiterator->next;
620 logcount++;
621 }
622 if (db_errcode) {
623 break;
624 }
625
626 /* update database counters if new data was inserted */
627 if (logcount) {
628 if (!db_setcounters(iterator->interface, iterator->currx, iterator->curtx)) {
629 handledatabaseerror(s);
630 break;
631 }
632 }
633
634 if (!iterator->active && !logcount) {
635 /* throw away if interface hasn't seen any data and is disabled */
636 if (!iterator->currx && !iterator->curtx) {
637 ret = db_getinterfaceinfo(iterator->interface, &info);
638 if (!ret || (!info.rxtotal && !info.txtotal)) {
639 snprintf(errorstring, 1024, "Removing interface \"%s\" from database as it is disabled and has seen no data.", iterator->interface);
640 printe(PT_Info);
641 if (!db_removeinterface(iterator->interface)) {
642 if (db_errcode) {
643 handledatabaseerror(s);
644 }
645 }
646 break;
647 }
648 }
649 }
650
651 /* update interface timestamp in database */
652 if (!db_setupdated(iterator->interface, iterator->updated)) {
653 handledatabaseerror(s);
654 break;
655 }
656
657 /* update interface activity status in database */
658 if (!db_setactive(iterator->interface, iterator->active)) {
659 handledatabaseerror(s);
660 break;
661 }
662
663 iterator = iterator->next;
664 }
665
666 if (db_intransaction && !db_errcode) {
667 if (!db_committransaction()) {
668 handledatabaseerror(s);
669 } else {
670 /* clear xferlog now that everything is in database */
671 iterator = s->dcache;
672 while (iterator != NULL) {
673 xferlog_clear(&iterator->log);
674 iterator = iterator->next;
675 }
676 s->dbretrycount = 0;
677 }
678 } else {
679 db_rollbacktransaction();
680 }
681 used_secs = timeused(__func__, 0);
682 if (used_secs > SLOWDBWARNLIMIT) {
683 snprintf(errorstring, 1024, "Writing cached data to database took %.1f seconds.", used_secs);
684 printe(PT_Warning);
685 }
686 }
687
handledatabaseerror(DSTATE * s)688 void handledatabaseerror(DSTATE *s)
689 {
690 if (db_iserrcodefatal(db_errcode)) {
691 snprintf(errorstring, 1024, "Fatal database error detected, exiting.");
692 printe(PT_Error);
693 errorexitdaemon(s, 1);
694 } else {
695 if (db_isdiskfull(db_errcode)) {
696 snprintf(errorstring, 1024, "Disk is full, continuing with data caching.");
697 printe(PT_Error);
698 } else {
699 s->dbretrycount++;
700 if (s->dbretrycount > DBRETRYLIMIT) {
701 snprintf(errorstring, 1024, "Database error retry limit of %d reached, exiting.", DBRETRYLIMIT);
702 printe(PT_Error);
703 errorexitdaemon(s, 1);
704 }
705 }
706 }
707 }
708
cleanremovedinterfaces(DSTATE * s)709 void cleanremovedinterfaces(DSTATE *s)
710 {
711 datacache *iterator = s->dcache;
712 iflist *dbifl = NULL, *dbifl_iterator = NULL;
713
714 timeused_debug(__func__, 1);
715
716 while (iterator != NULL) {
717 if (!db_getinterfacecountbyname(iterator->interface)) {
718 iflistadd(&dbifl, iterator->interface, 0);
719 }
720 iterator = iterator->next;
721 }
722
723 if (dbifl != NULL) {
724 dbifl_iterator = dbifl;
725 while (dbifl_iterator != NULL) {
726 snprintf(errorstring, 1024, "Interface \"%s\" no longer in database, stopping monitoring.", dbifl_iterator->interface);
727 printe(PT_Info);
728 datacache_remove(&s->dcache, dbifl_iterator->interface);
729 if (s->dbifcount > 0) {
730 s->dbifcount--;
731 }
732 dbifl_iterator = dbifl_iterator->next;
733 }
734 datacache_status(&s->dcache);
735 iflistfree(&dbifl);
736 }
737 timeused_debug(__func__, 0);
738 }
739
rescandatabaseforinterfaces(DSTATE * s)740 void rescandatabaseforinterfaces(DSTATE *s)
741 {
742 short interface_already_monitored = 0;
743 uint64_t dbifcount = s->dbifcount;
744 datacache *iterator = NULL;
745 iflist *dbifl = NULL, *dbifl_iterator = NULL;
746
747 timeused_debug(__func__, 1);
748
749 if (db_getiflist(&dbifl) > 0 && dbifl != NULL) {
750 dbifl_iterator = dbifl;
751 while (dbifl_iterator != NULL) {
752 iterator = s->dcache;
753 interface_already_monitored = 0;
754 while (iterator != NULL) {
755 if (strcmp(iterator->interface, dbifl_iterator->interface) == 0) {
756 interface_already_monitored = 1;
757 break;
758 }
759 iterator = iterator->next;
760 }
761 if (!interface_already_monitored) {
762 snprintf(errorstring, 1024, "Interface \"%s\" found from database, starting monitoring.", dbifl_iterator->interface);
763 printe(PT_Info);
764 if (!datacache_add(&s->dcache, dbifl_iterator->interface, 1)) {
765 snprintf(errorstring, 1024, "Cache memory allocation failed (%s), exiting.", strerror(errno));
766 printe(PT_Error);
767 errorexitdaemon(s, 1);
768 }
769 s->dbifcount++;
770 }
771 dbifl_iterator = dbifl_iterator->next;
772 }
773 if (s->dbifcount != dbifcount) {
774 datacache_status(&s->dcache);
775 }
776 iflistfree(&dbifl);
777 }
778
779 timeused_debug(__func__, 0);
780 }
781
handleintsignals(DSTATE * s)782 void handleintsignals(DSTATE *s)
783 {
784 switch (intsignal) {
785
786 case SIGHUP:
787 snprintf(errorstring, 1024, "SIGHUP received, flushing data to disk and reloading config.");
788 printe(PT_Info);
789 flushcachetodisk(s);
790 datacache_clear(&s->dcache);
791 s->dbifcount = 0;
792 ibwflush();
793 db_close();
794 loadcfg(s->cfgfile, CT_Daemon);
795 ibwloadcfg(s->cfgfile);
796 if (!db_open_rw(1)) {
797 snprintf(errorstring, 1024, "Opening database after SIGHUP failed (%s), exiting.", strerror(errno));
798 printe(PT_Error);
799 if (s->rundaemon && !debug) {
800 close(pidfile);
801 unlink(cfg.pidfile);
802 }
803 exit(EXIT_FAILURE);
804 }
805 break;
806
807 case SIGINT:
808 snprintf(errorstring, 1024, "SIGINT received, exiting.");
809 printe(PT_Info);
810 s->running = 0;
811 break;
812
813 case SIGTERM:
814 snprintf(errorstring, 1024, "SIGTERM received, exiting.");
815 printe(PT_Info);
816 s->running = 0;
817 break;
818
819 /* from filldatabaselist() */
820 case 42:
821 break;
822
823 case 0:
824 break;
825
826 default:
827 snprintf(errorstring, 1024, "Unknown signal %d received, ignoring.", intsignal);
828 printe(PT_Info);
829 break;
830 }
831
832 intsignal = 0;
833 }
834
preparedirs(DSTATE * s)835 void preparedirs(DSTATE *s)
836 {
837 /* database directory */
838 if (mkpath(cfg.dbdir, 0775)) {
839 updatedirowner(cfg.dbdir, s->user, s->group);
840 }
841
842 if (!cfg.createdirs || !s->rundaemon) {
843 return;
844 }
845
846 /* possible pid/lock and log directory */
847 preparevnstatdir(cfg.pidfile, s->user, s->group);
848 if (cfg.uselogging == 1) {
849 preparevnstatdir(cfg.logfile, s->user, s->group);
850 }
851 }
852
datacache_status(datacache ** dc)853 void datacache_status(datacache **dc)
854 {
855 char buffer[1024], bwtemp[32];
856 unsigned int b = 0, count = 0;
857 uint32_t bwlimit = 0;
858 datacache *iterator = *dc;
859
860 timeused_debug(__func__, 1);
861
862 snprintf(buffer, 1024, "Monitoring (%d): ", datacache_count(dc));
863 b = (unsigned int)strlen(buffer) + 1;
864
865 while (iterator != NULL) {
866 if ((b + strlen(iterator->interface) + 32) < 1020) {
867 if (!ibwget(iterator->interface, &bwlimit) || bwlimit == 0) {
868 snprintf(bwtemp, 32, " (no limit) ");
869 } else {
870 snprintf(bwtemp, 32, " (%" PRIu32 " Mbit) ", bwlimit);
871 }
872 strcat(buffer, iterator->interface);
873 strcat(buffer, bwtemp);
874 b += strlen(iterator->interface) + strlen(bwtemp);
875 } else {
876 strcat(buffer, "...");
877 break;
878 }
879 count++;
880 iterator = iterator->next;
881 }
882
883 if (count) {
884 strncpy_nt(errorstring, buffer, 1024);
885 } else {
886 snprintf(errorstring, 1024, "Nothing to monitor");
887 }
888 printe(PT_Info);
889 timeused_debug(__func__, 0);
890 }
891
interfacechangecheck(DSTATE * s)892 void interfacechangecheck(DSTATE *s)
893 {
894 char *ifacelist, interface[32];
895 datacache *iterator = s->dcache;
896 uint32_t newhash;
897 int offset, found;
898
899 timeused_debug(__func__, 1);
900
901 /* get list of currently visible interfaces */
902 if (getifliststring(&ifacelist, 0) == 0) {
903 free(ifacelist);
904 s->iflisthash = 0;
905 return;
906 }
907
908 newhash = simplehash(ifacelist, (int)strlen(ifacelist));
909
910 if (s->iflisthash == newhash) {
911 free(ifacelist);
912 return;
913 }
914
915 /* search for changes if hash doesn't match */
916 if (debug) {
917 printf("ifacelist changed: '%s' %u <> %u\n", ifacelist, s->iflisthash, newhash);
918 }
919
920 while (iterator != NULL) {
921
922 if (!iterator->filled) {
923 iterator = iterator->next;
924 continue;
925 }
926
927 found = offset = 0;
928
929 while (offset <= (int)strlen(ifacelist)) {
930 sscanf(ifacelist + offset, "%31s", interface);
931 if (strcmp(iterator->interface, interface) == 0) {
932 found = 1;
933 break;
934 }
935 offset += (int)strlen(interface) + 1;
936 }
937
938 if (iterator->active == 1 && found == 0) {
939 iterator->active = 0;
940 iterator->currx = 0;
941 iterator->curtx = 0;
942 if (cfg.savestatus) {
943 s->forcesave = 1;
944 }
945 snprintf(errorstring, 1024, "Interface \"%s\" disabled.", iterator->interface);
946 printe(PT_Info);
947 } else if (iterator->active == 0 && found == 1) {
948 iterator->active = 1;
949 iterator->currx = 0;
950 iterator->curtx = 0;
951 if (cfg.savestatus) {
952 s->forcesave = 1;
953 }
954 snprintf(errorstring, 1024, "Interface \"%s\" enabled.", iterator->interface);
955 printe(PT_Info);
956 }
957
958 iterator = iterator->next;
959 }
960 free(ifacelist);
961
962 s->iflisthash = newhash;
963 timeused_debug(__func__, 0);
964 }
965
simplehash(const char * data,int len)966 uint32_t simplehash(const char *data, int len)
967 {
968 uint32_t hash;
969
970 if (len <= 0 || data == NULL) {
971 return 0;
972 }
973
974 hash = (uint32_t)len;
975
976 for (len--; len >= 0; len--) {
977 if (len > 0) {
978 hash += (uint32_t)data[len] * (uint32_t)len;
979 } else {
980 hash += (uint32_t)data[len];
981 }
982 }
983
984 return hash;
985 }
986
errorexitdaemon(DSTATE * s,const int fataldberror)987 __attribute__((noreturn)) void errorexitdaemon(DSTATE *s, const int fataldberror)
988 {
989 if (!fataldberror) {
990 flushcachetodisk(s);
991 }
992 db_close();
993
994 datacache_clear(&s->dcache);
995 ibwflush();
996
997 if (s->rundaemon && !debug) {
998 close(pidfile);
999 unlink(cfg.pidfile);
1000 }
1001
1002 exit(EXIT_FAILURE);
1003 }
1004
getcurrenthour(void)1005 short getcurrenthour(void)
1006 {
1007 int ret = 0;
1008 time_t current;
1009 struct tm *stm;
1010 char buffer[4];
1011
1012 current = time(NULL);
1013 stm = localtime(¤t);
1014 if (stm == NULL) {
1015 return 0;
1016 }
1017
1018 if (!strftime(buffer, sizeof(buffer), "%H", stm)) {
1019 return 0;
1020 }
1021
1022 ret = atoi(buffer);
1023 if (ret > 23 || ret < 0) {
1024 ret = 0;
1025 }
1026
1027 return (short)ret;
1028 }
1029
waittimesync(DSTATE * s)1030 int waittimesync(DSTATE *s)
1031 {
1032 datacache *iterator = s->dcache;
1033 char timestamp[22], timestamp2[22];
1034
1035 if (cfg.timesyncwait == 0) {
1036 return 0;
1037 }
1038
1039 if (s->prevdbupdate == 0 && s->prevdbsave == 0) {
1040 while (iterator != NULL) {
1041 if (debug) {
1042 printf("w: processing %s...\n", iterator->interface);
1043 }
1044
1045 if (!iterator->filled) {
1046 if (!initcachevalues(s, &iterator)) {
1047 iterator = iterator->next;
1048 continue;
1049 }
1050 s->iflisthash = 0;
1051 }
1052
1053 if (debug) {
1054 strftime(timestamp, 22, DATETIMEFORMAT, localtime(&iterator->updated));
1055 printf("w: has %s\n", timestamp);
1056 }
1057 if (iterator->updated > s->prevdbsave) {
1058 s->prevdbsave = iterator->updated;
1059 }
1060 iterator = iterator->next;
1061 }
1062 if (s->prevdbsave == 0) {
1063 snprintf(errorstring, 1024, "Couldn't define when database was last updated. Continuing, some errors may follow.");
1064 printe(PT_Info);
1065 return 0;
1066 }
1067 }
1068
1069 s->current = time(NULL);
1070
1071 if (debug) {
1072 strftime(timestamp, 22, DATETIMEFORMAT, localtime(&s->current));
1073 printf("current time: %s\n", timestamp);
1074 strftime(timestamp2, 22, DATETIMEFORMAT, localtime(&s->prevdbsave));
1075 printf("latest db update: %s\n", timestamp2);
1076 }
1077
1078 if (s->current < s->prevdbsave) {
1079 if (s->prevdbupdate == 0) {
1080 s->prevdbupdate = s->current;
1081 strftime(timestamp, 22, DATETIMEFORMAT, localtime(&s->current));
1082 strftime(timestamp2, 22, DATETIMEFORMAT, localtime(&s->prevdbsave));
1083 snprintf(errorstring, 1024, "Latest database update is in the future (db: %s > now: %s). Giving the system clock up to %d minutes to sync before continuing.", timestamp2, timestamp, cfg.timesyncwait);
1084 printe(PT_Info);
1085 }
1086 if (s->current - s->prevdbupdate >= cfg.timesyncwait * 60) {
1087 strftime(timestamp, 22, DATETIMEFORMAT, localtime(&s->current));
1088 strftime(timestamp2, 22, DATETIMEFORMAT, localtime(&s->prevdbsave));
1089 snprintf(errorstring, 1024, "Latest database update is still in the future (db: %s > now: %s), continuing. Some errors may follow.", timestamp2, timestamp);
1090 printe(PT_Info);
1091 return 0;
1092 }
1093 } else {
1094 if (s->prevdbupdate != 0) {
1095 strftime(timestamp, 22, DATETIMEFORMAT, localtime(&s->current));
1096 strftime(timestamp2, 22, DATETIMEFORMAT, localtime(&s->prevdbsave));
1097 snprintf(errorstring, 1024, "Latest database update is no longer in the future (db: %s <= now: %s), continuing.", timestamp2, timestamp);
1098 printe(PT_Info);
1099 }
1100 s->prevdbsave = s->current;
1101 s->prevdbupdate = 0;
1102 if (debug) {
1103 printf("time sync ok\n\n");
1104 }
1105 return 0;
1106 }
1107
1108 return 1;
1109 }
1110