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