In userland, fix printf(-like) calls without literal format and no args.
[dragonfly.git] / usr.sbin / installer / libdfui / conn_npipe.c
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 */