xref: /openbsd-src/usr.bin/sndiod/sndiod.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: sndiod.c,v 1.6 2014/03/05 20:24:16 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/param.h>
18 #include <sys/queue.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <sys/resource.h>
22 
23 #include <err.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <grp.h>
27 #include <limits.h>
28 #include <pwd.h>
29 #include <signal.h>
30 #include <sndio.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 
36 #include "amsg.h"
37 #include "defs.h"
38 #include "dev.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  * priority when run as root
55  */
56 #ifndef SNDIO_PRIO
57 #define SNDIO_PRIO	(-20)
58 #endif
59 
60 /*
61  * sample rate if no ``-r'' is used
62  */
63 #ifndef DEFAULT_RATE
64 #define DEFAULT_RATE	48000
65 #endif
66 
67 /*
68  * block size if neither ``-z'' nor ``-b'' is used
69  */
70 #ifndef DEFAULT_ROUND
71 #define DEFAULT_ROUND	960
72 #endif
73 
74 /*
75  * buffer size if neither ``-z'' nor ``-b'' is used
76  */
77 #ifndef DEFAULT_BUFSZ
78 #define DEFAULT_BUFSZ	7860
79 #endif
80 
81 /*
82  * default device in server mode
83  */
84 #ifndef DEFAULT_DEV
85 #define DEFAULT_DEV "rsnd/0"
86 #endif
87 
88 void sigint(int);
89 void opt_ch(int *, int *);
90 void opt_enc(struct aparams *);
91 int opt_mmc(void);
92 int opt_onoff(void);
93 int getword(char *, char **);
94 unsigned int opt_mode(void);
95 void getbasepath(char *, size_t);
96 void setsig(void);
97 void unsetsig(void);
98 void privdrop(void);
99 struct dev *mkdev(char *, struct aparams *,
100     int, int, int, int, int, int);
101 struct opt *mkopt(char *, struct dev *,
102     int, int, int, int, int, int, int, int);
103 
104 unsigned int log_level = 0;
105 volatile sig_atomic_t quit_flag = 0;
106 
107 char usagestr[] = "usage: sndiod [-d] [-a flag] [-b nframes] "
108     "[-C min:max] [-c min:max] [-e enc]\n\t"
109     "[-f device] [-j flag] [-L addr] [-m mode] [-q port] [-r rate]\n\t"
110     "[-s name] [-t mode] [-U unit] [-v volume] [-w flag] [-z nframes]\n";
111 
112 /*
113  * SIGINT handler, it raises the quit flag. If the flag is already set,
114  * that means that the last SIGINT was not handled, because the process
115  * is blocked somewhere, so exit.
116  */
117 void
118 sigint(int s)
119 {
120 	if (quit_flag)
121 		_exit(1);
122 	quit_flag = 1;
123 }
124 
125 void
126 opt_ch(int *rcmin, int *rcmax)
127 {
128 	char *next, *end;
129 	long cmin, cmax;
130 
131 	errno = 0;
132 	cmin = strtol(optarg, &next, 10);
133 	if (next == optarg || *next != ':')
134 		goto failed;
135 	cmax = strtol(++next, &end, 10);
136 	if (end == next || *end != '\0')
137 		goto failed;
138 	if (cmin < 0 || cmax < cmin || cmax >= NCHAN_MAX)
139 		goto failed;
140 	*rcmin = cmin;
141 	*rcmax = cmax;
142 	return;
143 failed:
144 	errx(1, "%s: bad channel range", optarg);
145 }
146 
147 void
148 opt_enc(struct aparams *par)
149 {
150 	int len;
151 
152 	len = aparams_strtoenc(par, optarg);
153 	if (len == 0 || optarg[len] != '\0')
154 		errx(1, "%s: bad encoding", optarg);
155 }
156 
157 int
158 opt_mmc(void)
159 {
160 	if (strcmp("off", optarg) == 0)
161 		return 0;
162 	if (strcmp("slave", optarg) == 0)
163 		return 1;
164 	errx(1, "%s: off/slave expected", optarg);
165 }
166 
167 int
168 opt_onoff(void)
169 {
170 	if (strcmp("off", optarg) == 0)
171 		return 0;
172 	if (strcmp("on", optarg) == 0)
173 		return 1;
174 	errx(1, "%s: on/off expected", optarg);
175 }
176 
177 int
178 getword(char *word, char **str)
179 {
180 	char *p = *str;
181 
182 	for (;;) {
183 		if (*word == '\0')
184 			break;
185 		if (*word++ != *p++)
186 			return 0;
187 	}
188 	if (*p == ',' || *p == '\0') {
189 		*str = p;
190 		return 1;
191 	}
192 	return 0;
193 }
194 
195 unsigned int
196 opt_mode(void)
197 {
198 	unsigned int mode = 0;
199 	char *p = optarg;
200 
201 	for (;;) {
202 		if (getword("play", &p)) {
203 			mode |= MODE_PLAY;
204 		} else if (getword("rec", &p)) {
205 			mode |= MODE_REC;
206 		} else if (getword("mon", &p)) {
207 			mode |= MODE_MON;
208 		} else if (getword("midi", &p)) {
209 			mode |= MODE_MIDIMASK;
210 		} else
211 			errx(1, "%s: bad mode", optarg);
212 		if (*p == '\0')
213 			break;
214 		p++;
215 	}
216 	if (mode == 0)
217 		errx(1, "empty mode");
218 	return mode;
219 }
220 
221 void
222 setsig(void)
223 {
224 	struct sigaction sa;
225 
226 	quit_flag = 0;
227 	sigfillset(&sa.sa_mask);
228 	sa.sa_flags = SA_RESTART;
229 	sa.sa_handler = sigint;
230 	if (sigaction(SIGINT, &sa, NULL) < 0)
231 		err(1, "sigaction(int) failed");
232 	if (sigaction(SIGTERM, &sa, NULL) < 0)
233 		err(1, "sigaction(term) failed");
234 	if (sigaction(SIGHUP, &sa, NULL) < 0)
235 		err(1, "sigaction(hup) failed");
236 }
237 
238 void
239 unsetsig(void)
240 {
241 	struct sigaction sa;
242 
243 	sigfillset(&sa.sa_mask);
244 	sa.sa_flags = SA_RESTART;
245 	sa.sa_handler = SIG_DFL;
246 	if (sigaction(SIGHUP, &sa, NULL) < 0)
247 		err(1, "unsetsig(hup): sigaction failed\n");
248 	if (sigaction(SIGTERM, &sa, NULL) < 0)
249 		err(1, "unsetsig(term): sigaction failed\n");
250 	if (sigaction(SIGINT, &sa, NULL) < 0)
251 		err(1, "unsetsig(int): sigaction failed\n");
252 }
253 
254 void
255 getbasepath(char *base, size_t size)
256 {
257 	uid_t uid;
258 	struct stat sb;
259 	mode_t mask;
260 
261 	uid = geteuid();
262 	if (uid == 0) {
263 		mask = 022;
264 		snprintf(base, PATH_MAX, "/tmp/aucat");
265 	} else {
266 		mask = 077;
267 		snprintf(base, PATH_MAX, "/tmp/aucat-%u", uid);
268 	}
269 	if (mkdir(base, 0777 & ~mask) < 0) {
270 		if (errno != EEXIST)
271 			err(1, "mkdir(\"%s\")", base);
272 	}
273 	if (stat(base, &sb) < 0)
274 		err(1, "stat(\"%s\")", base);
275 	if (sb.st_uid != uid || (sb.st_mode & mask) != 0)
276 		errx(1, "%s has wrong permissions", base);
277 }
278 
279 void
280 privdrop(void)
281 {
282 	struct passwd *pw;
283 
284 	if ((pw = getpwnam(SNDIO_USER)) == NULL)
285 		errx(1, "unknown user %s", SNDIO_USER);
286 	if (setpriority(PRIO_PROCESS, 0, SNDIO_PRIO) < 0)
287 		err(1, "setpriority");
288 	if (setgroups(1, &pw->pw_gid) ||
289 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
290 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
291 		err(1, "cannot drop privileges");
292 }
293 
294 struct dev *
295 mkdev(char *path, struct aparams *par,
296     int mode, int bufsz, int round, int rate, int hold, int autovol)
297 {
298 	struct dev *d;
299 
300 	for (d = dev_list; d != NULL; d = d->next) {
301 		if (strcmp(d->path, path) == 0)
302 			return d;
303 	}
304 	if (!bufsz && !round) {
305 		round = DEFAULT_ROUND;
306 		bufsz = DEFAULT_BUFSZ;
307 	} else if (!bufsz) {
308 		bufsz = round * 2;
309 	} else if (!round)
310 		round = bufsz / 2;
311 	d = dev_new(path, par, mode, bufsz, round, rate, hold, autovol);
312 	if (d == NULL)
313 		exit(1);
314 	return d;
315 }
316 
317 struct opt *
318 mkopt(char *path, struct dev *d,
319     int pmin, int pmax, int rmin, int rmax,
320     int mode, int vol, int mmc, int dup)
321 {
322 	struct opt *o;
323 
324 	o = opt_new(path, d, pmin, pmax, rmin, rmax,
325 	    MIDI_TO_ADATA(vol), mmc, dup, mode);
326 	if (o == NULL)
327 		errx(1, "%s: couldn't create subdev", path);
328 	dev_adjpar(d, o->mode, o->pmin, o->pmax, o->rmin, o->rmax);
329 	return o;
330 }
331 
332 int
333 main(int argc, char **argv)
334 {
335 	int c, background, unit;
336 	int pmin, pmax, rmin, rmax;
337 	char base[PATH_MAX], path[PATH_MAX];
338 	unsigned int mode, dup, mmc, vol;
339 	unsigned int hold, autovol, bufsz, round, rate;
340 	const char *str;
341 	struct aparams par;
342 	struct dev *d;
343 	struct port *p;
344 	struct listen *l;
345 
346 	atexit(log_flush);
347 
348 	/*
349 	 * global options defaults
350 	 */
351 	vol = 118;
352 	dup = 1;
353 	mmc = 0;
354 	hold = 0;
355 	autovol = 1;
356 	bufsz = 0;
357 	round = 0;
358 	rate = DEFAULT_RATE;
359 	unit = 0;
360 	background = 1;
361 	pmin = 0;
362 	pmax = 1;
363 	rmin = 0;
364 	rmax = 1;
365 	aparams_init(&par);
366 	mode = MODE_PLAY | MODE_REC;
367 
368 	setsig();
369 	filelist_init();
370 
371 	while ((c = getopt(argc, argv, "a:b:c:C:de:f:j:L:m:Mq:r:s:t:U:v:w:x:z:")) != -1) {
372 		switch (c) {
373 		case 'd':
374 			log_level++;
375 			background = 0;
376 			break;
377 		case 'U':
378 			if (listen_list)
379 				errx(1, "-U must come before -L");
380 			unit = strtonum(optarg, 0, 15, &str);
381 			if (str)
382 				errx(1, "%s: unit number is %s", optarg, str);
383 			break;
384 		case 'L':
385 			listen_new_tcp(optarg, AUCAT_PORT + unit);
386 			break;
387 		case 'm':
388 			mode = opt_mode();
389 			break;
390 		case 'j':
391 			dup = opt_onoff();
392 			break;
393 		case 't':
394 			mmc = opt_mmc();
395 			break;
396 		case 'c':
397 			opt_ch(&pmin, &pmax);
398 			break;
399 		case 'C':
400 			opt_ch(&rmin, &rmax);
401 			break;
402 		case 'e':
403 			opt_enc(&par);
404 			break;
405 		case 'r':
406 			rate = strtonum(optarg, RATE_MIN, RATE_MAX, &str);
407 			if (str)
408 				errx(1, "%s: rate is %s", optarg, str);
409 			break;
410 		case 'v':
411 			vol = strtonum(optarg, 0, MIDI_MAXCTL, &str);
412 			if (str)
413 				errx(1, "%s: volume is %s", optarg, str);
414 			break;
415 		case 's':
416 			if ((d = dev_list) == NULL) {
417 				d = mkdev(DEFAULT_DEV, &par, 0, bufsz, round, rate,
418 				    hold, autovol);
419 			}
420 			mkopt(optarg, d, pmin, pmax, rmin, rmax,
421 			    mode, vol, mmc, dup);
422 			break;
423 		case 'q':
424 			p = port_new(optarg, MODE_MIDIMASK, hold);
425 			if (!p)
426 				errx(1, "%s: can't open port", optarg);
427 			break;
428 		case 'a':
429 			hold = opt_onoff();
430 			break;
431 		case 'w':
432 			autovol = opt_onoff();
433 			break;
434 		case 'b':
435 			bufsz = strtonum(optarg, 1, RATE_MAX, &str);
436 			if (str)
437 				errx(1, "%s: buffer size is %s", optarg, str);
438 			break;
439 		case 'z':
440 			round = strtonum(optarg, 1, SHRT_MAX, &str);
441 			if (str)
442 				errx(1, "%s: block size is %s", optarg, str);
443 			break;
444 		case 'f':
445 			mkdev(optarg, &par, 0, bufsz, round, rate, hold, autovol);
446 			break;
447 		case 'M':
448 			/* XXX: for compatibility with aucat, remove this */
449 			break;
450 		default:
451 			fputs(usagestr, stderr);
452 			return 1;
453 		}
454 	}
455 	argc -= optind;
456 	argv += optind;
457 	if (argc > 0) {
458 		fputs(usagestr, stderr);
459 		return 1;
460 	}
461 	if (dev_list == NULL)
462 		mkdev(DEFAULT_DEV, &par, 0, bufsz, round, rate, hold, autovol);
463 	for (d = dev_list; d != NULL; d = d->next) {
464 		if (opt_byname("default", d->num))
465 			continue;
466 		mkopt("default", d, pmin, pmax, rmin, rmax,
467 		    mode, vol, mmc, dup);
468 	}
469 	getbasepath(base, sizeof(base));
470 	snprintf(path, PATH_MAX, "%s/%s%u", base, AUCAT_PATH, unit);
471 	listen_new_un(path);
472 	if (geteuid() == 0)
473 		privdrop();
474 	midi_init();
475 	for (p = port_list; p != NULL; p = p->next) {
476 		if (!port_init(p))
477 			return 1;
478 	}
479 	for (d = dev_list; d != NULL; d = d->next) {
480 		if (!dev_init(d))
481 			return 1;
482 	}
483 	for (l = listen_list; l != NULL; l = l->next) {
484 		if (!listen_init(l))
485 			return 1;
486 	}
487 	if (background) {
488 		log_flush();
489 		log_level = 0;
490 		if (daemon(0, 0) < 0)
491 			err(1, "daemon");
492 	}
493 
494 	/*
495 	 * Loop, start audio.
496 	 */
497 	for (;;) {
498 		if (quit_flag)
499 			break;
500 		if (!file_poll())
501 			break;
502 	}
503 	while (listen_list != NULL)
504 		listen_close(listen_list);
505 	while (sock_list != NULL)
506 		sock_close(sock_list);
507 	while (opt_list != NULL)
508 		opt_del(opt_list);
509 	for (d = dev_list; d != NULL; d = d->next)
510 		dev_done(d);
511 	for (p = port_list; p != NULL; p = p->next)
512 		port_done(p);
513 	midi_done();
514 	while (file_poll())
515 		; /* nothing */
516 	while (dev_list)
517 		dev_del(dev_list);
518 	while (port_list)
519 		port_del(port_list);
520 	filelist_done();
521 	rmdir(base);
522 	unsetsig();
523 	return 0;
524 }
525