Coverage Report

Created: 2026-09-13 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/file.c
Line
Count
Source
1
/* $OpenBSD: file.c,v 1.22 2026/08/17 07:56:56 nicm Exp $ */
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_DEAD) ||
189
0
      (c->flags & CLIENT_CONTROL))
190
0
    return (0);
191
0
  return (1);
192
0
}
193
194
/* Print a message to a file. */
195
void
196
file_print(struct client *c, const char *fmt, ...)
197
0
{
198
0
  va_list ap;
199
200
0
  va_start(ap, fmt);
201
0
  file_vprint(c, fmt, ap);
202
0
  va_end(ap);
203
0
}
204
205
/* Print a message to a file. */
206
void
207
file_vprint(struct client *c, const char *fmt, va_list ap)
208
0
{
209
0
  struct client_file   find, *cf;
210
0
  struct msg_write_open  msg;
211
212
0
  if (!file_can_print(c))
213
0
    return;
214
215
0
  find.stream = 1;
216
0
  if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
217
0
    cf = file_create_with_client(c, 1, NULL, NULL);
218
0
    cf->path = xstrdup("-");
219
220
0
    evbuffer_add_vprintf(cf->buffer, fmt, ap);
221
222
0
    msg.stream = 1;
223
0
    msg.fd = STDOUT_FILENO;
224
0
    msg.flags = 0;
225
0
    proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
226
0
  } else {
227
0
    evbuffer_add_vprintf(cf->buffer, fmt, ap);
228
0
    file_push(cf);
229
0
  }
230
0
}
231
232
/* Print a buffer to a file. */
233
void
234
file_print_buffer(struct client *c, void *data, size_t size)
235
0
{
236
0
  struct client_file   find, *cf;
237
0
  struct msg_write_open  msg;
238
239
0
  if (!file_can_print(c))
240
0
    return;
241
242
0
  find.stream = 1;
243
0
  if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
244
0
    cf = file_create_with_client(c, 1, NULL, NULL);
245
0
    cf->path = xstrdup("-");
246
247
0
    evbuffer_add(cf->buffer, data, size);
248
249
0
    msg.stream = 1;
250
0
    msg.fd = STDOUT_FILENO;
251
0
    msg.flags = 0;
252
0
    proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
253
0
  } else {
254
0
    evbuffer_add(cf->buffer, data, size);
255
0
    file_push(cf);
256
0
  }
257
0
}
258
259
/* Report an error to a file. */
260
void
261
file_error(struct client *c, const char *fmt, ...)
262
0
{
263
0
  struct client_file   find, *cf;
264
0
  struct msg_write_open  msg;
265
0
  va_list      ap;
266
267
0
  if (!file_can_print(c))
268
0
    return;
269
270
0
  va_start(ap, fmt);
271
272
0
  find.stream = 2;
273
0
  if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
274
0
    cf = file_create_with_client(c, 2, NULL, NULL);
275
0
    cf->path = xstrdup("-");
276
277
0
    evbuffer_add_vprintf(cf->buffer, fmt, ap);
278
279
0
    msg.stream = 2;
280
0
    msg.fd = STDERR_FILENO;
281
0
    msg.flags = 0;
282
0
    proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
283
0
  } else {
284
0
    evbuffer_add_vprintf(cf->buffer, fmt, ap);
285
0
    file_push(cf);
286
0
  }
287
288
0
  va_end(ap);
289
0
}
290
291
/* Write data to a file. */
292
void
293
file_write(struct client *c, const char *path, int flags, const void *bdata,
294
    size_t bsize, client_file_cb cb, void *cbdata)
