xref: /openbsd/usr.bin/sndiod/sndiod.c (revision 4cfece93)
1 /*	$OpenBSD: sndiod.c,v 1.41 2020/06/18 05:11:13 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	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");
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, devindex;
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 = 118;
481 	dup = 1;
482 	mmc = 0;
483 	hold = 0;
484 	autovol = 1;
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 	devindex = 0;
498 
499 	while ((c = getopt(argc, argv,
500 	    "a:b:c:C:de:F:f:j:L:m:Q:q:r:s:t:U:v:w:x:z:")) != -1) {
501 		switch (c) {
502 		case 'd':
503 			log_level++;
504 			background = 0;
505 			break;
506 		case 'U':
507 			unit = strtonum(optarg, 0, 15, &str);
508 			if (str)
509 				errx(1, "%s: unit number is %s", optarg, str);
510 			break;
511 		case 'L':
512 			ta = xmalloc(sizeof(struct tcpaddr));
513 			ta->host = optarg;
514 			ta->next = tcpaddr_list;
515 			tcpaddr_list = ta;
516 			break;
517 		case 'm':
518 			mode = opt_mode();
519 			break;
520 		case 'j':
521 			dup = opt_onoff();
522 			break;
523 		case 't':
524 			mmc = opt_mmc();
525 			break;
526 		case 'c':
527 			opt_ch(&pmin, &pmax);
528 			break;
529 		case 'C':
530 			opt_ch(&rmin, &rmax);
531 			break;
532 		case 'e':
533 			opt_enc(&par);
534 			break;
535 		case 'r':
536 			rate = strtonum(optarg, RATE_MIN, RATE_MAX, &str);
537 			if (str)
538 				errx(1, "%s: rate is %s", optarg, str);
539 			break;
540 		case 'v':
541 			vol = strtonum(optarg, 0, MIDI_MAXCTL, &str);
542 			if (str)
543 				errx(1, "%s: volume is %s", optarg, str);
544 			break;
545 		case 's':
546 			if ((d = dev_list) == NULL) {
547 				d = mkdev(default_devs[devindex++], &par, 0,
548 				    bufsz, round, rate, hold, autovol);
549 			}
550 			if (mkopt(optarg, d, pmin, pmax, rmin, rmax,
551 				mode, vol, mmc, dup) == NULL)
552 				return 1;
553 			break;
554 		case 'q':
555 			mkport(optarg, hold);
556 			break;
557 		case 'Q':
558 			if (port_list == NULL)
559 				errx(1, "-Q %s: no ports defined", optarg);
560 			namelist_add(&port_list->path_list, optarg);
561 			break;
562 		case 'a':
563 			hold = opt_onoff();
564 			break;
565 		case 'w':
566 			autovol = opt_onoff();
567 			break;
568 		case 'b':
569 			bufsz = strtonum(optarg, 1, RATE_MAX, &str);
570 			if (str)
571 				errx(1, "%s: buffer size is %s", optarg, str);
572 			break;
573 		case 'z':
574 			round = strtonum(optarg, 1, SHRT_MAX, &str);
575 			if (str)
576 				errx(1, "%s: block size is %s", optarg, str);
577 			break;
578 		case 'f':
579 			mkdev(optarg, &par, 0, bufsz, round,
580 			    rate, hold, autovol);
581 			devindex = -1;
582 			break;
583 		case 'F':
584 			if ((d = dev_list) == NULL)
585 				errx(1, "-F %s: no devices defined", optarg);
586 			if (!dev_addname(d, optarg))
587 				exit(1);
588 			break;
589 		default:
590 			fputs(usagestr, stderr);
591 			return 1;
592 		}
593 	}
594 	argc -= optind;
595 	argv += optind;
596 	if (argc > 0) {
597 		fputs(usagestr, stderr);
598 		return 1;
599 	}
600 	if (port_list == NULL) {
601 		for (i = 0; default_ports[i] != NULL; i++)
602 			mkport(default_ports[i], 0);
603 	}
604 	if (devindex != -1) {
605 		for (i = devindex; default_devs[i] != NULL; i++) {
606 			mkdev(default_devs[i], &par, 0,
607 			    bufsz, round, rate, 0, autovol);
608 		}
609 	}
610 	for (d = dev_list; d != NULL; d = d->next) {
611 		if (opt_byname(d, "default"))
612 			continue;
613 		if (mkopt("default", d, pmin, pmax, rmin, rmax,
614 			mode, vol, mmc, dup) == NULL)
615 			return 1;
616 	}
617 
618 	setsig();
619 	filelist_init();
620 
621 	if (!start_helper(background))
622 		return 1;
623 
624 	if (geteuid() == 0) {
625 		if ((pw = getpwnam(SNDIO_USER)) == NULL)
626 			errx(1, "unknown user %s", SNDIO_USER);
627 	} else
628 		pw = NULL;
629 	getbasepath(base);
630 	snprintf(path, SOCKPATH_MAX, "%s/" SOCKPATH_FILE "%u", base, unit);
631 	if (!listen_new_un(path))
632 		return 1;
633 	for (ta = tcpaddr_list; ta != NULL; ta = ta->next) {
634 		if (!listen_new_tcp(ta->host, AUCAT_PORT + unit))
635 			return 1;
636 	}
637 	for (l = listen_list; l != NULL; l = l->next) {
638 		if (!listen_init(l))
639 			return 1;
640 	}
641 	midi_init();
642 	for (p = port_list; p != NULL; p = p->next) {
643 		if (!port_init(p))
644 			return 1;
645 	}
646 	for (d = dev_list; d != NULL; d = d->next) {
647 		if (!dev_init(d))
648 			return 1;
649 	}
650 	if (background) {
651 		log_flush();
652 		log_level = 0;
653 		if (daemon(0, 0) == -1)
654 			err(1, "daemon");
655 	}
656 	if (pw != NULL) {
657 		if (setpriority(PRIO_PROCESS, 0, SNDIO_PRIO) == -1)
658 			err(1, "setpriority");
659 		if (chroot(pw->pw_dir) == -1 || chdir("/") == -1)
660 			err(1, "cannot chroot to %s", pw->pw_dir);
661 		if (setgroups(1, &pw->pw_gid) == -1 ||
662 		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
663 		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1 )
664 			err(1, "cannot drop privileges");
665 	}
666 	if (tcpaddr_list) {
667 		if (pledge("stdio audio recvfd unix inet", NULL) == -1)
668 			err(1, "pledge");
669 	} else {
670 		if (pledge("stdio audio recvfd unix", NULL) == -1)
671 			err(1, "pledge");
672 	}
673 	for (;;) {
674 		if (quit_flag)
675 			break;
676 		if (reopen_flag) {
677 			reopen_flag = 0;
678 			for (d = dev_list; d != NULL; d = d->next)
679 				dev_reopen(d);
680 			for (p = port_list; p != NULL; p = p->next)
681 				port_reopen(p);
682 		}
683 		if (!fdpass_peer)
684 			break;
685 		if (!file_poll())
686 			break;
687 	}
688 	stop_helper();
689 	while (listen_list != NULL)
690 		listen_close(listen_list);
691 	while (sock_list != NULL)
692 		sock_close(sock_list);
693 	for (d = dev_list; d != NULL; d = d->next)
694 		dev_done(d);
695 	for (p = port_list; p != NULL; p = p->next)
696 		port_done(p);
697 	while (file_poll())
698 		; /* nothing */
699 	midi_done();
700 
701 	while (dev_list)
702 		dev_del(dev_list);
703 	while (port_list)
704 		port_del(port_list);
705 	while (tcpaddr_list) {
706 		ta = tcpaddr_list;
707 		tcpaddr_list = ta->next;
708 		xfree(ta);
709 	}
710 	filelist_done();
711 	unsetsig();
712 	return 0;
713 }
714