Do not replace existing IPC socket (#4638)

Imagine you are a happy i3 user and want to also write patches for i3.
You use "Xephyr :1" to get another X11 server and then start your newly
build i3 in it with "DISPLAY=:1 ./i3". You test your changes and
everything seems fine. You are happy. Later that day, you try to log
out, but the $mod+Shift+e key binding from the default config no longer
works. i3-msg cannot connect to the IPC socket because "No such file or
directory". What is going on?

The problem boils down to $I3SOCK having something like two meanings.
When i3 starts, it sets the environment variable $I3SOCK to the path of
its IPC socket. That way, any process started from i3 inherits this and
i3-msg knows how to talk to i3. However, when this variable is already
set when i3 starts, then i3 will replace the existing socket. Thus, in
the earlier experiments, the "separate i3" that was used for
experimenting stole the "main i3"'s socket and replaced it with its own.
When it exited, it deleted that socket.

This commit adds half a work around to this problem: When creating the
IPC socket, i3 will now first try to connect() to the socket. If this
succeeds, it will complain and refuse to use this socket. If the
connect() call fails, it will proceed as usual and create the socket.

Note that trying to connect() to a socket that no process listens on
will fail. Thus, this new code only really "triggers" when some process
is actively listening on this socket and accepting connections.

Example output for when the socket is already in use:

$ I3SOCK=/tmp/sdfdsf DISPLAY=:2 ./i3
31.10.2021 17:03:55 - [libi3] ERROR: Refusing to create UNIX socket at /tmp/sdfdsf: Socket is already in use
31.10.2021 17:03:55 - ERROR: Could not create the IPC socket, IPC disabled

This commit sadly only provides part of the solution. i3 will still
delete the socket when shutting down, even if it failed to create the
IPC socket. Thus, the ipc socket will still break, but now only later.
This will be fixed separately.

First-step-towards-fixing: https://github.com/i3/i3/issues/4381
Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
Uli Schlachter 2021-11-04 17:22:22 +01:00 committed by GitHub
parent 53f52beb09
commit 445cea8e3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 37 additions and 5 deletions

View File

@ -294,6 +294,12 @@ size_t i3string_get_num_glyphs(i3String *str);
*/
int ipc_connect(const char *socket_path);
/**
* Connects to the socket at the given path with no fallback paths. Returns
* -1 if connect() fails and die()s for other errors.
*/
int ipc_connect_impl(const char *socket_path);
/**
* Formats a message (payload) of the given size and type and sends it to i3 via
* the given socket file descriptor.

View File

@ -34,10 +34,20 @@ int create_socket(const char *filename, char **out_socketpath) {
}
free(copy);
/* Check if the socket is in use by another process (this call does not
* succeed if the socket is stale / the owner already exited) */
int sockfd = ipc_connect_impl(resolved);
if (sockfd != -1) {
ELOG("Refusing to create UNIX socket at %s: Socket is already in use\n", resolved);
close(sockfd);
errno = EEXIST;
return -1;
}
/* Unlink the unix domain socket before */
unlink(resolved);
int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
if (sockfd < 0) {
perror("socket()");
free(resolved);

View File

@ -13,6 +13,7 @@
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
/*
* Connects to the i3 IPC socket and returns the file descriptor for the
@ -39,6 +40,20 @@ int ipc_connect(const char *socket_path) {
path = sstrdup("/tmp/i3-ipc.sock");
}
int sockfd = ipc_connect_impl(path);
if (sockfd < 0) {
err(EXIT_FAILURE, "Could not connect to i3 on socket %s", path);
}
free(path);
return sockfd;
}
/**
* Connects to the socket at the given path with no fallback paths. Returns
* -1 if connect() fails and die()s for other errors.
*
*/
int ipc_connect_impl(const char *socket_path) {
int sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
if (sockfd == -1)
err(EXIT_FAILURE, "Could not create socket");
@ -48,9 +63,10 @@ int ipc_connect(const char *socket_path) {
struct sockaddr_un addr;
memset(&addr, 0, sizeof(struct sockaddr_un));
addr.sun_family = AF_LOCAL;
strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
if (connect(sockfd, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
err(EXIT_FAILURE, "Could not connect to i3 on socket %s", path);
free(path);
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
if (connect(sockfd, (const struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
close(sockfd);
return -1;
}
return sockfd;
}