Refactor some of the new weather code, fix docs.
[monky] / src / common.c
1 /* Conky, a system monitor, based on torsmo
2  *
3  * Any original torsmo code is licensed under the BSD license
4  *
5  * All code written since the fork of torsmo is licensed under the GPL
6  *
7  * Please see COPYING for details
8  *
9  * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
10  * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
11  *      (see AUTHORS)
12  * All rights reserved.
13  *
14  * This program is free software: you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation, either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * You should have received a copy of the GNU General Public License
24  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
25  *
26  */
27
28 #include "config.h"
29 #include "conky.h"
30 #include "fs.h"
31 #include "logging.h"
32 #include <ctype.h>
33 #include <errno.h>
34 #include <sys/time.h>
35 #include <sys/ioctl.h>
36 #include <net/if.h>
37 #include <netinet/in.h>
38 #include <pthread.h>
39 #include <unistd.h>
40 #include "diskio.h"
41
42 /* check for OS and include appropriate headers */
43 #if defined(__linux__)
44 #include "linux.h"
45 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
46 #include "freebsd.h"
47 #elif defined(__OpenBSD__)
48 #include "openbsd.h"
49 #endif
50
51 /* OS specific prototypes to be implemented by linux.c & Co. */
52 void update_entropy(void);
53
54 #ifndef HAVE_STRNDUP
55 // use our own strndup() if it's not available
56 char *strndup(const char *s, size_t n)
57 {
58         if (strlen(s) > n) {
59                 char *ret = malloc(n + 1);
60                 strncpy(ret, s, n);
61                 ret[n] = 0;
62                 return ret;
63         } else {
64                 return strdup(s);
65         }
66 }
67 #endif /* HAVE_STRNDUP */
68
69 void update_uname(void)
70 {
71         uname(&info.uname_s);
72 }
73
74 double get_time(void)
75 {
76         struct timeval tv;
77
78         gettimeofday(&tv, 0);
79         return tv.tv_sec + (tv.tv_usec / 1000000.0);
80 }
81
82 /* Converts '~/...' paths to '/home/blah/...' assumes that 'dest' is at least
83  * DEFAULT_TEXT_BUFFER_SIZE.  It's similar to variable_substitute, except only
84  * cheques for $HOME and ~/ in path */
85 void to_real_path(char *dest, const char *source)
86 {
87         char tmp[DEFAULT_TEXT_BUFFER_SIZE];
88         if (sscanf(source, "~/%s", tmp) || sscanf(source, "$HOME/%s", tmp)) {
89                 char *homedir = getenv("HOME");
90                 if (homedir) {
91                         snprintf(dest, DEFAULT_TEXT_BUFFER_SIZE, "%s/%s", homedir, tmp);
92                 } else {
93                         ERR("$HOME environment variable doesn't exist");
94                         strncpy(dest, source, DEFAULT_TEXT_BUFFER_SIZE);
95                 }
96         } else if (dest != source) {    //see changelog 2009-06-29 if you doubt that this check is necessary 
97                 strncpy(dest, source, DEFAULT_TEXT_BUFFER_SIZE);
98         }
99 }
100
101 FILE *open_file(const char *file, int *reported)
102 {
103         char path[DEFAULT_TEXT_BUFFER_SIZE];
104         FILE *fp = 0;
105
106         to_real_path(path, file);
107         fp = fopen(file, "r");
108
109         if (!fp) {
110                 if (!reported || *reported == 0) {
111                         ERR("can't open %s: %s", file, strerror(errno));
112                         if (reported) {
113                                 *reported = 1;
114                         }
115                 }
116                 return 0;
117         }
118
119         return fp;
120 }
121
122 void variable_substitute(const char *s, char *dest, unsigned int n)
123 {
124         while (*s && n > 1) {
125                 if (*s == '$') {
126                         s++;
127                         if (*s != '$') {
128                                 char buf[256];
129                                 const char *a, *var;
130                                 unsigned int len;
131
132                                 /* variable is either $foo or ${foo} */
133                                 if (*s == '{') {
134                                         s++;
135                                         a = s;
136                                         while (*s && *s != '}') {
137                                                 s++;
138                                         }
139                                 } else {
140                                         a = s;
141                                         while (*s && (isalnum((int) *s) || *s == '_')) {
142                                                 s++;
143                                         }
144                                 }
145
146                                 /* copy variable to buffer and look it up */
147                                 len = (s - a > 255) ? 255 : (s - a);
148                                 strncpy(buf, a, len);
149                                 buf[len] = '\0';
150
151                                 if (*s == '}') {
152                                         s++;
153                                 }
154
155                                 var = getenv(buf);
156
157                                 if (var) {
158                                         /* add var to dest */
159                                         len = strlen(var);
160                                         if (len >= n) {
161                                                 len = n - 1;
162                                         }
163                                         strncpy(dest, var, len);
164                                         dest += len;
165                                         n -= len;
166                                 }
167                                 continue;
168                         }
169                 }
170
171                 *dest++ = *s++;
172                 n--;
173         }
174
175         *dest = '\0';
176 }
177
178 /* network interface stuff */
179
180 static struct net_stat netstats[16];
181
182 struct net_stat *get_net_stat(const char *dev)
183 {
184         unsigned int i;
185
186         if (!dev) {
187                 return 0;
188         }
189
190         /* find interface stat */
191         for (i = 0; i < 16; i++) {
192                 if (netstats[i].dev && strcmp(netstats[i].dev, dev) == 0) {
193                         return &netstats[i];
194                 }
195         }
196
197         /* wasn't found? add it */
198         for (i = 0; i < 16; i++) {
199                 if (netstats[i].dev == 0) {
200                         netstats[i].dev = strndup(dev, text_buffer_size);
201                         return &netstats[i];
202                 }
203         }
204
205         CRIT_ERR("too many interfaces used (limit is 16)");
206         return 0;
207 }
208
209 void clear_net_stats(void)
210 {
211         int i;
212         for (i = 0; i < 16; i++) {
213                 if (netstats[i].dev) {
214                         free(netstats[i].dev);
215                 }
216         }
217         memset(netstats, 0, sizeof(netstats));
218 }
219
220 /* We should check if this is ok with OpenBSD and NetBSD as well. */
221 int interface_up(const char *dev)
222 {
223         int fd;
224         struct ifreq ifr;
225
226         if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
227                 CRIT_ERR("could not create sockfd");
228                 return 0;
229         }
230         strncpy(ifr.ifr_name, dev, IFNAMSIZ);
231         if (ioctl(fd, SIOCGIFFLAGS, &ifr)) {
232                 /* if device does not exist, treat like not up */
233                 if (errno != ENODEV && errno != ENXIO)
234                         perror("SIOCGIFFLAGS");
235                 goto END_FALSE;
236         }
237
238         if (!(ifr.ifr_flags & IFF_UP)) /* iface is not up */
239                 goto END_FALSE;
240         if (ifup_strictness == IFUP_UP)
241                 goto END_TRUE;
242
243         if (!(ifr.ifr_flags & IFF_RUNNING))
244                 goto END_FALSE;
245         if (ifup_strictness == IFUP_LINK)
246                 goto END_TRUE;
247
248         if (ioctl(fd, SIOCGIFADDR, &ifr)) {
249                 perror("SIOCGIFADDR");
250                 goto END_FALSE;
251         }
252         if (((struct sockaddr_in *)&(ifr.ifr_ifru.ifru_addr))->sin_addr.s_addr)
253                 goto END_TRUE;
254
255 END_FALSE:
256         close(fd);
257         return 0;
258 END_TRUE:
259         close(fd);
260         return 1;
261 }
262
263 void free_dns_data(void)
264 {
265         int i;
266         struct dns_data *data = &info.nameserver_info;
267         for (i = 0; i < data->nscount; i++)
268                 free(data->ns_list[i]);
269         if (data->ns_list)
270                 free(data->ns_list);
271         memset(data, 0, sizeof(struct dns_data));
272 }
273
274 //static double last_dns_update;
275
276 static void update_dns_data(void)
277 {
278         FILE *fp;
279         char line[256];
280         struct dns_data *data = &info.nameserver_info;
281
282         /* maybe updating too often causes higher load because of /etc lying on a real FS
283         if (current_update_time - last_dns_update < 10.0)
284                 return;
285         else
286                 last_dns_update = current_update_time;
287         */
288
289         free_dns_data();
290
291         if ((fp = fopen("/etc/resolv.conf", "r")) == NULL)
292                 return;
293         while(!feof(fp)) {
294                 if (fgets(line, 255, fp) == NULL) {
295                         break;
296                 }
297                 if (!strncmp(line, "nameserver ", 11)) {
298                         line[strlen(line) - 1] = '\0';  // remove trailing newline
299                         data->nscount++;
300                         data->ns_list = realloc(data->ns_list, data->nscount * sizeof(char *));
301                         data->ns_list[data->nscount - 1] = strndup(line + 11, text_buffer_size);
302                 }
303         }
304         fclose(fp);
305 }
306
307 void format_seconds(char *buf, unsigned int n, long seconds)
308 {
309         long days;
310         int hours, minutes;
311
312         days = seconds / 86400;
313         seconds %= 86400;
314         hours = seconds / 3600;
315         seconds %= 3600;
316         minutes = seconds / 60;
317         seconds %= 60;
318
319         if (days > 0) {
320                 snprintf(buf, n, "%ldd %dh %dm", days, hours, minutes);
321         } else {
322                 snprintf(buf, n, "%dh %dm %lds", hours, minutes, seconds);
323         }
324 }
325
326 void format_seconds_short(char *buf, unsigned int n, long seconds)
327 {
328         long days;
329         int hours, minutes;
330
331         days = seconds / 86400;
332         seconds %= 86400;
333         hours = seconds / 3600;
334         seconds %= 3600;
335         minutes = seconds / 60;
336         seconds %= 60;
337
338         if (days > 0) {
339                 snprintf(buf, n, "%ldd %dh", days, hours);
340         } else if (hours > 0) {
341                 snprintf(buf, n, "%dh %dm", hours, minutes);
342         } else {
343                 snprintf(buf, n, "%dm %lds", minutes, seconds);
344         }
345 }
346
347 static double last_meminfo_update;
348 static double last_fs_update;
349
350 unsigned long long need_mask;
351 int no_buffers;
352
353 #define NEED(a) ((need_mask & (1ULL << a)) && ((info.mask & (1ULL << a)) == 0))
354
355 void update_stuff(void)
356 {
357         int i;
358
359         info.mask = 0;
360
361         if (no_buffers) {
362                 need_mask |= 1 << INFO_BUFFERS;
363         }
364
365         /* clear speeds and up status in case device was removed and doesn't get
366          * updated */
367
368         #ifdef HAVE_OPENMP
369         #pragma omp parallel for
370         #endif /* HAVE_OPENMP */
371         for (i = 0; i < 16; i++) {
372                 if (netstats[i].dev) {
373                         netstats[i].up = 0;
374                         netstats[i].recv_speed = 0.0;
375                         netstats[i].trans_speed = 0.0;
376                 }
377         }
378
379         prepare_update();
380
381         if (NEED(INFO_UPTIME)) {
382                 update_uptime();
383         }
384
385         if (NEED(INFO_PROCS)) {
386                 update_total_processes();
387         }
388
389         if (NEED(INFO_RUN_PROCS)) {
390                 update_running_processes();
391         }
392
393         if (NEED(INFO_CPU)) {
394                 update_cpu_usage();
395         }
396
397         if (NEED(INFO_NET)) {
398                 update_net_stats();
399         }
400
401         if (NEED(INFO_DISKIO)) {
402                 update_diskio();
403         }
404
405 #if defined(__linux__)
406         if (NEED(INFO_I8K)) {
407                 update_i8k();
408         }
409 #endif /* __linux__ */
410
411 #ifdef MPD
412         if (NEED(INFO_MPD)) {
413                 update_mpd();
414         }
415 #endif
416
417 #ifdef MOC
418         if (NEED(INFO_MOC)) {
419                 run_moc_thread(info.music_player_interval * 100000);
420         }
421 #endif
422
423 #ifdef XMMS2
424         if (NEED(INFO_XMMS2)) {
425                 update_xmms2();
426         }
427 #endif
428
429 #ifdef AUDACIOUS
430         if (NEED(INFO_AUDACIOUS)) {
431                 update_audacious();
432         }
433 #endif
434
435 #ifdef BMPX
436         if (NEED(INFO_BMPX)) {
437                 update_bmpx();
438         }
439 #endif
440
441         if (NEED(INFO_LOADAVG)) {
442                 update_load_average();
443         }
444
445         if ((NEED(INFO_MEM) || NEED(INFO_BUFFERS) || NEED(INFO_TOP))
446                         && current_update_time - last_meminfo_update > 6.9) {
447                 update_meminfo();
448                 if (no_buffers) {
449                         info.mem -= info.bufmem;
450                         info.memeasyfree += info.bufmem;
451                 }
452                 last_meminfo_update = current_update_time;
453         }
454         
455 #ifdef X11
456         if (NEED(INFO_X11)) {
457                 update_x11info();
458         }
459 #endif
460
461         if (NEED(INFO_TOP)) {
462                 update_top();
463         }
464
465         /* update_fs_stat() won't do anything if there aren't fs -things */
466         if (NEED(INFO_FS) && current_update_time - last_fs_update > 12.9) {
467                 update_fs_stats();
468                 last_fs_update = current_update_time;
469         }
470 #ifdef TCP_PORT_MONITOR
471         if (NEED(INFO_TCP_PORT_MONITOR)) {
472                 tcp_portmon_update();
473         }
474 #endif
475         if (NEED(INFO_ENTROPY)) {
476                 update_entropy();
477         }
478 #if defined(__linux__)
479         if (NEED(INFO_USERS)) {
480                 update_users();
481         }
482         if (NEED(INFO_GW)) {
483                 update_gateway_info();
484         }
485 #endif /* __linux__ */
486         if (NEED(INFO_DNS)) {
487                 update_dns_data();
488         }
489 #ifdef APCUPSD
490         if (NEED(INFO_APCUPSD)) {
491                 update_apcupsd();
492         }
493 #endif
494 }
495
496 /* Ohkie to return negative values for temperatures */
497 int round_to_int_temp(float f)
498 {
499         if (f >= 0.0) {
500                 return (int) (f + 0.5);
501         } else {
502                 return (int) (f - 0.5);
503         }
504 }
505 /* Don't return negative values for cpugraph, bar, gauge, percentage.
506  * Causes unreasonable numbers to show */
507 unsigned int round_to_int(float f)
508 {
509         if (f >= 0.0) {
510                 return (int) (f + 0.5);
511         } else {
512                 return 0;
513         }
514 }
515
516 /* utility function used by RSS and Weather stuff for a curl callback.
517  */
518 size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
519 {
520         size_t realsize = size * nmemb;
521         struct MemoryStruct *mem = (struct MemoryStruct *) data;
522
523         mem->memory = (char *) realloc(mem->memory, mem->size + realsize + 1);
524         if (mem->memory) {
525                 memcpy(&(mem->memory[mem->size]), ptr, realsize);
526                 mem->size += realsize;
527                 mem->memory[mem->size] = 0;
528         }
529         return realsize;
530 }
531
532