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