diff --git a/clients/lcdexec/lcdexec.c b/clients/lcdexec/lcdexec.c index c78aca6..f47b12d 100644 --- a/clients/lcdexec/lcdexec.c +++ b/clients/lcdexec/lcdexec.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "getopt.h" @@ -31,6 +32,17 @@ #define DEFAULT_CONFIGFILE SYSCONFDIR "/lcdexec.conf" +/** information about a process started by lcdexec */ +typedef struct ProcInfo { + struct ProcInfo *next; /**< pointer to the next ProcInfo entry */ + const MenuEntry *cmd; /**< pointer to the corresponding menu entry */ + pid_t pid; /**< PID the process was started with */ + time_t starttime; /**< start time of the process */ + time_t endtime; /**< finishing time of the process */ + int status; /**< exit status of the process */ + int shown; /**< tell if the info has been shown to the user */ +} ProcInfo; + char * help_text = "lcdexec - LCDproc client to execute commands from the LCDd menu\n" @@ -53,8 +65,8 @@ char *progname = "lcdexec"; /* Variables set by config */ #define UNSET_INT -1 #define UNSET_STR "\01" -char * configfile = NULL; -char * address = NULL; +char *configfile = NULL; +char *address = NULL; int port = UNSET_INT; int foreground = FALSE; static int report_level = UNSET_INT; @@ -62,23 +74,26 @@ static int report_dest = UNSET_INT; char *displayname = NULL; char *default_shell = NULL; -MenuEntry *main_menu; - /* Other variables */ -int sock = -1; +MenuEntry *main_menu = NULL; /**< pointer to the main menu */ +ProcInfo *proc_queue = NULL; /**< pointer to the list of executed processes */ + +int lcd_wid = 0; /**< LCD display width reported by the server */ +int lcd_hgt = 0; /**< LCD display height reported by the server */ + +int sock = -1; /**< socket to connect to server */ + /* Function prototypes */ +static void sigchld_handler(int); static int process_command_line(int argc, char **argv); static int process_configfile(char * configfile); static int connect_and_setup(void); static int process_response(char *str); static int exec_command(MenuEntry *cmd); +static int show_procinfo_msg(ProcInfo *p); static int main_loop(void); -static pid_t last_child_pid = 0; -static int last_child_status = 0; -static void sigchld_handler(int); - #define CHAIN(e,f) { if (e>=0) { e=(f); }} #define CHAIN_END(e) { if (e<0) { report(RPT_CRIT,"Critical error, abort"); exit(e); }} @@ -124,29 +139,21 @@ int main(int argc, char **argv) /* the grim reaper ;-) */ static void sigchld_handler(int signal) { - pid_t pid; - int status; + pid_t pid; + int status; - if ((pid = wait(&status)) != -1) { - last_child_pid = pid; - last_child_status = status; + /* wait for the child that was signalled as finished */ + if ((pid = wait(&status)) != -1) { + ProcInfo *p; - // better: - // - build a queue with (pid,status) pairs (here) - // - show a screen for each pair (in the main_loop) - // - remove elements from the queue which are shown (dito) - // even better: - // - build a queue of (pid, name) on exec (in exec command) - // - display a screen about start (in main_loop) - // - add status to the appropriate queue element (here) - // - display screen about exit (in main_loop) - // - remove elements from the queue which are shown (dito) - // screens should be shown in main_loop to - // - not show a startup screen when the command is already done - // (note: danger for a race between sig_handler and main_loop, - // in the 1st case above, since both work on the queue) - // - automatically time screens - } + /* fill the procinfo structure with the necessary information */ + for (p = proc_queue; p != NULL; p = p->next) { + if (p->pid == pid) { + p->status = status; + p->endtime = time(NULL); + } + } + } } @@ -314,14 +321,14 @@ static int connect_and_setup(void) static int process_response(char *str) { - char *argv[15]; + char *argv[20]; int argc; char *str2 = strdup(str); /* get_args modifies str2 */ report(RPT_DEBUG, "Server said: \"%s\"", str); /* Check what the server just said to us... */ - argc = get_args(argv, str2, 10); + argc = get_args(argv, str2, sizeof(argv)/sizeof(argv[0])); if (argc < 1) { free(str2); return 0; @@ -358,6 +365,23 @@ static int process_response(char *str) ; /* Ignore other menuevents */ } } + else if (strcmp(argv[0], "connect") == 0) { + int a; + + /* determine display height and width */ + for (a = 1; a < argc; a++) { + if (strcmp(argv[a], "wid") == 0) + lcd_wid = atoi(argv[++a]); + else if (strcmp(argv[a], "hgt") == 0) + lcd_hgt = atoi(argv[++a]); + } + } + else if (strcmp(argv[0], "bye") == 0) { + // TODO: make it better + report(RPT_INFO, "Server said: \"%s\"", str); + sock_close(sock); + exit(EXIT_SUCCESS); + } else if (strcmp(argv[0], "huh?") == 0) { /* Report errors */ report(RPT_WARNING, "Server said: \"%s\"", str); @@ -375,6 +399,8 @@ static int exec_command(MenuEntry *cmd) if ((cmd != NULL) && (menu_command(cmd) != NULL)) { const char *command = menu_command(cmd); const char *argv[4]; + pid_t pid; + ProcInfo *p; report(RPT_NOTICE, "Executing: %s", command); @@ -383,18 +409,27 @@ static int exec_command(MenuEntry *cmd) argv[2] = command; argv[3] = NULL; - switch (fork()) { + switch (pid = fork()) { case 0: - /* We're the child. Execute the command. */ + /* We're the child: execute the command */ execv(argv[0], (char **) argv); exit(0); break; + default: + /* We're the parent: setup the ProcInfo structure */ + p = calloc(1, sizeof(ProcInfo)); + if (p != NULL) { + p->cmd = cmd; + p->pid = pid; + p->starttime = time(NULL); + /* prepend it to existing queue atomically */ + p->next = proc_queue; + proc_queue = p; + } + break; case -1: report(RPT_ERR, "Could not fork"); return -1; - default: - /* We're the parent */ - break; } return 0; } @@ -402,25 +437,119 @@ static int exec_command(MenuEntry *cmd) } +static int show_procinfo_msg(ProcInfo *p) +{ + if ((p != NULL) && (lcd_wid > 0) && (lcd_hgt > 0)) { + if (p->endtime > 0) { + sock_printf(sock, "screen_add [%u]\n", p->pid); + sock_printf(sock, "screen_set [%u] -name {lcdexec [%u]}" + " -priority alert -timeout %d" + " -heartbeat off\n", + p->pid, p->pid, 8*8); + + if (lcd_hgt > 2) { + sock_printf(sock, "widget_add [%u] t title\n", p->pid); + sock_printf(sock, "widget_set [%u] t {%s}\n", p->pid, p->cmd->displayname); + sock_printf(sock, "widget_add [%u] s1 string\n", p->pid); + sock_printf(sock, "widget_add [%u] s2 string\n", p->pid); + sock_printf(sock, "widget_add [%u] s3 string\n", p->pid); + + sock_printf(sock, "widget_set [%u] s1 1 2 {[%u] finished%s}\n", + p->pid, p->pid, (WIFSIGNALED(p->status) ? "," : "")); + + if (WIFEXITED(p->status)) { + if (WEXITSTATUS(p->status) == EXIT_SUCCESS) { + sock_printf(sock, "widget_set [%u] s2 1 3 {successfully.}\n", + p->pid); + } + else { + sock_printf(sock, "widget_set [%u] s2 1 3 {with code 0x%02X.}\n", + p->pid, WEXITSTATUS(p->status)); + } + } + else if (WIFSIGNALED(p->status)) { + sock_printf(sock, "widget_set [%u] s2 1 3 {killed by SIG %d.}\n", + p->pid, WTERMSIG(p->status)); + } + + sock_printf(sock, "widget_set [%u] s3 1 4 {Exec time: %lds}\n", + p->pid, p->endtime - p->starttime); + } + else { + sock_printf(sock, "widget_add [%u] s1 string\n", p->pid); + sock_printf(sock, "widget_add [%u] s2 string\n", p->pid); + sock_printf(sock, "widget_set [%u] s1 1 1 {%s}\n", + p->pid, p->cmd->displayname); + if (WIFEXITED(p->status)) { + if (WEXITSTATUS(p->status) == EXIT_SUCCESS) { + sock_printf(sock, "widget_set [%u] s2 1 2 {succeeded}\n", + p->pid, p->status); + } + else { + sock_printf(sock, "widget_set [%u] s2 1 2 {finished (0x%02X)}\n", + p->pid, p->status); + } + } + else if (WIFSIGNALED(p->status)) { + sock_printf(sock, "widget_set [%u] s2 1 2 {killed by SIG %d}\n", + p->pid, WTERMSIG(p->status)); + + } + } + } + } + return 1; +} + + static int main_loop(void) { int num_bytes; char buf[100]; - int w = 0; + int keepalive_delay = 0; + int status_delay = 0; /* Continuously check if we get a menu event... */ - while ((num_bytes = sock_recv_string(sock, buf, sizeof(buf)-1)) >= 0) { if (num_bytes == 0) { + ProcInfo *p; + + /* wait for 1/10th of a second */ usleep(100000); - /* Send an empty line every 3 seconds to make sure the server still exists */ - if (w++ >= 30) { - w = 0; + /* send an empty line every 3 seconds to make sure the server still exists */ + if (keepalive_delay++ >= 30) { + keepalive_delay = 0; if (sock_send_string(sock, "\n") < 0) { break; /* Out of while loop */ } } + + /* check for a screen to show and procinfo deletion every second */ + if (status_delay++ >= 10) { + status_delay = 0; + + /* delete the ProcInfo from the queue */ + for (p = proc_queue; p != NULL; p = p->next) { + ProcInfo *pn = p->next; + + if ((pn != NULL) && (pn->shown)) { + p->next = pn->next; + free(pn); + } + } + /* deleting queue head is special */ + if ((proc_queue != NULL) && (proc_queue->shown)) { + p = proc_queue; + proc_queue = proc_queue->next; + free(p); + } + + /* look for a process to display, display it & mark it as shown */ + for (p = proc_queue; p != NULL; p = p->next) { + p->shown |= show_procinfo_msg(p); + } + } } else { process_response(buf); @@ -430,3 +559,4 @@ static int main_loop(void) report(RPT_ERR, "Server disconnected (or connection error)"); return 0; } +