Coverage Report

Created: 2026-06-16 06:31

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