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 * 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 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 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 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 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 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 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 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 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 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 425 dfui_fe_connect(struct dfui_connection *c) 426 { 427 return(c->fe_connect(c)); 428 } 429 430 dfui_err_t 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 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 * 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 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 * 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 * 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 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 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 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 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 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 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 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 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