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

Annotation of src/usr.bin/sndiod/fdpass.c, Revision 1.8

1.8     ! ratchov     1: /*     $OpenBSD: fdpass.c,v 1.7 2019/09/21 04:42:46 ratchov Exp $      */
1.1       ratchov     2: /*
                      3:  * Copyright (c) 2015 Alexandre Ratchov <alex@caoua.org>
                      4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
                      6:  * purpose with or without fee is hereby granted, provided that the above
                      7:  * copyright notice and this permission notice appear in all copies.
                      8:  *
                      9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     16:  */
                     17: #include <sys/socket.h>
                     18: #include <errno.h>
                     19: #include <fcntl.h>
                     20: #include <poll.h>
                     21: #include <sndio.h>
                     22: #include <string.h>
                     23: #include <unistd.h>
                     24: #include "dev.h"
                     25: #include "fdpass.h"
                     26: #include "file.h"
                     27: #include "listen.h"
                     28: #include "midi.h"
                     29: #include "sock.h"
                     30: #include "utils.h"
                     31:
                     32: struct fdpass_msg {
                     33: #define FDPASS_OPEN_SND                0       /* open an audio device */
                     34: #define FDPASS_OPEN_MIDI       1       /* open a midi port */
                     35: #define FDPASS_RETURN          3       /* return after above commands */
                     36:        unsigned int cmd;               /* one of above */
                     37:        unsigned int num;               /* audio device or midi port number */
1.8     ! ratchov    38:        unsigned int idx;               /* index in the path list */
1.1       ratchov    39:        unsigned int mode;              /* SIO_PLAY, SIO_REC, MIO_IN, ... */
                     40: };
                     41:
                     42: int fdpass_pollfd(void *, struct pollfd *);
                     43: int fdpass_revents(void *, struct pollfd *);
                     44: void fdpass_in_worker(void *);
                     45: void fdpass_in_helper(void *);
                     46: void fdpass_out(void *);
                     47: void fdpass_hup(void *);
                     48:
                     49: struct fileops worker_fileops = {
                     50:        "worker",
                     51:        fdpass_pollfd,
                     52:        fdpass_revents,
                     53:        fdpass_in_worker,
                     54:        fdpass_out,
                     55:        fdpass_hup
                     56: };
                     57:
                     58: struct fileops helper_fileops = {
                     59:        "helper",
                     60:        fdpass_pollfd,
                     61:        fdpass_revents,
                     62:        fdpass_in_helper,
                     63:        fdpass_out,
                     64:        fdpass_hup
                     65: };
                     66:
                     67: struct fdpass {
                     68:        struct file *file;
                     69:        int fd;
                     70: } *fdpass_peer = NULL;
                     71:
                     72: static void
                     73: fdpass_log(struct fdpass *f)
                     74: {
                     75:        log_puts(f->file->name);
                     76: }
                     77:
                     78: static int