295
0
{
296
0
  struct client_file  *cf;
297
0
  struct msg_write_open *msg;
298
0
  size_t       msglen;
299
0
  int      fd = -1;
300
0
  u_int      stream = file_next_stream++;
301
0
  FILE      *f;
302
0
  const char    *mode;
303
304
0
  if (strcmp(path, "-") == 0) {
305
0
    cf = file_create_with_client(c, stream, cb, cbdata);
306
0
    cf->path = xstrdup("-");
307
308
0
    fd = STDOUT_FILENO;
309
0
    if (c == NULL ||
310
0
        (c->flags & CLIENT_ATTACHED) ||
311
0
        (c->flags & CLIENT_CONTROL)) {
312
0
      cf->error = EBADF;
313
0
      goto done;
314
0
    }
315
0
    goto skip;
316
0
  }
317
318
0
  cf = file_create_with_client(c, stream, cb, cbdata);
319
0
  cf->path = file_get_path(c, path);
320
321
0
  if (c == NULL || c->flags & CLIENT_ATTACHED) {
322
0
    if (flags & O_APPEND)
323
0
      mode = "ab";
324
0
    else
325
0
      mode = "wb";
326
0
    f = fopen(cf->path, mode);
327
0
    if (f == NULL) {
328
0
      cf->error = errno;
329
0
      goto done;
330
0
    }
331
0
    if (fwrite(bdata, 1, bsize, f) != bsize) {
332
0
      fclose(f);
333
0
      cf->error = EIO;
334
0
      goto done;
335
0
    }
336
0
    fclose(f);
337
0
    goto done;
338
0
  }
339
340
0
skip:
341
0
  evbuffer_add(cf->buffer, bdata, bsize);
342
343
0
  msglen = strlen(cf->path) + 1 + sizeof *msg;
344
0
  if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
345
0
    cf->error = E2BIG;
346
0
    goto done;
347
0
  }
348
0
  msg = xmalloc(msglen);
349
0
  msg->stream = cf->stream;
350
0
  msg->fd = fd;
351
0
  msg->flags = flags;
352
0
  memcpy(msg + 1, cf->path, msglen - sizeof *msg);
353
0
  if (proc_send(cf->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) {
354
0
    free(msg);
355
0
    cf->error = EINVAL;
356
0
    goto done;
357
0
  }
358
0
  free(msg);
359
0
  return;
360
361
0
done:
362
0
  file_fire_done(cf);
363
0
}
364
365
/* Read a file. */
366
struct client_file *
367
file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
368
0
{
369
0
  struct client_file  *cf;
370
0
  struct msg_read_open  *msg;
371
0
  size_t       msglen;
372
0
  int      fd = -1;
373
0
  u_int      stream = file_next_stream++;
374
0
  FILE      *f = NULL;
375
0
  size_t       size;
376
0
  char       buffer[BUFSIZ];
377
378
0
  if (strcmp(path, "-") == 0) {
379
0
    cf = file_create_with_client(c, stream, cb, cbdata);
380
0
    cf->path = xstrdup("-");
381
382
0
    fd = STDIN_FILENO;
383
0
    if (c == NULL ||
384
0
        (c->flags & CLIENT_ATTACHED) ||
385
0
        (c->flags & CLIENT_CONTROL)) {
386
0
      cf->error = EBADF;
387
0
      goto done;
388
0
    }
389
0
    goto skip;
390
0
  }
391
392
0
  cf = file_create_with_client(c, stream, cb, cbdata);
393
0
  cf->path = file_get_path(c, path);
394
395
0
  if (c == NULL || c->flags & CLIENT_ATTACHED) {
396
0
    f = fopen(cf->path, "rb");
397
0
    if (f == NULL) {
398
0
      cf->error = errno;
399
0
      goto done;
400
0
    }
401
0
    for (;;) {
402
0
      size = fread(buffer, 1, sizeof buffer, f);
403
0
      if (ferror(f)) {
404
0
        cf->error = errno;
405
0
        goto done;
406
0
      }
407
0
      if (evbuffer_add(cf->buffer, buffer, size) != 0) {
408
0
        cf->error = ENOMEM;
409
0
        goto done;
410
0
      }
411
0
      if (size != sizeof buffer)
412
0
        break;
413
0
    }
414
0
    if (ferror(f)) {
415
0
      cf->error = EIO;
416
0
      goto done;
417
0
    }
418
0
    goto done;
419
0
  }
420
421
0
skip:
422
0
  msglen = strlen(cf->path) + 1 + sizeof *msg;
423
0
  if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
424
0
    cf->error = E2BIG;
425
0
    goto done;
426
0
  }
427
0
  msg = xmalloc(msglen);
428
0
  msg->stream = cf->stream;
429
0
  msg->fd = fd;
430
0
  memcpy(msg + 1, cf->path, msglen - sizeof *msg);
