KWin
Loading...
Searching...
No Matches
xwaylandsocket.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "xwaylandsocket.h"
8#include "xwayland_logging.h"
9
10#include <QCoreApplication>
11#include <QFile>
12#include <QScopeGuard>
13
14#include <errno.h>
15#include <signal.h>
16#include <sys/socket.h>
17#include <sys/stat.h>
18#include <sys/un.h>
19#include <unistd.h>
20
21namespace KWin
22{
23
25{
26public:
27 enum class Type {
28 Unix,
30 };
31
32 UnixSocketAddress(const QString &socketPath, Type type);
33
34 const sockaddr *data() const;
35 int size() const;
36
37private:
38 QByteArray m_buffer;
39};
40
42{
43 const QByteArray encodedSocketPath = QFile::encodeName(socketPath);
44
45 int byteCount = offsetof(sockaddr_un, sun_path) + encodedSocketPath.size() + 1;
46 m_buffer.resize(byteCount);
47
48 sockaddr_un *address = reinterpret_cast<sockaddr_un *>(m_buffer.data());
49 address->sun_family = AF_UNIX;
50
51 if (type == Type::Unix) {
52 memcpy(address->sun_path, encodedSocketPath.data(), encodedSocketPath.size());
53 address->sun_path[encodedSocketPath.size()] = '\0';
54 } else {
55 // Abstract domain socket does not need the NUL-termination byte.
56 *address->sun_path = '\0';
57 memcpy(address->sun_path + 1, encodedSocketPath.data(), encodedSocketPath.size());
58 }
59}
60
61const sockaddr *UnixSocketAddress::data() const
62{
63 return reinterpret_cast<const sockaddr *>(m_buffer.data());
64}
65
67{
68 return m_buffer.size();
69}
70
71static QString lockFileNameForDisplay(int display)
72{
73 return QStringLiteral("/tmp/.X%1-lock").arg(display);
74}
75
76static QString socketFileNameForDisplay(int display)
77{
78 return QStringLiteral("/tmp/.X11-unix/X%1").arg(display);
79}
80
81static bool tryLockFile(const QString &lockFileName)
82{
83 for (int attempt = 0; attempt < 3; ++attempt) {
84 QFile lockFile(lockFileName);
85 if (lockFile.open(QFile::WriteOnly | QFile::NewOnly)) {
86 char buffer[12];
87 snprintf(buffer, sizeof(buffer), "%10lld\n", QCoreApplication::applicationPid());
88 if (lockFile.write(buffer, sizeof(buffer) - 1) != sizeof(buffer) - 1) {
89 qCWarning(KWIN_XWL) << "Failed to write pid to lock file:" << lockFile.errorString();
90 lockFile.remove();
91 return false;
92 }
93 return true;
94 } else if (lockFile.open(QFile::ReadOnly)) {
95 const int lockPid = lockFile.readLine().trimmed().toInt();
96 if (!lockPid) {
97 return false;
98 }
99 if (kill(lockPid, 0) < 0 && errno == ESRCH) {
100 lockFile.remove(); // Try to grab the lock file in the next loop iteration.
101 } else {
102 return false;
103 }
104 }
105 }
106 return false;
107}
108
109static int listen_helper(const QString &filePath, UnixSocketAddress::Type type, XwaylandSocket::OperationMode mode)
110{
111 const UnixSocketAddress socketAddress(filePath, type);
112
113 int socketFlags = SOCK_STREAM;
115 socketFlags |= SOCK_CLOEXEC;
116 }
117 int fileDescriptor = socket(AF_UNIX, socketFlags, 0);
118 if (fileDescriptor == -1) {
119 return -1;
120 }
121
122 if (bind(fileDescriptor, socketAddress.data(), socketAddress.size()) == -1) {
123 close(fileDescriptor);
124 return -1;
125 }
126
127 if (listen(fileDescriptor, 1) == -1) {
128 close(fileDescriptor);
129 return -1;
130 }
131
132 return fileDescriptor;
133}
134
135static bool checkSocketsDirectory()
136{
137 struct stat info;
138 const char *path = "/tmp/.X11-unix";
139
140 if (lstat(path, &info) != 0) {
141 if (errno == ENOENT) {
142 qCWarning(KWIN_XWL) << path << "does not exist. Please check your installation";
143 return false;
144 }
145
146 qCWarning(KWIN_XWL, "Failed to stat %s: %s", path, strerror(errno));
147 return false;
148 }
149
150 if (!S_ISDIR(info.st_mode)) {
151 qCWarning(KWIN_XWL) << path << "is not a directory. Broken system?";
152 return false;
153 }
154 if (info.st_uid != 0 && info.st_uid != getuid()) {
155 qCWarning(KWIN_XWL) << path << "is not owned by root or us";
156 return false;
157 }
158 if (!(info.st_mode & S_ISVTX)) {
159 qCWarning(KWIN_XWL) << path << "has no sticky bit on. Your system might be compromised!";
160 return false;
161 }
162
163 return true;
164}
165
167{
168 if (!checkSocketsDirectory()) {
169 return;
170 }
171
172 for (int display = 0; display < 100; ++display) {
173 const QString socketFilePath = socketFileNameForDisplay(display);
174 const QString lockFilePath = lockFileNameForDisplay(display);
175
176 if (!tryLockFile(lockFilePath)) {
177 continue;
178 }
179
180 QList<int> fileDescriptors;
181 auto socketCleanup = qScopeGuard([&fileDescriptors]() {
182 for (const int &fileDescriptor : std::as_const(fileDescriptors)) {
183 close(fileDescriptor);
184 }
185 });
186
187 QFile::remove(socketFilePath);
188 const int unixFileDescriptor = listen_helper(socketFilePath, UnixSocketAddress::Type::Unix, mode);
189 if (unixFileDescriptor == -1) {
190 QFile::remove(lockFilePath);
191 continue;
192 }
193 fileDescriptors << unixFileDescriptor;
194
195#if defined(Q_OS_LINUX)
196 const int abstractFileDescriptor = listen_helper(socketFilePath, UnixSocketAddress::Type::Abstract, mode);
197 if (abstractFileDescriptor == -1) {
198 QFile::remove(lockFilePath);
199 QFile::remove(socketFilePath);
200 continue;
201 }
202 fileDescriptors << abstractFileDescriptor;
203#endif
204
205 m_fileDescriptors = fileDescriptors;
206 socketCleanup.dismiss();
207
208 m_socketFilePath = socketFilePath;
209 m_lockFilePath = lockFilePath;
210 m_display = display;
211 return;
212 }
213
214 qCWarning(KWIN_XWL) << "Failed to find free X11 connection socket";
215}
216
218{
219 for (const int &fileDescriptor : std::as_const(m_fileDescriptors)) {
220 close(fileDescriptor);
221 }
222 if (!m_socketFilePath.isEmpty()) {
223 QFile::remove(m_socketFilePath);
224 }
225 if (!m_lockFilePath.isEmpty()) {
226 QFile::remove(m_lockFilePath);
227 }
228}
229
231{
232 return m_display != -1;
233}
234
236{
237 return m_fileDescriptors;
238}
239
241{
242 return m_display;
243}
244
245QString XwaylandSocket::name() const
246{
247 return ":" + QString::number(m_display);
248}
249
250} // namespace KWin
const sockaddr * data() const
UnixSocketAddress(const QString &socketPath, Type type)
XwaylandSocket(OperationMode operationMode)
QList< int > fileDescriptors() const
Session::Type type
Definition session.cpp:17