2021-01-15 14:48:10 +01:00
|
|
|
/*
|
|
|
|
* vim:ts=4:sw=4:expandtab
|
|
|
|
*
|
|
|
|
* i3 - an improved dynamic tiling window manager
|
|
|
|
* © 2009 Michael Stapelberg and contributors (see also: LICENSE)
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
#include "libi3.h"
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <libgen.h>
|
|
|
|
#include <err.h>
|
2021-11-04 19:16:41 +01:00
|
|
|
#include <errno.h>
|
2021-01-15 14:48:10 +01:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Creates the UNIX domain socket at the given path, sets it to non-blocking
|
|
|
|
* mode, bind()s and listen()s on it.
|
|
|
|
*
|
|
|
|
* The full path to the socket is stored in the char* that out_socketpath points
|
|
|
|
* to.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
int create_socket(const char *filename, char **out_socketpath) {
|
|
|
|
char *resolved = resolve_tilde(filename);
|
|
|
|
DLOG("Creating UNIX socket at %s\n", resolved);
|
|
|
|
char *copy = sstrdup(resolved);
|
|
|
|
const char *dir = dirname(copy);
|
2021-01-15 16:12:55 +01:00
|
|
|
if (!path_exists(dir)) {
|
2021-01-15 14:48:10 +01:00
|
|
|
mkdirp(dir, DEFAULT_DIR_MODE);
|
2021-01-15 16:12:55 +01:00
|
|
|
}
|
2021-01-15 14:48:10 +01:00
|
|
|
free(copy);
|
|
|
|
|
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>
2021-11-04 17:22:22 +01:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
2021-01-15 14:48:10 +01:00
|
|
|
/* Unlink the unix domain socket before */
|
|
|
|
unlink(resolved);
|
|
|
|
|
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>
2021-11-04 17:22:22 +01:00
|
|
|
sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
|
2021-01-15 16:12:55 +01:00
|
|
|
if (sockfd < 0) {
|
2021-01-15 14:48:10 +01:00
|
|
|
perror("socket()");
|
|
|
|
free(resolved);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
(void)fcntl(sockfd, F_SETFD, FD_CLOEXEC);
|
|
|
|
|
|
|
|
struct sockaddr_un addr;
|
|
|
|
memset(&addr, 0, sizeof(struct sockaddr_un));
|
|
|
|
addr.sun_family = AF_LOCAL;
|
|
|
|
strncpy(addr.sun_path, resolved, sizeof(addr.sun_path) - 1);
|
|
|
|
if (bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
|
|
|
|
perror("bind()");
|
|
|
|
free(resolved);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
set_nonblock(sockfd);
|
|
|
|
|
|
|
|
if (listen(sockfd, 5) < 0) {
|
|
|
|
perror("listen()");
|
|
|
|
free(resolved);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(*out_socketpath);
|
|
|
|
*out_socketpath = resolved;
|
|
|
|
return sockfd;
|
|
|
|
}
|