// Copyright (C) 2023 David Edmundson // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include #include /* This is the size of the char array in struct sock_addr_un. * No Wayland socket can be created with a path longer than this, * including the null terminator. */ #ifndef UNIX_PATH_MAX #define UNIX_PATH_MAX 108 #endif #define LOCK_SUFFIX ".lock" #define LOCK_SUFFIXLEN 5 struct wl_socket { int fd; int fd_lock; struct sockaddr_un addr; char lock_addr[UNIX_PATH_MAX + LOCK_SUFFIXLEN]; char display_name[16]; }; static struct wl_socket *wl_socket_alloc(void) { struct wl_socket *s; s = malloc(sizeof *s); if (!s) return NULL; s->fd = -1; s->fd_lock = -1; return s; } static int wl_socket_lock(struct wl_socket *socket) { struct stat socket_stat; snprintf(socket->lock_addr, sizeof socket->lock_addr, "%s%s", socket->addr.sun_path, LOCK_SUFFIX); // differening from kwin, we're back to setting CLOEXEC as we're all in process socket->fd_lock = open(socket->lock_addr, O_CREAT | O_RDWR | O_CLOEXEC, (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)); if (socket->fd_lock < 0) { printf("unable to open lockfile %s check permissions\n", socket->lock_addr); goto err; } if (flock(socket->fd_lock, LOCK_EX | LOCK_NB) < 0) { printf("unable to lock lockfile %s, maybe another compositor is running\n", socket->lock_addr); goto err_fd; } if (lstat(socket->addr.sun_path, &socket_stat) < 0) { if (errno != ENOENT) { printf("did not manage to stat file %s\n", socket->addr.sun_path); goto err_fd; } } else if (socket_stat.st_mode & S_IWUSR || socket_stat.st_mode & S_IWGRP) { unlink(socket->addr.sun_path); } return 0; err_fd: close(socket->fd_lock); socket->fd_lock = -1; err: *socket->lock_addr = 0; /* we did not set this value here, but without lock the * socket won't be created anyway. This prevents the * wl_socket_destroy from unlinking already existing socket * created by other compositor */ *socket->addr.sun_path = 0; return -1; } void wl_socket_destroy(struct wl_socket *s) { if (s->addr.sun_path[0]) unlink(s->addr.sun_path); if (s->fd >= 0) close(s->fd); if (s->lock_addr[0]) unlink(s->lock_addr); if (s->fd_lock >= 0) close(s->fd_lock); free(s); } const char *wl_socket_get_display_name(struct wl_socket *s) { return s->display_name; } int wl_socket_get_fd(struct wl_socket *s) { return s->fd; } struct wl_socket *wl_socket_create() { struct wl_socket *s; int displayno = 0; int name_size; /* A reasonable number of maximum default sockets. If * you need more than this, use the explicit add_socket API. */ const int MAX_DISPLAYNO = 32; const char *runtime_dir = getenv("XDG_RUNTIME_DIR"); if (!runtime_dir) { printf("XDG_RUNTIME_DIR not set"); return NULL; } s = wl_socket_alloc(); if (s == NULL) return NULL; do { snprintf(s->display_name, sizeof s->display_name, "wayland-%d", displayno); s->addr.sun_family = AF_LOCAL; name_size = snprintf(s->addr.sun_path, sizeof s->addr.sun_path, "%s/%s", runtime_dir, s->display_name) + 1; assert(name_size > 0); if (name_size > (int)sizeof s->addr.sun_path) { goto fail; } if (wl_socket_lock(s) < 0) continue; s->fd = socket(PF_LOCAL, SOCK_STREAM, 0); int size = SUN_LEN(&s->addr); int ret = bind(s->fd, (struct sockaddr*)&s->addr, size); if (ret < 0) { goto fail; } ret = listen(s->fd, 128); if (ret < 0) { goto fail; } return s; } while (displayno++ < MAX_DISPLAYNO); fail: wl_socket_destroy(s); return NULL; }