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
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
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.
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.
46 #include <libaura/mem.h>
47 #include <libaura/buffer.h>
50 #define NEEDS_DFUI_STRUCTURE_DEFINITIONS
52 #undef NEEDS_DFUI_STRUCTURE_DEFINITIONS
56 #include "conn_caps.h"
57 #include "conn_npipe.h"
60 struct dfui_connection *
61 dfui_connection_new(int transport, const char *rendezvous)
63 struct dfui_connection *c = NULL;
67 transport == DFUI_TRANSPORT_CAPS ||
70 transport == DFUI_TRANSPORT_NPIPE ||
73 transport == DFUI_TRANSPORT_TCP ||
81 if (dfui_debug_file == NULL) {
82 dfui_debug_file = stderr;
84 setvbuf(dfui_debug_file, NULL, _IOLBF, 0);
87 AURA_MALLOC(c, dfui_connection);
88 c->rendezvous = aura_strdup(rendezvous);
89 c->transport = transport;
90 c->ebuf = aura_buffer_new(16384);
96 case DFUI_TRANSPORT_CAPS:
97 AURA_MALLOC(c->t_data, dfui_conn_caps);
99 bzero(&T_CAPS(c)->msgid, sizeof(T_CAPS(c)->msgid));
102 * XXX Ideally, this value should grow as needed.
103 * However, CAPS currently has a size limit of
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);
115 * Set up dispatch functions.
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;
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;
125 #endif /* HAS_CAPS */
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;
136 * Set up dispatch functions.
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;
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;
146 #endif /* HAS_NPIPE */
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;
156 * Set up dispatch functions.
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;
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;
173 dfui_connection_free(struct dfui_connection *c)
178 switch (c->transport) {
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);
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);
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);
213 if (c->rendezvous != NULL)
215 AURA_FREE(c, dfui_connection);
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.
232 dfui_be_present_dialog(struct dfui_connection *c, const char *title,
233 const char *actions, const char *fmt, ...)
236 struct dfui_response *r;
239 char action_id[256], action_name[256];
240 size_t start, end, counter, i;
243 vasprintf(&message, fmt, args);
246 f = dfui_form_create("dialog", title, message, "", NULL);
251 while (actions[end] != '\0') {
253 while (actions[end] != '|' && actions[end] != '\0')
256 if ((end - start) >= 256)
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] == ' ')
265 dfui_form_action_add(f, action_id,
266 dfui_info_new(action_name, "", ""));
271 if (!dfui_be_present(c, f, &r)) {
273 dfui_response_free(r);
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] = ' ';
285 while (actions[end] != '\0') {
287 while (actions[end] != '|' && actions[end] != '\0')
290 if ((end - start) >= 256)
292 if (strlen(action_name) == (end - start) &&
293 strncmp(action_name, &actions[start], end - start) == 0) {
302 dfui_response_free(r);
307 /******** BACKEND ********/
310 * Connect to the frontend.
313 dfui_be_start(struct dfui_connection *c)
315 if (c->is_connected) {
316 return(DFUI_FAILURE);
317 } else if (c->be_start(c)) {
319 return(DFUI_SUCCESS);
321 return(DFUI_FAILURE);
326 * Tell the frontend that we're done and disconnect from it.
329 dfui_be_stop(struct dfui_connection *c)
331 if (!c->is_connected) {
332 return(DFUI_SUCCESS);
333 } else if (c->be_stop(c)) {
335 return(DFUI_SUCCESS);
337 return(DFUI_FAILURE);
342 * Present a form to the user. This call is synchronous;
343 * it does not return until the user has selected an action.
346 dfui_be_present(struct dfui_connection *c,
347 struct dfui_form *f, struct dfui_response **r)
349 struct aura_buffer *e;
351 e = aura_buffer_new(16384);
352 dfui_encode_form(e, f);
354 c->be_ll_exchange(c, DFUI_BE_MSG_PRESENT, aura_buffer_buf(e));
358 /* check for ABORT reply */
359 if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
360 return(DFUI_FAILURE);
364 * Now we've got the response; so decode it.
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);
372 return(DFUI_SUCCESS);
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.
383 dfui_be_progress_begin(struct dfui_connection *c, struct dfui_progress *pr)
385 struct aura_buffer *e;
387 e = aura_buffer_new(16384);
388 dfui_encode_progress(e, pr);
390 c->be_ll_exchange(c, DFUI_BE_MSG_PROG_BEGIN, aura_buffer_buf(e));
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);
397 return(DFUI_SUCCESS);
402 dfui_be_progress_update(struct dfui_connection *c,
403 struct dfui_progress *pr, int *cancelled)
405 struct aura_buffer *e;
407 e = aura_buffer_new(16384);
408 dfui_encode_progress(e, pr);
410 c->be_ll_exchange(c, DFUI_BE_MSG_PROG_UPDATE, aura_buffer_buf(e));
413 /* response might have been READY, CANCEL, or ABORT */
416 if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_CANCEL) {
419 if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
420 return(DFUI_FAILURE);
422 return(DFUI_SUCCESS);
427 dfui_be_progress_end(struct dfui_connection *c)
429 c->be_ll_exchange(c, DFUI_BE_MSG_PROG_END, "");
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);
435 return(DFUI_SUCCESS);
440 dfui_be_set_global_setting(struct dfui_connection *c,
441 const char *key, const char *value,
444 struct aura_buffer *e;
445 struct dfui_property *p;
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));
452 dfui_property_free(p);
454 /* response might have been READY, CANCEL, or ABORT */
457 if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_CANCEL) {
460 if (aura_buffer_buf(c->ebuf)[0] == DFUI_FE_MSG_ABORT) {
461 return(DFUI_FAILURE);
463 return(DFUI_SUCCESS);
467 /******** FRONTEND ********/
470 dfui_fe_connect(struct dfui_connection *c)
472 return(c->fe_connect(c));
476 dfui_fe_disconnect(struct dfui_connection *c)
478 dfui_debug("DISCONNECTING<<>>\n");
479 return(c->fe_disconnect(c));
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).
490 dfui_fe_receive(struct dfui_connection *c, char *msgtype, void **payload)
492 struct aura_buffer *e;
494 c->fe_ll_request(c, DFUI_FE_MSG_READY, "");
495 *msgtype = aura_buffer_buf(c->ebuf)[0];
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);
502 return(DFUI_SUCCESS);
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);
509 return(DFUI_SUCCESS);
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);
516 return(DFUI_SUCCESS);
518 case DFUI_BE_MSG_PROG_END:
520 return(DFUI_SUCCESS);
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);
527 return(DFUI_SUCCESS);
529 case DFUI_BE_MSG_STOP:
531 return(DFUI_SUCCESS);
535 return(DFUI_FAILURE);
540 * Wrapper function for dfui_fe_receive for binding generators which
541 * seem to (understandably) have problems wrapping void *'s themselves.
543 struct dfui_payload *
544 dfui_fe_receive_payload(struct dfui_connection *c)
548 struct dfui_payload *payload;
550 if (!dfui_fe_receive(c, &msgtype, &v)) {
554 AURA_MALLOC(payload, dfui_payload);
556 payload->msgtype = msgtype;
557 payload->form = NULL;
558 payload->progress = NULL;
561 case DFUI_BE_MSG_PRESENT:
565 case DFUI_BE_MSG_PROG_BEGIN:
566 case DFUI_BE_MSG_PROG_UPDATE:
567 payload->progress = v;
570 case DFUI_BE_MSG_SET_GLOBAL:
571 payload->global_setting = v;
574 case DFUI_BE_MSG_PROG_END:
575 case DFUI_BE_MSG_STOP:
583 dfui_payload_get_msg_type(const struct dfui_payload *p)
591 dfui_payload_get_form(const struct dfui_payload *p)
598 struct dfui_progress *
599 dfui_payload_get_progress(const struct dfui_payload *p)
607 dfui_payload_free(struct dfui_payload *p)
612 dfui_form_free(p->form);
613 if (p->progress != NULL)
614 dfui_progress_free(p->progress);
615 AURA_FREE(p, dfui_payload);
619 * Submit the result of a form to the backend.
622 dfui_fe_submit(struct dfui_connection *c, struct dfui_response *r)
624 struct aura_buffer *e;
625 dfui_err_t request_error;
627 e = aura_buffer_new(16384);
628 dfui_encode_response(e, r);
630 dfui_debug("ENCODE<<%s>>\n", aura_buffer_buf(e));
631 request_error = c->fe_ll_request(c, DFUI_FE_MSG_SUBMIT,
633 /* XXX we should check for READY from the backend? */
636 return(request_error);
640 dfui_fe_progress_continue(struct dfui_connection *c)
642 c->fe_ll_request(c, DFUI_FE_MSG_CONTINUE, "");
643 return(DFUI_SUCCESS);
647 dfui_fe_progress_cancel(struct dfui_connection *c)
649 c->fe_ll_request(c, DFUI_FE_MSG_CANCEL, "");
650 return(DFUI_SUCCESS);
654 dfui_fe_confirm_set_global(struct dfui_connection *c)
656 c->fe_ll_request(c, DFUI_FE_MSG_CONTINUE, "");
657 return(DFUI_SUCCESS);
661 dfui_fe_cancel_set_global(struct dfui_connection *c)
663 c->fe_ll_request(c, DFUI_FE_MSG_CANCEL, "");
664 return(DFUI_SUCCESS);
668 dfui_fe_confirm_stop(struct dfui_connection *c)
670 c->fe_ll_request(c, DFUI_FE_MSG_CONTINUE, "");
671 return(DFUI_SUCCESS);
676 * Note that you still must call dfui_fe_disconnect after this.
679 dfui_fe_abort(struct dfui_connection *c)
681 c->fe_ll_request(c, DFUI_FE_MSG_ABORT, "");
682 return(DFUI_SUCCESS);