1 /*
2 * This file is part of the Code::Blocks IDE and licensed under the GNU General Public License, version 3
3 * http://www.gnu.org/licenses/gpl-3.0.html
4 *
5 * $Revision: 8245 $
6 * $Id: ipc.cpp 8245 2012-08-22 06:51:14Z mortenmacfly $
7 * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/src/ipc.cpp $
8 */
9
10 #include "ipc.h"
11 #include "main.h"
12 #include <wx/tokenzr.h>
13
14 const wxString g_failed_shm(_T("Failed creating shared memory initialising IPC (error 0x00000d04)."));
15 const wxString g_failed_sem(_T("Failed creating semaphore/mutex initialising IPC (error 0x007f0002)."));
16
Send(const wxString & in)17 void IPC::Send(const wxString& in)
18 {
19 if (in.length() * sizeof(wxChar) > shm.Size())
20 cbThrow(_T("Input exceeds shared memory size (error 0x0000cde0)."));
21
22 if (shm.Lock(SharedMemory::writer) == 0)
23 {
24 // If locking failed here, this means the semaphore (and hence the shared memory, and the server process) was destroyed
25 // after we *just* checked that it exists (a few nanoseconds ago). This is a funny race condition
26 // which should be really, really rare, but which is of course nevertheless possible.
27 // We should consequently turn this process into a server, after seeing that the semaphore died, but this is really awful,
28 // so... we're not doing that... for now. The worst thing to happen is that double-clicking a file does not do anything once in a million times.
29 //
30 // Let's just throw and see how often we see this exception in normal everyday use.
31 // If it never happens, then simply ignoring the issue is a perfectly acceptable solution.
32 //
33 cbThrow(_T("Congrats, you managed to kill process 1 within nanoseconds after launching process 2, which is quite hard to do.\n\nPlease inform the Code::Blocks team of your achievement."));
34 }
35
36 memcpy(shm.BasePointer(), in.c_str(), (in.length()+1) * sizeof(wxChar));
37 shm.Unlock(SharedMemory::writer);
38 }
39
40
Shutdown()41 void IPC::Shutdown()
42 {
43 // Other than POSIX, Windows does not signal threads waiting for a semaphore when the semaphore is deleted (at least, MSDN says so),
44 // therefore we have to do unlock by hand before deleting, or we may lock up a process for all times.
45 // IMPORTANT: This must be called from Manager::Shutdown() or from any other appropriate place
46 is_shutdown = true;
47 shm.Unlock(SharedMemory::writer);
48 };
49
50
Entry()51 wxThread::ExitCode IPC::Entry() /* this is the receiving end */
52 {
53 for(;;)
54 {
55 if (shm.Lock(SharedMemory::reader) == 0 || is_shutdown)
56 return 0;
57
58 MainFrame* cbframe = static_cast<MainFrame*>(Manager::Get()->GetAppFrame());
59 if (cbframe == nullptr)
60 return 0;
61
62 cbframe->OnDropFiles(0,0, wxStringTokenize((const wxChar*) shm.BasePointer(), _T("\n"), wxTOKEN_STRTOK));
63
64 shm.Unlock(SharedMemory::reader);
65
66 if (is_shutdown)
67 return 0;
68 }
69 }
70
71
72 #if defined (__WIN32__) /* ------------------------------------------------------------- */
73
74
SharedMemory()75 SharedMemory::SharedMemory() : handle(0), semid(0), shared(0), ok(false), server(false)
76 {
77 SetLastError(0); // CreateSemaphore should already do this, but anyway...
78 sem[reader] = CreateSemaphore(nullptr, 0, 1, TEXT("CdeBlsemIPCr"));
79 sem[writer] = CreateSemaphore(nullptr, 1, 1, TEXT("CdeBlsemIPCw"));
80
81 if (GetLastError() != ERROR_ALREADY_EXISTS)
82 {
83 server = true;
84 }
85
86 handle = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, ipc_buf_size, TEXT("CdeBlshmIPC"));
87
88 if (handle == 0 || (shared = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, ipc_buf_size)) == 0)
89 {
90 LogManager::Get()->Panic(g_failed_shm);
91 return;
92 }
93
94 ok = true;
95 }
96
~SharedMemory()97 SharedMemory::~SharedMemory()
98 {
99 UnmapViewOfFile(shared);
100 CloseHandle(handle);
101 CloseHandle(sem[reader]);
102 CloseHandle(sem[writer]);
103 }
104
Lock(rw_t rw)105 bool SharedMemory::Lock(rw_t rw)
106 {
107 if (rw == reader)
108 {
109 return WaitForSingleObject(sem[reader], INFINITE) == WAIT_OBJECT_0
110 && WaitForSingleObject(sem[writer], INFINITE) == WAIT_OBJECT_0;
111 }
112 else // if (rw == writer)
113 {
114 return WaitForSingleObject(sem[writer], INFINITE) == WAIT_OBJECT_0;
115 }
116
117 return false;
118 }
119
Unlock(rw_t rw)120 void SharedMemory::Unlock(rw_t rw)
121 {
122 if (rw == reader)
123 {
124 ReleaseSemaphore(sem[writer], 1, nullptr);
125 }
126 else // if (rw == writer)
127 {
128 ReleaseSemaphore(sem[reader], 1, nullptr);
129 ReleaseSemaphore(sem[writer], 1, nullptr);
130 Sleep(0);
131 }
132 }
133
134
135 #else /* ------------------------------------------------------------- */
136
137
SharedMemory()138 SharedMemory::SharedMemory() : handle(0), semid(0), shared(0), ok(false), server(false)
139 {
140 char file[256];
141 key_t key;
142
143 /* Shared memory and semaphore functions expect unique IDs created with ftok().
144 * Unluckily, this is how POSIX works... we need a unique yet reproducable filename,
145 * and the file must exist, too.
146 * We'll use the executable file for this, if we can figure it out via /proc.
147 * Unluckily, again, this is nowhere near standardised or even guaranteed, so
148 * we'll have to create a file in /tmp if everything fails...
149 */
150 if (readlink("/proc/self/exe", file, sizeof(file)) < 0) /* Linux style */
151 {
152 if (readlink("/proc/self/file", file, sizeof(file)) < 0) /* failed, try BSD style */
153 {
154 strcpy(file, "/tmp/fuckyou"); /* failed again, use some bullshit */
155 close(open(file, O_CREAT, O_RDONLY|O_WRONLY));
156 }
157 }
158
159 key = ftok(file, 'a');
160 semid = semget(key, 2, IPC_CREAT | 0666);
161
162 if (semid == -1)
163 {
164 LogManager::Get()->Panic(g_failed_sem);
165 return;
166 }
167
168 key = ftok(file, 'b');
169 handle = shmget(key, ipc_buf_size, 0666 | IPC_CREAT | IPC_EXCL);
170
171 if (handle == -1) /* failed, because... */
172 {
173 if (errno == EEXIST) /* EEXIST ---> server already running */
174 {
175 handle = shmget(key, ipc_buf_size, 0666);
176 if (handle == -1)
177 {
178 LogManager::Get()->Panic(g_failed_shm);
179 return;
180 }
181 ok = true;
182 server = false;
183 }
184 else /* ...any other error ---> bad */
185 {
186 LogManager::Get()->Panic(g_failed_shm);
187 return;
188 }
189 }
190 else
191 {
192 ok = true;
193 server = true;
194
195 unsigned short int v[2] = {0, 1};
196 semctl(semid, 0, SETALL, v);
197 }
198
199 shared = shmat(handle, nullptr, 0);
200 ok = (shared != (void*) -1) ? ok : false;
201 }
202
203
~SharedMemory()204 SharedMemory::~SharedMemory()
205 {
206 shmdt(shared);
207 if (server)
208 {
209 shmctl(handle, IPC_RMID, 0);
210 semctl(semid, 0, IPC_RMID ); /* this will wake up the thread blocking in semop() */
211 }
212 }
213
Lock(rw_t rw)214 bool SharedMemory::Lock(rw_t rw)
215 {
216 if (rw == reader)
217 {
218 sembuf op[2];
219
220 op[0].sem_num = reader;
221 op[0].sem_op = -1;
222 op[0].sem_flg = 0;
223
224 op[1].sem_num = writer;
225 op[1].sem_op = -1;
226 op[1].sem_flg = 0;
227
228 return semop(semid, op, 2) == 0; /* if semaphore is deleted, EIDRM or EINVAL will be returned */
229 }
230
231 if (rw == writer)
232 {
233 sembuf op[1];
234 op[0].sem_num = writer;
235 op[0].sem_op = -1;
236 op[0].sem_flg = 0;
237
238 return semop(semid, op, 1) == 0;
239 }
240
241 return false;
242 }
243
Unlock(rw_t rw)244 void SharedMemory::Unlock(rw_t rw)
245 {
246 if (rw == writer)
247 {
248 sembuf op[2];
249 op[0].sem_num = reader;
250 op[0].sem_op = 1;
251 op[0].sem_flg = 0;
252
253 op[1].sem_num = writer;
254 op[1].sem_op = 1;
255 op[1].sem_flg = 0;
256
257 semop(semid, op, 2);
258 }
259
260 if (rw == reader)
261 {
262 sembuf op[1];
263 op[0].sem_num = writer;
264 op[0].sem_op = 1;
265 op[0].sem_flg = 0;
266
267 semop(semid, op, 1);
268 }
269 }
270
271 #endif /* ------------------------------------------------------------- */
272