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