File indexing completed on 2024-11-10 04:56:46

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2020 <davidedmundson@kde.org>
0006     SPDX-FileCopyrightText: 2008 Kristian Høgsberg
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #define _DEFAULT_SOURCE
0012 #include <assert.h>
0013 #include <errno.h>
0014 #include <fcntl.h>
0015 #include <stdio.h>
0016 #include <stdlib.h>
0017 #include <string.h>
0018 #include <sys/file.h>
0019 #include <sys/socket.h>
0020 #include <sys/stat.h>
0021 #include <sys/un.h>
0022 #include <unistd.h>
0023 
0024 /* This is the size of the char array in struct sock_addr_un.
0025  * No Wayland socket can be created with a path longer than this,
0026  * including the null terminator.
0027  */
0028 #ifndef UNIX_PATH_MAX
0029 #define UNIX_PATH_MAX 108
0030 #endif
0031 
0032 #define LOCK_SUFFIX ".lock"
0033 #define LOCK_SUFFIXLEN 5
0034 
0035 struct wl_socket {
0036     int fd;
0037     int fd_lock;
0038     struct sockaddr_un addr;
0039     char lock_addr[UNIX_PATH_MAX + LOCK_SUFFIXLEN];
0040     char display_name[20];
0041 };
0042 
0043 static struct wl_socket *wl_socket_alloc(void)
0044 {
0045     struct wl_socket *s;
0046 
0047     s = malloc(sizeof *s);
0048     if (!s)
0049         return NULL;
0050 
0051     s->fd = -1;
0052     s->fd_lock = -1;
0053 
0054     return s;
0055 }
0056 
0057 static int wl_socket_lock(struct wl_socket *socket)
0058 {
0059     struct stat socket_stat;
0060 
0061     snprintf(socket->lock_addr, sizeof socket->lock_addr, "%s%s", socket->addr.sun_path, LOCK_SUFFIX);
0062 
0063     socket->fd_lock = open(socket->lock_addr, O_CREAT | O_CLOEXEC | O_RDWR, (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP));
0064 
0065     if (socket->fd_lock < 0) {
0066         printf("unable to open lockfile %s check permissions\n", socket->lock_addr);
0067         goto err;
0068     }
0069 
0070     if (flock(socket->fd_lock, LOCK_EX | LOCK_NB) < 0) {
0071         printf("unable to lock lockfile %s, maybe another compositor is running\n", socket->lock_addr);
0072         goto err_fd;
0073     }
0074 
0075     if (lstat(socket->addr.sun_path, &socket_stat) < 0) {
0076         if (errno != ENOENT) {
0077             printf("did not manage to stat file %s\n", socket->addr.sun_path);
0078             goto err_fd;
0079         }
0080     } else if (socket_stat.st_mode & S_IWUSR || socket_stat.st_mode & S_IWGRP) {
0081         unlink(socket->addr.sun_path);
0082     }
0083 
0084     return 0;
0085 err_fd:
0086     close(socket->fd_lock);
0087     socket->fd_lock = -1;
0088 err:
0089     *socket->lock_addr = 0;
0090     /* we did not set this value here, but without lock the
0091      * socket won't be created anyway. This prevents the
0092      * wl_socket_destroy from unlinking already existing socket
0093      * created by other compositor */
0094     *socket->addr.sun_path = 0;
0095 
0096     return -1;
0097 }
0098 
0099 void wl_socket_destroy(struct wl_socket *s)
0100 {
0101     if (s->addr.sun_path[0])
0102         unlink(s->addr.sun_path);
0103     if (s->fd >= 0)
0104         close(s->fd);
0105     if (s->lock_addr[0])
0106         unlink(s->lock_addr);
0107     if (s->fd_lock >= 0)
0108         close(s->fd_lock);
0109 
0110     free(s);
0111 }
0112 
0113 const char *wl_socket_get_display_name(struct wl_socket *s)
0114 {
0115     return s->display_name;
0116 }
0117 
0118 int wl_socket_get_fd(struct wl_socket *s)
0119 {
0120     return s->fd;
0121 }
0122 
0123 struct wl_socket *wl_socket_create()
0124 {
0125     struct wl_socket *s;
0126     int displayno = 0;
0127     int name_size;
0128 
0129     /* A reasonable number of maximum default sockets. If
0130      * you need more than this, use the explicit add_socket API. */
0131     const int MAX_DISPLAYNO = 32;
0132     const char *runtime_dir = getenv("XDG_RUNTIME_DIR");
0133     if (!runtime_dir) {
0134         printf("XDG_RUNTIME_DIR not set");
0135         return NULL;
0136     }
0137 
0138     s = wl_socket_alloc();
0139     if (s == NULL)
0140         return NULL;
0141 
0142     do {
0143         snprintf(s->display_name, sizeof s->display_name, "wayland-%d", displayno);
0144         s->addr.sun_family = AF_LOCAL;
0145         name_size = snprintf(s->addr.sun_path, sizeof s->addr.sun_path, "%s/%s", runtime_dir, s->display_name) + 1;
0146         assert(name_size > 0);
0147 
0148         if (name_size > (int)sizeof s->addr.sun_path) {
0149             goto fail;
0150         }
0151 
0152         if (wl_socket_lock(s) < 0)
0153             continue;
0154 
0155         s->fd = socket(PF_LOCAL, SOCK_STREAM, 0);
0156 
0157         int size = SUN_LEN(&s->addr);
0158         int ret = bind(s->fd, (struct sockaddr*)&s->addr, size);
0159         if (ret < 0) {
0160             goto fail;
0161         }
0162         ret = listen(s->fd, 128);
0163         if (ret < 0) {
0164             goto fail;
0165         }
0166         return s;
0167     } while (displayno++ < MAX_DISPLAYNO);
0168 
0169 fail:
0170     wl_socket_destroy(s);
0171     return NULL;
0172 }