xref: /dragonfly/usr.sbin/installer/libdfui/connection.c (revision 94b98a915ba84699b2a2adab9146fbc2cf617459)
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 *
dfui_connection_new(int transport,const char * rendezvous)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
dfui_connection_free(struct dfui_connection * c)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
dfui_be_present_dialog(struct dfui_connection * c,const char * title,const char * actions,const char * fmt,...)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
dfui_be_start(struct dfui_connection * c)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
dfui_be_stop(struct dfui_connection * c)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
dfui_be_present(struct dfui_connection * c,struct dfui_form * f,struct dfui_response ** r)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
dfui_be_progress_begin(struct dfui_connection * c,struct dfui_progress * pr)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
dfui_be_progress_update(struct dfui_connection * c,struct dfui_progress * pr,int * cancelled)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
dfui_be_progress_end(struct dfui_connection * c)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
dfui_be_set_global_setting(struct dfui_connection * c,const char * key,const char * value,int * cancelled)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
dfui_fe_connect(struct dfui_connection * c)425 dfui_fe_connect(struct dfui_connection *c)
426 {
427           return(c->fe_connect(c));
428 }
429 
430 dfui_err_t
dfui_fe_disconnect(struct dfui_connection * c)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
dfui_fe_receive(struct dfui_connection * c,char * msgtype,void ** payload)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 *
dfui_fe_receive_payload(struct dfui_connection * c)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
dfui_payload_get_msg_type(const struct dfui_payload * p)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 *
dfui_payload_get_form(const struct dfui_payload * p)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 *
dfui_payload_get_progress(const struct dfui_payload * p)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
dfui_payload_free(struct dfui_payload * p)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
dfui_fe_submit(struct dfui_connection * c,struct dfui_response * r)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
dfui_fe_progress_continue(struct dfui_connection * c)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
dfui_fe_progress_cancel(struct dfui_connection * c)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
dfui_fe_confirm_set_global(struct dfui_connection * c)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
dfui_fe_cancel_set_global(struct dfui_connection * c)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
dfui_fe_confirm_stop(struct dfui_connection * c)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
dfui_fe_abort(struct dfui_connection * c)634 dfui_fe_abort(struct dfui_connection *c)
635 {
636           c->fe_ll_request(c, DFUI_FE_MSG_ABORT, "");
637           return(DFUI_SUCCESS);
638 }
639