Annotation of src/usr.bin/sndiod/sndiod.c, Revision 1.1
1.1 ! ratchov 1: /* $OpenBSD$ */
! 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:
! 339: while ((c = getopt(argc, argv, "a:b:c:C:de:f:j:L:m:q:r:s:t:U:v:w:x:z:")) != -1) {
! 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);
! 414: break;
! 415: default:
! 416: fputs(usagestr, stderr);
! 417: return 1;
! 418: }
! 419: }
! 420: argc -= optind;
! 421: argv += optind;
! 422: if (argc > 0) {
! 423: fputs(usagestr, stderr);
! 424: return 1;
! 425: }
! 426: if (dev_list == NULL)
! 427: mkdev(DEFAULT_DEV, &par, 0, bufsz, round, rate, hold, autovol);
! 428: for (d = dev_list; d != NULL; d = d->next) {
! 429: if (opt_byname("default", d->num))
! 430: continue;
! 431: mkopt("default", d, pmin, pmax, rmin, rmax,
! 432: mode, vol, mmc, dup);
! 433: }
! 434: getbasepath(base, sizeof(base));
! 435: snprintf(path, PATH_MAX, "%s/%s%u", base, AUCAT_PATH, unit);
! 436: listen_new_un(path);
! 437: if (geteuid() == 0)
! 438: privdrop();
! 439: midi_init();
! 440: for (p = port_list; p != NULL; p = p->next) {
! 441: if (!port_init(p))
! 442: return 1;
! 443: }
! 444: for (d = dev_list; d != NULL; d = d->next) {
! 445: if (!dev_init(d))
! 446: return 1;
! 447: if (d->autostart && (d->mode & MODE_AUDIOMASK))
! 448: dev_mmcstart(d);
! 449: }
! 450: for (l = listen_list; l != NULL; l = l->next) {
! 451: if (!listen_init(l))
! 452: return 1;
! 453: }
! 454: if (background) {
! 455: log_flush();
! 456: log_level = 0;
! 457: if (daemon(0, 0) < 0)
! 458: err(1, "daemon");
! 459: }
! 460:
! 461: /*
! 462: * Loop, start audio.
! 463: */
! 464: for (;;) {
! 465: if (quit_flag)
! 466: break;
! 467: if (!file_poll())
! 468: break;
! 469: }
! 470: while (listen_list != NULL)
! 471: listen_close(listen_list);
! 472: while (sock_list != NULL)
! 473: sock_close(sock_list);
! 474: while (opt_list != NULL)
! 475: opt_del(opt_list);
! 476: for (d = dev_list; d != NULL; d = d->next)
! 477: dev_done(d);
! 478: for (p = port_list; p != NULL; p = p->next)
! 479: port_done(p);
! 480: midi_done();
! 481: while (file_poll())
! 482: ; /* nothing */
! 483: while (dev_list)
! 484: dev_del(dev_list);
! 485: while (port_list)
! 486: port_del(port_list);
! 487: filelist_done();
! 488: rmdir(base);
! 489: unsetsig();
! 490: return 0;
! 491: }