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 } 684