[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.10

1.10    ! ratchov     1: /*     $OpenBSD: fdpass.c,v 1.9 2020/02/26 13:53:58 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 */
1.9       ratchov    35: #define FDPASS_OPEN_CTL                2       /* open an audio control device */
1.1       ratchov    36: #define FDPASS_RETURN          3       /* return after above commands */
                     37:        unsigned int cmd;               /* one of above */
                     38:        unsigned int num;               /* audio device or midi port number */
1.8       ratchov    39:        unsigned int idx;               /* index in the path list */
1.1       ratchov    40:        unsigned int mode;              /* SIO_PLAY, SIO_REC, MIO_IN, ... */
                     41: };
                     42:
                     43: int fdpass_pollfd(void *, struct pollfd *);
                     44: int fdpass_revents(void *, struct pollfd *);
                     45: void fdpass_in_worker(void *);
                     46: void fdpass_in_helper(void *);
                     47: void fdpass_out(void *);
                     48: void fdpass_hup(void *);
                     49:
                     50: struct fileops worker_fileops = {
                     51:        "worker",
                     52:        fdpass_pollfd,
                     53:        fdpass_revents,
                     54:        fdpass_in_worker,
                     55:        fdpass_out,
                     56:        fdpass_hup
                     57: };
                     58:
                     59: struct fileops helper_fileops = {
                     60:        "helper",
                     61:        fdpass_pollfd,
                     62:        fdpass_revents,
                     63:        fdpass_in_helper,
                     64:        fdpass_out,
                     65:        fdpass_hup
                     66: };
                     67:
                     68: struct fdpass {
                     69:        struct file *file;
                     70:        int fd;
                     71: } *fdpass_peer = NULL;
                     72:
                     73: static void
                     74: fdpass_log(struct fdpass *f)
                     75: {
                     76:        log_puts(f->file->name);
                     77: }
                     78:
                     79: static int
1.8       ratchov    80: fdpass_send(struct fdpass *f, int cmd, int num, int idx, int mode, int fd)
1.1       ratchov    81: {
                     82:        struct fdpass_msg data;
                     83:        struct msghdr msg;
                     84:        struct cmsghdr *cmsg;
                     85:        union {
                     86:                struct cmsghdr hdr;
                     87:                unsigned char buf[CMSG_SPACE(sizeof(int))];
                     88:        } cmsgbuf;
                     89:        struct iovec iov;
                     90:        ssize_t n;
                     91:
                     92:        data.cmd = cmd;
                     93:        data.num = num;
1.8       ratchov    94:        data.idx = idx;
1.1       ratchov    95:        data.mode = mode;
                     96:        iov.iov_base = &data;
                     97:        iov.iov_len = sizeof(struct fdpass_msg);
                     98:        memset(&msg, 0, sizeof(msg));
                     99:        msg.msg_iov = &iov;
                    100:        msg.msg_iovlen = 1;
                    101:        if (fd >= 0) {
                    102:                msg.msg_control = &cmsgbuf.buf;
                    103:                msg.msg_controllen = sizeof(cmsgbuf.buf);
                    104:                cmsg = CMSG_FIRSTHDR(&msg);
                    105:                cmsg->cmsg_len = CMSG_LEN(sizeof(int));
                    106:                cmsg->cmsg_level = SOL_SOCKET;
                    107:                cmsg->cmsg_type = SCM_RIGHTS;
                    108:                *(int *)CMSG_DATA(cmsg) = fd;
                    109:        }
                    110:        n = sendmsg(f->fd, &msg, 0);
1.6       deraadt   111:        if (n == -1) {
1.1       ratchov   112:                if (log_level >= 1) {
                    113:                        fdpass_log(f);
                    114:                        log_puts(": sendmsg failed\n");
                    115:                }
                    116:                fdpass_close(f);
                    117:                return 0;
                    118:        }
                    119:        if (n != sizeof(struct fdpass_msg)) {
                    120:                if (log_level >= 1) {
                    121:                        fdpass_log(f);
                    122:                        log_puts(": short write\n");
                    123:                }
                    124:                fdpass_close(f);
                    125:                return 0;
                    126:        }
                    127: #ifdef DEBUG
                    128:        if (log_level >= 3) {
                    129:                fdpass_log(f);
                    130:                log_puts(": send: cmd = ");
                    131:                log_puti(cmd);
                    132:                log_puts(", num = ");
                    133:                log_puti(num);
1.8       ratchov   134:                log_puts(", idx = ");
                    135:                log_puti(idx);
1.1       ratchov   136:                log_puts(", mode = ");
                    137:                log_puti(mode);
                    138:                log_puts(", fd = ");
                    139:                log_puti(fd);
                    140:                log_puts("\n");
                    141:        }
                    142: #endif
                    143:        if (fd >= 0)
                    144:                close(fd);
                    145:        return 1;
                    146: }
                    147:
                    148: static int
