On 1/17/19 10:12 AM, Giovanni Bechis wrote:
> Hi,
> after upgrading to i3 4.16 (OpenBSD 6.4-current (GENERIC.MP) #621: Wed Jan 16
> 22:29:11 MST 2019)
> i3status disappears as soon as I receive a pidgin message or when I receive a
> new mail in Thunderbird.
> Reverting to 4.15 is a workaround, any other i3 user with the same issue ?
>
> Giovanni
>
The following patch by Josh Grosse (from upstream git) fixes the issue.
Comments ? Ok ?
Cheers
Giovanni
Index: Makefile
===================================================================
RCS file: /systems/cvs/ports/x11/i3/Makefile,v
retrieving revision 1.114
diff -u -p -r1.114 Makefile
--- Makefile 13 Jan 2019 12:48:10 -0000 1.114
+++ Makefile 15 Jan 2019 02:19:54 -0000
@@ -3,6 +3,7 @@
COMMENT = improved dynamic tiling window manager
DISTNAME = i3-4.16
+REVISION = 0
CATEGORIES = x11
Index: patches/patch-docs_ipc
===================================================================
RCS file: patches/patch-docs_ipc
diff -N patches/patch-docs_ipc
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-docs_ipc 15 Jan 2019 02:18:43 -0000
@@ -0,0 +1,20 @@
+$OpenBSD$
+
+Index: docs/ipc
+--- docs/ipc.orig
++++ docs/ipc
+@@ -693,14 +693,6 @@ program does not want to cope which such kinds of race
+ event based library may not have a problem here), I suggest you create a
+ separate connection to receive events.
+
+-If an event message needs to be sent and the socket is not writeable (write
+-returns EAGAIN, happens when the socket doesn't have enough buffer space for
+-writing new data) then i3 uses a queue system to store outgoing messages for
+-each client. This is combined with a timer: if the message queue for a client is
+-not empty and no data where successfully written in the past 10 seconds, the
+-connection is killed. Practically, this means that your client should try to
+-always read events from the socket to avoid having its connection closed.
+-
+ === Subscribing to events
+
+ By sending a message of type SUBSCRIBE with a JSON-encoded array as payload
Index: patches/patch-include_config_directives_h
===================================================================
RCS file: patches/patch-include_config_directives_h
diff -N patches/patch-include_config_directives_h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-include_config_directives_h 15 Jan 2019 02:18:46 -0000
@@ -0,0 +1,13 @@
+$OpenBSD$
+
+Index: include/config_directives.h
+--- include/config_directives.h.orig
++++ include/config_directives.h
+@@ -63,7 +63,6 @@ CFGFUN(assign_output, const char *output);
+ CFGFUN(assign, const char *workspace, bool is_number);
+ CFGFUN(no_focus);
+ CFGFUN(ipc_socket, const char *path);
+-CFGFUN(ipc_kill_timeout, const long timeout_ms);
+ CFGFUN(restart_state, const char *path);
+ CFGFUN(popup_during_fullscreen, const char *value);
+ CFGFUN(color, const char *colorclass, const char *border, const char *background, const char *text, const char *indicator, const char *child_border);
Index: patches/patch-include_ipc_h
===================================================================
RCS file: patches/patch-include_ipc_h
diff -N patches/patch-include_ipc_h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-include_ipc_h 15 Jan 2019 02:18:48 -0000
@@ -0,0 +1,27 @@
+$OpenBSD$
+
+Index: include/ipc.h
+--- include/ipc.h.orig
++++ include/ipc.h
+@@ -35,11 +35,6 @@ typedef struct ipc_client {
+ * event has been sent by i3. */
+ bool first_tick_sent;
+
+- struct ev_io *callback;
+- struct ev_timer *timeout;
+- uint8_t *buffer;
+- size_t buffer_size;
+-
+ TAILQ_ENTRY(ipc_client)
+ clients;
+ } ipc_client;
+@@ -129,9 +124,3 @@ void ipc_send_barconfig_update_event(Barconfig *barcon
+ * For the binding events, we send the serialized binding struct.
+ */
+ void ipc_send_binding_event(const char *event_type, Binding *bind);
+-
+-/**
+- * Set the maximum duration that we allow for a connection with an unwriteable
+- * socket.
+- */
+-void ipc_set_kill_timeout(ev_tstamp new);
Index: patches/patch-include_libi3_h
===================================================================
RCS file: patches/patch-include_libi3_h
diff -N patches/patch-include_libi3_h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-include_libi3_h 15 Jan 2019 02:18:50 -0000
@@ -0,0 +1,20 @@
+$OpenBSD$
+
+Index: include/libi3.h
+--- include/libi3.h.orig
++++ include/libi3.h
+@@ -167,14 +167,6 @@ int sasprintf(char **strp, const char *fmt, ...);
+ ssize_t writeall(int fd, const void *buf, size_t count);
+
+ /**
+- * Like writeall, but instead of retrying upon EAGAIN (returned when a write
+- * would block), the function stops and returns the total number of bytes
+- * written so far.
+- *
+- */
+-ssize_t writeall_nonblock(int fd, const void *buf, size_t count);
+-
+-/**
+ * Safe-wrapper around writeall which exits if it returns -1 (meaning that
+ * write failed)
+ *
Index: patches/patch-libi3_safewrappers_c
===================================================================
RCS file: patches/patch-libi3_safewrappers_c
diff -N patches/patch-libi3_safewrappers_c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-libi3_safewrappers_c 15 Jan 2019 02:18:51 -0000
@@ -0,0 +1,43 @@
+$OpenBSD$
+
+Index: libi3/safewrappers.c
+--- libi3/safewrappers.c.orig
++++ libi3/safewrappers.c
+@@ -68,9 +68,10 @@ int sasprintf(char **strp, const char *fmt, ...) {
+
+ ssize_t writeall(int fd, const void *buf, size_t count) {
+ size_t written = 0;
++ ssize_t n = 0;
+
+ while (written < count) {
+- const ssize_t n = write(fd, ((char *)buf) + written, count - written);
++ n = write(fd, buf + written, count - written);
+ if (n == -1) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+@@ -79,25 +80,6 @@ ssize_t writeall(int fd, const void *buf, size_t count
+ written += (size_t)n;
+ }
+
+- return written;
+-}
+-
+-ssize_t writeall_nonblock(int fd, const void *buf, size_t count) {
+- size_t written = 0;
+-
+- while (written < count) {
+- const ssize_t n = write(fd, ((char *)buf) + written, count - written);
+- if (n == -1) {
+- if (errno == EAGAIN) {
+- return written;
+- } else if (errno == EINTR) {
+- continue;
+- } else {
+- return n;
+- }
+- }
+- written += (size_t)n;
+- }
+ return written;
+ }
+
Index: patches/patch-parser-specs_config_spec
===================================================================
RCS file: patches/patch-parser-specs_config_spec
diff -N patches/patch-parser-specs_config_spec
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-parser-specs_config_spec 15 Jan 2019 02:18:53 -0000
@@ -0,0 +1,25 @@
+$OpenBSD$
+
+Index: parser-specs/config.spec
+--- parser-specs/config.spec.orig
++++ parser-specs/config.spec
+@@ -49,7 +49,6 @@ state INITIAL:
+ 'show_marks' -> SHOW_MARKS
+ 'workspace' -> WORKSPACE
+ 'ipc_socket', 'ipc-socket' -> IPC_SOCKET
+- 'ipc_kill_timeout' -> IPC_KILL_TIMEOUT
+ 'restart_state' -> RESTART_STATE
+ 'popup_during_fullscreen' -> POPUP_DURING_FULLSCREEN
+ exectype = 'exec_always', 'exec' -> EXEC
+@@ -287,11 +286,6 @@ state WORKSPACE_OUTPUT_STR:
+ state IPC_SOCKET:
+ path = string
+ -> call cfg_ipc_socket($path)
+-
+-# ipc_kill_timeout
+-state IPC_KILL_TIMEOUT:
+- timeout = number
+- -> call cfg_ipc_kill_timeout(&timeout)
+
+ # restart_state <path> (for testcases)
+ state RESTART_STATE:
Index: patches/patch-src_config_directives_c
===================================================================
RCS file: patches/patch-src_config_directives_c
diff -N patches/patch-src_config_directives_c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-src_config_directives_c 15 Jan 2019 02:18:55 -0000
@@ -0,0 +1,16 @@
+$OpenBSD$
+
+Index: src/config_directives.c
+--- src/config_directives.c.orig
++++ src/config_directives.c
+@@ -468,10 +468,6 @@ CFGFUN(no_focus) {
+ TAILQ_INSERT_TAIL(&assignments, assignment, assignments);
+ }
+
+-CFGFUN(ipc_kill_timeout, const long timeout_ms) {
+- ipc_set_kill_timeout(timeout_ms / 1000.0);
+-}
+-
+ /*******************************************************************************
+ * Bar configuration (i3bar)
+ ******************************************************************************/
Index: patches/patch-src_ipc_c
===================================================================
RCS file: patches/patch-src_ipc_c
diff -N patches/patch-src_ipc_c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-src_ipc_c 15 Jan 2019 02:18:57 -0000
@@ -0,0 +1,209 @@
+$OpenBSD$
+
+Index: src/ipc.c
+--- src/ipc.c.orig
++++ src/ipc.c
+@@ -38,38 +38,8 @@ static void set_nonblock(int sockfd) {
+ err(-1, "Could not set O_NONBLOCK");
+ }
+
+-/*
+- * Given a message and a message type, create the corresponding header, merge it
+- * with the message and append it to the given client's output buffer.
+- *
+- */
+-static void append_payload(ipc_client *client, uint32_t message_type, const char *payload) {
+- const size_t size = strlen(payload);
+- const i3_ipc_header_t header = {
+- .magic = {'i', '3', '-', 'i', 'p', 'c'},
+- .size = size,
+- .type = message_type};
+- const size_t header_size = sizeof(i3_ipc_header_t);
+- const size_t message_size = header_size + size;
+-
+- client->buffer = srealloc(client->buffer, client->buffer_size + message_size);
+- memcpy(client->buffer + client->buffer_size, ((void *)&header), header_size);
+- memcpy(client->buffer + client->buffer_size + header_size, payload, size);
+- client->buffer_size += message_size;
+-}
+-
+ static void free_ipc_client(ipc_client *client) {
+ close(client->fd);
+-
+- ev_io_stop(main_loop, client->callback);
+- FREE(client->callback);
+- if (client->timeout) {
+- ev_timer_stop(main_loop, client->timeout);
+- FREE(client->timeout);
+- }
+-
+- free(client->buffer);
+-
+ for (int i = 0; i < client->num_events; i++) {
+ free(client->events[i]);
+ }
+@@ -78,69 +48,7 @@ static void free_ipc_client(ipc_client *client) {
+ free(client);
+ }
+
+-static void ipc_client_timeout(EV_P_ ev_timer *w, int revents);
+-static void ipc_socket_writeable_cb(EV_P_ struct ev_io *w, int revents);
+-
+-static ev_tstamp kill_timeout = 10.0;
+-
+-void ipc_set_kill_timeout(ev_tstamp new) {
+- kill_timeout = new;
+-}
+-
+ /*
+- * Try to write the contents of the pending buffer to the client's subscription
+- * socket. Will set, reset or clear the timeout and io callbacks depending on
+- * the result of the write operation.
+- *
+- */
+-static void ipc_push_pending(ipc_client *client) {
+- const ssize_t result = writeall_nonblock(client->fd, client->buffer, client->buffer_size);
+- if (result < 0) {
+- return;
+- }
+-
+- if ((size_t)result == client->buffer_size) {
+- /* Everything was written successfully: clear the timer and stop the io
+- * callback. */
+- FREE(client->buffer);
+- client->buffer_size = 0;
+- if (client->timeout) {
+- ev_timer_stop(main_loop, client->timeout);
+- FREE(client->timeout);
+- }
+- ev_io_stop(main_loop, client->callback);
+- return;
+- }
+-
+- /* Otherwise, make sure that the io callback is enabled and create a new
+- * timer if needed. */
+- ev_io_start(main_loop, client->callback);
+-
+- if (!client->timeout) {
+- struct ev_timer *timeout = scalloc(1, sizeof(struct ev_timer));
+- ev_timer_init(timeout, ipc_client_timeout, kill_timeout, 0.);
+- timeout->data = client;
+- client->timeout = timeout;
+- ev_set_priority(timeout, EV_MINPRI);
+- ev_timer_start(main_loop, client->timeout);
+- } else if (result > 0) {
+- /* Keep the old timeout when nothing is written. Otherwise, we would
+- * keep a dead connection by continuously renewing its timeouts. */
+- ev_timer_stop(main_loop, client->timeout);
+- ev_timer_set(client->timeout, kill_timeout, 0.0);
+- ev_timer_start(main_loop, client->timeout);
+- }
+- if (result == 0) {
+- return;
+- }
+-
+- /* Shift the buffer to the left and reduce the allocated space. */
+- client->buffer_size -= (size_t)result;
+- memmove(client->buffer, client->buffer + result, client->buffer_size);
+- client->buffer = srealloc(client->buffer, client->buffer_size);
+-}
+-
+-/*
+ * Sends the specified event to all IPC clients which are currently connected
+ * and subscribed to this kind of event.
+ *
+@@ -159,11 +67,7 @@ void ipc_send_event(const char *event, uint32_t messag
+ if (!interested)
+ continue;
+
+- const bool push_now = (current->buffer_size == 0);
+- append_payload(current, message_type, payload);
+- if (push_now) {
+- ipc_push_pending(current);
+- }
++ ipc_send_message(current->fd, strlen(payload), message_type, (const uint8_t *)payload);
+ }
+ }
+
+@@ -1382,62 +1286,6 @@ static void ipc_receive_message(EV_P_ struct ev_io *w,
+ FREE(message);
+ }
+
+-static void ipc_client_timeout(EV_P_ ev_timer *w, int revents) {
+- /* No need to be polite and check for writeability, the other callback would
+- * have been called by now. */
+- ipc_client *client = (ipc_client *)w->data;
+-
+- char *cmdline = NULL;
+-#if defined(__linux__) && defined(SO_PEERCRED)
+- struct ucred peercred;
+- socklen_t so_len = sizeof(peercred);
+- if (getsockopt(client->fd, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0) {
+- goto end;
+- }
+- char *exepath;
+- sasprintf(&exepath, "/proc/%d/cmdline", peercred.pid);
+-
+- int fd = open(exepath, O_RDONLY);
+- free(exepath);
+- if (fd == -1) {
+- goto end;
+- }
+- char buf[512] = {'\0'}; /* cut off cmdline for the error message. */
+- const ssize_t n = read(fd, buf, sizeof(buf));
+- close(fd);
+- if (n < 0) {
+- goto end;
+- }
+- for (char *walk = buf; walk < buf + n - 1; walk++) {
+- if (*walk == '\0') {
+- *walk = ' ';
+- }
+- }
+- cmdline = buf;
+-
+- if (cmdline) {
+- ELOG("client %p with pid %d and cmdline '%s' on fd %d timed out, killing\n", client, peercred.pid, cmdline, client->fd);
+- }
+-
+-end:
+-#endif
+- if (!cmdline) {
+- ELOG("client %p on fd %d timed out, killing\n", client, client->fd);
+- }
+-
+- free_ipc_client(client);
+-}
+-
+-static void ipc_socket_writeable_cb(EV_P_ ev_io *w, int revents) {
+- DLOG("fd %d writeable\n", w->fd);
+- ipc_client *client = (ipc_client *)w->data;
+-
+- /* If this callback is called then there should be a corresponding active
+- * timer. */
+- assert(client->timeout != NULL);
+- ipc_push_pending(client);
+-}
+-
+ /*
+ * Handler for activity on the listening socket, meaning that a new client
+ * has just connected and we should accept() him. Sets up the event handler
+@@ -1466,16 +1314,11 @@ void ipc_new_client(EV_P_ struct ev_io *w, int revents
+ ev_io_init(package, ipc_receive_message, client, EV_READ);
+ ev_io_start(EV_A_ package);
+
+- ipc_client *new = scalloc(1, sizeof(ipc_client));
+-
+- package = scalloc(1, sizeof(struct ev_io));
+- package->data = new;
+- ev_io_init(package, ipc_socket_writeable_cb, client, EV_WRITE);
+-
+ DLOG("IPC: new client connected on fd %d\n", w->fd);
+
++
++ ipc_client *new = scalloc(1, sizeof(ipc_client));
+ new->fd = client;
+- new->callback = package;
+
+ TAILQ_INSERT_TAIL(&all_clients, new, clients);
+ }
Index: patches/patch-testcases_t_201-config-parser_t
===================================================================
RCS file: patches/patch-testcases_t_201-config-parser_t
diff -N patches/patch-testcases_t_201-config-parser_t
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-testcases_t_201-config-parser_t 15 Jan 2019 02:18:59 -0000
@@ -0,0 +1,13 @@
+$OpenBSD$
+
+Index: testcases/t/201-config-parser.t
+--- testcases/t/201-config-parser.t.orig
++++ testcases/t/201-config-parser.t
+@@ -502,7 +502,6 @@ my $expected_all_tokens = "ERROR: CONFIG: Expected one
+ workspace
+ ipc_socket
+ ipc-socket
+- ipc_kill_timeout
+ restart_state
+ popup_during_fullscreen
+ exec_always
Index: patches/patch-testcases_t_298-ipc-misbehaving-connection_t
===================================================================
RCS file: patches/patch-testcases_t_298-ipc-misbehaving-connection_t
diff -N patches/patch-testcases_t_298-ipc-misbehaving-connection_t
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ patches/patch-testcases_t_298-ipc-misbehaving-connection_t 15 Jan 2019 02:19:01 -0000
@@ -0,0 +1,75 @@
+$OpenBSD$
+
+Index: testcases/t/298-ipc-misbehaving-connection.t
+--- testcases/t/298-ipc-misbehaving-connection.t.orig
++++ testcases/t/298-ipc-misbehaving-connection.t
+@@ -1,69 +0,0 @@
+-#!perl
+-# vim:ts=4:sw=4:expandtab
+-#
+-# Please read the following documents before working on tests:
+-# ⢠https://build.i3wm.org/docs/testsuite.html
+-# (or docs/testsuite)
+-#
+-# ⢠https://build.i3wm.org/docs/lib-i3test.html
+-# (alternatively: perldoc ./testcases/lib/i3test.pm)
+-#
+-# ⢠https://build.i3wm.org/docs/ipc.html
+-# (or docs/ipc)
+-#
+-# ⢠http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+-# (unless you are already familiar with Perl)
+-#
+-# Test that i3 will not hang if a connected client stops reading from its
+-# subscription socket and that the client is killed after a delay.
+-# Ticket: #2999
+-# Bug still in: 4.15-180-g715cea61
+-use i3test i3_config => <<EOT;
+-# i3 config file (v4)
+-font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+-# Set the timeout to 500ms to reduce the duration of this test.
+-ipc_kill_timeout 500
+-EOT
+-
+-# Manually connect to i3 so that we can choose to not read events
+-use IO::Socket::UNIX;
+-my $sock = IO::Socket::UNIX->new(Peer => get_socket_path());
+-my $magic = "i3-ipc";
+-my $payload = '["workspace"]';
+-my $message = $magic . pack("LL", length($payload), 2) . $payload;
+-print $sock $message;
+-
+-# Constantly switch between 2 workspaces to generate events.
+-fresh_workspace;
+-open_window;
+-fresh_workspace;
+-open_window;
+-
+-eval {
+- local $SIG{ALRM} = sub { die "Timeout\n" };
+- # 500 is an arbitrarily large number to make sure that the socket becomes
+- # non-writeable.
+- for (my $i = 0; $i < 500; $i++) {
+- alarm 1;
+- cmd 'workspace back_and_forth';
+- alarm 0;
+- }
+-};
+-ok(!$@, 'i3 didn\'t hang');
+-
+-# Wait for connection timeout
+-sleep 1;
+-
+-use IO::Select;
+-my $s = IO::Select->new($sock);
+-my $reached_eof = 0;
+-while ($s->can_read(0.05)) {
+- if (read($sock, my $buffer, 100) == 0) {
+- $reached_eof = 1;
+- last;
+- }
+-}
+-ok($reached_eof, 'socket connection closed');
+-
+-close $sock;
+-done_testing;