663fe0b536e1a26a7a93998003e20c288ec4cc79
[monky] / src / net_stat.c
1 /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
2  * vim: ts=4 sw=4 noet ai cindent syntax=c
3  *
4  * Conky, a system monitor, based on torsmo
5  *
6  * Any original torsmo code is licensed under the BSD license
7  *
8  * All code written since the fork of torsmo is licensed under the GPL
9  *
10  * Please see COPYING for details
11  *
12  * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
13  * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
14  *      (see AUTHORS)
15  * All rights reserved.
16  *
17  * This program is free software: you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation, either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  * You should have received a copy of the GNU General Public License
27  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28  *
29  */
30
31 #include "config.h"
32 #include "conky.h"
33 #include "logging.h"
34 #include "specials.h"
35 #include "net/if.h"
36 #include "text_object.h"
37 #include "net_stat.h"
38 #include <errno.h>
39 #include <string.h>
40 #include <sys/ioctl.h>
41
42 /* network interface stuff */
43
44 struct net_stat netstats[16];
45
46 struct net_stat *get_net_stat(const char *dev, void *free_at_crash1, void *free_at_crash2)
47 {
48         unsigned int i;
49
50         if (!dev) {
51                 return 0;
52         }
53
54         /* find interface stat */
55         for (i = 0; i < 16; i++) {
56                 if (netstats[i].dev && strcmp(netstats[i].dev, dev) == 0) {
57                         return &netstats[i];
58                 }
59         }
60
61         /* wasn't found? add it */
62         for (i = 0; i < 16; i++) {
63                 if (netstats[i].dev == 0) {
64                         netstats[i].dev = strndup(dev, text_buffer_size);
65                         return &netstats[i];
66                 }
67         }
68
69         CRIT_ERR(free_at_crash1, free_at_crash2, "too many interfaces used (limit is 16)");
70         return 0;
71 }
72
73 void parse_net_stat_arg(struct text_object *obj, const char *arg, void *free_at_crash)
74 {
75         if (!arg)
76                 arg = DEFAULTNETDEV;
77
78         obj->data.opaque = get_net_stat(arg, obj, free_at_crash);
79 }
80
81 void parse_net_stat_bar_arg(struct text_object *obj, const char *arg, void *free_at_crash)
82 {
83         SIZE_DEFAULTS(bar);
84         if (arg) {
85                 arg = scan_bar(arg, &obj->a, &obj->b);
86                 obj->data.opaque = get_net_stat(arg, obj, free_at_crash);
87         } else {
88                 // default to DEFAULTNETDEV
89                 char *buf = strndup(DEFAULTNETDEV, text_buffer_size);
90                 obj->data.opaque = get_net_stat(buf, obj, free_at_crash);
91                 free(buf);
92         }
93 }
94
95 void print_downspeed(struct text_object *obj, char *p, int p_max_size)
96 {
97         struct net_stat *ns = obj->data.opaque;
98
99         if (!ns)
100                 return;
101
102         human_readable(ns->recv_speed, p, p_max_size);
103 }
104
105 void print_downspeedf(struct text_object *obj, char *p, int p_max_size)
106 {
107         struct net_stat *ns = obj->data.opaque;
108
109         if (!ns)
110                 return;
111
112         spaced_print(p, p_max_size, "%.1f", 8, ns->recv_speed / 1024.0);
113 }
114
115 void print_upspeed(struct text_object *obj, char *p, int p_max_size)
116 {
117         struct net_stat *ns = obj->data.opaque;
118
119         if (!ns)
120                 return;
121
122         human_readable(ns->trans_speed, p, p_max_size);
123 }
124
125 void print_upspeedf(struct text_object *obj, char *p, int p_max_size)
126 {
127         struct net_stat *ns = obj->data.opaque;
128
129         if (!ns)
130                 return;
131
132         spaced_print(p, p_max_size, "%.1f", 8, ns->trans_speed / 1024.0);
133 }
134
135 void print_totaldown(struct text_object *obj, char *p, int p_max_size)
136 {
137         struct net_stat *ns = obj->data.opaque;
138
139         if (!ns)
140                 return;
141
142         human_readable(ns->recv, p, p_max_size);
143 }
144
145 void print_totalup(struct text_object *obj, char *p, int p_max_size)
146 {
147         struct net_stat *ns = obj->data.opaque;
148
149         if (!ns)
150                 return;
151
152         human_readable(ns->trans, p, p_max_size);
153 }
154
155 void print_addr(struct text_object *obj, char *p, int p_max_size)
156 {
157         struct net_stat *ns = obj->data.opaque;
158
159         if (!ns)
160                 return;
161
162         if ((ns->addr.sa_data[2] & 255) == 0 &&
163             (ns->addr.sa_data[3] & 255) == 0 &&
164             (ns->addr.sa_data[4] & 255) == 0 &&
165             (ns->addr.sa_data[5] & 255) == 0) {
166                 snprintf(p, p_max_size, "No Address");
167         } else {
168                 snprintf(p, p_max_size, "%u.%u.%u.%u",
169                          ns->addr.sa_data[2] & 255,
170                          ns->addr.sa_data[3] & 255,
171                          ns->addr.sa_data[4] & 255,
172                          ns->addr.sa_data[5] & 255);
173         }
174 }
175
176 #ifdef __linux__
177 void print_addrs(struct text_object *obj, char *p, int p_max_size)
178 {
179         struct net_stat *ns = obj->data.opaque;
180
181         if (!ns)
182                 return;
183
184         if (NULL != ns->addrs && strlen(ns->addrs) > 2) {
185                 ns->addrs[strlen(ns->addrs) - 2] = 0; /* remove ", " from end of string */
186                 strncpy(p, ns->addrs, p_max_size);
187         } else {
188                 strncpy(p, "0.0.0.0", p_max_size);
189         }
190 }
191 #endif /* __linux__ */
192
193 #ifdef X11
194 void parse_net_stat_graph_arg(struct text_object *obj, const char *arg, void *free_at_crash)
195 {
196         char *buf = 0;
197         SIZE_DEFAULTS(graph);
198         buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
199                         &obj->e, &obj->char_a, &obj->char_b);
200
201         // default to DEFAULTNETDEV
202         if (buf) {
203                 obj->data.opaque = get_net_stat(buf, obj, free_at_crash);
204                 free(buf);
205                 return;
206         }
207         obj->data.opaque = get_net_stat(DEFAULTNETDEV, obj, free_at_crash);
208 }
209
210 void print_downspeedgraph(struct text_object *obj, char *p)
211 {
212         struct net_stat *ns = obj->data.opaque;
213
214         if (!ns)
215                 return;
216
217         new_graph(p, obj->a, obj->b, obj->c, obj->d,
218                         ns->recv_speed / 1024.0, obj->e, 1, obj->char_a, obj->char_b);
219 }
220
221 void print_upspeedgraph(struct text_object *obj, char *p)
222 {
223         struct net_stat *ns = obj->data.opaque;
224
225         if (!ns)
226                 return;
227
228         new_graph(p, obj->a, obj->b, obj->c, obj->d,
229                         ns->trans_speed / 1024.0, obj->e, 1, obj->char_a, obj->char_b);
230 }
231 #endif /* X11 */
232
233 #ifdef __linux__
234 #ifdef HAVE_IWLIB
235 void print_wireless_essid(struct text_object *obj, char *p, int p_max_size)
236 {
237         struct net_stat *ns = obj->data.opaque;
238
239         if (!ns)
240                 return;
241
242         snprintf(p, p_max_size, "%s", ns->essid);
243 }
244 void print_wireless_mode(struct text_object *obj, char *p, int p_max_size)
245 {
246         struct net_stat *ns = obj->data.opaque;
247
248         if (!ns)
249                 return;
250
251         snprintf(p, p_max_size, "%s", ns->mode);
252 }
253 void print_wireless_bitrate(struct text_object *obj, char *p, int p_max_size)
254 {
255         struct net_stat *ns = obj->data.opaque;
256
257         if (!ns)
258                 return;
259
260         snprintf(p, p_max_size, "%s", ns->bitrate);
261 }
262 void print_wireless_ap(struct text_object *obj, char *p, int p_max_size)
263 {
264         struct net_stat *ns = obj->data.opaque;
265
266         if (!ns)
267                 return;
268
269         snprintf(p, p_max_size, "%s", ns->ap);
270 }
271 void print_wireless_link_qual(struct text_object *obj, char *p, int p_max_size)
272 {
273         struct net_stat *ns = obj->data.opaque;
274
275         if (!ns)
276                 return;
277
278         spaced_print(p, p_max_size, "%d", 4, ns->link_qual);
279 }
280 void print_wireless_link_qual_max(struct text_object *obj, char *p, int p_max_size)
281 {
282         struct net_stat *ns = obj->data.opaque;
283
284         if (!ns)
285                 return;
286
287         spaced_print(p, p_max_size, "%d", 4, ns->link_qual_max);
288 }
289 void print_wireless_link_qual_perc(struct text_object *obj, char *p, int p_max_size)
290 {
291         struct net_stat *ns = obj->data.opaque;
292
293         if (!ns)
294                 return;
295
296         if (ns->link_qual_max > 0) {
297                 spaced_print(p, p_max_size, "%.0f", 5,
298                                 (double) ns->link_qual /
299                                 ns->link_qual_max * 100);
300         } else {
301                 spaced_print(p, p_max_size, "unk", 5);
302         }
303 }
304 void print_wireless_link_bar(struct text_object *obj, char *p, int p_max_size)
305 {
306         struct net_stat *ns = obj->data.opaque;
307
308         if (!ns)
309                 return;
310
311 #ifdef X11
312         if(output_methods & TO_X) {
313                 new_bar(p, obj->a, obj->b, ((double) ns->link_qual /
314                                         ns->link_qual_max) * 255.0);
315         } else
316 #endif /* X11 */
317         {
318                 if(!obj->a) obj->a = DEFAULT_BAR_WIDTH_NO_X;
319                 new_bar_in_shell(p, p_max_size, ((double) ns->link_qual /
320                                         ns->link_qual_max) * 100.0, obj->a);
321         }
322 }
323 #endif /* HAVE_IWLIB */
324 #endif /* __linux__ */
325
326 void clear_net_stats(void)
327 {
328         int i;
329         for (i = 0; i < 16; i++) {
330                 if (netstats[i].dev) {
331                         free(netstats[i].dev);
332                 }
333         }
334         memset(netstats, 0, sizeof(netstats));
335 }
336
337 void parse_if_up_arg(struct text_object *obj, const char *arg)
338 {
339         obj->data.opaque = strndup(arg, text_buffer_size);
340 }
341
342 void free_if_up(struct text_object *obj)
343 {
344         if (obj->data.opaque) {
345                 free(obj->data.opaque);
346                 obj->data.opaque = NULL;
347         }
348 }
349
350 /* We should check if this is ok with OpenBSD and NetBSD as well. */
351 int interface_up(struct text_object *obj)
352 {
353         int fd;
354         struct ifreq ifr;
355         char *dev = obj->data.opaque;
356
357         if (!dev)
358                 return 0;
359
360         if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
361                 CRIT_ERR(NULL, NULL, "could not create sockfd");
362                 return 0;
363         }
364         strncpy(ifr.ifr_name, dev, IFNAMSIZ);
365         if (ioctl(fd, SIOCGIFFLAGS, &ifr)) {
366                 /* if device does not exist, treat like not up */
367                 if (errno != ENODEV && errno != ENXIO)
368                         perror("SIOCGIFFLAGS");
369                 goto END_FALSE;
370         }
371
372         if (!(ifr.ifr_flags & IFF_UP)) /* iface is not up */
373                 goto END_FALSE;
374         if (ifup_strictness == IFUP_UP)
375                 goto END_TRUE;
376
377         if (!(ifr.ifr_flags & IFF_RUNNING))
378                 goto END_FALSE;
379         if (ifup_strictness == IFUP_LINK)
380                 goto END_TRUE;
381
382         if (ioctl(fd, SIOCGIFADDR, &ifr)) {
383                 perror("SIOCGIFADDR");
384                 goto END_FALSE;
385         }
386         if (((struct sockaddr_in *)&(ifr.ifr_ifru.ifru_addr))->sin_addr.s_addr)
387                 goto END_TRUE;
388
389 END_FALSE:
390         close(fd);
391         return 0;
392 END_TRUE:
393         close(fd);
394         return 1;
395 }
396
397 static struct {
398         int nscount;
399         char **ns_list;
400 } dns_data = {
401         .nscount = 0,
402         .ns_list = NULL,
403 };
404
405 void free_dns_data(void)
406 {
407         int i;
408         for (i = 0; i < dns_data.nscount; i++)
409                 free(dns_data.ns_list[i]);
410         if (dns_data.ns_list)
411                 free(dns_data.ns_list);
412         memset(&dns_data, 0, sizeof(dns_data));
413 }
414
415 void update_dns_data(void)
416 {
417         FILE *fp;
418         char line[256];
419         //static double last_dns_update = 0.0;
420
421         /* maybe updating too often causes higher load because of /etc lying on a real FS
422         if (current_update_time - last_dns_update < 10.0)
423                 return;
424
425         last_dns_update = current_update_time;
426         */
427
428         free_dns_data();
429
430         if ((fp = fopen("/etc/resolv.conf", "r")) == NULL)
431                 return;
432         while(!feof(fp)) {
433                 if (fgets(line, 255, fp) == NULL) {
434                         break;
435                 }
436                 if (!strncmp(line, "nameserver ", 11)) {
437                         line[strlen(line) - 1] = '\0';  // remove trailing newline
438                         dns_data.nscount++;
439                         dns_data.ns_list = realloc(dns_data.ns_list, dns_data.nscount * sizeof(char *));
440                         dns_data.ns_list[dns_data.nscount - 1] = strndup(line + 11, text_buffer_size);
441                 }
442         }
443         fclose(fp);
444 }
445
446 void parse_nameserver_arg(struct text_object *obj, const char *arg)
447 {
448         obj->data.l = arg ? atoi(arg) : 0;
449 }
450
451 void print_nameserver(struct text_object *obj, char *p, int p_max_size)
452 {
453         if (dns_data.nscount > obj->data.l)
454                 snprintf(p, p_max_size, "%s", dns_data.ns_list[obj->data.l]);
455 }