Annotation of src/usr.bin/sndiod/sndiod.c, Revision 1.6
1.6 ! ratchov 1: /* $OpenBSD: sndiod.c,v 1.5 2013/11/18 17:37:45 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
1.5 ratchov 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);
1.1 ratchov 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:
1.4 ratchov 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:
1.1 ratchov 195: unsigned int
196: opt_mode(void)
197: {
198: unsigned int mode = 0;
199: char *p = optarg;
200:
1.4 ratchov 201: for (;;) {
202: if (getword("play", &p)) {
1.1 ratchov 203: mode |= MODE_PLAY;
1.4 ratchov 204: } else if (getword("rec", &p)) {
1.1 ratchov 205: mode |= MODE_REC;
1.4 ratchov 206: } else if (getword("mon", &p)) {
1.1 ratchov 207: mode |= MODE_MON;
1.4 ratchov 208: } else if (getword("midi", &p)) {
1.1 ratchov 209: mode |= MODE_MIDIMASK;
1.4 ratchov 210: } else
1.1 ratchov 211: errx(1, "%s: bad mode", optarg);
212: if (*p == '\0')
213: break;
1.4 ratchov 214: p++;
1.1 ratchov 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:
1.2 ratchov 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) {
1.1 ratchov 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':
1.3 ratchov 424: p = port_new(optarg, MODE_MIDIMASK, hold);
1.1 ratchov 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);
1.2 ratchov 446: break;
447: case 'M':
1.4 ratchov 448: /* XXX: for compatibility with aucat, remove this */
1.1 ratchov 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: }