1*36355b88Sratchov /* $OpenBSD: fdpass.c,v 1.11 2021/11/01 14:43:25 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 void
fdpass_log(struct fdpass * f)73395f8c55Sratchov fdpass_log(struct fdpass *f)
74395f8c55Sratchov {
75395f8c55Sratchov log_puts(f->file->name);
76395f8c55Sratchov }
77395f8c55Sratchov
78395f8c55Sratchov static int
fdpass_send(struct fdpass * f,int cmd,int num,int mode,int fd)79*36355b88Sratchov fdpass_send(struct fdpass *f, int cmd, int num, int mode, int fd)
80395f8c55Sratchov {
81395f8c55Sratchov struct fdpass_msg data;
82395f8c55Sratchov struct msghdr msg;
83395f8c55Sratchov struct cmsghdr *cmsg;
84395f8c55Sratchov union {
85395f8c55Sratchov struct cmsghdr hdr;
86395f8c55Sratchov unsigned char buf[CMSG_SPACE(sizeof(int))];
87395f8c55Sratchov } cmsgbuf;
88395f8c55Sratchov struct iovec iov;
89395f8c55Sratchov ssize_t n;
90395f8c55Sratchov
91395f8c55Sratchov data.cmd = cmd;
92395f8c55Sratchov data.num = num;
93395f8c55Sratchov data.mode = mode;
94395f8c55Sratchov iov.iov_base = &data;
95395f8c55Sratchov iov.iov_len = sizeof(struct fdpass_msg);
96395f8c55Sratchov memset(&msg, 0, sizeof(msg));
97395f8c55Sratchov msg.msg_iov = &iov;
98395f8c55Sratchov msg.msg_iovlen = 1;
99395f8c55Sratchov if (fd >= 0) {
100395f8c55Sratchov msg.msg_control = &cmsgbuf.buf;
101395f8c55Sratchov msg.msg_controllen = sizeof(cmsgbuf.buf);
102395f8c55Sratchov cmsg = CMSG_FIRSTHDR(&msg);
103395f8c55Sratchov cmsg->cmsg_len = CMSG_LEN(sizeof(int));
104395f8c55Sratchov cmsg->cmsg_level = SOL_SOCKET;
105395f8c55Sratchov cmsg->cmsg_type = SCM_RIGHTS;
106395f8c55Sratchov *(int *)CMSG_DATA(cmsg) = fd;
107395f8c55Sratchov }
108395f8c55Sratchov n = sendmsg(f->fd, &msg, 0);
1093aaa63ebSderaadt if (n == -1) {
110395f8c55Sratchov if (log_level >= 1) {
111395f8c55Sratchov fdpass_log(f);
112395f8c55Sratchov log_puts(": sendmsg failed\n");
113395f8c55Sratchov }
114395f8c55Sratchov fdpass_close(f);
115395f8c55Sratchov return 0;
116395f8c55Sratchov }
117395f8c55Sratchov if (n != sizeof(struct fdpass_msg)) {
118395f8c55Sratchov if (log_level >= 1) {
119395f8c55Sratchov fdpass_log(f);
120395f8c55Sratchov log_puts(": short write\n");
121395f8c55Sratchov }
122395f8c55Sratchov fdpass_close(f);
123395f8c55Sratchov return 0;
124395f8c55Sratchov }
125395f8c55Sratchov #ifdef DEBUG
126395f8c55Sratchov if (log_level >= 3) {
127395f8c55Sratchov fdpass_log(f);
128395f8c55Sratchov log_puts(": send: cmd = ");
129395f8c55Sratchov log_puti(cmd);
130395f8c55Sratchov log_puts(", num = ");
131395f8c55Sratchov log_puti(num);
132395f8c55Sratchov log_puts(", mode = ");
133395f8c55Sratchov log_puti(mode);
134395f8c55Sratchov log_puts(", fd = ");
135395f8c55Sratchov log_puti(fd);
136395f8c55Sratchov log_puts("\n");
137395f8c55Sratchov }
138395f8c55Sratchov #endif
139395f8c55Sratchov if (fd >= 0)
140395f8c55Sratchov close(fd);
141395f8c55Sratchov return 1;
142395f8c55Sratchov }
143395f8c55Sratchov
144395f8c55Sratchov static int
fdpass_recv(struct fdpass * f,int * cmd,int * num,int * mode,int * fd)145*36355b88Sratchov fdpass_recv(struct fdpass *f, int *cmd, int *num, int *mode, int *fd)
146395f8c55Sratchov {
147395f8c55Sratchov struct fdpass_msg data;
148395f8c55Sratchov struct msghdr msg;
149395f8c55Sratchov struct cmsghdr *cmsg;
150395f8c55Sratchov union {
151395f8c55Sratchov struct cmsghdr hdr;
152395f8c55Sratchov unsigned char buf[CMSG_SPACE(sizeof(int))];
153395f8c55Sratchov } cmsgbuf;
154395f8c55Sratchov struct iovec iov;
155395f8c55Sratchov ssize_t n;
156395f8c55Sratchov
157395f8c55Sratchov iov.iov_base = &data;
158395f8c55Sratchov iov.iov_len = sizeof(struct fdpass_msg);
159395f8c55Sratchov memset(&msg, 0, sizeof(msg));
160395f8c55Sratchov msg.msg_control = &cmsgbuf.buf;
161395f8c55Sratchov msg.msg_controllen = sizeof(cmsgbuf.buf);
162395f8c55Sratchov msg.msg_iov = &iov;
163395f8c55Sratchov msg.msg_iovlen = 1;
164395f8c55Sratchov n = recvmsg(f->fd, &msg, MSG_WAITALL);
1653aaa63ebSderaadt if (n == -1 && errno == EMSGSIZE) {
166395f8c55Sratchov if (log_level >= 1) {
167395f8c55Sratchov fdpass_log(f);
168395f8c55Sratchov log_puts(": out of fds\n");
169395f8c55Sratchov }
170395f8c55Sratchov /*
171395f8c55Sratchov * ancillary data (ie the fd) is discarded,
172395f8c55Sratchov * retrieve the message
173395f8c55Sratchov */
174395f8c55Sratchov n = recvmsg(f->fd, &msg, MSG_WAITALL);
175395f8c55Sratchov }
1763aaa63ebSderaadt if (n == -1) {
177395f8c55Sratchov if (log_level >= 1) {
178395f8c55Sratchov fdpass_log(f);
179395f8c55Sratchov log_puts(": recvmsg failed\n");
180395f8c55Sratchov }
181395f8c55Sratchov fdpass_close(f);
182395f8c55Sratchov return 0;
183395f8c55Sratchov }
184395f8c55Sratchov if (n == 0) {
185c28c4b14Sratchov if (log_level >= 3) {
186395f8c55Sratchov fdpass_log(f);
187395f8c55Sratchov log_puts(": recvmsg eof\n");
188395f8c55Sratchov }
189395f8c55Sratchov fdpass_close(f);
190395f8c55Sratchov return 0;
191395f8c55Sratchov }
192395f8c55Sratchov if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) {
193395f8c55Sratchov if (log_level >= 1) {
194395f8c55Sratchov fdpass_log(f);
195395f8c55Sratchov log_puts(": truncated\n");
196395f8c55Sratchov }
197395f8c55Sratchov fdpass_close(f);
198395f8c55Sratchov return 0;
199395f8c55Sratchov }
200395f8c55Sratchov cmsg = CMSG_FIRSTHDR(&msg);
201395f8c55Sratchov for (;;) {
202395f8c55Sratchov if (cmsg == NULL) {
203395f8c55Sratchov *fd = -1;
204395f8c55Sratchov break;
205395f8c55Sratchov }
206395f8c55Sratchov if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)) &&
207395f8c55Sratchov cmsg->cmsg_level == SOL_SOCKET &&
208395f8c55Sratchov cmsg->cmsg_type == SCM_RIGHTS) {
209395f8c55Sratchov *fd = *(int *)CMSG_DATA(cmsg);
210395f8c55Sratchov break;
211395f8c55Sratchov }
212395f8c55Sratchov cmsg = CMSG_NXTHDR(&msg, cmsg);
213395f8c55Sratchov }
214395f8c55Sratchov *cmd = data.cmd;
215395f8c55Sratchov *num = data.num;
216395f8c55Sratchov *mode = data.mode;
217395f8c55Sratchov #ifdef DEBUG
218395f8c55Sratchov if (log_level >= 3) {
219395f8c55Sratchov fdpass_log(f);
220395f8c55Sratchov log_puts(": recv: cmd = ");
221395f8c55Sratchov log_puti(*cmd);
222395f8c55Sratchov log_puts(", num = ");
223395f8c55Sratchov log_puti(*num);
224395f8c55Sratchov log_puts(", mode = ");
225395f8c55Sratchov log_puti(*mode);
226395f8c55Sratchov log_puts(", fd = ");
227395f8c55Sratchov log_puti(*fd);
228395f8c55Sratchov log_puts("\n");
229395f8c55Sratchov }
230395f8c55Sratchov #endif
231395f8c55Sratchov return 1;
232395f8c55Sratchov }
233395f8c55Sratchov
234395f8c55Sratchov static int
fdpass_waitret(struct fdpass * f,int * retfd)235395f8c55Sratchov fdpass_waitret(struct fdpass *f, int *retfd)
236395f8c55Sratchov {
237395f8c55Sratchov int cmd, unused;
238395f8c55Sratchov
239*36355b88Sratchov if (!fdpass_recv(fdpass_peer, &cmd, &unused, &unused, retfd))
240395f8c55Sratchov return 0;
241395f8c55Sratchov if (cmd != FDPASS_RETURN) {
242395f8c55Sratchov if (log_level >= 1) {
243395f8c55Sratchov fdpass_log(f);
244395f8c55Sratchov log_puts(": expected RETURN message\n");
245395f8c55Sratchov }
246395f8c55Sratchov fdpass_close(f);
247395f8c55Sratchov return 0;
248395f8c55Sratchov }
249395f8c55Sratchov return 1;
250395f8c55Sratchov }
251395f8c55Sratchov
252395f8c55Sratchov struct sio_hdl *
fdpass_sio_open(int num,unsigned int mode)253*36355b88Sratchov fdpass_sio_open(int num, unsigned int mode)
254395f8c55Sratchov {
255395f8c55Sratchov int fd;
256395f8c55Sratchov
25716a99293Sratchov if (fdpass_peer == NULL)
25816a99293Sratchov return NULL;
259*36355b88Sratchov if (!fdpass_send(fdpass_peer, FDPASS_OPEN_SND, num, mode, -1))
260395f8c55Sratchov return NULL;
261395f8c55Sratchov if (!fdpass_waitret(fdpass_peer, &fd))
262395f8c55Sratchov return NULL;
263395f8c55Sratchov if (fd < 0)
264395f8c55Sratchov return NULL;
265395f8c55Sratchov return sio_sun_fdopen(fd, mode, 1);
266395f8c55Sratchov }
267395f8c55Sratchov
268395f8c55Sratchov struct mio_hdl *
fdpass_mio_open(int num,unsigned int mode)269*36355b88Sratchov fdpass_mio_open(int num, unsigned int mode)
270395f8c55Sratchov {
271395f8c55Sratchov int fd;
272395f8c55Sratchov
27316a99293Sratchov if (fdpass_peer == NULL)
27416a99293Sratchov return NULL;
275*36355b88Sratchov if (!fdpass_send(fdpass_peer, FDPASS_OPEN_MIDI, num, mode, -1))
276395f8c55Sratchov return NULL;
277395f8c55Sratchov if (!fdpass_waitret(fdpass_peer, &fd))
278395f8c55Sratchov return NULL;
279395f8c55Sratchov if (fd < 0)
280395f8c55Sratchov return NULL;
281395f8c55Sratchov return mio_rmidi_fdopen(fd, mode, 1);
282395f8c55Sratchov }
283395f8c55Sratchov
284d07fece6Sratchov struct sioctl_hdl *
fdpass_sioctl_open(int num,unsigned int mode)285*36355b88Sratchov fdpass_sioctl_open(int num, unsigned int mode)
286d07fece6Sratchov {
287d07fece6Sratchov int fd;
288d07fece6Sratchov
289d07fece6Sratchov if (fdpass_peer == NULL)
290d07fece6Sratchov return NULL;
291*36355b88Sratchov if (!fdpass_send(fdpass_peer, FDPASS_OPEN_CTL, num, mode, -1))
292d07fece6Sratchov return NULL;
293d07fece6Sratchov if (!fdpass_waitret(fdpass_peer, &fd))
294d07fece6Sratchov return NULL;
295d07fece6Sratchov if (fd < 0)
296d07fece6Sratchov return NULL;
297d07fece6Sratchov return sioctl_sun_fdopen(fd, mode, 1);
298d07fece6Sratchov }
299d07fece6Sratchov
300395f8c55Sratchov void
fdpass_in_worker(void * arg)301395f8c55Sratchov fdpass_in_worker(void *arg)
302395f8c55Sratchov {
303395f8c55Sratchov struct fdpass *f = arg;
304395f8c55Sratchov
305c28c4b14Sratchov if (log_level >= 3) {
306395f8c55Sratchov fdpass_log(f);
307395f8c55Sratchov log_puts(": exit\n");
308395f8c55Sratchov }
309395f8c55Sratchov fdpass_close(f);
310395f8c55Sratchov return;
311395f8c55Sratchov }
312395f8c55Sratchov
313395f8c55Sratchov void
fdpass_in_helper(void * arg)314395f8c55Sratchov fdpass_in_helper(void *arg)
315395f8c55Sratchov {
316*36355b88Sratchov int cmd, num, mode, fd;
317395f8c55Sratchov struct fdpass *f = arg;
318395f8c55Sratchov struct dev *d;
319395f8c55Sratchov struct port *p;
320395f8c55Sratchov
321*36355b88Sratchov if (!fdpass_recv(f, &cmd, &num, &mode, &fd))
322395f8c55Sratchov return;
323395f8c55Sratchov switch (cmd) {
324395f8c55Sratchov case FDPASS_OPEN_SND:
325395f8c55Sratchov d = dev_bynum(num);
326395f8c55Sratchov if (d == NULL || !(mode & (SIO_PLAY | SIO_REC))) {
327395f8c55Sratchov if (log_level >= 1) {
328395f8c55Sratchov fdpass_log(f);
329395f8c55Sratchov log_puts(": bad audio device or mode\n");
330395f8c55Sratchov }
331395f8c55Sratchov fdpass_close(f);
332395f8c55Sratchov return;
333395f8c55Sratchov }
334*36355b88Sratchov fd = sio_sun_getfd(d->path, mode, 1);
335395f8c55Sratchov break;
336395f8c55Sratchov case FDPASS_OPEN_MIDI:
337395f8c55Sratchov p = port_bynum(num);
338395f8c55Sratchov if (p == NULL || !(mode & (MIO_IN | MIO_OUT))) {
339395f8c55Sratchov if (log_level >= 1) {
340395f8c55Sratchov fdpass_log(f);
341395f8c55Sratchov log_puts(": bad midi port or mode\n");
342395f8c55Sratchov }
343395f8c55Sratchov fdpass_close(f);
344395f8c55Sratchov return;
345395f8c55Sratchov }
346*36355b88Sratchov fd = mio_rmidi_getfd(p->path, mode, 1);
347395f8c55Sratchov break;
348d07fece6Sratchov case FDPASS_OPEN_CTL:
349d07fece6Sratchov d = dev_bynum(num);
350d07fece6Sratchov if (d == NULL || !(mode & (SIOCTL_READ | SIOCTL_WRITE))) {
351d07fece6Sratchov if (log_level >= 1) {
352d07fece6Sratchov fdpass_log(f);
353d07fece6Sratchov log_puts(": bad audio control device\n");
354d07fece6Sratchov }
355d07fece6Sratchov fdpass_close(f);
356d07fece6Sratchov return;
357d07fece6Sratchov }
358*36355b88Sratchov fd = sioctl_sun_getfd(d->path, mode, 1);
359d07fece6Sratchov break;
360395f8c55Sratchov default:
361395f8c55Sratchov fdpass_close(f);
362395f8c55Sratchov return;
363395f8c55Sratchov }
364*36355b88Sratchov fdpass_send(f, FDPASS_RETURN, 0, 0, fd);
365395f8c55Sratchov }
366395f8c55Sratchov
367395f8c55Sratchov void
fdpass_out(void * arg)368395f8c55Sratchov fdpass_out(void *arg)
369395f8c55Sratchov {
370395f8c55Sratchov }
371395f8c55Sratchov
372395f8c55Sratchov void
fdpass_hup(void * arg)373395f8c55Sratchov fdpass_hup(void *arg)
374395f8c55Sratchov {
375395f8c55Sratchov struct fdpass *f = arg;
376395f8c55Sratchov
377c28c4b14Sratchov if (log_level >= 3) {
378395f8c55Sratchov fdpass_log(f);
379395f8c55Sratchov log_puts(": hup\n");
380395f8c55Sratchov }
381395f8c55Sratchov fdpass_close(f);
382395f8c55Sratchov }
383395f8c55Sratchov
384395f8c55Sratchov struct fdpass *
fdpass_new(int sock,struct fileops * ops)385395f8c55Sratchov fdpass_new(int sock, struct fileops *ops)
386395f8c55Sratchov {
387395f8c55Sratchov struct fdpass *f;
388395f8c55Sratchov
389395f8c55Sratchov f = xmalloc(sizeof(struct fdpass));
390395f8c55Sratchov f->file = file_new(ops, f, ops->name, 1);
391395f8c55Sratchov if (f->file == NULL) {
392395f8c55Sratchov close(sock);
393c699f4b9Sratchov xfree(f);
394395f8c55Sratchov return NULL;
395395f8c55Sratchov }
396395f8c55Sratchov f->fd = sock;
397395f8c55Sratchov fdpass_peer = f;
398395f8c55Sratchov return f;
399395f8c55Sratchov }
400395f8c55Sratchov
401395f8c55Sratchov void
fdpass_close(struct fdpass * f)402395f8c55Sratchov fdpass_close(struct fdpass *f)
403395f8c55Sratchov {
404395f8c55Sratchov fdpass_peer = NULL;
405395f8c55Sratchov file_del(f->file);
406395f8c55Sratchov close(f->fd);
407395f8c55Sratchov xfree(f);
408395f8c55Sratchov }
409395f8c55Sratchov
410395f8c55Sratchov int
fdpass_pollfd(void * arg,struct pollfd * pfd)411395f8c55Sratchov fdpass_pollfd(void *arg, struct pollfd *pfd)
412395f8c55Sratchov {
413395f8c55Sratchov struct fdpass *f = arg;
414395f8c55Sratchov
415395f8c55Sratchov pfd->fd = f->fd;
416395f8c55Sratchov pfd->events = POLLIN;
417395f8c55Sratchov return 1;
418395f8c55Sratchov }
419395f8c55Sratchov
420395f8c55Sratchov int
fdpass_revents(void * arg,struct pollfd * pfd)421395f8c55Sratchov fdpass_revents(void *arg, struct pollfd *pfd)
422395f8c55Sratchov {
423395f8c55Sratchov return pfd->revents;
424395f8c55Sratchov }
425