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