1 /* SPDX-License-Identifier: LGPL-2.1-only */
2
3 #include "nl-default.h"
4
5 #include <signal.h>
6 #include <sys/time.h>
7 #include <time.h>
8
9 #include <linux/netlink.h>
10
11 #include <netlink/netlink.h>
12 #include <netlink/cache.h>
13 #include <netlink/cli/utils.h>
14
15 static int quit = 0;
16 static int change = 1;
17 static int print_ts = 0;
18
19 static struct nl_dump_params params = {
20 .dp_type = NL_DUMP_LINE,
21 };
22
23
print_timestamp(FILE * fp)24 static void print_timestamp(FILE *fp)
25 {
26 struct timeval tv;
27 char tshort[40];
28 struct tm *tm;
29 struct tm tm_buf;
30
31 gettimeofday(&tv, NULL);
32 tm = localtime_r(&tv.tv_sec, &tm_buf);
33
34 strftime(tshort, sizeof(tshort), "%Y-%m-%dT%H:%M:%S", tm);
35 fprintf(fp, "[%s.%06ld] ", tshort, tv.tv_usec);
36 }
37
change_cb(struct nl_cache * cache,struct nl_object * obj,int action,void * data)38 static void change_cb(struct nl_cache *cache, struct nl_object *obj,
39 int action, void *data)
40 {
41 if (print_ts)
42 print_timestamp(stdout);
43
44 if (action == NL_ACT_NEW)
45 printf("NEW ");
46 else if (action == NL_ACT_DEL)
47 printf("DEL ");
48 else if (action == NL_ACT_CHANGE)
49 printf("CHANGE ");
50
51 nl_object_dump(obj, ¶ms);
52 fflush(stdout);
53
54 change = 1;
55 }
56
sigint(int arg)57 static void sigint(int arg)
58 {
59 quit = 1;
60 }
61
print_usage(FILE * stream,const char * name)62 static void print_usage(FILE* stream, const char *name)
63 {
64 fprintf(stream,
65 "Usage: %s [OPTIONS]... <cache name>... \n"
66 "\n"
67 "OPTIONS\n"
68 " -f, --format=TYPE Output format { brief | details | stats }\n"
69 " Default: brief\n"
70 " -d, --dump Dump cache content after a change.\n"
71 " -i, --interval=TIME Dump cache content after TIME seconds when there is no\n"
72 " change; 0 to disable. Default: 1\n"
73 " -I, --iter Iterate over all address families when updating caches.\n"
74 " -t, --tshort Print a short timestamp before change messages.\n"
75 " -h, --help Show this help text.\n"
76 , name);
77 }
78
main(int argc,char * argv[])79 int main(int argc, char *argv[])
80 {
81 bool dump_on_change = false, dump_on_timeout = true, iter = false;
82 struct nl_cache_mngr *mngr;
83 int timeout = 1000, err;
84
85 for (;;) {
86 static struct option long_opts[] = {
87 { "format", required_argument, 0, 'f' },
88 { "dump", no_argument, 0, 'd' },
89 { "interval", required_argument, 0, 'i' },
90 { "iter", no_argument, 0, 'I' },
91 { "tshort", no_argument, 0, 't' },
92 { "help", 0, 0, 'h' },
93 { 0, 0, 0, 0 }
94 };
95 int c;
96
97 c = getopt_long(argc, argv, "hf:di:It", long_opts, NULL);
98 if (c == -1)
99 break;
100
101 switch (c) {
102 char *endptr;
103 long interval;
104
105 case 'f':
106 params.dp_type = nl_cli_parse_dumptype(optarg);
107 break;
108
109 case 'd':
110 dump_on_change = true;
111 break;
112
113 case 'i':
114 errno = 0;
115 interval = strtol(optarg, &endptr, 0);
116 if (interval < 0 || errno || *endptr) {
117 nl_cli_fatal(EINVAL, "Invalid interval \"%s\".\n",
118 optarg);
119 exit(1);
120 }
121 if (!interval) {
122 dump_on_timeout = false;
123 } else {
124 timeout = interval * 1000;
125 }
126
127 break;
128
129 case 'I':
130 iter = true;
131 break;
132
133 case 't':
134 print_ts = true;
135 break;
136
137 case 'h':
138 print_usage(stdout, argv[0]);
139 exit(0);
140
141 case '?':
142 print_usage(stderr, argv[0]);
143 exit(1);
144 }
145 }
146
147 err = nl_cache_mngr_alloc(NULL, NETLINK_ROUTE, NL_AUTO_PROVIDE, &mngr);
148 if (err < 0)
149 nl_cli_fatal(err, "Unable to allocate cache manager: %s",
150 nl_geterror(err));
151
152 while (optind < argc) {
153 struct nl_cache *cache;
154
155 err = nl_cache_alloc_name(argv[optind], &cache);
156 if (err < 0)
157 nl_cli_fatal(err, "Couldn't add cache %s: %s\n",
158 argv[optind], nl_geterror(err));
159
160 if (iter)
161 nl_cache_set_flags(cache, NL_CACHE_AF_ITER);
162
163 err = nl_cache_mngr_add_cache(mngr, cache, &change_cb, NULL);
164 if (err < 0)
165 nl_cli_fatal(err, "Unable to add cache %s: %s",
166 argv[optind], nl_geterror(err));
167
168 optind++;
169 }
170
171 params.dp_fd = stdout;
172 signal(SIGINT, sigint);
173
174 while (!quit) {
175 err = nl_cache_mngr_poll(mngr, timeout);
176 if (err < 0 && err != -NLE_INTR)
177 nl_cli_fatal(err, "Polling failed: %s", nl_geterror(err));
178
179 if (dump_on_timeout || (dump_on_change && change)) {
180 nl_cache_mngr_info(mngr, ¶ms);
181 fflush(stdout);
182 change = 0;
183 }
184 }
185
186 nl_cache_mngr_free(mngr);
187
188 return 0;
189 }
190