1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 // This code is based on Rust implementation at
7 // https://github.com/the8472/weyland-p5000
14 #include <sys/ioctl.h>
15 #include <sys/socket.h>
30 #include "wayland-proxy.h"
32 // The maximum number of fds libwayland can recvmsg at once
33 #define MAX_LIBWAY_FDS 28
34 #define MAX_DATA_SIZE 4096
35 #define POLL_TIMEOUT 30000
37 bool sPrintInfo
= false;
39 void Print(const char* aFormat
, ...) {
44 va_start(args
, aFormat
);
45 vfprintf(stderr
, aFormat
, args
);
49 void Warning(const char* aOperation
) {
50 fprintf(stderr
, "Warning: %s : %s\n", aOperation
, strerror(errno
));
53 void Error(const char* aOperation
) {
54 fprintf(stderr
, "Error: %s : %s\n", aOperation
, strerror(errno
));
57 void ErrorPlain(const char* aFormat
, ...) {
59 va_start(args
, aFormat
);
60 vfprintf(stderr
, aFormat
, args
);
64 class WaylandMessage
{
66 bool Write(int aSocket
);
68 bool Loaded() const { return !mFailed
&& (mFds
.size() || mData
.size()); }
69 bool Failed() const { return mFailed
; }
71 explicit WaylandMessage(int aSocket
) { Read(aSocket
); }
75 void Read(int aSocket
);
80 std::vector
<int> mFds
;
81 std::vector
<unsigned char> mData
;
84 class ProxiedConnection
{
86 bool Init(int aChildSocket
, char* aWaylandDisplay
);
87 bool IsConnected() { return mCompositorConnected
; }
89 struct pollfd
* AddToPollFd(struct pollfd
* aPfds
);
90 struct pollfd
* LoadPollFd(struct pollfd
* aPfds
);
92 // Process this connection (send/receive data).
93 // Returns false if connection is broken and should be removed.
99 // Try to connect to compositor. Returns false in case of fatal error.
100 bool ConnectToCompositor();
102 bool TransferOrQueue(
103 int aSourceSocket
, int aSourcePollFlags
, int aDestSocket
,
104 std::vector
<std::unique_ptr
<WaylandMessage
>>* aMessageQueue
);
105 bool FlushQueue(int aDestSocket
, int aDestPollFlags
,
106 std::vector
<std::unique_ptr
<WaylandMessage
>>& aMessageQueue
);
108 // Where we should connect.
109 // Weak pointer to parent WaylandProxy class.
110 char* mWaylandDisplay
= nullptr;
112 // We don't have connected compositor yet. Try to connect
113 bool mCompositorConnected
= false;
115 // We're disconnected from app or compositor. We will close this connection.
116 bool mFailed
= false;
118 int mCompositorSocket
= -1;
119 int mCompositorFlags
= 0;
121 int mApplicationSocket
= -1;
122 int mApplicationFlags
= 0;
124 // Stored proxied data
125 std::vector
<std::unique_ptr
<WaylandMessage
>> mToCompositorQueue
;
126 std::vector
<std::unique_ptr
<WaylandMessage
>> mToApplicationQueue
;
129 WaylandMessage::~WaylandMessage() {
130 for (auto const fd
: mFds
) {
135 void WaylandMessage::Read(int aSocket
) {
136 // We don't expect WaylandMessage re-read
137 assert(!Loaded() && !mFailed
);
139 mData
.resize(MAX_DATA_SIZE
);
141 struct msghdr msg
= {0};
142 struct iovec iov
= {mData
.data(), mData
.size()};
146 char cmsgdata
[(CMSG_LEN(MAX_LIBWAY_FDS
* sizeof(int32_t)))] = {0};
147 msg
.msg_control
= &cmsgdata
;
148 msg
.msg_controllen
= sizeof(cmsgdata
);
150 ssize_t ret
= recvmsg(aSocket
, &msg
, MSG_CMSG_CLOEXEC
| MSG_DONTWAIT
);
151 if (msg
.msg_flags
& (MSG_CTRUNC
| MSG_TRUNC
)) {
152 Error("WaylandMessage::Read() data truncated, small buffer?");
161 // Neither loaded nor failed, we'll try again later
162 Print("WaylandMessage::Read() failed %s\n", strerror(errno
));
165 Error("WaylandMessage::Read() failed");
171 // Set correct data size
175 struct cmsghdr
* header
= CMSG_FIRSTHDR(&msg
);
177 struct cmsghdr
* next
= CMSG_NXTHDR(&msg
, header
);
178 if (header
->cmsg_level
!= SOL_SOCKET
|| header
->cmsg_type
!= SCM_RIGHTS
) {
183 int* data
= (int*)CMSG_DATA(header
);
184 int filenum
= (int)((header
->cmsg_len
- CMSG_LEN(0)) / sizeof(int));
185 if (filenum
> MAX_LIBWAY_FDS
) {
186 ErrorPlain("WaylandMessage::Read(): too many files to read");
190 for (int i
= 0; i
< filenum
; i
++) {
192 int flags
= fcntl(data
[i
], F_GETFL
, 0);
193 if (flags
== -1 && errno
== EBADF
) {
194 Error("WaylandMessage::Read() invalid fd");
197 mFds
.push_back(data
[i
]);
203 bool WaylandMessage::Write(int aSocket
) {
208 struct msghdr msg
= {0};
209 struct iovec iov
= {mData
.data(), mData
.size()};
214 char buf
[CMSG_SPACE(sizeof(int) * MAX_LIBWAY_FDS
)];
215 struct cmsghdr align
;
217 memset(cmsgu
.buf
, 0, sizeof(cmsgu
.buf
));
219 int filenum
= mFds
.size();
221 if (filenum
> MAX_LIBWAY_FDS
) {
222 ErrorPlain("WaylandMessage::Write() too many files to send\n");
226 for (int i
= 0; i
< filenum
; i
++) {
227 int flags
= fcntl(mFds
[i
], F_GETFL
, 0);
228 if (flags
== -1 && errno
== EBADF
) {
229 Error("WaylandMessage::Write() invalid fd\n");
233 msg
.msg_control
= cmsgu
.buf
;
234 msg
.msg_controllen
= CMSG_SPACE(filenum
* sizeof(int));
236 struct cmsghdr
* cmsg
= CMSG_FIRSTHDR(&msg
);
237 cmsg
->cmsg_level
= SOL_SOCKET
;
238 cmsg
->cmsg_type
= SCM_RIGHTS
;
239 cmsg
->cmsg_len
= CMSG_LEN(filenum
* sizeof(int));
240 memcpy(CMSG_DATA(cmsg
), mFds
.data(), filenum
* sizeof(int));
243 ssize_t ret
= sendmsg(aSocket
, &msg
, MSG_CMSG_CLOEXEC
| MSG_DONTWAIT
);
248 // Neither loaded nor failed, we'll try again later
249 Print("WaylandMessage::Write() failed %s\n", strerror(errno
));
252 Warning("WaylandMessage::Write() failed");
258 if (ret
!= (ssize_t
)mData
.size()) {
259 Print("WaylandMessage::Write() failed to write all data! (%d vs. %d)\n", ret
,
265 ProxiedConnection::~ProxiedConnection() {
266 if (mCompositorSocket
!= -1) {
267 close(mCompositorSocket
);
269 if (mApplicationSocket
!= -1) {
270 close(mApplicationSocket
);
274 bool ProxiedConnection::Init(int aApplicationSocket
, char* aWaylandDisplay
) {
275 mWaylandDisplay
= aWaylandDisplay
;
276 mApplicationSocket
= aApplicationSocket
;
278 socket(AF_UNIX
, SOCK_STREAM
| SOCK_NONBLOCK
| SOCK_CLOEXEC
, 0);
279 if (mCompositorSocket
== -1) {
280 Error("WaylandProxy: ProxiedConnection::Init() socket()");
282 bool ret
= mApplicationSocket
>= 0 && mCompositorSocket
>= 0;
283 Print("WaylandProxy: ProxiedConnection::Init() %s\n", ret
? "OK" : "FAILED");
287 struct pollfd
* ProxiedConnection::AddToPollFd(struct pollfd
* aPfds
) {
288 // Listen application's requests
289 aPfds
->fd
= mApplicationSocket
;
290 aPfds
->events
= POLLIN
;
292 // We're connected and we have data for appplication from compositor.
293 // Add POLLOUT to request write to app socket.
294 if (mCompositorConnected
&& !mToApplicationQueue
.empty()) {
295 aPfds
->events
|= POLLOUT
;
299 aPfds
->fd
= mCompositorSocket
;
301 // We're waiting for connection or we have data for compositor
302 if (!mCompositorConnected
|| !mToCompositorQueue
.empty()) {
303 aPfds
->events
|= POLLOUT
;
305 if (mCompositorConnected
) {
306 aPfds
->events
|= POLLIN
;
313 struct pollfd
* ProxiedConnection::LoadPollFd(struct pollfd
* aPfds
) {
314 if (aPfds
->fd
!= mApplicationSocket
) {
317 mApplicationFlags
= aPfds
->revents
;
319 mCompositorFlags
= aPfds
->revents
;
324 bool ProxiedConnection::ConnectToCompositor() {
325 if (!(mCompositorFlags
& POLLOUT
)) {
330 struct sockaddr_un addr
= {};
331 addr
.sun_family
= AF_UNIX
;
332 strcpy(addr
.sun_path
, mWaylandDisplay
);
334 mCompositorConnected
=
335 connect(mCompositorSocket
, (const struct sockaddr
*)&addr
,
336 sizeof(struct sockaddr_un
)) != -1;
337 if (!mCompositorConnected
) {
346 // We can recover from these errors and try again
347 Warning("ConnectToCompositor() try again");
350 Error("ConnectToCompositor() connect()");
357 // Read data from aSourceSocket and try to twite them to aDestSocket.
358 // If data write fails, append them to aMessageQueue.
360 bool ProxiedConnection::TransferOrQueue(
361 int aSourceSocket
, int aSourcePollFlags
, int aDestSocket
,
362 std::vector
<std::unique_ptr
<WaylandMessage
>>* aMessageQueue
) {
363 // Don't read if we don't have any data ready
364 if (!(aSourcePollFlags
& POLLIN
)) {
369 int availableData
= 0;
370 if (ioctl(aSourceSocket
, FIONREAD
, &availableData
) < 0) {
371 // Broken connection, we're finished here
372 Warning("ProxiedConnection::TransferOrQueue() broken source socket %s\n");
375 if (availableData
== 0) {
379 auto message
= std::make_unique
<WaylandMessage
>(aSourceSocket
);
380 if (message
->Failed()) {
381 // Failed to read message due to error
384 if (!message
->Loaded()) {
388 if (!message
->Write(aDestSocket
)) {
389 if (message
->Failed()) {
390 // Failed to write and we can't recover
393 aMessageQueue
->push_back(std::move(message
));
398 // Try to flush all data to aMessageQueue.
399 bool ProxiedConnection::FlushQueue(
400 int aDestSocket
, int aDestPollFlags
,
401 std::vector
<std::unique_ptr
<WaylandMessage
>>& aMessageQueue
) {
402 // Can't write to destination yet
403 if (!(aDestPollFlags
& POLLOUT
) || aMessageQueue
.empty()) {
407 std::vector
<std::unique_ptr
<WaylandMessage
>>::iterator message
;
408 for (message
= aMessageQueue
.begin(); message
!= aMessageQueue
.end();) {
409 if (!(*message
)->Write(aDestSocket
)) {
410 // Failed to write the message, remove whole connection
412 if ((*message
)->Failed()) {
420 // Remove all written messages at once.
421 if (message
!= aMessageQueue
.begin()) {
422 aMessageQueue
.erase(aMessageQueue
.begin(), message
);
428 bool ProxiedConnection::Process() {
433 // Check if appplication is still listening
434 if (mApplicationFlags
& (POLLHUP
| POLLERR
)) {
438 // Check if compositor is still listening
439 if (mCompositorConnected
) {
440 if (mCompositorFlags
& (POLLHUP
| POLLERR
)) {
444 // Try to reconnect to compositor.
445 if (!ConnectToCompositor()) {
446 Print("Failed to connect to compositor\n");
449 // We're not connected yet but ConnectToCompositor() didn't return
450 // fatal error. Try again later.
451 if (!mCompositorConnected
) {
457 !TransferOrQueue(mCompositorSocket
, mCompositorFlags
, mApplicationSocket
,
458 &mToApplicationQueue
) ||
459 !TransferOrQueue(mApplicationSocket
, mApplicationFlags
, mCompositorSocket
,
460 &mToCompositorQueue
) ||
461 !FlushQueue(mCompositorSocket
, mCompositorFlags
, mToCompositorQueue
) ||
462 !FlushQueue(mApplicationSocket
, mApplicationFlags
, mToApplicationQueue
);
467 bool WaylandProxy::CheckWaylandDisplay(const char* aWaylandDisplay
) {
468 struct sockaddr_un addr
= {};
469 addr
.sun_family
= AF_UNIX
;
470 strcpy(addr
.sun_path
, aWaylandDisplay
);
472 int sc
= socket(AF_UNIX
, SOCK_STREAM
| SOCK_NONBLOCK
| SOCK_CLOEXEC
, 0);
474 Error("CheckWaylandDisplay(): failed to create socket");
479 connect(sc
, (const struct sockaddr
*)&addr
,
480 sizeof(struct sockaddr_un
)) != -1;
490 // We can recover from these errors and try again
495 "CheckWaylandDisplay(): Failed to connect to Wayland display '%s' error: %s\n",
496 mWaylandDisplay
, strerror(errno
));
506 bool WaylandProxy::SetupWaylandDisplays() {
507 char* waylandDisplay
= getenv("WAYLAND_DISPLAY_COMPOSITOR");
508 if (!waylandDisplay
) {
509 waylandDisplay
= getenv("WAYLAND_DISPLAY");
510 if (!waylandDisplay
|| waylandDisplay
[0] == '\0') {
511 ErrorPlain("WaylandProxy::SetupWaylandDisplays(), Missing Wayland display, WAYLAND_DISPLAY is empty.");
516 char* XDGRuntimeDir
= getenv("XDG_RUNTIME_DIR");
517 if (!XDGRuntimeDir
) {
518 ErrorPlain("WaylandProxy::SetupWaylandDisplays() Missing XDG_RUNTIME_DIR");
522 // WAYLAND_DISPLAY can be absolute path
523 if (waylandDisplay
[0] == '/') {
524 if (strlen(mWaylandDisplay
) >= sMaxDisplayNameLen
) {
525 ErrorPlain("WaylandProxy::SetupWaylandDisplays() WAYLAND_DISPLAY is too large.");
528 strcpy(mWaylandDisplay
, waylandDisplay
);
530 int ret
= snprintf(mWaylandDisplay
, sMaxDisplayNameLen
, "%s/%s",
531 XDGRuntimeDir
, waylandDisplay
);
532 if (ret
< 0 || ret
>= sMaxDisplayNameLen
) {
533 ErrorPlain("WaylandProxy::SetupWaylandDisplays() WAYLAND_DISPLAY/XDG_RUNTIME_DIR is too large.");
538 if (!CheckWaylandDisplay(mWaylandDisplay
)) {
542 int ret
= snprintf(mWaylandProxy
, sMaxDisplayNameLen
,
543 "%s/wayland-proxy-%d", XDGRuntimeDir
, getpid());
544 if (ret
< 0 || ret
>= sMaxDisplayNameLen
) {
545 ErrorPlain("WaylandProxy::SetupWaylandDisplays() WAYLAND_DISPLAY/XDG_RUNTIME_DIR is too large.");
549 // Save original Wayland display variable for potential reuse
550 setenv("WAYLAND_DISPLAY_COMPOSITOR", waylandDisplay
, /* overwrite = */ true);
552 Info("SetupWaylandDisplays() Wayland '%s' proxy '%s'\n",
553 mWaylandDisplay
, mWaylandProxy
);
557 bool WaylandProxy::StartProxyServer() {
559 socket(AF_UNIX
, SOCK_STREAM
| SOCK_NONBLOCK
| SOCK_CLOEXEC
, 0);
560 if (mProxyServerSocket
== -1) {
561 Error("StartProxyServer(): failed to create socket");
565 struct sockaddr_un serverName
= {0};
566 serverName
.sun_family
= AF_UNIX
;
567 strcpy(serverName
.sun_path
, mWaylandProxy
);
569 if (bind(mProxyServerSocket
, (struct sockaddr
*)&serverName
,
570 sizeof(serverName
)) == -1) {
571 Error("StartProxyServer(): bind() error");
574 if (listen(mProxyServerSocket
, 128) == -1) {
575 Error("StartProxyServer(): listen() error");
582 bool WaylandProxy::Init() {
585 if (!SetupWaylandDisplays()) {
589 if (!StartProxyServer()) {
593 Info("Init() finished\n");
597 void WaylandProxy::SetWaylandProxyDisplay() {
598 Info("SetWaylandProxyDisplay() WAYLAND_DISPLAY %s\n", mWaylandDisplay
);
599 setenv("WAYLAND_DISPLAY", mWaylandProxy
, /* overwrite = */ true);
602 void WaylandProxy::RestoreWaylandDisplay() {
603 unlink(mWaylandProxy
);
604 char* waylandDisplay
= getenv("WAYLAND_DISPLAY_COMPOSITOR");
605 if (waylandDisplay
) {
606 Info("RestoreWaylandDisplay() WAYLAND_DISPLAY restored to %s\n",
608 setenv("WAYLAND_DISPLAY", waylandDisplay
, /* overwrite = */ true);
609 unsetenv("WAYLAND_DISPLAY_COMPOSITOR");
613 bool WaylandProxy::IsChildAppTerminated() {
614 if (!mApplicationPID
) {
618 int ret
= waitpid(mApplicationPID
, &status
, WNOHANG
| WUNTRACED
| WCONTINUED
);
622 if (ret
== mApplicationPID
) {
623 // Child application is terminated, so quit too.
626 bool terminate
= (errno
== ECHILD
);
627 Error("IsChildAppTerminated: waitpid() error");
631 bool WaylandProxy::PollConnections() {
632 int nfds_max
= mConnections
.size() * 2 + 1;
634 struct pollfd pollfds
[nfds_max
];
635 struct pollfd
* addedPollfd
= pollfds
;
637 for (auto const& connection
: mConnections
) {
638 addedPollfd
= connection
->AddToPollFd(addedPollfd
);
640 int nfds
= (addedPollfd
- pollfds
);
642 // If all connections are attached to compositor, add another one
643 // for new potential connection from application.
644 bool addNewConnection
= mConnections
.empty() ||
645 mConnections
.back()->IsConnected();
646 if (addNewConnection
) {
647 addedPollfd
->fd
= mProxyServerSocket
;
648 addedPollfd
->events
= POLLIN
;
651 assert(addedPollfd
< pollfds
+ nfds_max
);
654 int ret
= poll(pollfds
, nfds
, POLL_TIMEOUT
);
658 } else if (ret
> 0) {
659 // We have FD to read
661 } else if (ret
== -1) {
665 if (IsChildAppTerminated()) {
670 Error("Run: poll() error");
676 struct pollfd
* loadedPollfd
= pollfds
;
677 for (auto const& connection
: mConnections
) {
678 loadedPollfd
= connection
->LoadPollFd(loadedPollfd
);
681 assert(loadedPollfd
== addedPollfd
);
682 assert(loadedPollfd
< pollfds
+ nfds_max
);
684 // Create a new connection if there's a new client waiting
685 if (addNewConnection
&& (loadedPollfd
->revents
& POLLIN
)) {
686 Info("new child connection\n");
687 int applicationSocket
= accept4(loadedPollfd
->fd
, nullptr, nullptr,
688 SOCK_NONBLOCK
| SOCK_CLOEXEC
);
689 if (applicationSocket
== -1) {
696 Error("Faild to accept connection from application");
700 auto connection
= std::make_unique
<ProxiedConnection
>();
701 if (connection
->Init(applicationSocket
, mWaylandDisplay
)) {
702 mConnections
.push_back(std::move(connection
));
710 bool WaylandProxy::ProcessConnections() {
711 std::vector
<std::unique_ptr
<ProxiedConnection
>>::iterator connection
;
712 for (connection
= mConnections
.begin(); connection
!= mConnections
.end();) {
713 if (!(*connection
)->Process()) {
714 Info("remove connection\n");
715 connection
= mConnections
.erase(connection
);
716 if (mConnections
.empty()) {
717 // We removed last connection - quit.
718 Info("removed last connection, quit\n");
728 void WaylandProxy::Run() {
729 while (!IsChildAppTerminated() && PollConnections() && ProcessConnections())
733 WaylandProxy::~WaylandProxy() {
734 Info("terminated\n");
735 if (mThreadRunning
) {
736 Info("thread is still running, terminating.\n");
737 mThreadRunning
= false;
738 pthread_cancel(mThread
);
739 pthread_join(mThread
, nullptr);
741 if (mProxyServerSocket
!= -1) {
742 close(mProxyServerSocket
);
744 RestoreWaylandDisplay();
747 void* WaylandProxy::RunProxyThread(WaylandProxy
* aProxy
) {
749 pthread_setname_np(pthread_self(), "WaylandProxy");
752 Print("[%d] WaylandProxy [%p]: thread exited.\n", getpid(), aProxy
);
756 std::unique_ptr
<WaylandProxy
> WaylandProxy::Create() {
757 auto proxy
= std::make_unique
<WaylandProxy
>();
758 Print("[%d] WaylandProxy [%p]: Created().\n", getpid(), proxy
.get());
759 if (!proxy
->Init()) {
760 Print("[%d] WaylandProxy [%p]: Init failed, exiting.\n", getpid(), proxy
.get());
766 bool WaylandProxy::RunChildApplication(char* argv
[]) {
768 ErrorPlain("WaylandProxy::RunChildApplication: missing application to run");
772 mApplicationPID
= fork();
773 if (mApplicationPID
== -1) {
774 Error("WaylandProxy::RunChildApplication: fork() error");
777 if (mApplicationPID
== 0) {
778 SetWaylandProxyDisplay();
779 if (execv(argv
[0], argv
) == -1) {
781 "WaylandProxy::RunChildApplication: failed to run %s error %s\n",
782 argv
[0], strerror(errno
));
791 bool WaylandProxy::RunThread() {
793 if (pthread_attr_init(&attr
) != 0) {
794 ErrorPlain("WaylandProxy::RunThread(): pthread_attr_init() failed\n");
799 if (pthread_attr_getschedparam(&attr
, ¶m
) == 0) {
800 param
.sched_priority
= sched_get_priority_min(SCHED_RR
);
801 pthread_attr_setschedparam(&attr
, ¶m
);
804 SetWaylandProxyDisplay();
806 mThreadRunning
= pthread_create(&mThread
, nullptr, (void* (*)(void*))RunProxyThread
, this) == 0;
807 if (!mThreadRunning
) {
808 ErrorPlain("WaylandProxy::RunThread(): pthread_create() failed\n");
809 // If we failed to run proxy thread, set WAYLAND_DISPLAY back.
810 RestoreWaylandDisplay();
813 pthread_attr_destroy(&attr
);
814 return mThreadRunning
;
817 void WaylandProxy::SetVerbose(bool aVerbose
) { sPrintInfo
= aVerbose
; }
819 void WaylandProxy::Info(const char* aFormat
, ...) {
823 fprintf(stderr
,"[%d] WaylandProxy [%p]: ", getpid(), this);
825 va_start(args
, aFormat
);
826 vfprintf(stderr
, aFormat
, args
);
830 void WaylandProxy::Warning(const char* aOperation
) {
831 fprintf(stderr
, "[%d] Wayland Proxy [%p] Warning: %s : %s\n",
832 getpid(), this, aOperation
, strerror(errno
));
835 void WaylandProxy::Error(const char* aOperation
) {
836 fprintf(stderr
, "[%d] Wayland Proxy [%p] Error: %s : %s\n",
837 getpid(), this, aOperation
, strerror(errno
));
840 void WaylandProxy::ErrorPlain(const char* aFormat
, ...) {
841 fprintf(stderr
, "[%d] Wayland Proxy [%p] Error: ", getpid(), this);
843 va_start(args
, aFormat
);
844 vfprintf(stderr
, aFormat
, args
);