51
76
/* Variables set by config */
52
77
#define UNSET_INT -1
53
78
#define UNSET_STR "\01"
54
char * configfile = NULL;
55
char * address = NULL;
79
char *configfile = NULL;
56
81
int port = UNSET_INT;
57
82
int foreground = FALSE;
58
83
static int report_level = UNSET_INT;
59
84
static int report_dest = UNSET_INT;
86
int pidfile_written = FALSE;
60
87
char *displayname = NULL;
61
88
char *default_shell = NULL;
90
/* Other global variables */
91
MenuEntry *main_menu = NULL; /**< pointer to the main menu */
92
ProcInfo *proc_queue = NULL; /**< pointer to the list of executed processes */
94
int lcd_wid = 0; /**< LCD display width reported by the server */
95
int lcd_hgt = 0; /**< LCD display height reported by the server */
97
int sock = -1; /**< socket to connect to server */
99
int Quit = 0; /**< indicate end of main loop */
68
102
/* Function prototypes */
103
static void exit_program(int val);
104
static void sigchld_handler(int signal);
69
105
static int process_command_line(int argc, char **argv);
70
106
static int process_configfile(char * configfile);
71
107
static int connect_and_setup(void);
72
108
static int process_response(char *str);
73
109
static int exec_command(MenuEntry *cmd);
110
static int show_procinfo_msg(ProcInfo *p);
74
111
static int main_loop(void);
101
139
if (daemon(1,1) != 0) {
102
140
report(RPT_ERR, "Error: daemonize failed");
143
if (pidfile != NULL) {
144
FILE *pidf = fopen(pidfile, "w");
147
fprintf(pidf, "%d\n", (int) getpid());
149
pidfile_written = TRUE;
151
fprintf(stderr, "Error creating pidfile %s: %s\n",
152
pidfile, strerror(errno));
153
return(EXIT_FAILURE);
158
/* setup signal handlers for common signals */
159
sigemptyset(&sa.sa_mask);
160
sa.sa_flags = SA_RESTART;
161
sa.sa_handler = exit_program;
162
sigaction(SIGINT, &sa, NULL); // Ctrl-C
163
sigaction(SIGTERM, &sa, NULL); // "regular" kill
164
sigaction(SIGHUP, &sa, NULL); // kill -HUP
165
sigaction(SIGPIPE, &sa, NULL); // write to closed socket
166
sigaction(SIGKILL, &sa, NULL); // kill -9 [cannot be trapped; but ...]
168
/* setup signal handler for children to avoid zombies */
169
sigemptyset(&sa.sa_mask);
170
sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
171
sa.sa_handler = sigchld_handler;
172
sigaction(SIGCHLD, &sa, NULL);
176
exit_program(EXIT_SUCCESS);
180
static void exit_program(int val)
182
//printf("exit program\n");
185
if ((foreground != TRUE) && (pidfile != NULL) && (pidfile_written == TRUE))
191
/* the grim reaper ;-) */
192
static void sigchld_handler(int signal)
197
/* wait for the child that was signalled as finished */
198
if ((pid = wait(&status)) != -1) {
201
/* fill the procinfo structure with the necessary information */
202
for (p = proc_queue; p != NULL; p = p->next) {
205
p->endtime = time(NULL);
307
exec = menu_find_by_id(main_menu, atoi(argv[2]));
309
report(RPT_WARNING, "Could not find the item id given by the server");
313
/* The id has been found */
410
/* Find the entry by id */
411
entry = menu_find_by_id(main_menu, atoi(argv[2]));
413
report(RPT_WARNING, "Could not find the item id given by the server");
418
/* The id has been found.
419
* We trigger on the following conditions:
420
* - command entry without args
421
* - last arg of a command entry with args */
422
if (((entry->type == MT_EXEC) && (entry->children == NULL)) ||
423
((entry->type & MT_ARGUMENT) && (entry->next == NULL))) {
425
// last arg => get parent entry
426
if ((entry->type & MT_ARGUMENT) && (entry->next == NULL))
427
entry = entry->parent;
429
if (entry->type == MT_EXEC)
433
else if ((strcmp(argv[1], "plus") == 0) ||
434
(strcmp(argv[1], "minus") == 0) ||
435
(strcmp(argv[1], "update") == 0)) {
439
report(RPT_WARNING, "Server gave invalid response");
444
/* Find the entry by id */
445
entry = menu_find_by_id(main_menu, atoi(argv[2]));
447
report(RPT_WARNING, "Could not find the item id given by the server");
452
switch (entry->type) {
454
entry->data.slider.value = atoi(argv[3]);
457
entry->data.ring.value = atoi(argv[3]);
460
entry->data.numeric.value = atoi(argv[3]);
463
entry->data.alpha.value = realloc(entry->data.alpha.value,
465
strcpy(entry->data.alpha.value, argv[3]);
468
entry->data.ip.value = realloc(entry->data.ip.value,
470
strcpy(entry->data.ip.value, argv[3]);
472
case MT_ARG_CHECKBOX:
473
if ((entry->data.checkbox.allow_gray) &&
474
(strcasecmp(argv[3], "gray") == 0))
475
entry->data.checkbox.value = 2;
476
else if (strcasecmp(argv[3], "on") == 0)
477
entry->data.checkbox.value = 1;
479
entry->data.checkbox.value = 0;
482
report(RPT_WARNING, "Illegal menu entry type for event");
317
488
; /* Ignore other menuevents */
491
else if (strcmp(argv[0], "connect") == 0) {
494
/* determine display height and width */
495
for (a = 1; a < argc; a++) {
496
if (strcmp(argv[a], "wid") == 0)
497
lcd_wid = atoi(argv[++a]);
498
else if (strcmp(argv[a], "hgt") == 0)
499
lcd_hgt = atoi(argv[++a]);
502
else if (strcmp(argv[0], "bye") == 0) {
503
// TODO: make it better
504
report(RPT_INFO, "Server said: \"%s\"", str);
505
exit_program(EXIT_SUCCESS);
320
507
else if (strcmp(argv[0], "huh?") == 0) {
321
508
/* Report errors */
322
509
report(RPT_WARNING, "Server said: \"%s\"", str);
334
521
if ((cmd != NULL) && (menu_command(cmd) != NULL)) {
335
522
const char *command = menu_command(cmd);
336
523
const char *argv[4];
338
report(RPT_NOTICE, "Executing: %s", command);
526
char *envp[cmd->numChildren+1];
530
/* set argument vector */
340
531
argv[0] = default_shell;
342
533
argv[2] = command;
536
/* set environment vector: allocate & fill contents */
537
for (arg = cmd->children, i = 0; arg != NULL; arg = arg->next, i++) {
542
snprintf(buf, sizeof(buf)-1, "%s=%d",
543
arg->name, arg->data.slider.value);
546
snprintf(buf, sizeof(buf)-1, "%s=%s", arg->name,
547
arg->data.ring.strings[arg->data.ring.value]);
550
snprintf(buf, sizeof(buf)-1, "%s=%d",
551
arg->name, arg->data.numeric.value);
554
snprintf(buf, sizeof(buf)-1, "%s=%s",
555
arg->name, arg->data.alpha.value);
558
snprintf(buf, sizeof(buf)-1, "%s=%s",
559
arg->name, arg->data.ip.value);
561
case MT_ARG_CHECKBOX:
562
if (arg->data.checkbox.map[arg->data.checkbox.value] != NULL)
563
strncpy(buf, arg->data.checkbox.map[arg->data.checkbox.value],
566
snprintf(buf, sizeof(buf)-1, "%s=%d",
567
arg->name, arg->data.checkbox.value);
573
buf[sizeof(buf)-1] ='\0';
574
envp[i] = strdup(buf);
576
debug(RPT_DEBUG, "Environment: %s", envp[i]);
578
envp[cmd->numChildren] = NULL;
580
debug(RPT_DEBUG, "Executing '%s' via Shell %s", command, default_shell);
582
switch (pid = fork()) {
347
/* We're the child. Execute the command. */
348
execv(argv[0], (char **) argv);
584
/* We're the child: execute the command */
585
execve(argv[0], (char **) argv, envp);
589
/* We're the parent: setup the ProcInfo structure */
590
p = calloc(1, sizeof(ProcInfo));
594
p->starttime = time(NULL);
595
p->feedback = cmd->data.exec.feedback;
596
/* prepend it to existing queue atomically */
597
p->next = proc_queue;
352
602
report(RPT_ERR, "Could not fork");
355
/* We're the parent */
606
/* free envp's contents */
607
for (i = 0; envp[i] != NULL; i++)
616
static int show_procinfo_msg(ProcInfo *p)
618
if ((p != NULL) && (lcd_wid > 0) && (lcd_hgt > 0)) {
619
if (p->endtime > 0) {
620
/* nothing to do => the quick way out (successful) */
621
if ((p->shown) || (!p->feedback))
624
sock_printf(sock, "screen_add [%u]\n", p->pid);
625
sock_printf(sock, "screen_set [%u] -name {lcdexec [%u]}"
626
" -priority alert -timeout %d"
628
p->pid, p->pid, 6*8);
631
sock_printf(sock, "widget_add [%u] t title\n", p->pid);
632
sock_printf(sock, "widget_set [%u] t {%s}\n", p->pid, p->cmd->displayname);
633
sock_printf(sock, "widget_add [%u] s1 string\n", p->pid);
634
sock_printf(sock, "widget_add [%u] s2 string\n", p->pid);
635
sock_printf(sock, "widget_add [%u] s3 string\n", p->pid);
637
sock_printf(sock, "widget_set [%u] s1 1 2 {[%u] finished%s}\n",
638
p->pid, p->pid, (WIFSIGNALED(p->status) ? "," : ""));
640
if (WIFEXITED(p->status)) {
641
if (WEXITSTATUS(p->status) == EXIT_SUCCESS) {
642
sock_printf(sock, "widget_set [%u] s2 1 3 {successfully.}\n",
646
sock_printf(sock, "widget_set [%u] s2 1 3 {with code 0x%02X.}\n",
647
p->pid, WEXITSTATUS(p->status));
650
else if (WIFSIGNALED(p->status)) {
651
sock_printf(sock, "widget_set [%u] s2 1 3 {killed by SIG %d.}\n",
652
p->pid, WTERMSIG(p->status));
656
sock_printf(sock, "widget_set [%u] s3 1 4 {Exec time: %lds}\n",
657
p->pid, p->endtime - p->starttime);
660
sock_printf(sock, "widget_add [%u] s1 string\n", p->pid);
661
sock_printf(sock, "widget_add [%u] s2 string\n", p->pid);
662
sock_printf(sock, "widget_set [%u] s1 1 1 {%s}\n",
663
p->pid, p->cmd->displayname);
664
if (WIFEXITED(p->status)) {
665
if (WEXITSTATUS(p->status) == EXIT_SUCCESS) {
666
sock_printf(sock, "widget_set [%u] s2 1 2 {succeeded}\n",
670
sock_printf(sock, "widget_set [%u] s2 1 2 {finished (0x%02X)}\n",
674
else if (WIFSIGNALED(p->status)) {
675
sock_printf(sock, "widget_set [%u] s2 1 2 {killed by SIG %d}\n",
676
p->pid, WTERMSIG(p->status));
364
687
static int main_loop(void)
691
int keepalive_delay = 0;
692
int status_delay = 0;
370
694
/* Continuously check if we get a menu event... */
372
while ((num_bytes = sock_recv_string(sock, buf, sizeof(buf)-1)) >= 0) {
695
while (!Quit && ((num_bytes = sock_recv_string(sock, buf, sizeof(buf)-1)) >= 0)) {
373
696
if (num_bytes == 0) {
699
/* wait for 1/10th of a second */
376
/* Send an empty line every 3 seconds to make sure the server still exists */
702
/* send an empty line every 3 seconds to make sure the server still exists */
703
if (keepalive_delay++ >= 30) {
379
705
if (sock_send_string(sock, "\n") < 0) {
380
706
break; /* Out of while loop */
710
/* check for a screen to show and procinfo deletion every second */
711
if (status_delay++ >= 10) {
714
/* delete the ProcInfo from the queue */
715
for (p = proc_queue; p != NULL; p = p->next) {
716
ProcInfo *pn = p->next;
718
if ((pn != NULL) && (pn->shown)) {
723
/* deleting queue head is special */
724
if ((proc_queue != NULL) && (proc_queue->shown)) {
726
proc_queue = proc_queue->next;
730
/* look for a process to display, display it & mark it as shown */
731
for (p = proc_queue; p != NULL; p = p->next) {
732
p->shown |= show_procinfo_msg(p);
385
737
process_response(buf);
389
report(RPT_ERR, "Server disconnected (or connection error)");
742
report(RPT_ERR, "Server disconnected (or connection error)");