Coverage Report

Created: 2025-07-12 06:33

/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;
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
    fclose(f);
405
0
    goto done;
406
0
  }
407
408
0
skip:
409
0
  msglen = strlen(cf->path) + 1 + sizeof *msg;
410
0
  if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
411
0
    cf->error = E2BIG;
412
0
    goto done;
413
0
  }
414
0
  msg = xmalloc(msglen);
415
0
  msg->stream = cf->stream;
416
0
  msg->fd = fd;
417
0
  memcpy(msg + 1, cf->path, msglen - sizeof *msg);
418
0
  if (proc_send(cf->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
419
0
    free(msg);
420
0
    cf->error = EINVAL;
421
0
    goto done;
422
0
  }
423
0
  free(msg);
424
0
  return cf;
425
426
0
done:
427
0
  file_fire_done(cf);
428
0
  return NULL;
429
0
}
430
431
/* Cancel a file read. */
432
void
433
file_cancel(struct client_file *cf)
434
0
{
435
0
  struct msg_read_cancel   msg;
436
437
0
  log_debug("read cancel file %d", cf->stream);
438
439
0
  if (cf->closed)
440
0
    return;
441
0
  cf->closed = 1;
442
443
0
  msg.stream = cf->stream;
444
0
  proc_send(cf->peer, MSG_READ_CANCEL, -1, &msg, sizeof msg);
445
0
}
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
0
{
451
0
  struct client_file  *cf = arg;
452
453
0
  if (cf->c == NULL || ~cf->c->flags & CLIENT_DEAD)
454
0
    file_push(cf);
455
0
  file_free(cf);
456
0
}
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
0
{
462
0
  struct msg_write_data *msg;
463
0
  size_t       msglen, sent, left;
464
0
  struct msg_write_close   close;
465
466
0
  msg = xmalloc(sizeof *msg);
467
0
  left = EVBUFFER_LENGTH(cf->buffer);
468
0
  while (left != 0) {
469
0
    sent = left;
470
0
    if (sent > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
471
0
      sent = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
472
473
0
    msglen = (sizeof *msg) + sent;
474
0
    msg = xrealloc(msg, msglen);
475
0
    msg->stream = cf->stream;
476
0
    memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent);
477
0
    if (proc_send(cf->peer, MSG_WRITE, -1, msg, msglen) != 0)
478
0
      break;
479
0
    evbuffer_drain(cf->buffer, sent);
480
481
0
    left = EVBUFFER_LENGTH(cf->buffer);
482
0
    log_debug("file %d sent %zu, left %zu", cf->stream, sent, left);
483
0
  }
484
0
  if (left != 0) {
485
0
    cf->references++;
486
0
    event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL);
487
0
  } else if (cf->stream > 2) {
488
0
    close.stream = cf->stream;
489
0
    proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
490
0
    file_fire_done(cf);
491
0
  }
492
0
  free(msg);
493
0
}
494
495
/* Check if any files have data left to write. */
496
int
497
file_write_left(struct client_files *files)
498
0
{
499
0
  struct client_file  *cf;
500
0
  size_t       left;
501
0
  int      waiting = 0;
502
503
0
  RB_FOREACH(cf, client_files, files) {
504
0
    if (cf->event == NULL)
505
0
      continue;
506
0
    left = EVBUFFER_LENGTH(cf->event->output);
507
0
    if (left != 0) {
508
0
      waiting++;
509
0
      log_debug("file %u %zu bytes left", cf->stream, left);
510
0
    }
511
0
  }
512
0
  return (waiting != 0);
513
0
}
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
0
{
520
0
  struct client_file  *cf = arg;
521
522
0
  log_debug("write error file %d", cf->stream);
523
524
0
  bufferevent_free(cf->event);
525
0
  cf->event = NULL;
526
527
0
  close(cf->fd);
528
0
  cf->fd = -1;
529
530
0
  if (cf->cb != NULL)
531
0
    cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
532
0
}
533
534
/* Client file write callback. */
535
static void
536
file_write_callback(__unused struct bufferevent *bev, void *arg)
537
0
{
538
0
  struct client_file  *cf = arg;
539
540
0
  log_debug("write check file %d", cf->stream);
541
542
0
  if (cf->cb != NULL)
543
0
    cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
544
545
0
  if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0) {
546
0
    bufferevent_free(cf->event);
547
0
    close(cf->fd);
548
0
    RB_REMOVE(client_files, cf->tree, cf);
549
0
    file_free(cf);
550
0
  }
