void block_reap(struct block *block) { int status, code; if (block->pid <= 0) { bdebug(block, "not spawned yet"); return; } if (waitpid(block->pid, &status, 0) == -1) { berrorx(block, "waitpid(%d)", block->pid); mark_as_failed(block, strerror(errno)); goto close; } code = WEXITSTATUS(status); bdebug(block, "process %d exited with %d", block->pid, code); /* Process successfully reaped, reset the block PID */ block->pid = 0; block_dump_stderr(block); if (code != 0 && code != EXIT_URGENT) { char reason[32]; if (code == EXIT_ERR_INTERNAL) sprintf(reason, "internal error"); else sprintf(reason, "bad exit code %d", code); berror(block, "%s", reason); mark_as_failed(block, reason); goto close; } /* Do not update unless it was meant to terminate */ if (block->interval == INTER_PERSIST) goto close; block_update(block); /* Exit code takes precedence over the output */ if (code == EXIT_URGENT) strcpy(block->updated_props.urgent, "true"); close: if (close(block->out) == -1) berrorx(block, "close stdout"); if (close(block->err) == -1) berrorx(block, "close stderr"); }
void block_update(struct block *block) { FILE *child_stdout; int child_status, code; char output[2048], *text = output; if (setup_env(block)) return mark_as_failed(block, "failed to setup env", -1); /* Pipe, fork and exec a shell for the block command line */ child_stdout = popen(block->command, "r"); if (!child_stdout) { berrorx(block, "popen(%s)", block->command); return mark_as_failed(block, "failed to fork", -1); } /* Do not distinguish EOF or error, just read child's output */ memset(output, 0, sizeof(output)); fread(output, 1, sizeof(output) - 1, child_stdout); /* Wait for the child process to terminate */ child_status = pclose(child_stdout); if (child_status == -1) { berrorx(block, "pclose"); return mark_as_failed(block, "failed to wait", -1); } if (!WIFEXITED(child_status)) { berror(block, "child did not exit correctly"); return mark_as_failed(block, "command did not exit", -1); } code = WEXITSTATUS(child_status); if (code != 0 && code != '!') { char reason[1024] = { 0 }; berror(block, "bad exit code %d", code); linecpy(&text, reason, sizeof(reason) - 1); return mark_as_failed(block, reason, code); } /* From here, the update went ok so merge the output */ strncpy(block->urgent, code == '!' ? "true" : "false", sizeof(block->urgent) - 1); linecpy(&text, block->full_text, sizeof(block->full_text) - 1); linecpy(&text, block->short_text, sizeof(block->short_text) - 1); linecpy(&text, block->color, sizeof(block->color) - 1); block->last_update = time(NULL); bdebug(block, "updated successfully"); }
void block_update(struct block *block) { struct properties *props = &block->updated_props; char buf[2048] = { 0 }; int nr; /* Read a single line for persistent block, everything otherwise */ if (block->interval == INTER_PERSIST) { nr = io_readline(block->out, buf, sizeof(buf)); if (nr < 0) { berror(block, "failed to read a line"); return mark_as_failed(block, "failed to read"); } else if (nr == 0) { berror(block, "pipe closed"); return mark_as_failed(block, "pipe closed"); } } else { /* Note: read(2) returns 0 for end-of-pipe */ if (read(block->out, buf, sizeof(buf) - 1) == -1) { berrorx(block, "read stdout"); return mark_as_failed(block, strerror(errno)); } } /* Reset the defaults and merge the output */ memcpy(props, &block->default_props, sizeof(struct properties)); if (block->format == FORMAT_JSON) block_update_json(block, buf); else block_update_plain_text(block, buf); if (*FULL_TEXT(block) && *LABEL(block)) { static const size_t size = sizeof(props->full_text); char concat[size]; snprintf(concat, size, "%s %s", LABEL(block), FULL_TEXT(block)); strcpy(props->full_text, concat); } bdebug(block, "updated successfully"); }
void block_spawn(struct block *block, struct click *click) { const unsigned long now = time(NULL); int out[2], err[2]; if (!*COMMAND(block)) { bdebug(block, "no command, skipping"); return; } if (block->pid > 0) { bdebug(block, "process already spawned"); return; } if (pipe(out) == -1 || pipe(err) == -1) { berrorx(block, "pipe"); return mark_as_failed(block, strerror(errno)); } if (block->interval == INTER_PERSIST) { if (io_signal(out[0], SIGRTMIN)) return mark_as_failed(block, "event I/O impossible"); } block->pid = fork(); if (block->pid == -1) { berrorx(block, "fork"); return mark_as_failed(block, strerror(errno)); } /* Child? */ if (block->pid == 0) { /* Error messages are merged into the parent's stderr... */ child_setup_env(block, click); child_reset_signals(block); child_redirect_write(block, out, STDOUT_FILENO); child_redirect_write(block, err, STDERR_FILENO); /* ... until here */ child_exec(block); } /* * Note: for non-persistent blocks, no need to set the pipe read end as * non-blocking, since it is meant to be read once the child has exited * (and thus the write end is closed and read is available). */ /* Parent */ if (close(out[1]) == -1) berrorx(block, "close stdout"); if (close(err[1]) == -1) berrorx(block, "close stderr"); block->out = out[0]; block->err = err[0]; if (!click) block->timestamp = now; bdebug(block, "forked child %d at %ld", block->pid, now); }