[BACK]Return to file.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / tmux

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: }