auesnthaeou
[monky] / src / conky.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 "text_object.h"
34 #include "conky.h"
35 #include "common.h"
36 #include "core.h"
37 #include <stdarg.h>
38 #include <math.h>
39 #include <time.h>
40 #include <locale.h>
41 #include <signal.h>
42 #include <errno.h>
43 #include <limits.h>
44 #if HAVE_DIRENT_H
45 #include <dirent.h>
46 #endif
47 #include <sys/time.h>
48 #include <sys/param.h>
49 #ifdef HAVE_SYS_INOTIFY_H
50 #include <sys/inotify.h>
51 #endif /* HAVE_SYS_INOTIFY_H */
52 #ifdef X11
53 #include "x11.h"
54 #include <X11/Xutil.h>
55 #ifdef HAVE_XDAMAGE
56 #include <X11/extensions/Xdamage.h>
57 #endif /* HAVE_XDAMAGE */
58 #endif /* X11 */
59 #include <sys/types.h>
60 #include <sys/stat.h>
61 #include <netinet/in.h>
62 #include <netdb.h>
63 #include <fcntl.h>
64 #include <getopt.h>
65 #ifdef XOAP
66 #include <libxml/parser.h>
67 #endif /* XOAP */
68
69 /* local headers */
70 #include "obj_create.h"
71 #include "obj_display.h"
72 #include "obj_destroy.h"
73 #include "algebra.h"
74 #include "build.h"
75 #include "colours.h"
76 #ifdef X11
77 #include "fonts.h"
78 #endif
79 #include "fs.h"
80 #include "logging.h"
81 #include "mixer.h"
82 #include "mail.h"
83 #include "mboxscan.h"
84 #include "specials.h"
85 #include "temphelper.h"
86 #include "tailhead.h"
87 #include "top.h"
88
89 /* check for OS and include appropriate headers */
90 #if defined(__linux__)
91 #include "linux.h"
92 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
93 #include "freebsd.h"
94 #elif defined(__OpenBSD__)
95 #include "openbsd.h"
96 #endif
97
98 #if defined(__FreeBSD_kernel__)
99 #include <bsd/bsd.h>
100 #endif
101
102 /* FIXME: apm_getinfo is unused here. maybe it's meant for common.c */
103 #if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__) \
104                 || defined(__OpenBSD__)) && (defined(i386) || defined(__i386__))
105 int apm_getinfo(int fd, apm_info_t aip);
106 char *get_apm_adapter(void);
107 char *get_apm_battery_life(void);
108 char *get_apm_battery_time(void);
109 #endif
110
111 #ifdef CONFIG_OUTPUT
112 #include "defconfig.h"
113 #include "conf_cookie.h"
114 #endif
115
116 #ifndef S_ISSOCK
117 #define S_ISSOCK(x)   ((x & S_IFMT) == S_IFSOCK)
118 #endif
119
120 #define MAX_IF_BLOCK_DEPTH 5
121
122 //#define SIGNAL_BLOCKING
123 #undef SIGNAL_BLOCKING
124
125 /* debugging level, used by logging.h */
126 int global_debug_level = 0;
127
128 static volatile int g_signal_pending;
129
130 int argc_copy;
131 char** argv_copy;
132
133 /* prototypes for internally used functions */
134 static void signal_handler(int);
135 static void print_version(void) __attribute__((noreturn));
136
137 static void print_version(void)
138 {
139         printf(PACKAGE_NAME" "VERSION" compiled "BUILD_DATE" for "BUILD_ARCH"\n");
140
141         printf("\nCompiled in features:\n\n"
142                    "System config file: "SYSTEM_CONFIG_FILE"\n"
143                    "Package library path: "PACKAGE_LIBDIR"\n\n"
144 #ifdef X11
145                    " X11:\n"
146 # ifdef HAVE_XDAMAGE
147                    "  * Xdamage extension\n"
148 # endif /* HAVE_XDAMAGE */
149 # ifdef HAVE_XDBE
150                    "  * XDBE (double buffer extension)\n"
151 # endif /* HAVE_XDBE */
152 # ifdef XFT
153                    "  * Xft\n"
154 # endif /* XFT */
155 #endif /* X11 */
156                    "\n Music detection:\n"
157 #ifdef AUDACIOUS
158                    "  * Audacious\n"
159 #endif /* AUDACIOUS */
160 #ifdef BMPX
161                    "  * BMPx\n"
162 #endif /* BMPX */
163 #ifdef MPD
164                    "  * MPD\n"
165 #endif /* MPD */
166 #ifdef MOC
167                    "  * MOC\n"
168 #endif /* MOC */
169 #ifdef XMMS2
170                    "  * XMMS2\n"
171 #endif /* XMMS2 */
172                    "\n General:\n"
173 #ifdef HAVE_OPENMP
174                    "  * OpenMP\n"
175 #endif /* HAVE_OPENMP */
176 #ifdef MATH
177                    "  * math\n"
178 #endif /* Math */
179 #ifdef HDDTEMP
180                    "  * hddtemp\n"
181 #endif /* HDDTEMP */
182 #ifdef TCP_PORT_MONITOR
183                    "  * portmon\n"
184 #endif /* TCP_PORT_MONITOR */
185 #ifdef HAVE_CURL
186                    "  * Curl\n"
187 #endif /* HAVE_CURL */
188 #ifdef RSS
189                    "  * RSS\n"
190 #endif /* RSS */
191 #ifdef WEATHER
192                    "  * Weather (METAR)\n"
193 #ifdef XOAP
194                    "  * Weather (XOAP)\n"
195 #endif /* XOAP */
196 #endif /* WEATHER */
197 #ifdef HAVE_IWLIB
198                    "  * wireless\n"
199 #endif /* HAVE_IWLIB */
200 #ifdef IBM
201                    "  * support for IBM/Lenovo notebooks\n"
202 #endif /* IBM */
203 #ifdef NVIDIA
204                    "  * nvidia\n"
205 #endif /* NVIDIA */
206 #ifdef EVE
207                    "  * eve-online\n"
208 #endif /* EVE */
209 #ifdef CONFIG_OUTPUT
210                    "  * config-output\n"
211 #endif /* CONFIG_OUTPUT */
212 #ifdef IMLIB2
213                    "  * Imlib2\n"
214 #endif /* IMLIB2 */
215 #ifdef MIXER_IS_ALSA
216                    "  * ALSA mixer support\n"
217 #endif /* MIXER_IS_ALSA */
218 #ifdef APCUPSD
219                    "  * apcupsd\n"
220 #endif /* APCUPSD */
221 #ifdef IOSTATS
222                    "  * iostats\n"
223 #endif /* IOSTATS */
224 #ifdef HAVE_LUA
225                    "  * Lua\n"
226                    "\n  Lua bindings:\n"
227 #ifdef HAVE_LUA_CAIRO
228                    "   * Cairo\n"
229 #endif /* HAVE_LUA_CAIRO */
230 #ifdef HAVE_LUA_IMLIB2
231                    "   * Imlib2\n"
232 #endif /* IMLIB2 */
233 #endif /* HAVE_LUA */
234         );
235
236         exit(EXIT_SUCCESS);
237 }
238
239 #ifdef HAVE_SYS_INOTIFY_H
240 int inotify_fd;
241 #endif
242
243 static void main_loop(conky_context *ctx)
244 {
245         int terminate = 0;
246 #ifdef SIGNAL_BLOCKING
247         sigset_t newmask, oldmask;
248 #endif
249         double t;
250 #ifdef HAVE_SYS_INOTIFY_H
251         int inotify_config_wd = -1;
252 #define INOTIFY_EVENT_SIZE  (sizeof(struct inotify_event))
253 #define INOTIFY_BUF_LEN     (20 * (INOTIFY_EVENT_SIZE + 16))
254         char inotify_buff[INOTIFY_BUF_LEN];
255 #endif /* HAVE_SYS_INOTIFY_H */
256
257
258 #ifdef SIGNAL_BLOCKING
259         sigemptyset(&newmask);
260         sigaddset(&newmask, SIGINT);
261         sigaddset(&newmask, SIGTERM);
262         sigaddset(&newmask, SIGUSR1);
263 #endif
264
265         ctx->next_update_time = get_time();
266         while (terminate == 0 && (ctx->total_run_times == 0 || ctx->info.looped < ctx->total_run_times)) {
267                 if (ctx->update_interval_bat != NOBATTERY && ctx->update_interval_bat != ctx->update_interval_old) {
268                         char buf[ctx->max_user_text];
269
270                         get_battery_short_status(buf, ctx->max_user_text, "BAT0");
271                         if(buf[0] == 'D') {
272                                 ctx->update_interval = ctx->update_interval_bat;
273                         } else {
274                                 ctx->update_interval = ctx->update_interval_old;
275                         }
276                 }
277                 ctx->info.looped++;
278
279 #ifdef SIGNAL_BLOCKING
280                 /* block signals.  we will inspect for pending signals later */
281                 if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0) {
282                         CRIT_ERR(NULL, NULL, "unable to sigprocmask()");
283                 }
284 #endif
285
286 #ifdef X11
287                 if (ctx->output_methods & TO_X) {
288                         XFlush(display);
289
290                         /* wait for X event or timeout */
291
292                         if (!XPending(display)) {
293                                 fd_set fdsr;
294                                 struct timeval tv;
295                                 int s;
296                                 t = ctx->next_update_time - get_time();
297
298                                 if (t < 0) {
299                                         t = 0;
300                                 } else if (t > ctx->update_interval) {
301                                         t = ctx->update_interval;
302                                 }
303
304                                 tv.tv_sec = (long) t;
305                                 tv.tv_usec = (long) (t * 1000000) % 1000000;
306                                 FD_ZERO(&fdsr);
307                                 FD_SET(ConnectionNumber(display), &fdsr);
308
309                                 s = select(ConnectionNumber(display) + 1, &fdsr, 0, 0, &tv);
310                                 if (s == -1) {
311                                         if (errno != EINTR) {
312                                                 NORM_ERR("can't select(): %s", strerror(errno));
313                                         }
314                                 } else {
315                                         /* timeout */
316                                         if (s == 0) {
317                                                 update_text(ctx);
318                                         }
319                                 }
320                         }
321
322                         if (ctx->need_to_update) {
323 #ifdef OWN_WINDOW
324                                 int wx = ctx->window.x, wy = ctx->window.y;
325 #endif
326
327                                 ctx->need_to_update = 0;
328                                 ctx->selected_font = 0;
329                                 update_text_area(ctx);
330 #ifdef OWN_WINDOW
331                                 if (ctx->own_window) {
332                                         int changed = 0;
333
334                                         /* resize ctx->window if it isn't right size */
335                                         if (!ctx->fixed_size
336                                                         && (ctx->text_width + ctx->window.border_inner_margin * 2 + ctx->window.border_outer_margin * 2 + ctx->window.border_width * 2 != ctx->window.width
337                                                                 || ctx->text_height + ctx->window.border_inner_margin * 2 + ctx->window.border_outer_margin * 2 + ctx->window.border_width * 2 != ctx->window.height)) {
338                                                 ctx->window.width = ctx->text_width + ctx->window.border_inner_margin * 2 + ctx->window.border_outer_margin * 2 + ctx->window.border_width * 2;
339                                                 ctx->window.height = ctx->text_height + ctx->window.border_inner_margin * 2 + ctx->window.border_outer_margin * 2 + ctx->window.border_width * 2;
340                                                 draw_stuff(ctx); /* redraw everything in our newly sized ctx->window */
341                                                 XResizeWindow(display, ctx->window.window, ctx->window.width,
342                                                                 ctx->window.height); /* resize ctx->window */
343                                                 set_transparent_background(ctx->window.window);
344 #ifdef HAVE_XDBE
345                                                 /* swap buffers */
346                                                 xdbe_swap_buffers();
347 #endif
348
349                                                 changed++;
350 #ifdef HAVE_LUA
351                                                 /* update lua ctx->window globals */
352                                                 llua_update_window_table(ctx->text_start_x, ctx->text_start_y, ctx->text_width, ctx->text_height);
353 #endif /* HAVE_LUA */
354                                         }
355
356                                         /* move ctx->window if it isn't in right position */
357                                         if (!ctx->fixed_pos && (ctx->window.x != wx || ctx->window.y != wy)) {
358                                                 XMoveWindow(display, ctx->window.window, ctx->window.x, ctx->window.y);
359                                                 changed++;
360                                         }
361
362                                         /* update struts */
363                                         if (changed && ctx->window.type == TYPE_PANEL) {
364                                                 int sidenum = -1;
365
366                                                 fprintf(stderr, PACKAGE_NAME": defining struts\n");
367                                                 fflush(stderr);
368
369                                                 switch (ctx->text_alignment) {
370                                                         case TOP_LEFT:
371                                                         case TOP_RIGHT:
372                                                         case TOP_MIDDLE:
373                                                                 {
374                                                                         sidenum = 2;
375                                                                         break;
376                                                                 }
377                                                         case BOTTOM_LEFT:
378                                                         case BOTTOM_RIGHT:
379                                                         case BOTTOM_MIDDLE:
380                                                                 {
381                                                                         sidenum = 3;
382                                                                         break;
383                                                                 }
384                                                         case MIDDLE_LEFT:
385                                                                 {
386                                                                         sidenum = 0;
387                                                                         break;
388                                                                 }
389                                                         case MIDDLE_RIGHT:
390                                                                 {
391                                                                         sidenum = 1;
392                                                                         break;
393                                                                 }
394                                                 }
395
396                                                 set_struts(sidenum);
397                                         }
398                                 }
399 #endif
400
401                                 clear_text(ctx, 1);
402
403 #ifdef HAVE_XDBE
404                                 if (use_xdbe) {
405                                         XRectangle r;
406
407                                         r.x = ctx->text_start_x - ctx->window.border_inner_margin - ctx->window.border_outer_margin - ctx->window.border_width;
408                                         r.y = ctx->text_start_y - ctx->window.border_inner_margin - ctx->window.border_outer_margin - ctx->window.border_width;
409                                         r.width = ctx->text_width + ctx->window.border_inner_margin * 2 + ctx->window.border_outer_margin * 2 + ctx->window.border_width * 2;
410                                         r.height = ctx->text_height + ctx->window.border_inner_margin * 2 + ctx->window.border_outer_margin * 2 + ctx->window.border_width * 2;
411                                         XUnionRectWithRegion(&r, ctx->window.region, ctx->window.region);
412                                 }
413 #endif
414                         }
415
416                         /* handle X events */
417                         while (XPending(display)) {
418                                 XEvent ev;
419
420                                 XNextEvent(display, &ev);
421                                 switch (ev.type) {
422                                         case Expose:
423                                         {
424                                                 XRectangle r;
425                                                 r.x = ev.xexpose.x;
426                                                 r.y = ev.xexpose.y;
427                                                 r.width = ev.xexpose.width;
428                                                 r.height = ev.xexpose.height;
429                                                 XUnionRectWithRegion(&r, ctx->window.region, ctx->window.region);
430                                                 break;
431                                         }
432
433                                         case PropertyNotify:
434                                         {
435                                                 if ( ev.xproperty.state == PropertyNewValue ) {
436                                                         get_x11_desktop_info( ev.xproperty.display, ev.xproperty.atom );
437                                                 }
438                                                 break;
439                                         }
440
441 #ifdef OWN_WINDOW
442                                         case ReparentNotify:
443                                                 /* set background to ParentRelative for all parents */
444                                                 if (ctx->own_window) {
445                                                         set_transparent_background(ctx->window.window);
446                                                 }
447                                                 break;
448
449                                         case ConfigureNotify:
450                                                 if (ctx->own_window) {
451                                                         /* if ctx->window size isn't what expected, set fixed size */
452                                                         if (ev.xconfigure.width != ctx->window.width
453                                                                         || ev.xconfigure.height != ctx->window.height) {
454                                                                 if (ctx->window.width != 0 && ctx->window.height != 0) {
455                                                                         ctx->fixed_size = 1;
456                                                                 }
457
458                                                                 /* clear old stuff before screwing up
459                                                                  * size and pos */
460                                                                 clear_text(ctx, 1);
461
462                                                                 {
463                                                                         XWindowAttributes attrs;
464                                                                         if (XGetWindowAttributes(display,
465                                                                                         ctx->window.window, &attrs)) {
466                                                                                 ctx->window.width = attrs.width;
467                                                                                 ctx->window.height = attrs.height;
468                                                                         }
469                                                                 }
470
471                                                                 ctx->text_width = ctx->window.width - ctx->window.border_inner_margin * 2 - ctx->window.border_outer_margin * 2 - ctx->window.border_width * 2;
472                                                                 ctx->text_height = ctx->window.height - ctx->window.border_inner_margin * 2 - ctx->window.border_outer_margin * 2 - ctx->window.border_width * 2;
473                                                                 if (ctx->text_width > ctx->maximum_width
474                                                                                 && ctx->maximum_width > 0) {
475                                                                         ctx->text_width = ctx->maximum_width;
476                                                                 }
477                                                         }
478
479                                                         /* if position isn't what expected, set fixed pos
480                                                          * total_updates avoids setting ctx->fixed_pos when ctx->window
481                                                          * is set to weird locations when started */
482                                                         /* // this is broken
483                                                         if (total_updates >= 2 && !ctx->fixed_pos
484                                                                         && (ctx->window.x != ev.xconfigure.x
485                                                                         || ctx->window.y != ev.xconfigure.y)
486                                                                         && (ev.xconfigure.x != 0
487                                                                         || ev.xconfigure.y != 0)) {
488                                                                 ctx->fixed_pos = 1;
489                                                         } */
490                                                 }
491                                                 break;
492
493                                         case ButtonPress:
494                                                 if (ctx->own_window) {
495                                                         /* if an ordinary ctx->window with decorations */
496                                                         if ((ctx->window.type == TYPE_NORMAL &&
497                                                                                 (!TEST_HINT(ctx->window.hints,
498                                                                                                         HINT_UNDECORATED))) ||
499                                                                         ctx->window.type == TYPE_DESKTOP) {
500                                                                 /* allow conky to hold input focus. */
501                                                                 break;
502                                                         } else {
503                                                                 /* forward the click to the desktop ctx->window */
504                                                                 XUngrabPointer(display, ev.xbutton.time);
505                                                                 ev.xbutton.window = ctx->window.desktop;
506                                                                 ev.xbutton.x = ev.xbutton.x_root;
507                                                                 ev.xbutton.y = ev.xbutton.y_root;
508                                                                 XSendEvent(display, ev.xbutton.window, False,
509                                                                         ButtonPressMask, &ev);
510                                                                 XSetInputFocus(display, ev.xbutton.window,
511                                                                         RevertToParent, ev.xbutton.time);
512                                                         }
513                                                 }
514                                                 break;
515
516                                         case ButtonRelease:
517                                                 if (ctx->own_window) {
518                                                         /* if an ordinary ctx->window with decorations */
519                                                         if ((ctx->window.type == TYPE_NORMAL)
520                                                                         && (!TEST_HINT(ctx->window.hints,
521                                                                         HINT_UNDECORATED))) {
522                                                                 /* allow conky to hold input focus. */
523                                                                 break;
524                                                         } else {
525                                                                 /* forward the release to the desktop ctx->window */
526                                                                 ev.xbutton.window = ctx->window.desktop;
527                                                                 ev.xbutton.x = ev.xbutton.x_root;
528                                                                 ev.xbutton.y = ev.xbutton.y_root;
529                                                                 XSendEvent(display, ev.xbutton.window, False,
530                                                                         ButtonReleaseMask, &ev);
531                                                         }
532                                                 }
533                                                 break;
534
535 #endif
536
537                                         default:
538 #ifdef HAVE_XDAMAGE
539                                                 if (ev.type == ctx->window.event_base + XDamageNotify) {
540                                                         XDamageNotifyEvent *dev = (XDamageNotifyEvent *) &ev;
541
542                                                         XFixesSetRegion(display, ctx->window.part, &dev->area, 1);
543                                                         XFixesUnionRegion(display, ctx->window.region2, ctx->window.region2, ctx->window.part);
544                                                 }
545 #endif /* HAVE_XDAMAGE */
546                                                 break;
547                                 }
548                         }
549
550 #ifdef HAVE_XDAMAGE
551                         XDamageSubtract(display, ctx->window.damage, ctx->window.region2, None);
552                         XFixesSetRegion(display, ctx->window.region2, 0, 0);
553 #endif /* HAVE_XDAMAGE */
554
555                         /* XDBE doesn't seem to provide a way to clear the back buffer
556                          * without interfering with the front buffer, other than passing
557                          * XdbeBackground to XdbeSwapBuffers. That means that if we're
558                          * using XDBE, we need to redraw the text even if it wasn't part of
559                          * the exposed area. OTOH, if we're not going to call draw_stuff at
560                          * all, then no swap happens and we can safely do nothing. */
561
562                         if (!XEmptyRegion(ctx->window.region)) {
563 #ifdef HAVE_XDBE
564                                 if (use_xdbe) {
565                                         XRectangle r;
566
567                                         r.x = ctx->text_start_x - ctx->window.border_inner_margin - ctx->window.border_outer_margin - ctx->window.border_width;
568                                         r.y = ctx->text_start_y - ctx->window.border_inner_margin - ctx->window.border_outer_margin - ctx->window.border_width;
569                                         r.width = ctx->text_width + ctx->window.border_inner_margin * 2 + ctx->window.border_outer_margin * 2 + ctx->window.border_width * 2;
570                                         r.height = ctx->text_height + ctx->window.border_inner_margin * 2 + ctx->window.border_outer_margin * 2 + ctx->window.border_width * 2;
571                                         XUnionRectWithRegion(&r, ctx->window.region, ctx->window.region);
572                                 }
573 #endif
574                                 XSetRegion(display, ctx->window.gc, ctx->window.region);
575 #ifdef XFT
576                                 if (use_xft) {
577                                         XftDrawSetClip(ctx->window.xftdraw, ctx->window.region);
578                                 }
579 #endif
580                                 draw_stuff(ctx);
581                                 XDestroyRegion(ctx->window.region);
582                                 ctx->window.region = XCreateRegion();
583                         }
584                 } else {
585 #endif /* X11 */
586                         t = (ctx->next_update_time - get_time()) * 1000000;
587                         if(t > 0) usleep((useconds_t)t);
588                         update_text(ctx);
589                         draw_stuff(ctx);
590 #ifdef NCURSES
591                         if(ctx->output_methods & TO_NCURSES) {
592                                 refresh();
593                                 clear();
594                         }
595 #endif
596 #ifdef X11
597                 }
598 #endif /* X11 */
599
600 #ifdef SIGNAL_BLOCKING
601                 /* unblock signals of interest and let handler fly */
602                 if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) {
603                         CRIT_ERR(NULL, NULL, "unable to sigprocmask()");
604                 }
605 #endif
606
607                 switch (g_signal_pending) {
608                         case SIGHUP:
609                         case SIGUSR1:
610                                 NORM_ERR("received SIGHUP or SIGUSR1. reloading the config file.");
611                                 reload_config(ctx);
612                                 break;
613                         case SIGINT:
614                         case SIGTERM:
615                                 NORM_ERR("received SIGINT or SIGTERM to terminate. bye!");
616                                 terminate = 1;
617 #ifdef X11
618                                 if (ctx->output_methods & TO_X) {
619                                         XDestroyRegion(ctx->window.region);
620                                         ctx->window.region = NULL;
621 #ifdef HAVE_XDAMAGE
622                                         XDamageDestroy(display, ctx->window.damage);
623                                         XFixesDestroyRegion(display, ctx->window.region2);
624                                         XFixesDestroyRegion(display, ctx->window.part);
625 #endif /* HAVE_XDAMAGE */
626                                         if (ctx->disp) {
627                                                 free(ctx->disp);
628                                         }
629                                 }
630 #endif /* X11 */
631                                 if(ctx->overwrite_file) {
632                                         free(ctx->overwrite_file);
633                                         ctx->overwrite_file = 0;
634                                 }
635                                 if(ctx->append_file) {
636                                         free(ctx->append_file);
637                                         ctx->append_file = 0;
638                                 }
639                                 break;
640                         default:
641                                 /* Reaching here means someone set a signal
642                                  * (SIGXXXX, signal_handler), but didn't write any code
643                                  * to deal with it.
644                                  * If you don't want to handle a signal, don't set a handler on
645                                  * it in the first place. */
646                                 if (g_signal_pending) {
647                                         NORM_ERR("ignoring signal (%d)", g_signal_pending);
648                                 }
649                                 break;
650                 }
651 #ifdef HAVE_SYS_INOTIFY_H
652                 if (inotify_fd != -1 && inotify_config_wd == -1 && ctx->current_config != 0) {
653                         inotify_config_wd = inotify_add_watch(inotify_fd,
654                                         ctx->current_config,
655                                         IN_MODIFY);
656                 }
657                 if (inotify_fd != -1 && inotify_config_wd != -1 && ctx->current_config != 0) {
658                         int len = 0, idx = 0;
659                         fd_set descriptors;
660                         struct timeval time_to_wait;
661
662                         FD_ZERO(&descriptors);
663                         FD_SET(inotify_fd, &descriptors);
664
665                         time_to_wait.tv_sec = time_to_wait.tv_usec = 0;
666
667                         select(inotify_fd + 1, &descriptors, NULL, NULL, &time_to_wait);
668                         if (FD_ISSET(inotify_fd, &descriptors)) {
669                                 /* process inotify events */
670                                 len = read(inotify_fd, inotify_buff, INOTIFY_BUF_LEN);
671                                 while (len > 0 && idx < len) {
672                                         struct inotify_event *ev = (struct inotify_event *) &inotify_buff[idx];
673                                         if (ev->wd == inotify_config_wd && (ev->mask & IN_MODIFY || ev->mask & IN_IGNORED)) {
674                                                 /* ctx->current_config should be reloaded */
675                                                 NORM_ERR("'%s' modified, reloading...", ctx->current_config);
676                                                 reload_config(ctx);
677                                                 if (ev->mask & IN_IGNORED) {
678                                                         /* for some reason we get IN_IGNORED here
679                                                          * sometimes, so we need to re-add the watch */
680                                                         inotify_config_wd = inotify_add_watch(inotify_fd,
681                                                                         ctx->current_config,
682                                                                         IN_MODIFY);
683                                                 }
684                                         }
685 #ifdef HAVE_LUA
686                                         else {
687                                                 llua_inotify_query(ev->wd, ev->mask);
688                                         }
689 #endif /* HAVE_LUA */
690                                         idx += INOTIFY_EVENT_SIZE + ev->len;
691                                 }
692                         }
693                 }
694 #endif /* HAVE_SYS_INOTIFY_H */
695
696 #ifdef HAVE_LUA
697         llua_update_info(ctx, ctx->update_interval);
698 #endif /* HAVE_LUA */
699                 g_signal_pending = 0;
700         }
701         clean_up(ctx, NULL, NULL);
702
703 #ifdef HAVE_SYS_INOTIFY_H
704         if (inotify_fd != -1) {
705                 inotify_rm_watch(inotify_fd, inotify_config_wd);
706                 close(inotify_fd);
707                 inotify_fd = inotify_config_wd = 0;
708         }
709 #endif /* HAVE_SYS_INOTIFY_H */
710 }
711
712 static void print_help(const char *prog_name) {
713         printf("Usage: %s [OPTION]...\n"
714                         PACKAGE_NAME" is a system monitor that renders text on desktop or to own transparent\n"
715                         "ctx->window. Command line options will override configurations defined in config\n"
716                         "file.\n"
717                         "   -v, --version             version\n"
718                         "   -q, --quiet               quiet mode\n"
719                         "   -D, --debug               increase debugging output, ie. -DD for more debugging\n"
720                         "   -c, --config=FILE         config file to load\n"
721 #ifdef CONFIG_OUTPUT
722                         "   -C, --print-config        print the builtin default config to stdout\n"
723                         "                             e.g. 'conky -C > ~/.conkyrc' will create a new default config\n"
724 #endif
725                         "   -d, --daemonize           daemonize, fork to background\n"
726                         "   -h, --help                help\n"
727 #ifdef X11
728                         "   -a, --alignment=ALIGNMENT text alignment on screen, {top,bottom,middle}_{left,right,middle}\n"
729                         "   -f, --font=FONT           font to use\n"
730                         "   -X, --display=DISPLAY     X11 display to use\n"
731 #ifdef OWN_WINDOW
732                         "   -o, --own-ctx->window          create own ctx->window to draw\n"
733 #endif
734 #ifdef HAVE_XDBE
735                         "   -b, --double-buffer       double buffer (prevents flickering)\n"
736 #endif
737                         "   -w, --ctx->window-id=WIN_ID    ctx->window id to draw\n"
738                         "   -x X                      x position\n"
739                         "   -y Y                      y position\n"
740 #endif /* X11 */
741                         "   -t, --text=TEXT           text to render, remember single quotes, like -t '$uptime'\n"
742                         "   -u, --interval=SECS       update interval\n"
743                         "   -i COUNT                  number of times to update "PACKAGE_NAME" (and quit)\n",
744                         prog_name
745         );
746 }
747
748 /* : means that character before that takes an argument */
749 static const char *getopt_string = "vVqdDt:u:i:hc:"
750 #ifdef X11
751         "x:y:w:a:f:X:"
752 #ifdef OWN_WINDOW
753         "o"
754 #endif
755 #ifdef HAVE_XDBE
756         "b"
757 #endif
758 #endif /* X11 */
759 #ifdef CONFIG_OUTPUT
760         "C"
761 #endif
762         ;
763
764 static const struct option longopts[] = {
765         { "help", 0, NULL, 'h' },
766         { "version", 0, NULL, 'V' },
767         { "debug", 0, NULL, 'D' },
768         { "config", 1, NULL, 'c' },
769 #ifdef CONFIG_OUTPUT
770         { "print-config", 0, NULL, 'C' },
771 #endif
772         { "daemonize", 0, NULL, 'd' },
773 #ifdef X11
774         { "alignment", 1, NULL, 'a' },
775         { "font", 1, NULL, 'f' },
776         { "display", 1, NULL, 'X' },
777 #ifdef OWN_WINDOW
778         { "own-ctx->window", 0, NULL, 'o' },
779 #endif
780 #ifdef HAVE_XDBE
781         { "double-buffer", 0, NULL, 'b' },
782 #endif
783         { "ctx->window-id", 1, NULL, 'w' },
784 #endif /* X11 */
785         { "text", 1, NULL, 't' },
786         { "interval", 0, NULL, 'u' },
787         { 0, 0, 0, 0 }
788 };
789
790 void initialisation(conky_context *ctx, int argc, char **argv)
791 {
792         struct sigaction act, oact;
793
794         set_default_configurations(ctx);
795         load_config_file(ctx, ctx->current_config);
796         currentconffile = conftree_add(currentconffile, ctx->current_config);
797
798         /* init specials array */
799         if ((specials = calloc(sizeof(struct special_t), max_specials)) == 0) {
800                 NORM_ERR("failed to create specials array");
801         }
802
803 #ifdef MAIL_FILE
804         if (current_mail_spool == NULL) {
805                 char buf[256];
806
807                 variable_substitute(MAIL_FILE, buf, 256);
808
809                 if (buf[0] != '\0') {
810                         current_mail_spool = strndup(buf, text_buffer_size);
811                 }
812         }
813 #endif
814
815         /* handle other command line arguments */
816
817 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) \
818                 || defined(__NetBSD__)
819         optind = optreset = 1;
820 #else
821         optind = 0;
822 #endif
823
824 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
825         if ((kd = kvm_open("/dev/null", "/dev/null", "/dev/null", O_RDONLY,
826                         "kvm_open")) == NULL) {
827                 CRIT_ERR(NULL, NULL, "cannot read kvm");
828         }
829 #endif
830
831         while (1) {
832                 int c = getopt_long(argc, argv, getopt_string, longopts, NULL);
833
834                 if (c == -1) {
835                         break;
836                 }
837
838                 switch (c) {
839                         case 'd':
840                                 ctx->fork_to_background = 1;
841                                 break;
842                         case 'D':
843                                 global_debug_level++;
844                                 break;
845 #ifdef X11
846                         case 'f':
847                                 set_first_font(ctx, optarg);
848                                 break;
849                         case 'a':
850                                 ctx->text_alignment = string_to_alignment(optarg);
851                                 break;
852
853 #ifdef OWN_WINDOW
854                         case 'o':
855                                 ctx->own_window = 1;
856                                 break;
857 #endif
858 #ifdef HAVE_XDBE
859                         case 'b':
860                                 use_xdbe = 1;
861                                 break;
862 #endif
863 #endif /* X11 */
864                         case 't':
865                                 if (ctx->global_text) {
866                                         free(ctx->global_text);
867                                         ctx->global_text = 0;
868                                 }
869                                 ctx->global_text = strndup(optarg, ctx->max_user_text);
870                                 convert_escapes(ctx->global_text);
871                                 break;
872
873                         case 'u':
874                                 ctx->update_interval = strtod(optarg, 0);
875                                 ctx->update_interval_old = ctx->update_interval;
876                                 if (ctx->info.music_player_interval == 0) {
877                                         // default to ctx->update_interval
878                                         ctx->info.music_player_interval = ctx->update_interval;
879                                 }
880                                 break;
881
882                         case 'i':
883                                 ctx->total_run_times = strtod(optarg, 0);
884                                 break;
885 #ifdef X11
886                         case 'x':
887                                 ctx->gap_x = atoi(optarg);
888                                 break;
889
890                         case 'y':
891                                 ctx->gap_y = atoi(optarg);
892                                 break;
893 #endif /* X11 */
894
895                         case '?':
896                                 exit(EXIT_FAILURE);
897                 }
898         }
899
900 #ifdef X11
901         /* load font */
902         if (ctx->output_methods & TO_X) {
903                 load_config_file_x11(ctx, ctx->current_config);
904         }
905 #endif /* X11 */
906
907         /* generate text and get initial size */
908         extract_variable_text(ctx, ctx->global_text);
909         if (ctx->global_text) {
910                 free(ctx->global_text);
911                 ctx->global_text = 0;
912         }
913         ctx->global_text = NULL;
914         /* fork */
915         if (ctx->fork_to_background) {
916                 int pid = fork();
917
918                 switch (pid) {
919                         case -1:
920                                 NORM_ERR(PACKAGE_NAME": couldn't fork() to background: %s",
921                                         strerror(errno));
922                                 break;
923
924                         case 0:
925                                 /* child process */
926                                 usleep(25000);
927                                 fprintf(stderr, "\n");
928                                 fflush(stderr);
929                                 break;
930
931                         default:
932                                 /* parent process */
933                                 fprintf(stderr, PACKAGE_NAME": forked to background, pid is %d\n",
934                                         pid);
935                                 fflush(stderr);
936                                 exit(EXIT_SUCCESS);
937                 }
938         }
939
940         ctx->text_buffer = malloc(ctx->max_user_text);
941         memset(ctx->text_buffer, 0, ctx->max_user_text);
942         ctx->tmpstring1 = malloc(text_buffer_size);
943         memset(ctx->tmpstring1, 0, text_buffer_size);
944         ctx->tmpstring2 = malloc(text_buffer_size);
945         memset(ctx->tmpstring2, 0, text_buffer_size);
946
947 #ifdef X11
948         ctx->xargc = argc;
949         ctx->xargv = argv;
950         X11_create_window(ctx);
951 #endif /* X11 */
952 #ifdef HAVE_LUA
953         llua_setup_info(ctx, ctx->update_interval);
954 #endif /* HAVE_LUA */
955 #ifdef XOAP
956         xmlInitParser();
957 #endif /* XOAP */
958
959         /* Set signal handlers */
960         act.sa_handler = signal_handler;
961         sigemptyset(&act.sa_mask);
962         act.sa_flags = 0;
963 #ifdef SA_RESTART
964         act.sa_flags |= SA_RESTART;
965 #endif
966
967         if (            sigaction(SIGINT,  &act, &oact) < 0
968                         ||      sigaction(SIGALRM, &act, &oact) < 0
969                         ||      sigaction(SIGUSR1, &act, &oact) < 0
970                         ||      sigaction(SIGHUP,  &act, &oact) < 0
971                         ||      sigaction(SIGTERM, &act, &oact) < 0) {
972                 NORM_ERR("error setting signal handler: %s", strerror(errno));
973         }
974
975 #ifdef HAVE_LUA
976         llua_startup_hook();
977 #endif /* HAVE_LUA */
978 }
979
980 int main(int argc, char **argv)
981 {
982         /* Conky's main context struct */
983         conky_context mctx;
984         conky_context *ctx = &mctx;
985
986 #ifdef X11
987         char *s, *temp;
988         unsigned int x;
989 #endif
990
991         argc_copy = argc;
992         argv_copy = argv;
993         g_signal_pending = 0;
994         memset(ctx, 0, sizeof(conky_context));
995         ctx->max_user_text = MAX_USER_TEXT_DEFAULT;
996         clear_net_stats();
997
998 #ifdef TCP_PORT_MONITOR
999         /* set default connection limit */
1000         tcp_portmon_set_max_connections(0);
1001 #endif
1002
1003         /* handle command line parameters that don't change configs */
1004 #ifdef X11
1005         if (((s = getenv("LC_ALL")) && *s) || ((s = getenv("LC_CTYPE")) && *s)
1006                         || ((s = getenv("LANG")) && *s)) {
1007                 temp = (char *) malloc((strlen(s) + 1) * sizeof(char));
1008                 if (temp == NULL) {
1009                         NORM_ERR("malloc failed");
1010                 }
1011                 for (x = 0; x < strlen(s); x++) {
1012                         temp[x] = tolower(s[x]);
1013                 }
1014                 temp[x] = 0;
1015                 if (strstr(temp, "utf-8") || strstr(temp, "utf8")) {
1016                         ctx->utf8_mode = 1;
1017                 }
1018
1019                 free(temp);
1020         }
1021         if (!setlocale(LC_CTYPE, "")) {
1022                 NORM_ERR("Can't set the specified locale!\nCheck LANG, LC_CTYPE, LC_ALL.");
1023         }
1024 #endif /* X11 */
1025         while (1) {
1026                 int c = getopt_long(argc, argv, getopt_string, longopts, NULL);
1027
1028                 if (c == -1) {
1029                         break;
1030                 }
1031
1032                 switch (c) {
1033                         case 'v':
1034                         case 'V':
1035                                 print_version();
1036                         case 'c':
1037                                 if (ctx->current_config) {
1038                                         free(ctx->current_config);
1039                                 }
1040                                 ctx->current_config = strndup(optarg, ctx->max_user_text);
1041                                 break;
1042                         case 'q':
1043                                 freopen("/dev/null", "w", stderr);
1044                                 break;
1045                         case 'h':
1046                                 print_help(argv[0]);
1047                                 return 0;
1048 #ifdef CONFIG_OUTPUT
1049                         case 'C':
1050                                 print_defconfig();
1051                                 return 0;
1052 #endif
1053 #ifdef X11
1054                         case 'w':
1055                                 ctx->window.window = strtol(optarg, 0, 0);
1056                                 break;
1057                         case 'X':
1058                                 if (ctx->disp)
1059                                         free(ctx->disp);
1060                                 ctx->disp = strdup(optarg);
1061                                 break;
1062 #endif /* X11 */
1063
1064                         case '?':
1065                                 exit(EXIT_FAILURE);
1066                 }
1067         }
1068
1069         /* check if specified config file is valid */
1070         if (ctx->current_config) {
1071                 struct stat sb;
1072                 if (stat(ctx->current_config, &sb) ||
1073                                 (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode))) {
1074                         NORM_ERR("invalid configuration file '%s'\n", ctx->current_config);
1075                         free(ctx->current_config);
1076                         ctx->current_config = 0;
1077                 }
1078         }
1079
1080         /* load ctx->current_config, CONFIG_FILE or SYSTEM_CONFIG_FILE */
1081
1082         if (!ctx->current_config) {
1083                 /* load default config file */
1084                 char buf[DEFAULT_TEXT_BUFFER_SIZE];
1085                 FILE *fp;
1086
1087                 /* Try to use personal config file first */
1088                 to_real_path(buf, CONFIG_FILE);
1089                 if (buf[0] && (fp = fopen(buf, "r"))) {
1090                         ctx->current_config = strndup(buf, ctx->max_user_text);
1091                         fclose(fp);
1092                 }
1093
1094                 /* Try to use system config file if personal config not readable */
1095                 if (!ctx->current_config && (fp = fopen(SYSTEM_CONFIG_FILE, "r"))) {
1096                         ctx->current_config = strndup(SYSTEM_CONFIG_FILE, ctx->max_user_text);
1097                         fclose(fp);
1098                 }
1099
1100                 /* No readable config found */
1101                 if (!ctx->current_config) {
1102 #ifdef CONFIG_OUTPUT
1103                         ctx->current_config = strdup("==builtin==");
1104                         NORM_ERR("no readable personal or system-wide config file found,"
1105                                         " using builtin default");
1106 #else
1107                         CRIT_ERR(NULL, NULL, "no readable personal or system-wide config file found");
1108 #endif /* ! CONF_OUTPUT */
1109                 }
1110         }
1111
1112 #ifdef XOAP
1113         /* Load xoap keys, if existing */
1114         load_xoap_keys();
1115 #endif /* XOAP */
1116
1117 #ifdef HAVE_SYS_INOTIFY_H
1118         inotify_fd = inotify_init();
1119 #endif /* HAVE_SYS_INOTIFY_H */
1120
1121         initialisation(&mctx, argc, argv);
1122
1123         main_loop(&mctx);
1124
1125 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
1126         kvm_close(kd);
1127 #endif
1128
1129         return 0;
1130
1131 }
1132
1133 static void signal_handler(int sig)
1134 {
1135         /* signal handler is light as a feather, as it should be.  we will poll
1136          * g_signal_pending with each loop of conky and do any signal processing
1137          * there, NOT here */
1138                 g_signal_pending = sig;
1139 }