1.8       ratchov   149: fdpass_recv(struct fdpass *f, int *cmd, int *num, int *idx, int *mode, int *fd)
1.1       ratchov   150: {
                    151:        struct fdpass_msg data;
                    152:        struct msghdr msg;
                    153:        struct cmsghdr *cmsg;
                    154:        union {
                    155:                struct cmsghdr hdr;
                    156:                unsigned char buf[CMSG_SPACE(sizeof(int))];
                    157:        } cmsgbuf;
                    158:        struct iovec iov;
                    159:        ssize_t n;
                    160:
                    161:        iov.iov_base = &data;
                    162:        iov.iov_len = sizeof(struct fdpass_msg);
                    163:        memset(&msg, 0, sizeof(msg));
                    164:        msg.msg_control = &cmsgbuf.buf;
                    165:        msg.msg_controllen = sizeof(cmsgbuf.buf);
                    166:        msg.msg_iov = &iov;
                    167:        msg.msg_iovlen = 1;
                    168:        n = recvmsg(f->fd, &msg, MSG_WAITALL);
1.6       deraadt   169:        if (n == -1 && errno == EMSGSIZE) {
1.1       ratchov   170:                if (log_level >= 1) {
                    171:                        fdpass_log(f);
                    172:                        log_puts(": out of fds\n");
                    173:                }
                    174:                /*
                    175:                 * ancillary data (ie the fd) is discarded,
                    176:                 * retrieve the message
                    177:                 */
                    178:                n = recvmsg(f->fd, &msg, MSG_WAITALL);
                    179:        }
1.6       deraadt   180:        if (n == -1) {
1.1       ratchov   181:                if (log_level >= 1) {
                    182:                        fdpass_log(f);
                    183:                        log_puts(": recvmsg failed\n");
                    184:                }
                    185:                fdpass_close(f);
                    186:                return 0;
                    187:        }
                    188:        if (n == 0) {
1.2       ratchov   189:                if (log_level >= 3) {
1.1       ratchov   190:                        fdpass_log(f);
                    191:                        log_puts(": recvmsg eof\n");
                    192:                }
                    193:                fdpass_close(f);
                    194:                return 0;
                    195:        }
                    196:        if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) {
                    197:                if (log_level >= 1) {
                    198:                        fdpass_log(f);
                    199:                        log_puts(": truncated\n");
                    200:                }
                    201:                fdpass_close(f);
                    202:                return 0;
                    203:        }
                    204:        cmsg = CMSG_FIRSTHDR(&msg);
                    205:        for (;;) {
                    206:                if (cmsg == NULL) {
                    207:                        *fd = -1;
                    208:                        break;
                    209:                }
                    210:                if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)) &&
                    211:                    cmsg->cmsg_level == SOL_SOCKET &&
                    212:                    cmsg->cmsg_type == SCM_RIGHTS) {
                    213:                        *fd = *(int *)CMSG_DATA(cmsg);
                    214:                        break;
                    215:                }
                    216:                cmsg = CMSG_NXTHDR(&msg, cmsg);
                    217:        }
                    218:        *cmd = data.cmd;
                    219:        *num = data.num;
