1 /* $OpenBSD: fdpass.c,v 1.11 2021/11/01 14:43:25 ratchov Exp $ */
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_OPEN_CTL 2 /* open an audio control device */
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
fdpass_log(struct fdpass * f)73 fdpass_log(struct fdpass *f)
74 {
75 log_puts(f->file->name);
76 }
77
78 static int
fdpass_send(struct fdpass * f,int cmd,int num,int mode,int fd)79 fdpass_send(struct fdpass *f, int cmd, int num, int mode, int fd)
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);
109 if (n == -1) {
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
fdpass_recv(struct fdpass * f,int * cmd,int * num,int * mode,int * fd)145 fdpass_recv(struct fdpass *f, int *cmd, int *num, int *mode, int *fd)
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);
165 if (n == -1 && errno == EMSGSIZE) {
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 }
176 if (n == -1) {
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) {
185 if (log_level >= 3) {
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
fdpass_waitret(struct fdpass * f,int * retfd)235 fdpass_waitret(struct fdpass *f, int *retfd)
236 {
237 int cmd, unused;
238
239 if (!fdpass_recv(fdpass_peer, &cmd, &unused, &unused, retfd))
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 *
fdpass_sio_open(int num,unsigned int mode)253 fdpass_sio_open(int num, unsigned int mode)
254 {
255 int fd;
256
257 if (fdpass_peer == NULL)
258 return NULL;
259 if (!fdpass_send(fdpass_peer, FDPASS_OPEN_SND, num, mode, -1))
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 *
fdpass_mio_open(int num,unsigned int mode)269 fdpass_mio_open(int num, unsigned int mode)
270 {
271 int fd;
272
273 if (fdpass_peer == NULL)
274 return NULL;
275 if (!fdpass_send(fdpass_peer, FDPASS_OPEN_MIDI, num, mode, -1))
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
284 struct sioctl_hdl *
fdpass_sioctl_open(int num,unsigned int mode)285 fdpass_sioctl_open(int num, unsigned int mode)
286 {
287 int fd;
288
289 if (fdpass_peer == NULL)
290 return NULL;
291 if (!fdpass_send(fdpass_peer, FDPASS_OPEN_CTL, num, mode, -1))
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
300 void
fdpass_in_worker(void * arg)301 fdpass_in_worker(void *arg)
302 {
303 struct fdpass *f = arg;
304
305 if (log_level >= 3) {
306 fdpass_log(f);
307 log_puts(": exit\n");
308 }
309 fdpass_close(f);
310 return;
311 }
312
313 void
fdpass_in_helper(void * arg)314 fdpass_in_helper(void *arg)
315 {
316 int cmd, num, mode, fd;
317 struct fdpass *f = arg;
318 struct dev *d;
319 struct port *p;
320
321 if (!fdpass_recv(f, &cmd, &num, &mode, &fd))
322 return;
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 }
334 fd = sio_sun_getfd(d->path, mode, 1);
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 }
346 fd = mio_rmidi_getfd(p->path, mode, 1);
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 }
358 fd = sioctl_sun_getfd(d->path, mode, 1);
359 break;
360 default:
361 fdpass_close(f);
362 return;
363 }
364 fdpass_send(f, FDPASS_RETURN, 0, 0, fd);
365 }
366
367 void
fdpass_out(void * arg)368 fdpass_out(void *arg)
369 {
370 }
371
372 void
fdpass_hup(void * arg)373 fdpass_hup(void *arg)
374 {
375 struct fdpass *f = arg;
376
377 if (log_level >= 3) {
378 fdpass_log(f);
379 log_puts(": hup\n");
380 }
381 fdpass_close(f);
382 }
383
384 struct fdpass *
fdpass_new(int sock,struct fileops * ops)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);
393 xfree(f);
394 return NULL;
395 }
396 f->fd = sock;
397 fdpass_peer = f;
398 return f;
399 }
400
401 void
fdpass_close(struct fdpass * f)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
fdpass_pollfd(void * arg,struct pollfd * pfd)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
fdpass_revents(void * arg,struct pollfd * pfd)421 fdpass_revents(void *arg, struct pollfd *pfd)
422 {
423 return pfd->revents;
424 }
425