weather objects: convert to generic object payload
[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 "logging.h"
33 #include "specials.h"
34 #include "net/if.h"
35 #include "text_object.h"
36 #include "net_stat.h"
37 #include <errno.h>
38 #include <string.h>
39 #include <sys/ioctl.h>
40
41 /* network interface stuff */
42
43 struct net_stat netstats[16];
44
45 struct net_stat *get_net_stat(const char *dev, void *free_at_crash1, void *free_at_crash2)
46 {
47         unsigned int i;
48
49         if (!dev) {
50                 return 0;
51         }
52
53         /* find interface stat */
54         for (i = 0; i < 16; i++) {
55                 if (netstats[i].dev && strcmp(netstats[i].dev, dev) == 0) {
56                         return &netstats[i];
57                 }
58         }
59
60         /* wasn't found? add it */
61         for (i = 0; i < 16; i++) {
62                 if (netstats[i].dev == 0) {
63                         netstats[i].dev = strndup(dev, text_buffer_size);
64                         return &netstats[i];
65                 }
66         }
67
68         CRIT_ERR(free_at_crash1, free_at_crash2, "too many interfaces used (limit is 16)");
69         return 0;
70 }
71
72 void parse_net_stat_arg(struct text_object *obj, const char *arg, void *free_at_crash)
73 {
74         if (!arg)
75                 arg = DEFAULTNETDEV;
76
77         obj->data.net = get_net_stat(arg, obj, free_at_crash);
78 }
79
80 void parse_net_stat_bar_arg(struct text_object *obj, const char *arg, void *free_at_crash)
81 {
82         SIZE_DEFAULTS(bar);
83         if (arg) {
84                 arg = scan_bar(arg, &obj->a, &obj->b);
85                 obj->data.net = 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.net = 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         if (!obj->data.net)
97                 return;
98
99         human_readable(obj->data.net.recv_speed, p, p_max_size);
100 }
101
102 void print_downspeedf(struct text_object *obj, char *p, int p_max_size)
103 {
104         if (!obj->data.net)
105                 return;
106
107         spaced_print(p, p_max_size, "%.1f", 8, obj->data.netrecv_speed / 1024.0);
108 }
109
110 void print_upspeed(struct text_object *obj, char *p, int p_max_size)
111 {
112         if (!obj->data.net)
113                 return;
114
115         human_readable(obj->data.net.trans_speed, p, p_max_size);
116 }
117
118 void print_upspeedf(struct text_object *obj, char *p, int p_max_size)
119 {
120         if (!obj->data.net)
121                 return;
122
123         spaced_print(p, p_max_size, "%.1f", 8, obj->data.net.trans_speed / 1024.0);
124 }
125
126 void print_totaldown(struct text_object *obj, char *p, int p_max_size)
127 {
128         if (!obj->data.net)
129                 return;
130
131         human_readable(obj->data.net.recv, p, p_max_size);
132 }
133
134 void print_totalup(struct text_object *obj, char *p, int p_max_size)
135 {
136         if (!obj->data.net)
137                 return;
138
139         human_readable(obj->data.net.trans, p, p_max_size);
140 }
141
142 void print_addr(struct text_object *obj, char *p, int p_max_size)
143 {
144         if (!obj->data.net)
145                 return;
146
147         if ((obj->data.net.addr.sa_data[2] & 255) == 0 &&
148             (obj->data.net.addr.sa_data[3] & 255) == 0 &&
149             (obj->data.net.addr.sa_data[4] & 255) == 0 &&
150             (obj->data.net.addr.sa_data[5] & 255) == 0) {
151                 snprintf(p, p_max_size, "No Address");
152         } else {
153                 snprintf(p, p_max_size, "%u.%u.%u.%u",
154                          obj->data.net.addr.sa_data[2] & 255,
155                          obj->data.net.addr.sa_data[3] & 255,
156                          obj->data.net.addr.sa_data[4] & 255,
157                          obj->data.net.addr.sa_data[5] & 255);
158         }
159 }
160
161 #ifdef __linux__
162 void print_addrs(struct text_object *obj, char *p, int p_max_size)
163 {
164         if (!obj->data.net)
165                 return;
166
167         if (NULL != obj->data.net.addrs && strlen(obj->data.net.addrs) > 2) {
168                 obj->data.net.addrs[strlen(obj->data.net.addrs) - 2] = 0; /* remove ", " from end of string */
169                 strncpy(p, obj->data.net.addrs, p_max_size);
170         } else {
171                 strncpy(p, "0.0.0.0", p_max_size);
172         }
173 }
174 #endif /* __linux__ */
175
176 #ifdef X11
177 void parse_net_stat_graph_arg(struct text_object *obj, const char *arg, void *free_at_crash)
178 {
179         char *buf = 0;
180         SIZE_DEFAULTS(graph);
181         buf = scan_graph(arg, &obj->a, &obj->b, &obj->c, &obj->d,
182                         &obj->e, &obj->char_a, &obj->char_b);
183
184         // default to DEFAULTNETDEV
185         if (buf) {
186                 obj->data.net = get_net_stat(buf, obj, free_at_crash);
187                 free(buf);
188                 return;
189         }
190         obj->data.net = get_net_stat(DEFAULTNETDEV, obj, free_at_crash);
191 }
192
193 void print_downspeedgraph(struct text_object *obj, char *p)
194 {
195         if (!obj->data.net)
196                 return;
197
198         new_graph(p, obj->a, obj->b, obj->c, obj->d,
199                         obj->data.net.recv_speed / 1024.0, obj->e, 1, obj->char_a, obj->char_b);
200 }
201
202 void print_upspeedgraph(struct text_object *obj, char *p)
203 {
204         if (!obj->data.net)
205                 return;
206
207         new_graph(p, obj->a, obj->b, obj->c, obj->d,
208                         obj->data.net.trans_speed / 1024.0, obj->e, 1, obj->char_a, obj->char_b);
209 }
210 #endif /* X11 */
211
212 #ifdef __linux__
213 #ifdef HAVE_IWLIB
214 void print_wireless_essid(struct text_object *obj, char *p, int p_max_size)
215 {
216         if (!obj->data.net)
217                 return;
218
219         snprintf(p, p_max_size, "%s", obj->data.net.essid);
220 }
221 void print_wireless_mode(struct text_object *obj, char *p, int p_max_size)
222 {
223         if (!obj->data.net)
224                 return;
225
226         snprintf(p, p_max_size, "%s", obj->data.net.mode);
227 }
228 void print_wireless_bitrate(struct text_object *obj, char *p, int p_max_size)
229 {
230         if (!obj->data.net)
231                 return;
232
233         snprintf(p, p_max_size, "%s", obj->data.net.bitrate);
234 }
235 void print_wireless_ap(struct text_object *obj, char *p, int p_max_size)
236 {
237         if (!obj->data.net)
238                 return;
239
240         snprintf(p, p_max_size, "%s", obj->data.net.ap);
241 }
242 void print_wireless_link_qual(struct text_object *obj, char *p, int p_max_size)
243 {
244         if (!obj->data.net)
245                 return;
246
247         spaced_print(p, p_max_size, "%d", 4, obj->data.net.link_qual);
248 }
249 void print_wireless_link_qual_max(struct text_object *obj, char *p, int p_max_size)
250 {
251         if (!obj->data.net)
252                 return;
253
254         spaced_print(p, p_max_size, "%d", 4, obj->data.net.link_qual_max);
255 }
256 void print_wireless_link_qual_perc(struct text_object *obj, char *p, int p_max_size)
257 {
258         if (!obj->data.net)
259                 return;
260
261         if (obj->data.net.link_qual_max > 0) {
262                 spaced_print(p, p_max_size, "%.0f", 5,
263                                 (double) obj->data.net.link_qual /
264                                 obj->data.net.link_qual_max * 100);
265         } else {
266                 spaced_print(p, p_max_size, "unk", 5);
267         }
268 }
269 void print_wireless_link_bar(struct text_object *obj, char *p, int p_max_size)
270 {
271         if (!obj->data.net)
272                 return;
273
274 #ifdef X11
275         if(output_methods & TO_X) {
276                 new_bar(p, obj->a, obj->b, ((double) obj->data.net.link_qual /
277                                         obj->data.net.link_qual_max) * 255.0);
278         } else
279 #endif /* X11 */
280         {
281                 if(!obj->a) obj->a = DEFAULT_BAR_WIDTH_NO_X;
282                 new_bar_in_shell(p, p_max_size, ((double) obj->data.net.link_qual /
283                                         obj->data.net.link_qual_max) * 100.0, obj->a);
284         }
285 }
286 #endif /* HAVE_IWLIB */
287 #endif /* __linux__ */
288
289 void clear_net_stats(void)
290 {
291         int i;
292         for (i = 0; i < 16; i++) {
293                 if (netstats[i].dev) {
294                         free(netstats[i].dev);
295                 }
296         }
297         memset(netstats, 0, sizeof(netstats));
298 }
299
300 /* We should check if this is ok with OpenBSD and NetBSD as well. */
301 int interface_up(const char *dev)
302 {
303         int fd;
304         struct ifreq ifr;
305
306         if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
307                 CRIT_ERR(NULL, NULL, "could not create sockfd");
308                 return 0;
309         }
310         strncpy(ifr.ifr_name, dev, IFNAMSIZ);
311         if (ioctl(fd, SIOCGIFFLAGS, &ifr)) {
312                 /* if device does not exist, treat like not up */
313                 if (errno != ENODEV && errno != ENXIO)
314                         perror("SIOCGIFFLAGS");
315                 goto END_FALSE;
316         }
317
318         if (!(ifr.ifr_flags & IFF_UP)) /* iface is not up */
319                 goto END_FALSE;
320         if (ifup_strictness == IFUP_UP)
321                 goto END_TRUE;
322
323         if (!(ifr.ifr_flags & IFF_RUNNING))
324                 goto END_FALSE;
325         if (ifup_strictness == IFUP_LINK)
326                 goto END_TRUE;
327
328         if (ioctl(fd, SIOCGIFADDR, &ifr)) {
329                 perror("SIOCGIFADDR");
330                 goto END_FALSE;
331         }
332         if (((struct sockaddr_in *)&(ifr.ifr_ifru.ifru_addr))->sin_addr.s_addr)
333                 goto END_TRUE;
334
335 END_FALSE:
336         close(fd);
337         return 0;
338 END_TRUE:
339         close(fd);
340         return 1;
341 }
342
343 static struct {
344         int nscount;
345         char **ns_list;
346 } dns_data = {
347         .nscount = 0,
348         .ns_list = NULL,
349 };
350
351 void free_dns_data(void)
352 {
353         int i;
354         for (i = 0; i < dns_data.nscount; i++)
355                 free(dns_data.ns_list[i]);
356         if (dns_data.ns_list)
357                 free(dns_data.ns_list);
358         memset(&dns_data, 0, sizeof(dns_data));
359 }
360
361 void update_dns_data(void)
362 {
363         FILE *fp;
364         char line[256];
365         //static double last_dns_update = 0.0;
366
367         /* maybe updating too often causes higher load because of /etc lying on a real FS
368         if (current_update_time - last_dns_update < 10.0)
369                 return;
370
371         last_dns_update = current_update_time;
372         */
373
374         free_dns_data();
375
376         if ((fp = fopen("/etc/resolv.conf", "r")) == NULL)
377                 return;
378         while(!feof(fp)) {
379                 if (fgets(line, 255, fp) == NULL) {
380                         break;
381                 }
382                 if (!strncmp(line, "nameserver ", 11)) {
383                         line[strlen(line) - 1] = '\0';  // remove trailing newline
384                         dns_data.nscount++;
385                         dns_data.ns_list = realloc(dns_data.ns_list, dns_data.nscount * sizeof(char *));
386                         dns_data.ns_list[dns_data.nscount - 1] = strndup(line + 11, text_buffer_size);
387                 }
388         }
389         fclose(fp);
390 }
391
392 void parse_nameserver_arg(struct text_object *obj, const char *arg)
393 {
394         obj->data.l = arg ? atoi(arg) : 0;
395 }
396
397 void print_nameserver(struct text_object *obj, char *p, int p_max_size)
398 {
399         if (dns_data.nscount > obj->data.l)
400                 snprintf(p, p_max_size, "%s", dns_data.ns_list[obj->data.l]);
401 }