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