diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-03-02 20:48:01 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2021-03-02 20:48:01 +0200 |
commit | 8d98b4e0d1d83b9a170dd8dccd86451c1c104419 (patch) | |
tree | 7edb6a0c92d5c07999ea222037e782b30e19d8c6 /src | |
parent | f992ba117fe420a7231f005e62627380689d57ab (diff) |
Windows: Implement IPC with mailslots
Diffstat (limited to 'src')
-rw-r--r-- | src/app.c | 3 | ||||
-rw-r--r-- | src/ipc.c | 204 |
2 files changed, 172 insertions, 35 deletions
@@ -466,7 +466,8 @@ static void communicateWithRunningInstance_App_(iApp *d, iProcessId instance, | |||
466 | if (!isEmpty_String(cmds)) { | 466 | if (!isEmpty_String(cmds)) { |
467 | iString *result = communicate_Ipc(cmds); | 467 | iString *result = communicate_Ipc(cmds); |
468 | if (result) { | 468 | if (result) { |
469 | puts(cstr_String(result)); | 469 | fwrite(cstr_String(result), 1, size_String(result), stdout); |
470 | fflush(stdout); | ||
470 | } | 471 | } |
471 | delete_String(result); | 472 | delete_String(result); |
472 | } | 473 | } |
@@ -57,37 +57,31 @@ void init_Ipc(const char *runDir) { | |||
57 | signal(SIGUSR1, SIG_IGN); | 57 | signal(SIGUSR1, SIG_IGN); |
58 | } | 58 | } |
59 | 59 | ||
60 | void deinit_Ipc(void) { | 60 | static void doStopListening_Ipc_(iIpc *d) { |
61 | iIpc *d = &ipc_; | ||
62 | signal(SIGUSR1, SIG_IGN); | ||
63 | if (d->isListening) { | 61 | if (d->isListening) { |
64 | remove(lockFilePath_(d)); | 62 | remove(lockFilePath_(d)); |
63 | d->isListening = iFalse; | ||
65 | } | 64 | } |
66 | deinit_String(&d->dir); | ||
67 | } | 65 | } |
68 | 66 | ||
69 | static void handleUserSignal_(int sig) { | 67 | iProcessId check_Ipc(void) { |
70 | iIpc *d = &ipc_; | 68 | const iIpc *d = &ipc_; |
71 | iAssert(sig == SIGUSR1); | 69 | iProcessId pid = 0; |
72 | iUnused(sig); | 70 | iFile *f = newCStr_File(lockFilePath_(d)); |
73 | const char *path = inputFilePath_(d, 0); | ||
74 | iFile *f = newCStr_File(path); | ||
75 | if (open_File(f, readOnly_FileMode)) { | 71 | if (open_File(f, readOnly_FileMode)) { |
76 | iString *cmds = new_String(); | 72 | const iBlock *running = collect_Block(readAll_File(f)); |
77 | initBlock_String(cmds, collect_Block(readAll_File(f))); | 73 | close_File(f); |
78 | iRangecc line = iNullRange; | 74 | pid = atoi(constData_Block(running)); |
79 | while (nextSplit_Rangecc(range_String(cmds), "\n", &line)) { | 75 | if (!exists_Process(pid)) { |
80 | postCommand_App(cstr_Rangecc(line)); | 76 | pid = 0; |
77 | remove(cstr_String(path_File(f))); /* Stale. */ | ||
81 | } | 78 | } |
82 | delete_String(cmds); | ||
83 | } | 79 | } |
84 | iRelease(f); | 80 | iRelease(f); |
85 | remove(path); | 81 | return pid; |
86 | } | 82 | } |
87 | 83 | ||
88 | void listen_Ipc(void) { | 84 | static void doListen_Ipc_(iIpc *d) { |
89 | iIpc *d = &ipc_; | ||
90 | signal(SIGUSR1, handleUserSignal_); | ||
91 | iFile *f = newCStr_File(lockFilePath_(d)); | 85 | iFile *f = newCStr_File(lockFilePath_(d)); |
92 | if (open_File(f, writeOnly_FileMode)) { | 86 | if (open_File(f, writeOnly_FileMode)) { |
93 | printf_Stream(stream_File(f), "%u", currentId_Process()); | 87 | printf_Stream(stream_File(f), "%u", currentId_Process()); |
@@ -96,24 +90,41 @@ void listen_Ipc(void) { | |||
96 | iRelease(f); | 90 | iRelease(f); |
97 | } | 91 | } |
98 | 92 | ||
99 | iProcessId check_Ipc(void) { | 93 | static void postCommands_Ipc_(const iBlock *cmds) { |
100 | const iIpc *d = &ipc_; | 94 | iRangecc line = iNullRange; |
101 | iProcessId pid = 0; | 95 | while (nextSplit_Rangecc(range_Block(cmds), "\n", &line)) { |
102 | iFile *f = newCStr_File(lockFilePath_(d)); | 96 | postCommand_App(cstr_Rangecc(line)); |
97 | } | ||
98 | } | ||
99 | |||
100 | /*----------------------------------------------------------------------------------------------*/ | ||
101 | #if !defined (iPlatformMsys) | ||
102 | |||
103 | void deinit_Ipc(void) { | ||
104 | iIpc *d = &ipc_; | ||
105 | signal(SIGUSR1, SIG_IGN); | ||
106 | doStopListening_Ipc_(d); | ||
107 | deinit_String(&d->dir); | ||
108 | } | ||
109 | |||
110 | static void handleUserSignal_(int sig) { | ||
111 | iIpc *d = &ipc_; | ||
112 | iAssert(sig == SIGUSR1); | ||
113 | iUnused(sig); | ||
114 | const char *path = inputFilePath_(d, 0); | ||
115 | iFile *f = newCStr_File(path); | ||
103 | if (open_File(f, readOnly_FileMode)) { | 116 | if (open_File(f, readOnly_FileMode)) { |
104 | const iBlock *running = collect_Block(readAll_File(f)); | 117 | postCommands_Ipc_(collect_Block(readAll_File(f))); |
105 | close_File(f); | ||
106 | pid = atoi(constData_Block(running)); | ||
107 | if (!exists_Process(pid)) { | ||
108 | pid = 0; | ||
109 | remove(cstr_String(path_File(f))); /* Stale. */ | ||
110 | } | ||
111 | } | 118 | } |
112 | iRelease(f); | 119 | iRelease(f); |
113 | return pid; | 120 | remove(path); |
114 | } | 121 | } |
115 | 122 | ||
116 | /*----------------------------------------------------------------------------------------------*/ | 123 | void listen_Ipc(void) { |
124 | iIpc *d = &ipc_; | ||
125 | signal(SIGUSR1, handleUserSignal_); | ||
126 | doListen_Ipc_(d); | ||
127 | } | ||
117 | 128 | ||
118 | iDeclareType(IpcResponse) | 129 | iDeclareType(IpcResponse) |
119 | 130 | ||
@@ -206,5 +217,130 @@ iString *communicate_Ipc(const iString *command) { | |||
206 | } | 217 | } |
207 | 218 | ||
208 | void signal_Ipc(iProcessId pid) { | 219 | void signal_Ipc(iProcessId pid) { |
209 | kill(pid, SIGUSR1); | 220 | if (kill(pid, SIGUSR1)) { |
221 | printf("kill failed: %s\n", strerror(errno)); | ||
222 | fflush(stdout); | ||
223 | } | ||
224 | } | ||
225 | |||
226 | #endif | ||
227 | /*----------------------------------------------------------------------------------------------*/ | ||
228 | #if defined (iPlatformMsys) | ||
229 | /* Windows doesn't have user signals, so we'll use one of the simpler native | ||
230 | Win32 IPC APIs: mailslots. */ | ||
231 | |||
232 | #include <the_Foundation/thread.h> | ||
233 | |||
234 | #define WIN32_LEAN_AND_MEAN | ||
235 | #include <windows.h> | ||
236 | |||
237 | static iThread *listenThread_; | ||
238 | static HANDLE listenSlot_; | ||
239 | |||
240 | static iThreadResult readSlotThread_Ipc_(iThread *thd) { | ||
241 | iIpc *d = &ipc_; | ||
242 | DWORD msgSize; | ||
243 | while (d->isListening) { | ||
244 | BOOL ok = GetMailslotInfo(listenSlot_, NULL, &msgSize, NULL, NULL); | ||
245 | if (msgSize == MAILSLOT_NO_MESSAGE) { | ||
246 | sleep_Thread(0.333); | ||
247 | continue; | ||
248 | } | ||
249 | if (!ok) break; | ||
250 | /* Read the message.*/ | ||
251 | DWORD readBytes = 0; | ||
252 | iBlock *msg = new_Block(msgSize); | ||
253 | if (ReadFile(listenSlot_, data_Block(msg), size_Block(msg), &readBytes, NULL)) { | ||
254 | postCommands_Ipc_(msg); | ||
255 | } | ||
256 | delete_Block(msg); | ||
257 | } | ||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | static const char *slotName_(int pid) { | ||
262 | return format_CStr("\\\\.\\mailslot\\fi.skyjake.Lagrange\\%u", pid); | ||
263 | } | ||
264 | |||
265 | void deinit_Ipc(void) { | ||
266 | iIpc *d = &ipc_; | ||
267 | doStopListening_Ipc_(d); | ||
268 | CloseHandle(listenSlot_); | ||
269 | if (listenThread_) { | ||
270 | join_Thread(listenThread_); | ||
271 | iRelease(listenThread_); | ||
272 | } | ||
273 | deinit_String(&d->dir); | ||
274 | } | ||
275 | |||
276 | void listen_Ipc(void) { | ||
277 | iIpc *d = &ipc_; | ||
278 | /* Create a mailslot for listening. */ | ||
279 | listenSlot_ = CreateMailslotA(slotName_(currentId_Process()), 0, 1000, NULL); | ||
280 | listenThread_ = new_Thread(readSlotThread_Ipc_); | ||
281 | doListen_Ipc_(d); | ||
282 | start_Thread(listenThread_); | ||
283 | } | ||
284 | |||
285 | iBool write_Ipc(iProcessId pid, const iString *input, enum iIpcWrite type) { | ||
286 | iUnused(type); | ||
287 | HANDLE slot = CreateFile(slotName_(pid), | ||
288 | GENERIC_WRITE, | ||
289 | FILE_SHARE_READ, | ||
290 | NULL, | ||
291 | OPEN_EXISTING, | ||
292 | FILE_ATTRIBUTE_NORMAL, | ||
293 | NULL); | ||
294 | if (slot == INVALID_HANDLE_VALUE) { | ||
295 | return iFalse; | ||
296 | } | ||
297 | DWORD writeBytes = 0; | ||
298 | iBool ok = | ||
299 | WriteFile( | ||
300 | slot, constData_Block(utf8_String(input)), size_String(input), &writeBytes, NULL) && | ||
301 | writeBytes == size_String(input); | ||
302 | CloseHandle(slot); | ||
303 | return ok; | ||
304 | } | ||
305 | |||
306 | iString *communicate_Ipc(const iString *command) { | ||
307 | iProcessId pid = check_Ipc(); | ||
308 | if (!pid) { | ||
309 | return NULL; | ||
310 | } | ||
311 | /* Open a mailslot for the response. */ | ||
312 | HANDLE responseSlot = CreateMailslotA(slotName_(currentId_Process()), 0, 1000, NULL); | ||
313 | /* Write the commands. */ | ||
314 | if (!write_Ipc(pid, command, command_IpcWrite)) { | ||
315 | CloseHandle(responseSlot); | ||
316 | return NULL; | ||
317 | } | ||
318 | /* Read the response. */ | ||
319 | iString *output = NULL; | ||
320 | DWORD msgSize = 0; | ||
321 | iTime startTime; | ||
322 | for (initCurrent_Time(&startTime); elapsedSeconds_Time(&startTime) < 2; ) { | ||
323 | if (!GetMailslotInfo(responseSlot, NULL, &msgSize, NULL, NULL)) { | ||
324 | break; | ||
325 | } | ||
326 | if (msgSize == MAILSLOT_NO_MESSAGE) { | ||
327 | sleep_Thread(0.1); | ||
328 | continue; | ||
329 | } | ||
330 | iBlock *resp = new_Block(msgSize); | ||
331 | DWORD bytesRead = 0; | ||
332 | ReadFile(responseSlot, data_Block(resp), msgSize, &bytesRead, NULL); | ||
333 | output = newBlock_String(resp); | ||
334 | delete_Block(resp); | ||
335 | break; | ||
336 | } | ||
337 | CloseHandle(responseSlot); | ||
338 | return output; | ||
339 | } | ||
340 | |||
341 | void signal_Ipc(iProcessId pid) { | ||
342 | /* The write to the mailslot will trigger a read. */ | ||
343 | iUnused(pid); | ||
210 | } | 344 | } |
345 | |||
346 | #endif \ No newline at end of file | ||