GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/sndiod/sndiod.c Lines: 0 291 0.0 %
Date: 2017-11-07 Branches: 0 237 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: sndiod.c,v 1.32 2016/10/20 05:48:50 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
static int
344
start_helper(int background)
345
{
346
	struct passwd *pw;
347
	int s[2];
348
	pid_t pid;
349
350
	if (geteuid() == 0) {
351
		if ((pw = getpwnam(SNDIO_PRIV_USER)) == NULL)
352
			errx(1, "unknown user %s", SNDIO_PRIV_USER);
353
	} else
354
		pw = NULL;
355
	if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) < 0) {
356
		perror("socketpair");
357
		return 0;
358
	}
359
	pid = fork();
360
	if (pid	== -1) {
361
		log_puts("can't fork\n");
362
		return 0;
363
	}
364
	if (pid == 0) {
365
		setproctitle("helper");
366
		close(s[0]);
367
		if (fdpass_new(s[1], &helper_fileops) == NULL)
368
			return 0;
369
		if (background) {
370
			log_flush();
371
			log_level = 0;
372
			if (daemon(0, 0) < 0)
373
				err(1, "daemon");
374
		}
375
		if (pw != NULL) {
376
			if (setgroups(1, &pw->pw_gid) ||
377
			    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
378
			    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
379
				err(1, "cannot drop privileges");
380
		}
381
		if (pledge("stdio sendfd rpath wpath flock cpath", NULL) < 0)
382
			err(1, "pledge");
383
		while (file_poll())
384
			; /* nothing */
385
		exit(0);
386
	} else {
387
		close(s[1]);
388
		if (fdpass_new(s[0], &worker_fileops) == NULL)
389
			return 0;
390
	}
391
	return 1;
392
}
393
394
static void
395
stop_helper(void)
396
{
397
	if (fdpass_peer)
398
		fdpass_close(fdpass_peer);
399
}
400
401
int
402
main(int argc, char **argv)
403
{
404
	int c, background, unit;
405
	int pmin, pmax, rmin, rmax;
406
	char base[SOCKPATH_MAX], path[SOCKPATH_MAX];
407
	unsigned int mode, dup, mmc, vol;
408
	unsigned int hold, autovol, bufsz, round, rate;
409
	const char *str;
410
	struct aparams par;
411
	struct dev *d;
412
	struct port *p;
413
	struct listen *l;
414
	struct passwd *pw;
415
	struct tcpaddr {
416
		char *host;
417
		struct tcpaddr *next;
418
	} *tcpaddr_list, *ta;
419
420
	atexit(log_flush);
421
422
	/*
423
	 * global options defaults
424
	 */
425
	vol = 118;
426
	dup = 1;
427
	mmc = 0;
428
	hold = 0;
429
	autovol = 1;
430
	bufsz = 0;
431
	round = 0;
432
	rate = DEFAULT_RATE;
433
	unit = 0;
434
	background = 1;
435
	pmin = 0;
436
	pmax = 1;
437
	rmin = 0;
438
	rmax = 1;
439
	aparams_init(&par);
440
	mode = MODE_PLAY | MODE_REC;
441
	tcpaddr_list = NULL;
442
443
	while ((c = getopt(argc, argv, "a:b:c:C:de:f:j:L:m:q:r:s:t:U:v:w:x:z:")) != -1) {
444
		switch (c) {
445
		case 'd':
446
			log_level++;
447
			background = 0;
448
			break;
449
		case 'U':
450
			unit = strtonum(optarg, 0, 15, &str);
451
			if (str)
452
				errx(1, "%s: unit number is %s", optarg, str);
453
			break;
454
		case 'L':
455
			ta = xmalloc(sizeof(struct tcpaddr));
456
			ta->host = optarg;
457
			ta->next = tcpaddr_list;
458
			tcpaddr_list = ta;
459
			break;
460
		case 'm':
461
			mode = opt_mode();
462
			break;
463
		case 'j':
464
			dup = opt_onoff();
465
			break;
466
		case 't':
467
			mmc = opt_mmc();
468
			break;
469
		case 'c':
470
			opt_ch(&pmin, &pmax);
471
			break;
472
		case 'C':
473
			opt_ch(&rmin, &rmax);
474
			break;
475
		case 'e':
476
			opt_enc(&par);
477
			break;
478
		case 'r':
479
			rate = strtonum(optarg, RATE_MIN, RATE_MAX, &str);
480
			if (str)
481
				errx(1, "%s: rate is %s", optarg, str);
482
			break;
483
		case 'v':
484
			vol = strtonum(optarg, 0, MIDI_MAXCTL, &str);
485
			if (str)
486
				errx(1, "%s: volume is %s", optarg, str);
487
			break;
488
		case 's':
489
			if ((d = dev_list) == NULL) {
490
				d = mkdev(DEFAULT_DEV, &par, 0, bufsz, round,
491
				    rate, hold, autovol);
492
			}
493
			if (mkopt(optarg, d, pmin, pmax, rmin, rmax,
494
				mode, vol, mmc, dup) == NULL)
495
				return 1;
496
			break;
497
		case 'q':
498
			mkport(optarg, hold);
499
			break;
500
		case 'a':
501
			hold = opt_onoff();
502
			break;
503
		case 'w':
504
			autovol = opt_onoff();
505
			break;
506
		case 'b':
507
			bufsz = strtonum(optarg, 1, RATE_MAX, &str);
508
			if (str)
509
				errx(1, "%s: buffer size is %s", optarg, str);
510
			break;
511
		case 'z':
512
			round = strtonum(optarg, 1, SHRT_MAX, &str);
513
			if (str)
514
				errx(1, "%s: block size is %s", optarg, str);
515
			break;
516
		case 'f':
517
			mkdev(optarg, &par, 0, bufsz, round,
518
			    rate, hold, autovol);
519
			break;
520
		default:
521
			fputs(usagestr, stderr);
522
			return 1;
523
		}
524
	}
525
	argc -= optind;
526
	argv += optind;
527
	if (argc > 0) {
528
		fputs(usagestr, stderr);
529
		return 1;
530
	}
531
	if (dev_list == NULL)
532
		mkdev(DEFAULT_DEV, &par, 0, bufsz, round, rate, hold, autovol);
533
	for (d = dev_list; d != NULL; d = d->next) {
534
		if (opt_byname("default", d->num))
535
			continue;
536
		if (mkopt("default", d, pmin, pmax, rmin, rmax,
537
			mode, vol, mmc, dup) == NULL)
538
			return 1;
539
	}
540
541
	setsig();
542
	filelist_init();
543
544
	if (!start_helper(background))
545
		return 1;
546
547
	if (geteuid() == 0) {
548
		if ((pw = getpwnam(SNDIO_USER)) == NULL)
549
			errx(1, "unknown user %s", SNDIO_USER);
550
	} else
551
		pw = NULL;
552
	getbasepath(base);
553
	snprintf(path, SOCKPATH_MAX, "%s/" SOCKPATH_FILE "%u", base, unit);
554
	if (!listen_new_un(path))
555
		return 1;
556
	for (ta = tcpaddr_list; ta != NULL; ta = ta->next) {
557
		if (!listen_new_tcp(ta->host, AUCAT_PORT + unit))
558
			return 1;
559
	}
560
	for (l = listen_list; l != NULL; l = l->next) {
561
		if (!listen_init(l))
562
			return 1;
563
	}
564
	midi_init();
565
	for (p = port_list; p != NULL; p = p->next) {
566
		if (!port_init(p))
567
			return 1;
568
	}
569
	for (d = dev_list; d != NULL; d = d->next) {
570
		if (!dev_init(d))
571
			return 1;
572
	}
573
	if (background) {
574
		log_flush();
575
		log_level = 0;
576
		if (daemon(0, 0) < 0)
577
			err(1, "daemon");
578
	}
579
	if (pw != NULL) {
580
		if (setpriority(PRIO_PROCESS, 0, SNDIO_PRIO) < 0)
581
			err(1, "setpriority");
582
		if (chroot(pw->pw_dir) != 0 || chdir("/") != 0)
583
			err(1, "cannot chroot to %s", pw->pw_dir);
584
		if (setgroups(1, &pw->pw_gid) ||
585
		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
586
		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
587
			err(1, "cannot drop privileges");
588
	}
589
	if (tcpaddr_list) {
590
		if (pledge("stdio audio recvfd unix inet flock rpath cpath wpath", NULL) == -1)
591
			err(1, "pledge");
592
	} else {
593
		if (pledge("stdio audio recvfd unix flock rpath cpath wpath", NULL) == -1)
594
			err(1, "pledge");
595
	}
596
	for (;;) {
597
		if (quit_flag)
598
			break;
599
		if (!fdpass_peer)
600
			break;
601
		if (!file_poll())
602
			break;
603
	}
604
	stop_helper();
605
	while (listen_list != NULL)
606
		listen_close(listen_list);
607
	while (sock_list != NULL)
608
		sock_close(sock_list);
609
	for (d = dev_list; d != NULL; d = d->next)
610
		dev_done(d);
611
	for (p = port_list; p != NULL; p = p->next)
612
		port_done(p);
613
	while (file_poll())
614
		; /* nothing */
615
	midi_done();
616
617
	while (opt_list != NULL)
618
		opt_del(opt_list);
619
	while (dev_list)
620
		dev_del(dev_list);
621
	while (port_list)
622
		port_del(port_list);
623
	while (tcpaddr_list) {
624
		ta = tcpaddr_list;
625
		tcpaddr_list = ta->next;
626
		xfree(ta);
627
	}
628
	filelist_done();
629
	unsetsig();
630
	return 0;
631
}