aboutsummaryrefslogtreecommitdiff
path: root/libi3
diff options
context:
space:
mode:
authorUli Schlachter <psychon@users.noreply.github.com>2021-11-04 17:22:22 +0100
committerGitHub <noreply@github.com>2021-11-04 17:22:22 +0100
commit445cea8e3a2cd32807d4da50ea340e991ebcff74 (patch)
treefdc2fbf1a8f1f854d0618c0102438274be0c13c2 /libi3
parent53f52beb095296050f9dfb094e64f80a6aec7934 (diff)
downloadi3-445cea8e3a2cd32807d4da50ea340e991ebcff74.tar.gz
i3-445cea8e3a2cd32807d4da50ea340e991ebcff74.zip
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>
Diffstat (limited to 'libi3')
-rw-r--r--libi3/create_socket.c12
-rw-r--r--libi3/ipc_connect.c24
2 files changed, 31 insertions, 5 deletions
diff --git a/libi3/create_socket.c b/libi3/create_socket.c
index 4b93ff2d..8b756a38 100644
--- a/libi3/create_socket.c
+++ b/libi3/create_socket.c
@@ -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);
diff --git a/libi3/ipc_connect.c b/libi3/ipc_connect.c
index 871fe083..5da9f129 100644
--- a/libi3/ipc_connect.c
+++ b/libi3/ipc_connect.c
@@ -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;
}