Remove CAPS.
[dragonfly.git] / usr.sbin / installer / libdfui / connection.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  * connection.c
36  * $Id: connection.c,v 1.20 2005/02/07 06:39:59 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  * and is therefore also subject to the license conditions on that file.
40  */
41
42 #include <stdarg.h>
43 #include <stdlib.h>
44 #include <string.h>
45
46 #include <libaura/mem.h>
47 #include <libaura/buffer.h>
48
49 #include "system.h"
50 #define NEEDS_DFUI_STRUCTURE_DEFINITIONS
51 #include "dfui.h"
52 #undef  NEEDS_DFUI_STRUCTURE_DEFINITIONS
53 #include "encoding.h"
54 #include "dump.h"
55
56 #include "conn_npipe.h"
57 #include "conn_tcp.h"
58
59 struct dfui_connection *
60 dfui_connection_new(int transport, const char *rendezvous)
61 {
62         struct dfui_connection *c = NULL;
63
64         if (
65 #ifdef HAS_NPIPE
66             transport == DFUI_TRANSPORT_NPIPE ||
67 #endif
68 #ifdef HAS_TCP
69             transport == DFUI_TRANSPORT_TCP ||
70 #endif
71             0) {
72                 /* We're OK. */
73         } else {
74                 return(NULL);
75         }
76
77         if (dfui_debug_file == NULL) {
78                 dfui_debug_file = stderr;
79         } else {
80                 setvbuf(dfui_debug_file, NULL, _IOLBF, 0);
81         }
82
83         AURA_MALLOC(c, dfui_connection);
84         c->rendezvous = aura_strdup(rendezvous);
85         c->transport = transport;
86         c->ebuf = aura_buffer_new(16384);
87         c->is_connected = 0;
88         c->t_data = NULL;
89
90         switch (transport) {
91 #ifdef HAS_NPIPE
92         case DFUI_TRANSPORT_NPIPE:
93                 AURA_MALLOC(c->t_data, dfui_conn_npipe);
94                 T_NPIPE(c)->in_pipename = NULL;
95                 T_NPIPE(c)->out_pipename = NULL;
96                 T_NPIPE(c)->in = NULL;
97                 T_NPIPE(c)->out = NULL;
98
99                 /*
100                  * Set up dispatch functions.
101                  */
102                 c->be_start = dfui_npipe_be_start;
103                 c->be_stop = dfui_npipe_be_stop;
104                 c->be_ll_exchange = dfui_npipe_be_ll_exchange;
105
106                 c->fe_connect = dfui_npipe_fe_connect;
107                 c->fe_disconnect = dfui_npipe_fe_disconnect;
108                 c->fe_ll_request = dfui_npipe_fe_ll_request;
109                 break;
110 #endif /* HAS_NPIPE */
111
112 #ifdef HAS_TCP
113         case DFUI_TRANSPORT_TCP:
114                 AURA_MALLOC(c->t_data, dfui_conn_tcp);
115                 T_TCP(c)->listen_sd = -1;
116                 T_TCP(c)->connected_sd = -1;
117                 T_TCP(c)->is_connected = 0;
118
119                 /*
120                  * Set up dispatch functions.
121                  */
122                 c->be_start = dfui_tcp_be_start;
123                 c->be_stop = dfui_tcp_be_stop;
124                 c->be_ll_exchange = dfui_tcp_be_ll_exchange;
125
126                 c->fe_connect = dfui_tcp_fe_connect;
127                 c->fe_disconnect = dfui_tcp_fe_disconnect;
128                 c->fe_ll_request = dfui_tcp_fe_ll_request;
129                 break;
130 #endif /* HAS_TCP */
131         }
132
133         return(c);
134 }
135
136 void
137 dfui_connection_free(struct dfui_connection *c)
138 {
139         if (c == NULL)
140                 return;
141
142         switch (c->transport) {
143 #ifdef HAS_NPIPE
144         case DFUI_TRANSPORT_NPIPE:
145                 if (T_NPIPE(c) != NULL) {
146                         if (T_NPIPE(c)->in_pipename != NULL)
147                                 aura_free(T_NPIPE(c)->in_pipename, "pipename");
148                         if (T_NPIPE(c)->out_pipename != NULL)
149                                 aura_free(T_NPIPE(c)->out_pipename, "pipename");
150                         if (T_NPIPE(c)->in != NULL)
151                                 fclose(T_NPIPE(c)->in);
152                         if (T_NPIPE(c)->out != NULL)
153                                 fclose(T_NPIPE(c)->out);
154                         AURA_FREE(T_NPIPE(c), dfui_conn_npipe);
155                 }
156                 break;
157 #endif
158 #ifdef HAS_TCP
159         case DFUI_TRANSPORT_TCP:
160                 if (T_TCP(c) != NULL) {
161                         /* XXX close sockets/files here */
162                         AURA_FREE(T_NPIPE(c), dfui_conn_tcp);
163                 }
164                 break;
165 #endif
166         }
167
168         if (c->rendezvous != NULL)
169                 free(c->rendezvous);
170         AURA_FREE(c, dfui_connection);
171 }
172
173 /*
174  * VERY HIGH LEVEL
175  */
176
177 /*
178  * Create and present a generic `dialog box'-type form for the user
179  * and return their response.  actions is a pipe-seperated list of
180  * actions to be put on the form (e.g. "OK|Cancel".)  The return
181  * value is the ordinal position of the action that was selected,
182  * starting at 1 for the first action.  A return value of 0 indicates
183  * that an error occurred.  A return value of -1 indicates that the
184  * front end aborted the communications.
185  */
186 int
187 dfui_be_present_dialog(struct dfui_connection *c, const char *title,
188                         const char *actions, const char *fmt, ...)
189 {
190         struct dfui_form *f;
191         struct dfui_response *r;
192         va_list args;
193         char *message;
194         char action_id[256], action_name[256];
195         size_t start, end, counter, i;
196
197         va_start(args, fmt);
198         vasprintf(&message, fmt, args);
199         va_end(args);
200
201         f = dfui_form_create("dialog", title, message, "", NULL);
202
203         free(message);
204
205         start = end = 0;
206         while (actions[end] != '\0') {
207                 end = start;
208                 while (actions[end] != '|' && actions[end] != '\0')
209                         end++;
210
211                 if ((end - start) >= 256)
212                         break;
213                 strncpy(action_name, &actions[start], end - start);
214                 action_name[end - start] = '\0';
215                 strcpy(action_id, action_name);
216                 for(i = 0; action_id[i] != '\0'; i++) {
217                         if (action_id[i] == ' ')
218                                 action_id[i] = '_';
219                 }
220                 dfui_form_action_add(f, action_id,
221                     dfui_info_new(action_name, "", ""));
222
223                 start = end + 1;
224         }
225
226         if (!dfui_be_present(c, f, &r)) {
227                 dfui_form_free(f);
228                 dfui_response_free(r);
229                 return(-1);
230         }
231
232         strlcpy(action_name, dfui_response_get_action_id(r), 256);
233         for(i = 0; action_name[i] != '\0'; i++) {
234                 if (action_name[i] == '_')
235                         action_name[i] = ' ';
236         }
237
238         start = end = 0;
239         counter = 1;
240         while (actions[end] != '\0') {
241                 end = start;
242                 while (actions[end] != '|' && actions[end] != '\0')
243                         end++;
244
245                 if ((end - start) >= 256)
246                         break;
247                 if (strlen(action_name) == (end - start) &&
248                     strncmp(action_name, &actions[start], end - start) == 0) {
249                         break;
250                 }
251                 counter++;
252
253                 start = end + 1;
254         }
255
256         dfui_form_free(f);
257         dfui_response_free(r);
258
259         return(counter);
260 }
261
262 /******** BACKEND ********/
263
264 /*
265  * Connect to the frontend.
266  */
267 dfui_err_t
268 dfui_be_start(struct dfui_connection *c)
269 {
270         if (c->is_connected) {
271                 return(DFUI_FAILURE);
272         } else if (c->be_start(c)) {
273                 c->is_connected = 1;
274                 return(DFUI_SUCCESS);
275         } else {
276                 return(DFUI_FAILURE);
277         }
278 }
279
280 /*
281  * Tell the frontend that we're done and disconnect from it.
282  */
283 dfui_err_t
284 dfui_be_stop(struct dfui_connection *c)
285 {
286         if (!c->is_connected) {
287                 return(DFUI_SUCCESS);
288         } else if (c->be_stop(c)) {
289                 c->is_connected = 0;
290                 return(DFUI_SUCCESS);
291         } else {
292                 return(DFUI_FAILURE);
293         }
294 }
295
296 /*
297  * Present a form to the user.  This call is synchronous;
298  * it does not return until the user has selected an action.
299  */
300 dfui_err_t
301 dfui_be_present(struct dfui_connection *c,
302                 struct dfui_form *f, struct dfui_response **r)
303 {
304         struct aura_buffer *e;
305
306         e = aura_buffer_new(16384);
307         dfui_encode_form(e, f);
308
309         c->be_ll_exchange(c, DFUI_BE_MSG_PRESENT, aura_buffer_buf(e));
310
311         aura_buffer_free(e);
312
313         /* check for ABORT reply */
314         if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
315                 return(DFUI_FAILURE);
316         }
317
318         /*
319          * Now we've got the response; so decode it.
320          */
321
322         e = aura_buffer_new(16384);
323         aura_buffer_set(e, aura_buffer_buf(c->ebuf) + 1, aura_buffer_len(c->ebuf) - 1);
324         *r = dfui_decode_response(e);
325         aura_buffer_free(e);
326
327         return(DFUI_SUCCESS);
328 }
329
330 /*
331  * Begin showing a progress bar to the user.
332  * This function is asynchronous; it returns immediately.
333  * The assumption is that the backend will make subsequent
334  * calls to dfui_be_progress_update() frequently, and in
335  * them, check to see if the user cancelled.
336  */
337 dfui_err_t
338 dfui_be_progress_begin(struct dfui_connection *c, struct dfui_progress *pr)
339 {
340         struct aura_buffer *e;
341
342         e = aura_buffer_new(16384);
343         dfui_encode_progress(e, pr);
344
345         c->be_ll_exchange(c, DFUI_BE_MSG_PROG_BEGIN, aura_buffer_buf(e));
346         aura_buffer_free(e);
347
348         /* response might have been be READY or ABORT */
349         if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
350                 return(DFUI_FAILURE);
351         } else {
352                 return(DFUI_SUCCESS);
353         }
354 }
355
356 dfui_err_t
357 dfui_be_progress_update(struct dfui_connection *c,
358                         struct dfui_progress *pr, int *cancelled)
359 {
360         struct aura_buffer *e;
361
362         e = aura_buffer_new(16384);
363         dfui_encode_progress(e, pr);
364
365         c->be_ll_exchange(c, DFUI_BE_MSG_PROG_UPDATE, aura_buffer_buf(e));
366         aura_buffer_free(e);
367
368         /* response might have been READY, CANCEL, or ABORT */
369
370         *cancelled = 0;
371         if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_CANCEL) {
372                 *cancelled = 1;
373         }
374         if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
375                 return(DFUI_FAILURE);
376         } else {
377                 return(DFUI_SUCCESS);
378         }
379 }
380
381 dfui_err_t
382 dfui_be_progress_end(struct dfui_connection *c)
383 {
384         c->be_ll_exchange(c, DFUI_BE_MSG_PROG_END, "");
385
386         /* response might have been be READY or ABORT */
387         if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
388                 return(DFUI_FAILURE);
389         } else {
390                 return(DFUI_SUCCESS);
391         }
392 }
393
394 dfui_err_t
395 dfui_be_set_global_setting(struct dfui_connection *c,
396                            const char *key, const char *value,
397                            int *cancelled)
398 {
399         struct aura_buffer *e;
400         struct dfui_property *p;
401
402         e = aura_buffer_new(16384);
403         p = dfui_property_new(key, value);
404         dfui_encode_property(e, p);
405         c->be_ll_exchange(c, DFUI_BE_MSG_SET_GLOBAL, aura_buffer_buf(e));
406         aura_buffer_free(e);
407         dfui_property_free(p);
408
409         /* response might have been READY, CANCEL, or ABORT */
410
411         *cancelled = 0;
412         if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_CANCEL) {
413                 *cancelled = 1;
414         }
415         if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
416                 return(DFUI_FAILURE);
417         } else {
418                 return(DFUI_SUCCESS);
419         }
420 }
421
422 /******** FRONTEND ********/
423
424 dfui_err_t
425 dfui_fe_connect(struct dfui_connection *c)
426 {
427         return(c->fe_connect(c));
428 }
429
430 dfui_err_t
431 dfui_fe_disconnect(struct dfui_connection *c)
432 {
433         dfui_debug("DISCONNECTING<<>>\n");
434         return(c->fe_disconnect(c));
435 }
436
437 /*
438  * Receive a message from the backend.  This call is synchronous;
439  * it does not return until a message comes in from the backend.
440  * After this call, the message type is available in *msgtype,
441  * and the message itself (if any) is available in *payload, ready
442  * to be casted to its real type (as per *msgtype).
443  */
444 dfui_err_t
445 dfui_fe_receive(struct dfui_connection *c, char *msgtype, void **payload)
446 {
447         struct aura_buffer *e;
448
449         c->fe_ll_request(c, DFUI_FE_MSG_READY, "");
450         *msgtype = aura_buffer_buf(c->ebuf)[0];
451         switch (*msgtype) {
452         case DFUI_BE_MSG_PRESENT:
453                 e = aura_buffer_new(16384);
454                 aura_buffer_set(e, aura_buffer_buf(c->ebuf) + 1, aura_buffer_len(c->ebuf) - 1);
455                 *payload = dfui_decode_form(e);
456                 aura_buffer_free(e);
457                 return(DFUI_SUCCESS);
458
459         case DFUI_BE_MSG_PROG_BEGIN:
460                 e = aura_buffer_new(16384);
461                 aura_buffer_set(e, aura_buffer_buf(c->ebuf) + 1, aura_buffer_len(c->ebuf) - 1);
462                 *payload = dfui_decode_progress(e);
463                 aura_buffer_free(e);
464                 return(DFUI_SUCCESS);
465
466         case DFUI_BE_MSG_PROG_UPDATE:
467                 e = aura_buffer_new(16384);
468                 aura_buffer_set(e, aura_buffer_buf(c->ebuf) + 1, aura_buffer_len(c->ebuf) - 1);
469                 *payload = dfui_decode_progress(e);
470                 aura_buffer_free(e);
471                 return(DFUI_SUCCESS);
472
473         case DFUI_BE_MSG_PROG_END:
474                 *payload = NULL;
475                 return(DFUI_SUCCESS);
476
477         case DFUI_BE_MSG_SET_GLOBAL:
478                 e = aura_buffer_new(16384);
479                 aura_buffer_set(e, aura_buffer_buf(c->ebuf) + 1, aura_buffer_len(c->ebuf) - 1);
480                 *payload = dfui_decode_property(e);
481                 aura_buffer_free(e);
482                 return(DFUI_SUCCESS);
483
484         case DFUI_BE_MSG_STOP:
485                 *payload = NULL;
486                 return(DFUI_SUCCESS);
487
488         default:
489                 /* XXX ??? */
490                 return(DFUI_FAILURE);
491         }
492 }
493
494 /*
495  * Wrapper function for dfui_fe_receive for binding generators which
496  * seem to (understandably) have problems wrapping void *'s themselves.
497  */
498 struct dfui_payload *
499 dfui_fe_receive_payload(struct dfui_connection *c)
500 {
501         char msgtype;
502         void *v;
503         struct dfui_payload *payload;
504
505         if (!dfui_fe_receive(c, &msgtype, &v)) {
506                 return(NULL);
507         }
508
509         AURA_MALLOC(payload, dfui_payload);
510
511         payload->msgtype = msgtype;
512         payload->form = NULL;
513         payload->progress = NULL;
514
515         switch (msgtype) {
516         case DFUI_BE_MSG_PRESENT:
517                 payload->form = v;
518                 break;
519
520         case DFUI_BE_MSG_PROG_BEGIN:
521         case DFUI_BE_MSG_PROG_UPDATE:
522                 payload->progress = v;
523                 break;
524
525         case DFUI_BE_MSG_SET_GLOBAL:
526                 payload->global_setting = v;
527                 break;
528
529         case DFUI_BE_MSG_PROG_END:
530         case DFUI_BE_MSG_STOP:
531                 break;
532         }
533
534         return(payload);
535 }
536
537 char
538 dfui_payload_get_msg_type(const struct dfui_payload *p)
539 {
540         if (p == NULL)
541                 return(' ');
542         return(p->msgtype);
543 }
544
545 struct dfui_form *
546 dfui_payload_get_form(const struct dfui_payload *p)
547 {
548         if (p == NULL)
549                 return(NULL);
550         return(p->form);
551 }
552
553 struct dfui_progress *
554 dfui_payload_get_progress(const struct dfui_payload *p)
555 {
556         if (p == NULL)
557                 return(NULL);
558         return(p->progress);
559 }
560
561 void
562 dfui_payload_free(struct dfui_payload *p)
563 {
564         if (p == NULL)
565                 return;
566         if (p->form != NULL)
567                 dfui_form_free(p->form);
568         if (p->progress != NULL)
569                 dfui_progress_free(p->progress);
570         AURA_FREE(p, dfui_payload);
571 }
572
573 /*
574  * Submit the result of a form to the backend.
575  */
576 dfui_err_t
577 dfui_fe_submit(struct dfui_connection *c, struct dfui_response *r)
578 {
579         struct aura_buffer *e;
580         dfui_err_t request_error;
581
582         e = aura_buffer_new(16384);
583         dfui_encode_response(e, r);
584
585         dfui_debug("ENCODE<<%s>>\n", aura_buffer_buf(e));
586         request_error = c->fe_ll_request(c, DFUI_FE_MSG_SUBMIT,
587             aura_buffer_buf(e));
588         /* XXX we should check for READY from the backend? */
589         aura_buffer_free(e);
590
591         return(request_error);
592 }
593
594 dfui_err_t
595 dfui_fe_progress_continue(struct dfui_connection *c)
596 {
597         c->fe_ll_request(c, DFUI_FE_MSG_CONTINUE, "");
598         return(DFUI_SUCCESS);
599 }
600
601 dfui_err_t
602 dfui_fe_progress_cancel(struct dfui_connection *c)
603 {
604         c->fe_ll_request(c, DFUI_FE_MSG_CANCEL, "");
605         return(DFUI_SUCCESS);
606 }
607
608 dfui_err_t
609 dfui_fe_confirm_set_global(struct dfui_connection *c)
610 {
611         c->fe_ll_request(c, DFUI_FE_MSG_CONTINUE, "");
612         return(DFUI_SUCCESS);
613 }
614
615 dfui_err_t
616 dfui_fe_cancel_set_global(struct dfui_connection *c)
617 {
618         c->fe_ll_request(c, DFUI_FE_MSG_CANCEL, "");
619         return(DFUI_SUCCESS);
620 }
621
622 dfui_err_t
623 dfui_fe_confirm_stop(struct dfui_connection *c)
624 {
625         c->fe_ll_request(c, DFUI_FE_MSG_CONTINUE, "");
626         return(DFUI_SUCCESS);
627 }
628
629 /*
630  * Abort the backend.
631  * Note that you still must call dfui_fe_disconnect after this.
632  */
633 dfui_err_t
634 dfui_fe_abort(struct dfui_connection *c)
635 {
636         c->fe_ll_request(c, DFUI_FE_MSG_ABORT, "");
637         return(DFUI_SUCCESS);
638 }