]> git.proxmox.com Git - mirror_iproute2.git/blame - misc/nstat.c
ss: show header for --processes/-p
[mirror_iproute2.git] / misc / nstat.c
CommitLineData
aba5acdf
SH
1/*
2 * nstat.c handy utility to read counters /proc/net/netstat and snmp
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 */
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <fcntl.h>
16#include <string.h>
17#include <errno.h>
18#include <time.h>
19#include <sys/time.h>
20#include <fnmatch.h>
21#include <sys/file.h>
22#include <sys/socket.h>
23#include <sys/un.h>
24#include <sys/poll.h>
25#include <sys/wait.h>
26#include <sys/stat.h>
27#include <signal.h>
28#include <math.h>
404582c8 29#include <getopt.h>
aba5acdf 30
fcc16c22 31#include <json_writer.h>
aba5acdf 32#include <SNAPSHOT.h>
62000e51 33#include "utils.h"
aba5acdf 34
acd1e437
SH
35int dump_zeros;
36int reset_history;
37int ignore_history;
38int no_output;
39int json_output;
acd1e437
SH
40int no_update;
41int scan_interval;
42int time_constant;
aba5acdf
SH
43double W;
44char **patterns;
45int npatterns;
46
47char info_source[128];
48int source_mismatch;
49
4ffc44ca 50static int generic_proc_open(const char *env, char *name)
aba5acdf
SH
51{
52 char store[128];
53 char *p = getenv(env);
acd1e437 54
aba5acdf
SH
55 if (!p) {
56 p = getenv("PROC_ROOT") ? : "/proc";
57 snprintf(store, sizeof(store)-1, "%s/%s", p, name);
58 p = store;
59 }
4ffc44ca 60 return open(p, O_RDONLY);
aba5acdf
SH
61}
62
d1f28cf1 63static int net_netstat_open(void)
aba5acdf
SH
64{
65 return generic_proc_open("PROC_NET_NETSTAT", "net/netstat");
66}
67
d1f28cf1 68static int net_snmp_open(void)
aba5acdf
SH
69{
70 return generic_proc_open("PROC_NET_SNMP", "net/snmp");
71}
72
d1f28cf1 73static int net_snmp6_open(void)
aba5acdf
SH
74{
75 return generic_proc_open("PROC_NET_SNMP6", "net/snmp6");
76}
77
45a0dc16
HL
78static int net_sctp_snmp_open(void)
79{
80 return generic_proc_open("PROC_NET_SCTP_SNMP", "net/sctp/snmp");
81}
82
acd1e437 83struct nstat_ent {
aba5acdf
SH
84 struct nstat_ent *next;
85 char *id;
86 unsigned long long val;
aba5acdf
SH
87 double rate;
88};
89
90struct nstat_ent *kern_db;
91struct nstat_ent *hist_db;
92
d1f28cf1
SH
93static const char *useless_numbers[] = {
94 "IpForwarding", "IpDefaultTTL",
95 "TcpRtoAlgorithm", "TcpRtoMin", "TcpRtoMax",
96 "TcpMaxConn", "TcpCurrEstab"
aba5acdf
SH
97};
98
d1f28cf1 99static int useless_number(const char *id)
aba5acdf
SH
100{
101 int i;
acd1e437 102
62000e51 103 for (i = 0; i < ARRAY_SIZE(useless_numbers); i++)
aba5acdf
SH
104 if (strcmp(id, useless_numbers[i]) == 0)
105 return 1;
106 return 0;
107}
108
d1f28cf1 109static int match(const char *id)
aba5acdf
SH
110{
111 int i;
112
113 if (npatterns == 0)
114 return 1;
115
acd1e437 116 for (i = 0; i < npatterns; i++) {
aba5acdf
SH
117 if (!fnmatch(patterns[i], id, 0))
118 return 1;
119 }
120 return 0;
121}
122
d1f28cf1 123static void load_good_table(FILE *fp)
aba5acdf
SH
124{
125 char buf[4096];
126 struct nstat_ent *db = NULL;
127 struct nstat_ent *n;
128
129 while (fgets(buf, sizeof(buf), fp) != NULL) {
130 int nr;
131 unsigned long long val;
132 double rate;
b482ffa6 133 char idbuf[sizeof(buf)];
acd1e437 134
aba5acdf
SH
135 if (buf[0] == '#') {
136 buf[strlen(buf)-1] = 0;
137 if (info_source[0] && strcmp(info_source, buf+1))
138 source_mismatch = 1;
b482ffa6 139 info_source[0] = 0;
140 strncat(info_source, buf+1, sizeof(info_source)-1);
aba5acdf
SH
141 continue;
142 }
b482ffa6 143 /* idbuf is as big as buf, so this is safe */
aba5acdf
SH
144 nr = sscanf(buf, "%s%llu%lg", idbuf, &val, &rate);
145 if (nr < 2)
146 abort();
147 if (nr < 3)
148 rate = 0;
149 if (useless_number(idbuf))
150 continue;
151 if ((n = malloc(sizeof(*n))) == NULL)
152 abort();
153 n->id = strdup(idbuf);
aba5acdf
SH
154 n->val = val;
155 n->rate = rate;
156 n->next = db;
157 db = n;
158 }
159
160 while (db) {
161 n = db;
162 db = db->next;
163 n->next = kern_db;
164 kern_db = n;
165 }
166}
167
d4717914
ED
168static int count_spaces(const char *line)
169{
170 int count = 0;
171 char c;
172
173 while ((c = *line++) != 0)
174 count += c == ' ' || c == '\n';
175 return count;
176}
aba5acdf 177
d1f28cf1 178static void load_ugly_table(FILE *fp)
aba5acdf 179{
72cdb77d
ED
180 char *buf = NULL;
181 size_t buflen = 0;
182 ssize_t nread;
aba5acdf
SH
183 struct nstat_ent *db = NULL;
184 struct nstat_ent *n;
185
72cdb77d 186 while ((nread = getline(&buf, &buflen, fp)) != -1) {
da8034a0 187 char idbuf[4096];
aba5acdf
SH
188 int off;
189 char *p;
d4717914 190 int count1, count2, skip = 0;
aba5acdf
SH
191
192 p = strchr(buf, ':');
193 if (!p)
194 abort();
d4717914 195 count1 = count_spaces(buf);
aba5acdf 196 *p = 0;
b482ffa6 197 idbuf[0] = 0;
198 strncat(idbuf, buf, sizeof(idbuf) - 1);
199 off = p - buf;
aba5acdf
SH
200 p += 2;
201
202 while (*p) {
203 char *next;
acd1e437 204
aba5acdf
SH
205 if ((next = strchr(p, ' ')) != NULL)
206 *next++ = 0;
207 else if ((next = strchr(p, '\n')) != NULL)
208 *next++ = 0;
b482ffa6 209 if (off < sizeof(idbuf)) {
210 idbuf[off] = 0;
211 strncat(idbuf, p, sizeof(idbuf) - off - 1);
212 }
aba5acdf
SH
213 n = malloc(sizeof(*n));
214 if (!n)
215 abort();
216 n->id = strdup(idbuf);
217 n->rate = 0;
218 n->next = db;
219 db = n;
220 p = next;
221 }
222 n = db;
72cdb77d
ED
223 nread = getline(&buf, &buflen, fp);
224 if (nread == -1)
aba5acdf 225 abort();
d4717914
ED
226 count2 = count_spaces(buf);
227 if (count2 > count1)
228 skip = count2 - count1;
aba5acdf
SH
229 do {
230 p = strrchr(buf, ' ');
231 if (!p)
232 abort();
233 *p = 0;
cdb2227e 234 if (sscanf(p+1, "%llu", &n->val) != 1)
aba5acdf 235 abort();
aba5acdf 236 /* Trick to skip "dummy" trailing ICMP MIB in 2.4 */
d4717914
ED
237 if (skip)
238 skip--;
aba5acdf
SH
239 else
240 n = n->next;
241 } while (p > buf + off + 2);
242 }
72cdb77d 243 free(buf);
aba5acdf
SH
244
245 while (db) {
246 n = db;
247 db = db->next;
248 if (useless_number(n->id)) {
249 free(n->id);
250 free(n);
251 } else {
252 n->next = kern_db;
253 kern_db = n;
254 }
255 }
256}
257
45a0dc16
HL
258static void load_sctp_snmp(void)
259{
260 FILE *fp = fdopen(net_sctp_snmp_open(), "r");
261
262 if (fp) {
263 load_good_table(fp);
264 fclose(fp);
265 }
266}
267
d1f28cf1 268static void load_snmp(void)
aba5acdf
SH
269{
270 FILE *fp = fdopen(net_snmp_open(), "r");
acd1e437 271
aba5acdf
SH
272 if (fp) {
273 load_ugly_table(fp);
274 fclose(fp);
275 }
276}
277
d1f28cf1 278static void load_snmp6(void)
aba5acdf
SH
279{
280 FILE *fp = fdopen(net_snmp6_open(), "r");
acd1e437 281
aba5acdf
SH
282 if (fp) {
283 load_good_table(fp);
284 fclose(fp);
285 }
286}
287
d1f28cf1 288static void load_netstat(void)
aba5acdf
SH
289{
290 FILE *fp = fdopen(net_netstat_open(), "r");
acd1e437 291
aba5acdf
SH
292 if (fp) {
293 load_ugly_table(fp);
294 fclose(fp);
295 }
296}
297
d48ed3f4 298
d1f28cf1 299static void dump_kern_db(FILE *fp, int to_hist)
aba5acdf 300{
fcc16c22 301 json_writer_t *jw = json_output ? jsonw_new(fp) : NULL;
aba5acdf 302 struct nstat_ent *n, *h;
d48ed3f4 303
aba5acdf 304 h = hist_db;
fcc16c22 305 if (jw) {
d721a145 306 jsonw_start_object(jw);
fcc16c22
SH
307 jsonw_pretty(jw, pretty);
308 jsonw_name(jw, info_source);
309 jsonw_start_object(jw);
310 } else
d48ed3f4 311 fprintf(fp, "#%s\n", info_source);
404582c8 312
acd1e437 313 for (n = kern_db; n; n = n->next) {
aba5acdf 314 unsigned long long val = n->val;
acd1e437 315
aba5acdf
SH
316 if (!dump_zeros && !val && !n->rate)
317 continue;
318 if (!match(n->id)) {
319 struct nstat_ent *h1;
acd1e437 320
aba5acdf
SH
321 if (!to_hist)
322 continue;
323 for (h1 = h; h1; h1 = h1->next) {
324 if (strcmp(h1->id, n->id) == 0) {
325 val = h1->val;
326 h = h1->next;
327 break;
328 }
329 }
330 }
d48ed3f4 331
fcc16c22
SH
332 if (jw)
333 jsonw_uint_field(jw, n->id, val);
334 else
d48ed3f4 335 fprintf(fp, "%-32s%-16llu%6.1f\n", n->id, val, n->rate);
aba5acdf 336 }
fcc16c22
SH
337
338 if (jw) {
d721a145
AK
339 jsonw_end_object(jw);
340
fcc16c22
SH
341 jsonw_end_object(jw);
342 jsonw_destroy(&jw);
343 }
aba5acdf
SH
344}
345
d1f28cf1 346static void dump_incr_db(FILE *fp)
aba5acdf 347{
fcc16c22 348 json_writer_t *jw = json_output ? jsonw_new(fp) : NULL;
aba5acdf 349 struct nstat_ent *n, *h;
d48ed3f4 350
aba5acdf 351 h = hist_db;
fcc16c22 352 if (jw) {
d721a145 353 jsonw_start_object(jw);
fcc16c22
SH
354 jsonw_pretty(jw, pretty);
355 jsonw_name(jw, info_source);
356 jsonw_start_object(jw);
357 } else
d48ed3f4
SH
358 fprintf(fp, "#%s\n", info_source);
359
acd1e437 360 for (n = kern_db; n; n = n->next) {
aba5acdf
SH
361 int ovfl = 0;
362 unsigned long long val = n->val;
363 struct nstat_ent *h1;
acd1e437 364
aba5acdf
SH
365 for (h1 = h; h1; h1 = h1->next) {
366 if (strcmp(h1->id, n->id) == 0) {
367 if (val < h1->val) {
368 ovfl = 1;
369 val = h1->val;
370 }
371 val -= h1->val;
372 h = h1->next;
373 break;
374 }
375 }
376 if (!dump_zeros && !val && !n->rate)
377 continue;
378 if (!match(n->id))
379 continue;
d48ed3f4 380
fcc16c22
SH
381 if (jw)
382 jsonw_uint_field(jw, n->id, val);
383 else
d48ed3f4
SH
384 fprintf(fp, "%-32s%-16llu%6.1f%s\n", n->id, val,
385 n->rate, ovfl?" (overflow)":"");
aba5acdf 386 }
fcc16c22
SH
387
388 if (jw) {
d721a145
AK
389 jsonw_end_object(jw);
390
fcc16c22
SH
391 jsonw_end_object(jw);
392 jsonw_destroy(&jw);
393 }
aba5acdf
SH
394}
395
396static int children;
397
d1f28cf1 398static void sigchild(int signo)
aba5acdf
SH
399{
400}
401
d1f28cf1 402static void update_db(int interval)
aba5acdf
SH
403{
404 struct nstat_ent *n, *h;
405
406 n = kern_db;
407 kern_db = NULL;
408
409 load_netstat();
410 load_snmp6();
411 load_snmp();
45a0dc16 412 load_sctp_snmp();
aba5acdf
SH
413
414 h = kern_db;
415 kern_db = n;
416
417 for (n = kern_db; n; n = n->next) {
418 struct nstat_ent *h1;
acd1e437 419
aba5acdf
SH
420 for (h1 = h; h1; h1 = h1->next) {
421 if (strcmp(h1->id, n->id) == 0) {
422 double sample;
cdb2227e
ED
423 unsigned long long incr = h1->val - n->val;
424
425 n->val = h1->val;
426 sample = (double)incr * 1000.0 / interval;
aba5acdf
SH
427 if (interval >= scan_interval) {
428 n->rate += W*(sample-n->rate);
429 } else if (interval >= 1000) {
430 if (interval >= time_constant) {
431 n->rate = sample;
432 } else {
433 double w = W*(double)interval/scan_interval;
acd1e437 434
aba5acdf
SH
435 n->rate += w*(sample-n->rate);
436 }
437 }
438
439 while (h != h1) {
440 struct nstat_ent *tmp = h;
acd1e437 441
aba5acdf
SH
442 h = h->next;
443 free(tmp->id);
444 free(tmp);
445 };
446 h = h1->next;
447 free(h1->id);
448 free(h1);
449 break;
450 }
451 }
452 }
453}
454
acd1e437 455#define T_DIFF(a, b) (((a).tv_sec-(b).tv_sec)*1000 + ((a).tv_usec-(b).tv_usec)/1000)
aba5acdf
SH
456
457
d1f28cf1 458static void server_loop(int fd)
aba5acdf 459{
737f15f6 460 struct timeval snaptime = { 0 };
aba5acdf 461 struct pollfd p;
acd1e437 462
aba5acdf
SH
463 p.fd = fd;
464 p.events = p.revents = POLLIN;
ae665a52 465
aba5acdf
SH
466 sprintf(info_source, "%d.%lu sampling_interval=%d time_const=%d",
467 getpid(), (unsigned long)random(), scan_interval/1000, time_constant/1000);
468
469 load_netstat();
470 load_snmp6();
471 load_snmp();
45a0dc16 472 load_sctp_snmp();
aba5acdf
SH
473
474 for (;;) {
475 int status;
dd81ee04 476 time_t tdiff;
aba5acdf 477 struct timeval now;
acd1e437 478
aba5acdf
SH
479 gettimeofday(&now, NULL);
480 tdiff = T_DIFF(now, snaptime);
481 if (tdiff >= scan_interval) {
482 update_db(tdiff);
483 snaptime = now;
484 tdiff = 0;
485 }
dd81ee04 486 if (poll(&p, 1, scan_interval - tdiff) > 0
aba5acdf
SH
487 && (p.revents&POLLIN)) {
488 int clnt = accept(fd, NULL, NULL);
acd1e437 489
aba5acdf
SH
490 if (clnt >= 0) {
491 pid_t pid;
acd1e437 492
aba5acdf
SH
493 if (children >= 5) {
494 close(clnt);
495 } else if ((pid = fork()) != 0) {
acd1e437 496 if (pid > 0)
aba5acdf
SH
497 children++;
498 close(clnt);
499 } else {
500 FILE *fp = fdopen(clnt, "w");
acd1e437 501
dd81ee04 502 if (fp)
aba5acdf 503 dump_kern_db(fp, 0);
aba5acdf
SH
504 exit(0);
505 }
506 }
507 }
508 while (children && waitpid(-1, &status, WNOHANG) > 0)
509 children--;
510 }
511}
512
d1f28cf1 513static int verify_forging(int fd)
aba5acdf
SH
514{
515 struct ucred cred;
737f15f6
SH
516 socklen_t olen = sizeof(cred);
517
acd1e437 518 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, (void *)&cred, &olen) ||
aba5acdf
SH
519 olen < sizeof(cred))
520 return -1;
521 if (cred.uid == getuid() || cred.uid == 0)
522 return 0;
523 return -1;
524}
525
526static void usage(void) __attribute__((noreturn));
527
528static void usage(void)
529{
530 fprintf(stderr,
8589eb4e
MC
531 "Usage: nstat [OPTION] [ PATTERN [ PATTERN ] ]\n"
532 " -h, --help this message\n"
533 " -a, --ignore ignore history\n"
534 " -d, --scan=SECS sample every statistics every SECS\n"
535 " -j, --json format output in JSON\n"
536 " -n, --nooutput do history only\n"
537 " -p, --pretty pretty print\n"
538 " -r, --reset reset history\n"
539 " -s, --noupdate don't update history\n"
540 " -t, --interval=SECS report average over the last SECS\n"
541 " -V, --version output version information\n"
542 " -z, --zeros show entries with zero activity\n");
aba5acdf
SH
543 exit(-1);
544}
545
404582c8
SH
546static const struct option longopts[] = {
547 { "help", 0, 0, 'h' },
548 { "ignore", 0, 0, 'a' },
549 { "scan", 1, 0, 'd'},
550 { "nooutput", 0, 0, 'n' },
551 { "json", 0, 0, 'j' },
552 { "reset", 0, 0, 'r' },
553 { "noupdate", 0, 0, 's' },
fcc16c22 554 { "pretty", 0, 0, 'p' },
404582c8
SH
555 { "interval", 1, 0, 't' },
556 { "version", 0, 0, 'V' },
557 { "zeros", 0, 0, 'z' },
558 { 0 }
559};
aba5acdf
SH
560
561int main(int argc, char *argv[])
562{
896ebd6c 563 char *hist_name;
aba5acdf
SH
564 struct sockaddr_un sun;
565 FILE *hist_fp = NULL;
566 int ch;
567 int fd;
568
fcc16c22 569 while ((ch = getopt_long(argc, argv, "h?vVzrnasd:t:jp",
404582c8 570 longopts, NULL)) != EOF) {
acd1e437 571 switch (ch) {
aba5acdf
SH
572 case 'z':
573 dump_zeros = 1;
574 break;
575 case 'r':
576 reset_history = 1;
577 break;
578 case 'a':
579 ignore_history = 1;
580 break;
581 case 's':
582 no_update = 1;
583 break;
584 case 'n':
585 no_output = 1;
586 break;
587 case 'd':
588 scan_interval = 1000*atoi(optarg);
589 break;
590 case 't':
591 if (sscanf(optarg, "%d", &time_constant) != 1 ||
592 time_constant <= 0) {
593 fprintf(stderr, "nstat: invalid time constant divisor\n");
594 exit(-1);
595 }
596 break;
d48ed3f4
SH
597 case 'j':
598 json_output = 1;
599 break;
fcc16c22
SH
600 case 'p':
601 pretty = 1;
602 break;
aba5acdf
SH
603 case 'v':
604 case 'V':
605 printf("nstat utility, iproute2-ss%s\n", SNAPSHOT);
606 exit(0);
607 case 'h':
608 case '?':
609 default:
610 usage();
611 }
612 }
613
614 argc -= optind;
615 argv += optind;
616
617 sun.sun_family = AF_UNIX;
618 sun.sun_path[0] = 0;
619 sprintf(sun.sun_path+1, "nstat%d", getuid());
620
621 if (scan_interval > 0) {
622 if (time_constant == 0)
623 time_constant = 60;
624 time_constant *= 1000;
625 W = 1 - 1/exp(log(10)*(double)scan_interval/time_constant);
626 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
627 perror("nstat: socket");
628 exit(-1);
629 }
acd1e437 630 if (bind(fd, (struct sockaddr *)&sun, 2+1+strlen(sun.sun_path+1)) < 0) {
aba5acdf
SH
631 perror("nstat: bind");
632 exit(-1);
633 }
634 if (listen(fd, 5) < 0) {
635 perror("nstat: listen");
636 exit(-1);
637 }
a7a9ddbb
MF
638 if (daemon(0, 0)) {
639 perror("nstat: daemon");
640 exit(-1);
641 }
aba5acdf
SH
642 signal(SIGPIPE, SIG_IGN);
643 signal(SIGCHLD, sigchild);
644 server_loop(fd);
645 exit(0);
646 }
647
648 patterns = argv;
649 npatterns = argc;
650
896ebd6c
SH
651 if ((hist_name = getenv("NSTAT_HISTORY")) == NULL) {
652 hist_name = malloc(128);
aba5acdf 653 sprintf(hist_name, "/tmp/.nstat.u%d", getuid());
896ebd6c 654 }
aba5acdf
SH
655
656 if (reset_history)
657 unlink(hist_name);
658
659 if (!ignore_history || !no_update) {
660 struct stat stb;
661
662 fd = open(hist_name, O_RDWR|O_CREAT|O_NOFOLLOW, 0600);
663 if (fd < 0) {
664 perror("nstat: open history file");
665 exit(-1);
666 }
667 if ((hist_fp = fdopen(fd, "r+")) == NULL) {
668 perror("nstat: fdopen history file");
669 exit(-1);
670 }
671 if (flock(fileno(hist_fp), LOCK_EX)) {
672 perror("nstat: flock history file");
673 exit(-1);
674 }
675 if (fstat(fileno(hist_fp), &stb) != 0) {
676 perror("nstat: fstat history file");
677 exit(-1);
678 }
679 if (stb.st_nlink != 1 || stb.st_uid != getuid()) {
680 fprintf(stderr, "nstat: something is so wrong with history file, that I prefer not to proceed.\n");
681 exit(-1);
682 }
683 if (!ignore_history) {
684 FILE *tfp;
9a230771 685 long uptime = -1;
acd1e437 686
aba5acdf
SH
687 if ((tfp = fopen("/proc/uptime", "r")) != NULL) {
688 if (fscanf(tfp, "%ld", &uptime) != 1)
689 uptime = -1;
690 fclose(tfp);
691 }
692 if (uptime >= 0 && time(NULL) >= stb.st_mtime+uptime) {
693 fprintf(stderr, "nstat: history is aged out, resetting\n");
d572ed4d
PS
694 if (ftruncate(fileno(hist_fp), 0) < 0)
695 perror("nstat: ftruncate");
aba5acdf
SH
696 }
697 }
698
699 load_good_table(hist_fp);
700
701 hist_db = kern_db;
702 kern_db = NULL;
703 }
704
705 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0 &&
acd1e437 706 (connect(fd, (struct sockaddr *)&sun, 2+1+strlen(sun.sun_path+1)) == 0
aba5acdf 707 || (strcpy(sun.sun_path+1, "nstat0"),
acd1e437 708 connect(fd, (struct sockaddr *)&sun, 2+1+strlen(sun.sun_path+1)) == 0))
aba5acdf
SH
709 && verify_forging(fd) == 0) {
710 FILE *sfp = fdopen(fd, "r");
acd1e437 711
6d02518f
PS
712 if (!sfp) {
713 fprintf(stderr, "nstat: fdopen failed: %s\n",
714 strerror(errno));
715 close(fd);
716 } else {
717 load_good_table(sfp);
718 if (hist_db && source_mismatch) {
719 fprintf(stderr, "nstat: history is stale, ignoring it.\n");
720 hist_db = NULL;
721 }
722 fclose(sfp);
aba5acdf 723 }
aba5acdf
SH
724 } else {
725 if (fd >= 0)
726 close(fd);
727 if (hist_db && info_source[0] && strcmp(info_source, "kernel")) {
728 fprintf(stderr, "nstat: history is stale, ignoring it.\n");
729 hist_db = NULL;
730 info_source[0] = 0;
731 }
732 load_netstat();
733 load_snmp6();
734 load_snmp();
45a0dc16 735 load_sctp_snmp();
aba5acdf
SH
736 if (info_source[0] == 0)
737 strcpy(info_source, "kernel");
738 }
739
740 if (!no_output) {
741 if (ignore_history || hist_db == NULL)
742 dump_kern_db(stdout, 0);
743 else
744 dump_incr_db(stdout);
745 }
746 if (!no_update) {
d572ed4d
PS
747 if (ftruncate(fileno(hist_fp), 0) < 0)
748 perror("nstat: ftruncate");
aba5acdf 749 rewind(hist_fp);
404582c8
SH
750
751 json_output = 0;
aba5acdf 752 dump_kern_db(hist_fp, 1);
404582c8 753 fclose(hist_fp);
aba5acdf
SH
754 }
755 exit(0);
756}