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

Line Branch Exec Source
1
/*	$OpenBSD: sndiod.c,v 1.31 2016/03/23 06:16:35 ratchov Exp $	*/
2
/*
3
 * Copyright (c) 2008-2012 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/stat.h>
18
#include <sys/types.h>
19
#include <sys/resource.h>
20
#include <sys/socket.h>
21
22
#include <err.h>
23
#include <errno.h>
24
#include <fcntl.h>
25
#include <grp.h>
26
#include <limits.h>
27
#include <pwd.h>
28
#include <signal.h>
29
#include <sndio.h>
30
#include <stdio.h>
31
#include <stdlib.h>
32
#include <string.h>
33
#include <unistd.h>
34
35
#include "amsg.h"
36
#include "defs.h"
37
#include "dev.h"
38
#include "fdpass.h"
39
#include "file.h"
40
#include "listen.h"
41
#include "midi.h"
42
#include "opt.h"
43
#include "sock.h"
44
#include "utils.h"
45
46
/*
47
 * unprivileged user name
48
 */
49
#ifndef SNDIO_USER
50
#define SNDIO_USER	"_sndio"
51
#endif
52
53
/*
54
 * privileged user name
55
 */
56
#ifndef SNDIO_PRIV_USER
57
#define SNDIO_PRIV_USER	"_sndiop"
58
#endif
59
60
/*
61
 * priority when run as root
62
 */
63
#ifndef SNDIO_PRIO
64
#define SNDIO_PRIO	(-20)
65
#endif
66
67
/*
68
 * sample rate if no ``-r'' is used
69
 */
70
#ifndef DEFAULT_RATE
71
#define DEFAULT_RATE	48000
72
#endif
73
74
/*
75
 * block size if neither ``-z'' nor ``-b'' is used
76
 */
77
#ifndef DEFAULT_ROUND
78
#define DEFAULT_ROUND	960
79
#endif
80
81
/*
82
 * buffer size if neither ``-z'' nor ``-b'' is used
83
 */
84
#ifndef DEFAULT_BUFSZ
85
#define DEFAULT_BUFSZ	7680
86
#endif
87
88
/*
89
 * default device in server mode
90
 */
91
#ifndef DEFAULT_DEV
92
#define DEFAULT_DEV "rsnd/0"
93
#endif
94
95
void sigint(int);
96
void opt_ch(int *, int *);
97
void opt_enc(struct aparams *);
98
int opt_mmc(void);
99
int opt_onoff(void);
100
int getword(char *, char **);
101
unsigned int opt_mode(void);
102
void getbasepath(char *);
103
void setsig(void);
104
void unsetsig(void);
105
struct dev *mkdev(char *, struct aparams *,
106
    int, int, int, int, int, int);
107
struct port *mkport(char *, int);
108
struct opt *mkopt(char *, struct dev *,
109
    int, int, int, int, int, int, int, int);
110
111
unsigned int log_level = 0;
112
volatile sig_atomic_t quit_flag = 0;
113
114
char usagestr[] = "usage: sndiod [-d] [-a flag] [-b nframes] "
115
    "[-C min:max] [-c min:max] [-e enc]\n\t"
116
    "[-f device] [-j flag] [-L addr] [-m mode] [-q port] [-r rate]\n\t"
117
    "[-s name] [-t mode] [-U unit] [-v volume] [-w flag] [-z nframes]\n";
118
119
/*
120
 * SIGINT handler, it raises the quit flag. If the flag is already set,
121
 * that means that the last SIGINT was not handled, because the process
122
 * is blocked somewhere, so exit.
123
 */
