Exemple #1
0
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");
}
Exemple #2
0
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");
}
Exemple #3
0
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");
}
Exemple #4
0
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);
}