Annotation of src/usr.bin/tmux/file.c, Revision 1.3
1.3 ! nicm 1: /* $OpenBSD: file.c,v 1.2 2019/12/12 11:51:32 nicm Exp $ */
1.1 nicm 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>
1.3 ! nicm 20: #include <sys/queue.h>
1.1 nicm 21:
22: #include <errno.h>
23: #include <fcntl.h>
1.3 ! nicm 24: #include <imsg.h>
1.1 nicm 25: #include <stdio.h>
26: #include <stdlib.h>
27: #include <string.h>
28: #include <unistd.h>
29:
30: #include "tmux.h"
31:
32: static int file_next_stream = 3;
33:
34: RB_GENERATE(client_files, client_file, entry, file_cmp);
35:
36: int
37: file_cmp(struct client_file *cf1, struct client_file *cf2)
38: {
39: if (cf1->stream < cf2->stream)
40: return (-1);
41: if (cf1->stream > cf2->stream)
42: return (1);
43: return (0);
44: }
45:
46: struct client_file *
47: file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
48: {
49: struct client_file *cf;
50:
51: cf = xcalloc(1, sizeof *cf);
52: cf->c = c;
53: cf->references = 1;
54: cf->stream = stream;
55:
56: cf->buffer = evbuffer_new();
57: if (cf->buffer == NULL)
58: fatalx("out of memory");
59:
60: cf->cb = cb;
61: cf->data = cbdata;
62:
63: if (cf->c != NULL) {
64: RB_INSERT(client_files, &cf->c->files, cf);
65: cf->c->references++;
66: }
67:
68: return (cf);
69: }
70:
71: void
72: file_free(struct client_file *cf)
73: {
74: if (--cf->references != 0)
75: return;
76:
77: evbuffer_free(cf->buffer);
78: free(cf->path);
79:
80: if (cf->c != NULL) {
81: RB_REMOVE(client_files, &cf->c->files, cf);
82: server_client_unref(cf->c);
83: }
84: free(cf);
85: }
86:
87: static void
88: file_fire_done_cb(__unused int fd, __unused short events, void *arg)
89: {
90: struct client_file *cf = arg;
91: struct client *c = cf->c;
92:
1.2 nicm 93: if (cf->cb != NULL && (c == NULL || (~c->flags & CLIENT_DEAD)))
1.1 nicm 94: cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
95: file_free(cf);
96: }
97:
98: void
99: file_fire_done(struct client_file *cf)
100: {
101: event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL);
102: }
103:
104: void
105: file_fire_read(struct client_file *cf)
106: {
107: struct client *c = cf->c;
108:
109: if (cf->cb != NULL)
110: cf->cb(c, cf->path, cf->error, 0, cf->buffer, cf->data);
111: }
112:
113: int
114: file_can_print(struct client *c)
115: {
116: if (c == NULL)
117: return (0);
118: if (c->session != NULL && (~c->flags & CLIENT_CONTROL))
119: return (0);
120: return (1);
121: }
122:
123: void
124: file_print(struct client *c, const char *fmt, ...)
125: {
126: va_list ap;
127:
128: va_start(ap, fmt);
129: file_vprint(c, fmt, ap);
130: va_end(ap);
131: }
132:
133: void
134: file_vprint(struct client *c, const char *fmt, va_list ap)
135: {
136: struct client_file find, *cf;
137: struct msg_write_open msg;
138:
139: if (!file_can_print(c))
140: return;
141:
142: find.stream = 1;
143: if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
144: cf = file_create(c, 1, NULL, NULL);
145: cf->path = xstrdup("-");
146:
147: evbuffer_add_vprintf(cf->buffer, fmt, ap);
148:
149: msg.stream = 1;
150: msg.fd = STDOUT_FILENO;
151: msg.flags = 0;
152: proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
153: } else {
154: evbuffer_add_vprintf(cf->buffer, fmt, ap);
155: file_push(cf);
156: }
157: }
158:
159: void
160: file_print_buffer(struct client *c, void *data, size_t size)
161: {
162: struct client_file find, *cf;
163: struct msg_write_open msg;
164:
165: if (!file_can_print(c))
166: return;
167:
168: find.stream = 1;
169: if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
170: cf = file_create(c, 1, NULL, NULL);
171: cf->path = xstrdup("-");
172:
173: evbuffer_add(cf->buffer, data, size);
174:
175: msg.stream = 1;
176: msg.fd = STDOUT_FILENO;
177: msg.flags = 0;
178: proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
179: } else {
180: evbuffer_add(cf->buffer, data, size);
181: file_push(cf);
182: }
183: }
184:
185: void
186: file_error(struct client *c, const char *fmt, ...)
187: {
188: struct client_file find, *cf;
189: struct msg_write_open msg;
190: va_list ap;
191:
192: if (!file_can_print(c))
193: return;
194:
195: va_start(ap, fmt);
196:
197: find.stream = 2;
198: if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
199: cf = file_create(c, 2, NULL, NULL);
200: cf->path = xstrdup("-");
201:
202: evbuffer_add_vprintf(cf->buffer, fmt, ap);
203:
204: msg.stream = 2;
205: msg.fd = STDERR_FILENO;
206: msg.flags = 0;
207: proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
208: } else {
209: evbuffer_add_vprintf(cf->buffer, fmt, ap);
210: file_push(cf);
211: }
212:
213: va_end(ap);
214: }
215:
216: void
217: file_write(struct client *c, const char *path, int flags, const void *bdata,
218: size_t bsize, client_file_cb cb, void *cbdata)
219: {
220: struct client_file *cf;
221: FILE *f;
1.3 ! nicm 222: struct msg_write_open *msg;
! 223: size_t msglen;
1.1 nicm 224: int fd = -1;
225: const char *mode;
226:
227: if (strcmp(path, "-") == 0) {
228: cf = file_create(c, file_next_stream++, cb, cbdata);
229: cf->path = xstrdup("-");
230:
231: fd = STDOUT_FILENO;
232: if (c == NULL || c->flags & CLIENT_ATTACHED) {
233: cf->error = EBADF;
234: goto done;
235: }
236: goto skip;
237: }
238:
239: cf = file_create(c, file_next_stream++, cb, cbdata);
240: cf->path = server_client_get_path(c, path);
241:
242: if (c == NULL || c->flags & CLIENT_ATTACHED) {
243: if (flags & O_APPEND)
244: mode = "ab";
245: else
246: mode = "wb";
247: f = fopen(cf->path, mode);
248: if (f == NULL) {
249: cf->error = errno;
250: goto done;
251: }
252: if (fwrite(bdata, 1, bsize, f) != bsize) {
253: fclose(f);
254: cf->error = EIO;
255: goto done;
256: }
257: fclose(f);
258: goto done;
259: }
260:
261: skip:
262: evbuffer_add(cf->buffer, bdata, bsize);
263:
1.3 ! nicm 264: msglen = strlen(cf->path) + 1 + sizeof *msg;
! 265: if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
1.1 nicm 266: cf->error = E2BIG;
267: goto done;
268: }
1.3 ! nicm 269: msg = xmalloc(msglen);
! 270: msg->stream = cf->stream;
! 271: msg->fd = fd;
! 272: msg->flags = flags;
! 273: memcpy(msg + 1, cf->path, msglen - sizeof *msg);
! 274: if (proc_send(c->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) {
! 275: free(msg);
1.1 nicm 276: cf->error = EINVAL;
277: goto done;
278: }
1.3 ! nicm 279: free(msg);
1.1 nicm 280: return;
281:
282: done:
283: file_fire_done(cf);
284: }
285:
286: void
287: file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
288: {
289: struct client_file *cf;
290: FILE *f;
1.3 ! nicm 291: struct msg_read_open *msg;
! 292: size_t msglen, size;
1.1 nicm 293: int fd = -1;
294: char buffer[BUFSIZ];
295:
296: if (strcmp(path, "-") == 0) {
297: cf = file_create(c, file_next_stream++, cb, cbdata);
298: cf->path = xstrdup("-");
299:
300: fd = STDIN_FILENO;
301: if (c == NULL || c->flags & CLIENT_ATTACHED) {
302: cf->error = EBADF;
303: goto done;
304: }
305: goto skip;
306: }
307:
308: cf = file_create(c, file_next_stream++, cb, cbdata);
309: cf->path = server_client_get_path(c, path);
310:
311: if (c == NULL || c->flags & CLIENT_ATTACHED) {
312: f = fopen(cf->path, "rb");
313: if (f == NULL) {
314: cf->error = errno;
315: goto done;
316: }
317: for (;;) {
318: size = fread(buffer, 1, sizeof buffer, f);
319: if (evbuffer_add(cf->buffer, buffer, size) != 0) {
320: cf->error = ENOMEM;
321: goto done;
322: }
323: if (size != sizeof buffer)
324: break;
325: }
326: if (ferror(f)) {
327: cf->error = EIO;
328: goto done;
329: }
330: fclose(f);
331: goto done;
332: }
333:
334: skip:
1.3 ! nicm 335: msglen = strlen(cf->path) + 1 + sizeof *msg;
! 336: if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
1.1 nicm 337: cf->error = E2BIG;
338: goto done;
339: }
1.3 ! nicm 340: msg = xmalloc(msglen);
! 341: msg->stream = cf->stream;
! 342: msg->fd = fd;
! 343: memcpy(msg + 1, cf->path, msglen - sizeof *msg);
! 344: if (proc_send(c->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
! 345: free(msg);
1.1 nicm 346: cf->error = EINVAL;
347: goto done;
348: }
1.3 ! nicm 349: free(msg);
1.1 nicm 350: return;
351:
352: done:
353: file_fire_done(cf);
354: }
355:
356: static void
357: file_push_cb(__unused int fd, __unused short events, void *arg)
358: {
359: struct client_file *cf = arg;
360: struct client *c = cf->c;
361:
362: if (~c->flags & CLIENT_DEAD)
363: file_push(cf);
364: file_free(cf);
365: }
366:
367: void
368: file_push(struct client_file *cf)
369: {
370: struct client *c = cf->c;
1.3 ! nicm 371: struct msg_write_data *msg;
! 372: size_t msglen, sent, left;
1.1 nicm 373: struct msg_write_close close;
374:
1.3 ! nicm 375: msg = xmalloc(sizeof *msg);
1.1 nicm 376: left = EVBUFFER_LENGTH(cf->buffer);
377: while (left != 0) {
378: sent = left;
1.3 ! nicm 379: if (sent > MAX_IMSGSIZE - IMSG_HEADER_SIZE)
! 380: sent = MAX_IMSGSIZE - IMSG_HEADER_SIZE;
1.1 nicm 381:
1.3 ! nicm 382: msglen = (sizeof *msg) + sent;
! 383: msg = xrealloc(msg, msglen);
! 384: msg->stream = cf->stream;
! 385: memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent);
! 386: if (proc_send(c->peer, MSG_WRITE, -1, msg, msglen) != 0)
1.1 nicm 387: break;
388: evbuffer_drain(cf->buffer, sent);
389:
390: left = EVBUFFER_LENGTH(cf->buffer);
391: log_debug("%s: file %d sent %zu, left %zu", c->name, cf->stream,
392: sent, left);
393: }
394: if (left != 0) {
395: cf->references++;
396: event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL);
397: } else if (cf->stream > 2) {
398: close.stream = cf->stream;
399: proc_send(c->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
400: file_fire_done(cf);
401: }
1.3 ! nicm 402: free(msg);
1.1 nicm 403: }