Executor: if no user tooltip is set, display stderr output

This commit is contained in:
o9000 2017-06-27 12:24:52 +02:00
parent ece3bc4d85
commit f45e107207
4 changed files with 191 additions and 69 deletions

View file

@ -700,6 +700,7 @@ void add_entry(char *key, char *value)
Execp *execp = get_or_create_last_execp(); Execp *execp = get_or_create_last_execp();
free_and_null(execp->backend->tooltip); free_and_null(execp->backend->tooltip);
execp->backend->tooltip = strdup(value); execp->backend->tooltip = strdup(value);
execp->backend->has_user_tooltip = TRUE;
} else if (strcmp(key, "execp_font") == 0) { } else if (strcmp(key, "execp_font") == 0) {
Execp *execp = get_or_create_last_execp(); Execp *execp = get_or_create_last_execp();
pango_font_description_free(execp->backend->font_desc); pango_font_description_free(execp->backend->font_desc);

View file

@ -30,9 +30,10 @@ void default_execp()
Execp *create_execp() Execp *create_execp()
{ {
Execp *execp = calloc(1, sizeof(Execp)); Execp *execp = (Execp *)calloc(1, sizeof(Execp));
execp->backend = calloc(1, sizeof(ExecpBackend)); execp->backend = (ExecpBackend *)calloc(1, sizeof(ExecpBackend));
execp->backend->child_pipe = -1; execp->backend->child_pipe_stdout = -1;
execp->backend->child_pipe_stderr = -1;
execp->backend->cmd_pids = g_tree_new(cmp_ptr); execp->backend->cmd_pids = g_tree_new(cmp_ptr);
execp->backend->interval = 30; execp->backend->interval = 30;
execp->backend->cache_icon = TRUE; execp->backend->cache_icon = TRUE;
@ -45,10 +46,10 @@ gpointer create_execp_frontend(gconstpointer arg, gpointer data)
{ {
Execp *execp_backend = (Execp *)arg; Execp *execp_backend = (Execp *)arg;
Execp *execp_frontend = calloc(1, sizeof(Execp)); Execp *execp_frontend = (Execp *)calloc(1, sizeof(Execp));
execp_frontend->backend = execp_backend->backend; execp_frontend->backend = execp_backend->backend;
execp_backend->backend->instances = g_list_append(execp_backend->backend->instances, execp_frontend); execp_backend->backend->instances = g_list_append(execp_backend->backend->instances, execp_frontend);
execp_frontend->frontend = calloc(1, sizeof(ExecpFrontend)); execp_frontend->frontend = (ExecpFrontend *)calloc(1, sizeof(ExecpFrontend));
return execp_frontend; return execp_frontend;
} }
@ -72,16 +73,20 @@ void destroy_execp(void *obj)
imlib_free_image(); imlib_free_image();
execp->backend->icon = NULL; execp->backend->icon = NULL;
} }
free_and_null(execp->backend->buf_output); free_and_null(execp->backend->buf_stdout);
free_and_null(execp->backend->text); free_and_null(execp->backend->text);
free_and_null(execp->backend->icon_path); free_and_null(execp->backend->icon_path);
if (execp->backend->child) { if (execp->backend->child) {
kill(-execp->backend->child, SIGHUP); kill(-execp->backend->child, SIGHUP);
execp->backend->child = 0; execp->backend->child = 0;
} }
if (execp->backend->child_pipe >= 0) { if (execp->backend->child_pipe_stdout >= 0) {
close(execp->backend->child_pipe); close(execp->backend->child_pipe_stdout);
execp->backend->child_pipe = -1; execp->backend->child_pipe_stdout = -1;
}
if (execp->backend->child_pipe_stderr >= 0) {
close(execp->backend->child_pipe_stderr);
execp->backend->child_pipe_stderr = -1;
} }
if (execp->backend->cmd_pids) { if (execp->backend->cmd_pids) {
g_tree_destroy(execp->backend->cmd_pids); g_tree_destroy(execp->backend->cmd_pids);
@ -138,8 +143,10 @@ void init_execp()
// Set missing config options // Set missing config options
if (!execp->backend->bg) if (!execp->backend->bg)
execp->backend->bg = &g_array_index(backgrounds, Background, 0); execp->backend->bg = &g_array_index(backgrounds, Background, 0);
execp->backend->buf_capacity = 1024; execp->backend->buf_stdout_capacity = 1024;
execp->backend->buf_output = calloc(execp->backend->buf_capacity, 1); execp->backend->buf_stdout = calloc(execp->backend->buf_stdout_capacity, 1);
execp->backend->buf_stderr_capacity = 1024;
execp->backend->buf_stderr = calloc(execp->backend->buf_stderr_capacity, 1);
execp->backend->text = strdup(""); execp->backend->text = strdup("");
execp->backend->icon_path = NULL; execp->backend->icon_path = NULL;
} }
@ -530,7 +537,7 @@ void execp_dump_geometry(void *obj, int indent)
void execp_force_update(Execp *execp) void execp_force_update(Execp *execp)
{ {
if (execp->backend->child_pipe > 0) { if (execp->backend->child_pipe_stdout > 0) {
// Command currently running, nothing to do // Command currently running, nothing to do
} else { } else {
if (execp->backend->timer) if (execp->backend->timer)
@ -592,32 +599,48 @@ void execp_timer_callback(void *arg)
return; return;
// Still running! // Still running!
if (execp->backend->child_pipe > 0) if (execp->backend->child_pipe_stdout > 0)
return; return;
int pipe_fd[2]; int pipe_fd_stdout[2];
if (pipe(pipe_fd)) { if (pipe(pipe_fd_stdout)) {
// TODO maybe write this in tooltip, but if this happens we're screwed anyways // TODO maybe write this in tooltip, but if this happens we're screwed anyways
fprintf(stderr, "Execp: Creating pipe failed!\n"); fprintf(stderr, "Execp: Creating pipe failed!\n");
return; return;
} }
fcntl(pipe_fd[0], F_SETFL, O_NONBLOCK | fcntl(pipe_fd[0], F_GETFL)); fcntl(pipe_fd_stdout[0], F_SETFL, O_NONBLOCK | fcntl(pipe_fd_stdout[0], F_GETFL));
int pipe_fd_stderr[2];
if (pipe(pipe_fd_stderr)) {
close(pipe_fd_stdout[1]);
close(pipe_fd_stdout[0]);
// TODO maybe write this in tooltip, but if this happens we're screwed anyways
fprintf(stderr, "Execp: Creating pipe failed!\n");
return;
}
fcntl(pipe_fd_stderr[0], F_SETFL, O_NONBLOCK | fcntl(pipe_fd_stderr[0], F_GETFL));
// Fork and run command, capturing stdout in pipe // Fork and run command, capturing stdout in pipe
pid_t child = fork(); pid_t child = fork();
if (child == -1) { if (child == -1) {
// TODO maybe write this in tooltip, but if this happens we're screwed anyways // TODO maybe write this in tooltip, but if this happens we're screwed anyways
fprintf(stderr, "Fork failed.\n"); fprintf(stderr, "Fork failed.\n");
close(pipe_fd[1]); close(pipe_fd_stdout[1]);
close(pipe_fd[0]); close(pipe_fd_stdout[0]);
close(pipe_fd_stderr[1]);
close(pipe_fd_stderr[0]);
return; return;
} else if (child == 0) { } else if (child == 0) {
fprintf(stderr, "Executing: %s\n", execp->backend->command); fprintf(stderr, "Executing: %s\n", execp->backend->command);
// We are in the child // We are in the child
close(pipe_fd[0]); close(pipe_fd_stdout[0]);
dup2(pipe_fd[1], 1); // 1 is stdout dup2(pipe_fd_stdout[1], 1); // 1 is stdout
close(pipe_fd[1]); close(pipe_fd_stdout[1]);
close(pipe_fd_stderr[0]);
dup2(pipe_fd_stderr[1], 2); // 2 is stderr
close(pipe_fd_stderr[1]);
setpgid(0, 0); setpgid(0, 0);
execl("/bin/sh", "/bin/sh", "-c", execp->backend->command, NULL); execl("/bin/sh", "/bin/sh", "-c", execp->backend->command, NULL);
// This should never happen! // This should never happen!
@ -625,39 +648,38 @@ void execp_timer_callback(void *arg)
fflush(stdout); fflush(stdout);
exit(0); exit(0);
} }
close(pipe_fd[1]); close(pipe_fd_stdout[1]);
close(pipe_fd_stderr[1]);
execp->backend->child = child; execp->backend->child = child;
execp->backend->child_pipe = pipe_fd[0]; execp->backend->child_pipe_stdout = pipe_fd_stdout[0];
execp->backend->buf_length = 0; execp->backend->child_pipe_stderr = pipe_fd_stderr[0];
execp->backend->buf_output[execp->backend->buf_length] = '\0'; execp->backend->buf_stdout_length = 0;
execp->backend->buf_stdout[execp->backend->buf_stdout_length] = '\0';
execp->backend->buf_stderr_length = 0;
execp->backend->buf_stderr[execp->backend->buf_stderr_length] = '\0';
execp->backend->last_update_start_time = time(NULL); execp->backend->last_update_start_time = time(NULL);
} }
gboolean read_execp(void *obj) void read_from_pipe(int fd, char **buffer, ssize_t *buffer_length, ssize_t *buffer_capacity, gboolean *eof)
{ {
Execp *execp = (Execp *)obj; *eof = FALSE;
if (execp->backend->child_pipe < 0)
return FALSE;
gboolean command_finished = FALSE;
while (1) { while (1) {
// Make sure there is free space in the buffer // Make sure there is free space in the buffer
if (execp->backend->buf_capacity - execp->backend->buf_length < 1024) { if (*buffer_capacity - *buffer_length < 1024) {
execp->backend->buf_capacity *= 2; *buffer_capacity *= 2;
execp->backend->buf_output = realloc(execp->backend->buf_output, execp->backend->buf_capacity); *buffer = (char *)realloc(*buffer, *buffer_capacity);
} }
ssize_t count = read(execp->backend->child_pipe, ssize_t count = read(fd,
execp->backend->buf_output + execp->backend->buf_length, *buffer + *buffer_length,
execp->backend->buf_capacity - execp->backend->buf_length - 1); *buffer_capacity - *buffer_length - 1);
if (count > 0) { if (count > 0) {
// Successful read // Successful read
execp->backend->buf_length += count; *buffer_length += count;
execp->backend->buf_output[execp->backend->buf_length] = '\0'; (*buffer)[*buffer_length] = '\0';
continue; continue;
} else if (count == 0) { } else if (count == 0) {
// End of file // End of file
command_finished = TRUE; *eof = TRUE;
break; break;
} else if (errno == EAGAIN || errno == EWOULDBLOCK) { } else if (errno == EAGAIN || errno == EWOULDBLOCK) {
// No more data available at the moment // No more data available at the moment
@ -667,28 +689,87 @@ gboolean read_execp(void *obj)
continue; continue;
} else { } else {
// Error // Error
command_finished = TRUE; *eof = TRUE;
break; break;
} }
break; break;
} }
}
gboolean starts_with(char *s, char *prefix)
{
char *p, *q;
for (p = s, q = prefix; *p && *q; p++, q++) {
if (*p != *q)
return FALSE;
}
return *q == '\0';
}
char *last_substring(char *s, char *sub)
{
char *result = NULL;
for (char *p = s; *p; p++) {
if (starts_with(p, sub))
result = p;
}
return result;
}
void rstrip(char *s)
{
size_t len = strlen(s);
while (len > 0) {
if (s[len-1] == ' ' || s[len-1] == '\n') {
s[len-1] = 0;
len--;
} else {
break;
}
}
}
gboolean read_execp(void *obj)
{
Execp *execp = (Execp *)obj;
if (execp->backend->child_pipe_stdout < 0)
return FALSE;
gboolean stdout_eof, stderr_eof;
read_from_pipe(execp->backend->child_pipe_stdout,
&execp->backend->buf_stdout,
&execp->backend->buf_stdout_length,
&execp->backend->buf_stdout_capacity,
&stdout_eof);
read_from_pipe(execp->backend->child_pipe_stderr,
&execp->backend->buf_stderr,
&execp->backend->buf_stderr_length,
&execp->backend->buf_stderr_capacity,
&stderr_eof);
gboolean command_finished = stdout_eof && stderr_eof;
if (command_finished) { if (command_finished) {
execp->backend->child = 0; execp->backend->child = 0;
close(execp->backend->child_pipe); close(execp->backend->child_pipe_stdout);
execp->backend->child_pipe = -1; execp->backend->child_pipe_stdout = -1;
close(execp->backend->child_pipe_stderr);
execp->backend->child_pipe_stderr = -1;
if (execp->backend->interval) if (execp->backend->interval)
execp->backend->timer = execp->backend->timer =
add_timeout(execp->backend->interval * 1000, 0, execp_timer_callback, execp, &execp->backend->timer); add_timeout(execp->backend->interval * 1000, 0, execp_timer_callback, execp, &execp->backend->timer);
} }
char *ansi_clear_screen = (char*)"\x1b[2J";
if (!execp->backend->continuous && command_finished) { if (!execp->backend->continuous && command_finished) {
// Handle stdout
free_and_null(execp->backend->text); free_and_null(execp->backend->text);
free_and_null(execp->backend->icon_path); free_and_null(execp->backend->icon_path);
if (!execp->backend->has_icon) { if (!execp->backend->has_icon) {
execp->backend->text = strdup(execp->backend->buf_output); execp->backend->text = strdup(execp->backend->buf_stdout);
} else { } else {
char *text = strchr(execp->backend->buf_output, '\n'); char *text = strchr(execp->backend->buf_stdout, '\n');
if (text) { if (text) {
*text = '\0'; *text = '\0';
text++; text++;
@ -696,22 +777,52 @@ gboolean read_execp(void *obj)
} else { } else {
execp->backend->text = strdup(""); execp->backend->text = strdup("");
} }
execp->backend->icon_path = strdup(execp->backend->buf_output); execp->backend->icon_path = strdup(execp->backend->buf_stdout);
} }
int len = strlen(execp->backend->text); int len = strlen(execp->backend->text);
if (len > 0 && execp->backend->text[len - 1] == '\n') if (len > 0 && execp->backend->text[len - 1] == '\n')
execp->backend->text[len - 1] = '\0'; execp->backend->text[len - 1] = '\0';
execp->backend->buf_length = 0; execp->backend->buf_stdout_length = 0;
execp->backend->buf_output[execp->backend->buf_length] = '\0'; execp->backend->buf_stdout[execp->backend->buf_stdout_length] = '\0';
// Handle stderr
if (!execp->backend->has_user_tooltip) {
free_and_null(execp->backend->tooltip);
char *start = last_substring(execp->backend->buf_stderr, ansi_clear_screen);
if (start)
start += strlen(ansi_clear_screen);
else
start = execp->backend->buf_stderr;
execp->backend->tooltip = strdup(start);
rstrip(execp->backend->tooltip);
}
execp->backend->buf_stderr_length = 0;
execp->backend->buf_stderr[execp->backend->buf_stderr_length] = '\0';
//
execp->backend->last_update_finish_time = time(NULL); execp->backend->last_update_finish_time = time(NULL);
execp->backend->last_update_duration = execp->backend->last_update_duration =
execp->backend->last_update_finish_time - execp->backend->last_update_start_time; execp->backend->last_update_finish_time - execp->backend->last_update_start_time;
return TRUE; return TRUE;
} else if (execp->backend->continuous > 0) { } else if (execp->backend->continuous > 0) {
// Handle stderr
if (!execp->backend->has_user_tooltip) {
free_and_null(execp->backend->tooltip);
char *start = last_substring(execp->backend->buf_stderr, ansi_clear_screen);
if (start) {
start += strlen(ansi_clear_screen);
memmove(execp->backend->buf_stderr, start, strlen(start) + 1);
execp->backend->buf_stderr_length = (ssize_t)strlen(execp->backend->buf_stderr);
}
execp->backend->tooltip = strdup(execp->backend->buf_stderr);
rstrip(execp->backend->tooltip);
} else {
execp->backend->buf_stderr_length = 0;
execp->backend->buf_stderr[execp->backend->buf_stderr_length] = '\0';
}
// Handle stdout
// Count lines in buffer // Count lines in buffer
int num_lines = 0; int num_lines = 0;
char *end = NULL; char *end = NULL;
for (char *c = execp->backend->buf_output; *c; c++) { for (char *c = execp->backend->buf_stdout; *c; c++) {
if (*c == '\n') { if (*c == '\n') {
num_lines++; num_lines++;
if (num_lines == execp->backend->continuous) if (num_lines == execp->backend->continuous)
@ -724,9 +835,9 @@ gboolean read_execp(void *obj)
free_and_null(execp->backend->text); free_and_null(execp->backend->text);
free_and_null(execp->backend->icon_path); free_and_null(execp->backend->icon_path);
if (!execp->backend->has_icon) { if (!execp->backend->has_icon) {
execp->backend->text = strdup(execp->backend->buf_output); execp->backend->text = strdup(execp->backend->buf_stdout);
} else { } else {
char *text = strchr(execp->backend->buf_output, '\n'); char *text = strchr(execp->backend->buf_stdout, '\n');
if (text) { if (text) {
*text = '\0'; *text = '\0';
text++; text++;
@ -734,23 +845,23 @@ gboolean read_execp(void *obj)
} else { } else {
execp->backend->text = strdup(""); execp->backend->text = strdup("");
} }
execp->backend->icon_path = strdup(execp->backend->buf_output); execp->backend->icon_path = strdup(execp->backend->buf_stdout);
} }
int len = strlen(execp->backend->text); size_t len = strlen(execp->backend->text);
if (len > 0 && execp->backend->text[len - 1] == '\n') if (len > 0 && execp->backend->text[len - 1] == '\n')
execp->backend->text[len - 1] = '\0'; execp->backend->text[len - 1] = '\0';
if (end) { if (end) {
char *next = end + 1; char *next = end + 1;
int copied = next - execp->backend->buf_output; ssize_t copied = next - execp->backend->buf_stdout;
int remaining = execp->backend->buf_length - copied; ssize_t remaining = execp->backend->buf_stdout_length - copied;
if (remaining > 0) { if (remaining > 0) {
memmove(execp->backend->buf_output, next, remaining); memmove(execp->backend->buf_stdout, next, (size_t)remaining);
execp->backend->buf_length = remaining; execp->backend->buf_stdout_length = remaining;
execp->backend->buf_output[execp->backend->buf_length] = '\0'; execp->backend->buf_stdout[execp->backend->buf_stdout_length] = '\0';
} else { } else {
execp->backend->buf_length = 0; execp->backend->buf_stdout_length = 0;
execp->backend->buf_output[execp->backend->buf_length] = '\0'; execp->backend->buf_stdout[execp->backend->buf_stdout_length] = '\0';
} }
} }
@ -799,7 +910,7 @@ char *execp_get_tooltip(void *obj)
char tmp_buf1[256]; char tmp_buf1[256];
char tmp_buf2[256]; char tmp_buf2[256];
char tmp_buf3[256]; char tmp_buf3[256];
if (execp->backend->child_pipe < 0) { if (execp->backend->child_pipe_stdout < 0) {
// Not executing command // Not executing command
if (execp->backend->last_update_finish_time) { if (execp->backend->last_update_finish_time) {
// We updated at least once // We updated at least once

View file

@ -27,6 +27,7 @@ typedef struct ExecpBackend {
gboolean cache_icon; gboolean cache_icon;
int icon_w; int icon_w;
int icon_h; int icon_h;
gboolean has_user_tooltip;
char *tooltip; char *tooltip;
gboolean centered; gboolean centered;
gboolean has_font; gboolean has_font;
@ -46,20 +47,24 @@ typedef struct ExecpBackend {
// Backend state: // Backend state:
timeout *timer; timeout *timer;
int child_pipe; int child_pipe_stdout;
int child_pipe_stderr;
pid_t child; pid_t child;
// Command output buffer // Command output buffer
char *buf_output; char *buf_stdout;
int buf_length; ssize_t buf_stdout_length;
int buf_capacity; ssize_t buf_stdout_capacity;
char *buf_stderr;
ssize_t buf_stderr_length;
ssize_t buf_stderr_capacity;
// Text extracted from the output buffer // Text extracted from the output buffer
char *text; char *text;
// Icon path extracted from the output buffer // Icon path extracted from the output buffer
char *icon_path; char *icon_path;
Imlib_Image icon; Imlib_Image icon;
char tooltip_text[512]; gchar tooltip_text[512];
// The time the last command was started // The time the last command was started
time_t last_update_start_time; time_t last_update_start_time;

View file

@ -1832,7 +1832,12 @@ start:
} }
for (GList *l = panel_config.execp_list; l; l = l->next) { for (GList *l = panel_config.execp_list; l; l = l->next) {
Execp *execp = (Execp *)l->data; Execp *execp = (Execp *)l->data;
int fd = execp->backend->child_pipe; int fd = execp->backend->child_pipe_stdout;
if (fd > 0) {
FD_SET(fd, &fdset);
maxfd = maxfd < fd ? fd : maxfd;
}
fd = execp->backend->child_pipe_stderr;
if (fd > 0) { if (fd > 0) {
FD_SET(fd, &fdset); FD_SET(fd, &fdset);
maxfd = maxfd < fd ? fd : maxfd; maxfd = maxfd < fd ? fd : maxfd;
@ -1861,7 +1866,7 @@ start:
if (read_execp(execp)) { if (read_execp(execp)) {
GList *l_instance; GList *l_instance;
for (l_instance = execp->backend->instances; l_instance; l_instance = l_instance->next) { for (l_instance = execp->backend->instances; l_instance; l_instance = l_instance->next) {
Execp *instance = l_instance->data; Execp *instance = (Execp *)l_instance->data;
execp_update_post_read(instance); execp_update_post_read(instance);
} }
} }