X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=i3bar%2Fsrc%2Fchild.c;h=52019b368611753be6d7d08352da1d27fbcc98ca;hb=9c15b9504ec4c7201376781234e5611215d120ee;hp=4e5e49c928edf37f1e3ff450c0cb5b5c0f87ddaa;hpb=8677936f6ccab4f733580d72926a06a8e1f081a2;p=i3%2Fi3 diff --git a/i3bar/src/child.c b/i3bar/src/child.c index 4e5e49c9..52019b36 100644 --- a/i3bar/src/child.c +++ b/i3bar/src/child.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -22,11 +23,12 @@ #include #include #include +#include #include "common.h" /* Global variables for child_*() */ -i3bar_child child = { 0 }; +i3bar_child child = {}; /* stdin- and sigchild-watchers */ ev_io *stdin_io; @@ -59,6 +61,58 @@ char *statusline_buffer = NULL; int child_stdin; +/* + * Clears all blocks from the statusline structure in memory and frees their + * associated resources. + */ +static void clear_status_blocks() { + struct status_block *first; + while (!TAILQ_EMPTY(&statusline_head)) { + first = TAILQ_FIRST(&statusline_head); + I3STRING_FREE(first->full_text); + TAILQ_REMOVE(&statusline_head, first, blocks); + free(first); + } +} + +/* + * Replaces the statusline in memory with an error message. Pass a format + * string and format parameters as you would in `printf'. The next time + * `draw_bars' is called, the error message text will be drawn on the bar in + * the space allocated for the statusline. + */ + +/* forward function declaration is needed to add __attribute__ mechanism which + * helps the compiler understand we are defining a printf wrapper */ +static void set_statusline_error(const char *format, ...) __attribute__ ((format (printf, 1, 2))); + +static void set_statusline_error(const char *format, ...) { + clear_status_blocks(); + + char *message; + va_list args; + va_start(args, format); + vasprintf(&message, format, args); + + struct status_block *err_block = scalloc(sizeof(struct status_block)); + err_block->full_text = i3string_from_utf8("Error: "); + err_block->name = "error"; + err_block->color = "red"; + err_block->no_separator = true; + + struct status_block *message_block = scalloc(sizeof(struct status_block)); + message_block->full_text = i3string_from_utf8(message); + message_block->name = "error_message"; + message_block->color = "red"; + message_block->no_separator = true; + + TAILQ_INSERT_HEAD(&statusline_head, err_block, blocks); + TAILQ_INSERT_TAIL(&statusline_head, message_block, blocks); + + FREE(message); + va_end(args); +} + /* * Stop and free() the stdin- and sigchild-watchers * @@ -240,6 +294,7 @@ static unsigned char *get_buffer(ev_io *watcher, int *ret_buffer_len) { /* end of file, kill the watcher */ ELOG("stdin: received EOF\n"); cleanup(); + set_statusline_error("Received EOF from statusline process"); draw_bars(false); *ret_buffer_len = -1; return NULL; @@ -279,8 +334,18 @@ static bool read_json_input(unsigned char *input, int length) { #else if (status != yajl_status_ok && status != yajl_status_insufficient_data) { #endif - fprintf(stderr, "[i3bar] Could not parse JSON input (code %d): %.*s\n", - status, length, input); + char *message = (char *)yajl_get_error(parser, 0, input, length); + + /* strip the newline yajl adds to the error message */ + if (message[strlen(message) - 1] == '\n') + message[strlen(message) - 1] = '\0'; + + fprintf(stderr, "[i3bar] Could not parse JSON input (code = %d, message = %s): %.*s\n", + status, message, length, input); + + set_statusline_error("Could not parse JSON (%s)", message); + yajl_free_error(parser, (unsigned char*)message); + draw_bars(false); } else if (parser_context.has_urgent) { has_urgent = true; } @@ -350,10 +415,23 @@ void stdin_io_first_line_cb(struct ev_loop *loop, ev_io *watcher, int revents) { * */ void child_sig_cb(struct ev_loop *loop, ev_child *watcher, int revents) { + int exit_status = WEXITSTATUS(watcher->rstatus); + ELOG("Child (pid: %d) unexpectedly exited with status %d\n", child.pid, - watcher->rstatus); + exit_status); + + /* this error is most likely caused by a user giving a nonexecutable or + * nonexistent file, so we will handle those cases separately. */ + if (exit_status == 126) + set_statusline_error("status_command is not executable (exit %d)", exit_status); + else if (exit_status == 127) + set_statusline_error("status_command not found (exit %d)", exit_status); + else + set_statusline_error("status_command process exited unexpectedly (exit %d)", exit_status); + cleanup(); + draw_bars(false); } void child_write_output(void) { @@ -374,10 +452,16 @@ void child_write_output(void) { /* * Start a child-process with the specified command and reroute stdin. * We actually start a $SHELL to execute the command so we don't have to care - * about arguments and such + * about arguments and such. + * + * If `command' is NULL, such as in the case when no `status_command' is given + * in the bar config, no child will be started. * */ void start_child(char *command) { + if (command == NULL) + return; + /* Allocate a yajl parser which will be used to parse stdin. */ memset(&callbacks, '\0', sizeof(yajl_callbacks)); callbacks.yajl_map_key = stdin_map_key; @@ -400,47 +484,41 @@ void start_child(char *command) { gen = yajl_gen_alloc(NULL); #endif - if (command != NULL) { - int pipe_in[2]; /* pipe we read from */ - int pipe_out[2]; /* pipe we write to */ - - if (pipe(pipe_in) == -1) - err(EXIT_FAILURE, "pipe(pipe_in)"); - if (pipe(pipe_out) == -1) - err(EXIT_FAILURE, "pipe(pipe_out)"); + int pipe_in[2]; /* pipe we read from */ + int pipe_out[2]; /* pipe we write to */ - child.pid = fork(); - switch (child.pid) { - case -1: - ELOG("Couldn't fork(): %s\n", strerror(errno)); - exit(EXIT_FAILURE); - case 0: - /* Child-process. Reroute streams and start shell */ + if (pipe(pipe_in) == -1) + err(EXIT_FAILURE, "pipe(pipe_in)"); + if (pipe(pipe_out) == -1) + err(EXIT_FAILURE, "pipe(pipe_out)"); - close(pipe_in[0]); - close(pipe_out[1]); - - dup2(pipe_in[1], STDOUT_FILENO); - dup2(pipe_out[0], STDIN_FILENO); + child.pid = fork(); + switch (child.pid) { + case -1: + ELOG("Couldn't fork(): %s\n", strerror(errno)); + exit(EXIT_FAILURE); + case 0: + /* Child-process. Reroute streams and start shell */ - static const char *shell = NULL; + close(pipe_in[0]); + close(pipe_out[1]); - if ((shell = getenv("SHELL")) == NULL) - shell = "/bin/sh"; + dup2(pipe_in[1], STDOUT_FILENO); + dup2(pipe_out[0], STDIN_FILENO); - execl(shell, shell, "-c", command, (char*) NULL); - return; - default: - /* Parent-process. Reroute streams */ + setpgid(child.pid, 0); + execl(_PATH_BSHELL, _PATH_BSHELL, "-c", command, (char*) NULL); + return; + default: + /* Parent-process. Reroute streams */ - close(pipe_in[1]); - close(pipe_out[0]); + close(pipe_in[1]); + close(pipe_out[0]); - dup2(pipe_in[0], STDIN_FILENO); - child_stdin = pipe_out[1]; + dup2(pipe_in[0], STDIN_FILENO); + child_stdin = pipe_out[1]; - break; - } + break; } /* We set O_NONBLOCK because blocking is evil in event-driven software */ @@ -511,8 +589,8 @@ void send_block_clicked(int button, const char *name, const char *instance, int void kill_child_at_exit(void) { if (child.pid > 0) { if (child.cont_signal > 0 && child.stopped) - kill(child.pid, child.cont_signal); - kill(child.pid, SIGTERM); + killpg(child.pid, child.cont_signal); + killpg(child.pid, SIGTERM); } } @@ -524,8 +602,8 @@ void kill_child_at_exit(void) { void kill_child(void) { if (child.pid > 0) { if (child.cont_signal > 0 && child.stopped) - kill(child.pid, child.cont_signal); - kill(child.pid, SIGTERM); + killpg(child.pid, child.cont_signal); + killpg(child.pid, SIGTERM); int status; waitpid(child.pid, &status, 0); cleanup(); @@ -539,7 +617,7 @@ void kill_child(void) { void stop_child(void) { if (child.stop_signal > 0 && !child.stopped) { child.stopped = true; - kill(child.pid, child.stop_signal); + killpg(child.pid, child.stop_signal); } } @@ -550,6 +628,6 @@ void stop_child(void) { void cont_child(void) { if (child.cont_signal > 0 && child.stopped) { child.stopped = false; - kill(child.pid, child.cont_signal); + killpg(child.pid, child.cont_signal); } }