eis: use a lockfile to determine whether we can remove the old socket

Same approach as libwayland-server

Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Peter Hutterer 2021-08-18 09:10:43 +10:00
parent 225e001c55
commit 014c0ce081
2 changed files with 40 additions and 12 deletions

View file

@ -24,8 +24,11 @@
#include "config.h"
#include <stdlib.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <fcntl.h>
#include "util-io.h"
#include "util-object.h"
@ -41,6 +44,8 @@ struct eis_socket {
struct object object;
struct source *listener;
char *socketpath;
char *lockpath;
int lockfd;
};
static inline struct eis_socket *
@ -54,6 +59,10 @@ eis_socket_destroy(struct eis_socket *socket)
{
source_remove(socket->listener);
socket->listener = source_unref(socket->listener);
if (socket->lockpath) {
unlink(socket->lockpath);
xclose(socket->lockfd);
}
if (socket->socketpath) {
unlink(socket->socketpath);
free(socket->socketpath);
@ -115,6 +124,32 @@ eis_setup_backend_socket(struct eis *eis, const char *socketpath)
path = xaprintf("%s/%s", xdg, socketpath);
}
/* Create a lockfile first, if that succeeds but the real socket path exists
* it's a leftover from an unclean shutdown and we can remove the old
* socket file. */
_cleanup_free_ char *lockfile = xaprintf("%s.lock", path);
_cleanup_close_ int lockfd = open(lockfile, O_CREAT|O_CLOEXEC|O_RDWR,
(S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP));
int rc = flock(lockfd, LOCK_EX | LOCK_NB);
if (rc < 0) {
log_error(eis, "Failed to create lockfile %s, is another EIS running?", lockfile);
return -errno;
}
struct stat st;
rc = lstat(path, &st);
if (rc < 0) {
if (errno != ENOENT) {
log_error(eis, "Failed to stat socket path %s (%s)",
path, strerror(errno));
return -errno;
}
} else if (st.st_mode & (S_IWUSR|S_IWGRP)) {
unlink(path);
}
/* Lockfile succeeded and path is unlinked (if it existed), let's set
* up the socket */
struct sockaddr_un addr = {
.sun_family = AF_UNIX,
.sun_path = {0},
@ -133,16 +168,19 @@ eis_setup_backend_socket(struct eis *eis, const char *socketpath)
return -errno;
struct source *s = source_new(sockfd, listener_dispatch, eis_socket);
int rc = sink_add_source(eis->sink, s);
rc = sink_add_source(eis->sink, s);
if (rc == 0) {
eis_socket->listener = source_ref(s);
eis_socket->socketpath = steal(&path);
eis_socket->lockpath = steal(&lockfile);
eis_socket->lockfd = lockfd;
eis->backend = steal(&eis_socket);
eis->backend_interface = interface;
}
source_unref(s);
sockfd = -1;
lockfd = -1;
return rc;
}

View file

@ -288,14 +288,13 @@ static void
usage(FILE *fp, const char *argv0)
{
fprintf(fp,
"Usage: %s [--verbose] [--uinput] [--socketpath=/path/to/socket] [--force]\n"
"Usage: %s [--verbose] [--uinput] [--socketpath=/path/to/socket]\n"
"\n"
"Start an EIS demo server. The server accepts all client connections\n"
"and devices and prints any events from the client to stdout.\n"
"\n"
"Options:\n"
" --socketpath Use the given socket path. Default: $XDG_RUNTIME/eis-0\n"
" --force Remove the socket if it already exists\n"
" --layout Use the given XKB layout (requires libxkbcommon). Default: none\n"
" --uinput Set up each device as uinput device (this requires root)\n"
" --verbose Enable debugging output\n"
@ -307,7 +306,6 @@ int main(int argc, char **argv)
{
bool verbose = false;
bool uinput = false;
bool force = false;
const char *layout = NULL;
_cleanup_unlink_free_ char *socketpath = NULL;
@ -320,14 +318,12 @@ int main(int argc, char **argv)
OPT_VERBOSE,
OPT_LAYOUT,
OPT_SOCKETPATH,
OPT_FORCE,
OPT_UINPUT,
};
static struct option long_opts[] = {
{"socketpath", required_argument, 0, OPT_SOCKETPATH},
{"layout", required_argument, 0, OPT_LAYOUT},
{"uinput", no_argument, 0, OPT_UINPUT},
{"force", no_argument, 0, OPT_FORCE},
{"verbose", no_argument,0, OPT_VERBOSE},
{"help", no_argument,0, 'h'},
{NULL},
@ -352,9 +348,6 @@ int main(int argc, char **argv)
case OPT_UINPUT:
uinput = true;
break;
case OPT_FORCE:
force = true;
break;
case OPT_VERBOSE:
verbose = true;
break;
@ -394,9 +387,6 @@ int main(int argc, char **argv)
signal(SIGINT, sighandler);
if (force)
unlink(socketpath);
int rc = eis_setup_backend_socket(eis, socketpath);
if (rc != 0) {
fprintf(stderr, "init failed: %s\n", strerror(errno));