1.8     ! ratchov    79: fdpass_send(struct fdpass *f, int cmd, int num, int idx, int mode, int fd)
1.1       ratchov    80: {
                     81:        struct fdpass_msg data;
                     82:        struct msghdr msg;
                     83:        struct cmsghdr *cmsg;
                     84:        union {
                     85:                struct cmsghdr hdr;
                     86:                unsigned char buf[CMSG_SPACE(sizeof(int))];
                     87:        } cmsgbuf;
                     88:        struct iovec iov;
                     89:        ssize_t n;
                     90:
                     91:        data.cmd = cmd;
                     92:        data.num = num;
1.8     ! ratchov    93:        data.idx = idx;
1.1       ratchov    94:        data.mode = mode;
                     95:        iov.iov_base = &data;
                     96:        iov.iov_len = sizeof(struct fdpass_msg);
                     97:        memset(&msg, 0, sizeof(msg));
                     98:        msg.msg_iov = &iov;
                     99:        msg.msg_iovlen = 1;
                    100:        if (fd >= 0) {
                    101:                msg.msg_control = &cmsgbuf.buf;
                    102:                msg.msg_controllen = sizeof(cmsgbuf.buf);
                    103:                cmsg = CMSG_FIRSTHDR(&msg);
                    104:                cmsg->cmsg_len = CMSG_LEN(sizeof(int));
                    105:                cmsg->cmsg_level = SOL_SOCKET;
                    106:                cmsg->cmsg_type = SCM_RIGHTS;
                    107:                *(int *)CMSG_DATA(cmsg) = fd;
                    108:        }
                    109:        n = sendmsg(f->fd, &msg, 0);
1.6       deraadt   110:        if (n == -1) {
1.1       ratchov   111:                if (log_level >= 1) {
                    112:                        fdpass_log(f);
                    113:                        log_puts(": sendmsg failed\n");
                    114:                }
                    115:                fdpass_close(f);
                    116:                return 0;
                    117:        }
                    118:        if (n != sizeof(struct fdpass_msg)) {
                    119:                if (log_level >= 1) {
                    120:                        fdpass_log(f);
                    121:                        log_puts(": short write\n");
                    122:                }
                    123:                fdpass_close(f);
                    124:                return 0;
                    125:        }
                    126: #ifdef DEBUG
                    127:        if (log_level >= 3) {
                    128:                fdpass_log(f);
                    129:                log_puts(": send: cmd = ");
                    130:                log_puti(cmd);
                    131:                log_puts(", num = ");
                    132:                log_puti(num);
1.8     ! ratchov   133:                log_puts(", idx = ");
        !           134:                log_puti(idx);
1.1       ratchov   135:                log_puts(", mode = ");
                    136:                log_puti(mode);
                    137:                log_puts(", fd = ");
                    138:                log_puti(fd);
                    139:                log_puts("\n");
                    140:        }
                    141: #endif
                    142:        if (fd >= 0)
                    143:                close(fd);
                    144:        return 1;
                    145: }
                    146:
                    147: static int
1.8     ! ratchov   148: fdpass_recv(struct fdpass *f, int *cmd, int *num, int *idx, int *mode, int *fd)
1.1       ratchov   149: {
                    150:        struct fdpass_msg data;
                    151:        struct msghdr msg;
                    152:        struct cmsghdr *cmsg;
                    153:        union {
                    154:                struct cmsghdr hdr;
                    155:                unsigned char buf[CMSG_SPACE(sizeof(int))];
                    156:        } cmsgbuf;
                    157:        struct iovec iov;
                    158:        ssize_t n;
                    159:
                    160:        iov.iov_base = &data;
                    161:        iov.iov_len = sizeof(struct fdpass_msg);
                    162:        memset(&msg, 0, sizeof(msg));
                    163:        msg.msg_control = &cmsgbuf.buf;
                    164:        msg.msg_controllen = sizeof(cmsgbuf.buf);
                    165:        msg.msg_iov = &iov;
                    166:        msg.msg_iovlen = 1;
                    167:        n = recvmsg(f->fd, &msg, MSG_WAITALL);
1.6       deraadt   168:        if (n == -1 && errno == EMSGSIZE) {
1.1       ratchov   169:                if (log_level >= 1) {
                    170:                        fdpass_log(f);
                    171:                        log_puts(": out of fds\n");
                    172:                }
                    173:                /*
                    174:                 * ancillary data (ie the fd) is discarded,
                    175:                 * retrieve the message
                    176:                 */
                    177:                n = recvmsg(f->fd, &msg, MSG_WAITALL);
                    178:        }
1.6       deraadt   179:        if (n == -1) {
1.1       ratchov   180:                if (log_level >= 1) {
                    181:                        fdpass_log(f);
                    182:                        log_puts(": recvmsg failed\n");
                    183:                }
                    184:                fdpass_close(f);
                    185:                return 0;
                    186:        }
                    187:        if (n == 0) {
1.2       ratchov   188:                if (log_level >= 3) {
1.1       ratchov   189:                        fdpass_log(f);
                    190:                        log_puts(": recvmsg eof\n");
                    191:                }
                    192:                fdpass_close(f);
                    193:                return 0;
                    194:        }
                    195:        if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) {
                    196:                if (log_level >= 1) {
                    197:                        fdpass_log(f);
                    198:                        log_puts(": truncated\n");
                    199:                }
                    200:                fdpass_close(f);
                    201:                return 0;
                    202:        }
                    203:        cmsg = CMSG_FIRSTHDR(&msg);
                    204:        for (;;) {
                    205:                if (cmsg == NULL) {
                    206:                        *fd = -1;
                    207:                        break;
                    208:                }
                    209:                if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)) &&
                    210:                    cmsg->cmsg_level == SOL_SOCKET &&
                    211:                    cmsg->cmsg_type == SCM_RIGHTS) {
                    212:                        *fd = *(int *)CMSG_DATA(cmsg);
                    213:                        break;
                    214:                }
                    215:                cmsg = CMSG_NXTHDR(&msg, cmsg);
                    216:        }
                    217:        *cmd = data.cmd;
                    218:        *num = data.num;
