30f6764963af9b678dc295b52f84b6732ec91638
[dragonfly.git] / contrib / bsdinstaller-1.1.6 / src / lib / libdfui / conn_caps.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_caps.c
36  * $Id: conn_caps.c,v 1.12 2005/02/06 19:53:19 cpressey Exp $
37  * This code was derived in part from:
38  * $_DragonFly: src/test/caps/client.c,v 1.3 2004/03/31 20:27:34 dillon Exp $
39  * $_DragonFly: src/test/caps/server.c,v 1.4 2004/03/06 22:15:00 dillon Exp $
40  * and is therefore also subject to the license conditions on those files.
41  */
42
43 #include "system.h"
44 #ifdef HAS_CAPS
45
46 #include <sys/types.h>
47 #include <sys/time.h>
48 #include <sys/caps.h>
49 #include <sys/errno.h>
50
51 #include <stdarg.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56
57 #include <aura/mem.h>
58 #include <aura/buffer.h>
59
60 #define NEEDS_DFUI_STRUCTURE_DEFINITIONS
61 #include "dfui.h"
62 #undef  NEEDS_DFUI_STRUCTURE_DEFINITIONS
63 #include "encoding.h"
64 #include "dump.h"
65 #include "conn_caps.h"
66
67 /***** BACKEND ******/
68
69 /** High Level **/
70
71 /*
72  * Connect to the frontend.
73  */
74 dfui_err_t
75 dfui_caps_be_start(struct dfui_connection *c)
76 {
77         T_CAPS(c)->cid = caps_sys_service(c->rendezvous, getuid(), getgid(),
78                                   0, CAPF_ANYCLIENT);
79         return(T_CAPS(c)->cid < 0 ? DFUI_FAILURE : DFUI_SUCCESS);
80 }
81
82 /*
83  * Tell the frontend that we're done and disconnect from it.
84  */
85 dfui_err_t
86 dfui_caps_be_stop(struct dfui_connection *c)
87 {
88         if (dfui_caps_be_ll_exchange(c, DFUI_BE_MSG_STOP, "")) {
89                 caps_sys_close(T_CAPS(c)->cid);
90                 return(DFUI_SUCCESS);
91         } else
92                 return(DFUI_FAILURE);
93 }
94
95 /** Low Level **/
96
97 /*
98  * Exchange a message with the frontend.  This involves two receive()/reply()
99  * cycles: one to provide our message, one to get a reply from the frontend.
100  *
101  * Note that this does not immediately send the message to the frontend -
102  * it can't, because we're a service and it's a client.  What it does is
103  * keep the message handy and wait for a frontend request to come in.  It
104  * then replies to that request with our message.
105  *
106  * The protocol looks something like the following, using the PRESENT and
107  * SUBMIT exchange as an example:
108  *
109  * frontend (client) | backend (service)
110  * ------------------+------------------
111  *
112  *                                     [stage 1]
113  * READY            -->                ll_receive()
114  *                 <--  PRESENT(form)  ll_reply()
115  *
116  *                                     [stage 2]
117  * SUBMIT(form)     -->                ll_receive()
118  *                 <--  READY          ll_reply()
119  *
120  * Each of those exchanges is a pair of calls, on our end, to
121  * dfui_caps_be_ll_receive() and dfui_caps_be_ll_reply().
122  *
123  * The set of messages that the client can pass us is determined by
124  * the conversation state:
125  *
126  *   o  In stage 1, only READY and ABORT are meaningful.
127  *   o  After a PRESENT, the messages SUBMIT and ABORT are meaningul
128  *      in stage 2.
129  *   o  During a PROG_*, the messages CONTINUE, CANCEL, and ABORT
130  *      are meaningful in stage 2.
131  *
132  * If the frontend sends us with READY in stage 2, we assume it has
133  * fallen out of sync, so we send the same initial reply again, going
134  * back to stage 1 as it were.
135  *
136  * After this call, the message is available in c->ebuf.
137  */
138 dfui_err_t
139 dfui_caps_be_ll_exchange(struct dfui_connection *c, char msgtype, const char *msg)
140 {
141         char *fmsg;
142
143         /*
144          * Construct our message to send.
145          */
146
147         fmsg = aura_malloc(strlen(msg) + 2, "exchange message");
148         fmsg[0] = msgtype;
149         strcpy(fmsg + 1, msg);
150
151         /*
152          * Get the frontend's message.
153          */
154
155         dfui_caps_be_ll_receive(c);
156
157         /*
158          * Frontend message should have been either READY or ABORT.
159          * If ABORT, we get out of here pronto.
160          */
161
162         if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
163                 aura_free(fmsg, "exchange message");
164                 return(DFUI_FAILURE);
165         }
166
167         /* XXX if (!READY) ??? */
168
169         do {
170                 dfui_caps_be_ll_reply(c, fmsg);
171
172                 /*
173                  * Here, the frontend has picked up our request and is
174                  * processing it.  We have to wait for the response.
175                  */
176
177                 dfui_caps_be_ll_receive(c);
178         
179                 /*
180                  * Did we get READY from this?
181                  * If so, loop!
182                  */
183
184         } while (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_READY);
185
186         fmsg[0] = DFUI_BE_MSG_READY;
187         fmsg[1] = '\0';
188         dfui_caps_be_ll_reply(c, fmsg);
189
190         aura_free(fmsg, "exchange message");
191         return(DFUI_SUCCESS);   
192 }
193
194 /*
195  * Receive a message from the frontend.
196  * This call is synchronous.
197  * After this call, the message is available in c->ebuf.
198  */
199 dfui_err_t
200 dfui_caps_be_ll_receive(struct dfui_connection *c)
201 {
202         /*
203          * XXX Eventually, the message should be received directly
204          * into c->ebuf.  For now, receive into T_CAPS(c)->buf,
205          * and copy into c->ebuf.
206          */
207         do {
208                 T_CAPS(c)->wresult = caps_sys_wait(T_CAPS(c)->cid, T_CAPS(c)->buf,
209                     T_CAPS(c)->size, &T_CAPS(c)->msgid, NULL);
210                 if (T_CAPS(c)->wresult < 0)
211                         return(DFUI_FAILURE);
212                 /*
213                  * This might have been a CAPMS_DISPOSE message from the kernel.
214                  * If so, just accept it and try, try again.
215                  */
216                 dfui_debug("DISPOSE?<<%s>>\n",
217                     T_CAPS(c)->msgid.c_state == CAPMS_DISPOSE ? "Yes" : "No");
218         } while (T_CAPS(c)->msgid.c_state == CAPMS_DISPOSE);
219
220         aura_buffer_set(c->ebuf, T_CAPS(c)->buf, T_CAPS(c)->wresult);
221         dfui_debug("RECV<<%s>>\n", aura_buffer_buf(c->ebuf));
222         return(DFUI_SUCCESS);
223 }
224
225 /*
226  * Send a NUL-terminated reply to the frontend.
227  */
228 dfui_err_t
229 dfui_caps_be_ll_reply(struct dfui_connection *c, const char *fmsg)
230 {
231         dfui_debug("SEND<<%s>>\n", fmsg);
232         T_CAPS(c)->wresult = caps_sys_reply(T_CAPS(c)->cid,
233             __DECONST(char *, fmsg), strlen(fmsg), T_CAPS(c)->msgid.c_id);
234
235         /*
236          * We may get a CAPMS_DISPOSE message after this, if the client
237          * process is still around.  If so, it'll be handled in the next
238          * call to dfui_caps_be_ll_receive().
239          */
240
241         return(DFUI_SUCCESS);
242 }
243
244 /******** FRONTEND ********/
245
246 /** High Level **/
247
248 dfui_err_t
249 dfui_caps_fe_connect(struct dfui_connection *c)
250 {
251         T_CAPS(c)->cid = caps_sys_client(c->rendezvous, getuid(), getgid(), 0,
252             CAPF_ANYCLIENT | CAPF_WAITSVC);
253         return(T_CAPS(c)->cid > 0 ? DFUI_SUCCESS : DFUI_FAILURE);
254 }
255
256 dfui_err_t
257 dfui_caps_fe_disconnect(struct dfui_connection *c)
258 {
259         caps_sys_close(T_CAPS(c)->cid);
260         return(DFUI_SUCCESS);
261 }
262
263 /** Low Level **/
264
265 /*
266  * Ask for, and subsequently receieve, a message from the backend.
267  * msgtype should be one of the DFUI_FE_MSG_* constants.
268  * This call is synchronous.
269  * After this call, the encoded message is available in c->ebuf.
270  */
271 dfui_err_t
272 dfui_caps_fe_ll_request(struct dfui_connection *c, char msgtype, const char *msg)
273 {
274         char *fmsg;
275         off_t msgcid = 0;
276
277         /*
278          * First, assert that the connection is open.
279          */
280         
281         if (c == NULL || T_CAPS(c)->cid < 0)
282                 return(DFUI_FAILURE);
283
284         /*
285          * Construct a message.
286          */
287
288         fmsg = aura_malloc(strlen(msg) + 2, "exchange message");
289         fmsg[0] = msgtype;
290         strcpy(fmsg + 1, msg);
291         dfui_debug("SEND<<%s>>\n", fmsg);
292
293         /*
294          * Send a NUL-terminated message to the backend.
295          */
296
297         errno = 0;
298         msgcid = caps_sys_put(T_CAPS(c)->cid, fmsg, strlen(fmsg));
299         if (msgcid < 0 && errno == ENOTCONN)
300                 return(DFUI_FAILURE);
301
302         /*
303          * Receive a reply from the backend.
304          * If our message was a READY, this should be a message like PRESENT.
305          * Otherwise it should simply be a READY.
306          */
307
308         dfui_debug("WAITING<<>>\n");
309
310         T_CAPS(c)->wresult = caps_sys_wait(T_CAPS(c)->cid, T_CAPS(c)->buf,
311             T_CAPS(c)->size, &T_CAPS(c)->msgid, NULL);
312         if (T_CAPS(c)->wresult < 0)
313                 return(DFUI_FAILURE);
314
315         aura_buffer_set(c->ebuf, T_CAPS(c)->buf, T_CAPS(c)->wresult);
316         dfui_debug("RECV<<%s>>\n", aura_buffer_buf(c->ebuf));
317
318         aura_free(fmsg, "exchange message");
319
320         return(DFUI_SUCCESS);
321 }
322
323 #endif /* HAS_CAPS */