@@ -54,14 +54,96 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
54
54
namespace Sys {
55
55
static Cvar::Cvar<bool > cvar_common_shutdownOnDrop (" common.shutdownOnDrop" , " shut down engine on game drop" , Cvar::TEMPORARY, false );
56
56
57
+ static std::string singletonSocketPath;
57
58
#ifdef _WIN32
58
59
static HANDLE singletonSocket;
59
60
#else
61
+ #define SINGLETON_SOCKET_BASENAME " socket"
60
62
static int singletonSocket;
61
63
static FS::File lockFile;
62
64
static bool haveSingletonLock = false ;
65
+
66
+ static void FillSocketStruct (struct sockaddr_un &addr)
67
+ {
68
+ addr.sun_family = AF_UNIX;
69
+ #ifdef __APPLE__
70
+ Q_strncpyz (addr.sun_path , SINGLETON_SOCKET_BASENAME, sizeof (addr.sun_path ));
71
+ #else
72
+ if (singletonSocketPath.size () > sizeof (addr.sun_path )) {
73
+ Sys::Error (" Singleton socket name '%s' is too long. Try configuring a shorter $TMPDIR" ,
74
+ singletonSocketPath);
75
+ }
76
+ Q_strncpyz (addr.sun_path , singletonSocketPath.c_str (), sizeof (addr.sun_path ));
63
77
#endif
64
- static std::string singletonSocketPath;
78
+ }
79
+
80
+ #ifdef __APPLE__
81
+ // Secret Apple APIs. Chrome just declares them like this
82
+ extern " C" {
83
+ int pthread_chdir_np (const char * path);
84
+ int pthread_fchdir_np (int fd);
85
+ }
86
+
87
+ // These ChdirWrapper* functions return 0 on success or an errno on failure
88
+ static int ChdirWrapperSingletonSocketConnect ()
89
+ {
90
+ std::string dirName = FS::Path::DirName (singletonSocketPath);
91
+ int error = 0 ;
92
+ if (0 == pthread_chdir_np (dirName.c_str ())) {
93
+ struct sockaddr_un addr;
94
+ FillSocketStruct (addr);
95
+ if (0 != connect (singletonSocket, reinterpret_cast <struct sockaddr *>(&addr), sizeof (addr))) {
96
+ error = errno;
97
+ }
98
+ } else {
99
+ // Assume the directory didn't exist. If it does but it is not accessible, we should
100
+ // hit another error soon when trying to create the singleton socket
101
+ error = ENOENT;
102
+ }
103
+ pthread_fchdir_np (-1 ); // reset CWD
104
+ return error;
105
+ }
106
+
107
+ static int ChdirWrapperSingletonSocketBind ()
108
+ {
109
+ std::string dirName = FS::Path::DirName (singletonSocketPath);
110
+ bool chdirSuccess = 0 == pthread_chdir_np (dirName.c_str ());
111
+ int error = 0 ;
112
+ if (chdirSuccess) {
113
+ struct sockaddr_un addr;
114
+ FillSocketStruct (addr);
115
+ if (0 != bind (singletonSocket, reinterpret_cast <struct sockaddr *>(&addr), sizeof (addr))) {
116
+ error = errno;
117
+ }
118
+ }
119
+ pthread_fchdir_np (-1 ); // reset CWD
120
+ if (!chdirSuccess) {
121
+ Sys::Error (" Failed to create or failed to access singleton socket directory '%s'" , dirName);
122
+ }
123
+ return error;
124
+ }
125
+ #else // ! ifdef __APPLE__
126
+ // TODO: supposedly there is an API that can be used to make chdir thread safe
127
+ // on Linux too called "unshare"?
128
+ static int ChdirWrapperSingletonSocketConnect ()
129
+ {
130
+ struct sockaddr_un addr;
131
+ FillSocketStruct (addr);
132
+ return 0 == connect (singletonSocket, reinterpret_cast <struct sockaddr *>(&addr), sizeof (addr))
133
+ ? 0
134
+ : errno;
135
+ }
136
+
137
+ static int ChdirWrapperSingletonSocketBind ()
138
+ {
139
+ struct sockaddr_un addr;
140
+ FillSocketStruct (addr);
141
+ return 0 == bind (singletonSocket, reinterpret_cast <struct sockaddr *>(&addr), sizeof (addr))
142
+ ? 0
143
+ : errno;
144
+ }
145
+ #endif // ! ifdef __APPLE__
146
+ #endif // ! ifdef _WIN32
65
147
66
148
// Get the path of a singleton socket
67
149
std::string GetSingletonSocketPath ()
@@ -78,7 +160,8 @@ std::string GetSingletonSocketPath()
78
160
// We use a temporary directory rather that using the homepath because
79
161
// socket paths are limited to about 100 characters. This also avoids issues
80
162
// when the homepath is on a network filesystem.
81
- return FS::Path::Build (FS::Path::Build (FS::DefaultTempPath (), " ." PRODUCT_NAME_LOWER + suffix), " socket" );
163
+ return FS::Path::Build (FS::Path::Build (
164
+ FS::DefaultTempPath (), " ." PRODUCT_NAME_LOWER + suffix), SINGLETON_SOCKET_BASENAME);
82
165
#endif
83
166
}
84
167
@@ -120,11 +203,9 @@ static void CreateSingletonSocket()
120
203
fchmod (singletonSocket, 0600 );
121
204
mkdir (dirName.c_str (), 0700 );
122
205
123
- struct sockaddr_un addr;
124
- addr.sun_family = AF_UNIX;
125
- Q_strncpyz (addr.sun_path , singletonSocketPath.c_str (), sizeof (addr.sun_path ));
126
- if (bind (singletonSocket, reinterpret_cast <struct sockaddr *>(&addr), sizeof (addr)) == -1 )
127
- Sys::Error (" Could not bind singleton socket at file: \" %s\" , error: \" %s\" " , singletonSocketPath, strerror (errno) );
206
+ int bindErr = ChdirWrapperSingletonSocketBind ();
207
+ if (bindErr != 0 )
208
+ Sys::Error (" Could not bind singleton socket at file: \" %s\" , error: \" %s\" " , singletonSocketPath, strerror (bindErr) );
128
209
129
210
if (listen (singletonSocket, SOMAXCONN) == -1 )
130
211
Sys::Error (" Could not listen on singleton socket file \" %s\" , error: \" %s\" " , singletonSocketPath, strerror (errno) );
@@ -154,12 +235,10 @@ static bool ConnectSingletonSocket()
154
235
if (singletonSocket == -1 )
155
236
Sys::Error (" Could not create socket: %s" , strerror (errno));
156
237
157
- struct sockaddr_un addr;
158
- addr.sun_family = AF_UNIX;
159
- Q_strncpyz (addr.sun_path , singletonSocketPath.c_str (), sizeof (addr.sun_path ));
160
- if (connect (singletonSocket, reinterpret_cast <struct sockaddr *>(&addr), sizeof (addr)) == -1 ) {
161
- if (errno != ENOENT)
162
- Log::Warn (" Could not connect to existing instance: %s" , strerror (errno));
238
+ int connectErr = ChdirWrapperSingletonSocketConnect ();
239
+ if (connectErr != 0 ) {
240
+ if (connectErr != ENOENT)
241
+ Log::Warn (" Could not connect to existing instance: %s" , strerror (connectErr));
163
242
close (singletonSocket);
164
243
return false ;
165
244
}
0 commit comments