@@ -54,14 +54,96 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
5454namespace Sys {
5555static Cvar::Cvar<bool > cvar_common_shutdownOnDrop (" common.shutdownOnDrop" , " shut down engine on game drop" , Cvar::TEMPORARY, false );
5656
57+ static std::string singletonSocketPath;
5758#ifdef _WIN32
5859static HANDLE singletonSocket;
5960#else
61+ #define SINGLETON_SOCKET_BASENAME " socket"
6062static int singletonSocket;
6163static FS::File lockFile;
6264static 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 ));
6377#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
65147
66148// Get the path of a singleton socket
67149std::string GetSingletonSocketPath ()
@@ -78,7 +160,8 @@ std::string GetSingletonSocketPath()
78160 // We use a temporary directory rather that using the homepath because
79161 // socket paths are limited to about 100 characters. This also avoids issues
80162 // 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);
82165#endif
83166}
84167
@@ -120,11 +203,9 @@ static void CreateSingletonSocket()
120203 fchmod (singletonSocket, 0600 );
121204 mkdir (dirName.c_str (), 0700 );
122205
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) );
128209
129210 if (listen (singletonSocket, SOMAXCONN) == -1 )
130211 Sys::Error (" Could not listen on singleton socket file \" %s\" , error: \" %s\" " , singletonSocketPath, strerror (errno) );
@@ -154,12 +235,10 @@ static bool ConnectSingletonSocket()
154235 if (singletonSocket == -1 )
155236 Sys::Error (" Could not create socket: %s" , strerror (errno));
156237
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));
163242 close (singletonSocket);
164243 return false ;
165244 }
0 commit comments