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

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