1.8     ! ratchov   219:        *idx = data.idx;
1.1       ratchov   220:        *mode = data.mode;
                    221: #ifdef DEBUG
                    222:        if (log_level >= 3) {
                    223:                fdpass_log(f);
                    224:                log_puts(": recv: cmd = ");
                    225:                log_puti(*cmd);
                    226:                log_puts(", num = ");
                    227:                log_puti(*num);
1.8     ! ratchov   228:                log_puts(", idx = ");
        !           229:                log_puti(*idx);
1.1       ratchov   230:                log_puts(", mode = ");
                    231:                log_puti(*mode);
                    232:                log_puts(", fd = ");
                    233:                log_puti(*fd);
                    234:                log_puts("\n");
                    235:        }
                    236: #endif
                    237:        return 1;
                    238: }
                    239:
                    240: static int
                    241: fdpass_waitret(struct fdpass *f, int *retfd)
                    242: {
                    243:        int cmd, unused;
                    244:
1.8     ! ratchov   245:        if (!fdpass_recv(fdpass_peer, &cmd, &unused, &unused, &unused, retfd))
1.1       ratchov   246:                return 0;
                    247:        if (cmd != FDPASS_RETURN) {
                    248:                if (log_level >= 1) {
                    249:                        fdpass_log(f);
                    250:                        log_puts(": expected RETURN message\n");
                    251:                }
                    252:                fdpass_close(f);
                    253:                return 0;
                    254:        }
                    255:        return 1;
                    256: }
                    257:
                    258: struct sio_hdl *
1.8     ! ratchov   259: fdpass_sio_open(int num, int idx, unsigned int mode)
1.1       ratchov   260: {
                    261:        int fd;
                    262:
1.5       ratchov   263:        if (fdpass_peer == NULL)
                    264:                return NULL;
1.8     ! ratchov   265:        if (!fdpass_send(fdpass_peer, FDPASS_OPEN_SND, num, idx, mode, -1))
1.1       ratchov   266:                return NULL;
                    267:        if (!fdpass_waitret(fdpass_peer, &fd))
                    268:                return NULL;
                    269:        if (fd < 0)
                    270:                return NULL;
                    271:        return sio_sun_fdopen(fd, mode, 1);
                    272: }
                    273:
                    274: struct mio_hdl *
