Annotation of src/usr.bin/sndiod/fdpass.c, Revision 1.1
1.1 ! ratchov 1: /* $OpenBSD$ */
! 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) {
! 184: if (log_level >= 1) {
! 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:
! 256: if (!fdpass_send(fdpass_peer, FDPASS_OPEN_SND, num, mode, -1))
! 257: return NULL;
! 258: if (!fdpass_waitret(fdpass_peer, &fd))
! 259: return NULL;
! 260: if (fd < 0)
! 261: return NULL;
! 262: return sio_sun_fdopen(fd, mode, 1);
! 263: }
! 264:
! 265: struct mio_hdl *
! 266: fdpass_mio_open(int num, unsigned int mode)
! 267: {
! 268: int fd;
! 269:
! 270: if (!fdpass_send(fdpass_peer, FDPASS_OPEN_MIDI, num, mode, -1))
! 271: return NULL;
! 272: if (!fdpass_waitret(fdpass_peer, &fd))
! 273: return NULL;
! 274: if (fd < 0)
! 275: return NULL;
! 276: return mio_rmidi_fdopen(fd, mode, 1);
! 277: }
! 278:
! 279: void
! 280: fdpass_in_worker(void *arg)
! 281: {
! 282: struct fdpass *f = arg;
! 283:
! 284: if (log_level >= 1) {
! 285: fdpass_log(f);
! 286: log_puts(": exit\n");
! 287: }
! 288: fdpass_close(f);
! 289: return;
! 290: }
! 291:
! 292: void
! 293: fdpass_in_helper(void *arg)
! 294: {
! 295: int cmd, num, mode, fd;
! 296: struct fdpass *f = arg;
! 297: struct dev *d;
! 298: struct port *p;
! 299:
! 300: if (!fdpass_recv(f, &cmd, &num, &mode, &fd))
! 301: return;
! 302: switch (cmd) {
! 303: case FDPASS_OPEN_SND:
! 304: d = dev_bynum(num);
! 305: if (d == NULL || !(mode & (SIO_PLAY | SIO_REC))) {
! 306: if (log_level >= 1) {
! 307: fdpass_log(f);
! 308: log_puts(": bad audio device or mode\n");
! 309: }
! 310: fdpass_close(f);
! 311: return;
! 312: }
! 313: fd = sio_sun_getfd(d->path, mode, 1);
! 314: break;
! 315: case FDPASS_OPEN_MIDI:
! 316: p = port_bynum(num);
! 317: if (p == NULL || !(mode & (MIO_IN | MIO_OUT))) {
! 318: if (log_level >= 1) {
! 319: fdpass_log(f);
! 320: log_puts(": bad midi port or mode\n");
! 321: }
! 322: fdpass_close(f);
! 323: return;
! 324: }
! 325: fd = mio_rmidi_getfd(p->path, mode, 1);
! 326: break;
! 327: default:
! 328: fdpass_close(f);
! 329: return;
! 330: }
! 331: fdpass_send(f, FDPASS_RETURN, 0, 0, fd);
! 332: }
! 333:
! 334: void
! 335: fdpass_out(void *arg)
! 336: {
! 337: }
! 338:
! 339: void
! 340: fdpass_hup(void *arg)
! 341: {
! 342: struct fdpass *f = arg;
! 343:
! 344: if (log_level >= 1) {
! 345: fdpass_log(f);
! 346: log_puts(": hup\n");
! 347: }
! 348: fdpass_close(f);
! 349: }
! 350:
! 351: struct fdpass *
! 352: fdpass_new(int sock, struct fileops *ops)
! 353: {
! 354: struct fdpass *f;
! 355:
! 356: f = xmalloc(sizeof(struct fdpass));
! 357: f->file = file_new(ops, f, ops->name, 1);
! 358: if (f->file == NULL) {
! 359: close(sock);
! 360: return NULL;
! 361: }
! 362: f->fd = sock;
! 363: fdpass_peer = f;
! 364: return f;
! 365: }
! 366:
! 367: void
! 368: fdpass_close(struct fdpass *f)
! 369: {
! 370: fdpass_peer = NULL;
! 371: file_del(f->file);
! 372: close(f->fd);
! 373: xfree(f);
! 374: }
! 375:
! 376: int
! 377: fdpass_pollfd(void *arg, struct pollfd *pfd)
! 378: {
! 379: struct fdpass *f = arg;
! 380:
! 381: pfd->fd = f->fd;
! 382: pfd->events = POLLIN;
! 383: return 1;
! 384: }
! 385:
! 386: int
! 387: fdpass_revents(void *arg, struct pollfd *pfd)
! 388: {
! 389: return pfd->revents;
! 390: }