sbin/hammer: Make info command print a proper error message
[dragonfly.git] / usr.sbin / installer / libdfui / connection.c
blob8d00e9359a6a0f0597e05ad1d3c79508fd132bbf
1 /*
2 * Copyright (c)2004 Cat's Eye Technologies. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
8 * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
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.
42 #include <stdarg.h>
43 #include <stdlib.h>
44 #include <string.h>
46 #include <libaura/mem.h>
47 #include <libaura/buffer.h>
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"
56 #include "conn_npipe.h"
57 #include "conn_tcp.h"
59 struct dfui_connection *
60 dfui_connection_new(int transport, const char *rendezvous)
62 struct dfui_connection *c = NULL;
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);
77 if (dfui_debug_file == NULL) {
78 dfui_debug_file = stderr;
79 } else {
80 setvbuf(dfui_debug_file, NULL, _IOLBF, 0);
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;
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;
100 * Set up dispatch functions.
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;
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 */
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;
120 * Set up dispatch functions.
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;
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 */
133 return(c);
136 void
137 dfui_connection_free(struct dfui_connection *c)
139 if (c == NULL)
140 return;
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);
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);
164 break;
165 #endif
168 if (c->rendezvous != NULL)
169 free(c->rendezvous);
170 AURA_FREE(c, dfui_connection);
174 * VERY HIGH LEVEL
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.
187 dfui_be_present_dialog(struct dfui_connection *c, const char *title,
188 const char *actions, const char *fmt, ...)
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;
197 va_start(args, fmt);
198 vasprintf(&message, fmt, args);
199 va_end(args);
201 f = dfui_form_create("dialog", title, message, "", NULL);
203 free(message);
205 start = end = 0;
206 while (actions[end] != '\0') {
207 end = start;
208 while (actions[end] != '|' && actions[end] != '\0')
209 end++;
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] = '_';
220 dfui_form_action_add(f, action_id,
221 dfui_info_new(action_name, "", ""));
223 start = end + 1;
226 if (!dfui_be_present(c, f, &r)) {
227 dfui_form_free(f);
228 dfui_response_free(r);
229 return(-1);
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] = ' ';
238 start = end = 0;
239 counter = 1;
240 while (actions[end] != '\0') {
241 end = start;
242 while (actions[end] != '|' && actions[end] != '\0')
243 end++;
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;
251 counter++;
253 start = end + 1;
256 dfui_form_free(f);
257 dfui_response_free(r);
259 return(counter);
262 /******** BACKEND ********/
265 * Connect to the frontend.
267 dfui_err_t
268 dfui_be_start(struct dfui_connection *c)
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);
281 * Tell the frontend that we're done and disconnect from it.
283 dfui_err_t
284 dfui_be_stop(struct dfui_connection *c)
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);
297 * Present a form to the user. This call is synchronous;
298 * it does not return until the user has selected an action.
300 dfui_err_t
301 dfui_be_present(struct dfui_connection *c,
302 struct dfui_form *f, struct dfui_response **r)
304 struct aura_buffer *e;
306 e = aura_buffer_new(16384);
307 dfui_encode_form(e, f);
309 c->be_ll_exchange(c, DFUI_BE_MSG_PRESENT, aura_buffer_buf(e));
311 aura_buffer_free(e);
313 /* check for ABORT reply */
314 if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
315 return(DFUI_FAILURE);
319 * Now we've got the response; so decode it.
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);
327 return(DFUI_SUCCESS);
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.
337 dfui_err_t
338 dfui_be_progress_begin(struct dfui_connection *c, struct dfui_progress *pr)
340 struct aura_buffer *e;
342 e = aura_buffer_new(16384);
343 dfui_encode_progress(e, pr);
345 c->be_ll_exchange(c, DFUI_BE_MSG_PROG_BEGIN, aura_buffer_buf(e));
346 aura_buffer_free(e);
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);
356 dfui_err_t
357 dfui_be_progress_update(struct dfui_connection *c,
358 struct dfui_progress *pr, int *cancelled)
360 struct aura_buffer *e;
362 e = aura_buffer_new(16384);
363 dfui_encode_progress(e, pr);
365 c->be_ll_exchange(c, DFUI_BE_MSG_PROG_UPDATE, aura_buffer_buf(e));
366 aura_buffer_free(e);
368 /* response might have been READY, CANCEL, or ABORT */
370 *cancelled = 0;
371 if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_CANCEL) {
372 *cancelled = 1;
374 if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
375 return(DFUI_FAILURE);
376 } else {
377 return(DFUI_SUCCESS);
381 dfui_err_t
382 dfui_be_progress_end(struct dfui_connection *c)
384 c->be_ll_exchange(c, DFUI_BE_MSG_PROG_END, "");
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);
394 dfui_err_t
395 dfui_be_set_global_setting(struct dfui_connection *c,
396 const char *key, const char *value,
397 int *cancelled)
399 struct aura_buffer *e;
400 struct dfui_property *p;
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);
409 /* response might have been READY, CANCEL, or ABORT */
411 *cancelled = 0;
412 if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_CANCEL) {
413 *cancelled = 1;
415 if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
416 return(DFUI_FAILURE);
417 } else {
418 return(DFUI_SUCCESS);
422 /******** FRONTEND ********/
424 dfui_err_t
425 dfui_fe_connect(struct dfui_connection *c)
427 return(c->fe_connect(c));
430 dfui_err_t
431 dfui_fe_disconnect(struct dfui_connection *c)
433 dfui_debug("DISCONNECTING<<>>\n");
434 return(c->fe_disconnect(c));
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).
444 dfui_err_t
445 dfui_fe_receive(struct dfui_connection *c, char *msgtype, void **payload)
447 struct aura_buffer *e;
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);
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);
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);
473 case DFUI_BE_MSG_PROG_END:
474 *payload = NULL;
475 return(DFUI_SUCCESS);
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);
484 case DFUI_BE_MSG_STOP:
485 *payload = NULL;
486 return(DFUI_SUCCESS);
488 default:
489 /* XXX ??? */
490 return(DFUI_FAILURE);
495 * Wrapper function for dfui_fe_receive for binding generators which
496 * seem to (understandably) have problems wrapping void *'s themselves.
498 struct dfui_payload *
499 dfui_fe_receive_payload(struct dfui_connection *c)
501 char msgtype;
502 void *v;
503 struct dfui_payload *payload;
505 if (!dfui_fe_receive(c, &msgtype, &v)) {
506 return(NULL);
509 AURA_MALLOC(payload, dfui_payload);
511 payload->msgtype = msgtype;
512 payload->form = NULL;
513 payload->progress = NULL;
515 switch (msgtype) {
516 case DFUI_BE_MSG_PRESENT:
517 payload->form = v;
518 break;
520 case DFUI_BE_MSG_PROG_BEGIN:
521 case DFUI_BE_MSG_PROG_UPDATE:
522 payload->progress = v;
523 break;
525 case DFUI_BE_MSG_SET_GLOBAL:
526 payload->global_setting = v;
527 break;
529 case DFUI_BE_MSG_PROG_END:
530 case DFUI_BE_MSG_STOP:
531 break;
534 return(payload);
537 char
538 dfui_payload_get_msg_type(const struct dfui_payload *p)
540 if (p == NULL)
541 return(' ');
542 return(p->msgtype);
545 struct dfui_form *
546 dfui_payload_get_form(const struct dfui_payload *p)
548 if (p == NULL)
549 return(NULL);
550 return(p->form);
553 struct dfui_progress *
554 dfui_payload_get_progress(const struct dfui_payload *p)
556 if (p == NULL)
557 return(NULL);
558 return(p->progress);
561 void
562 dfui_payload_free(struct dfui_payload *p)
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);
574 * Submit the result of a form to the backend.
576 dfui_err_t
577 dfui_fe_submit(struct dfui_connection *c, struct dfui_response *r)
579 struct aura_buffer *e;
580 dfui_err_t request_error;
582 e = aura_buffer_new(16384);
583 dfui_encode_response(e, r);
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);
591 return(request_error);
594 dfui_err_t
595 dfui_fe_progress_continue(struct dfui_connection *c)
597 c->fe_ll_request(c, DFUI_FE_MSG_CONTINUE, "");
598 return(DFUI_SUCCESS);
601 dfui_err_t
602 dfui_fe_progress_cancel(struct dfui_connection *c)
604 c->fe_ll_request(c, DFUI_FE_MSG_CANCEL, "");
605 return(DFUI_SUCCESS);
608 dfui_err_t
609 dfui_fe_confirm_set_global(struct dfui_connection *c)
611 c->fe_ll_request(c, DFUI_FE_MSG_CONTINUE, "");
612 return(DFUI_SUCCESS);
615 dfui_err_t
616 dfui_fe_cancel_set_global(struct dfui_connection *c)
618 c->fe_ll_request(c, DFUI_FE_MSG_CANCEL, "");
619 return(DFUI_SUCCESS);
622 dfui_err_t
623 dfui_fe_confirm_stop(struct dfui_connection *c)
625 c->fe_ll_request(c, DFUI_FE_MSG_CONTINUE, "");
626 return(DFUI_SUCCESS);
630 * Abort the backend.
631 * Note that you still must call dfui_fe_disconnect after this.
633 dfui_err_t
634 dfui_fe_abort(struct dfui_connection *c)
636 c->fe_ll_request(c, DFUI_FE_MSG_ABORT, "");
637 return(DFUI_SUCCESS);