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: }