1.8     ! ratchov   275: fdpass_mio_open(int num, int idx, unsigned int mode)
1.1       ratchov   276: {
                    277:        int fd;
                    278:
1.5       ratchov   279:        if (fdpass_peer == NULL)
                    280:                return NULL;
1.8     ! ratchov   281:        if (!fdpass_send(fdpass_peer, FDPASS_OPEN_MIDI, num, idx, mode, -1))
1.1       ratchov   282:                return NULL;
                    283:        if (!fdpass_waitret(fdpass_peer, &fd))
                    284:                return NULL;
                    285:        if (fd < 0)
                    286:                return NULL;
                    287:        return mio_rmidi_fdopen(fd, mode, 1);
                    288: }
                    289:
                    290: void
                    291: fdpass_in_worker(void *arg)
                    292: {
                    293:        struct fdpass *f = arg;
                    294:
1.2       ratchov   295:        if (log_level >= 3) {
1.1       ratchov   296:                fdpass_log(f);
                    297:                log_puts(": exit\n");
                    298:        }
                    299:        fdpass_close(f);
                    300:        return;
                    301: }
                    302:
                    303: void
                    304: fdpass_in_helper(void *arg)
                    305: {
1.8     ! ratchov   306:        int cmd, num, idx, mode, fd;
1.1       ratchov   307:        struct fdpass *f = arg;
                    308:        struct dev *d;
                    309:        struct port *p;
1.8     ! ratchov   310:        char *path;
1.1       ratchov   311:
1.8     ! ratchov   312:        if (!fdpass_recv(f, &cmd, &num, &idx, &mode, &fd))
1.3       ratchov   313:                return;
1.1       ratchov   314:        switch (cmd) {
                    315:        case FDPASS_OPEN_SND:
                    316:                d = dev_bynum(num);
                    317:                if (d == NULL || !(mode & (SIO_PLAY | SIO_REC))) {
                    318:                        if (log_level >= 1) {
                    319:                                fdpass_log(f);
                    320:                                log_puts(": bad audio device or mode\n");
                    321:                        }
                    322:                        fdpass_close(f);
                    323:                        return;
                    324:                }
1.8     ! ratchov   325:                path = namelist_byindex(&d->path_list, idx);
        !           326:                if (path == NULL) {
        !           327:                        fdpass_close(f);
        !           328:                        return;
1.7       ratchov   329:                }
1.8     ! ratchov   330:                fd = sio_sun_getfd(path, mode, 1);
1.1       ratchov   331:                break;
                    332:        case FDPASS_OPEN_MIDI:
                    333:                p = port_bynum(num);
                    334:                if (p == NULL || !(mode & (MIO_IN | MIO_OUT))) {
                    335:                        if (log_level >= 1) {
                    336:                                fdpass_log(f);
                    337:                                log_puts(": bad midi port or mode\n");
                    338:                        }
                    339:                        fdpass_close(f);
                    340:                        return;
                    341:                }
1.8     ! ratchov   342:                path = namelist_byindex(&p->path_list, idx);
        !           343:                if (path == NULL) {
        !           344:                        fdpass_close(f);
        !           345:                        return;
1.7       ratchov   346:                }
1.8     ! ratchov   347:                fd = mio_rmidi_getfd(path, mode, 1);
1.1       ratchov   348:                break;
                    349:        default:
                    350:                fdpass_close(f);
                    351:                return;
                    352:        }
1.8     ! ratchov   353:        fdpass_send(f, FDPASS_RETURN, 0, 0, 0, fd);
1.1       ratchov   354: }
                    355:
                    356: void
                    357: fdpass_out(void *arg)
                    358: {
                    359: }
                    360:
                    361: void
                    362: fdpass_hup(void *arg)
                    363: {
                    364:        struct fdpass *f = arg;
                    365:
1.2       ratchov   366:        if (log_level >= 3) {
1.1       ratchov   367:                fdpass_log(f);
                    368:                log_puts(": hup\n");
                    369:        }
                    370:        fdpass_close(f);
                    371: }
                    372:
                    373: struct fdpass *
                    374: fdpass_new(int sock, struct fileops *ops)
                    375: {
                    376:        struct fdpass *f;
                    377:
                    378:        f = xmalloc(sizeof(struct fdpass));
                    379:        f->file = file_new(ops, f, ops->name, 1);
                    380:        if (f->file == NULL) {
                    381:                close(sock);
1.4       ratchov   382:                xfree(f);
1.1       ratchov   383:                return NULL;
                    384:        }
                    385:        f->fd = sock;
                    386:        fdpass_peer = f;
                    387:        return f;
                    388: }
                    389:
                    390: void
                    391: fdpass_close(struct fdpass *f)
                    392: {
                    393:        fdpass_peer = NULL;
                    394:        file_del(f->file);
                    395:        close(f->fd);
                    396:        xfree(f);
                    397: }
                    398:
                    399: int
                    400: fdpass_pollfd(void *arg, struct pollfd *pfd)
                    401: {
                    402:        struct fdpass *f = arg;
                    403:
                    404:        pfd->fd = f->fd;
                    405:        pfd->events = POLLIN;
                    406:        return 1;
                    407: }
                    408:
                    409: int
                    410: fdpass_revents(void *arg, struct pollfd *pfd)
                    411: {
                    412:        return pfd->revents;
                    413: }