Annotation of src/usr.bin/sndiod/fdpass.c, Revision 1.6
1.6 ! deraadt 1: /* $OpenBSD: fdpass.c,v 1.5 2019/05/10 04:39:08 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);
1.6 ! deraadt 108: if (n == -1) {
1.1 ratchov 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);
1.6 ! deraadt 164: if (n == -1 && errno == EMSGSIZE) {
1.1 ratchov 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: }
1.6 ! deraadt 175: if (n == -1) {
1.1 ratchov 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: }