Skip to content

Commit d32867f

Browse files
committed
Recursively search for any available IPC sockets
Restricts the search to Snap and Flatpak sockets, since only sandboxed Discord instances require a recursive search. This is better than hardcoding paths for specific apps that might change or break in the future or that do not work for certain other Discord clients.
1 parent 379206f commit d32867f

File tree

1 file changed

+163
-24
lines changed

1 file changed

+163
-24
lines changed

src/connection_unix.cpp

Lines changed: 163 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88
#include <sys/types.h>
99
#include <sys/un.h>
1010
#include <unistd.h>
11+
#include <dirent.h>
12+
13+
#include <queue>
14+
#include <string>
15+
#include <utility>
16+
#include <functional>
1117

1218
int GetProcessId()
1319
{
@@ -26,14 +32,8 @@ static int MsgFlags = MSG_NOSIGNAL;
2632
static int MsgFlags = 0;
2733
#endif
2834

29-
// Directories where the discord-ipc socket might live
30-
// https://github.com/flathub/com.discordapp.Discord/wiki/Rich-Precense-(discord-rpc)
31-
static const char* DiscordIpcSocketDirs[] = {
32-
"", // Default
33-
"snap.discord", // https://snapcraft.io/discord
34-
".flatpak/com.discordapp.Discord/xdg-run", // https://flathub.org/apps/com.discordapp.Discord
35-
".flatpak/dev.vencord.Vesktop/xdg-run", // https://flathub.org/apps/dev.vencord.Vesktop
36-
};
35+
static std::string IpcFilenamePrefix = "discord-ipc-";
36+
static std::string IpcExtraRootDirPrefixes[] = {"snap.", ".flatpak"};
3737

3838
static const char* GetTempPath()
3939
{
@@ -45,6 +45,145 @@ static const char* GetTempPath()
4545
return temp;
4646
}
4747

48+
class directory_iterator {
49+
public:
50+
directory_iterator(std::string const& path)
51+
: m_path{path}
52+
{
53+
}
54+
directory_iterator(std::string&& path)
55+
: m_path{std::move(path)}
56+
{
57+
}
58+
directory_iterator(const directory_iterator& o)
59+
: m_path{o.m_path}
60+
{
61+
}
62+
directory_iterator(directory_iterator&& o) noexcept
63+
: m_path(std::move(o.m_path))
64+
, m_stream(std::exchange(o.m_stream, nullptr))
65+
{
66+
}
67+
directory_iterator& operator=(const directory_iterator& o)
68+
{
69+
if (this == &o)
70+
return *this;
71+
close();
72+
this->m_path = o.m_path;
73+
return *this;
74+
}
75+
directory_iterator& operator=(directory_iterator&& o) noexcept
76+
{
77+
if (this == &o)
78+
return *this;
79+
close();
80+
m_path = std::move(o.m_path);
81+
m_stream = std::exchange(o.m_stream, nullptr);
82+
return *this;
83+
}
84+
~directory_iterator() { close(); }
85+
std::string const& path() const { return m_path; }
86+
DIR* stream() const { return m_stream; }
87+
explicit operator bool() const noexcept { return m_stream != nullptr; }
88+
bool open()
89+
{
90+
m_stream = opendir(m_path.c_str());
91+
return m_stream != nullptr;
92+
}
93+
struct dirent* next()
94+
{
95+
if (!m_stream) {
96+
return nullptr;
97+
}
98+
auto result = readdir(m_stream);
99+
if (!result) {
100+
close();
101+
}
102+
return result;
103+
}
104+
void close()
105+
{
106+
if (m_stream) {
107+
closedir(m_stream);
108+
m_stream = nullptr;
109+
}
110+
}
111+
112+
private:
113+
std::string m_path;
114+
DIR* m_stream{nullptr};
115+
};
116+
117+
static bool discord_ipc_directory_predicate(std::string const& root_parent,
118+
std::string const& parent,
119+
const char* directory)
120+
{
121+
if (parent != root_parent)
122+
return true;
123+
for (std::string const& prefix : IpcExtraRootDirPrefixes)
124+
if (prefix.compare(0, std::string::npos, directory, prefix.size()) == 0)
125+
return true;
126+
return false;
127+
}
128+
129+
static bool discord_ipc_file_predicate(struct dirent* entry)
130+
{
131+
return entry->d_type == DT_SOCK &&
132+
strncmp(IpcFilenamePrefix.c_str(), entry->d_name, IpcFilenamePrefix.size()) == 0 &&
133+
isdigit(entry->d_name[IpcFilenamePrefix.size()]);
134+
}
135+
136+
static std::string resolve_path(std::string const& dir, struct dirent* entry)
137+
{
138+
std::string file;
139+
file.reserve(dir.size() + 1 + sizeof(entry->d_name) + 1);
140+
file.append(dir);
141+
file.append("/");
142+
file.append(entry->d_name);
143+
return file;
144+
}
145+
146+
// Reads the next iterator entry and sees if it satisfies the predicate.
147+
// If there is no next entry, this function returns an empty string. If all
148+
// remaining files read by this iterator are not a matching file, the next
149+
// directory is popped from the queue, the iterator is set to that directory,
150+
// it is opened and the function proceeds with the search using this iterator.
151+
// This is done until either a matching file is found or the queue is empty.
152+
// Once an entry satisfies the predicate, the full file path is returned.
153+
// Otherwise an empty string is returned.
154+
static std::string directory_find_next_recursive(
155+
directory_iterator& directory,
156+
std::function<bool(std::string const& parent, const char* directory)> directory_predicate,
157+
std::function<bool(struct dirent* entry)> filename_predicate,
158+
std::queue<std::string>& directory_queue)
159+
{
160+
while (directory) {
161+
while (auto entry = directory.next()) {
162+
if (entry->d_type == DT_DIR) {
163+
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
164+
continue;
165+
}
166+
if (!directory_predicate(directory.path(), entry->d_name)) {
167+
continue;
168+
}
169+
directory_queue.push(resolve_path(directory.path(), entry));
170+
continue;
171+
}
172+
if (filename_predicate(entry)) {
173+
auto res = resolve_path(directory.path(), entry);
174+
return res;
175+
}
176+
}
177+
directory.close();
178+
while (!directory && !directory_queue.empty()) {
179+
directory = directory_iterator(std::move(directory_queue.front()));
180+
directory.open();
181+
directory_queue.pop();
182+
}
183+
}
184+
return "";
185+
}
186+
48187
/*static*/ BaseConnection* BaseConnection::Create()
49188
{
50189
PipeAddr.sun_family = AF_UNIX;
@@ -71,22 +210,22 @@ bool BaseConnection::Open()
71210
int optval = 1;
72211
setsockopt(self->sock, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval));
73212
#endif
74-
75-
for (const char* dir : DiscordIpcSocketDirs) {
76-
const char* dir_prefix = dir ? "/" : "";
77-
for (int pipeNum = 0; pipeNum < 10; ++pipeNum) {
78-
snprintf(PipeAddr.sun_path,
79-
sizeof(PipeAddr.sun_path),
80-
"%s%s%s/discord-ipc-%d",
81-
tempPath,
82-
dir_prefix,
83-
dir,
84-
pipeNum);
85-
int err = connect(self->sock, (const sockaddr*)&PipeAddr, sizeof(PipeAddr));
86-
if (err == 0) {
87-
self->isOpen = true;
88-
return true;
89-
}
213+
directory_iterator directory(tempPath);
214+
directory.open();
215+
std::queue<std::string> queue;
216+
auto directory_predicate = std::bind(
217+
discord_ipc_directory_predicate, tempPath, std::placeholders::_1, std::placeholders::_2);
218+
while (true) {
219+
std::string path = directory_find_next_recursive(
220+
directory, directory_predicate, discord_ipc_file_predicate, queue);
221+
if (path.empty()) {
222+
break;
223+
}
224+
snprintf(PipeAddr.sun_path, sizeof(PipeAddr.sun_path), "%s", path.c_str());
225+
int err = connect(self->sock, (const sockaddr*)&PipeAddr, sizeof(PipeAddr));
226+
if (err == 0) {
227+
self->isOpen = true;
228+
return true;
90229
}
91230
}
92231
self->Close();

0 commit comments

Comments
 (0)