1 /* $OpenBSD$ */ 2 3 /* 4 * Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 21 #include <errno.h> 22 #include <fcntl.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <unistd.h> 27 28 #include "tmux.h" 29 30 /* 31 * IPC file handling. Both client and server use the same data structures 32 * (client_file and client_files) to store list of active files. Most functions 33 * are for use either in client or server but not both. 34 */ 35 36 static int file_next_stream = 3; 37 38 RB_GENERATE(client_files, client_file, entry, file_cmp); 39 40 /* Get path for file, either as given or from working directory. */ 41 static char * 42 file_get_path(struct client *c, const char *file) 43 { 44 char *path; 45 46 if (*file == '/') 47 path = xstrdup(file); 48 else 49 xasprintf(&path, "%s/%s", server_client_get_cwd(c, NULL), file); 50 return (path); 51 } 52 53 /* Tree comparison function. */ 54 int 55 file_cmp(struct client_file *cf1, struct client_file *cf2) 56 { 57 if (cf1->stream < cf2->stream) 58 return (-1); 59 if (cf1->stream > cf2->stream) 60 return (1); 61 return (0); 62 } 63 64 /* 65 * Create a file object in the client process - the peer is the server to send 66 * messages to. Check callback is fired when the file is finished with so the 67 * process can decide if it needs to exit (if it is waiting for files to 68 * flush). 69 */ 70 struct client_file * 71 file_create_with_peer(struct tmuxpeer *peer, struct client_files *files, 72 int stream, client_file_cb cb, void *cbdata) 73 { 74 struct client_file *cf; 75 76 cf = xcalloc(1, sizeof *cf); 77 cf->c = NULL; 78 cf->references = 1; 79 cf->stream = stream; 80 81 cf->buffer = evbuffer_new(); 82 if (cf->buffer == NULL) 83 fatalx("out of memory"); 84 85 cf->cb = cb; 86 cf->data = cbdata; 87 88 cf->peer = peer; 89 cf->tree = files; 90 RB_INSERT(client_files, files, cf); 91 92 return (cf); 93 } 94 95 /* Create a file object in the server, communicating with the given client. */ 96 struct client_file * 97 file_create_with_client(struct client *c, int stream, client_file_cb cb, 98 void *cbdata) 99 { 100 struct client_file *cf; 101 102 if (c != NULL && (c->flags & CLIENT_ATTACHED)) 103 c = NULL; 104 105 cf = xcalloc(1, sizeof *cf); 106 cf->c = c; 107 cf->references = 1; 108 cf->stream = stream; 109 110 cf->buffer = evbuffer_new(); 111 if (cf->buffer == NULL) 112 fatalx("out of memory"); 113 114 cf->cb = cb; 115 cf->data = cbdata; 116 117 if (cf->c != NULL) { 118 cf->peer = cf->c->peer; 119 cf->tree = &cf->c->files; 120 RB_INSERT(client_files, &cf->c->files, cf); 121 cf->c->references++; 122 } 123 124 return (cf); 125 } 126 127 /* Free a file. */ 128 void 129 file_free(struct client_file *cf) 130 { 131 if (--cf->references != 0) 132 return; 133 134 evbuffer_free(cf->buffer); 135 free(cf->path); 136 137 if (cf->tree != NULL) 138 RB_REMOVE(client_files, cf->tree, cf); 139 if (cf->c != NULL) 140 server_client_unref(cf->c); 141 142 free(cf); 143 } 144 145 /* Event to fire the done callback. */ 146 static void 147 file_fire_done_cb(__unused int fd, __unused short events, void *arg) 148 { 149 struct client_file *cf = arg; 150 struct client *c = cf->c; 151 152 if (cf->cb != NULL && 153 (cf->closed || c == NULL || (~c->flags & CLIENT_DEAD))) 154 cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data); 155 file_free(cf); 156 } 157 158 /* Add an event to fire the done callback (used by the server). */ 159 void 160 file_fire_done(struct client_file *cf) 161 { 162 event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL); 163 } 164 165 /* Fire the read callback. */ 166 void 167 file_fire_read(struct client_file *cf) 168 { 169 if (cf->cb != NULL) 170 cf->cb(cf->c, cf->path, cf->error, 0, cf->buffer, cf->data); 171 } 172 173 /* Can this file be printed to? */ 174 int 175 file_can_print(struct client *c) 176 { 177 if (c == NULL || 178 (c->flags & CLIENT_ATTACHED) || 179 (c->flags & CLIENT_CONTROL)) 180 return (0); 181 return (1); 182 } 183 184 /* Print a message to a file. */ 185 void 186 file_print(struct client *c, const char *fmt, ...) 187 { 188 va_list ap; 189 190 va_start(ap, fmt); 191 file_vprint(c, fmt, ap); 192 va_end(ap); 193 } 194 195 /* Print a message to a file. */ 196 void 197 file_vprint(struct client *c, const char *fmt, va_list ap) 198 { 199 struct client_file find, *cf; 200 struct msg_write_open msg; 201 202 if (!file_can_print(c)) 203 return; 204 205 find.stream = 1; 206 if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) { 207 cf = file_create_with_client(c, 1, NULL, NULL); 208 cf->path = xstrdup("-"); 209 210 evbuffer_add_vprintf(cf->buffer, fmt, ap); 211 212 msg.stream = 1; 213 msg.fd = STDOUT_FILENO; 214 msg.flags = 0; 215 proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg); 216 } else { 217 evbuffer_add_vprintf(cf->buffer, fmt, ap); 218 file_push(cf); 219 } 220 } 221 222 /* Print a buffer to a file. */ 223 void 224 file_print_buffer(struct client *c, void *data, size_t size) 225 { 226 struct client_file find, *cf; 227 struct msg_write_open msg; 228 229 if (!file_can_print(c)) 230 return; 231 232 find.stream = 1; 233 if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) { 234 cf = file_create_with_client(c, 1, NULL, NULL); 235 cf->path = xstrdup("-"); 236 237 evbuffer_add(cf->buffer, data, size); 238 239 msg.stream = 1; 240 msg.fd = STDOUT_FILENO; 241 msg.flags = 0; 242 proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg); 243 } else { 244 evbuffer_add(cf->buffer, data, size); 245 file_push(cf); 246 } 247 } 248 249 /* Report an error to a file. */ 250 void 251 file_error(struct client *c, const char *fmt, ...) 252 { 253 struct client_file find, *cf; 254 struct msg_write_open msg; 255 va_list ap; 256 257 if (!file_can_print(c)) 258 return; 259 260 va_start(ap, fmt); 261 262 find.stream = 2; 263 if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) { 264 cf = file_create_with_client(c, 2, NULL, NULL); 265 cf->path = xstrdup("-"); 266 267 evbuffer_add_vprintf(cf->buffer, fmt, ap); 268 269 msg.stream = 2; 270 msg.fd = STDERR_FILENO; 271 msg.flags = 0; 272 proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg); 273 } else { 274 evbuffer_add_vprintf(cf->buffer, fmt, ap); 275 file_push(cf); 276 } 277 278 va_end(ap); 279 } 280 281 /* Write data to a file. */ 282 void 283 file_write(struct client *c, const char *path, int flags, const void *bdata, 284 size_t bsize, client_file_cb cb, void *cbdata) 285 { 286 struct client_file *cf; 287 struct msg_write_open *msg; 288 size_t msglen; 289 int fd = -1; 290 u_int stream = file_next_stream++; 291 FILE *f; 292 const char *mode; 293 294 if (strcmp(path, "-") == 0) { 295 cf = file_create_with_client(c, stream, cb, cbdata); 296 cf->path = xstrdup("-"); 297 298 fd = STDOUT_FILENO; 299 if (c == NULL || 300 (c->flags & CLIENT_ATTACHED) || 301 (c->flags & CLIENT_CONTROL)) { 302 cf->error = EBADF; 303 goto done; 304 } 305 goto skip; 306 } 307 308 cf = file_create_with_client(c, stream, cb, cbdata); 309 cf->path = file_get_path(c, path); 310 311 if (c == NULL || c->flags & CLIENT_ATTACHED) { 312 if (flags & O_APPEND) 313 mode = "ab"; 314 else 315 mode = "wb"; 316 f = fopen(cf->path, mode); 317 if (f == NULL) { 318 cf->error = errno; 319 goto done; 320 } 321 if (fwrite(bdata, 1, bsize, f) != bsize) { 322 fclose(f); 323 cf->error = EIO; 324 goto done; 325 } 326 fclose(f); 327 goto done; 328 } 329 330 skip: 331 evbuffer_add(cf->buffer, bdata, bsize); 332 333 msglen = strlen(cf->path) + 1 + sizeof *msg; 334 if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) { 335 cf->error = E2BIG; 336 goto done; 337 } 338 msg = xmalloc(msglen); 339 msg->stream = cf->stream; 340 msg->fd = fd; 341 msg->flags = flags; 342 memcpy(msg + 1, cf->path, msglen - sizeof *msg); 343 if (proc_send(cf->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) { 344 free(msg); 345 cf->error = EINVAL; 346 goto done; 347 } 348 free(msg); 349 return; 350 351 done: 352 file_fire_done(cf); 353 } 354 355 /* Read a file. */ 356 struct client_file * 357 file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata) 358 { 359 struct client_file *cf; 360 struct msg_read_open *msg; 361 size_t msglen; 362 int fd = -1; 363 u_int stream = file_next_stream++; 364 FILE *f; 365 size_t size; 366 char buffer[BUFSIZ]; 367 368 if (strcmp(path, "-") == 0) { 369 cf = file_create_with_client(c, stream, cb, cbdata); 370 cf->path = xstrdup("-"); 371 372 fd = STDIN_FILENO; 373 if (c == NULL || 374 (c->flags & CLIENT_ATTACHED) || 375 (c->flags & CLIENT_CONTROL)) { 376 cf->error = EBADF; 377 goto done; 378 } 379 goto skip; 380 } 381 382 cf = file_create_with_client(c, stream, cb, cbdata); 383 cf->path = file_get_path(c, path); 384 385 if (c == NULL || c->flags & CLIENT_ATTACHED) { 386 f = fopen(cf->path, "rb"); 387 if (f == NULL) { 388 cf->error = errno; 389 goto done; 390 } 391 for (;;) { 392 size = fread(buffer, 1, sizeof buffer, f); 393 if (evbuffer_add(cf->buffer, buffer, size) != 0) { 394 cf->error = ENOMEM; 395 goto done; 396 } 397 if (size != sizeof buffer) 398 break; 399 } 400 if (ferror(f)) { 401 cf->error = EIO; 402 goto done; 403 } 404 fclose(f); 405 goto done; 406 } 407 408 skip: 409 msglen = strlen(cf->path) + 1 + sizeof *msg; 410 if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) { 411 cf->error = E2BIG; 412 goto done; 413 } 414 msg = xmalloc(msglen); 415 msg->stream = cf->stream; 416 msg->fd = fd; 417 memcpy(msg + 1, cf->path, msglen - sizeof *msg); 418 if (proc_send(cf->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) { 419 free(msg); 420 cf->error = EINVAL; 421 goto done; 422 } 423 free(msg); 424 return cf; 425 426 done: 427 file_fire_done(cf); 428 return NULL; 429 } 430 431 /* Cancel a file read. */ 432 void 433 file_cancel(struct client_file *cf) 434 { 435 struct msg_read_cancel msg; 436 437 log_debug("read cancel file %d", cf->stream); 438 439 if (cf->closed) 440 return; 441 cf->closed = 1; 442 443 msg.stream = cf->stream; 444 proc_send(cf->peer, MSG_READ_CANCEL, -1, &msg, sizeof msg); 445 } 446 447 /* Push event, fired if there is more writing to be done. */ 448 static void 449 file_push_cb(__unused int fd, __unused short events, void *arg) 450 { 451 struct client_file *cf = arg; 452 453 if (cf->c == NULL || ~cf->c->flags & CLIENT_DEAD) 454 file_push(cf); 455 file_free(cf); 456 } 457 458 /* Push uwritten data to the client for a file, if it will accept it. */ 459 void 460 file_push(struct client_file *cf) 461 { 462 struct msg_write_data *msg; 463 size_t msglen, sent, left; 464 struct msg_write_close close; 465 466 msg = xmalloc(sizeof *msg); 467 left = EVBUFFER_LENGTH(cf->buffer); 468 while (left != 0) { 469 sent = left; 470 if (sent > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg) 471 sent = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg; 472 473 msglen = (sizeof *msg) + sent; 474 msg = xrealloc(msg, msglen); 475 msg->stream = cf->stream; 476 memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent); 477 if (proc_send(cf->peer, MSG_WRITE, -1, msg, msglen) != 0) 478 break; 479 evbuffer_drain(cf->buffer, sent); 480 481 left = EVBUFFER_LENGTH(cf->buffer); 482 log_debug("file %d sent %zu, left %zu", cf->stream, sent, left); 483 } 484 if (left != 0) { 485 cf->references++; 486 event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL); 487 } else if (cf->stream > 2) { 488 close.stream = cf->stream; 489 proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close); 490 file_fire_done(cf); 491 } 492 free(msg); 493 } 494 495 /* Check if any files have data left to write. */ 496 int 497 file_write_left(struct client_files *files) 498 { 499 struct client_file *cf; 500 size_t left; 501 int waiting = 0; 502 503 RB_FOREACH(cf, client_files, files) { 504 if (cf->event == NULL) 505 continue; 506 left = EVBUFFER_LENGTH(cf->event->output); 507 if (left != 0) { 508 waiting++; 509 log_debug("file %u %zu bytes left", cf->stream, left); 510 } 511 } 512 return (waiting != 0); 513 } 514 515 /* Client file write error callback. */ 516 static void 517 file_write_error_callback(__unused struct bufferevent *bev, __unused short what, 518 void *arg) 519 { 520 struct client_file *cf = arg; 521 522 log_debug("write error file %d", cf->stream); 523 524 bufferevent_free(cf->event); 525 cf->event = NULL; 526 527 close(cf->fd); 528 cf->fd = -1; 529 530 if (cf->cb != NULL) 531 cf->cb(NULL, NULL, 0, -1, NULL, cf->data); 532 } 533 534 /* Client file write callback. */ 535 static void 536 file_write_callback(__unused struct bufferevent *bev, void *arg) 537 { 538 struct client_file *cf = arg; 539 540 log_debug("write check file %d", cf->stream); 541 542 if (cf->cb != NULL) 543 cf->cb(NULL, NULL, 0, -1, NULL, cf->data); 544 545 if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) { 546 bufferevent_free(cf->event); 547 close(cf->fd); 548 RB_REMOVE(client_files, cf->tree, cf); 549 file_free(cf); 550 } 551 } 552 553 /* Handle a file write open message (client). */ 554 void 555 file_write_open(struct client_files *files, struct tmuxpeer *peer, 556 struct imsg *imsg, int allow_streams, int close_received, 557 client_file_cb cb, void *cbdata) 558 { 559 struct msg_write_open *msg = imsg->data; 560 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; 561 const char *path; 562 struct msg_write_ready reply; 563 struct client_file find, *cf; 564 const int flags = O_NONBLOCK|O_WRONLY|O_CREAT; 565 int error = 0; 566 567 if (msglen < sizeof *msg) 568 fatalx("bad MSG_WRITE_OPEN size"); 569 if (msglen == sizeof *msg) 570 path = "-"; 571 else 572 path = (const char *)(msg + 1); 573 log_debug("open write file %d %s", msg->stream, path); 574 575 find.stream = msg->stream; 576 if (RB_FIND(client_files, files, &find) != NULL) { 577 error = EBADF; 578 goto reply; 579 } 580 cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata); 581 if (cf->closed) { 582 error = EBADF; 583 goto reply; 584 } 585 586 cf->fd = -1; 587 if (msg->fd == -1) 588 cf->fd = open(path, msg->flags|flags, 0644); 589 else if (allow_streams) { 590 if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO) 591 errno = EBADF; 592 else { 593 cf->fd = dup(msg->fd); 594 if (close_received) 595 close(msg->fd); /* can only be used once */ 596 } 597 } else 598 errno = EBADF; 599 if (cf->fd == -1) { 600 error = errno; 601 goto reply; 602 } 603 604 cf->event = bufferevent_new(cf->fd, NULL, file_write_callback, 605 file_write_error_callback, cf); 606 if (cf->event == NULL) 607 fatalx("out of memory"); 608 bufferevent_enable(cf->event, EV_WRITE); 609 goto reply; 610 611 reply: 612 reply.stream = msg->stream; 613 reply.error = error; 614 proc_send(peer, MSG_WRITE_READY, -1, &reply, sizeof reply); 615 } 616 617 /* Handle a file write data message (client). */ 618 void 619 file_write_data(struct client_files *files, struct imsg *imsg) 620 { 621 struct msg_write_data *msg = imsg->data; 622 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; 623 struct client_file find, *cf; 624 size_t size = msglen - sizeof *msg; 625 626 if (msglen < sizeof *msg) 627 fatalx("bad MSG_WRITE size"); 628 find.stream = msg->stream; 629 if ((cf = RB_FIND(client_files, files, &find)) == NULL) 630 fatalx("unknown stream number"); 631 log_debug("write %zu to file %d", size, cf->stream); 632 633 if (cf->event != NULL) 634 bufferevent_write(cf->event, msg + 1, size); 635 } 636 637 /* Handle a file write close message (client). */ 638 void 639 file_write_close(struct client_files *files, struct imsg *imsg) 640 { 641 struct msg_write_close *msg = imsg->data; 642 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; 643 struct client_file find, *cf; 644 645 if (msglen != sizeof *msg) 646 fatalx("bad MSG_WRITE_CLOSE size"); 647 find.stream = msg->stream; 648 if ((cf = RB_FIND(client_files, files, &find)) == NULL) 649 fatalx("unknown stream number"); 650 log_debug("close file %d", cf->stream); 651 652 if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) { 653 if (cf->event != NULL) 654 bufferevent_free(cf->event); 655 if (cf->fd != -1) 656 close(cf->fd); 657 RB_REMOVE(client_files, files, cf); 658 file_free(cf); 659 } 660 } 661 662 /* Client file read error callback. */ 663 static void 664 file_read_error_callback(__unused struct bufferevent *bev, __unused short what, 665 void *arg) 666 { 667 struct client_file *cf = arg; 668 struct msg_read_done msg; 669 670 log_debug("read error file %d", cf->stream); 671 672 msg.stream = cf->stream; 673 msg.error = 0; 674 proc_send(cf->peer, MSG_READ_DONE, -1, &msg, sizeof msg); 675 676 bufferevent_free(cf->event); 677 close(cf->fd); 678 RB_REMOVE(client_files, cf->tree, cf); 679 file_free(cf); 680 } 681 682 /* Client file read callback. */ 683 static void 684 file_read_callback(__unused struct bufferevent *bev, void *arg) 685 { 686 struct client_file *cf = arg; 687 void *bdata; 688 size_t bsize; 689 struct msg_read_data *msg; 690 size_t msglen; 691 692 msg = xmalloc(sizeof *msg); 693 for (;;) { 694 bdata = EVBUFFER_DATA(cf->event->input); 695 bsize = EVBUFFER_LENGTH(cf->event->input); 696 697 if (bsize == 0) 698 break; 699 if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg) 700 bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg; 701 log_debug("read %zu from file %d", bsize, cf->stream); 702 703 msglen = (sizeof *msg) + bsize; 704 msg = xrealloc(msg, msglen); 705 msg->stream = cf->stream; 706 memcpy(msg + 1, bdata, bsize); 707 proc_send(cf->peer, MSG_READ, -1, msg, msglen); 708 709 evbuffer_drain(cf->event->input, bsize); 710 } 711 free(msg); 712 } 713 714 /* Handle a file read open message (client). */ 715 void 716 file_read_open(struct client_files *files, struct tmuxpeer *peer, 717 struct imsg *imsg, int allow_streams, int close_received, client_file_cb cb, 718 void *cbdata) 719 { 720 struct msg_read_open *msg = imsg->data; 721 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; 722 const char *path; 723 struct msg_read_done reply; 724 struct client_file find, *cf; 725 const int flags = O_NONBLOCK|O_RDONLY; 726 int error; 727 728 if (msglen < sizeof *msg) 729 fatalx("bad MSG_READ_OPEN size"); 730 if (msglen == sizeof *msg) 731 path = "-"; 732 else 733 path = (const char *)(msg + 1); 734 log_debug("open read file %d %s", msg->stream, path); 735 736 find.stream = msg->stream; 737 if (RB_FIND(client_files, files, &find) != NULL) { 738 error = EBADF; 739 goto reply; 740 } 741 cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata); 742 if (cf->closed) { 743 error = EBADF; 744 goto reply; 745 } 746 747 cf->fd = -1; 748 if (msg->fd == -1) 749 cf->fd = open(path, flags); 750 else if (allow_streams) { 751 if (msg->fd != STDIN_FILENO) 752 errno = EBADF; 753 else { 754 cf->fd = dup(msg->fd); 755 if (close_received) 756 close(msg->fd); /* can only be used once */ 757 } 758 } else 759 errno = EBADF; 760 if (cf->fd == -1) { 761 error = errno; 762 goto reply; 763 } 764 765 cf->event = bufferevent_new(cf->fd, file_read_callback, NULL, 766 file_read_error_callback, cf); 767 if (cf->event == NULL) 768 fatalx("out of memory"); 769 bufferevent_enable(cf->event, EV_READ); 770 return; 771 772 reply: 773 reply.stream = msg->stream; 774 reply.error = error; 775 proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply); 776 } 777 778 /* Handle a read cancel message (client). */ 779 void 780 file_read_cancel(struct client_files *files, struct imsg *imsg) 781 { 782 struct msg_read_cancel *msg = imsg->data; 783 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; 784 struct client_file find, *cf; 785 786 if (msglen != sizeof *msg) 787 fatalx("bad MSG_READ_CANCEL size"); 788 find.stream = msg->stream; 789 if ((cf = RB_FIND(client_files, files, &find)) == NULL) 790 fatalx("unknown stream number"); 791 log_debug("cancel file %d", cf->stream); 792 793 file_read_error_callback(NULL, 0, cf); 794 } 795 796 /* Handle a write ready message (server). */ 797 void 798 file_write_ready(struct client_files *files, struct imsg *imsg) 799 { 800 struct msg_write_ready *msg = imsg->data; 801 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; 802 struct client_file find, *cf; 803 804 if (msglen != sizeof *msg) 805 fatalx("bad MSG_WRITE_READY size"); 806 find.stream = msg->stream; 807 if ((cf = RB_FIND(client_files, files, &find)) == NULL) 808 return; 809 if (msg->error != 0) { 810 cf->error = msg->error; 811 file_fire_done(cf); 812 } else 813 file_push(cf); 814 } 815 816 /* Handle read data message (server). */ 817 void 818 file_read_data(struct client_files *files, struct imsg *imsg) 819 { 820 struct msg_read_data *msg = imsg->data; 821 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; 822 struct client_file find, *cf; 823 void *bdata = msg + 1; 824 size_t bsize = msglen - sizeof *msg; 825 826 if (msglen < sizeof *msg) 827 fatalx("bad MSG_READ_DATA size"); 828 find.stream = msg->stream; 829 if ((cf = RB_FIND(client_files, files, &find)) == NULL) 830 return; 831 832 log_debug("file %d read %zu bytes", cf->stream, bsize); 833 if (cf->error == 0 && !cf->closed) { 834 if (evbuffer_add(cf->buffer, bdata, bsize) != 0) { 835 cf->error = ENOMEM; 836 file_fire_done(cf); 837 } else 838 file_fire_read(cf); 839 } 840 } 841 842 /* Handle a read done message (server). */ 843 void 844 file_read_done(struct client_files *files, struct imsg *imsg) 845 { 846 struct msg_read_done *msg = imsg->data; 847 size_t msglen = imsg->hdr.len - IMSG_HEADER_SIZE; 848 struct client_file find, *cf; 849 850 if (msglen != sizeof *msg) 851 fatalx("bad MSG_READ_DONE size"); 852 find.stream = msg->stream; 853 if ((cf = RB_FIND(client_files, files, &find)) == NULL) 854 return; 855 856 log_debug("file %d read done", cf->stream); 857 cf->error = msg->error; 858 file_fire_done(cf); 859 } 860