1 /*
2 * Copyright (c)2004 Cat's Eye Technologies. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in
13 * the documentation and/or other materials provided with the
14 * distribution.
15 *
16 * Neither the name of Cat's Eye Technologies nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31 * OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 /*
35 * conn_npipe.c
36 * $Id: conn_npipe.c,v 1.13 2005/02/06 19:53:19 cpressey Exp $
37 */
38
39 #include "system.h"
40 #ifdef HAS_NPIPE
41
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <sys/time.h>
45 #include <sys/errno.h>
46
47 #include <err.h>
48 #include <stdarg.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53
54 #include <libaura/buffer.h>
55 #include <libaura/fspred.h>
56
57 #define NEEDS_DFUI_STRUCTURE_DEFINITIONS
58 #include "dfui.h"
59 #undef NEEDS_DFUI_STRUCTURE_DEFINITIONS
60 #include "encoding.h"
61 #include "dump.h"
62 #include "conn_npipe.h"
63
64 /***** BACKEND ******/
65
66 /** High Level **/
67
68 /*
69 * Connect to the frontend.
70 */
71 dfui_err_t
dfui_npipe_be_start(struct dfui_connection * c)72 dfui_npipe_be_start(struct dfui_connection *c)
73 {
74 asprintf(&T_NPIPE(c)->out_pipename, "/tmp/dfui.%s.to_fe", c->rendezvous);
75 asprintf(&T_NPIPE(c)->in_pipename, "/tmp/dfui.%s.from_fe", c->rendezvous);
76
77 /*
78 * Create the named pipes.
79 */
80 errno = 0;
81 if (mkfifo(T_NPIPE(c)->in_pipename, 0600) < 0) {
82 if (errno != EEXIST) {
83 warn("mkfifo (to_be)");
84 return(DFUI_FAILURE);
85 }
86 }
87 errno = 0;
88 if (mkfifo(T_NPIPE(c)->out_pipename, 0600) < 0) {
89 if (errno != EEXIST) {
90 warn("mkfifo (to_fe)");
91 return(DFUI_FAILURE);
92 }
93 }
94 dfui_debug("opening pipes...\n");
95 if ((T_NPIPE(c)->out = fopen(T_NPIPE(c)->out_pipename, "w")) == NULL) {
96 return(DFUI_FAILURE);
97 }
98 dfui_debug("opened to_fe pipe\n");
99 setvbuf(T_NPIPE(c)->out, NULL, _IONBF, 0);
100 if ((T_NPIPE(c)->in = fopen(T_NPIPE(c)->in_pipename, "r")) == NULL) {
101 fclose(T_NPIPE(c)->out);
102 return(DFUI_FAILURE);
103 }
104 dfui_debug("opened to_be pipe\n");
105 return(DFUI_SUCCESS);
106 }
107
108 /*
109 * Tell the frontend that we're done and disconnect from it.
110 */
111 dfui_err_t
dfui_npipe_be_stop(struct dfui_connection * c)112 dfui_npipe_be_stop(struct dfui_connection *c)
113 {
114 if (dfui_npipe_be_ll_exchange(c, DFUI_BE_MSG_STOP, "")) {
115 fclose(T_NPIPE(c)->in);
116 fclose(T_NPIPE(c)->out);
117 return(DFUI_SUCCESS);
118 } else
119 return(DFUI_FAILURE);
120 }
121
122 /** Low Level **/
123
124 /*
125 * Exchange a message with the frontend. This involves two receive()/reply()
126 * cycles: one to provide our message, one to get a reply from the frontend.
127 *
128 * Note that this does not immediately send the message to the frontend -
129 * it can't, because we're a service and it's a client. What it does is
130 * keep the message handy and wait for a frontend request to come in. It
131 * then replies to that request with our message.
132 *
133 * The protocol looks something like the following, using the PRESENT and
134 * SUBMIT exchange as an example:
135 *
136 * frontend (client) | backend (service)
137 * ------------------+------------------
138 *
139 * [stage 1]
140 * READY --> ll_receive()
141 * <-- PRESENT(form) ll_reply()
142 *
143 * [stage 2]
144 * SUBMIT(form) --> ll_receive()
145 * <-- READY ll_reply()
146 *
147 * Each of those exchanges is a pair of calls, on our end, to
148 * dfui_npipe_be_ll_receive() and dfui_npipe_be_ll_reply().
149 *
150 * The set of messages that the client can pass us is determined by
151 * the conversation state:
152 *
153 * o In stage 1, only READY and ABORT are meaningful.
154 * o After a PRESENT, the messages SUBMIT and ABORT are meaningul
155 * in stage 2.
156 * o During a PROG_*, the messages CONTINUE, CANCEL, and ABORT
157 * are meaningful in stage 2.
158 *
159 * If the frontend sends us with READY in stage 2, we assume it has
160 * fallen out of sync, so we send the same initial reply again, going
161 * back to stage 1 as it were.
162 *
163 * After this call, the message is available in c->ebuf.
164 */
165 dfui_err_t
dfui_npipe_be_ll_exchange(struct dfui_connection * c,char msgtype,const char * msg)166 dfui_npipe_be_ll_exchange(struct dfui_connection *c, char msgtype, const char *msg)
167 {
168 char *fmsg;
169
170 /*
171 * Construct our message to send.
172 */
173
174 fmsg = malloc(strlen(msg) + 2);
175 fmsg[0] = msgtype;
176 strcpy(fmsg + 1, msg);
177
178 /*
179 * Get the frontend's message.
180 */
181
182 dfui_npipe_be_ll_receive(c);
183
184 /*
185 * Frontend message should have been either READY or ABORT.
186 * If ABORT, we get out of here pronto.
187 */
188
189 if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
190 free(fmsg);
191 return(DFUI_FAILURE);
192 }
193
194 /* XXX if (!READY) ??? */
195
196 do {
197 dfui_npipe_be_ll_reply(c, fmsg);
198
199 /*
200 * Here, the frontend has picked up our request and is
201 * processing it. We have to wait for the response.
202 */
203
204 dfui_npipe_be_ll_receive(c);
205
206 /*
207 * Did we get READY from this?
208 * If so, loop!
209 */
210
211 } while (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_READY);
212
213 fmsg[0] = DFUI_BE_MSG_READY;
214 fmsg[1] = '\0';
215 dfui_npipe_be_ll_reply(c, fmsg);
216
217 free(fmsg);
218 return(DFUI_SUCCESS);
219 }
220
221 /*
222 * Receive a message from the frontend.
223 * This call is synchronous.
224 * After this call, the NUL-terminated message is available in
225 * c->ebuf.
226 */
227 dfui_err_t
dfui_npipe_be_ll_receive(struct dfui_connection * c)228 dfui_npipe_be_ll_receive(struct dfui_connection *c)
229 {
230 int length;
231 char *buf;
232
233 dfui_debug("WAITING<<>>\n");
234
235 fread(&length, 4, 1, T_NPIPE(c)->in);
236
237 dfui_debug("LENGTH<<%d>>\n", length);
238
239 buf = malloc(length + 1);
240 fread(buf, length, 1, T_NPIPE(c)->in);
241 aura_buffer_set(c->ebuf, buf, length);
242 free(buf);
243
244 dfui_debug("RECEIVED<<%s>>\n", aura_buffer_buf(c->ebuf));
245
246 return(DFUI_SUCCESS);
247 }
248
249 /*
250 * Send a NUL-terminated reply to the frontend.
251 */
252 dfui_err_t
dfui_npipe_be_ll_reply(struct dfui_connection * c,const char * fmsg)253 dfui_npipe_be_ll_reply(struct dfui_connection *c, const char *fmsg)
254 {
255 int length;
256
257 dfui_debug("SEND<<%s>>\n", fmsg);
258
259 length = strlen(fmsg);
260
261 fwrite(&length, 4, 1, T_NPIPE(c)->out);
262 fwrite(fmsg, length, 1, T_NPIPE(c)->out);
263
264 return(DFUI_SUCCESS);
265 }
266
267 /******** FRONTEND ********/
268
269 /** High Level **/
270
271 dfui_err_t
dfui_npipe_fe_connect(struct dfui_connection * c)272 dfui_npipe_fe_connect(struct dfui_connection *c)
273 {
274 asprintf(&T_NPIPE(c)->in_pipename, "/tmp/dfui.%s.to_fe", c->rendezvous);
275 asprintf(&T_NPIPE(c)->out_pipename, "/tmp/dfui.%s.from_fe", c->rendezvous);
276
277 dfui_debug("waiting for named pipes...\n");
278
279 /*
280 * Wait for named pipes to be created.
281 */
282 if (!is_named_pipe("%s", T_NPIPE(c)->in_pipename)) {
283 while (!is_named_pipe("%s", T_NPIPE(c)->in_pipename)) {
284 sleep(1);
285 }
286 sleep(1);
287 }
288
289 dfui_debug("opening inflow pipe...\n");
290
291 if ((T_NPIPE(c)->in = fopen(T_NPIPE(c)->in_pipename, "r")) == NULL) {
292 return(DFUI_FAILURE);
293 }
294
295 dfui_debug("opening outflow pipe...\n");
296
297 if ((T_NPIPE(c)->out = fopen(T_NPIPE(c)->out_pipename, "w")) == NULL) {
298 fclose(T_NPIPE(c)->in);
299 return(DFUI_FAILURE);
300 }
301
302 dfui_debug("making outflow pipe raw...\n");
303
304 setvbuf(T_NPIPE(c)->out, NULL, _IONBF, 0);
305 return(DFUI_SUCCESS);
306 }
307
308 dfui_err_t
dfui_npipe_fe_disconnect(struct dfui_connection * c)309 dfui_npipe_fe_disconnect(struct dfui_connection *c)
310 {
311 fclose(T_NPIPE(c)->in);
312 fclose(T_NPIPE(c)->out);
313 return(DFUI_SUCCESS);
314 }
315
316 /** Low Level **/
317
318 /*
319 * Ask for, and subsequently receieve, a message from the backend.
320 * msgtype should be one of the DFUI_FE_MSG_* constants.
321 * This call is synchronous.
322 * After this call, the null-terminated, encoded message is
323 * available in T_NPIPE(c)->buf.
324 */
325 dfui_err_t
dfui_npipe_fe_ll_request(struct dfui_connection * c,char msgtype,const char * msg)326 dfui_npipe_fe_ll_request(struct dfui_connection *c, char msgtype, const char *msg)
327 {
328 char *fmsg, *buf;
329 int length;
330
331 /*
332 * First, assert that the connection is open.
333 */
334
335 if (c == NULL || T_NPIPE(c)->in == NULL || T_NPIPE(c)->out == NULL)
336 return(DFUI_FAILURE);
337
338 /*
339 * Construct a message.
340 */
341
342 fmsg = malloc(strlen(msg) + 2);
343 fmsg[0] = msgtype;
344 strcpy(fmsg + 1, msg);
345
346 dfui_debug("SEND<<%s>>\n", fmsg);
347
348 /*
349 * Send a NUL-terminated message to the backend.
350 */
351
352 length = strlen(fmsg);
353 fwrite(&length, 4, 1, T_NPIPE(c)->out);
354 fwrite(fmsg, length, 1, T_NPIPE(c)->out);
355
356 /*
357 * Receive a reply from the backend.
358 * If our message was a READY, this should be a message like PRESENT.
359 * Otherwise it should simply be a READY.
360 */
361
362 dfui_debug("WAITING<<>>\n");
363
364 fread(&length, 4, 1, T_NPIPE(c)->in);
365 buf = malloc(length + 1);
366 fread(buf, length, 1, T_NPIPE(c)->in);
367 aura_buffer_set(c->ebuf, buf, length);
368 free(buf);
369
370 dfui_debug("RECV<<%s>>\n", aura_buffer_buf(c->ebuf));
371
372 free(fmsg);
373
374 return(DFUI_SUCCESS);
375 }
376
377 #endif /* HAS_NPIPE */
378