Skip to content

Commit

Permalink
block: drain persistent pipe line by line
Browse files Browse the repository at this point in the history
i3blocks currently wrongly assumes that a persistent program will
be line buffered (or act as such) and flush one line at a time. In
this common scenario, it is safe to read a single line per signal,
but there are cases where this behavior is not not wanted.

For example, a program may print several lines at once, resulting in
updating only the first line, while filling up the pipe and eventually
have the block program hang on the next write operation.

To fix this, drain the pipe of a persistent block upon reception of
a signal, but line by line to avoid breaking non JSON blocks.

Also make sure to update a block only after having successfully read
its output, to avoid resetting a block on error such as on incomplete
line, or when the pipe was already flushed.

Closes #422
Refs #425
  • Loading branch information
vivien committed Jan 13, 2023
1 parent 9d18a26 commit ebc275a
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 35 deletions.
10 changes: 5 additions & 5 deletions bar.c
Expand Up @@ -151,7 +151,7 @@ static void bar_poll_exited(struct bar *bar)
if (block->interval == INTERVAL_PERSIST) {
block_debug(block, "unexpected exit?");
} else {
block_update(block);
block_drain(block);
}
block_close(block);
if (block->interval == INTERVAL_REPEAT) {
Expand All @@ -167,14 +167,14 @@ static void bar_poll_exited(struct bar *bar)
}
}

static void bar_poll_readable(struct bar *bar, const int fd)
static void bar_poll_flushed(struct bar *bar, const int fd)
{
struct block *block = bar->blocks;

while (block) {
if (block->out[0] == fd) {
block_debug(block, "readable");
block_update(block);
block_debug(block, "flushed");
block_drain(block);
break;
}

Expand Down Expand Up @@ -370,7 +370,7 @@ static int bar_poll(struct bar *bar)
}

if (sig == SIGRTMIN) {
bar_poll_readable(bar, fd);
bar_poll_flushed(bar, fd);
bar_print(bar);
continue;
}
Expand Down
84 changes: 55 additions & 29 deletions block.c
Expand Up @@ -89,25 +89,28 @@ static int block_child_env(struct block *block)
return block_for_each(block, block_setenv, NULL);
}

static int block_stdout(struct block *block)
static int block_read(const struct block *block, size_t count, struct map *map)
{
const char *label, *full_text;
int out = block->out[0];
char buf[BUFSIZ];
size_t count;
int err;

if (block->interval == INTERVAL_PERSIST)
count = 1;
else
count = -1; /* SIZE_MAX */

if (block->format == FORMAT_JSON)
err = json_read(out, count, block->env);
return json_read(out, count, map);
else
err = i3bar_read(out, count, block->env);
return i3bar_read(out, count, map);
}

if (err && err != -EAGAIN)
static int block_update(struct block *block, const struct map *map)
{
const char *label, *full_text;
char buf[BUFSIZ];
int err;

err = block_reset(block);
if (err)
return err;

err = map_copy(block->env, map);
if (err)
return err;

/* Deprecated label */
Expand All @@ -120,32 +123,55 @@ static int block_stdout(struct block *block)
return err;
}

/* Exit code takes precedence over the output */
if (block->code == EXIT_URGENT) {
err = block_set(block, "urgent", "true");
if (err)
return err;
}

return 0;
}

int block_update(struct block *block)
int block_drain(struct block *block)
{
struct map *map;
int err;

/* Reset properties to default before updating from output */
err = block_reset(block);
if (err)
return err;

err = block_stdout(block);
if (err)
return err;
map = map_create();
if (!map)
return -ENOMEM;

/* Exit code takes precedence over the output */
if (block->code == EXIT_URGENT) {
err = block_set(block, "urgent", "true");
if (err)
return err;
if (block->interval == INTERVAL_PERSIST) {
for (;;) {
err = block_read(block, 1, map);
if (err) {
if (err == -EAGAIN)
err = 0;
break;
}

err = block_update(block, map);
if (err)
block_error(block, "failed to update");

map_clear(map);
}
} else {
err = block_read(block, -1, map);
if (err == 0)
err = -EINVAL; /* Unlikely more than SIZE_MAX lines */

if (err == -EAGAIN) {
err = block_update(block, map);
if (err)
block_error(block, "failed to update");
}
}

block_debug(block, "updated successfully");
map_destroy(map);

return 0;
return err;
}

static int block_send_key(const char *key, const char *value, void *data)
Expand Down
2 changes: 1 addition & 1 deletion block.h
Expand Up @@ -106,7 +106,7 @@ int block_click(struct block *block);
int block_spawn(struct block *block);
void block_touch(struct block *block);
int block_reap(struct block *block);
int block_update(struct block *block);
int block_drain(struct block *block);
void block_close(struct block *block);

#endif /* BLOCK_H */

0 comments on commit ebc275a

Please sign in to comment.