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