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
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
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
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
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
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
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
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
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