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 <errno.h>
49 #include <stdarg.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <unistd.h>
54 
55 #include <libaura/buffer.h>
56 #include <libaura/fspred.h>
57 
58 #define	NEEDS_DFUI_STRUCTURE_DEFINITIONS
59 #include "dfui.h"
60 #undef	NEEDS_DFUI_STRUCTURE_DEFINITIONS
61 #include "encoding.h"
62 #include "dump.h"
63 #include "conn_npipe.h"
64 
65 /***** BACKEND ******/
66 
67 /** High Level **/
68 
69 /*
70  * Connect to the frontend.
71  */
72 dfui_err_t
73 dfui_npipe_be_start(struct dfui_connection *c)
74 {
75 	asprintf(&T_NPIPE(c)->out_pipename, "/tmp/dfui.%s.to_fe", c->rendezvous);
76 	asprintf(&T_NPIPE(c)->in_pipename, "/tmp/dfui.%s.from_fe", c->rendezvous);
77 
78 	/*
79 	 * Create the named pipes.
80 	 */
81 	errno = 0;
82 	if (mkfifo(T_NPIPE(c)->in_pipename, 0600) < 0) {
83 		if (errno != EEXIST) {
84 			warn("mkfifo (to_be)");
85 			return(DFUI_FAILURE);
86 		}
87 	}
88 	errno = 0;
89 	if (mkfifo(T_NPIPE(c)->out_pipename, 0600) < 0) {
90 		if (errno != EEXIST) {
91 			warn("mkfifo (to_fe)");
92 			return(DFUI_FAILURE);
93 		}
94 	}
95 	dfui_debug("opening pipes...\n");
96 	if ((T_NPIPE(c)->out = fopen(T_NPIPE(c)->out_pipename, "w")) == NULL) {
97 		return(DFUI_FAILURE);
98 	}
99 	dfui_debug("opened to_fe pipe\n");
100 	setvbuf(T_NPIPE(c)->out, NULL, _IONBF, 0);
101 	if ((T_NPIPE(c)->in = fopen(T_NPIPE(c)->in_pipename, "r")) == NULL) {
102 		fclose(T_NPIPE(c)->out);
103 		return(DFUI_FAILURE);
104 	}
105 	dfui_debug("opened to_be pipe\n");
106 	return(DFUI_SUCCESS);
107 }
108 
109 /*
110  * Tell the frontend that we're done and disconnect from it.
111  */
112 dfui_err_t
113 dfui_npipe_be_stop(struct dfui_connection *c)
114 {
115 	if (dfui_npipe_be_ll_exchange(c, DFUI_BE_MSG_STOP, "")) {
116 		fclose(T_NPIPE(c)->in);
117 		fclose(T_NPIPE(c)->out);
118 		return(DFUI_SUCCESS);
119 	} else
120 		return(DFUI_FAILURE);
121 }
122 
123 /** Low Level **/
124 
125 /*
126  * Exchange a message with the frontend.  This involves two receive()/reply()
127  * cycles: one to provide our message, one to get a reply from the frontend.
128  *
129  * Note that this does not immediately send the message to the frontend -
130  * it can't, because we're a service and it's a client.  What it does is
131  * keep the message handy and wait for a frontend request to come in.  It
132  * then replies to that request with our message.
133  *
134  * The protocol looks something like the following, using the PRESENT and
135  * SUBMIT exchange as an example:
136  *
137  * frontend (client) | backend (service)
138  * ------------------+------------------
139  *
140  *                                     [stage 1]
141  * READY            -->                ll_receive()
142  *                 <--  PRESENT(form)  ll_reply()
143  *
144  *                                     [stage 2]
145  * SUBMIT(form)     -->                ll_receive()
146  *                 <--  READY          ll_reply()
147  *
148  * Each of those exchanges is a pair of calls, on our end, to
149  * dfui_npipe_be_ll_receive() and dfui_npipe_be_ll_reply().
150  *
151  * The set of messages that the client can pass us is determined by
152  * the conversation state:
153  *
154  *   o  In stage 1, only READY and ABORT are meaningful.
155  *   o  After a PRESENT, the messages SUBMIT and ABORT are meaningul
156  *      in stage 2.
157  *   o  During a PROG_*, the messages CONTINUE, CANCEL, and ABORT
158  *      are meaningful in stage 2.
159  *
160  * If the frontend sends us with READY in stage 2, we assume it has
161  * fallen out of sync, so we send the same initial reply again, going
162  * back to stage 1 as it were.
163  *
164  * After this call, the message is available in c->ebuf.
165  */
166 dfui_err_t
167 dfui_npipe_be_ll_exchange(struct dfui_connection *c, char msgtype, const char *msg)
168 {
169 	char *fmsg;
170 
171 	/*
172 	 * Construct our message to send.
173 	 */
174 
175 	fmsg = malloc(strlen(msg) + 2);
176 	fmsg[0] = msgtype;
177 	strcpy(fmsg + 1, msg);
178 
179 	/*
180 	 * Get the frontend's message.
181 	 */
182 
183 	dfui_npipe_be_ll_receive(c);
184 
185 	/*
186 	 * Frontend message should have been either READY or ABORT.
187 	 * If ABORT, we get out of here pronto.
188 	 */
189 
190 	if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
191 		free(fmsg);
192 		return(DFUI_FAILURE);
193 	}
194 
195 	/* XXX if (!READY) ??? */
196 
197 	do {
198 		dfui_npipe_be_ll_reply(c, fmsg);
199 
200 		/*
201 		 * Here, the frontend has picked up our request and is
202 		 * processing it.  We have to wait for the response.
203 		 */
204 
205 		dfui_npipe_be_ll_receive(c);
206 
207 		/*
208 		 * Did we get READY from this?
209 		 * If so, loop!
210 		 */
211 
212 	} while (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_READY);
213 
214 	fmsg[0] = DFUI_BE_MSG_READY;
215 	fmsg[1] = '\0';
216 	dfui_npipe_be_ll_reply(c, fmsg);
217 
218 	free(fmsg);
219 	return(DFUI_SUCCESS);
220 }
221 
222 /*
223  * Receive a message from the frontend.
224  * This call is synchronous.
225  * After this call, the NUL-terminated message is available in
226  * c->ebuf.
227  */
228 dfui_err_t
229 dfui_npipe_be_ll_receive(struct dfui_connection *c)
230 {
231 	int length;
232 	char *buf;
233 
234 	dfui_debug("WAITING<<>>\n");
235 
236 	fread(&length, 4, 1, T_NPIPE(c)->in);
237 
238 	dfui_debug("LENGTH<<%d>>\n", length);
239 
240 	buf = malloc(length + 1);
241 	fread(buf, length, 1, T_NPIPE(c)->in);
242 	aura_buffer_set(c->ebuf, buf, length);
243 	free(buf);
244 
245 	dfui_debug("RECEIVED<<%s>>\n", aura_buffer_buf(c->ebuf));
246 
247 	return(DFUI_SUCCESS);
248 }
249 
250 /*
251  * Send a NUL-terminated reply to the frontend.
252  */
253 dfui_err_t
254 dfui_npipe_be_ll_reply(struct dfui_connection *c, const char *fmsg)
255 {
256 	int length;
257 
258 	dfui_debug("SEND<<%s>>\n", fmsg);
259 
260 	length = strlen(fmsg);
261 
262 	fwrite(&length, 4, 1, T_NPIPE(c)->out);
263 	fwrite(fmsg, length, 1, T_NPIPE(c)->out);
264 
265 	return(DFUI_SUCCESS);
266 }
267 
268 /******** FRONTEND ********/
269 
270 /** High Level **/
271 
272 dfui_err_t
273 dfui_npipe_fe_connect(struct dfui_connection *c)
274 {
275 	asprintf(&T_NPIPE(c)->in_pipename, "/tmp/dfui.%s.to_fe", c->rendezvous);
276 	asprintf(&T_NPIPE(c)->out_pipename, "/tmp/dfui.%s.from_fe", c->rendezvous);
277 
278 	dfui_debug("waiting for named pipes...\n");
279 
280 	/*
281 	 * Wait for named pipes to be created.
282 	 */
283 	if (!is_named_pipe("%s", T_NPIPE(c)->in_pipename)) {
284 		while (!is_named_pipe("%s", T_NPIPE(c)->in_pipename)) {
285 			sleep(1);
286 		}
287 		sleep(1);
288 	}
289 
290 	dfui_debug("opening inflow pipe...\n");
291 
292 	if ((T_NPIPE(c)->in = fopen(T_NPIPE(c)->in_pipename, "r")) == NULL) {
293 		return(DFUI_FAILURE);
294 	}
295 
296 	dfui_debug("opening outflow pipe...\n");
297 
298 	if ((T_NPIPE(c)->out = fopen(T_NPIPE(c)->out_pipename, "w")) == NULL) {
299 		fclose(T_NPIPE(c)->in);
300 		return(DFUI_FAILURE);
301 	}
302 
303 	dfui_debug("making outflow pipe raw...\n");
304 
305 	setvbuf(T_NPIPE(c)->out, NULL, _IONBF, 0);
306 	return(DFUI_SUCCESS);
307 }
308 
309 dfui_err_t
310 dfui_npipe_fe_disconnect(struct dfui_connection *c)
311 {
312 	fclose(T_NPIPE(c)->in);
313 	fclose(T_NPIPE(c)->out);
314 	return(DFUI_SUCCESS);
315 }
316 
317 /** Low Level **/
318 
319 /*
320  * Ask for, and subsequently receieve, a message from the backend.
321  * msgtype should be one of the DFUI_FE_MSG_* constants.
322  * This call is synchronous.
323  * After this call, the null-terminated, encoded message is
324  * available in T_NPIPE(c)->buf.
325  */
326 dfui_err_t
327 dfui_npipe_fe_ll_request(struct dfui_connection *c, char msgtype, const char *msg)
328 {
329 	char *fmsg, *buf;
330 	int length;
331 
332 	/*
333 	 * First, assert that the connection is open.
334 	 */
335 
336 	if (c == NULL || T_NPIPE(c)->in == NULL || T_NPIPE(c)->out == NULL)
337 		return(DFUI_FAILURE);
338 
339 	/*
340 	 * Construct a message.
341 	 */
342 
343 	fmsg = malloc(strlen(msg) + 2);
344 	fmsg[0] = msgtype;
345 	strcpy(fmsg + 1, msg);
346 
347 	dfui_debug("SEND<<%s>>\n", fmsg);
348 
349 	/*
350 	 * Send a NUL-terminated message to the backend.
351 	 */
352 
353 	length = strlen(fmsg);
354 	fwrite(&length, 4, 1, T_NPIPE(c)->out);
355 	fwrite(fmsg, length, 1, T_NPIPE(c)->out);
356 
357 	/*
358 	 * Receive a reply from the backend.
359 	 * If our message was a READY, this should be a message like PRESENT.
360 	 * Otherwise it should simply be a READY.
361 	 */
362 
363 	dfui_debug("WAITING<<>>\n");
364 
365 	fread(&length, 4, 1, T_NPIPE(c)->in);
366 	buf = malloc(length + 1);
367 	fread(buf, length, 1, T_NPIPE(c)->in);
368 	aura_buffer_set(c->ebuf, buf, length);
369 	free(buf);
370 
371 	dfui_debug("RECV<<%s>>\n", aura_buffer_buf(c->ebuf));
372 
373 	free(fmsg);
374 
375 	return(DFUI_SUCCESS);
376 }
377 
378 #endif /* HAS_NPIPE */
379