Annotation of src/usr.bin/sndiod/sndiod.c, Revision 1.26
1.18 ratchov 1: /* $OpenBSD$ */
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/stat.h>
18: #include <sys/types.h>
19: #include <sys/resource.h>
1.18 ratchov 20: #include <sys/socket.h>
1.1 ratchov 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"
1.18 ratchov 38: #include "fdpass.h"
1.1 ratchov 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: /*
1.18 ratchov 54: * privileged user name
55: */
56: #ifndef SNDIO_PRIV_USER
57: #define SNDIO_PRIV_USER "_sndiop"
58: #endif
59:
60: /*
1.1 ratchov 61: * priority when run as root
62: */
63: #ifndef SNDIO_PRIO
64: #define SNDIO_PRIO (-20)
65: #endif
66:
67: /*
68: * sample rate if no ``-r'' is used
69: */
70: #ifndef DEFAULT_RATE
71: #define DEFAULT_RATE 48000
72: #endif
73:
74: /*
75: * block size if neither ``-z'' nor ``-b'' is used
76: */
77: #ifndef DEFAULT_ROUND
78: #define DEFAULT_ROUND 960
79: #endif
80:
81: /*
82: * buffer size if neither ``-z'' nor ``-b'' is used
83: */
84: #ifndef DEFAULT_BUFSZ
1.8 dcoppa 85: #define DEFAULT_BUFSZ 7680
1.1 ratchov 86: #endif
87:
88: /*
89: * default device in server mode
90: */
91: #ifndef DEFAULT_DEV
92: #define DEFAULT_DEV "rsnd/0"
93: #endif
1.5 ratchov 94:
95: void sigint(int);
96: void opt_ch(int *, int *);
97: void opt_enc(struct aparams *);
98: int opt_mmc(void);
99: int opt_onoff(void);
100: int getword(char *, char **);
101: unsigned int opt_mode(void);
102: void getbasepath(char *, size_t);
103: void setsig(void);
104: void unsetsig(void);
105: struct dev *mkdev(char *, struct aparams *,
106: int, int, int, int, int, int);
107: struct opt *mkopt(char *, struct dev *,
108: int, int, int, int, int, int, int, int);
1.1 ratchov 109:
110: unsigned int log_level = 0;
111: volatile sig_atomic_t quit_flag = 0;
112:
113: char usagestr[] = "usage: sndiod [-d] [-a flag] [-b nframes] "
114: "[-C min:max] [-c min:max] [-e enc]\n\t"
115: "[-f device] [-j flag] [-L addr] [-m mode] [-q port] [-r rate]\n\t"
116: "[-s name] [-t mode] [-U unit] [-v volume] [-w flag] [-z nframes]\n";
117:
118: /*
119: * SIGINT handler, it raises the quit flag. If the flag is already set,
120: * that means that the last SIGINT was not handled, because the process
121: * is blocked somewhere, so exit.
122: */
123: void
124: sigint(int s)
125: {
126: if (quit_flag)
127: _exit(1);
128: quit_flag = 1;
129: }
130:
131: void
132: opt_ch(int *rcmin, int *rcmax)
133: {
134: char *next, *end;
135: long cmin, cmax;
136:
137: errno = 0;
138: cmin = strtol(optarg, &next, 10);
139: if (next == optarg || *next != ':')
140: goto failed;
141: cmax = strtol(++next, &end, 10);
142: if (end == next || *end != '\0')
143: goto failed;
144: if (cmin < 0 || cmax < cmin || cmax >= NCHAN_MAX)
145: goto failed;
146: *rcmin = cmin;
147: *rcmax = cmax;
148: return;
149: failed:
150: errx(1, "%s: bad channel range", optarg);
151: }
152:
153: void
154: opt_enc(struct aparams *par)
155: {
156: int len;
157:
158: len = aparams_strtoenc(par, optarg);
159: if (len == 0 || optarg[len] != '\0')
160: errx(1, "%s: bad encoding", optarg);
161: }
162:
163: int
164: opt_mmc(void)
165: {
166: if (strcmp("off", optarg) == 0)
167: return 0;
168: if (strcmp("slave", optarg) == 0)
169: return 1;
170: errx(1, "%s: off/slave expected", optarg);
171: }
172:
173: int
174: opt_onoff(void)
175: {
176: if (strcmp("off", optarg) == 0)
177: return 0;
178: if (strcmp("on", optarg) == 0)
179: return 1;
180: errx(1, "%s: on/off expected", optarg);
181: }
182:
1.4 ratchov 183: int
184: getword(char *word, char **str)
185: {
186: char *p = *str;
187:
188: for (;;) {
189: if (*word == '\0')
190: break;
191: if (*word++ != *p++)
192: return 0;
193: }
194: if (*p == ',' || *p == '\0') {
195: *str = p;
196: return 1;
197: }
198: return 0;
199: }
200:
1.1 ratchov 201: unsigned int
202: opt_mode(void)
203: {
204: unsigned int mode = 0;
205: char *p = optarg;
206:
1.4 ratchov 207: for (;;) {
208: if (getword("play", &p)) {
1.1 ratchov 209: mode |= MODE_PLAY;
1.4 ratchov 210: } else if (getword("rec", &p)) {
1.1 ratchov 211: mode |= MODE_REC;
1.4 ratchov 212: } else if (getword("mon", &p)) {
1.1 ratchov 213: mode |= MODE_MON;
1.4 ratchov 214: } else if (getword("midi", &p)) {
1.1 ratchov 215: mode |= MODE_MIDIMASK;
1.4 ratchov 216: } else
1.1 ratchov 217: errx(1, "%s: bad mode", optarg);
218: if (*p == '\0')
219: break;
1.4 ratchov 220: p++;
1.1 ratchov 221: }
222: if (mode == 0)
223: errx(1, "empty mode");
224: return mode;
225: }
226:
227: void
228: setsig(void)
229: {
230: struct sigaction sa;
231:
232: quit_flag = 0;
233: sigfillset(&sa.sa_mask);
234: sa.sa_flags = SA_RESTART;
235: sa.sa_handler = sigint;
236: if (sigaction(SIGINT, &sa, NULL) < 0)
237: err(1, "sigaction(int) failed");
238: if (sigaction(SIGTERM, &sa, NULL) < 0)
239: err(1, "sigaction(term) failed");
240: if (sigaction(SIGHUP, &sa, NULL) < 0)
241: err(1, "sigaction(hup) failed");
242: }
243:
244: void
245: unsetsig(void)
246: {
247: struct sigaction sa;
248:
249: sigfillset(&sa.sa_mask);
250: sa.sa_flags = SA_RESTART;
251: sa.sa_handler = SIG_DFL;
252: if (sigaction(SIGHUP, &sa, NULL) < 0)
253: err(1, "unsetsig(hup): sigaction failed\n");
254: if (sigaction(SIGTERM, &sa, NULL) < 0)
255: err(1, "unsetsig(term): sigaction failed\n");
256: if (sigaction(SIGINT, &sa, NULL) < 0)
257: err(1, "unsetsig(int): sigaction failed\n");
258: }
259:
260: void
261: getbasepath(char *base, size_t size)
262: {
263: uid_t uid;
264: struct stat sb;
1.14 ratchov 265: mode_t mask, omask;
1.1 ratchov 266:
267: uid = geteuid();
268: if (uid == 0) {
269: mask = 022;
1.10 ratchov 270: snprintf(base, SOCKPATH_MAX, SOCKPATH_DIR);
1.1 ratchov 271: } else {
272: mask = 077;
1.10 ratchov 273: snprintf(base, SOCKPATH_MAX, SOCKPATH_DIR "-%u", uid);
1.1 ratchov 274: }
1.14 ratchov 275: omask = umask(mask);
276: if (mkdir(base, 0777) < 0) {
1.1 ratchov 277: if (errno != EEXIST)
278: err(1, "mkdir(\"%s\")", base);
279: }
1.14 ratchov 280: umask(omask);
1.1 ratchov 281: if (stat(base, &sb) < 0)
282: err(1, "stat(\"%s\")", base);
283: if (sb.st_uid != uid || (sb.st_mode & mask) != 0)
284: errx(1, "%s has wrong permissions", base);
285: }
286:
287: struct dev *
288: mkdev(char *path, struct aparams *par,
289: int mode, int bufsz, int round, int rate, int hold, int autovol)
290: {
291: struct dev *d;
292:
293: for (d = dev_list; d != NULL; d = d->next) {
294: if (strcmp(d->path, path) == 0)
295: return d;
296: }
297: if (!bufsz && !round) {
298: round = DEFAULT_ROUND;
299: bufsz = DEFAULT_BUFSZ;
300: } else if (!bufsz) {
301: bufsz = round * 2;
302: } else if (!round)
303: round = bufsz / 2;
304: d = dev_new(path, par, mode, bufsz, round, rate, hold, autovol);
305: if (d == NULL)
306: exit(1);
307: return d;
308: }
309:
310: struct opt *
311: mkopt(char *path, struct dev *d,
312: int pmin, int pmax, int rmin, int rmax,
313: int mode, int vol, int mmc, int dup)
314: {
315: struct opt *o;
316:
317: o = opt_new(path, d, pmin, pmax, rmin, rmax,
318: MIDI_TO_ADATA(vol), mmc, dup, mode);
319: if (o == NULL)
1.26 ! ratchov 320: return NULL;
1.1 ratchov 321: dev_adjpar(d, o->mode, o->pmin, o->pmax, o->rmin, o->rmax);
322: return o;
323: }
324:
325: int
326: main(int argc, char **argv)
327: {
328: int c, background, unit;
329: int pmin, pmax, rmin, rmax;
1.25 ratchov 330: char base[SOCKPATH_MAX], path[SOCKPATH_MAX];
1.1 ratchov 331: unsigned int mode, dup, mmc, vol;
332: unsigned int hold, autovol, bufsz, round, rate;
333: const char *str;
334: struct aparams par;
335: struct dev *d;
336: struct port *p;
337: struct listen *l;
1.17 ratchov 338: struct passwd *pw;
1.25 ratchov 339: struct tcpaddr {
340: char *host;
341: struct tcpaddr *next;
342: } *tcpaddr_list, *ta;
1.18 ratchov 343: int s[2];
344: pid_t pid;
1.20 ratchov 345: uid_t euid, hpw_uid, wpw_uid;
346: gid_t hpw_gid, wpw_gid;
347: char *wpw_dir;
1.1 ratchov 348:
349: atexit(log_flush);
350:
351: /*
352: * global options defaults
353: */
354: vol = 118;
355: dup = 1;
356: mmc = 0;
357: hold = 0;
358: autovol = 1;
359: bufsz = 0;
360: round = 0;
361: rate = DEFAULT_RATE;
362: unit = 0;
363: background = 1;
364: pmin = 0;
365: pmax = 1;
366: rmin = 0;
367: rmax = 1;
368: aparams_init(&par);
369: mode = MODE_PLAY | MODE_REC;
1.25 ratchov 370: tcpaddr_list = NULL;
1.1 ratchov 371:
1.15 ratchov 372: 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 373: switch (c) {
374: case 'd':
375: log_level++;
376: background = 0;
377: break;
378: case 'U':
379: unit = strtonum(optarg, 0, 15, &str);
380: if (str)
381: errx(1, "%s: unit number is %s", optarg, str);
382: break;
383: case 'L':
1.25 ratchov 384: ta = xmalloc(sizeof(struct tcpaddr));
385: ta->host = optarg;
386: ta->next = tcpaddr_list;
387: tcpaddr_list = ta;
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: }
1.26 ! ratchov 422: if (mkopt(optarg, d, pmin, pmax, rmin, rmax,
! 423: mode, vol, mmc, dup) == NULL)
! 424: return 1;
1.1 ratchov 425: break;
426: case 'q':
1.3 ratchov 427: p = port_new(optarg, MODE_MIDIMASK, hold);
1.1 ratchov 428: if (!p)
429: errx(1, "%s: can't open port", optarg);
430: break;
431: case 'a':
432: hold = opt_onoff();
433: break;
434: case 'w':
435: autovol = opt_onoff();
436: break;
437: case 'b':
438: bufsz = strtonum(optarg, 1, RATE_MAX, &str);
439: if (str)
440: errx(1, "%s: buffer size is %s", optarg, str);
441: break;
442: case 'z':
443: round = strtonum(optarg, 1, SHRT_MAX, &str);
444: if (str)
445: errx(1, "%s: block size is %s", optarg, str);
446: break;
447: case 'f':
448: mkdev(optarg, &par, 0, bufsz, round, rate, hold, autovol);
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;
1.26 ! ratchov 466: if (mkopt("default", d, pmin, pmax, rmin, rmax,
! 467: mode, vol, mmc, dup) == NULL)
! 468: return 1;
1.1 ratchov 469: }
1.17 ratchov 470:
471: setsig();
472: filelist_init();
473:
1.20 ratchov 474: euid = geteuid();
475: if (euid == 0) {
476: if ((pw = getpwnam(SNDIO_PRIV_USER)) == NULL)
477: errx(1, "unknown user %s", SNDIO_PRIV_USER);
478: hpw_uid = pw->pw_uid;
479: hpw_gid = pw->pw_gid;
480: if ((pw = getpwnam(SNDIO_USER)) == NULL)
481: errx(1, "unknown user %s", SNDIO_USER);
482: wpw_uid = pw->pw_uid;
483: wpw_gid = pw->pw_gid;
484: wpw_dir = xstrdup(pw->pw_dir);
485: } else {
1.21 ratchov 486: hpw_uid = wpw_uid = hpw_gid = wpw_gid = 0xdeadbeef;
487: wpw_dir = NULL;
1.20 ratchov 488: }
489:
1.18 ratchov 490: /* start subprocesses */
491:
492: if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) < 0) {
493: perror("socketpair");
494: return 1;
1.13 ratchov 495: }
1.18 ratchov 496: pid = fork();
497: if (pid == -1) {
498: log_puts("can't fork\n");
499: return 1;
1.1 ratchov 500: }
1.18 ratchov 501: if (pid == 0) {
502: setproctitle("helper");
503: close(s[0]);
504: if (fdpass_new(s[1], &helper_fileops) == NULL)
1.1 ratchov 505: return 1;
1.18 ratchov 506: if (background) {
507: log_flush();
508: log_level = 0;
509: if (daemon(0, 0) < 0)
510: err(1, "daemon");
511: }
1.20 ratchov 512: if (euid == 0) {
513: if (setgroups(1, &hpw_gid) ||
514: setresgid(hpw_gid, hpw_gid, hpw_gid) ||
515: setresuid(hpw_uid, hpw_uid, hpw_uid))
1.18 ratchov 516: err(1, "cannot drop privileges");
517: }
1.22 ratchov 518: if (pledge("stdio sendfd rpath wpath", NULL) < 0)
519: err(1, "pledge");
1.18 ratchov 520: while (file_poll())
521: ; /* nothing */
522: } else {
523: close(s[1]);
524: if (fdpass_new(s[0], &worker_fileops) == NULL)
1.1 ratchov 525: return 1;
1.18 ratchov 526:
527: getbasepath(base, sizeof(base));
528: snprintf(path,
529: SOCKPATH_MAX, "%s/" SOCKPATH_FILE "%u",
530: base, unit);
1.23 ratchov 531: if (!listen_new_un(path))
532: return 1;
1.25 ratchov 533: for (ta = tcpaddr_list; ta != NULL; ta = ta->next) {
534: if (!listen_new_tcp(ta->host, AUCAT_PORT + unit))
1.23 ratchov 535: return 1;
536: }
1.18 ratchov 537: for (l = listen_list; l != NULL; l = l->next) {
538: if (!listen_init(l))
539: return 1;
540: }
541:
542: midi_init();
543: for (p = port_list; p != NULL; p = p->next) {
544: if (!port_init(p))
545: return 1;
546: }
547: for (d = dev_list; d != NULL; d = d->next) {
548: if (!dev_init(d))
549: return 1;
550: }
551: if (background) {
552: log_flush();
553: log_level = 0;
554: if (daemon(0, 0) < 0)
555: err(1, "daemon");
556: }
1.20 ratchov 557: if (euid == 0) {
1.18 ratchov 558: if (setpriority(PRIO_PROCESS, 0, SNDIO_PRIO) < 0)
559: err(1, "setpriority");
1.20 ratchov 560: if (chroot(wpw_dir) != 0 || chdir("/") != 0)
561: err(1, "cannot chroot to %s", wpw_dir);
562: if (setgroups(1, &wpw_gid) ||
563: setresgid(wpw_gid, wpw_gid, wpw_gid) ||
564: setresuid(wpw_uid, wpw_uid, wpw_uid))
1.18 ratchov 565: err(1, "cannot drop privileges");
1.22 ratchov 566: }
1.25 ratchov 567: if (tcpaddr_list) {
1.22 ratchov 568: if (pledge("stdio audio recvfd unix inet", NULL) == -1)
569: err(1, "pledge");
570: } else {
571: if (pledge("stdio audio recvfd unix", NULL) == -1)
572: err(1, "pledge");
1.18 ratchov 573: }
574: for (;;) {
575: if (quit_flag)
576: break;
577: if (!fdpass_peer)
578: break;
579: if (!file_poll())
580: break;
581: }
582: if (fdpass_peer)
583: fdpass_close(fdpass_peer);
584: while (listen_list != NULL)
585: listen_close(listen_list);
586: while (sock_list != NULL)
587: sock_close(sock_list);
588: for (d = dev_list; d != NULL; d = d->next)
589: dev_done(d);
590: for (p = port_list; p != NULL; p = p->next)
591: port_done(p);
592: while (file_poll())
593: ; /* nothing */
594: midi_done();
1.1 ratchov 595: }
596: while (opt_list != NULL)
597: opt_del(opt_list);
598: while (dev_list)
599: dev_del(dev_list);
600: while (port_list)
601: port_del(port_list);
1.25 ratchov 602: while (tcpaddr_list) {
603: ta = tcpaddr_list;
604: tcpaddr_list = ta->next;
605: xfree(ta);
606: }
1.1 ratchov 607: filelist_done();
608: unsetsig();
609: return 0;
610: }