Annotation of src/usr.bin/sndiod/sndiod.c, Revision 1.2
1.2 ! ratchov 1: /* $OpenBSD: sndiod.c,v 1.1 2012/11/23 07:03:28 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:
161: unsigned int
162: opt_mode(void)
163: {
164: unsigned int mode = 0;
165: char *p = optarg;
166: size_t len;
167:
168: for (p = optarg; *p != '\0'; p++) {
169: len = strcspn(p, ",");
170: if (strncmp("play", p, len) == 0) {
171: mode |= MODE_PLAY;
172: } else if (strncmp("rec", p, len) == 0) {
173: mode |= MODE_REC;
174: } else if (strncmp("mon", p, len) == 0) {
175: mode |= MODE_MON;
176: } else if (strncmp("midi", p, len) == 0) {
177: mode |= MODE_MIDIMASK;
178: } else
179: errx(1, "%s: bad mode", optarg);
180: p += len;
181: if (*p == '\0')
182: break;
183: }
184: if (mode == 0)
185: errx(1, "empty mode");
186: return mode;
187: }
188:
189: void
190: setsig(void)
191: {
192: struct sigaction sa;
193:
194: quit_flag = 0;
195: sigfillset(&sa.sa_mask);
196: sa.sa_flags = SA_RESTART;
197: sa.sa_handler = sigint;
198: if (sigaction(SIGINT, &sa, NULL) < 0)
199: err(1, "sigaction(int) failed");
200: if (sigaction(SIGTERM, &sa, NULL) < 0)
201: err(1, "sigaction(term) failed");
202: if (sigaction(SIGHUP, &sa, NULL) < 0)
203: err(1, "sigaction(hup) failed");
204: }
205:
206: void
207: unsetsig(void)
208: {
209: struct sigaction sa;
210:
211: sigfillset(&sa.sa_mask);
212: sa.sa_flags = SA_RESTART;
213: sa.sa_handler = SIG_DFL;
214: if (sigaction(SIGHUP, &sa, NULL) < 0)
215: err(1, "unsetsig(hup): sigaction failed\n");
216: if (sigaction(SIGTERM, &sa, NULL) < 0)
217: err(1, "unsetsig(term): sigaction failed\n");
218: if (sigaction(SIGINT, &sa, NULL) < 0)
219: err(1, "unsetsig(int): sigaction failed\n");
220: }
221:
222: void
223: getbasepath(char *base, size_t size)
224: {
225: uid_t uid;
226: struct stat sb;
227: mode_t mask;
228:
229: uid = geteuid();
230: if (uid == 0) {
231: mask = 022;
232: snprintf(base, PATH_MAX, "/tmp/aucat");
233: } else {
234: mask = 077;
235: snprintf(base, PATH_MAX, "/tmp/aucat-%u", uid);
236: }
237: if (mkdir(base, 0777 & ~mask) < 0) {
238: if (errno != EEXIST)
239: err(1, "mkdir(\"%s\")", base);
240: }
241: if (stat(base, &sb) < 0)
242: err(1, "stat(\"%s\")", base);
243: if (sb.st_uid != uid || (sb.st_mode & mask) != 0)
244: errx(1, "%s has wrong permissions", base);
245: }
246:
247: void
248: privdrop(void)
249: {
250: struct passwd *pw;
251:
252: if ((pw = getpwnam(SNDIO_USER)) == NULL)
253: errx(1, "unknown user %s", SNDIO_USER);
254: if (setpriority(PRIO_PROCESS, 0, SNDIO_PRIO) < 0)
255: err(1, "setpriority");
256: if (setgroups(1, &pw->pw_gid) ||
257: setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
258: setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
259: err(1, "cannot drop privileges");
260: }
261:
262: struct dev *
263: mkdev(char *path, struct aparams *par,
264: int mode, int bufsz, int round, int rate, int hold, int autovol)
265: {
266: struct dev *d;
267:
268: for (d = dev_list; d != NULL; d = d->next) {
269: if (strcmp(d->path, path) == 0)
270: return d;
271: }
272: if (!bufsz && !round) {
273: round = DEFAULT_ROUND;
274: bufsz = DEFAULT_BUFSZ;
275: } else if (!bufsz) {
276: bufsz = round * 2;
277: } else if (!round)
278: round = bufsz / 2;
279: d = dev_new(path, par, mode, bufsz, round, rate, hold, autovol);
280: if (d == NULL)
281: exit(1);
282: return d;
283: }
284:
285: struct opt *
286: mkopt(char *path, struct dev *d,
287: int pmin, int pmax, int rmin, int rmax,
288: int mode, int vol, int mmc, int dup)
289: {
290: struct opt *o;
291:
292: o = opt_new(path, d, pmin, pmax, rmin, rmax,
293: MIDI_TO_ADATA(vol), mmc, dup, mode);
294: if (o == NULL)
295: errx(1, "%s: couldn't create subdev", path);
296: dev_adjpar(d, o->mode, o->pmin, o->pmax, o->rmin, o->rmax);
297: return o;
298: }
299:
300: int
301: main(int argc, char **argv)
302: {
303: int c, background, unit;
304: int pmin, pmax, rmin, rmax;
305: char base[PATH_MAX], path[PATH_MAX];
306: unsigned int mode, dup, mmc, vol;
307: unsigned int hold, autovol, bufsz, round, rate;
308: const char *str;
309: struct aparams par;
310: struct dev *d;
311: struct port *p;
312: struct listen *l;
313:
314: atexit(log_flush);
315:
316: /*
317: * global options defaults
318: */
319: vol = 118;
320: dup = 1;
321: mmc = 0;
322: hold = 0;
323: autovol = 1;
324: bufsz = 0;
325: round = 0;
326: rate = DEFAULT_RATE;
327: unit = 0;
328: background = 1;
329: pmin = 0;
330: pmax = 1;
331: rmin = 0;
332: rmax = 1;
333: aparams_init(&par);
334: mode = MODE_PLAY | MODE_REC;
335:
336: setsig();
337: filelist_init();
338:
1.2 ! ratchov 339: 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 340: switch (c) {
341: case 'd':
342: log_level++;
343: background = 0;
344: break;
345: case 'U':
346: if (listen_list)
347: errx(1, "-U must come before -L");
348: unit = strtonum(optarg, 0, 15, &str);
349: if (str)
350: errx(1, "%s: unit number is %s", optarg, str);
351: break;
352: case 'L':
353: listen_new_tcp(optarg, AUCAT_PORT + unit);
354: break;
355: case 'm':
356: mode = opt_mode();
357: break;
358: case 'j':
359: dup = opt_onoff();
360: break;
361: case 't':
362: mmc = opt_mmc();
363: break;
364: case 'c':
365: opt_ch(&pmin, &pmax);
366: break;
367: case 'C':
368: opt_ch(&rmin, &rmax);
369: break;
370: case 'e':
371: opt_enc(&par);
372: break;
373: case 'r':
374: rate = strtonum(optarg, RATE_MIN, RATE_MAX, &str);
375: if (str)
376: errx(1, "%s: rate is %s", optarg, str);
377: break;
378: case 'v':
379: vol = strtonum(optarg, 0, MIDI_MAXCTL, &str);
380: if (str)
381: errx(1, "%s: volume is %s", optarg, str);
382: break;
383: case 's':
384: if ((d = dev_list) == NULL) {
385: d = mkdev(DEFAULT_DEV, &par, 0, bufsz, round, rate,
386: hold, autovol);
387: }
388: mkopt(optarg, d, pmin, pmax, rmin, rmax,
389: mode, vol, mmc, dup);
390: break;
391: case 'q':
392: p = port_new(optarg, MODE_MIDIMASK);
393: if (!p)
394: errx(1, "%s: can't open port", optarg);
395: break;
396: case 'a':
397: hold = opt_onoff();
398: break;
399: case 'w':
400: autovol = opt_onoff();
401: break;
402: case 'b':
403: bufsz = strtonum(optarg, 1, RATE_MAX, &str);
404: if (str)
405: errx(1, "%s: buffer size is %s", optarg, str);
406: break;
407: case 'z':
408: round = strtonum(optarg, 1, SHRT_MAX, &str);
409: if (str)
410: errx(1, "%s: block size is %s", optarg, str);
411: break;
412: case 'f':
413: mkdev(optarg, &par, 0, bufsz, round, rate, hold, autovol);
1.2 ! ratchov 414: break;
! 415: case 'M':
! 416: /* XXX: compatibility with aucat */
1.1 ratchov 417: break;
418: default:
419: fputs(usagestr, stderr);
420: return 1;
421: }
422: }
423: argc -= optind;
424: argv += optind;
425: if (argc > 0) {
426: fputs(usagestr, stderr);
427: return 1;
428: }
429: if (dev_list == NULL)
430: mkdev(DEFAULT_DEV, &par, 0, bufsz, round, rate, hold, autovol);
431: for (d = dev_list; d != NULL; d = d->next) {
432: if (opt_byname("default", d->num))
433: continue;
434: mkopt("default", d, pmin, pmax, rmin, rmax,
435: mode, vol, mmc, dup);
436: }
437: getbasepath(base, sizeof(base));
438: snprintf(path, PATH_MAX, "%s/%s%u", base, AUCAT_PATH, unit);
439: listen_new_un(path);
440: if (geteuid() == 0)
441: privdrop();
442: midi_init();
443: for (p = port_list; p != NULL; p = p->next) {
444: if (!port_init(p))
445: return 1;
446: }
447: for (d = dev_list; d != NULL; d = d->next) {
448: if (!dev_init(d))
449: return 1;
450: if (d->autostart && (d->mode & MODE_AUDIOMASK))
451: dev_mmcstart(d);
452: }
453: for (l = listen_list; l != NULL; l = l->next) {
454: if (!listen_init(l))
455: return 1;
456: }
457: if (background) {
458: log_flush();
459: log_level = 0;
460: if (daemon(0, 0) < 0)
461: err(1, "daemon");
462: }
463:
464: /*
465: * Loop, start audio.
466: */
467: for (;;) {
468: if (quit_flag)
469: break;
470: if (!file_poll())
471: break;
472: }
473: while (listen_list != NULL)
474: listen_close(listen_list);
475: while (sock_list != NULL)
476: sock_close(sock_list);
477: while (opt_list != NULL)
478: opt_del(opt_list);
479: for (d = dev_list; d != NULL; d = d->next)
480: dev_done(d);
481: for (p = port_list; p != NULL; p = p->next)
482: port_done(p);
483: midi_done();
484: while (file_poll())
485: ; /* nothing */
486: while (dev_list)
487: dev_del(dev_list);
488: while (port_list)
489: port_del(port_list);
490: filelist_done();
491: rmdir(base);
492: unsetsig();
493: return 0;
494: }