124
void
125
sigint(int s)
126
{
127
	if (quit_flag)
128
		_exit(1);
129
	quit_flag = 1;
130
}
131
132
void
133
opt_ch(int *rcmin, int *rcmax)
134
{
135
	char *next, *end;
136
	long cmin, cmax;
137
138
	errno = 0;
139
	cmin = strtol(optarg, &next, 10);
140
	if (next == optarg || *next != ':')
141
		goto failed;
142
	cmax = strtol(++next, &end, 10);
143
	if (end == next || *end != '\0')
144
		goto failed;
145
	if (cmin < 0 || cmax < cmin || cmax >= NCHAN_MAX)
146
		goto failed;
147
	*rcmin = cmin;
148
	*rcmax = cmax;
149
	return;
150
failed:
151
	errx(1, "%s: bad channel range", optarg);
152
}
153
154
void
155
opt_enc(struct aparams *par)
156
{
157
	int len;
158
159
	len = aparams_strtoenc(par, optarg);
160
	if (len == 0 || optarg[len] != '\0')
161
		errx(1, "%s: bad encoding", optarg);
162
}
163
164
int
165
opt_mmc(void)
166
{
167
	if (strcmp("off", optarg) == 0)
168
		return 0;
169
	if (strcmp("slave", optarg) == 0)
170
		return 1;
171
	errx(1, "%s: off/slave expected", optarg);
172
}
173
174
int
175
opt_onoff(void)
176
{
177
	if (strcmp("off", optarg) == 0)
178
		return 0;
179
	if (strcmp("on", optarg) == 0)
180
		return 1;
181
	errx(1, "%s: on/off expected", optarg);
182
}
183
184
int
185
getword(char *word, char **str)
186
{
187
	char *p = *str;
188
189
	for (;;) {
190
		if (*word == '\0')
191
			break;
192
		if (*word++ != *p++)
193
			return 0;
194
	}
195
	if (*p == ',' || *p == '\0') {
196
		*str = p;
197
		return 1;
198
	}
199
	return 0;
200
}
201
202
unsigned int
203
opt_mode(void)
204
{
205
	unsigned int mode = 0;
206
	char *p = optarg;
207
208
	for (;;) {
209
		if (getword("play", &p)) {
210
			mode |= MODE_PLAY;
211
		} else if (getword("rec", &p)) {
212
			mode |= MODE_REC;
213
		} else if (getword("mon", &p)) {
214
			mode |= MODE_MON;
215
		} else if (getword("midi", &p)) {
216
			mode |= MODE_MIDIMASK;
217
		} else
218
			errx(1, "%s: bad mode", optarg);
219
		if (*p == '\0')
220
			break;
221
		p++;
222
	}
223
	if (mode == 0)
224
		errx(1, "empty mode");
225
	return mode;
226
}
227
228
void
229
setsig(void)
230
{
231
	struct sigaction sa;
232
233
	quit_flag = 0;
234
	sigfillset(&sa.sa_mask);
235
	sa.sa_flags = SA_RESTART;
236
	sa.sa_handler = sigint;
237
	if (sigaction(SIGINT, &sa, NULL) < 0)
238
		err(1, "sigaction(int) failed");
239
	if (sigaction(SIGTERM, &sa, NULL) < 0)
240
		err(1, "sigaction(term) failed");
241
	if (sigaction(SIGHUP, &sa, NULL) < 0)
242
		err(1, "sigaction(hup) failed");
243
}
244
245
void
246
unsetsig(void)
247
{
248
	struct sigaction sa;
249
250
	sigfillset(&sa.sa_mask);
251
	sa.sa_flags = SA_RESTART;
252
	sa.sa_handler = SIG_DFL;
253
	if (sigaction(SIGHUP, &sa, NULL) < 0)
254
		err(1, "unsetsig(hup): sigaction failed");
255
	if (sigaction(SIGTERM, &sa, NULL) < 0)
256
		err(1, "unsetsig(term): sigaction failed");
257
	if (sigaction(SIGINT, &sa, NULL) < 0)
258
		err(1, "unsetsig(int): sigaction failed");
259
}
260
261
void
262
getbasepath(char *base)
263
{
264
	uid_t uid;
265
	struct stat sb;
266
	mode_t mask, omask;
267
268
	uid = geteuid();
269
	if (uid == 0) {
270
		mask = 022;
271
		snprintf(base, SOCKPATH_MAX, SOCKPATH_DIR);
272
	} else {
273
		mask = 077;
274
		snprintf(base, SOCKPATH_MAX, SOCKPATH_DIR "-%u", uid);
275
	}
276
	omask = umask(mask);
277
	if (mkdir(base, 0777) < 0) {
278
		if (errno != EEXIST)
279
			err(1, "mkdir(\"%s\")", base);
280
	}
281
	umask(omask);
282
	if (stat(base, &sb) < 0)
283
		err(1, "stat(\"%s\")", base);
284
	if (!S_ISDIR(sb.st_mode))
285
		errx(1, "%s is not a directory", base);
286
	if (sb.st_uid != uid || (sb.st_mode & mask) != 0)
287
		errx(1, "%s has wrong permissions", base);
288
}
289
290
struct dev *
291
mkdev(char *path, struct aparams *par,
292
    int mode, int bufsz, int round, int rate, int hold, int autovol)
