Coverage Report

Created: 2025-08-29 06:28

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