summaryrefslogtreecommitdiff
path: root/src/ipc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ipc.c')
-rw-r--r--src/ipc.c186
1 files changed, 186 insertions, 0 deletions
diff --git a/src/ipc.c b/src/ipc.c
index 81d4e35b..8fdc3bd5 100644
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -21,4 +21,190 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ 21SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
22 22
23#include "ipc.h" 23#include "ipc.h"
24#include "app.h"
24 25
26#include <the_Foundation/file.h>
27#include <the_Foundation/fileinfo.h>
28#include <the_Foundation/mutex.h>
29#include <the_Foundation/path.h>
30#include <the_Foundation/process.h>
31#include <the_Foundation/time.h>
32
33#include <signal.h>
34
35iDeclareType(Ipc)
36
37struct Impl_Ipc {
38 iString dir;
39 iBool isListening;
40};
41
42static iIpc ipc_;
43
44static const char *lockFilePath_(const iIpc *d) {
45 return concatPath_CStr(cstr_String(&d->dir), ".pid");
46}
47
48static const char *inputFilePath_(const iIpc *d, int pid) {
49 return concatPath_CStr(cstr_String(&d->dir),
50 format_CStr(".run.%d.cfg", pid ? pid : currentId_Process()));
51}
52
53void init_Ipc(const char *runDir) {
54 iIpc *d = &ipc_;
55 initCStr_String(&d->dir, runDir);
56 d->isListening = iFalse;
57 signal(SIGUSR1, SIG_IGN);
58}
59
60void deinit_Ipc(void) {
61 iIpc *d = &ipc_;
62 signal(SIGUSR1, SIG_IGN);
63 if (d->isListening) {
64 remove(lockFilePath_(d));
65 }
66 deinit_String(&d->dir);
67}
68
69static void handleUserSignal_(int sig) {
70 iIpc *d = &ipc_;
71 iAssert(sig == SIGUSR1);
72 iUnused(sig);
73 const char *path = inputFilePath_(d, 0);
74 iFile *f = newCStr_File(path);
75 if (open_File(f, readOnly_FileMode)) {
76 iString *cmds = new_String();
77 initBlock_String(cmds, collect_Block(readAll_File(f)));
78 iRangecc line = iNullRange;
79 while (nextSplit_Rangecc(range_String(cmds), "\n", &line)) {
80 postCommand_App(cstr_Rangecc(line));
81 }
82 delete_String(cmds);
83 }
84 iRelease(f);
85 remove(path);
86}
87
88void listen_Ipc(void) {
89 iIpc *d = &ipc_;
90 signal(SIGUSR1, handleUserSignal_);
91 iFile *f = newCStr_File(lockFilePath_(d));
92 if (open_File(f, writeOnly_FileMode)) {
93 printf_Stream(stream_File(f), "%u", currentId_Process());
94 d->isListening = iTrue;
95 }
96 iRelease(f);
97}
98
99iProcessId check_Ipc(void) {
100 const iIpc *d = &ipc_;
101 iProcessId pid = 0;
102 iFile *f = newCStr_File(lockFilePath_(d));
103 if (open_File(f, readOnly_FileMode)) {
104 const iBlock *running = 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 }
112 iRelease(f);
113 return pid;
114}
115
116/*----------------------------------------------------------------------------------------------*/
117
118iDeclareType(IpcResponse)
119
120struct Impl_IpcResponse {
121 iString *output;
122 iBool success;
123 iMutex mtx;
124 iCondition finished;
125};
126
127static void init_IpcResponse(iIpcResponse *d) {
128 d->output = new_String();
129 d->success = iFalse;
130 init_Mutex(&d->mtx);
131 init_Condition(&d->finished);
132}
133
134static void deinit_IpcResponse(iIpcResponse *d) {
135 deinit_Condition(&d->finished);
136 deinit_Mutex(&d->mtx);
137 delete_String(d->output);
138}
139
140iDefineTypeConstruction(IpcResponse)
141
142static iIpcResponse *response_;
143
144static void handleSignal_IpcResponse_(int sig) {
145 iUnused(sig);
146 iAssert(response_);
147 iIpcResponse *d = response_;
148 lock_Mutex(&d->mtx);
149 iFile *f = newCStr_File(inputFilePath_(&ipc_, 0));
150 if (open_File(f, text_FileMode | readOnly_FileMode)) {
151 iBlock *input = readAll_File(f);
152 close_File(f);
153 remove(cstr_String(path_File(f)));
154 setBlock_String(d->output, input);
155 d->success = iTrue;
156 delete_Block(input);
157 }
158 iRelease(f);
159 signal_Condition(&d->finished);
160 unlock_Mutex(&d->mtx);
161}
162
163iBool write_Ipc(iProcessId pid, const iString *input, enum iIpcWrite type) {
164 iBool ok = iFalse;
165 iFile *f = newCStr_File(inputFilePath_(&ipc_, pid));
166 if (open_File(f, text_FileMode | append_FileMode)) {
167 write_File(f, utf8_String(input));
168 if (type == command_IpcWrite) {
169 printf_Stream(stream_File(f), "\nipc.signal arg:%d\n", currentId_Process());
170 }
171 close_File(f);
172 ok = iTrue;
173 }
174 iRelease(f);
175 return ok;
176}
177
178iString *communicate_Ipc(const iString *command) {
179 const iProcessId dst = check_Ipc();
180 if (dst) {
181 if (write_Ipc(dst, command, command_IpcWrite)) {
182 response_ = new_IpcResponse();
183 signal(SIGUSR1, handleSignal_IpcResponse_);
184 lock_Mutex(&response_->mtx);
185 if (kill(dst, SIGUSR1) == 0) {
186 iTime until;
187 initTimeout_Time(&until, 1.0);
188 waitTimeout_Condition(&response_->finished, &response_->mtx, &until);
189 }
190 unlock_Mutex(&response_->mtx);
191 if (!response_->success) {
192 delete_IpcResponse(response_);
193 response_ = NULL;
194 }
195 }
196 }
197 signal(SIGUSR1, SIG_IGN);
198 if (response_) {
199 iString *result = copy_String(response_->output);
200 trimEnd_String(result);
201 delete_IpcResponse(response_);
202 response_ = NULL;
203 return result;
204 }
205 return NULL;
206}
207
208void signal_Ipc(iProcessId pid) {
209 kill(pid, SIGUSR1);
210}