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