551
0
}
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
0
{
559
0
  struct msg_write_open *msg = imsg->data;
560
0
  size_t       msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
561
0
  const char    *path;
562
0
  struct msg_write_ready   reply;
563
0
  struct client_file   find, *cf;
564
0
  const int    flags = O_NONBLOCK|O_WRONLY|O_CREAT;
565
0
  int      error = 0;
566
567
0
  if (msglen < sizeof *msg)
568
0
    fatalx("bad MSG_WRITE_OPEN size");
569
0
  if (msglen == sizeof *msg)
570
0
    path = "-";
571
0
  else
572
0
    path = (const char *)(msg + 1);
573
0
  log_debug("open write file %d %s", msg->stream, path);
574
575
0
  find.stream = msg->stream;
576
0
  if (RB_FIND(client_files, files, &find) != NULL) {
577
0
    error = EBADF;
578
0
    goto reply;
579
0
  }
580
0
  cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
581
0
  if (cf->closed) {
582
0
    error = EBADF;
583
0
    goto reply;
584
0
  }
585
586
0
  cf->fd = -1;
587
0
  if (msg->fd == -1)
588
0
    cf->fd = open(path, msg->flags|flags, 0644);
589
0
  else if (allow_streams) {
590
0
    if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO)
591
0
      errno = EBADF;
592
0
    else {
593
0
      cf->fd = dup(msg->fd);
594
0
      if (close_received)
595
0
        close(msg->fd); /* can only be used once */
596
0
    }
597
0
  } else
598
0
        errno = EBADF;
599
0
  if (cf->fd == -1) {
600
0
    error = errno;
601
0
    goto reply;
602
0
  }
603
604
0
  cf->event = bufferevent_new(cf->fd, NULL, file_write_callback,
605
0
      file_write_error_callback, cf);
606
0
  if (cf->event == NULL)
607
0
    fatalx("out of memory");
608
0
  bufferevent_enable(cf->event, EV_WRITE);
609
0
  goto reply;
610
611
0
reply:
612
0
  reply.stream = msg->stream;
613
0
  reply.error = error;
614
0
  proc_send(peer, MSG_WRITE_READY, -1, &reply, sizeof reply);
615
0
}
616
617
/* Handle a file write data message (client). */
618
void
619
file_write_data(struct client_files *files, struct imsg *imsg)
620
0
{
621
0
  struct msg_write_data *msg = imsg->data;
622
0
  size_t       msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
623
0
  struct client_file   find, *cf;
624
0
  size_t       size = msglen - sizeof *msg;
625
626
0
  if (msglen < sizeof *msg)
627
0
    fatalx("bad MSG_WRITE size");
628
0
  find.stream = msg->stream;
629
0
  if ((cf = RB_FIND(client_files, files, &find)) == NULL)
630
0
    fatalx("unknown stream number");
631
0
  log_debug("write %zu to file %d", size, cf->stream);
632
633
0
  if (cf->event != NULL)
634
0
    bufferevent_write(cf->event, msg + 1, size);
635
0
}
636
637
/* Handle a file write close message (client). */
638
void
639
file_write_close(struct client_files *files, struct imsg *imsg)
640
0
{
641
0
  struct msg_write_close  *msg = imsg->data;
642
0
  size_t       msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
643
0
  struct client_file   find, *cf;
644
645
0
  if (msglen != sizeof *msg)
646
0
    fatalx("bad MSG_WRITE_CLOSE size");
647
0
  find.stream = msg->stream;
648
0
  if ((cf = RB_FIND(client_files, files, &find)) == NULL)
649
0
    fatalx("unknown stream number");
650
0
  log_debug("close file %d", cf->stream);
651
652
0
  if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
653
0
    if (cf->event != NULL)
654
0
      bufferevent_free(cf->event);
655
0
    if (cf->fd != -1)
656
0
      close(cf->fd);
657
0
    RB_REMOVE(client_files, files, cf);
658
0
    file_free(cf);
659
0
  }
