specials: introduce dedicated per-object data and merge graph objects
[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         if (arg) {
84                 arg = scan_bar(obj, arg);
85                 obj->data.opaque = get_net_stat(arg, obj, free_at_crash);
86         } else {
87                 // default to DEFAULTNETDEV
88                 char *buf = strndup(DEFAULTNETDEV, text_buffer_size);
89                 obj->data.opaque = get_net_stat(buf, obj, free_at_crash);
90                 free(buf);
91         }
92 }
93
94 void print_downspeed(struct text_object *obj, char *p, int p_max_size)
95 {
96         struct net_stat *ns = obj->data.opaque;
97
98         if (!ns)
99                 return;
100
101         human_readable(ns->recv_speed, p, p_max_size);
102 }
103
104 void print_downspeedf(struct text_object *obj, char *p, int p_max_size)
105 {
106         struct net_stat *ns = obj->data.opaque;
107
108         if (!ns)
109                 return;
110
111         spaced_print(p, p_max_size, "%.1f", 8, ns->recv_speed / 1024.0);
112 }
113
114 void print_upspeed(struct text_object *obj, char *p, int p_max_size)
115 {
116         struct net_stat *ns = obj->data.opaque;
117
118         if (!ns)
119                 return;
120
121         human_readable(ns->trans_speed, p, p_max_size);
122 }
123
124 void print_upspeedf(struct text_object *obj, char *p, int p_max_size)
125 {
126         struct net_stat *ns = obj->data.opaque;
127
128         if (!ns)
129                 return;
130
131         spaced_print(p, p_max_size, "%.1f", 8, ns->trans_speed / 1024.0);
132 }
133
134 void print_totaldown(struct text_object *obj, char *p, int p_max_size)
135 {
136         struct net_stat *ns = obj->data.opaque;
137
138         if (!ns)
139                 return;
140
141         human_readable(ns->recv, p, p_max_size);
142 }
143
144 void print_totalup(struct text_object *obj, char *p, int p_max_size)
145 {
146         struct net_stat *ns = obj->data.opaque;
147
148         if (!ns)
149                 return;
150
151         human_readable(ns->trans, p, p_max_size);
152 }
153
154 void print_addr(struct text_object *obj, char *p, int p_max_size)
155 {
156         struct net_stat *ns = obj->data.opaque;
157
158         if (!ns)
159                 return;
160
161         if ((ns->addr.sa_data[2] & 255) == 0 &&
162             (ns->addr.sa_data[3] & 255) == 0 &&
163             (ns->addr.sa_data[4] & 255) == 0 &&
164             (ns->addr.sa_data[5] & 255) == 0) {
165                 snprintf(p, p_max_size, "No Address");
166         } else {
167                 snprintf(p, p_max_size, "%u.%u.%u.%u",
168                          ns->addr.sa_data[2] & 255,
169                          ns->addr.sa_data[3] & 255,
170                          ns->addr.sa_data[4] & 255,
171                          ns->addr.sa_data[5] & 255);
172         }
173 }
174
175 #ifdef __linux__
176 void print_addrs(struct text_object *obj, char *p, int p_max_size)
177 {
178         struct net_stat *ns = obj->data.opaque;
179
180         if (!ns)
181                 return;
182
183         if (NULL != ns->addrs && strlen(ns->addrs) > 2) {
184                 ns->addrs[strlen(ns->addrs) - 2] = 0; /* remove ", " from end of string */
185                 strncpy(p, ns->addrs, p_max_size);
186         } else {
187                 strncpy(p, "0.0.0.0", p_max_size);
188         }
189 }
190 #endif /* __linux__ */
191
192 #ifdef X11
193 void parse_net_stat_graph_arg(struct text_object *obj, const char *arg, void *free_at_crash)
194 {
195         char *buf = 0;
196         SIZE_DEFAULTS(graph);
197         buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
198                         &obj->e, &obj->char_a, &obj->char_b);
199
200         // default to DEFAULTNETDEV
201         if (buf) {
202                 obj->data.opaque = get_net_stat(buf, obj, free_at_crash);
203                 free(buf);
204                 return;
205         }
206         obj->data.opaque = get_net_stat(DEFAULTNETDEV, obj, free_at_crash);
207 }
208
209 void print_downspeedgraph(struct text_object *obj, char *p)
210 {
211         struct net_stat *ns = obj->data.opaque;
212
213         if (!ns)
214                 return;
215
216         new_graph(p, obj->a, obj->b, obj->c, obj->d,
217                         ns->recv_speed / 1024.0, obj->e, 1, obj->char_a, obj->char_b);
218 }
219
220 void print_upspeedgraph(struct text_object *obj, char *p)
221 {
222         struct net_stat *ns = obj->data.opaque;
223
224         if (!ns)
225                 return;
226
227         new_graph(p, obj->a, obj->b, obj->c, obj->d,
228                         ns->trans_speed / 1024.0, obj->e, 1, obj->char_a, obj->char_b);
229 }
230 #endif /* X11 */
231
232 #ifdef __linux__
233 #ifdef HAVE_IWLIB
234 void print_wireless_essid(struct text_object *obj, char *p, int p_max_size)
235 {
236         struct net_stat *ns = obj->data.opaque;
237
238         if (!ns)
239                 return;
240
241         snprintf(p, p_max_size, "%s", ns->essid);
242 }
243 void print_wireless_mode(struct text_object *obj, char *p, int p_max_size)
244 {
245         struct net_stat *ns = obj->data.opaque;
246
247         if (!ns)
248                 return;
249
250         snprintf(p, p_max_size, "%s", ns->mode);
251 }
252 void print_wireless_bitrate(struct text_object *obj, char *p, int p_max_size)
253 {
254         struct net_stat *ns = obj->data.opaque;
255
256         if (!ns)
257                 return;
258
259         snprintf(p, p_max_size, "%s", ns->bitrate);
260 }
261 void print_wireless_ap(struct text_object *obj, char *p, int p_max_size)
262 {
263         struct net_stat *ns = obj->data.opaque;
264
265         if (!ns)
266                 return;
267
268         snprintf(p, p_max_size, "%s", ns->ap);
269 }
270 void print_wireless_link_qual(struct text_object *obj, char *p, int p_max_size)
271 {
272         struct net_stat *ns = obj->data.opaque;
273
274         if (!ns)
275                 return;
276
277         spaced_print(p, p_max_size, "%d", 4, ns->link_qual);
278 }
279 void print_wireless_link_qual_max(struct text_object *obj, char *p, int p_max_size)
280 {
281         struct net_stat *ns = obj->data.opaque;
282
283         if (!ns)
284                 return;
285
286         spaced_print(p, p_max_size, "%d", 4, ns->link_qual_max);
287 }
288 void print_wireless_link_qual_perc(struct text_object *obj, char *p, int p_max_size)
289 {
290         struct net_stat *ns = obj->data.opaque;
291
292         if (!ns)
293                 return;
294
295         if (ns->link_qual_max > 0) {
296                 spaced_print(p, p_max_size, "%.0f", 5,
297                                 (double) ns->link_qual /
298                                 ns->link_qual_max * 100);
299         } else {
300                 spaced_print(p, p_max_size, "unk", 5);
301         }
302 }
303 void print_wireless_link_bar(struct text_object *obj, char *p, int p_max_size)
304 {
305         struct net_stat *ns = obj->data.opaque;
306
307         if (!ns)
308                 return;
309
310 #ifdef X11
311         if(output_methods & TO_X) {
312                 new_bar(obj, p, ((double) ns->link_qual /
313                                         ns->link_qual_max) * 255.0);
314         } else
315 #endif /* X11 */
316                 new_bar_in_shell(obj, p, p_max_size, ((double) ns->link_qual /
317                                         ns->link_qual_max) * 100.0);
318 }
319 #endif /* HAVE_IWLIB */
320 #endif /* __linux__ */
321
322 void clear_net_stats(void)
323 {
324         int i;
325         for (i = 0; i < 16; i++) {
326                 if (netstats[i].dev) {
327                         free(netstats[i].dev);
328                 }
329         }
330         memset(netstats, 0, sizeof(netstats));
331 }
332
333 void parse_if_up_arg(struct text_object *obj, const char *arg)
334 {
335         obj->data.opaque = strndup(arg, text_buffer_size);
336 }
337
338 void free_if_up(struct text_object *obj)
339 {
340         if (obj->data.opaque) {
341                 free(obj->data.opaque);
342                 obj->data.opaque = NULL;
343         }
344 }
345
346 /* We should check if this is ok with OpenBSD and NetBSD as well. */
347 int interface_up(struct text_object *obj)
348 {
349         int fd;
350         struct ifreq ifr;
351         char *dev = obj->data.opaque;
352
353         if (!dev)
354                 return 0;
355
356         if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
357                 CRIT_ERR(NULL, NULL, "could not create sockfd");
358                 return 0;
359         }
360         strncpy(ifr.ifr_name, dev, IFNAMSIZ);
361         if (ioctl(fd, SIOCGIFFLAGS, &ifr)) {
362                 /* if device does not exist, treat like not up */
363                 if (errno != ENODEV && errno != ENXIO)
364                         perror("SIOCGIFFLAGS");
365                 goto END_FALSE;
366         }
367
368         if (!(ifr.ifr_flags & IFF_UP)) /* iface is not up */
369                 goto END_FALSE;
370         if (ifup_strictness == IFUP_UP)
371                 goto END_TRUE;
372
373         if (!(ifr.ifr_flags & IFF_RUNNING))
374                 goto END_FALSE;
375         if (ifup_strictness == IFUP_LINK)
376                 goto END_TRUE;
377
378         if (ioctl(fd, SIOCGIFADDR, &ifr)) {
379                 perror("SIOCGIFADDR");
380                 goto END_FALSE;
381         }
382         if (((struct sockaddr_in *)&(ifr.ifr_ifru.ifru_addr))->sin_addr.s_addr)
383                 goto END_TRUE;
384
385 END_FALSE:
386         close(fd);
387         return 0;
388 END_TRUE:
389         close(fd);
390         return 1;
391 }
392
393 static struct {
394         int nscount;
395         char **ns_list;
396 } dns_data = {
397         .nscount = 0,
398         .ns_list = NULL,
399 };
400
401 void free_dns_data(void)
402 {
403         int i;
404         for (i = 0; i < dns_data.nscount; i++)
405                 free(dns_data.ns_list[i]);
406         if (dns_data.ns_list)
407                 free(dns_data.ns_list);
408         memset(&dns_data, 0, sizeof(dns_data));
409 }
410
411 void update_dns_data(void)
412 {
413         FILE *fp;
414         char line[256];
415         //static double last_dns_update = 0.0;
416
417         /* maybe updating too often causes higher load because of /etc lying on a real FS
418         if (current_update_time - last_dns_update < 10.0)
419                 return;
420
421         last_dns_update = current_update_time;
422         */
423
424         free_dns_data();
425
426         if ((fp = fopen("/etc/resolv.conf", "r")) == NULL)
427                 return;
428         while(!feof(fp)) {
429                 if (fgets(line, 255, fp) == NULL) {
430                         break;
431                 }
432                 if (!strncmp(line, "nameserver ", 11)) {
433                         line[strlen(line) - 1] = '\0';  // remove trailing newline
434                         dns_data.nscount++;
435                         dns_data.ns_list = realloc(dns_data.ns_list, dns_data.nscount * sizeof(char *));
436                         dns_data.ns_list[dns_data.nscount - 1] = strndup(line + 11, text_buffer_size);
437                 }
438         }
439         fclose(fp);
440 }
441
442 void parse_nameserver_arg(struct text_object *obj, const char *arg)
443 {
444         obj->data.l = arg ? atoi(arg) : 0;
445 }
446
447 void print_nameserver(struct text_object *obj, char *p, int p_max_size)
448 {
449         if (dns_data.nscount > obj->data.l)
450                 snprintf(p, p_max_size, "%s", dns_data.ns_list[obj->data.l]);
451 }