Base: LCDproc 0.5.2
[lcdproc-de200c.git] / server / main.c
blob7cd5c451aaab41213a5979c9e0b946c9825f5644
1 /*
2 * main.c
3 * This file is part of LCDd, the lcdproc server.
5 * This file is released under the GNU General Public License. Refer to the
6 * COPYING file distributed with this package.
8 * Copyright (c) 1999, William Ferrell, Scott Scriven
9 * 2001, Joris Robijn
10 * 2001, Rene Wagner
11 * 2002, Mike Patnode
12 * 2002, Guillaume Filion
13 * 2003, Benjamin Tse (Win32 support)
14 * 2005-2006, Peter Marschall (cleanup)
16 * Contains main(), plus signal callback functions and a help screen.
18 * Program init, command-line handling, and the main loop are
19 * implemented here. Also, minimal data about the program such as
20 * the revision number.
22 * Some of this stuff should probably be move elsewhere eventually,
23 * such as command-line handling and the main loop. main() is supposed
24 * to be "dumb".
28 #include "config.h"
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <signal.h>
34 #include <unistd.h>
35 #ifndef WIN32
36 # include <pwd.h>
37 # include <sys/wait.h>
38 #endif
40 #include <errno.h>
41 #include <math.h>
42 #include <sys/stat.h>
43 #include <sys/types.h>
44 #include <fcntl.h>
45 #include <sys/types.h>
46 #include <limits.h>
48 #include "getopt.h"
50 #ifdef HAVE_SYS_TIME_H
51 # include <sys/time.h>
52 #endif
53 /* TODO: fill in what to include otherwise */
55 #include "shared/report.h"
57 #include "drivers.h"
58 #include "sock.h"
59 #include "clients.h"
60 #include "screenlist.h"
61 #include "screen.h"
62 #include "parse.h"
63 #include "render.h"
64 #include "serverscreens.h"
65 #include "menuscreens.h"
66 #include "input.h"
67 #include "shared/configfile.h"
68 #include "drivers.h"
69 #include "main.h"
72 #if !defined(SYSCONFDIR)
73 # define SYSCONFDIR "/etc"
74 #endif
76 #define DEFAULT_BIND_ADDR "127.0.0.1"
77 #define DEFAULT_BIND_PORT LCDPORT
78 #define DEFAULT_CONFIGFILE SYSCONFDIR "/LCDd.conf"
79 #define DEFAULT_USER "nobody"
80 #define DEFAULT_DRIVER "curses"
81 #define DEFAULT_DRIVER_PATH "" /* not needed */
82 #define MAX_DRIVERS 8
83 #define DEFAULT_FOREGROUND_MODE 0
84 #define DEFAULT_ROTATE_SERVER_SCREEN 1
85 #define DEFAULT_REPORTDEST RPT_DEST_STDERR
86 #define DEFAULT_REPORTLEVEL RPT_WARNING
88 #define DEFAULT_SCREEN_DURATION 32
89 #define DEFAULT_HEARTBEAT HEARTBEAT_ON
91 /* All variables are set to 'unset' values*/
92 #define UNSET_INT -1
93 #define UNSET_STR "\01"
95 /* Socket to bind to...
97 Using loopback is much more secure; it means that this port is
98 accessible only to programs running locally on the same host as LCDd.
100 Using variables for these means that (later) we can select which port
101 and which address to bind to at run time. */
104 /* Store some standard defines into vars... */
105 char *version = VERSION;
106 char *protocol_version = PROTOCOL_VERSION;
107 char *api_version = API_VERSION;
108 char *build_date = __DATE__;
111 /**** Configuration variables ****/
112 /* Some variables are settable on the command line. This are variables that
113 * change the mode of operation. This includes settings that you can use to
114 * enable debugging: driver selection, report settings, bind address etc.
115 * These variables should be in main.h and main.c (below).
117 * All other settings do not need to be settable from the command line. They
118 * also do not necesarily need to be read in main.c but can better be read in
119 * in the file concerned.
122 unsigned int bind_port = UNSET_INT;
123 char bind_addr[64]; /* Do not preinit these strings as they will occupy */
124 char configfile[256]; /* a lot of space in the executable. */
125 char user[64]; /* The values will be overwritten anyway... */
127 /* The drivers and their driver parameters */
128 char *drivernames[MAX_DRIVERS];
129 int num_drivers = 0;
131 /* End of configuration variables */
133 /* Local variables */
134 static int foreground_mode = UNSET_INT;
135 static int report_dest = UNSET_INT;
136 static int report_level = UNSET_INT;
138 static int stored_argc;
139 static char **stored_argv;
140 static volatile short got_reload_signal = 0;
142 /* Local exported variables */
143 long int timer = 0;
145 /**** Local functions ****/
146 static void clear_settings(void);
147 static int process_command_line(int argc, char **argv);
148 static int process_configfile(char *cfgfile);
149 static void set_default_settings(void);
150 static void install_signal_handlers(int allow_reload);
151 static void child_ok_func(int signal);
152 static pid_t daemonize(void);
153 static int wave_to_parent(pid_t parent_pid);
154 static int init_drivers(void);
155 static int drop_privs(char *user);
156 static int init_screens(void);
157 static void do_reload(void);
158 static void do_mainloop(void);
159 static void exit_program(int val);
160 static void catch_reload_signal(int val);
161 static int interpret_boolean_arg(char *s);
162 static void output_help_screen(void);
163 static void output_GPL_notice(void);
165 #define CHAIN(e,f) { if (e>=0) { e=(f); }}
166 #define CHAIN_END(e,msg) { if (e<0) { report(RPT_CRIT,(msg)); exit(e); }}
170 main(int argc, char **argv)
172 int e = 0;
173 pid_t parent_pid = 0;
175 stored_argc = argc;
176 stored_argv = argv;
179 * Settings in order of preference:
181 * 1: Settings specified in command line options...
182 * 2: Settings specified in configuration file...
183 * 3: Default settings
185 * Because of this, and because one option (-c) specifies where
186 * the configuration file is, things are done in this order:
188 * 1. Read and set options.
189 * 2. Read configuration file; if option is read in configuration
190 * file and not already set, then set it.
191 * 3. Having read configuration file, if parameter is not set,
192 * set it to the default value.
194 * It is for this reason that the default values are **NOT** set
195 * in the variable declaration...
198 /* Report that server is starting (report will be delayed) */
199 report(RPT_NOTICE, "LCDd version %s starting", version);
200 report(RPT_INFO, "Built on %s, protocol version %s, API version %s",
201 build_date, protocol_version, api_version);
203 clear_settings();
205 /* Read command line*/
206 CHAIN(e, process_command_line(argc, argv));
208 /* Read config file
209 * If config file was not given on command line use default */
210 if (strcmp(configfile, UNSET_STR) == 0)
211 strncpy(configfile, DEFAULT_CONFIGFILE, sizeof(configfile));
212 CHAIN(e, process_configfile(configfile));
214 /* Set default values*/
215 set_default_settings();
217 /* Set reporting settings (will also flush delayed reports) */
218 set_reporting("LCDd", report_level, report_dest);
219 report(RPT_INFO, "Set report level to %d, output to %s", report_level,
220 ((report_dest == RPT_DEST_SYSLOG) ? "syslog" : "stderr"));
221 CHAIN_END(e, "Critical error while processing settings, abort.");
223 /* Now, go into daemon mode (if we should)...
224 * We wait for the child to report it is running OK. This mechanism
225 * is used because forking after starting the drivers causes the
226 * child to loose the (LPT) port access. */
227 if (!foreground_mode) {
228 report(RPT_INFO, "Server forking to background");
229 CHAIN(e, parent_pid = daemonize());
230 } else {
231 output_GPL_notice();
232 report(RPT_INFO, "Server running in foreground");
234 install_signal_handlers(!foreground_mode);
235 /* Only catch SIGHUP if not in foreground mode */
237 /* Startup the subparts of the server */
238 CHAIN(e, sock_init(bind_addr, bind_port));
239 CHAIN(e, screenlist_init());
240 CHAIN(e, init_drivers());
241 CHAIN(e, clients_init());
242 CHAIN(e, input_init());
243 CHAIN(e, menuscreens_init());
244 CHAIN(e, server_screen_init());
245 CHAIN_END(e, "Critical error while initializing, abort.");
246 if (!foreground_mode) {
247 /* Tell to parent that startup went OK. */
248 wave_to_parent(parent_pid);
250 drop_privs(user); /* This can't be done before, because sending a
251 signal to a process of a different user will fail */
253 do_mainloop();
254 /* This loop never stops; we'll get out only with a signal...*/
256 return 0;
260 static void
261 clear_settings(void)
263 int i;
265 debug(RPT_DEBUG, "%s()", __FUNCTION__);
267 bind_port = UNSET_INT;
268 strncpy(bind_addr, UNSET_STR, sizeof(bind_addr));
269 strncpy(configfile, UNSET_STR, sizeof(configfile));
270 strncpy(user, UNSET_STR, sizeof(user));
271 foreground_mode = UNSET_INT;
272 rotate_server_screen = UNSET_INT;
273 backlight = UNSET_INT;
275 default_duration = UNSET_INT;
276 report_dest = UNSET_INT;
277 report_level = UNSET_INT;
279 for (i = 0; i < num_drivers; i++) {
280 free(drivernames[i]);
281 drivernames[i] = NULL;
283 num_drivers = 0;
287 /* parses arguments given on command line */
288 static int
289 process_command_line(int argc, char **argv)
291 int c, b;
292 int e = 0, help = 0;
294 debug(RPT_DEBUG, "%s(argc=%d, argv=...)", __FUNCTION__, argc);
296 /* Reset getopt */
297 opterr = 0; /* Prevent some messages to stderr */
299 /* Analyze options here.. (please try to keep list of options the
300 * same everywhere) */
301 while ((c = getopt(argc, argv, "hc:d:fa:p:u:w:s:r:i:")) > 0) {
302 switch(c) {
303 case 'h':
304 help = 1; /* Continue to process the other
305 * options */
306 break;
307 case 'c':
308 strncpy(configfile, optarg, sizeof(configfile));
309 configfile[sizeof(configfile)-1] = 0; /* Terminate string */
310 break;
311 case 'd':
312 /* Add to a list of drivers to be initialized later...*/
313 if (num_drivers < MAX_DRIVERS) {
314 drivernames[num_drivers] = strdup(optarg);
315 if (drivernames[num_drivers] != NULL) {
316 num_drivers ++;
318 else {
319 report(RPT_ERR, "alloc error storing driver name: %s", optarg);
320 e = -1;
322 } else {
323 report(RPT_ERR, "Too many drivers!");
324 e = -1;
326 break;
327 case 'f':
328 foreground_mode = 1;
329 break;
330 case 'a':
331 strncpy(bind_addr, optarg, sizeof(bind_addr));
332 bind_addr[sizeof(bind_addr)-1] = 0; /* Terminate string */
333 break;
334 case 'p':
335 bind_port = atoi(optarg);
336 break;
337 case 'u':
338 strncpy(user, optarg, sizeof(user));
339 user[sizeof(user)-1] = 0; /* Terminate string */
340 break;
341 case 'w':
342 default_duration = (int) (atof(optarg) * 1e6 / TIME_UNIT);
343 if (default_duration * TIME_UNIT < 2e6) {
344 report(RPT_ERR, "Waittime should be at least 2 (seconds), not %.8s", optarg);
345 e = -1;
347 break;
348 case 's':
349 b = interpret_boolean_arg(optarg);
350 if (b == -1) {
351 report(RPT_ERR, "Not a boolean value: '%s'", optarg);
352 e = -1;
353 } else {
354 report_dest = (b) ? RPT_DEST_SYSLOG : RPT_DEST_STDERR;
356 break;
357 case 'r':
358 report_level = atoi(optarg);
359 break;
360 case 'i':
361 b = interpret_boolean_arg(optarg);
362 if (b == -1) {
363 report(RPT_ERR, "Not a boolean value: '%s'", optarg);
364 e = -1;
365 } else {
366 rotate_server_screen = b;
368 break;
369 case '?':
370 /* For some reason getopt also returns an '?'
371 * when an option argument is mission... */
372 report(RPT_ERR, "Unknown option: '%c'", optopt);
373 e = -1;
374 break;
375 case ':':
376 report(RPT_ERR, "Missing option argument!");
377 e = -1;
378 break;
382 if (optind < argc) {
383 report(RPT_ERR, "Non-option arguments on the command line !");
384 e = -1;
386 if (help) {
387 output_help_screen();
388 e = -1;
390 return e;
394 /* reads and parses configuration file */
395 static int
396 process_configfile(char *configfile)
398 const char *s;
399 /*char buf[64];*/
401 debug(RPT_DEBUG, "%s()", __FUNCTION__);
403 /* Read server settings*/
405 if (config_read_file(configfile) != 0) {
406 report(RPT_CRIT, "Could not read config file: %s", configfile);
407 return -1;
410 if (bind_port == UNSET_INT)
411 bind_port = config_get_int("server", "port", 0, UNSET_INT);
413 if (strcmp(bind_addr, UNSET_STR) == 0)
414 strncpy(bind_addr, config_get_string("server", "bind", 0, UNSET_STR), sizeof(bind_addr));
416 if (strcmp(user, UNSET_STR) == 0)
417 strncpy(user, config_get_string("server", "user", 0, UNSET_STR), sizeof(user));
419 if (default_duration == UNSET_INT) {
420 default_duration = (config_get_float("server", "waittime", 0, 0) * 1e6 / TIME_UNIT);
421 if (default_duration == 0)
422 default_duration = UNSET_INT;
423 else if (default_duration * TIME_UNIT < 2e6) {
424 report(RPT_WARNING, "Waittime should be at least 2 (seconds). Set to 2 seconds.");
425 default_duration = 2e6 / TIME_UNIT;
429 if (foreground_mode == UNSET_INT) {
430 int fg = config_get_bool("server", "foreground", 0, UNSET_INT);
432 if (fg != UNSET_INT)
433 foreground_mode = fg;
436 if (rotate_server_screen == UNSET_INT) {
437 rotate_server_screen = config_get_bool("server", "serverscreen", 0, UNSET_INT);
440 if (backlight == UNSET_INT) {
441 s = config_get_string("server", "backlight", 0, UNSET_STR);
442 if (strcmp(s, "on") == 0) {
443 backlight = BACKLIGHT_ON;
445 else if (strcmp(s, "off") == 0) {
446 backlight = BACKLIGHT_OFF;
448 else if (strcmp(s, "open") == 0) {
449 backlight = BACKLIGHT_OPEN;
451 else if (strcmp(s, UNSET_STR) != 0) {
452 report(RPT_WARNING, "Backlight state should be on, off or open");
456 if (report_dest == UNSET_INT) {
457 int rs = config_get_bool("server", "reportToSyslog", 0, UNSET_INT);
459 if (rs != UNSET_INT)
460 report_dest = (rs) ? RPT_DEST_SYSLOG : RPT_DEST_STDERR;
462 if (report_level == UNSET_INT) {
463 report_level = config_get_int("server", "reportLevel", 0, UNSET_INT);
467 /* Read drivers*/
469 /* If drivers have been specified on the command line, then do not
470 * use the driver list from the config file.
472 if (num_drivers == 0) {
473 /* read the drivernames*/
475 while(1) {
476 s = config_get_string("server", "driver", num_drivers, NULL);
477 if (!s)
478 break;
479 if (s[0] != 0) {
480 drivernames[num_drivers] = malloc(strlen(s)+1);
481 strcpy(drivernames[num_drivers], s);
482 num_drivers++;
487 return 0;
491 static void
492 set_default_settings(void)
494 debug(RPT_DEBUG, "%s()", __FUNCTION__);
496 /* Set defaults into unfilled variables....*/
498 if (bind_port == UNSET_INT)
499 bind_port = DEFAULT_BIND_PORT;
500 if (strcmp(bind_addr, UNSET_STR) == 0)
501 strncpy(bind_addr, DEFAULT_BIND_ADDR, sizeof(bind_addr));
502 if (strcmp(user, UNSET_STR) == 0)
503 strncpy(user, DEFAULT_USER, sizeof(user));
505 if (foreground_mode == UNSET_INT)
506 foreground_mode = DEFAULT_FOREGROUND_MODE;
507 if (rotate_server_screen == UNSET_INT)
508 rotate_server_screen = DEFAULT_ROTATE_SERVER_SCREEN;
510 if (default_duration == UNSET_INT)
511 default_duration = DEFAULT_SCREEN_DURATION;
512 if (backlight == UNSET_INT)
513 backlight = BACKLIGHT_OPEN;
515 if (report_dest == UNSET_INT)
516 report_dest = DEFAULT_REPORTDEST;
517 if (report_level == UNSET_INT)
518 report_level = DEFAULT_REPORTLEVEL;
521 /* Use default driver*/
522 if (num_drivers == 0) {
523 drivernames[0] = malloc(strlen(DEFAULT_DRIVER)+1);
524 strcpy(drivernames[0], DEFAULT_DRIVER);
525 num_drivers = 1;
530 static void
531 install_signal_handlers(int allow_reload)
533 #ifndef WIN32
534 /* Installs signal handlers so that the program does clean exit and
535 * can also receive a reload signal.
536 * sigaction() is favoured over signal() */
538 struct sigaction sa;
540 debug(RPT_DEBUG, "%s(allow_reload=%d)", __FUNCTION__, allow_reload);
542 sigemptyset(&(sa.sa_mask));
544 /* Clients can cause SIGPIPE if they quit unexpectedly, and the
545 * default action is to kill the server. Just ignore it. */
546 sa.sa_handler = SIG_IGN;
547 sigaction(SIGPIPE, &sa, NULL);
549 sa.sa_handler = exit_program;
550 sa.sa_flags = SA_RESTART;
552 sigaction(SIGINT, &sa, NULL); /* Ctrl-C will cause a clean exit...*/
553 sigaction(SIGTERM, &sa, NULL); /* and "kill"...*/
555 if (allow_reload) {
556 sa.sa_handler = catch_reload_signal;
557 /* On SIGHUP reread config and restart the drivers ! */
559 else {
560 /* Treat this signal just like INT and TERM */
562 sigaction(SIGHUP, &sa, NULL);
563 #else
564 /* Win32 does not support POSIX signals i.e. sigaction(). However, it does
565 * support ANSI signals in mingw. */
566 signal(SIGINT, exit_program); /* Ctrl-C will cause a clean exit...*/
567 signal(SIGTERM, exit_program); /* and "kill"...*/
568 signal(SIGPIPE, SIG_IGN);
570 /* REVISIT: implement SIGHUP on windows */
571 #endif
575 static void
576 child_ok_func(int signal)
578 /* We only catch this signal to be sure the child runs OK. */
580 debug(RPT_INFO, "%s(signal=%d)", __FUNCTION__, signal);
582 /* Exit now ! because of bug? in wait() */
583 _exit(0); /* Parent exits normally. */
587 static pid_t
588 daemonize(void)
590 #ifdef WIN32
591 /* WIN32 does not support fork() - CreateProcess() does not even have
592 * similar functionality. Instead don't daemonize on WIN32.
594 pid_t parent;
595 parent = getpid();
596 #else
597 pid_t child;
598 pid_t parent;
599 int child_status;
600 struct sigaction sa;
602 debug(RPT_DEBUG, "%s()", __FUNCTION__);
604 parent = getpid();
605 debug(RPT_INFO, "parent = %d", parent);
607 /* Install handler at parent for child's signal */
608 /* sigaction should be more portable than signal, but it does not
609 * work for some reason. */
610 sa.sa_handler = child_ok_func;
611 sigemptyset(&(sa.sa_mask));
612 sa.sa_flags = SA_RESTART;
613 sigaction(SIGUSR1, &sa, NULL);
615 /* Do the fork */
616 switch ((child = fork())) {
617 case -1:
618 report(RPT_ERR, "Could not fork");
619 return -1;
620 case 0: /* We are the child */
621 break;
622 default: /* We are the parent */
623 debug(RPT_INFO, "child = %d", child);
624 wait(&child_status);
625 /* BUG? According to the man page wait() should also return
626 * when a signal comes in that is caught. Instead it
627 * continues to wait. */
629 if (WIFEXITED(child_status)) {
630 /* Child exited normally, probably because of some
631 * error. */
632 debug(RPT_INFO, "Child has terminated!");
633 exit(WEXITSTATUS(child_status));
634 /* Parent exits with same status as child did... */
636 /* Child is still running and has signalled it's OK.
637 * This means the parent can now rest in peace. */
638 debug(RPT_INFO, "Got OK signal from child.");
639 exit(0); /* Parent exits normally. */
641 /* At this point we are always the child. */
642 /* Reset signal handler */
643 sa.sa_handler = SIG_DFL;
644 sigaction(SIGUSR1, &sa, NULL);
646 setsid(); /* Create a new session because otherwise we'll
647 * catch a SIGHUP when the shell is closed. */
648 #endif
650 return parent;
654 static int
655 wave_to_parent(pid_t parent_pid)
657 #ifndef WIN32
658 debug(RPT_DEBUG, "%s(parent_pid=%d)", __FUNCTION__, parent_pid);
660 kill(parent_pid, SIGUSR1);
661 #endif
663 return 0;
667 static int
668 init_drivers(void)
670 int i, res;
672 int output_loaded = 0;
674 debug(RPT_DEBUG, "%s()", __FUNCTION__);
676 for (i = 0; i < num_drivers; i++) {
678 res = drivers_load_driver(drivernames[i]);
679 if (res >= 0) {
680 /* Load went OK */
682 switch(res) {
683 case 0: /* Driver does input only */
684 break;
685 case 1: /* Driver does output */
686 output_loaded = 1;
687 break;
688 case 2: /* Driver does output in foreground (don't daemonize) */
689 foreground_mode = 1;
690 output_loaded = 1;
691 break;
693 } else {
694 report(RPT_ERR, "Could not load driver %.40s", drivernames[i]);
698 /* Do we have a running output driver ?*/
699 if (output_loaded) {
700 return 0;
701 } else {
702 report(RPT_ERR, "There is no output driver");
703 return -1;
708 static int
709 drop_privs(char *user)
711 #ifndef WIN32
712 struct passwd *pwent;
714 debug(RPT_DEBUG, "%s(user=\"%.40s\")", __FUNCTION__, user);
716 if (getuid() == 0 || geteuid() == 0) {
717 if ((pwent = getpwnam(user)) == NULL) {
718 report(RPT_ERR, "User %.40s not a valid user!", user);
719 return -1;
720 } else {
721 if (setuid(pwent->pw_uid) < 0) {
722 report(RPT_ERR, "Unable to switch to user %.40s", user);
723 return -1;
727 #else
728 /* Don't alter privileges in WIN32 */
729 #endif
731 return 0;
735 static int
736 init_screens(void)
738 debug(RPT_DEBUG, "%s()", __FUNCTION__);
740 if (screenlist_init() < 0) {
741 report(RPT_ERR, "Error initializing screen list");
742 return -1;
745 /* Add the server screen */
746 if (server_screen_init() < 0) {
747 report(RPT_ERR, "Error initializing server screens");
748 return -1;
750 if (!rotate_server_screen) {
751 /* Display the server screen only when there are no other
752 * screens */
753 /* server_screen->priority = PRI_BACKGROUND; */
755 return 0;
759 static void
760 do_reload(void)
762 int e = 0;
764 drivers_unload_all(); /* Close all drivers */
766 config_clear();
767 clear_settings();
769 /* Reread command line*/
770 CHAIN(e, process_command_line(stored_argc, stored_argv));
772 /* Reread config file */
773 if (strcmp(configfile, UNSET_STR)==0)
774 strncpy(configfile, DEFAULT_CONFIGFILE, sizeof(configfile));
775 CHAIN(e, process_configfile(configfile));
777 /* Set default values */
778 CHAIN(e, (set_default_settings(), 0));
780 /* Set reporting values */
781 CHAIN(e, set_reporting("LCDd", report_level, report_dest));
782 CHAIN(e, (report(RPT_INFO, "Set report level to %d, output to %s", report_level,
783 ((report_dest == RPT_DEST_SYSLOG) ? "syslog" : "stderr")), 0));
785 /* And restart the drivers */
786 CHAIN(e, init_drivers());
787 CHAIN_END(e, "Critical error while reloading, abort.");
791 static void
792 do_mainloop(void)
794 Screen *s;
795 #ifndef WIN32
796 struct timeval t;
797 struct timeval last_t;
798 #else
799 LARGE_INTEGER t;
800 LARGE_INTEGER last_t;
801 LARGE_INTEGER perf_freq; /* frequency of high perf counter (cycles per usec) */
802 #endif
803 int sleeptime;
804 long int process_lag = 0;
805 long int render_lag = 0;
806 long int t_diff;
808 debug(RPT_DEBUG, "%s()", __FUNCTION__);
810 #ifndef WIN32
811 gettimeofday(&t, NULL); /* Get initial time */
812 #else
813 QueryPerformanceFrequency(&perf_freq);
814 /* scale perf_freq to number of cycles per micro-sec */
815 /* REVISIT: this causes rounding errors below but is more efficient */
816 perf_freq.QuadPart /= 1e6;
817 QueryPerformanceCounter(&t);
818 #endif
820 while (1) {
821 /* Get current time */
822 last_t = t;
823 #ifndef WIN32
824 gettimeofday(&t, NULL);
825 t_diff = t.tv_sec - last_t.tv_sec;
826 if ((t_diff + 1) > (LONG_MAX / 1e6)) {
827 /* We're going to overflow the calculation - probably been to sleep, fudge the values */
828 t_diff = 0;
829 process_lag = 1;
830 render_lag = (1e6/RENDER_FREQ);
831 } else {
832 t_diff *= 1e6;
833 t_diff += t.tv_usec - last_t.tv_usec;
835 #else
836 QueryPerformanceCounter(&t);
837 /*t_diff.HighPart = t.HighPart - last_t.HighPart;
838 t_diff.LowPart = t.LowPart - last_t.LowPart;
840 t_diff = t.QuadPart - last_t.QuadPart;
841 t_diff /= perf_freq.QuadPart; /* scale to microseconds */
842 /* REVISIT: assumes fits into long */
843 #endif
844 process_lag += t_diff;
845 if (process_lag > 0) {
846 /* Time for a processing stroke */
847 sock_poll_clients(); /* poll clients for input*/
848 parse_all_client_messages(); /* analyze input from network clients*/
849 handle_input(); /* handle key input from devices*/
851 /* We've done the job... */
852 process_lag = 0 - (1e6/PROCESS_FREQ);
853 /* Note : this does not make a fixed frequency */
856 render_lag += t_diff;
857 if (render_lag > 0) {
858 /* Time for a rendering stroke */
859 timer ++;
860 screenlist_process();
861 s = screenlist_current();
863 /* TODO: Move this call to every client connection
864 * and every screen add...
866 if (s == server_screen) {
867 update_server_screen();
869 render_screen(s, timer);
871 /* We've done the job... */
872 if (render_lag > (1e6/RENDER_FREQ) * MAX_RENDER_LAG_FRAMES) {
873 /* Cause rendering slowdown because too much lag */
874 render_lag = (1e6/RENDER_FREQ) * MAX_RENDER_LAG_FRAMES;
876 render_lag -= (1e6/RENDER_FREQ);
877 /* Note: this DOES make a fixed frequency (except with slowdown) */
880 /* Sleep just as long as needed */
881 sleeptime = min(0-process_lag, 0-render_lag);
882 if (sleeptime > 0) {
883 #ifndef WIN32
884 usleep(sleeptime);
885 #else
886 /* Sleep in Windows takes milliseconds argument */
887 Sleep(sleeptime / 1000);
888 #endif
891 /* Check if a SIGHUP has been caught */
892 if (got_reload_signal) {
893 got_reload_signal = 0;
894 do_reload();
898 /* Quit! */
899 exit_program(0);
903 static void
904 exit_program(int val)
906 char buf[64];
908 debug(RPT_DEBUG, "%s(val=%d)", __FUNCTION__, val);
910 /* TODO: These things shouldn't be so interdependent. The order
911 * things are shut down in shouldn't matter...
914 if (val > 0) {
915 strncpy(buf, "Server shutting down on ", sizeof(buf));
916 switch(val) {
917 case 1: strcat(buf, "SIGHUP"); break;
918 case 2: strcat(buf, "SIGINT"); break;
919 case 15: strcat(buf, "SIGTERM"); break;
920 default: snprintf(buf, sizeof(buf), "Server shutting down on signal %d", val); break;
921 /* Other values should not be seen, but just in case.. */
923 report(RPT_NOTICE, buf); /* report it */
926 /* Set emergency reporting and flush all messages if not done already. */
927 if (report_level == UNSET_INT)
928 report_level = DEFAULT_REPORTLEVEL;
929 if (report_dest == UNSET_INT)
930 report_dest = DEFAULT_REPORTDEST;
931 set_reporting("LCDd", report_level, report_dest);
933 goodbye_screen(); /* display goodbye screen on LCD display */
934 drivers_unload_all(); /* release driver memory and file descriptors */
936 /* Shutdown things if server start was complete */
937 clients_shutdown(); /* shutdown clients (must come first) */
938 menuscreens_shutdown();
939 screenlist_shutdown(); /* shutdown screens (must come after client_shutdown) */
940 input_shutdown(); /* shutdown key input part */
941 sock_shutdown(); /* shutdown the sockets server */
943 report(RPT_INFO, "Exiting.");
944 _exit(0);
948 static void
949 catch_reload_signal(int val)
951 debug(RPT_DEBUG, "%s(val=%d)", __FUNCTION__, val);
953 got_reload_signal = 1;
957 static int
958 interpret_boolean_arg(char *s)
960 /* keep these checks consistent with config_get_boolean() */
961 if (strcasecmp(s, "0") == 0 || strcasecmp(s, "false") == 0
962 || strcasecmp(s, "n") == 0 || strcasecmp(s, "no") == 0
963 || strcasecmp(s, "off") == 0) {
964 return 0;
966 if (strcasecmp(s, "1") == 0 || strcasecmp(s, "true") == 0
967 || strcasecmp(s, "y") == 0 || strcasecmp(s, "yes") == 0
968 || strcasecmp(s, "on") == 0) {
969 return 1;
971 /* no legal boolean string given */
972 return -1;
976 static void
977 output_GPL_notice(void)
979 /* This will only be invoked when running in foreground
980 * So, directly output to stderr
982 fprintf(stderr, "LCDd %s, LCDproc Protocol %s\n", VERSION, PROTOCOL_VERSION);
983 fprintf(stderr, "Part of the LCDproc suite\n");
984 fprintf(stderr, "Copyright (C) 1998-2007 William Ferrell, Scott Scriven\n"
985 " and many other contributors\n\n");
987 fprintf(stderr, "This program is free software; you can redistribute it and/or\n"
988 "modify it under the terms of the GNU General Public License\n"
989 "as published by the Free Software Foundation; either version 2\n"
990 "of the License, or (at your option) any later version.\n\n");
992 fprintf(stderr, "This program is distributed in the hope that it will be useful,\n"
993 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
994 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
995 "GNU General Public License for more details.\n\n");
997 fprintf(stderr, "You should have received a copy of the GNU General Public License\n"
998 "along with this program; if not, write to the Free Software Foundation,\n"
999 "Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n\n");
1003 static void
1004 output_help_screen(void)
1006 /* Help screen is printed to stdout on purpose. No reason to have
1007 * this in syslog...
1009 debug(RPT_DEBUG, "%s()", __FUNCTION__);
1011 fprintf(stdout, "LCDd - LCDproc Server Daemon, %s\n\n", version);
1012 fprintf(stdout, "Copyright (c) 1999-2006 Scott Scriven, William Ferrell, and misc. contributors.\n");
1013 fprintf(stdout, "This program is released under the terms of the GNU General Public License.\n\n");
1014 fprintf(stdout, "Usage: LCDd [<options>]\n");
1015 fprintf(stdout, " where <options> are:\n");
1016 fprintf(stdout, " -h Display this help screen\n");
1017 fprintf(stdout, " -c <config> Use a configuration file other than %s\n",
1018 DEFAULT_CONFIGFILE);
1019 fprintf(stdout, " -d <driver> Add a driver to use (overrides drivers in config file) [%s]\n",
1020 DEFAULT_DRIVER);
1021 fprintf(stdout, " -f Run in the foreground\n");
1022 fprintf(stdout, " -a <addr> Network (IP) address to bind to [%s]\n",
1023 DEFAULT_BIND_ADDR);
1024 fprintf(stdout, " -p <port> Network port to listen for connections on [%i]\n",
1025 DEFAULT_BIND_PORT);
1026 fprintf(stdout, " -u <user> User to run as [%s]\n",
1027 DEFAULT_USER);
1028 fprintf(stdout, " -w <waittime> Time to pause at each screen (in seconds) [%d]\n",
1029 DEFAULT_SCREEN_DURATION/RENDER_FREQ);
1030 fprintf(stdout, " -s <bool> If set, reporting will be done using syslog\n");
1031 fprintf(stdout, " -r <level> Report level [%d]\n",
1032 DEFAULT_REPORTLEVEL);
1033 fprintf(stdout, " -i <bool> Whether to rotate the server info screen\n");
1035 /* Error messages will be flushed to the configured output after this
1036 * help message.