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