GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/sndiod/fdpass.c Lines: 0 173 0.0 %
Date: 2016-12-06 Branches: 0 89 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: fdpass.c,v 1.3 2016/01/08 16:17:31 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_RETURN		3	/* return after above commands */
36
	unsigned int cmd;		/* one of above */
37
	unsigned int num;		/* audio device or midi port number */
38
	unsigned int mode;		/* SIO_PLAY, SIO_REC, MIO_IN, ... */
39
};
40
41
int fdpass_pollfd(void *, struct pollfd *);
42
int fdpass_revents(void *, struct pollfd *);
43
void fdpass_in_worker(void *);
44
void fdpass_in_helper(void *);
45
void fdpass_out(void *);
46
void fdpass_hup(void *);
47
48
struct fileops worker_fileops = {
49
	"worker",
50
	fdpass_pollfd,
51
	fdpass_revents,
52
	fdpass_in_worker,
53
	fdpass_out,
54
	fdpass_hup
55
};
56
57
struct fileops helper_fileops = {
58
	"helper",
59
	fdpass_pollfd,
60
	fdpass_revents,
61
	fdpass_in_helper,
62
	fdpass_out,
63
	fdpass_hup
64
};
65
66
struct fdpass {
67
	struct file *file;
68
	int fd;
69
} *fdpass_peer = NULL;
70
71
static void
72
fdpass_log(struct fdpass *f)
73
{
74
	log_puts(f->file->name);
75
}
76
77
static int
78
fdpass_send(struct fdpass *f, int cmd, int num, int mode, int fd)
79
{
80
	struct fdpass_msg data;
81
	struct msghdr msg;
82
	struct cmsghdr *cmsg;
83
	union {
84
		struct cmsghdr hdr;
85
		unsigned char buf[CMSG_SPACE(sizeof(int))];
86
	} cmsgbuf;
87
	struct iovec iov;
88
	ssize_t n;
89
90
	data.cmd = cmd;
91
	data.num = num;
92
	data.mode = mode;
93
	iov.iov_base = &data;
94
	iov.iov_len = sizeof(struct fdpass_msg);
95
	memset(&msg, 0, sizeof(msg));
96
	msg.msg_iov = &iov;
97
	msg.msg_iovlen = 1;
98
	if (fd >= 0) {
99
		msg.msg_control = &cmsgbuf.buf;
100
		msg.msg_controllen = sizeof(cmsgbuf.buf);
101
		cmsg = CMSG_FIRSTHDR(&msg);
102
		cmsg->cmsg_len = CMSG_LEN(sizeof(int));
103
		cmsg->cmsg_level = SOL_SOCKET;
104
		cmsg->cmsg_type = SCM_RIGHTS;
105
		*(int *)CMSG_DATA(cmsg) = fd;
106
	}
107
	n = sendmsg(f->fd, &msg, 0);
108
	if (n < 0) {
109
		if (log_level >= 1) {
110
			fdpass_log(f);
111
			log_puts(": sendmsg failed\n");
112
		}
113
		fdpass_close(f);
114
		return 0;
115
	}
116
	if (n != sizeof(struct fdpass_msg)) {
117
		if (log_level >= 1) {
118
			fdpass_log(f);
119
			log_puts(": short write\n");
120
		}
121
		fdpass_close(f);
122
		return 0;
123
	}
124
#ifdef DEBUG
125
	if (log_level >= 3) {
126
		fdpass_log(f);
127
		log_puts(": send: cmd = ");
128
		log_puti(cmd);
129
		log_puts(", num = ");
130
		log_puti(num);
131
		log_puts(", mode = ");
132
		log_puti(mode);
133
		log_puts(", fd = ");
134
		log_puti(fd);
135
		log_puts("\n");
136
	}
137
#endif
138
	if (fd >= 0)
139
		close(fd);
140
	return 1;
141
}
142
143
static int
144
fdpass_recv(struct fdpass *f, int *cmd, int *num, int *mode, int *fd)
145
{
146
	struct fdpass_msg data;
147
	struct msghdr msg;
148
	struct cmsghdr *cmsg;
149
	union {
150
		struct cmsghdr hdr;
151
		unsigned char buf[CMSG_SPACE(sizeof(int))];
152
	} cmsgbuf;
153
	struct iovec iov;
154
	ssize_t n;
155
156
	iov.iov_base = &data;
157
	iov.iov_len = sizeof(struct fdpass_msg);
158
	memset(&msg, 0, sizeof(msg));
159
	msg.msg_control = &cmsgbuf.buf;
160
	msg.msg_controllen = sizeof(cmsgbuf.buf);
161
	msg.msg_iov = &iov;
162
	msg.msg_iovlen = 1;
163
	n = recvmsg(f->fd, &msg, MSG_WAITALL);
164
	if (n < 0 && errno == EMSGSIZE) {
165
		if (log_level >= 1) {
166
			fdpass_log(f);
167
			log_puts(": out of fds\n");
168
		}
169
		/*
170
		 * ancillary data (ie the fd) is discarded,
171
		 * retrieve the message
172
		 */
173
		n = recvmsg(f->fd, &msg, MSG_WAITALL);
174
	}
175
	if (n < 0) {
176
		if (log_level >= 1) {
177
			fdpass_log(f);
178
			log_puts(": recvmsg failed\n");
179
		}
180
		fdpass_close(f);
181
		return 0;
182
	}
183
	if (n == 0) {
184
		if (log_level >= 3) {
185
			fdpass_log(f);
186
			log_puts(": recvmsg eof\n");
187
		}
188
		fdpass_close(f);
189
		return 0;
190
	}
191
	if (msg.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) {
192
		if (log_level >= 1) {
193
			fdpass_log(f);
194
			log_puts(": truncated\n");
195
		}
196
		fdpass_close(f);
197
		return 0;
198
	}
199
	cmsg = CMSG_FIRSTHDR(&msg);
200
	for (;;) {
201
		if (cmsg == NULL) {
202
			*fd = -1;
203
			break;
204
		}
205
		if (cmsg->cmsg_len == CMSG_LEN(sizeof(int)) &&
206
		    cmsg->cmsg_level == SOL_SOCKET &&
207
		    cmsg->cmsg_type == SCM_RIGHTS) {
208
			*fd = *(int *)CMSG_DATA(cmsg);
209
			break;
210
		}
211
		cmsg = CMSG_NXTHDR(&msg, cmsg);
212
	}
213
	*cmd = data.cmd;
214
	*num = data.num;
215
	*mode = data.mode;
216
#ifdef DEBUG
217
	if (log_level >= 3) {
218
		fdpass_log(f);
219
		log_puts(": recv: cmd = ");
220
		log_puti(*cmd);
221
		log_puts(", num = ");
222
		log_puti(*num);
223
		log_puts(", mode = ");
224
		log_puti(*mode);
225
		log_puts(", fd = ");
226
		log_puti(*fd);
227
		log_puts("\n");
228
	}
229
#endif
230
	return 1;
231
}
232
233
static int
234
fdpass_waitret(struct fdpass *f, int *retfd)
235
{
236
	int cmd, unused;
237
238
	if (!fdpass_recv(fdpass_peer, &cmd, &unused, &unused, retfd))
239
		return 0;
240
	if (cmd != FDPASS_RETURN) {
241
		if (log_level >= 1) {
242
			fdpass_log(f);
243
			log_puts(": expected RETURN message\n");
244
		}
245
		fdpass_close(f);
246
		return 0;
247
	}
248
	return 1;
249
}
250
251
struct sio_hdl *
252
fdpass_sio_open(int num, unsigned int mode)
253
{
254
	int fd;
255
256
	if (!fdpass_send(fdpass_peer, FDPASS_OPEN_SND, num, mode, -1))
257
		return NULL;
258
	if (!fdpass_waitret(fdpass_peer, &fd))
259
		return NULL;
260
	if (fd < 0)
261
		return NULL;
262
	return sio_sun_fdopen(fd, mode, 1);
263
}
264
265
struct mio_hdl *
266
fdpass_mio_open(int num, unsigned int mode)
267
{
268
	int fd;
269
270
	if (!fdpass_send(fdpass_peer, FDPASS_OPEN_MIDI, num, mode, -1))
271
		return NULL;
272
	if (!fdpass_waitret(fdpass_peer, &fd))
273
		return NULL;
274
	if (fd < 0)
275
		return NULL;
276
	return mio_rmidi_fdopen(fd, mode, 1);
277
}
278
279
void
280
fdpass_in_worker(void *arg)
281
{
282
	struct fdpass *f = arg;
283
284
	if (log_level >= 3) {
285
		fdpass_log(f);
286
		log_puts(": exit\n");
287
	}
288
	fdpass_close(f);
289
	return;
290
}
291
292
void
293
fdpass_in_helper(void *arg)
294
{
295
	int cmd, num, mode, fd;
296
	struct fdpass *f = arg;
297
	struct dev *d;
298
	struct port *p;
299
300
	if (!fdpass_recv(f, &cmd, &num, &mode, &fd))
301
		return;
302
	switch (cmd) {
303
	case FDPASS_OPEN_SND:
304
		d = dev_bynum(num);
305
		if (d == NULL || !(mode & (SIO_PLAY | SIO_REC))) {
306
			if (log_level >= 1) {
307
				fdpass_log(f);
308
				log_puts(": bad audio device or mode\n");
309
			}
310
			fdpass_close(f);
311
			return;
312
		}
313
		fd = sio_sun_getfd(d->path, mode, 1);
314
		break;
315
	case FDPASS_OPEN_MIDI:
316
		p = port_bynum(num);
317
		if (p == NULL || !(mode & (MIO_IN | MIO_OUT))) {
318
			if (log_level >= 1) {
319
				fdpass_log(f);
320
				log_puts(": bad midi port or mode\n");
321
			}
322
			fdpass_close(f);
323
			return;
324
		}
325
		fd = mio_rmidi_getfd(p->path, mode, 1);
326
		break;
327
	default:
328
		fdpass_close(f);
329
		return;
330
	}
331
	fdpass_send(f, FDPASS_RETURN, 0, 0, fd);
332
}
333
334
void
335
fdpass_out(void *arg)
336
{
337
}
338
339
void
340
fdpass_hup(void *arg)
341
{
342
	struct fdpass *f = arg;
343
344
	if (log_level >= 3) {
345
		fdpass_log(f);
346
		log_puts(": hup\n");
347
	}
348
	fdpass_close(f);
349
}
350
351
struct fdpass *
352
fdpass_new(int sock, struct fileops *ops)
353
{
354
	struct fdpass *f;
355
356
	f = xmalloc(sizeof(struct fdpass));
357
	f->file = file_new(ops, f, ops->name, 1);
358
	if (f->file == NULL) {
359
		close(sock);
360
		return NULL;
361
	}
362
	f->fd = sock;
363
	fdpass_peer = f;
364
	return f;
365
}
366
367
void
368
fdpass_close(struct fdpass *f)
369
{
370
	fdpass_peer = NULL;
371
	file_del(f->file);
372
	close(f->fd);
373
	xfree(f);
374
}
375
376
int
377
fdpass_pollfd(void *arg, struct pollfd *pfd)
378
{
379
	struct fdpass *f = arg;
380
381
	pfd->fd = f->fd;
382
	pfd->events = POLLIN;
383
	return 1;
384
}
385
386
int
387
fdpass_revents(void *arg, struct pollfd *pfd)
388
{
389
	return pfd->revents;
390
}