1.8       ratchov   220:        *idx = data.idx;
1.1       ratchov   221:        *mode = data.mode;
                    222: #ifdef DEBUG
                    223:        if (log_level >= 3) {
                    224:                fdpass_log(f);
                    225:                log_puts(": recv: cmd = ");
                    226:                log_puti(*cmd);
                    227:                log_puts(", num = ");
                    228:                log_puti(*num);
1.8       ratchov   229:                log_puts(", idx = ");
                    230:                log_puti(*idx);
1.1       ratchov   231:                log_puts(", mode = ");
                    232:                log_puti(*mode);
                    233:                log_puts(", fd = ");
                    234:                log_puti(*fd);
                    235:                log_puts("\n");
                    236:        }
                    237: #endif
                    238:        return 1;
                    239: }
                    240:
                    241: static int
                    242: fdpass_waitret(struct fdpass *f, int *retfd)
                    243: {
                    244:        int cmd, unused;
                    245:
1.8       ratchov   246:        if (!fdpass_recv(fdpass_peer, &cmd, &unused, &unused, &unused, retfd))
1.1       ratchov   247:                return 0;
                    248:        if (cmd != FDPASS_RETURN) {
                    249:                if (log_level >= 1) {
                    250:                        fdpass_log(f);
                    251:                        log_puts(": expected RETURN message\n");
                    252:                }
                    253:                fdpass_close(f);
                    254:                return 0;
                    255:        }
                    256:        return 1;
                    257: }
                    258:
                    259: struct sio_hdl *
1.8       ratchov   260: fdpass_sio_open(int num, int idx, unsigned int mode)
1.1       ratchov   261: {
                    262:        int fd;
                    263:
1.5       ratchov   264:        if (fdpass_peer == NULL)
                    265:                return NULL;
1.8       ratchov   266:        if (!fdpass_send(fdpass_peer, FDPASS_OPEN_SND, num, idx, mode, -1))
1.1       ratchov   267:                return NULL;
                    268:        if (!fdpass_waitret(fdpass_peer, &fd))
                    269:                return NULL;
                    270:        if (fd < 0)
                    271:                return NULL;
                    272:        return sio_sun_fdopen(fd, mode, 1);
                    273: }
                    274:
                    275: struct mio_hdl *
1.8       ratchov   276: fdpass_mio_open(int num, int idx, unsigned int mode)
1.1       ratchov   277: {
                    278:        int fd;
                    279:
1.5       ratchov   280:        if (fdpass_peer == NULL)
                    281:                return NULL;
1.8       ratchov   282:        if (!fdpass_send(fdpass_peer, FDPASS_OPEN_MIDI, num, idx, mode, -1))
1.1       ratchov   283:                return NULL;
                    284:        if (!fdpass_waitret(fdpass_peer, &fd))
                    285:                return NULL;
                    286:        if (fd < 0)
                    287:                return NULL;
                    288:        return mio_rmidi_fdopen(fd, mode, 1);
                    289: }
                    290:
1.9       ratchov   291: struct sioctl_hdl *
                    292: fdpass_sioctl_open(int num, int idx, unsigned int mode)
                    293: {
                    294:        int fd;
                    295:
                    296:        if (fdpass_peer == NULL)
                    297:                return NULL;
                    298:        if (!fdpass_send(fdpass_peer, FDPASS_OPEN_CTL, num, idx, mode, -1))
                    299:                return NULL;
                    300:        if (!fdpass_waitret(fdpass_peer, &fd))
                    301:                return NULL;
                    302:        if (fd < 0)
                    303:                return NULL;
                    304:        return sioctl_sun_fdopen(fd, mode, 1);
                    305: }
                    306:
1.1       ratchov   307: void
                    308: fdpass_in_worker(void *arg)
                    309: {
                    310:        struct fdpass *f = arg;
                    311:
1.2       ratchov   312:        if (log_level >= 3) {
1.1       ratchov   313:                fdpass_log(f);
                    314:                log_puts(": exit\n");
                    315:        }
                    316:        fdpass_close(f);
                    317:        return;
                    318: }
                    319:
                    320: void
                    321: fdpass_in_helper(void *arg)
                    322: {
1.8       ratchov   323:        int cmd, num, idx, mode, fd;
1.1       ratchov   324:        struct fdpass *f = arg;
                    325:        struct dev *d;
1.10    ! ratchov   326:        struct dev_alt *da;
1.1       ratchov   327:        struct port *p;
1.8       ratchov   328:        char *path;
1.1       ratchov   329:
1.8       ratchov   330:        if (!fdpass_recv(f, &cmd, &num, &idx, &mode, &fd))
1.3       ratchov   331:                return;
1.1       ratchov   332:        switch (cmd) {
                    333:        case FDPASS_OPEN_SND:
                    334:                d = dev_bynum(num);
                    335:                if (d == NULL || !(mode & (SIO_PLAY | SIO_REC))) {
                    336:                        if (log_level >= 1) {
                    337:                                fdpass_log(f);
                    338:                                log_puts(": bad audio device or mode\n");
                    339:                        }
                    340:                        fdpass_close(f);
                    341:                        return;
                    342:                }
1.10    ! ratchov   343:                for (da = d->alt_list; ; da = da->next) {
        !           344:                        if (da == NULL) {
        !           345:                                fdpass_close(f);
        !           346:                                return;
        !           347:                        }
        !           348:                        if (da->idx == idx)
        !           349:                                break;
1.7       ratchov   350:                }
1.10    ! ratchov   351:                fd = sio_sun_getfd(da->name, mode, 1);
1.1       ratchov   352:                break;
                    353:        case FDPASS_OPEN_MIDI:
                    354:                p = port_bynum(num);
                    355:                if (p == NULL || !(mode & (MIO_IN | MIO_OUT))) {
                    356:                        if (log_level >= 1) {
                    357:                                fdpass_log(f);
                    358:                                log_puts(": bad midi port or mode\n");
                    359:                        }
                    360:                        fdpass_close(f);
                    361:                        return;
                    362:                }
1.8       ratchov   363:                path = namelist_byindex(&p->path_list, idx);
                    364:                if (path == NULL) {
                    365:                        fdpass_close(f);
                    366:                        return;
1.7       ratchov   367:                }
1.8       ratchov   368:                fd = mio_rmidi_getfd(path, mode, 1);
1.9       ratchov   369:                break;
                    370:        case FDPASS_OPEN_CTL:
                    371:                d = dev_bynum(num);
                    372:                if (d == NULL || !(mode & (SIOCTL_READ | SIOCTL_WRITE))) {
                    373:                        if (log_level >= 1) {
                    374:                                fdpass_log(f);
                    375:                                log_puts(": bad audio control device\n");
                    376:                        }
                    377:                        fdpass_close(f);
                    378:                        return;
                    379:                }
1.10    ! ratchov   380:                for (da = d->alt_list; ; da = da->next) {
        !           381:                        if (da == NULL) {
        !           382:                                fdpass_close(f);
        !           383:                                return;
        !           384:                        }
        !           385:                        if (da->idx == idx)
        !           386:                                break;
1.9       ratchov   387:                }
1.10    ! ratchov   388:                fd = sioctl_sun_getfd(da->name, mode, 1);
1.1       ratchov   389:                break;
                    390:        default:
                    391:                fdpass_close(f);
                    392:                return;
                    393:        }
1.8       ratchov   394:        fdpass_send(f, FDPASS_RETURN, 0, 0, 0, fd);
1.1       ratchov   395: }
                    396:
                    397: void
                    398: fdpass_out(void *arg)
                    399: {
                    400: }
                    401:
                    402: void
                    403: fdpass_hup(void *arg)
                    404: {
                    405:        struct fdpass *f = arg;
                    406:
1.2       ratchov   407:        if (log_level >= 3) {
1.1       ratchov   408:                fdpass_log(f);
                    409:                log_puts(": hup\n");
                    410:        }
                    411:        fdpass_close(f);
                    412: }
                    413:
                    414: struct fdpass *
                    415: fdpass_new(int sock, struct fileops *ops)
                    416: {
                    417:        struct fdpass *f;
                    418:
                    419:        f = xmalloc(sizeof(struct fdpass));
                    420:        f->file = file_new(ops, f, ops->name, 1);
                    421:        if (f->file == NULL) {
                    422:                close(sock);
1.4       ratchov   423:                xfree(f);
1.1       ratchov   424:                return NULL;
                    425:        }
                    426:        f->fd = sock;
                    427:        fdpass_peer = f;
                    428:        return f;
                    429: }
                    430:
                    431: void
                    432: fdpass_close(struct fdpass *f)
                    433: {
                    434:        fdpass_peer = NULL;
                    435:        file_del(f->file);
                    436:        close(f->fd);
                    437:        xfree(f);
                    438: }
                    439:
                    440: int
                    441: fdpass_pollfd(void *arg, struct pollfd *pfd)
                    442: {
                    443:        struct fdpass *f = arg;
                    444:
                    445:        pfd->fd = f->fd;
                    446:        pfd->events = POLLIN;
                    447:        return 1;
                    448: }
                    449:
                    450: int
                    451: fdpass_revents(void *arg, struct pollfd *pfd)
                    452: {
                    453:        return pfd->revents;
                    454: }