431
0
  if (proc_send(cf->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
432
0
    free(msg);
433
0
    cf->error = EINVAL;
434
0
    goto done;
435
0
  }
436
0
  free(msg);
437
0
  return cf;
438
439
0
done:
440
0
  if (f != NULL)
441
0
    fclose(f);
442
0
  file_fire_done(cf);
443
0
  return NULL;
444
0
}
445
446
/* Cancel a file read. */
447
void
448
file_cancel(struct client_file *cf)
449
0
{
450
0
  struct msg_read_cancel   msg;
451
452
0
  log_debug("read cancel file %d", cf->stream);
453
454
0
  if (cf->closed)
455
0
    return;
456
0
  cf->closed = 1;
457
458
0
  msg.stream = cf->stream;
459
0
  proc_send(cf->peer, MSG_READ_CANCEL, -1, &msg, sizeof msg);
460
0
}
461
462
/* Push event, fired if there is more writing to be done. */
463
static void
464
file_push_cb(__unused int fd, __unused short events, void *arg)
465
0
{
466
0
  struct client_file  *cf = arg;
467
468
0
  if (cf->c == NULL || ~cf->c->flags & CLIENT_DEAD)
469
0
    file_push(cf);
470
0
  file_free(cf);
471
0
}
472
473
/* Push uwritten data to the client for a file, if it will accept it. */
474
void
475
file_push(struct client_file *cf)
476
0
{
477
0
  struct msg_write_data *msg;
478
0
  size_t       msglen, sent, left;
479
0
  struct msg_write_close   close;
480
481
0
  msg = xmalloc(sizeof *msg);
482
0
  left = EVBUFFER_LENGTH(cf->buffer);
483
0
  while (left != 0) {
484
0
    sent = left;
485
0
    if (sent > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
486
0
      sent = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
487
488
0
    msglen = (sizeof *msg) + sent;
489
0
    msg = xrealloc(msg, msglen);
490
0
    msg->stream = cf->stream;
491
0
    memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent);
492
0
    if (proc_send(cf->peer, MSG_WRITE, -1, msg, msglen) != 0)
493
0
      break;
494
0
    evbuffer_drain(cf->buffer, sent);
495
496
0
    left = EVBUFFER_LENGTH(cf->buffer);
497
0
    log_debug("file %d sent %zu, left %zu", cf->stream, sent, left);
498
0
  }
499
0
  if (left != 0) {
500
0
    cf->references++;
501
0
    event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL);
502
0
  } else if (cf->stream > 2) {
503
0
    close.stream = cf->stream;
504
0
    proc_send(cf->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
505
0
    if (cf->c == NULL || (~cf->c->flags & CLIENT_WRITE_ACK))
506
0
      file_fire_done(cf);
507
0
  }
508
0
  free(msg);
509
0
}
510
511
/* Check if any files have data left to write. */
512
int
513
file_write_left(struct client_files *files)
514
0
{
515
0
  struct client_file  *cf;
516
0
  size_t       left;
517
0
  int      waiting = 0;
518
519
0
  RB_FOREACH(cf, client_files, files) {
520
0
    if (cf->event == NULL)
521
0
      continue;
522
0
    left = EVBUFFER_LENGTH(cf->event->output);
523
0
    if (left != 0) {
524
0
      waiting++;
525
0
      log_debug("file %u %zu bytes left", cf->stream, left);
526
0
    }
527
0
  }
528
0
  return (waiting != 0);
529
0
}
530
531
/* Finish writing a client file. */
532
static void
533
file_write_finished(struct client_file *cf)
534
0
{
535
0
  struct msg_write_done  msg;
536
537
0
  if (cf->event != NULL) {
538
0
    bufferevent_free(cf->event);
539
0
    cf->event = NULL;
540
0
  }
541
0
  if (cf->fd != -1) {
542
0
    if (close(cf->fd) != 0 && cf->error == 0)
543
0
      cf->error = errno;
544
0
    cf->fd = -1;
545
0
  }
546
547
0
  msg.stream = cf->stream;
548
0
  msg.error = cf->error;
549
0
  proc_send(cf->peer, MSG_WRITE_DONE, -1, &msg, sizeof msg);
550
551
0
  if (cf->cb != NULL)
552
0
    cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
553
0
  file_free(cf);
554
0
}
555
556
/* Client file write error callback. */
557
static void
558
file_write_error_callback(__unused struct bufferevent *bev, short what,
559
    void *arg)
