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