293
{
294
	struct dev *d;
295
296
	for (d = dev_list; d != NULL; d = d->next) {
297
		if (strcmp(d->path, path) == 0)
298
			return d;
299
	}
300
	if (!bufsz && !round) {
301
		round = DEFAULT_ROUND;
302
		bufsz = DEFAULT_BUFSZ;
303
	} else if (!bufsz) {
304
		bufsz = round * 2;
305
	} else if (!round)
306
		round = bufsz / 2;
307
	d = dev_new(path, par, mode, bufsz, round, rate, hold, autovol);
308
	if (d == NULL)
309
		exit(1);
310
	return d;
311
}
312
313
struct port *
314
mkport(char *path, int hold)
315
{
316
	struct port *c;
317
318
	for (c = port_list; c != NULL; c = c->next) {
319
		if (strcmp(c->path, path) == 0)
320
			return c;
321
	}
322
	c = port_new(path, MODE_MIDIMASK, hold);
323
	if (c == NULL)
324
		exit(1);
325
	return c;
326
}
327
328
struct opt *
329
mkopt(char *path, struct dev *d,
330
    int pmin, int pmax, int rmin, int rmax,
331
    int mode, int vol, int mmc, int dup)
332
{
333
	struct opt *o;
334
335
	o = opt_new(path, d, pmin, pmax, rmin, rmax,
336
	    MIDI_TO_ADATA(vol), mmc, dup, mode);
337
	if (o == NULL)
338
		return NULL;
339
	dev_adjpar(d, o->mode, o->pmax, o->rmax);
340
	return o;
341
}
342
343
int
344
main(int argc, char **argv)
345
{
346
	int c, background, unit;
347
	int pmin, pmax, rmin, rmax;
348
	char base[SOCKPATH_MAX], path[SOCKPATH_MAX];
349
	unsigned int mode, dup, mmc, vol;
350
	unsigned int hold, autovol, bufsz, round, rate;
351
	const char *str;
352
	struct aparams par;
353
	struct dev *d;
354
	struct port *p;
355
	struct listen *l;
356
	struct passwd *pw;
357
	struct tcpaddr {
358
		char *host;
359
		struct tcpaddr *next;
360
	} *tcpaddr_list, *ta;
361
	int s[2];
362
	pid_t pid;
363
	uid_t euid, hpw_uid, wpw_uid;
364
	gid_t hpw_gid, wpw_gid;
365
	char *wpw_dir;
366
367
	atexit(log_flush);
368
369
	/*
370
	 * global options defaults
371
	 */
372
	vol = 118;
373
	dup = 1;
374
	mmc = 0;
375
	hold = 0;
376
	autovol = 1;
377
	bufsz = 0;
378
	round = 0;
379
	rate = DEFAULT_RATE;
380
	unit = 0;
381
	background = 1;
382
	pmin = 0;
383
	pmax = 1;
384
	rmin = 0;
385
	rmax = 1;
386
	aparams_init(&par);
387
	mode = MODE_PLAY | MODE_REC;
388
	tcpaddr_list = NULL;
389
390
	while ((c = getopt(argc, argv, "a:b:c:C:de:f:j:L:m:q:r:s:t:U:v:w:x:z:")) != -1) {
391
		switch (c) {
392
		case 'd':
393
			log_level++;
394
			background = 0;
395
			break;
396
		case 'U':
397
			unit = strtonum(optarg, 0, 15, &str);
398
			if (str)
399
				errx(1, "%s: unit number is %s", optarg, str);
400
			break;
401
		case 'L':
402
			ta = xmalloc(sizeof(struct tcpaddr));
403
			ta->host = optarg;
404
			ta->next = tcpaddr_list;
405
			tcpaddr_list = ta;
406
			break;
407
		case 'm':
408
			mode = opt_mode();
409
			break;
410
		case 'j':
411
			dup = opt_onoff();
412
			break;
413
		case 't':
414
			mmc = opt_mmc();
415
			break;
416
		case 'c':
417
			opt_ch(&pmin, &pmax);
418
			break;
419
		case 'C':
420
			opt_ch(&rmin, &rmax);
421
			break;
422
		case 'e':
423
			opt_enc(&par);
424
			break;
425
		case 'r':
426
			rate = strtonum(optarg, RATE_MIN, RATE_MAX, &str);
427
			if (str)
428
				errx(1, "%s: rate is %s", optarg, str);
429
			break;
430
		case 'v':
431
			vol = strtonum(optarg, 0, MIDI_MAXCTL, &str);
432
			if (str)
433
				errx(1, "%s: volume is %s", optarg, str);
434
			break;
435
		case 's':
436
			if ((d = dev_list) == NULL) {
437
				d = mkdev(DEFAULT_DEV, &par, 0, bufsz, round,
438
				    rate, hold, autovol);
439
			}
440
			if (mkopt(optarg, d, pmin, pmax, rmin, rmax,
441
				mode, vol, mmc, dup) == NULL)
442
				return 1;
443
			break;
444
		case 'q':
445
			mkport(optarg, hold);
446
			break;
447
		case 'a':
448
			hold = opt_onoff();
449
			break;
450
		case 'w':
451
			autovol = opt_onoff();
452
			break;
453
		case 'b':
454
			bufsz = strtonum(optarg, 1, RATE_MAX, &str);
455
			if (str)
456
				errx(1, "%s: buffer size is %s", optarg, str);
457
			break;
458
		case 'z':
459
			round = strtonum(optarg, 1, SHRT_MAX, &str);
460
			if (str)
461
				errx(1, "%s: block size is %s", optarg, str);
462
			break;
463
		case 'f':
464
			mkdev(optarg, &par, 0, bufsz, round,
465
			    rate, hold, autovol);
466
			break;
467
		default:
468
			fputs(usagestr, stderr);
469
			return 1;
470
		}
471
	}
472
	argc -= optind;
473
	argv += optind;
474
	if (argc > 0) {
475
		fputs(usagestr, stderr);
476
		return 1;
477
	}
478
	if (dev_list == NULL)
479
		mkdev(DEFAULT_DEV, &par, 0, bufsz, round, rate, hold, autovol);
480
	for (d = dev_list; d != NULL; d = d->next) {
481
		if (opt_byname("default", d->num))
482
			continue;
483
		if (mkopt("default", d, pmin, pmax, rmin, rmax,
484
			mode, vol, mmc, dup) == NULL)
485
			return 1;
486
	}
487
488
	setsig();
489
	filelist_init();
490
491
	euid = geteuid();
492
	if (euid == 0) {
493
		if ((pw = getpwnam(SNDIO_PRIV_USER)) == NULL)
494
			errx(1, "unknown user %s", SNDIO_PRIV_USER);
495
		hpw_uid = pw->pw_uid;
496
		hpw_gid = pw->pw_gid;
497
		if ((pw = getpwnam(SNDIO_USER)) == NULL)
498
			errx(1, "unknown user %s", SNDIO_USER);
499
		wpw_uid = pw->pw_uid;
500
		wpw_gid = pw->pw_gid;
501
		wpw_dir = xstrdup(pw->pw_dir);
502
	} else {
503
		hpw_uid = wpw_uid = hpw_gid = wpw_gid = 0xdeadbeef;
504
		wpw_dir = NULL;
505
	}
506
507
	/* start subprocesses */
508
509
	if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) < 0) {
510
		perror("socketpair");
511
		return 1;
512
	}
