Coverage Report

Created: 2026-03-01 06:26

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