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