513
	pid = fork();
514
	if (pid	== -1) {
515
		log_puts("can't fork\n");
516
		return 1;
517
	}
518
	if (pid == 0) {
519
		setproctitle("helper");
520
		close(s[0]);
521
		if (fdpass_new(s[1], &helper_fileops) == NULL)
522
			return 1;
523
		if (background) {
524
			log_flush();
525
			log_level = 0;
526
			if (daemon(0, 0) < 0)
527
				err(1, "daemon");
528
		}
529
		if (euid == 0) {
530
			if (setgroups(1, &hpw_gid) ||
531
			    setresgid(hpw_gid, hpw_gid, hpw_gid) ||
532
			    setresuid(hpw_uid, hpw_uid, hpw_uid))
533
				err(1, "cannot drop privileges");
534
		}
535
		if (pledge("stdio sendfd rpath wpath cpath", NULL) < 0)
536
			err(1, "pledge");
537
		while (file_poll())
538
			; /* nothing */
539
	} else {
540
		close(s[1]);
541
		if (fdpass_new(s[0], &worker_fileops) == NULL)
542
			return 1;
543
544
		getbasepath(base);
545
		snprintf(path,
546
		    SOCKPATH_MAX, "%s/" SOCKPATH_FILE "%u",
547
		    base, unit);
548
		if (!listen_new_un(path))
549
			return 1;
550
		for (ta = tcpaddr_list; ta != NULL; ta = ta->next) {
551
			if (!listen_new_tcp(ta->host, AUCAT_PORT + unit))
552
				return 1;
553
		}
554
		for (l = listen_list; l != NULL; l = l->next) {
555
			if (!listen_init(l))
556
				return 1;
557
		}
558
559
		midi_init();
560
		for (p = port_list; p != NULL; p = p->next) {
561
			if (!port_init(p))
562
				return 1;
563
		}
564
		for (d = dev_list; d != NULL; d = d->next) {
565
			if (!dev_init(d))
566
				return 1;
567
		}
568
		if (background) {
569
			log_flush();
570
			log_level = 0;
571
			if (daemon(0, 0) < 0)
572
				err(1, "daemon");
573
		}
574
		if (euid == 0) {
575
			if (setpriority(PRIO_PROCESS, 0, SNDIO_PRIO) < 0)
576
				err(1, "setpriority");
577
			if (chroot(wpw_dir) != 0 || chdir("/") != 0)
578
				err(1, "cannot chroot to %s", wpw_dir);
579
			if (setgroups(1, &wpw_gid) ||
580
			    setresgid(wpw_gid, wpw_gid, wpw_gid) ||
581
			    setresuid(wpw_uid, wpw_uid, wpw_uid))
582
				err(1, "cannot drop privileges");
583
		}
584
		if (tcpaddr_list) {
585
			if (pledge("stdio audio recvfd unix inet wpath cpath rpath", NULL) == -1)
586
				err(1, "pledge");
587
		} else {
588
			if (pledge("stdio audio recvfd unix wpath cpath rpath", NULL) == -1)
589
				err(1, "pledge");
590
		}
591
		for (;;) {
592
			if (quit_flag)
593
				break;
594
			if (!fdpass_peer)
595
				break;
596
			if (!file_poll())
597
				break;
598
		}
599
		if (fdpass_peer)
600
			fdpass_close(fdpass_peer);
601
		while (listen_list != NULL)
602
			listen_close(listen_list);
603
		while (sock_list != NULL)
604
			sock_close(sock_list);
605
		for (d = dev_list; d != NULL; d = d->next)
606
			dev_done(d);
607
		for (p = port_list; p != NULL; p = p->next)
608
			port_done(p);
609
		while (file_poll())
610
			; /* nothing */
611
		midi_done();
612
	}
613
	while (opt_list != NULL)
614
		opt_del(opt_list);
615
	while (dev_list)
616
		dev_del(dev_list);
617
	while (port_list)
618
		port_del(port_list);
619
	while (tcpaddr_list) {
620
		ta = tcpaddr_list;
621
		tcpaddr_list = ta->next;
622
		xfree(ta);
623
	}
624
	filelist_done();
625
	unsetsig();
626
	return 0;
627
}