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

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