560
0
{
561
0
  struct client_file  *cf = arg;
562
0
  int      error;
563
564
0
  if (what & EVBUFFER_ERROR)
565
0
    error = errno;
566
0
  else
567
0
    error = EIO;
568
0
  if (error == 0)
569
0
    error = EIO;
570
571
0
  log_debug("write error file %d", cf->stream);
572
0
  cf->error = error;
573
574
0
  bufferevent_free(cf->event);
575
0
  cf->event = NULL;
576
577
0
  close(cf->fd);
578
0
  cf->fd = -1;
579
580
0
  if (cf->closed)
581
0
    file_write_finished(cf);
582
0
  else if (cf->cb != NULL)
583
0
    cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
584
0
}
585
586
/* Client file write callback. */
587
static void
588
file_write_callback(__unused struct bufferevent *bev, void *arg)
589
0
{
590
0
  struct client_file  *cf = arg;
591
592
0
  log_debug("write check file %d", cf->stream);
593
594
0
  if (cf->closed && EVBUFFER_LENGTH(cf->event->output) == 0)
595
0
    file_write_finished(cf);
596
0
  else if (cf->cb != NULL)
597
0
    cf->cb(NULL, NULL, 0, -1, NULL, cf->data);
598
0
}
599
600
/* Handle a file write open message (client). */
601
void
602
file_write_open(struct client_files *files, struct tmuxpeer *peer,
603
    struct imsg *imsg, int allow_streams, int close_received,
604
    client_file_cb cb, void *cbdata)
605
0
{
606
0
  struct msg_write_open *msg = imsg->data;
607
0
  size_t       msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
608
0
  const char    *path;
609
0
  struct msg_write_ready   reply;
610
0
  struct client_file   find, *cf;
611
0
  const int    flags = O_NONBLOCK|O_WRONLY|O_CREAT;
612
0
  int      error = 0;
613
614
0
  if (msglen < sizeof *msg)
615
0
    fatalx("bad MSG_WRITE_OPEN size");
616
0
  if (msglen == sizeof *msg)
617
0
    path = "-";
618
0
  else
619
0
    path = (const char *)(msg + 1);
620
0
  log_debug("open write file %d %s", msg->stream, path);
621
622
0
  find.stream = msg->stream;
623
0
  if (RB_FIND(client_files, files, &find) != NULL) {
624
0
    error = EBADF;
625
0
    goto reply;
626
0
  }
627
0
  cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
628
0
  if (cf->closed) {
629
0
    error = EBADF;
630
0
    goto reply;
631
0
  }
632
633
0
  cf->fd = -1;
634
0
  if (msg->fd == -1)
635
0
    cf->fd = open(path, msg->flags|flags, 0644);
636
0
  else if (allow_streams) {
637
0
    if (msg->fd != STDOUT_FILENO && msg->fd != STDERR_FILENO)
638
0
      errno = EBADF;
639
0
    else {
640
0
      cf->fd = dup(msg->fd);
641
0
      if (close_received)
642
0
        close(msg->fd); /* can only be used once */
643
0
    }
644
0
  } else
645
0
        errno = EBADF;
646
0
  if (cf->fd == -1) {
647
0
    error = errno;
648
0
    goto reply;
649
0
  }
650
651
0
  cf->event = bufferevent_new(cf->fd, NULL, file_write_callback,
652
0
      file_write_error_callback, cf);
653
0
  if (cf->event == NULL)
654
0
    fatalx("out of memory");
655
0
  bufferevent_enable(cf->event, EV_WRITE);
656
0
  goto reply;
657
658
0
reply:
659
0
  reply.stream = msg->stream;
660
0
  reply.error = error;
661
0
  proc_send(peer, MSG_WRITE_READY, -1, &reply, sizeof reply);
662
0
}
663
664
/* Handle a file write data message (client). */
665
void
666
file_write_data(struct client_files *files, struct imsg *imsg)
667
0
{
668
0
  struct msg_write_data *msg = imsg->data;
669
0
  size_t       msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
670
0
  struct client_file   find, *cf;
671
0
  size_t       size = msglen - sizeof *msg;
672
673
0
  if (msglen < sizeof *msg)
674
0
    fatalx("bad MSG_WRITE size");
675
0
  find.stream = msg->stream;
676
0
  if ((cf = RB_FIND(client_files, files, &find)) == NULL)
677
0
    fatalx("unknown stream number");
678
0
  log_debug("write %zu to file %d", size, cf->stream);
679
680
0
  if (cf->event != NULL)
681
0
    bufferevent_write(cf->event, msg + 1, size);
682
0
}
683
684
/* Handle a file write close message (client). */
685
void
686
file_write_close(struct client_files *files, struct imsg *imsg)
687
0
{
688
0
  struct msg_write_close  *msg = imsg->data;
689
0
  size_t       msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
690
0
  struct client_file   find, *cf;
691
692
0
  if (msglen != sizeof *msg)
693
0
    fatalx("bad MSG_WRITE_CLOSE size");
694
0
  find.stream = msg->stream;
695
0
  if ((cf = RB_FIND(client_files, files, &find)) == NULL)
696
0
    fatalx("unknown stream number");
697
0
  log_debug("close file %d", cf->stream);
698
0
  cf->closed = 1;
699
700
0
  if (cf->event == NULL || EVBUFFER_LENGTH(cf->event->output) == 0) {
701
0
    file_write_finished(cf);
702
0
  }
703
0
}
704
705
/* Client file read error callback. */
706
static void
707
file_read_error_callback(__unused struct bufferevent *bev, short what,
708
    void *arg)
