xref: /openbsd-src/usr.bin/sndiod/sndiod.c (revision 4e1ee0786f11cc571bd0be17d38e46f635c719fc)
1 /*	$OpenBSD: sndiod.c,v 1.46 2021/07/12 15:09:20 beck 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	480
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 void sigint(int);
89 void sighup(int);
90 void opt_ch(int *, int *);
91 void opt_enc(struct aparams *);
92 int opt_mmc(void);
93 int opt_onoff(void);
94 int getword(char *, char **);
95 unsigned int opt_mode(void);
96 void getbasepath(char *);
97 void setsig(void);
98 void unsetsig(void);
99 struct dev *mkdev(char *, struct aparams *,
100     int, int, int, int, int, int);
101 struct port *mkport(char *, int);
102 struct opt *mkopt(char *, struct dev *,
103     int, int, int, int, int, int, int, int);
104 
105 unsigned int log_level = 0;
106 volatile sig_atomic_t quit_flag = 0, reopen_flag = 0;
107 
108 char usagestr[] = "usage: sndiod [-d] [-a flag] [-b nframes] "
109     "[-C min:max] [-c min:max]\n\t"
110     "[-e enc] [-F device] [-f device] [-j flag] [-L addr] [-m mode]\n\t"
111     "[-Q port] [-q port] [-r rate] [-s name] [-t mode] [-U unit]\n\t"
112     "[-v volume] [-w flag] [-z nframes]\n";
113 
114 /*
115  * default audio devices
116  */
117 static char *default_devs[] = {
118 	"rsnd/0", "rsnd/1", "rsnd/2", "rsnd/3",
119 	NULL
120 };
121 
122 /*
123  * default MIDI ports
124  */
125 static char *default_ports[] = {
126 	"rmidi/0", "rmidi/1", "rmidi/2", "rmidi/3",
127 	"rmidi/4", "rmidi/5", "rmidi/6", "rmidi/7",
128 	NULL
129 };
130 
131 /*
132  * SIGINT handler, it raises the quit flag. If the flag is already set,
133  * that means that the last SIGINT was not handled, because the process
134  * is blocked somewhere, so exit.
135  */
136 void
137 sigint(int s)
138 {
139 	if (quit_flag)
140 		_exit(1);
141 	quit_flag = 1;
142 }
143 
144 /*
145  * SIGHUP handler, it raises the reopen flag, which requests devices
146  * to be reopened.
147  */
148 void
149 sighup(int s)
150 {
151 	reopen_flag = 1;
152 }
153 
154 void
155 opt_ch(int *rcmin, int *rcmax)
156 {
157 	char *next, *end;
158 	long cmin, cmax;
159 
160 	errno = 0;
161 	cmin = strtol(optarg, &next, 10);
162 	if (next == optarg || *next != ':')
163 		goto failed;
164 	cmax = strtol(++next, &end, 10);
165 	if (end == next || *end != '\0')
166 		goto failed;
167 	if (cmin < 0 || cmax < cmin || cmax >= NCHAN_MAX)
168 		goto failed;
169 	*rcmin = cmin;
170 	*rcmax = cmax;
171 	return;
172 failed:
173 	errx(1, "%s: bad channel range", optarg);
174 }
175 
176 void
177 opt_enc(struct aparams *par)
178 {
179 	int len;
180 
181 	len = aparams_strtoenc(par, optarg);
182 	if (len == 0 || optarg[len] != '\0')
183 		errx(1, "%s: bad encoding", optarg);
184 }
185 
186 int
187 opt_mmc(void)
188 {
189 	if (strcmp("off", optarg) == 0)
190 		return 0;
191 	if (strcmp("slave", optarg) == 0)
192 		return 1;
193 	errx(1, "%s: off/slave expected", optarg);
194 }
195 
196 int
197 opt_onoff(void)
198 {
199 	if (strcmp("off", optarg) == 0)
200 		return 0;
201 	if (strcmp("on", optarg) == 0)
202 		return 1;
203 	errx(1, "%s: on/off expected", optarg);
204 }
205 
206 int
207 getword(char *word, char **str)
208 {
209 	char *p = *str;
210 
211 	for (;;) {
212 		if (*word == '\0')
213 			break;
214 		if (*word++ != *p++)
215 			return 0;
216 	}
217 	if (*p == ',' || *p == '\0') {
218 		*str = p;
219 		return 1;
220 	}
221 	return 0;
222 }
223 
224 unsigned int
225 opt_mode(void)
226 {
227 	unsigned int mode = 0;
228 	char *p = optarg;
229 
230 	for (;;) {
231 		if (getword("play", &p)) {
232 			mode |= MODE_PLAY;
233 		} else if (getword("rec", &p)) {
234 			mode |= MODE_REC;
235 		} else if (getword("mon", &p)) {
236 			mode |= MODE_MON;
237 		} else if (getword("midi", &p)) {
238 			mode |= MODE_MIDIMASK;
239 		} else
240 			errx(1, "%s: bad mode", optarg);
241 		if (*p == '\0')
242 			break;
243 		p++;
244 	}
245 	if (mode == 0)
246 		errx(1, "empty mode");
247 	return mode;
248 }
249 
250 void
251 setsig(void)
252 {
253 	struct sigaction sa;
254 
255 	quit_flag = 0;
256 	reopen_flag = 0;
257 	sigfillset(&sa.sa_mask);
258 	sa.sa_flags = SA_RESTART;
259 	sa.sa_handler = sigint;
260 	if (sigaction(SIGINT, &sa, NULL) == -1)
261 		err(1, "sigaction(int) failed");
262 	if (sigaction(SIGTERM, &sa, NULL) == -1)
263 		err(1, "sigaction(term) failed");
264 	sa.sa_handler = sighup;
265 	if (sigaction(SIGHUP, &sa, NULL) == -1)
266 		err(1, "sigaction(hup) failed");
267 }
268 
269 void
270 unsetsig(void)
271 {
272 	struct sigaction sa;
273 
274 	sigfillset(&sa.sa_mask);
275 	sa.sa_flags = SA_RESTART;
276 	sa.sa_handler = SIG_DFL;
277 	if (sigaction(SIGHUP, &sa, NULL) == -1)
278 		err(1, "unsetsig(hup): sigaction failed");
279 	if (sigaction(SIGTERM, &sa, NULL) == -1)
280 		err(1, "unsetsig(term): sigaction failed");
281 	if (sigaction(SIGINT, &sa, NULL) == -1)
282 		err(1, "unsetsig(int): sigaction failed");
283 }
284 
285 void
286 getbasepath(char *base)
287 {
288 	uid_t uid;
289 	struct stat sb;
290 	mode_t mask, omask;
291 
292 	uid = geteuid();
293 	if (uid == 0) {
294 		mask = 022;
295 		snprintf(base, SOCKPATH_MAX, SOCKPATH_DIR);
296 	} else {
297 		mask = 077;
298 		snprintf(base, SOCKPATH_MAX, SOCKPATH_DIR "-%u", uid);
299 	}
300 	omask = umask(mask);
301 	if (mkdir(base, 0777) == -1) {
302 		if (errno != EEXIST)
303 			err(1, "mkdir(\"%s\")", base);
304 	}
305 	umask(omask);
306 	if (stat(base, &sb) == -1)
307 		err(1, "stat(\"%s\")", base);
308 	if (!S_ISDIR(sb.st_mode))
309 		errx(1, "%s is not a directory", base);
310 	if (sb.st_uid != uid || (sb.st_mode & mask) != 0)
311 		errx(1, "%s has wrong permissions", base);
312 }
313 
314 struct dev *
315 mkdev(char *path, struct aparams *par,
316     int mode, int bufsz, int round, int rate, int hold, int autovol)
317 {
318 	struct dev *d;
319 
320 	for (d = dev_list; d != NULL; d = d->next) {
321 		if (d->alt_list->next == NULL &&
322 		    strcmp(d->alt_list->name, path) == 0)
323 			return d;
324 	}
325 	if (!bufsz && !round) {
326 		round = DEFAULT_ROUND;
327 		bufsz = DEFAULT_BUFSZ;
328 	} else if (!bufsz) {
329 		bufsz = round * 2;
330 	} else if (!round)
331 		round = bufsz / 2;
332 	d = dev_new(path, par, mode, bufsz, round, rate, hold, autovol);
333 	if (d == NULL)
334 		exit(1);
335 	return d;
336 }
337 
338 struct port *
339 mkport(char *path, int hold)
340 {
341 	struct port *c;
342 
343 	for (c = port_list; c != NULL; c = c->next) {
344 		if (c->path_list->next == NULL &&
345 		    strcmp(c->path_list->str, path) == 0)
346 			return c;
347 	}
348 	c = port_new(path, MODE_MIDIMASK, hold);
349 	if (c == NULL)
350 		exit(1);
351 	return c;
352 }
353 
354 struct opt *
355 mkopt(char *path, struct dev *d,
356     int pmin, int pmax, int rmin, int rmax,
357     int mode, int vol, int mmc, int dup)
358 {
359 	struct opt *o;
360 
361 	o = opt_new(d, path, pmin, pmax, rmin, rmax,
362 	    MIDI_TO_ADATA(vol), mmc, dup, mode);
363 	if (o == NULL)
364 		return NULL;
365 	dev_adjpar(d, o->mode, o->pmax, o->rmax);
366 	return o;
367 }
368 
369 static void
370 dounveil(char *name, char *prefix, char *path_prefix)
371 {
372 	size_t prefix_len;
373 	char path[PATH_MAX];
374 
375 	prefix_len = strlen(prefix);
376 
377 	if (strncmp(name, prefix, prefix_len) != 0)
378 		errx(1, "%s: unsupported device or port format", name);
379 	snprintf(path, sizeof(path), "%s%s", path_prefix, name + prefix_len);
380 	if (unveil(path, "rw") == -1)
381 		err(1, "unveil %s", path);
382 }
383 
384 static int
385 start_helper(int background)
386 {
387 	struct dev *d;
388 	struct dev_alt *da;
389 	struct port *p;
390 	struct passwd *pw;
391 	struct name *n;
392 	int s[2];
393 	pid_t pid;
394 
395 	if (geteuid() == 0) {
396 		if ((pw = getpwnam(SNDIO_PRIV_USER)) == NULL)
397 			errx(1, "unknown user %s", SNDIO_PRIV_USER);
398 	} else
399 		pw = NULL;
400 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == -1) {
401 		perror("socketpair");
402 		return 0;
403 	}
404 	pid = fork();
405 	if (pid	== -1) {
406 		log_puts("can't fork\n");
407 		return 0;
408 	}
409 	if (pid == 0) {
410 		setproctitle("helper");
411 		close(s[0]);
412 		if (fdpass_new(s[1], &helper_fileops) == NULL)
413 			return 0;
414 		if (background) {
415 			log_flush();
416 			log_level = 0;
417 			if (daemon(0, 0) == -1)
418 				err(1, "daemon");
419 		}
420 		if (pw != NULL) {
421 			if (setgroups(1, &pw->pw_gid) ||
422 			    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
423 			    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
424 				err(1, "cannot drop privileges");
425 		}
426 		for (d = dev_list; d != NULL; d = d->next) {
427 			for (da = d->alt_list; da != NULL; da = da->next) {
428 				dounveil(da->name, "rsnd/", "/dev/audio");
429 				dounveil(da->name, "rsnd/", "/dev/audioctl");
430 			}
431 		}
432 		for (p = port_list; p != NULL; p = p->next) {
433 			for (n = p->path_list; n != NULL; n = n->next)
434 				dounveil(n->str, "rmidi/", "/dev/rmidi");
435 		}
436 		if (pledge("stdio sendfd rpath wpath", NULL) == -1)
437 			err(1, "pledge");
438 		while (file_poll())
439 			; /* nothing */
440 		exit(0);
441 	} else {
442 		close(s[1]);
443 		if (fdpass_new(s[0], &worker_fileops) == NULL)
444 			return 0;
445 	}
446 	return 1;
447 }
448 
449 static void
450 stop_helper(void)
451 {
452 	if (fdpass_peer)
453 		fdpass_close(fdpass_peer);
454 }
455 
456 int
457 main(int argc, char **argv)
458 {
459 	int c, i, background, unit;
460 	int pmin, pmax, rmin, rmax;
461 	char base[SOCKPATH_MAX], path[SOCKPATH_MAX];
462 	unsigned int mode, dup, mmc, vol;
463 	unsigned int hold, autovol, bufsz, round, rate;
464 	const char *str;
465 	struct aparams par;
466 	struct dev *d;
467 	struct port *p;
468 	struct listen *l;
469 	struct passwd *pw;
470 	struct tcpaddr {
471 		char *host;
472 		struct tcpaddr *next;
473 	} *tcpaddr_list, *ta;
474 
475 	atexit(log_flush);
476 
477 	/*
478 	 * global options defaults
479 	 */
480 	vol = 127;
481 	dup = 1;
482 	mmc = 0;
483 	hold = 0;
484 	autovol = 0;
485 	bufsz = 0;
486 	round = 0;
487 	rate = DEFAULT_RATE;
488 	unit = 0;
489 	background = 1;
490 	pmin = 0;
491 	pmax = 1;
492 	rmin = 0;
493 	rmax = 1;
494 	aparams_init(&par);
495 	mode = MODE_PLAY | MODE_REC;
496 	tcpaddr_list = NULL;
497 	d = NULL;
498 	p = NULL;
499 
500 	slot_array_init();
501 
502 	while ((c = getopt(argc, argv,
503 	    "a:b:c:C:de:F:f:j:L:m:Q:q:r:s:t:U:v:w:x:z:")) != -1) {
504 		switch (c) {
505 		case 'd':
506 			log_level++;
507 			background = 0;
508 			break;
509 		case 'U':
510 			unit = strtonum(optarg, 0, 15, &str);
511 			if (str)
512 				errx(1, "%s: unit number is %s", optarg, str);
513 			break;
514 		case 'L':
515 			ta = xmalloc(sizeof(struct tcpaddr));
516 			ta->host = optarg;
517 			ta->next = tcpaddr_list;
518 			tcpaddr_list = ta;
519 			break;
520 		case 'm':
521 			mode = opt_mode();
522 			break;
523 		case 'j':
524 			dup = opt_onoff();
525 			break;
526 		case 't':
527 			mmc = opt_mmc();
528 			break;
529 		case 'c':
530 			opt_ch(&pmin, &pmax);
531 			break;
532 		case 'C':
533 			opt_ch(&rmin, &rmax);
534 			break;
535 		case 'e':
536 			opt_enc(&par);
537 			break;
538 		case 'r':
539 			rate = strtonum(optarg, RATE_MIN, RATE_MAX, &str);
540 			if (str)
541 				errx(1, "%s: rate is %s", optarg, str);
542 			break;
543 		case 'v':
544 			vol = strtonum(optarg, 0, MIDI_MAXCTL, &str);
545 			if (str)
546 				errx(1, "%s: volume is %s", optarg, str);
547 			break;
548 		case 's':
549 			if (d == NULL) {
550 				for (i = 0; default_devs[i] != NULL; i++) {
551 					mkdev(default_devs[i], &par, 0,
552 					    bufsz, round, rate, 0, autovol);
553 				}
554 				d = dev_list;
555 			}
556 			if (mkopt(optarg, d, pmin, pmax, rmin, rmax,
557 				mode, vol, mmc, dup) == NULL)
558 				return 1;
559 			break;
560 		case 'q':
561 			p = mkport(optarg, hold);
562 			break;
563 		case 'Q':
564 			if (p == NULL)
565 				errx(1, "-Q %s: no ports defined", optarg);
566 			namelist_add(&p->path_list, optarg);
567 			break;
568 		case 'a':
569 			hold = opt_onoff();
570 			break;
571 		case 'w':
572 			autovol = opt_onoff();
573 			break;
574 		case 'b':
575 			bufsz = strtonum(optarg, 1, RATE_MAX, &str);
576 			if (str)
577 				errx(1, "%s: buffer size is %s", optarg, str);
578 			break;
579 		case 'z':
580 			round = strtonum(optarg, 1, SHRT_MAX, &str);
581 			if (str)
582 				errx(1, "%s: block size is %s", optarg, str);
583 			break;
584 		case 'f':
585 			d = mkdev(optarg, &par, 0, bufsz, round,
586 			    rate, hold, autovol);
587 			break;
588 		case 'F':
589 			if (d == NULL)
590 				errx(1, "-F %s: no devices defined", optarg);
591 			if (!dev_addname(d, optarg))
592 				exit(1);
593 			break;
594 		default:
595 			fputs(usagestr, stderr);
596 			return 1;
597 		}
598 	}
599 	argc -= optind;
600 	argv += optind;
601 	if (argc > 0) {
602 		fputs(usagestr, stderr);
603 		return 1;
604 	}
605 	if (port_list == NULL) {
606 		for (i = 0; default_ports[i] != NULL; i++)
607 			mkport(default_ports[i], 0);
608 	}
609 	if (dev_list == NULL) {
610 		for (i = 0; default_devs[i] != NULL; i++) {
611 			mkdev(default_devs[i], &par, 0,
612 			    bufsz, round, rate, 0, autovol);
613 		}
614 	}
615 	for (d = dev_list; d != NULL; d = d->next) {
616 		if (opt_byname(d, "default"))
617 			continue;
618 		if (mkopt("default", d, pmin, pmax, rmin, rmax,
619 			mode, vol, mmc, dup) == NULL)
620 			return 1;
621 	}
622 
623 	setsig();
624 	filelist_init();
625 
626 	if (!start_helper(background))
627 		return 1;
628 
629 	if (geteuid() == 0) {
630 		if ((pw = getpwnam(SNDIO_USER)) == NULL)
631 			errx(1, "unknown user %s", SNDIO_USER);
632 	} else
633 		pw = NULL;
634 	getbasepath(base);
635 	snprintf(path, SOCKPATH_MAX, "%s/" SOCKPATH_FILE "%u", base, unit);
636 	if (!listen_new_un(path))
637 		return 1;
638 	for (ta = tcpaddr_list; ta != NULL; ta = ta->next) {
639 		if (!listen_new_tcp(ta->host, AUCAT_PORT + unit))
640 			return 1;
641 	}
642 	for (l = listen_list; l != NULL; l = l->next) {
643 		if (!listen_init(l))
644 			return 1;
645 	}
646 	midi_init();
647 	for (p = port_list; p != NULL; p = p->next) {
648 		if (!port_init(p))
649 			return 1;
650 	}
651 	for (d = dev_list; d != NULL; d = d->next) {
652 		if (!dev_init(d))
653 			return 1;
654 	}
655 	if (background) {
656 		log_flush();
657 		log_level = 0;
658 		if (daemon(0, 0) == -1)
659 			err(1, "daemon");
660 	}
661 	if (pw != NULL) {
662 		if (setpriority(PRIO_PROCESS, 0, SNDIO_PRIO) == -1)
663 			err(1, "setpriority");
664 		if (chroot(pw->pw_dir) == -1 || chdir("/") == -1)
665 			err(1, "cannot chroot to %s", pw->pw_dir);
666 		if (setgroups(1, &pw->pw_gid) == -1 ||
667 		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
668 		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1 )
669 			err(1, "cannot drop privileges");
670 	}
671 	if (tcpaddr_list) {
672 		if (pledge("stdio audio recvfd unix inet", NULL) == -1)
673 			err(1, "pledge");
674 	} else {
675 		if (pledge("stdio audio recvfd unix", NULL) == -1)
676 			err(1, "pledge");
677 	}
678 	for (;;) {
679 		if (quit_flag)
680 			break;
681 		if (reopen_flag) {
682 			reopen_flag = 0;
683 			for (d = dev_list; d != NULL; d = d->next)
684 				dev_reopen(d);
685 			for (p = port_list; p != NULL; p = p->next)
686 				port_reopen(p);
687 		}
688 		if (!fdpass_peer)
689 			break;
690 		if (!file_poll())
691 			break;
692 	}
693 	stop_helper();
694 	while (listen_list != NULL)
695 		listen_close(listen_list);
696 	while (sock_list != NULL)
697 		sock_close(sock_list);
698 	for (d = dev_list; d != NULL; d = d->next)
699 		dev_done(d);
700 	for (p = port_list; p != NULL; p = p->next)
701 		port_done(p);
702 	while (file_poll())
703 		; /* nothing */
704 	midi_done();
705 
706 	while (opt_list)
707 		opt_del(opt_list);
708 	while (dev_list)
709 		dev_del(dev_list);
710 	while (port_list)
711 		port_del(port_list);
712 	while (tcpaddr_list) {
713 		ta = tcpaddr_list;
714 		tcpaddr_list = ta->next;
715 		xfree(ta);
716 	}
717 	filelist_done();
718 	unsetsig();
719 	return 0;
720 }
721