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