Skip to content

Commit ebc275a

Browse files
committed
block: drain persistent pipe line by line
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
1 parent 9d18a26 commit ebc275a

File tree

3 files changed

+61
-35
lines changed

3 files changed

+61
-35
lines changed

bar.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ static void bar_poll_exited(struct bar *bar)
151151
if (block->interval == INTERVAL_PERSIST) {
152152
block_debug(block, "unexpected exit?");
153153
} else {
154-
block_update(block);
154+
block_drain(block);
155155
}
156156
block_close(block);
157157
if (block->interval == INTERVAL_REPEAT) {
@@ -167,14 +167,14 @@ static void bar_poll_exited(struct bar *bar)
167167
}
168168
}
169169

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

174174
while (block) {
175175
if (block->out[0] == fd) {
176-
block_debug(block, "readable");
177-
block_update(block);
176+
block_debug(block, "flushed");
177+
block_drain(block);
178178
break;
179179
}
180180

@@ -370,7 +370,7 @@ static int bar_poll(struct bar *bar)
370370
}
371371

372372
if (sig == SIGRTMIN) {
373-
bar_poll_readable(bar, fd);
373+
bar_poll_flushed(bar, fd);
374374
bar_print(bar);
375375
continue;
376376
}

block.c

Lines changed: 55 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -89,25 +89,28 @@ static int block_child_env(struct block *block)
8989
return block_for_each(block, block_setenv, NULL);
9090
}
9191

92-
static int block_stdout(struct block *block)
92+
static int block_read(const struct block *block, size_t count, struct map *map)
9393
{
94-
const char *label, *full_text;
9594
int out = block->out[0];
96-
char buf[BUFSIZ];
97-
size_t count;
98-
int err;
99-
100-
if (block->interval == INTERVAL_PERSIST)
101-
count = 1;
102-
else
103-
count = -1; /* SIZE_MAX */
10495

10596
if (block->format == FORMAT_JSON)
106-
err = json_read(out, count, block->env);
97+
return json_read(out, count, map);
10798
else
108-
err = i3bar_read(out, count, block->env);
99+
return i3bar_read(out, count, map);
100+
}
109101

110-
if (err && err != -EAGAIN)
102+
static int block_update(struct block *block, const struct map *map)
103+
{
104+
const char *label, *full_text;
105+
char buf[BUFSIZ];
106+
int err;
107+
108+
err = block_reset(block);
109+
if (err)
110+
return err;
111+
112+
err = map_copy(block->env, map);
113+
if (err)
111114
return err;
112115

113116
/* Deprecated label */
@@ -120,32 +123,55 @@ static int block_stdout(struct block *block)
120123
return err;
121124
}
122125

126+
/* Exit code takes precedence over the output */
127+
if (block->code == EXIT_URGENT) {
128+
err = block_set(block, "urgent", "true");
129+
if (err)
130+
return err;
131+
}
132+
123133
return 0;
124134
}
125135

126-
int block_update(struct block *block)
136+
int block_drain(struct block *block)
127137
{
138+
struct map *map;
128139
int err;
129140

130-
/* Reset properties to default before updating from output */
131-
err = block_reset(block);
132-
if (err)
133-
return err;
134-
135-
err = block_stdout(block);
136-
if (err)
137-
return err;
141+
map = map_create();
142+
if (!map)
143+
return -ENOMEM;
138144

139-
/* Exit code takes precedence over the output */
140-
if (block->code == EXIT_URGENT) {
141-
err = block_set(block, "urgent", "true");
142-
if (err)
143-
return err;
145+
if (block->interval == INTERVAL_PERSIST) {
146+
for (;;) {
147+
err = block_read(block, 1, map);
148+
if (err) {
149+
if (err == -EAGAIN)
150+
err = 0;
151+
break;
152+
}
153+
154+
err = block_update(block, map);
155+
if (err)
156+
block_error(block, "failed to update");
157+
158+
map_clear(map);
159+
}
160+
} else {
161+
err = block_read(block, -1, map);
162+
if (err == 0)
163+
err = -EINVAL; /* Unlikely more than SIZE_MAX lines */
164+
165+
if (err == -EAGAIN) {
166+
err = block_update(block, map);
167+
if (err)
168+
block_error(block, "failed to update");
169+
}
144170
}
145171

146-
block_debug(block, "updated successfully");
172+
map_destroy(map);
147173

148-
return 0;
174+
return err;
149175
}
150176

151177
static int block_send_key(const char *key, const char *value, void *data)

block.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ int block_click(struct block *block);
106106
int block_spawn(struct block *block);
107107
void block_touch(struct block *block);
108108
int block_reap(struct block *block);
109-
int block_update(struct block *block);
109+
int block_drain(struct block *block);
110110
void block_close(struct block *block);
111111

112112
#endif /* BLOCK_H */

0 commit comments

Comments
 (0)