Annotation of src/usr.bin/tmux/file.c, Revision 1.7
1.7 ! nicm 1: /* $OpenBSD: file.c,v 1.6 2020/05/08 14:15:11 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;
1.7 ! nicm 245: if (c == NULL ||
! 246: (c->flags & CLIENT_ATTACHED) ||
! 247: (c->flags & CLIENT_CONTROL)) {
1.1 nicm 248: cf->error = EBADF;
249: goto done;
250: }
251: goto skip;
252: }
253:
254: cf = file_create(c, file_next_stream++, cb, cbdata);
1.5 nicm 255: cf->path = file_get_path(c, path);
1.1 nicm 256:
257: if (c == NULL || c->flags & CLIENT_ATTACHED) {
258: if (flags & O_APPEND)
259: mode = "ab";
260: else
261: mode = "wb";
262: f = fopen(cf->path, mode);
263: if (f == NULL) {
264: cf->error = errno;
265: goto done;
266: }
267: if (fwrite(bdata, 1, bsize, f) != bsize) {
268: fclose(f);
269: cf->error = EIO;
270: goto done;
271: }
272: fclose(f);
273: goto done;
274: }
275:
276: skip:
277: evbuffer_add(cf->buffer, bdata, bsize);
278:
1.3 nicm 279: msglen = strlen(cf->path) + 1 + sizeof *msg;
280: if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
1.1 nicm 281: cf->error = E2BIG;
282: goto done;
283: }
1.3 nicm 284: msg = xmalloc(msglen);
285: msg->stream = cf->stream;
286: msg->fd = fd;
287: msg->flags = flags;
288: memcpy(msg + 1, cf->path, msglen - sizeof *msg);
289: if (proc_send(c->peer, MSG_WRITE_OPEN, -1, msg, msglen) != 0) {
290: free(msg);
1.1 nicm 291: cf->error = EINVAL;
292: goto done;
293: }
1.3 nicm 294: free(msg);
1.1 nicm 295: return;
296:
297: done:
298: file_fire_done(cf);
299: }
300:
301: void
302: file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
303: {
304: struct client_file *cf;
305: FILE *f;
1.3 nicm 306: struct msg_read_open *msg;
307: size_t msglen, size;
1.1 nicm 308: int fd = -1;
309: char buffer[BUFSIZ];
310:
311: if (strcmp(path, "-") == 0) {
312: cf = file_create(c, file_next_stream++, cb, cbdata);
313: cf->path = xstrdup("-");
314:
315: fd = STDIN_FILENO;
1.7 ! nicm 316: if (c == NULL ||
! 317: (c->flags & CLIENT_ATTACHED) ||
! 318: (c->flags & CLIENT_CONTROL)) {
1.1 nicm 319: cf->error = EBADF;
320: goto done;
321: }
322: goto skip;
323: }
324:
325: cf = file_create(c, file_next_stream++, cb, cbdata);
1.5 nicm 326: cf->path = file_get_path(c, path);
1.1 nicm 327:
328: if (c == NULL || c->flags & CLIENT_ATTACHED) {
329: f = fopen(cf->path, "rb");
330: if (f == NULL) {
331: cf->error = errno;
332: goto done;
333: }
334: for (;;) {
335: size = fread(buffer, 1, sizeof buffer, f);
336: if (evbuffer_add(cf->buffer, buffer, size) != 0) {
337: cf->error = ENOMEM;
338: goto done;
339: }
340: if (size != sizeof buffer)
341: break;
342: }
343: if (ferror(f)) {
344: cf->error = EIO;
345: goto done;
346: }
347: fclose(f);
348: goto done;
349: }
350:
351: skip:
1.3 nicm 352: msglen = strlen(cf->path) + 1 + sizeof *msg;
353: if (msglen > MAX_IMSGSIZE - IMSG_HEADER_SIZE) {
1.1 nicm 354: cf->error = E2BIG;
355: goto done;
356: }
1.3 nicm 357: msg = xmalloc(msglen);
358: msg->stream = cf->stream;
359: msg->fd = fd;
360: memcpy(msg + 1, cf->path, msglen - sizeof *msg);
361: if (proc_send(c->peer, MSG_READ_OPEN, -1, msg, msglen) != 0) {
362: free(msg);
1.1 nicm 363: cf->error = EINVAL;
364: goto done;
365: }
1.3 nicm 366: free(msg);
1.1 nicm 367: return;
368:
369: done:
370: file_fire_done(cf);
371: }
372:
373: static void
374: file_push_cb(__unused int fd, __unused short events, void *arg)
375: {
376: struct client_file *cf = arg;
377: struct client *c = cf->c;
378:
379: if (~c->flags & CLIENT_DEAD)
380: file_push(cf);
381: file_free(cf);
382: }
383:
384: void
385: file_push(struct client_file *cf)
386: {
387: struct client *c = cf->c;
1.3 nicm 388: struct msg_write_data *msg;
389: size_t msglen, sent, left;
1.1 nicm 390: struct msg_write_close close;
391:
1.3 nicm 392: msg = xmalloc(sizeof *msg);
1.1 nicm 393: left = EVBUFFER_LENGTH(cf->buffer);
394: while (left != 0) {
395: sent = left;
1.4 nicm 396: if (sent > MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg)
397: sent = MAX_IMSGSIZE - IMSG_HEADER_SIZE - sizeof *msg;
1.1 nicm 398:
1.3 nicm 399: msglen = (sizeof *msg) + sent;
400: msg = xrealloc(msg, msglen);
401: msg->stream = cf->stream;
402: memcpy(msg + 1, EVBUFFER_DATA(cf->buffer), sent);
403: if (proc_send(c->peer, MSG_WRITE, -1, msg, msglen) != 0)
1.1 nicm 404: break;
405: evbuffer_drain(cf->buffer, sent);
406:
407: left = EVBUFFER_LENGTH(cf->buffer);
408: log_debug("%s: file %d sent %zu, left %zu", c->name, cf->stream,
409: sent, left);
410: }
411: if (left != 0) {
412: cf->references++;
413: event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL);
414: } else if (cf->stream > 2) {
415: close.stream = cf->stream;
416: proc_send(c->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
417: file_fire_done(cf);
418: }
1.3 nicm 419: free(msg);
1.1 nicm 420: }