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