Annotation of src/usr.bin/tmux/file.c, Revision 1.2
1.2 ! nicm 1: /* $OpenBSD: file.c,v 1.1 2019/12/12 11:39:56 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>
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: static int file_next_stream = 3;
31:
32: RB_GENERATE(client_files, client_file, entry, file_cmp);
33:
34: int
35: file_cmp(struct client_file *cf1, struct client_file *cf2)
36: {
37: if (cf1->stream < cf2->stream)
38: return (-1);
39: if (cf1->stream > cf2->stream)
40: return (1);
41: return (0);
42: }
43:
44: struct client_file *
45: file_create(struct client *c, int stream, client_file_cb cb, void *cbdata)
46: {
47: struct client_file *cf;
48:
49: cf = xcalloc(1, sizeof *cf);
50: cf->c = c;
51: cf->references = 1;
52: cf->stream = stream;
53:
54: cf->buffer = evbuffer_new();
55: if (cf->buffer == NULL)
56: fatalx("out of memory");
57:
58: cf->cb = cb;
59: cf->data = cbdata;
60:
61: if (cf->c != NULL) {
62: RB_INSERT(client_files, &cf->c->files, cf);
63: cf->c->references++;
64: }
65:
66: return (cf);
67: }
68:
69: void
70: file_free(struct client_file *cf)
71: {
72: if (--cf->references != 0)
73: return;
74:
75: evbuffer_free(cf->buffer);
76: free(cf->path);
77:
78: if (cf->c != NULL) {
79: RB_REMOVE(client_files, &cf->c->files, cf);
80: server_client_unref(cf->c);
81: }
82: free(cf);
83: }
84:
85: static void
86: file_fire_done_cb(__unused int fd, __unused short events, void *arg)
87: {
88: struct client_file *cf = arg;
89: struct client *c = cf->c;
90:
1.2 ! nicm 91: if (cf->cb != NULL && (c == NULL || (~c->flags & CLIENT_DEAD)))
1.1 nicm 92: cf->cb(c, cf->path, cf->error, 1, cf->buffer, cf->data);
93: file_free(cf);
94: }
95:
96: void
97: file_fire_done(struct client_file *cf)
98: {
99: event_once(-1, EV_TIMEOUT, file_fire_done_cb, cf, NULL);
100: }
101:
102: void
103: file_fire_read(struct client_file *cf)
104: {
105: struct client *c = cf->c;
106:
107: if (cf->cb != NULL)
108: cf->cb(c, cf->path, cf->error, 0, cf->buffer, cf->data);
109: }
110:
111: int
112: file_can_print(struct client *c)
113: {
114: if (c == NULL)
115: return (0);
116: if (c->session != NULL && (~c->flags & CLIENT_CONTROL))
117: return (0);
118: return (1);
119: }
120:
121: void
122: file_print(struct client *c, const char *fmt, ...)
123: {
124: va_list ap;
125:
126: va_start(ap, fmt);
127: file_vprint(c, fmt, ap);
128: va_end(ap);
129: }
130:
131: void
132: file_vprint(struct client *c, const char *fmt, va_list ap)
133: {
134: struct client_file find, *cf;
135: struct msg_write_open msg;
136:
137: if (!file_can_print(c))
138: return;
139:
140: find.stream = 1;
141: if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
142: cf = file_create(c, 1, NULL, NULL);
143: cf->path = xstrdup("-");
144:
145: evbuffer_add_vprintf(cf->buffer, fmt, ap);
146:
147: msg.stream = 1;
148: msg.fd = STDOUT_FILENO;
149: msg.flags = 0;
150: strlcpy(msg.path, "-", sizeof msg.path);
151: proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
152: } else {
153: evbuffer_add_vprintf(cf->buffer, fmt, ap);
154: file_push(cf);
155: }
156: }
157:
158: void
159: file_print_buffer(struct client *c, void *data, size_t size)
160: {
161: struct client_file find, *cf;
162: struct msg_write_open msg;
163:
164: if (!file_can_print(c))
165: return;
166:
167: find.stream = 1;
168: if ((cf = RB_FIND(client_files, &c->files, &find)) == NULL) {
169: cf = file_create(c, 1, NULL, NULL);
170: cf->path = xstrdup("-");
171:
172: evbuffer_add(cf->buffer, data, size);
173:
174: msg.stream = 1;
175: msg.fd = STDOUT_FILENO;
176: msg.flags = 0;
177: strlcpy(msg.path, "-", sizeof msg.path);
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: strlcpy(msg.path, "-", sizeof msg.path);
208: proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg);
209: } else {
210: evbuffer_add_vprintf(cf->buffer, fmt, ap);
211: file_push(cf);
212: }
213:
214: va_end(ap);
215: }
216:
217: void
218: file_write(struct client *c, const char *path, int flags, const void *bdata,
219: size_t bsize, client_file_cb cb, void *cbdata)
220: {
221: struct client_file *cf;
222: FILE *f;
223: struct msg_write_open msg;
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:
264: msg.stream = cf->stream;
265: msg.fd = fd;
266: msg.flags = flags;
267: if (strlcpy(msg.path, cf->path, sizeof msg.path) >= sizeof msg.path) {
268: cf->error = E2BIG;
269: goto done;
270: }
271: if (proc_send(c->peer, MSG_WRITE_OPEN, -1, &msg, sizeof msg) != 0) {
272: cf->error = EINVAL;
273: goto done;
274: }
275: return;
276:
277: done:
278: file_fire_done(cf);
279: }
280:
281: void
282: file_read(struct client *c, const char *path, client_file_cb cb, void *cbdata)
283: {
284: struct client_file *cf;
285: FILE *f;
286: struct msg_read_open msg;
287: int fd = -1;
288: char buffer[BUFSIZ];
289: size_t size;
290:
291: if (strcmp(path, "-") == 0) {
292: cf = file_create(c, file_next_stream++, cb, cbdata);
293: cf->path = xstrdup("-");
294:
295: fd = STDIN_FILENO;
296: if (c == NULL || c->flags & CLIENT_ATTACHED) {
297: cf->error = EBADF;
298: goto done;
299: }
300: goto skip;
301: }
302:
303: cf = file_create(c, file_next_stream++, cb, cbdata);
304: cf->path = server_client_get_path(c, path);
305:
306: if (c == NULL || c->flags & CLIENT_ATTACHED) {
307: f = fopen(cf->path, "rb");
308: if (f == NULL) {
309: cf->error = errno;
310: goto done;
311: }
312: for (;;) {
313: size = fread(buffer, 1, sizeof buffer, f);
314: if (evbuffer_add(cf->buffer, buffer, size) != 0) {
315: cf->error = ENOMEM;
316: goto done;
317: }
318: if (size != sizeof buffer)
319: break;
320: }
321: if (ferror(f)) {
322: cf->error = EIO;
323: goto done;
324: }
325: fclose(f);
326: goto done;
327: }
328:
329: skip:
330: msg.stream = cf->stream;
331: msg.fd = fd;
332: if (strlcpy(msg.path, cf->path, sizeof msg.path) >= sizeof msg.path) {
333: cf->error = E2BIG;
334: goto done;
335: }
336: if (proc_send(c->peer, MSG_READ_OPEN, -1, &msg, sizeof msg) != 0) {
337: cf->error = EINVAL;
338: goto done;
339: }
340: return;
341:
342: done:
343: file_fire_done(cf);
344: }
345:
346: static void
347: file_push_cb(__unused int fd, __unused short events, void *arg)
348: {
349: struct client_file *cf = arg;
350: struct client *c = cf->c;
351:
352: if (~c->flags & CLIENT_DEAD)
353: file_push(cf);
354: file_free(cf);
355: }
356:
357: void
358: file_push(struct client_file *cf)
359: {
360: struct client *c = cf->c;
361: struct msg_write_data msg;
362: struct msg_write_close close;
363: size_t sent, left;
364:
365: left = EVBUFFER_LENGTH(cf->buffer);
366: while (left != 0) {
367: sent = left;
368: if (sent > sizeof msg.data)
369: sent = sizeof msg.data;
370: memcpy(msg.data, EVBUFFER_DATA(cf->buffer), sent);
371: msg.size = sent;
372:
373: msg.stream = cf->stream;
374: if (proc_send(c->peer, MSG_WRITE, -1, &msg, sizeof msg) != 0)
375: break;
376: evbuffer_drain(cf->buffer, sent);
377:
378: left = EVBUFFER_LENGTH(cf->buffer);
379: log_debug("%s: file %d sent %zu, left %zu", c->name, cf->stream,
380: sent, left);
381: }
382: if (left != 0) {
383: cf->references++;
384: event_once(-1, EV_TIMEOUT, file_push_cb, cf, NULL);
385: } else if (cf->stream > 2) {
386: close.stream = cf->stream;
387: proc_send(c->peer, MSG_WRITE_CLOSE, -1, &close, sizeof close);
388: file_fire_done(cf);
389: }
390: }