660
0
}
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
0
{
667
0
  struct client_file  *cf = arg;
668
0
  struct msg_read_done   msg;
669
670
0
  log_debug("read error file %d", cf->stream);
671
672
0
  msg.stream = cf->stream;
673
0
  msg.error = 0;
674
0
  proc_send(cf->peer, MSG_READ_DONE, -1, &msg, sizeof msg);
675
676
0
  bufferevent_free(cf->event);
677
0
  close(cf->fd);
678
0
  RB_REMOVE(client_files, cf->tree, cf);
679
0
  file_free(cf);
680
0
}
681
682
/* Client file read callback. */
683
static void
684
file_read_callback(__unused struct bufferevent *bev, void *arg)
685
0
{
686
0
  struct client_file  *cf = arg;
687
0
  void      *bdata;
688
0
  size_t       bsize;
689
0
  struct msg_read_data  *msg;
690
0
  size_t       msglen;
691
692
0
  msg = xmalloc(sizeof *msg);
693
0
  for (;;) {
694
0
    bdata = EVBUFFER_DATA(cf->event->input);
695
0
    bsize = EVBUFFER_LENGTH(cf->event->input);
696
697
0
    if (bsize == 0)
698
0
      break;
699
0
    if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
700
0
      bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
701
0
    log_debug("read %zu from file %d", bsize, cf->stream);
702
703
0
    msglen = (sizeof *msg) + bsize;
704
0
    msg = xrealloc(msg, msglen);
705
0
    msg->stream = cf->stream;
706
0
    memcpy(msg + 1, bdata, bsize);
707
0
    proc_send(cf->peer, MSG_READ, -1, msg, msglen);
708
709
0
    evbuffer_drain(cf->event->input, bsize);
710
0
  }
711
0
  free(msg);
712
0
}
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
0
{
720
0
  struct msg_read_open  *msg = imsg->data;
721
0
  size_t       msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
722
0
  const char    *path;
723
0
  struct msg_read_done   reply;
724
0
  struct client_file   find, *cf;
725
0
  const int    flags = O_NONBLOCK|O_RDONLY;
726
0
  int      error;
727
728
0
  if (msglen < sizeof *msg)
729
0
    fatalx("bad MSG_READ_OPEN size");
730
0
  if (msglen == sizeof *msg)
731
0
    path = "-";
732
0
  else
733
0
    path = (const char *)(msg + 1);
734
0
  log_debug("open read file %d %s", msg->stream, path);
735
736
0
  find.stream = msg->stream;
737
0
  if (RB_FIND(client_files, files, &find) != NULL) {
738
0
    error = EBADF;
739
0
    goto reply;
740
0
  }
741
0
  cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
742
0
  if (cf->closed) {
743
0
    error = EBADF;
744
0
    goto reply;
745
0
  }
746
747
0
  cf->fd = -1;
748
0
  if (msg->fd == -1)
749
0
    cf->fd = open(path, flags);
750
0
  else if (allow_streams) {
751
0
    if (msg->fd != STDIN_FILENO)
752
0
      errno = EBADF;
753
0
    else {
754
0
      cf->fd = dup(msg->fd);
755
0
      if (close_received)
756
0
        close(msg->fd); /* can only be used once */
757
0
    }
758
0
  } else
759
0
    errno = EBADF;
760
0
  if (cf->fd == -1) {
761
0
    error = errno;
762
0
    goto reply;
763
0
  }
764
765
0
  cf->event = bufferevent_new(cf->fd, file_read_callback, NULL,
766
0
      file_read_error_callback, cf);
767
0
  if (cf->event == NULL)
768
0
    fatalx("out of memory");
769
0
  bufferevent_enable(cf->event, EV_READ);
770
0
  return;
771
772
0
reply:
773
0
  reply.stream = msg->stream;
774
0
  reply.error = error;
775
0
  proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply);