709
0
{
710
0
  struct client_file  *cf = arg;
711
0
  struct msg_read_done   msg;
712
713
0
  log_debug("read error file %d", cf->stream);
714
715
0
  msg.stream = cf->stream;
716
0
  msg.error = (what & EVBUFFER_ERROR) ? EIO : 0;
717
0
  proc_send(cf->peer, MSG_READ_DONE, -1, &msg, sizeof msg);
718
719
0
  bufferevent_free(cf->event);
720
0
  close(cf->fd);
721
0
  RB_REMOVE(client_files, cf->tree, cf);
722
0
  file_free(cf);
723
0
}
724
725
/* Client file read callback. */
726
static void
727
file_read_callback(__unused struct bufferevent *bev, void *arg)
728
0
{
729
0
  struct client_file  *cf = arg;
730
0
  void      *bdata;
731
0
  size_t       bsize;
732
0
  struct msg_read_data  *msg;
733
0
  size_t       msglen;
734
735
0
  msg = xmalloc(sizeof *msg);
736
0
  for (;;) {
737
0
    bdata = EVBUFFER_DATA(cf->event->input);
738
0
    bsize = EVBUFFER_LENGTH(cf->event->input);
739
740
0
    if (bsize == 0)
741
0
      break;
742
0
    if (bsize > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
743
0
      bsize = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
744
0
    log_debug("read %zu from file %d", bsize, cf->stream);
745
746
0
    msglen = (sizeof *msg) + bsize;
747
0
    msg = xrealloc(msg, msglen);
748
0
    msg->stream = cf->stream;
749
0
    memcpy(msg + 1, bdata, bsize);
750
0
    proc_send(cf->peer, MSG_READ, -1, msg, msglen);
751
752
0
    evbuffer_drain(cf->event->input, bsize);
753
0
  }
754
0
  free(msg);
755
0
}
756
757
/* Handle a file read open message (client). */
758
void
759
file_read_open(struct client_files *files, struct tmuxpeer *peer,
760
    struct imsg *imsg, int allow_streams, int close_received, client_file_cb cb,
761
    void *cbdata)
762
0
{
763
0
  struct msg_read_open  *msg = imsg->data;
764
0
  size_t       msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
765
0
  const char    *path;
766
0
  struct msg_read_done   reply;
767
0
  struct client_file   find, *cf;
768
0
  const int    flags = O_NONBLOCK|O_RDONLY;
769
0
  int      error;
770
771
0
  if (msglen < sizeof *msg)
772
0
    fatalx("bad MSG_READ_OPEN size");
773
0
  if (msglen == sizeof *msg)
774
0
    path = "-";
775
0
  else
776
0
    path = (const char *)(msg + 1);
777
0
  log_debug("open read file %d %s", msg->stream, path);
778
779
0
  find.stream = msg->stream;
780
0
  if (RB_FIND(client_files, files, &find) != NULL) {
781
0
    error = EBADF;
782
0
    goto reply;
783
0
  }
784
0
  cf = file_create_with_peer(peer, files, msg->stream, cb, cbdata);
785
0
  if (cf->closed) {
786
0
    error = EBADF;
787
0
    goto reply;
788
0
  }
789
790
0
  cf->fd = -1;
791
0
  if (msg->fd == -1)
792
0
    cf->fd = open(path, flags);
793
0
  else if (allow_streams) {
794
0
    if (msg->fd != STDIN_FILENO)
795
0
      errno = EBADF;
796
0
    else {
797
0
      cf->fd = dup(msg->fd);
798
0
      if (close_received)
799
0
        close(msg->fd); /* can only be used once */
800
0
    }
801
0
  } else
802
0
    errno = EBADF;
803
0
  if (cf->fd == -1) {
804
0
    error = errno;
805
0
    goto reply;
806
0
  }
807
808
0
  cf->event = bufferevent_new(cf->fd, file_read_callback, NULL,
809
0
      file_read_error_callback, cf);
810
0
  if (cf->event == NULL)
811
0
    fatalx("out of memory");
812
0
  bufferevent_enable(cf->event, EV_READ);
813
0
  return;
814
815
0
reply:
816
0
  reply.stream = msg->stream;
817
0
  reply.error = error;
818
0
  proc_send(peer, MSG_READ_DONE, -1, &reply, sizeof reply);
819
0
}
820
821
/* Handle a read cancel message (client). */
822
void
823
file_read_cancel(struct client_files *files, struct imsg *imsg)
824
0
{
825
0
  struct msg_read_cancel  *msg = imsg->data;
826
0
  size_t       msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
827
0
  struct client_file   find, *cf;
828
829
0
  if (msglen != sizeof *msg)
830
0
    fatalx("bad MSG_READ_CANCEL size");
831
0
  find.stream = msg->stream;
832
0
  if ((cf = RB_FIND(client_files, files, &find)) == NULL)
833
0
    fatalx("unknown stream number");
834
0
  log_debug("cancel file %d", cf->stream);
835
836
0
  file_read_error_callback(NULL, 0, cf);
837
0
}
838
839
/* Handle a write ready message (server). */
840
int
841
file_write_ready(struct client_files *files, struct imsg *imsg)
842
0
{
843
0
  struct msg_write_ready  *msg = imsg->data;
844
0
  size_t       msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
845
0
  struct client_file   find, *cf;
846
847
0
  if (msglen != sizeof *msg)
848
0
    return (-1);
849
0
  find.stream = msg->stream;
850
0
  if ((cf = RB_FIND(client_files, files, &find)) == NULL)
851
0
    return (0);
852
0
  if (msg->error != 0) {
853
0
    cf->error = msg->error;
854
0
    file_fire_done(cf);
855
0
  } else
856
0
    file_push(cf);
857
0
  return (0);
858
0
}
859
860
/* Handle a write done message (server). */
861
int
862
file_write_done(struct client_files *files, struct imsg *imsg)
863
0
{
864
0
  struct msg_write_done *msg = imsg->data;
865
0
  size_t       msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
866
0
  struct client_file   find, *cf;
867
868
0
  if (msglen != sizeof *msg)
869
0
    return (-1);
870
0
  find.stream = msg->stream;
871
0
  if ((cf = RB_FIND(client_files, files, &find)) == NULL)
872
0
    return (0);
873
0
  if (cf->c == NULL || (~cf->c->flags & CLIENT_WRITE_ACK))
874
0
    return (0);
875
876
0
  log_debug("file %d write done", cf->stream);
877
0
  cf->error = msg->error;
878
0
  file_fire_done(cf);
879
0
  return (0);
880
0
}
881
882
/* Handle read data message (server). */
883
int
884
file_read_data(struct client_files *files, struct imsg *imsg)
885
0
{
886
0
  struct msg_read_data  *msg = imsg->data;
887
0
  size_t       msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
888
0
  struct client_file   find, *cf;
889
0
  void      *bdata = msg + 1;
890
0
  size_t       bsize = msglen - sizeof *msg;
891
892
0
  if (msglen < sizeof *msg)
893
0
    return (-1);
894
0
  find.stream = msg->stream;
895
0
  if ((cf = RB_FIND(client_files, files, &find)) == NULL)
896
0
    return (0);
897
898
0
  log_debug("file %d read %zu bytes", cf->stream, bsize);
899
0
  if (cf->error == 0 && !cf->closed) {
900
0
    if (evbuffer_add(cf->buffer, bdata, bsize) != 0) {
901
0
      cf->error = ENOMEM;
902
0
      file_fire_done(cf);
903
0
    } else
904
0
      file_fire_read(cf);
905
0
  }
906
0
  return (0);
907
0
}
908
909
/* Handle a read done message (server). */
910
int
911
file_read_done(struct client_files *files, struct imsg *imsg)
912
0
{
913
0
  struct msg_read_done  *msg = imsg->data;
914
0
  size_t       msglen = imsg->hdr.len - IMSG_HEADER_SIZE;
915
0
  struct client_file   find, *cf;
916
917
0
  if (msglen != sizeof *msg)
918
0
    return (-1);
919
0
  find.stream = msg->stream;
920
0
  if ((cf = RB_FIND(client_files, files, &find)) == NULL)
921
0
    return (0);
922
923
0
  log_debug("file %d read done", cf->stream);
924
0
  cf->error = msg->error;
925
0
  file_fire_done(cf);
926
0
  return (0);
927
0
}