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