776
0
}
777
778
/* Handle a read cancel message (client). */
779
void
780
file_read_cancel(struct client_files *files, struct imsg *imsg)
781
0
{
782
0
  struct msg_read_cancel  *msg = imsg->data;
783
0
  size_t       msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
784
0
  struct client_file   find, *cf;
785
786
0
  if (msglen != sizeof *msg)
787
0
    fatalx("bad MSG_READ_CANCEL size");
788
0
  find.stream = msg->stream;
789
0
  if ((cf = RB_FIND(client_files, files, &find)) == NULL)
790
0
    fatalx("unknown stream number");
791
0
  log_debug("cancel file %d", cf->stream);
792
793
0
  file_read_error_callback(NULL, 0, cf);
794
0
}
795
796
/* Handle a write ready message (server). */
797
void
798
file_write_ready(struct client_files *files, struct imsg *imsg)
799
0
{
800
0
  struct msg_write_ready  *msg = imsg->data;
801
0
  size_t       msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
802
0
  struct client_file   find, *cf;
803
804
0
  if (msglen != sizeof *msg)
805
0
    fatalx("bad MSG_WRITE_READY size");
806
0
  find.stream = msg->stream;
807
0
  if ((cf = RB_FIND(client_files, files, &find)) == NULL)
808
0
    return;
809
0
  if (msg->error != 0) {
810
0
    cf->error = msg->error;
811
0
    file_fire_done(cf);
812
0
  } else
813
0
    file_push(cf);
814
0
}
815
816
/* Handle read data message (server). */
817
void
818
file_read_data(struct client_files *files, struct imsg *imsg)
819
0
{
820
0
  struct msg_read_data  *msg = imsg->data;
821
0
  size_t       msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
822
0
  struct client_file   find, *cf;
823
0
  void      *bdata = msg + 1;
824
0
  size_t       bsize = msglen - sizeof *msg;
825
826
0
  if (msglen < sizeof *msg)
827
0
    fatalx("bad MSG_READ_DATA size");
828
0
  find.stream = msg->stream;
829
0
  if ((cf = RB_FIND(client_files, files, &find)) == NULL)
830
0
    return;
831
832
0
  log_debug("file %d read %zu bytes", cf->stream, bsize);
833
0
  if (cf->error == 0 && !cf->closed) {
834
0
    if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
835
0
      cf->error = ENOMEM;
836
0
      file_fire_done(cf);
837
0
    } else
838
0
      file_fire_read(cf);
839
0
  }
840
0
}
841
842
/* Handle a read done message (server). */
843
void
844
file_read_done(struct client_files *files, struct imsg *imsg)
845
0
{
846
0
  struct msg_read_done  *msg = imsg->data;
847
0
  size_t       msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
848
0
  struct client_file   find, *cf;
849
850
0
  if (msglen != sizeof *msg)
851
0
    fatalx("bad MSG_READ_DONE size");
852
0
  find.stream = msg->stream;
853
0
  if ((cf = RB_FIND(client_files, files, &find)) == NULL)
854
0
    return;
855
856
0
  log_debug("file %d read done", cf->stream);
857
0
  cf->error = msg->error;
858
0
  file_fire_done(cf);
859
0
}