display info screen upon exit of a command invoked by lcdexec
This commit is contained in:
+168
-38
@@ -14,6 +14,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/utsname.h>
|
#include <sys/utsname.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <time.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
#include "getopt.h"
|
#include "getopt.h"
|
||||||
@@ -31,6 +32,17 @@
|
|||||||
|
|
||||||
#define DEFAULT_CONFIGFILE SYSCONFDIR "/lcdexec.conf"
|
#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 =
|
char * help_text =
|
||||||
"lcdexec - LCDproc client to execute commands from the LCDd menu\n"
|
"lcdexec - LCDproc client to execute commands from the LCDd menu\n"
|
||||||
@@ -53,8 +65,8 @@ char *progname = "lcdexec";
|
|||||||
/* Variables set by config */
|
/* Variables set by config */
|
||||||
#define UNSET_INT -1
|
#define UNSET_INT -1
|
||||||
#define UNSET_STR "\01"
|
#define UNSET_STR "\01"
|
||||||
char * configfile = NULL;
|
char *configfile = NULL;
|
||||||
char * address = NULL;
|
char *address = NULL;
|
||||||
int port = UNSET_INT;
|
int port = UNSET_INT;
|
||||||
int foreground = FALSE;
|
int foreground = FALSE;
|
||||||
static int report_level = UNSET_INT;
|
static int report_level = UNSET_INT;
|
||||||
@@ -62,23 +74,26 @@ static int report_dest = UNSET_INT;
|
|||||||
char *displayname = NULL;
|
char *displayname = NULL;
|
||||||
char *default_shell = NULL;
|
char *default_shell = NULL;
|
||||||
|
|
||||||
MenuEntry *main_menu;
|
|
||||||
|
|
||||||
/* Other variables */
|
/* 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 */
|
/* Function prototypes */
|
||||||
|
static void sigchld_handler(int);
|
||||||
static int process_command_line(int argc, char **argv);
|
static int process_command_line(int argc, char **argv);
|
||||||
static int process_configfile(char * configfile);
|
static int process_configfile(char * configfile);
|
||||||
static int connect_and_setup(void);
|
static int connect_and_setup(void);
|
||||||
static int process_response(char *str);
|
static int process_response(char *str);
|
||||||
static int exec_command(MenuEntry *cmd);
|
static int exec_command(MenuEntry *cmd);
|
||||||
|
static int show_procinfo_msg(ProcInfo *p);
|
||||||
static int main_loop(void);
|
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(e,f) { if (e>=0) { e=(f); }}
|
||||||
#define CHAIN_END(e) { if (e<0) { report(RPT_CRIT,"Critical error, abort"); exit(e); }}
|
#define CHAIN_END(e) { if (e<0) { report(RPT_CRIT,"Critical error, abort"); exit(e); }}
|
||||||
|
|
||||||
@@ -127,25 +142,17 @@ static void sigchld_handler(int signal)
|
|||||||
pid_t pid;
|
pid_t pid;
|
||||||
int status;
|
int status;
|
||||||
|
|
||||||
|
/* wait for the child that was signalled as finished */
|
||||||
if ((pid = wait(&status)) != -1) {
|
if ((pid = wait(&status)) != -1) {
|
||||||
last_child_pid = pid;
|
ProcInfo *p;
|
||||||
last_child_status = status;
|
|
||||||
|
|
||||||
// better:
|
/* fill the procinfo structure with the necessary information */
|
||||||
// - build a queue with (pid,status) pairs (here)
|
for (p = proc_queue; p != NULL; p = p->next) {
|
||||||
// - show a screen for each pair (in the main_loop)
|
if (p->pid == pid) {
|
||||||
// - remove elements from the queue which are shown (dito)
|
p->status = status;
|
||||||
// even better:
|
p->endtime = time(NULL);
|
||||||
// - 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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -314,14 +321,14 @@ static int connect_and_setup(void)
|
|||||||
|
|
||||||
static int process_response(char *str)
|
static int process_response(char *str)
|
||||||
{
|
{
|
||||||
char *argv[15];
|
char *argv[20];
|
||||||
int argc;
|
int argc;
|
||||||
char *str2 = strdup(str); /* get_args modifies str2 */
|
char *str2 = strdup(str); /* get_args modifies str2 */
|
||||||
|
|
||||||
report(RPT_DEBUG, "Server said: \"%s\"", str);
|
report(RPT_DEBUG, "Server said: \"%s\"", str);
|
||||||
|
|
||||||
/* Check what the server just said to us... */
|
/* 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) {
|
if (argc < 1) {
|
||||||
free(str2);
|
free(str2);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -358,6 +365,23 @@ static int process_response(char *str)
|
|||||||
; /* Ignore other menuevents */
|
; /* 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) {
|
else if (strcmp(argv[0], "huh?") == 0) {
|
||||||
/* Report errors */
|
/* Report errors */
|
||||||
report(RPT_WARNING, "Server said: \"%s\"", str);
|
report(RPT_WARNING, "Server said: \"%s\"", str);
|
||||||
@@ -375,6 +399,8 @@ static int exec_command(MenuEntry *cmd)
|
|||||||
if ((cmd != NULL) && (menu_command(cmd) != NULL)) {
|
if ((cmd != NULL) && (menu_command(cmd) != NULL)) {
|
||||||
const char *command = menu_command(cmd);
|
const char *command = menu_command(cmd);
|
||||||
const char *argv[4];
|
const char *argv[4];
|
||||||
|
pid_t pid;
|
||||||
|
ProcInfo *p;
|
||||||
|
|
||||||
report(RPT_NOTICE, "Executing: %s", command);
|
report(RPT_NOTICE, "Executing: %s", command);
|
||||||
|
|
||||||
@@ -383,18 +409,27 @@ static int exec_command(MenuEntry *cmd)
|
|||||||
argv[2] = command;
|
argv[2] = command;
|
||||||
argv[3] = NULL;
|
argv[3] = NULL;
|
||||||
|
|
||||||
switch (fork()) {
|
switch (pid = fork()) {
|
||||||
case 0:
|
case 0:
|
||||||
/* We're the child. Execute the command. */
|
/* We're the child: execute the command */
|
||||||
execv(argv[0], (char **) argv);
|
execv(argv[0], (char **) argv);
|
||||||
exit(0);
|
exit(0);
|
||||||
break;
|
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:
|
case -1:
|
||||||
report(RPT_ERR, "Could not fork");
|
report(RPT_ERR, "Could not fork");
|
||||||
return -1;
|
return -1;
|
||||||
default:
|
|
||||||
/* We're the parent */
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return 0;
|
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)
|
static int main_loop(void)
|
||||||
{
|
{
|
||||||
int num_bytes;
|
int num_bytes;
|
||||||
char buf[100];
|
char buf[100];
|
||||||
int w = 0;
|
int keepalive_delay = 0;
|
||||||
|
int status_delay = 0;
|
||||||
|
|
||||||
/* Continuously check if we get a menu event... */
|
/* Continuously check if we get a menu event... */
|
||||||
|
|
||||||
while ((num_bytes = sock_recv_string(sock, buf, sizeof(buf)-1)) >= 0) {
|
while ((num_bytes = sock_recv_string(sock, buf, sizeof(buf)-1)) >= 0) {
|
||||||
if (num_bytes == 0) {
|
if (num_bytes == 0) {
|
||||||
|
ProcInfo *p;
|
||||||
|
|
||||||
|
/* wait for 1/10th of a second */
|
||||||
usleep(100000);
|
usleep(100000);
|
||||||
|
|
||||||
/* Send an empty line every 3 seconds to make sure the server still exists */
|
/* send an empty line every 3 seconds to make sure the server still exists */
|
||||||
if (w++ >= 30) {
|
if (keepalive_delay++ >= 30) {
|
||||||
w = 0;
|
keepalive_delay = 0;
|
||||||
if (sock_send_string(sock, "\n") < 0) {
|
if (sock_send_string(sock, "\n") < 0) {
|
||||||
break; /* Out of while loop */
|
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 {
|
else {
|
||||||
process_response(buf);
|
process_response(buf);
|
||||||
@@ -430,3 +559,4 @@ static int main_loop(void)
|
|||||||
report(RPT_ERR, "Server disconnected (or connection error)");
|
report(RPT_ERR, "Server disconnected (or connection error)");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user