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;
  • i3 regression Giovanni Bechis
    • Re: i3 regression Giovanni Bechis

Reply via email to