xref: /openbsd-src/usr.bin/sndiod/fdpass.c (revision 7b6392009e6e5a7f8e494c162a4d259ea5e13a62)
1*7b639200Sratchov /*	$OpenBSD: fdpass.c,v 1.12 2024/12/20 07:35:56 ratchov Exp $	*/
2395f8c55Sratchov /*
3395f8c55Sratchov  * Copyright (c) 2015 Alexandre Ratchov <alex@caoua.org>
4395f8c55Sratchov  *
5395f8c55Sratchov  * Permission to use, copy, modify, and distribute this software for any
6395f8c55Sratchov  * purpose with or without fee is hereby granted, provided that the above
7395f8c55Sratchov  * copyright notice and this permission notice appear in all copies.
8395f8c55Sratchov  *
9395f8c55Sratchov  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10395f8c55Sratchov  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11395f8c55Sratchov  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12395f8c55Sratchov  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13395f8c55Sratchov  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14395f8c55Sratchov  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15395f8c55Sratchov  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16395f8c55Sratchov  */
17395f8c55Sratchov #include <sys/socket.h>
18395f8c55Sratchov #include <errno.h>
19395f8c55Sratchov #include <fcntl.h>
20395f8c55Sratchov #include <poll.h>
21395f8c55Sratchov #include <sndio.h>
22395f8c55Sratchov #include <string.h>
23395f8c55Sratchov #include <unistd.h>
24395f8c55Sratchov #include "dev.h"
25395f8c55Sratchov #include "fdpass.h"
26395f8c55Sratchov #include "file.h"
27395f8c55Sratchov #include "listen.h"
28395f8c55Sratchov #include "midi.h"
29395f8c55Sratchov #include "sock.h"
30395f8c55Sratchov #include "utils.h"
31395f8c55Sratchov 
32395f8c55Sratchov struct fdpass_msg {
33395f8c55Sratchov #define FDPASS_OPEN_SND		0	/* open an audio device */
34395f8c55Sratchov #define FDPASS_OPEN_MIDI	1	/* open a midi port */
35d07fece6Sratchov #define FDPASS_OPEN_CTL		2	/* open an audio control device */
36395f8c55Sratchov #define FDPASS_RETURN		3	/* return after above commands */
37395f8c55Sratchov 	unsigned int cmd;		/* one of above */
38395f8c55Sratchov 	unsigned int num;		/* audio device or midi port number */
39395f8c55Sratchov 	unsigned int mode;		/* SIO_PLAY, SIO_REC, MIO_IN, ... */
40395f8c55Sratchov };
41395f8c55Sratchov 
42395f8c55Sratchov int fdpass_pollfd(void *, struct pollfd *);
43395f8c55Sratchov int fdpass_revents(void *, struct pollfd *);
44395f8c55Sratchov void fdpass_in_worker(void *);
45395f8c55Sratchov void fdpass_in_helper(void *);
46395f8c55Sratchov void fdpass_out(void *);
47395f8c55Sratchov void fdpass_hup(void *);
48395f8c55Sratchov 
49395f8c55Sratchov struct fileops worker_fileops = {
50395f8c55Sratchov 	"worker",
51395f8c55Sratchov 	fdpass_pollfd,
52395f8c55Sratchov 	fdpass_revents,
53395f8c55Sratchov 	fdpass_in_worker,
54395f8c55Sratchov 	fdpass_out,
55395f8c55Sratchov 	fdpass_hup
56395f8c55Sratchov };
57395f8c55Sratchov 
58395f8c55Sratchov struct fileops helper_fileops = {
59395f8c55Sratchov 	"helper",
60395f8c55Sratchov 	fdpass_pollfd,
61395f8c55Sratchov 	fdpass_revents,
62395f8c55Sratchov 	fdpass_in_helper,
63395f8c55Sratchov 	fdpass_out,
64395f8c55Sratchov 	fdpass_hup
65395f8c55Sratchov };
66395f8c55Sratchov 
67395f8c55Sratchov struct fdpass {
68395f8c55Sratchov 	struct file *file;
69395f8c55Sratchov 	int fd;
70395f8c55Sratchov } *fdpass_peer = NULL;
71395f8c55Sratchov 
72395f8c55Sratchov static int
7336355b88Sratchov fdpass_send(struct fdpass *f, int cmd, int num, int mode, int fd)
74395f8c55Sratchov {
75395f8c55Sratchov 	struct fdpass_msg data;
76395f8c55Sratchov 	struct msghdr msg;
77395f8c55Sratchov 	struct cmsghdr *cmsg;
78395f8c55Sratchov 	union {
79395f8c55Sratchov 		struct cmsghdr hdr;
80395f8c55Sratchov 		unsigned char buf[CMSG_SPACE(sizeof(int))];
81395f8c55Sratchov 	} cmsgbuf;
82395f8c55Sratchov 	struct iovec iov;
83395f8c55Sratchov 	ssize_t n;
84395f8c55Sratchov 
85395f8c55Sratchov 	data.cmd = cmd;
86395f8c55Sratchov 	data.num = num;
87395f8c55Sratchov 	data.mode = mode;
88395f8c55Sratchov 	iov.iov_base = &data;
89395f8c55Sratchov 	iov.iov_len = sizeof(struct fdpass_msg);
90395f8c55Sratchov 	memset(&msg, 0, sizeof(msg));
91395f8c55Sratchov 	msg.msg_iov = &iov;
92395f8c55Sratchov 	msg.msg_iovlen = 1;
93395f8c55Sratchov 	if (fd >= 0) {
94395f8c55Sratchov 		msg.msg_control = &cmsgbuf.buf;
95395f8c55Sratchov 		msg.msg_controllen = sizeof(cmsgbuf.buf);
96395f8c55Sratchov 		cmsg = CMSG_FIRSTHDR(&msg);
97395f8c55Sratchov 		cmsg->cmsg_len = CMSG_LEN(sizeof(int));
98395f8c55Sratchov 		cmsg->cmsg_level = SOL_SOCKET;
99395f8c55Sratchov 		cmsg->cmsg_type = SCM_RIGHTS;
100395f8c55Sratchov 		*(int *)CMSG_DATA(cmsg) = fd;
101395f8c55Sratchov 	}
102395f8c55Sratchov 	n = sendmsg(f->fd, &msg, 0);
1033aaa63ebSderaadt 	if (n == -1) {
104*7b639200Sratchov 		logx(1, "%s: sendmsg failed", f->file->name);
105395f8c55Sratchov 		fdpass_close(f);
106395f8c55Sratchov 		return 0;
107395f8c55Sratchov 	}
108395f8c55Sratchov 	if (n != sizeof(struct fdpass_msg)) {
109*7b639200Sratchov 		logx(1, "%s: short write", f->file->name);
110395f8c55Sratchov 		fdpass_close(f);
111395f8c55Sratchov 		return 0;
112395f8c55Sratchov 	}
113395f8c55Sratchov #ifdef DEBUG
114*7b639200Sratchov 	logx(3, "%s: send: cmd = %d, num = %d, mode = %d, fd = %d",
115*7b639200Sratchov 	    f->file->name, cmd, num, mode, fd);
116395f8c55Sratchov #endif
117395f8c55Sratchov 	if (fd >= 0)
118395f8c55Sratchov 		close(fd);
119395f8c55Sratchov 	return 1;
120395f8c55Sratchov }
121395f8c55Sratchov 
122395f8c55Sratchov static int
12336355b88Sratchov fdpass_recv(struct fdpass *f, int *cmd, int *num, int *mode, int *fd)
124395f8c55Sratchov {
125395f8c55Sratchov 	struct fdpass_msg data;
126395f8c55Sratchov 	struct msghdr msg;
127395f8c55Sratchov 	struct cmsghdr *cmsg;
128395f8c55Sratchov 	union {
129395f8c55Sratchov 		struct cmsghdr hdr;
130395f8c55Sratchov 		unsigned char buf[CMSG_SPACE(sizeof(int))];
131395f8c55Sratchov 	} cmsgbuf;
132395f8c55Sratchov 	struct iovec iov;
133395f8c55Sratchov 	ssize_t n;
134395f8c55Sratchov 
135395f8c55Sratchov 	iov.iov_base = &data;
136395f8c55Sratchov 	iov.iov_len = sizeof(struct fdpass_msg);
137395f8c55Sratchov 	memset(&msg, 0, sizeof(msg));
138395f8c55Sratchov 	msg.msg_control = &cmsgbuf.buf;
139395f8c55Sratchov 	msg.msg_controllen = sizeof(cmsgbuf.buf);
140395f8c55Sratchov 	msg.msg_iov = &iov;
141395f8c55Sratchov 	msg.msg_iovlen = 1;
142395f8c55Sratchov 	n = recvmsg(f->fd, &msg, MSG_WAITALL);
1433aaa63ebSderaadt 	if (n == -1 && errno == EMSGSIZE) {
144*7b639200Sratchov 		logx(1, "%s: out of fds", f->file->name);
145395f8c55Sratchov 		/*
146395f8c55Sratchov 		 * ancillary data (ie the fd) is discarded,
147395f8c55Sratchov 		 * retrieve the message
148395f8c55Sratchov 		 */
149395f8c55Sratchov 		n = recvmsg(f->fd, &msg, MSG_WAITALL);
150395f8c55Sratchov 	}
1513aaa63ebSderaadt 	if (n == -1) {
152*7b639200Sratchov 		logx(1, "%s: recvmsg failed", f->file->name);
153395f8c55Sratchov 		fdpass_close(f);
154395f8c55Sratchov 		return 0;
155395f8c55Sratchov 	}
156395f8c55Sratchov 	if (n == 0) {
157*7b639200Sratchov 		logx(3, "%s: recvmsg eof", f->file->name);
158395f8c55Sratchov 		fdpass_close(f);
159395f8c55Sratchov 		return 0;
160395f8c55Sratchov 	}
161395f8c55Sratchov 	if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) {
162*7b639200Sratchov 		logx(1, "%s: truncated", f->file->name);
163395f8c55Sratchov 		fdpass_close(f);
164395f8c55Sratchov 		return 0;
165395f8c55Sratchov 	}
166395f8c55Sratchov 	cmsg = CMSG_FIRSTHDR(&msg);
167395f8c55Sratchov 	for (;;) {
168395f8c55Sratchov 		if (cmsg == NULL) {
169395f8c55Sratchov 			*fd = -1;
170395f8c55Sratchov 			break;
171395f8c55Sratchov 		}
172395f8c55Sratchov 		if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)) &&
173395f8c55Sratchov 		    cmsg->cmsg_level == SOL_SOCKET &&
174395f8c55Sratchov 		    cmsg->cmsg_type == SCM_RIGHTS) {
175395f8c55Sratchov 			*fd = *(int *)CMSG_DATA(cmsg);
176395f8c55Sratchov 			break;
177395f8c55Sratchov 		}
178395f8c55Sratchov 		cmsg = CMSG_NXTHDR(&msg, cmsg);
179395f8c55Sratchov 	}
180395f8c55Sratchov 	*cmd = data.cmd;
181395f8c55Sratchov 	*num = data.num;
182395f8c55Sratchov 	*mode = data.mode;
183395f8c55Sratchov #ifdef DEBUG
184*7b639200Sratchov 	logx(3, "%s: recv: cmd = %d, num = %d, mode = %d, fd = %d",
185*7b639200Sratchov 	    f->file->name, *cmd, *num, *mode, *fd);
186395f8c55Sratchov #endif
187395f8c55Sratchov 	return 1;
188395f8c55Sratchov }
189395f8c55Sratchov 
190395f8c55Sratchov static int
191395f8c55Sratchov fdpass_waitret(struct fdpass *f, int *retfd)
192395f8c55Sratchov {
193395f8c55Sratchov 	int cmd, unused;
194395f8c55Sratchov 
19536355b88Sratchov 	if (!fdpass_recv(fdpass_peer, &cmd, &unused, &unused, retfd))
196395f8c55Sratchov 		return 0;
197395f8c55Sratchov 	if (cmd != FDPASS_RETURN) {
198*7b639200Sratchov 		logx(1, "%s: expected RETURN message", f->file->name);
199395f8c55Sratchov 		fdpass_close(f);
200395f8c55Sratchov 		return 0;
201395f8c55Sratchov 	}
202395f8c55Sratchov 	return 1;
203395f8c55Sratchov }
204395f8c55Sratchov 
205395f8c55Sratchov struct sio_hdl *
20636355b88Sratchov fdpass_sio_open(int num, unsigned int mode)
207395f8c55Sratchov {
208395f8c55Sratchov 	int fd;
209395f8c55Sratchov 
21016a99293Sratchov 	if (fdpass_peer == NULL)
21116a99293Sratchov 		return NULL;
21236355b88Sratchov 	if (!fdpass_send(fdpass_peer, FDPASS_OPEN_SND, num, mode, -1))
213395f8c55Sratchov 		return NULL;
214395f8c55Sratchov 	if (!fdpass_waitret(fdpass_peer, &fd))
215395f8c55Sratchov 		return NULL;
216395f8c55Sratchov 	if (fd < 0)
217395f8c55Sratchov 		return NULL;
218395f8c55Sratchov 	return sio_sun_fdopen(fd, mode, 1);
219395f8c55Sratchov }
220395f8c55Sratchov 
221395f8c55Sratchov struct mio_hdl *
22236355b88Sratchov fdpass_mio_open(int num, unsigned int mode)
223395f8c55Sratchov {
224395f8c55Sratchov 	int fd;
225395f8c55Sratchov 
22616a99293Sratchov 	if (fdpass_peer == NULL)
22716a99293Sratchov 		return NULL;
22836355b88Sratchov 	if (!fdpass_send(fdpass_peer, FDPASS_OPEN_MIDI, num, mode, -1))
229395f8c55Sratchov 		return NULL;
230395f8c55Sratchov 	if (!fdpass_waitret(fdpass_peer, &fd))
231395f8c55Sratchov 		return NULL;
232395f8c55Sratchov 	if (fd < 0)
233395f8c55Sratchov 		return NULL;
234395f8c55Sratchov 	return mio_rmidi_fdopen(fd, mode, 1);
235395f8c55Sratchov }
236395f8c55Sratchov 
237d07fece6Sratchov struct sioctl_hdl *
23836355b88Sratchov fdpass_sioctl_open(int num, unsigned int mode)
239d07fece6Sratchov {
240d07fece6Sratchov 	int fd;
241d07fece6Sratchov 
242d07fece6Sratchov 	if (fdpass_peer == NULL)
243d07fece6Sratchov 		return NULL;
24436355b88Sratchov 	if (!fdpass_send(fdpass_peer, FDPASS_OPEN_CTL, num, mode, -1))
245d07fece6Sratchov 		return NULL;
246d07fece6Sratchov 	if (!fdpass_waitret(fdpass_peer, &fd))
247d07fece6Sratchov 		return NULL;
248d07fece6Sratchov 	if (fd < 0)
249d07fece6Sratchov 		return NULL;
250d07fece6Sratchov 	return sioctl_sun_fdopen(fd, mode, 1);
251d07fece6Sratchov }
252d07fece6Sratchov 
253395f8c55Sratchov void
254395f8c55Sratchov fdpass_in_worker(void *arg)
255395f8c55Sratchov {
256395f8c55Sratchov 	struct fdpass *f = arg;
257395f8c55Sratchov 
258*7b639200Sratchov 	logx(3, "%s: exit", f->file->name);
259395f8c55Sratchov 	fdpass_close(f);
260395f8c55Sratchov 	return;
261395f8c55Sratchov }
262395f8c55Sratchov 
263395f8c55Sratchov void
264395f8c55Sratchov fdpass_in_helper(void *arg)
265395f8c55Sratchov {
26636355b88Sratchov 	int cmd, num, mode, fd;
267395f8c55Sratchov 	struct fdpass *f = arg;
268395f8c55Sratchov 	struct dev *d;
269395f8c55Sratchov 	struct port *p;
270395f8c55Sratchov 
27136355b88Sratchov 	if (!fdpass_recv(f, &cmd, &num, &mode, &fd))
272395f8c55Sratchov 		return;
273395f8c55Sratchov 	switch (cmd) {
274395f8c55Sratchov 	case FDPASS_OPEN_SND:
275395f8c55Sratchov 		d = dev_bynum(num);
276395f8c55Sratchov 		if (d == NULL || !(mode & (SIO_PLAY | SIO_REC))) {
277*7b639200Sratchov 			logx(1, "%s: bad audio device or mode", f->file->name);
278395f8c55Sratchov 			fdpass_close(f);
279395f8c55Sratchov 			return;
280395f8c55Sratchov 		}
28136355b88Sratchov 		fd = sio_sun_getfd(d->path, mode, 1);
282395f8c55Sratchov 		break;
283395f8c55Sratchov 	case FDPASS_OPEN_MIDI:
284395f8c55Sratchov 		p = port_bynum(num);
285395f8c55Sratchov 		if (p == NULL || !(mode & (MIO_IN | MIO_OUT))) {
286*7b639200Sratchov 			logx(1, "%s: bad midi port or mode", f->file->name);
287395f8c55Sratchov 			fdpass_close(f);
288395f8c55Sratchov 			return;
289395f8c55Sratchov 		}
29036355b88Sratchov 		fd = mio_rmidi_getfd(p->path, mode, 1);
291395f8c55Sratchov 		break;
292d07fece6Sratchov 	case FDPASS_OPEN_CTL:
293d07fece6Sratchov 		d = dev_bynum(num);
294d07fece6Sratchov 		if (d == NULL || !(mode & (SIOCTL_READ | SIOCTL_WRITE))) {
295*7b639200Sratchov 			logx(1, "%s: bad control device", f->file->name);
296d07fece6Sratchov 			fdpass_close(f);
297d07fece6Sratchov 			return;
298d07fece6Sratchov 		}
29936355b88Sratchov 		fd = sioctl_sun_getfd(d->path, mode, 1);
300d07fece6Sratchov 		break;
301395f8c55Sratchov 	default:
302395f8c55Sratchov 		fdpass_close(f);
303395f8c55Sratchov 		return;
304395f8c55Sratchov 	}
30536355b88Sratchov 	fdpass_send(f, FDPASS_RETURN, 0, 0, fd);
306395f8c55Sratchov }
307395f8c55Sratchov 
308395f8c55Sratchov void
309395f8c55Sratchov fdpass_out(void *arg)
310395f8c55Sratchov {
311395f8c55Sratchov }
312395f8c55Sratchov 
313395f8c55Sratchov void
314395f8c55Sratchov fdpass_hup(void *arg)
315395f8c55Sratchov {
316395f8c55Sratchov 	struct fdpass *f = arg;
317395f8c55Sratchov 
318*7b639200Sratchov 	logx(3, "%s: hup", f->file->name);
319395f8c55Sratchov 	fdpass_close(f);
320395f8c55Sratchov }
321395f8c55Sratchov 
322395f8c55Sratchov struct fdpass *
323395f8c55Sratchov fdpass_new(int sock, struct fileops *ops)
324395f8c55Sratchov {
325395f8c55Sratchov 	struct fdpass *f;
326395f8c55Sratchov 
327395f8c55Sratchov 	f = xmalloc(sizeof(struct fdpass));
328395f8c55Sratchov 	f->file = file_new(ops, f, ops->name, 1);
329395f8c55Sratchov 	if (f->file == NULL) {
330395f8c55Sratchov 		close(sock);
331c699f4b9Sratchov 		xfree(f);
332395f8c55Sratchov 		return NULL;
333395f8c55Sratchov 	}
334395f8c55Sratchov 	f->fd = sock;
335395f8c55Sratchov 	fdpass_peer = f;
336395f8c55Sratchov 	return f;
337395f8c55Sratchov }
338395f8c55Sratchov 
339395f8c55Sratchov void
340395f8c55Sratchov fdpass_close(struct fdpass *f)
341395f8c55Sratchov {
342395f8c55Sratchov 	fdpass_peer = NULL;
343395f8c55Sratchov 	file_del(f->file);
344395f8c55Sratchov 	close(f->fd);
345395f8c55Sratchov 	xfree(f);
346395f8c55Sratchov }
347395f8c55Sratchov 
348395f8c55Sratchov int
349395f8c55Sratchov fdpass_pollfd(void *arg, struct pollfd *pfd)
350395f8c55Sratchov {
351395f8c55Sratchov 	struct fdpass *f = arg;
352395f8c55Sratchov 
353395f8c55Sratchov 	pfd->fd = f->fd;
354395f8c55Sratchov 	pfd->events = POLLIN;
355395f8c55Sratchov 	return 1;
356395f8c55Sratchov }
357395f8c55Sratchov 
358395f8c55Sratchov int
359395f8c55Sratchov fdpass_revents(void *arg, struct pollfd *pfd)
360395f8c55Sratchov {
361395f8c55Sratchov 	return pfd->revents;
362395f8c55Sratchov }
363