Annotation of src/usr.bin/aucat/aucat.c, Revision 1.89
1.89 ! ratchov 1: /* $OpenBSD: aucat.c,v 1.88 2010/04/24 14:33:46 ratchov Exp $ */
1.1 kstailey 2: /*
1.15 ratchov 3: * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
1.1 kstailey 4: *
1.15 ratchov 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>
1.55 ratchov 19: #include <sys/stat.h>
1.62 ratchov 20: #include <sys/types.h>
1.86 ratchov 21: #include <sys/resource.h>
1.13 uwe 22:
1.15 ratchov 23: #include <err.h>
1.55 ratchov 24: #include <errno.h>
1.1 kstailey 25: #include <fcntl.h>
1.55 ratchov 26: #include <limits.h>
1.86 ratchov 27: #include <pwd.h>
1.15 ratchov 28: #include <signal.h>
1.1 kstailey 29: #include <stdio.h>
1.4 millert 30: #include <stdlib.h>
1.8 david 31: #include <string.h>
1.1 kstailey 32: #include <unistd.h>
1.15 ratchov 33: #include <varargs.h>
1.1 kstailey 34:
1.62 ratchov 35: #include "abuf.h"
1.15 ratchov 36: #include "aparams.h"
37: #include "aproc.h"
1.62 ratchov 38: #include "conf.h"
39: #include "dev.h"
1.28 ratchov 40: #include "listen.h"
1.61 ratchov 41: #include "midi.h"
42: #include "opt.h"
1.62 ratchov 43: #include "wav.h"
1.78 ratchov 44: #ifdef DEBUG
45: #include "dbg.h"
46: #endif
1.11 jaredy 47:
1.86 ratchov 48: /*
49: * unprivileged user name
50: */
51: #define SNDIO_USER "_sndio"
52:
53: /*
54: * priority when run as root
55: */
56: #define SNDIO_PRIO (-20)
57:
1.61 ratchov 58: #define PROG_AUCAT "aucat"
59: #define PROG_MIDICAT "midicat"
60:
1.78 ratchov 61: #ifdef DEBUG
62: int debug_level = 0;
63: #endif
1.28 ratchov 64: volatile int quit_flag = 0;
1.7 deraadt 65:
1.28 ratchov 66: /*
67: * SIGINT handler, it raises the quit flag. If the flag is already set,
68: * that means that the last SIGINT was not handled, because the process
1.62 ratchov 69: * is blocked somewhere, so exit.
1.28 ratchov 70: */
71: void
72: sigint(int s)
73: {
74: if (quit_flag)
75: _exit(1);
76: quit_flag = 1;
77: }
1.22 ratchov 78:
1.78 ratchov 79: #ifdef DEBUG
80: /*
81: * Increase debug level on SIGUSR1.
82: */
83: void
84: sigusr1(int s)
85: {
86: if (debug_level < 4)
87: debug_level++;
88: }
89:
90: /*
91: * Decrease debug level on SIGUSR2.
92: */
93: void
94: sigusr2(int s)
95: {
96: if (debug_level > 0)
97: debug_level--;
98: }
99: #endif
1.15 ratchov 100:
101: void
102: opt_ch(struct aparams *par)
103: {
1.76 ratchov 104: char *next, *end;
105: long cmin, cmax;
1.13 uwe 106:
1.76 ratchov 107: errno = 0;
108: cmin = strtol(optarg, &next, 10);
109: if (next == optarg || *next != ':')
110: goto failed;
111: if (errno == ERANGE && (cmin == LONG_MIN || cmin == LONG_MAX))
112: goto failed;
113: cmax = strtol(++next, &end, 10);
114: if (end == next || *end != '\0')
115: goto failed;
116: if (errno == ERANGE && (cmax == LONG_MIN || cmax == LONG_MAX))
117: goto failed;
118: if (cmin < 0 || cmax < cmin || cmax > NCHAN_MAX)
119: goto failed;
120: par->cmin = cmin;
121: par->cmax = cmax;
122: return;
123: failed:
124: errx(1, "%s: bad channel range", optarg);
1.15 ratchov 125: }
1.13 uwe 126:
1.15 ratchov 127: void
128: opt_enc(struct aparams *par)
129: {
1.28 ratchov 130: int len;
131:
132: len = aparams_strtoenc(par, optarg);
133: if (len == 0 || optarg[len] != '\0')
134: errx(1, "%s: bad encoding", optarg);
1.15 ratchov 135: }
1.4 millert 136:
1.15 ratchov 137: int
138: opt_hdr(void)
139: {
140: if (strcmp("auto", optarg) == 0)
141: return HDR_AUTO;
142: if (strcmp("raw", optarg) == 0)
143: return HDR_RAW;
144: if (strcmp("wav", optarg) == 0)
145: return HDR_WAV;
1.35 ratchov 146: errx(1, "%s: bad header specification", optarg);
1.1 kstailey 147: }
148:
1.22 ratchov 149: int
1.74 ratchov 150: opt_mmc(void)
151: {
152: if (strcmp("off", optarg) == 0)
153: return 0;
154: if (strcmp("slave", optarg) == 0)
155: return 1;
156: errx(1, "%s: bad MMC mode", optarg);
157: }
158:
159: int
1.84 ratchov 160: opt_join(void)
161: {
162: if (strcmp("off", optarg) == 0)
163: return 0;
164: if (strcmp("on", optarg) == 0)
165: return 1;
166: errx(1, "%s: bad join/expand setting", optarg);
167: }
168:
169: int
1.22 ratchov 170: opt_xrun(void)
171: {
172: if (strcmp("ignore", optarg) == 0)
173: return XRUN_IGNORE;
174: if (strcmp("sync", optarg) == 0)
175: return XRUN_SYNC;
176: if (strcmp("error", optarg) == 0)
177: return XRUN_ERROR;
1.73 ratchov 178: errx(1, "%s: bad underrun/overrun policy", optarg);
1.22 ratchov 179: }
180:
1.83 ratchov 181: unsigned
1.43 ratchov 182: opt_mode(void)
183: {
1.83 ratchov 184: unsigned mode = 0;
185: char *p = optarg;
186: size_t len;
187:
188: for (p = optarg; *p != NULL; p++) {
189: len = strcspn(p, ",");
190: if (strncmp("play", p, len) == 0) {
191: mode |= MODE_PLAY;
192: } else if (strncmp("rec", p, len) == 0) {
193: mode |= MODE_REC;
194: } else if (strncmp("mon", p, len) == 0) {
195: mode |= MODE_MON;
196: } else if (strncmp("duplex", p, len) == 0) {
197: /* XXX: backward compat, remove this */
198: mode |= MODE_REC | MODE_PLAY;
199: } else
200: errx(1, "%s: bad mode", optarg);
201: p += len;
202: if (*p == '\0')
203: break;
204: }
205: if (mode == 0)
206: errx(1, "empty mode");
207: return mode;
1.43 ratchov 208: }
209:
1.13 uwe 210: /*
1.42 ratchov 211: * Arguments of -i, -o and -s options are stored in a list.
1.13 uwe 212: */
1.15 ratchov 213: struct farg {
214: SLIST_ENTRY(farg) entry;
1.42 ratchov 215: struct aparams ipar; /* input (read) parameters */
216: struct aparams opar; /* output (write) parameters */
1.15 ratchov 217: unsigned vol; /* last requested volume */
218: char *name; /* optarg pointer (no need to copy it */
219: int hdr; /* header format */
1.22 ratchov 220: int xrun; /* overrun/underrun policy */
1.74 ratchov 221: int mmc; /* MMC mode */
1.84 ratchov 222: int join; /* join/expand enabled */
1.83 ratchov 223: unsigned mode;
1.15 ratchov 224: };
1.13 uwe 225:
1.15 ratchov 226: SLIST_HEAD(farglist, farg);
1.13 uwe 227:
1.15 ratchov 228: /*
229: * Add a farg entry to the given list, corresponding
230: * to the given file name.
231: */
232: void
1.52 ratchov 233: farg_add(struct farglist *list,
1.42 ratchov 234: struct aparams *ipar, struct aparams *opar, unsigned vol,
1.84 ratchov 235: int hdr, int xrun, int mmc, int join, unsigned mode, char *name)
1.15 ratchov 236: {
237: struct farg *fa;
238: size_t namelen;
1.52 ratchov 239:
1.15 ratchov 240: fa = malloc(sizeof(struct farg));
241: if (fa == NULL)
1.77 ratchov 242: err(1, "%s", name);
1.15 ratchov 243:
244: if (hdr == HDR_AUTO) {
1.77 ratchov 245: if (name != NULL && (namelen = strlen(name)) >= 4 &&
246: strcasecmp(name + namelen - 4, ".wav") == 0) {
1.15 ratchov 247: fa->hdr = HDR_WAV;
248: } else {
249: fa->hdr = HDR_RAW;
1.13 uwe 250: }
1.52 ratchov 251: } else
1.15 ratchov 252: fa->hdr = hdr;
1.83 ratchov 253: if (mmc && xrun == XRUN_IGNORE)
254: xrun = XRUN_SYNC;
1.22 ratchov 255: fa->xrun = xrun;
1.42 ratchov 256: fa->ipar = *ipar;
257: fa->opar = *opar;
1.15 ratchov 258: fa->vol = vol;
1.77 ratchov 259: fa->name = name;
1.74 ratchov 260: fa->mmc = mmc;
1.84 ratchov 261: fa->join = join;
1.83 ratchov 262: fa->mode = mode;
1.15 ratchov 263: SLIST_INSERT_HEAD(list, fa, entry);
264: }
1.13 uwe 265:
1.61 ratchov 266: void
267: setsig(void)
268: {
269: struct sigaction sa;
270:
271: quit_flag = 0;
272: sigfillset(&sa.sa_mask);
273: sa.sa_flags = SA_RESTART;
274: sa.sa_handler = sigint;
275: if (sigaction(SIGINT, &sa, NULL) < 0)
1.68 ratchov 276: err(1, "sigaction(int) failed");
1.61 ratchov 277: if (sigaction(SIGTERM, &sa, NULL) < 0)
1.68 ratchov 278: err(1, "sigaction(term) failed");
1.61 ratchov 279: if (sigaction(SIGHUP, &sa, NULL) < 0)
1.68 ratchov 280: err(1, "sigaction(hup) failed");
1.78 ratchov 281: #ifdef DEBUG
282: sa.sa_handler = sigusr1;
283: if (sigaction(SIGUSR1, &sa, NULL) < 0)
284: err(1, "sigaction(usr1) failed");
285: sa.sa_handler = sigusr2;
286: if (sigaction(SIGUSR2, &sa, NULL) < 0)
287: err(1, "sigaction(usr2) failed1n");
288: #endif
1.61 ratchov 289: }
290:
291: void
292: unsetsig(void)
293: {
294: struct sigaction sa;
295:
296: sigfillset(&sa.sa_mask);
297: sa.sa_flags = SA_RESTART;
298: sa.sa_handler = SIG_DFL;
1.78 ratchov 299: #ifdef DEBUG
300: if (sigaction(SIGUSR2, &sa, NULL) < 0)
301: err(1, "unsetsig(usr2): sigaction failed");
302: if (sigaction(SIGUSR1, &sa, NULL) < 0)
303: err(1, "unsetsig(usr1): sigaction failed");
304: #endif
1.61 ratchov 305: if (sigaction(SIGHUP, &sa, NULL) < 0)
1.68 ratchov 306: err(1, "unsetsig(hup): sigaction failed\n");
1.61 ratchov 307: if (sigaction(SIGTERM, &sa, NULL) < 0)
1.68 ratchov 308: err(1, "unsetsig(term): sigaction failed\n");
1.61 ratchov 309: if (sigaction(SIGINT, &sa, NULL) < 0)
1.68 ratchov 310: err(1, "unsetsig(int): sigaction failed\n");
1.61 ratchov 311: }
312:
313: void
314: getbasepath(char *base, size_t size)
315: {
316: uid_t uid;
317: struct stat sb;
1.86 ratchov 318: mode_t mask;
1.61 ratchov 319:
320: uid = geteuid();
1.86 ratchov 321: if (uid == 0) {
322: mask = 022;
323: snprintf(base, PATH_MAX, "/tmp/aucat");
324: } else {
325: mask = 077;
326: snprintf(base, PATH_MAX, "/tmp/aucat-%u", uid);
327: }
328: if (mkdir(base, 0777 & ~mask) < 0) {
1.61 ratchov 329: if (errno != EEXIST)
330: err(1, "mkdir(\"%s\")", base);
331: }
332: if (stat(base, &sb) < 0)
333: err(1, "stat(\"%s\")", base);
1.86 ratchov 334: if (sb.st_uid != uid || (sb.st_mode & mask) != 0)
1.61 ratchov 335: errx(1, "%s has wrong permissions", base);
336: }
337:
338: void
1.86 ratchov 339: privdrop(void)
340: {
341: struct passwd *pw;
342: struct stat sb;
343:
344: if ((pw = getpwnam(SNDIO_USER)) == NULL)
345: err(1, "getpwnam");
346: if (stat(pw->pw_dir, &sb) < 0)
347: err(1, "stat(\"%s\")", pw->pw_dir);
348: if (sb.st_uid != 0 || (sb.st_mode & 022) != 0)
349: errx(1, "%s has wrong permissions", pw->pw_dir);
350: if (setpriority(PRIO_PROCESS, 0, SNDIO_PRIO) < 0)
351: err(1, "setpriority");
352: if (setgroups(1, &pw->pw_gid) ||
353: setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
354: setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
355: err(1, "cannot drop privileges");
356: }
357:
358: void
1.89 ! ratchov 359: stopall(void)
1.83 ratchov 360: {
361: struct file *f;
362:
363: restart:
364: LIST_FOREACH(f, &file_list, entry) {
365: /*
1.89 ! ratchov 366: * skip connected streams (handled by dev_close())
1.83 ratchov 367: */
1.89 ! ratchov 368: if (APROC_OK(dev_mix)) {
! 369: if (f->rproc && aproc_depend(dev_mix, f->rproc))
! 370: continue;
! 371: if (f->wproc && aproc_depend(f->wproc, dev_mix))
! 372: continue;
! 373: }
! 374: if (APROC_OK(dev_sub)) {
! 375: if (f->rproc && aproc_depend(dev_sub, f->rproc))
! 376: continue;
! 377: if (f->wproc && aproc_depend(f->wproc, dev_sub))
! 378: continue;
! 379: }
! 380: if (APROC_OK(dev_submon)) {
! 381: if (f->rproc && aproc_depend(dev_submon, f->rproc))
! 382: continue;
! 383: if (f->wproc && aproc_depend(f->wproc, dev_submon))
! 384: continue;
! 385: }
1.83 ratchov 386: if (APROC_OK(dev_midi)) {
387: if (f->rproc && aproc_depend(dev_midi, f->rproc))
388: continue;
389: if (f->wproc && aproc_depend(f->wproc, dev_midi))
390: continue;
391: }
392: /*
393: * kill anything else
394: */
395: file_close(f);
396: goto restart;
397: }
398: }
399:
400: void
1.61 ratchov 401: aucat_usage(void)
402: {
1.69 ratchov 403: (void)fputs("usage: " PROG_AUCAT " [-dlnu] [-b nframes] "
1.83 ratchov 404: "[-C min:max] [-c min:max] [-e enc]\n\t"
1.84 ratchov 405: "[-f device] [-h fmt] [-i file] [-j flag] [-m mode]"
1.83 ratchov 406: "[-o file] [-q device]\n\t"
407: "[-r rate] [-s name] [-t mode] [-U unit] "
408: "[-v volume] [-x policy]\n\t"
409: "[-z nframes]\n",
1.61 ratchov 410: stderr);
411: }
412:
1.1 kstailey 413: int
1.61 ratchov 414: aucat_main(int argc, char **argv)
1.1 kstailey 415: {
1.83 ratchov 416: int c, u_flag, d_flag, l_flag, n_flag, hdr, xrun, unit;
1.15 ratchov 417: struct farg *fa;
1.83 ratchov 418: struct farglist ifiles, ofiles, sfiles, qfiles;
1.26 ratchov 419: struct aparams ipar, opar, dipar, dopar;
1.77 ratchov 420: char base[PATH_MAX], path[PATH_MAX], *file;
1.74 ratchov 421: unsigned bufsz, round, mode;
1.61 ratchov 422: char *devpath;
1.76 ratchov 423: const char *str;
1.40 ratchov 424: unsigned volctl;
1.84 ratchov 425: int mmc, autostart, join;
1.19 ratchov 426:
1.15 ratchov 427: aparams_init(&ipar, 0, 1, 44100);
428: aparams_init(&opar, 0, 1, 44100);
1.26 ratchov 429: u_flag = 0;
1.69 ratchov 430: d_flag = 0;
1.28 ratchov 431: l_flag = 0;
1.51 ratchov 432: n_flag = 0;
1.61 ratchov 433: unit = -1;
1.74 ratchov 434: mmc = 0;
1.15 ratchov 435: devpath = NULL;
436: SLIST_INIT(&ifiles);
437: SLIST_INIT(&ofiles);
1.42 ratchov 438: SLIST_INIT(&sfiles);
1.83 ratchov 439: SLIST_INIT(&qfiles);
1.28 ratchov 440: hdr = HDR_AUTO;
441: xrun = XRUN_IGNORE;
1.40 ratchov 442: volctl = MIDI_MAXCTL;
1.83 ratchov 443: mode = MODE_PLAY | MODE_REC;
1.74 ratchov 444: bufsz = 0;
445: round = 0;
1.83 ratchov 446: autostart = 1;
1.84 ratchov 447: join = 1;
1.15 ratchov 448:
1.84 ratchov 449: while ((c = getopt(argc, argv, "dnb:c:C:e:r:h:x:v:i:o:f:m:luq:s:U:t:j:z:")) != -1) {
1.15 ratchov 450: switch (c) {
1.69 ratchov 451: case 'd':
1.78 ratchov 452: #ifdef DEBUG
453: if (d_flag)
454: debug_level++;
455: #endif
1.69 ratchov 456: d_flag = 1;
457: break;
1.51 ratchov 458: case 'n':
459: n_flag = 1;
460: break;
1.43 ratchov 461: case 'm':
462: mode = opt_mode();
463: break;
1.15 ratchov 464: case 'h':
1.28 ratchov 465: hdr = opt_hdr();
1.15 ratchov 466: break;
1.22 ratchov 467: case 'x':
1.28 ratchov 468: xrun = opt_xrun();
1.22 ratchov 469: break;
1.84 ratchov 470: case 'j':
471: join = opt_join();
472: break;
1.74 ratchov 473: case 't':
474: mmc = opt_mmc();
1.83 ratchov 475: if (mmc)
476: autostart = 0;
1.74 ratchov 477: break;
1.15 ratchov 478: case 'c':
479: opt_ch(&ipar);
480: break;
481: case 'C':
482: opt_ch(&opar);
483: break;
484: case 'e':
485: opt_enc(&ipar);
1.28 ratchov 486: aparams_copyenc(&opar, &ipar);
1.15 ratchov 487: break;
488: case 'r':
1.76 ratchov 489: ipar.rate = strtonum(optarg, RATE_MIN, RATE_MAX, &str);
490: if (str)
491: errx(1, "%s: rate is %s", optarg, str);
1.28 ratchov 492: opar.rate = ipar.rate;
1.15 ratchov 493: break;
1.35 ratchov 494: case 'v':
1.76 ratchov 495: volctl = strtonum(optarg, 0, MIDI_MAXCTL, &str);
496: if (str)
497: errx(1, "%s: volume is %s", optarg, str);
1.35 ratchov 498: break;
1.15 ratchov 499: case 'i':
1.77 ratchov 500: file = optarg;
501: if (strcmp(file, "-") == 0)
502: file = NULL;
1.42 ratchov 503: farg_add(&ifiles, &ipar, &opar, volctl,
1.84 ratchov 504: hdr, xrun, mmc, join, mode & MODE_PLAY, file);
1.15 ratchov 505: break;
506: case 'o':
1.77 ratchov 507: file = optarg;
508: if (strcmp(file, "-") == 0)
509: file = NULL;
1.42 ratchov 510: farg_add(&ofiles, &ipar, &opar, volctl,
1.84 ratchov 511: hdr, xrun, mmc, join, mode & MODE_RECMASK, file);
1.42 ratchov 512: break;
513: case 's':
514: farg_add(&sfiles, &ipar, &opar, volctl,
1.84 ratchov 515: hdr, xrun, mmc, join, mode, optarg);
1.83 ratchov 516: break;
517: case 'q':
518: farg_add(&qfiles, &aparams_none, &aparams_none,
1.84 ratchov 519: 0, HDR_RAW, 0, 0, 0, 0, optarg);
1.15 ratchov 520: break;
1.4 millert 521: case 'f':
1.15 ratchov 522: if (devpath)
523: err(1, "only one -f allowed");
524: devpath = optarg;
1.28 ratchov 525: dipar = opar;
526: dopar = ipar;
1.15 ratchov 527: break;
1.28 ratchov 528: case 'l':
529: l_flag = 1;
1.83 ratchov 530: autostart = 0;
1.17 jakemsr 531: break;
1.15 ratchov 532: case 'u':
533: u_flag = 1;
1.4 millert 534: break;
1.28 ratchov 535: case 'b':
1.76 ratchov 536: bufsz = strtonum(optarg, 1, RATE_MAX * 5, &str);
537: if (str)
538: errx(1, "%s: buffer size is %s", optarg, str);
1.28 ratchov 539: break;
1.61 ratchov 540: case 'U':
1.76 ratchov 541: unit = strtonum(optarg, 0, MIDI_MAXCTL, &str);
542: if (str)
543: errx(1, "%s: device number is %s", optarg, str);
1.61 ratchov 544: break;
1.74 ratchov 545: case 'z':
1.76 ratchov 546: round = strtonum(optarg, 1, SHRT_MAX, &str);
547: if (str)
548: errx(1, "%s: block size is %s", optarg, str);
1.74 ratchov 549: break;
1.11 jaredy 550: default:
1.61 ratchov 551: aucat_usage();
1.15 ratchov 552: exit(1);
1.4 millert 553: }
554: }
555: argc -= optind;
556: argv += optind;
557:
1.15 ratchov 558: if (!devpath) {
1.47 ratchov 559: dopar = ipar;
560: dipar = opar;
1.15 ratchov 561: }
1.88 ratchov 562: if (!l_flag && SLIST_EMPTY(&ifiles) && SLIST_EMPTY(&ofiles)) {
563: if (argc > 0) {
564: /*
565: * Legacy mode: if no -i or -o options are provided, and
566: * there are arguments then assume the arguments are files
567: * to play.
568: */
569: for (c = 0; c < argc; c++)
570: if (legacy_play(devpath, argv[c]) != 0) {
571: errx(1, "%s: could not play\n", argv[c]);
572: }
573: exit(0);
574: } else {
575: aucat_usage();
576: exit(1);
577: }
1.15 ratchov 578: }
579:
1.61 ratchov 580: if (!l_flag && (!SLIST_EMPTY(&sfiles) || unit >= 0))
581: errx(1, "can't use -s or -U without -l");
1.83 ratchov 582: if (l_flag && (!SLIST_EMPTY(&ofiles) || !SLIST_EMPTY(&ifiles)))
583: errx(1, "can't use -l, and -s with -o or -i");
1.51 ratchov 584: if (n_flag) {
1.83 ratchov 585: if (devpath != NULL || !SLIST_EMPTY(&qfiles) ||
586: l_flag || !autostart)
587: errx(1, "can't use -n with -f, -q, -t or -l");
1.51 ratchov 588: if (SLIST_EMPTY(&ifiles) || SLIST_EMPTY(&ofiles))
589: errx(1, "both -i and -o are required with -n");
590: }
1.15 ratchov 591:
1.46 ratchov 592: /*
1.62 ratchov 593: * If there are no sockets paths provided use the default.
1.46 ratchov 594: */
595: if (l_flag && SLIST_EMPTY(&sfiles)) {
1.85 ratchov 596: farg_add(&sfiles, &ipar, &opar,
597: volctl, HDR_RAW, XRUN_IGNORE, mmc, join, mode, DEFAULT_OPT);
1.46 ratchov 598: }
599:
1.83 ratchov 600: /*
601: * Check modes and calculate "best" device parameters. Iterate over all
602: * inputs and outputs and find the maximum sample rate and channel
603: * number.
604: */
605: mode = 0;
606: aparams_init(&dipar, dipar.cmin, dipar.cmax, dipar.rate);
607: aparams_init(&dopar, dopar.cmin, dopar.cmax, dopar.rate);
608: SLIST_FOREACH(fa, &ifiles, entry) {
609: if (fa->mode == 0)
610: errx(1, "%s: not in play mode", fa->name);
611: mode |= fa->mode;
612: if (!u_flag)
1.46 ratchov 613: aparams_grow(&dopar, &fa->ipar);
1.83 ratchov 614: }
615: SLIST_FOREACH(fa, &ofiles, entry) {
616: if (fa->mode == 0)
617: errx(1, "%s: not in rec/mon mode", fa->name);
618: if ((fa->mode & MODE_REC) && (fa->mode & MODE_MON))
619: errx(1, "%s: can't record and monitor", fa->name);
620: mode |= fa->mode;
621: if (!u_flag)
1.46 ratchov 622: aparams_grow(&dipar, &fa->opar);
1.83 ratchov 623: }
624: SLIST_FOREACH(fa, &sfiles, entry) {
625: if ((fa->mode & MODE_REC) && (fa->mode & MODE_MON))
626: errx(1, "%s: can't record and monitor", fa->name);
627: mode |= fa->mode;
628: if (!u_flag) {
1.46 ratchov 629: aparams_grow(&dopar, &fa->ipar);
630: aparams_grow(&dipar, &fa->opar);
1.17 jakemsr 631: }
1.42 ratchov 632: }
1.83 ratchov 633:
1.74 ratchov 634: if (!round)
635: round = ((mode & MODE_REC) ? dipar.rate : dopar.rate) / 15;
636: if (!bufsz)
637: bufsz = ((mode & MODE_REC) ? dipar.rate : dopar.rate) * 4 / 15;
1.42 ratchov 638:
1.55 ratchov 639: if (l_flag) {
1.61 ratchov 640: getbasepath(base, sizeof(base));
641: if (unit < 0)
642: unit = 0;
1.55 ratchov 643: }
1.61 ratchov 644: setsig();
1.28 ratchov 645: filelist_init();
1.51 ratchov 646:
1.15 ratchov 647: /*
1.32 ratchov 648: * Open the device. Give half of the buffer to the device,
1.62 ratchov 649: * the other half is for the socket/files.
1.15 ratchov 650: */
1.51 ratchov 651: if (n_flag) {
1.83 ratchov 652: if (mode & MODE_MON)
653: errx(1, "monitoring not allowed in loopback mode");
1.51 ratchov 654: dev_loopinit(&dipar, &dopar, bufsz);
655: } else {
1.83 ratchov 656: if ((mode & MODE_MON) && !(mode & MODE_PLAY))
657: errx(1, "no playback stream to monitor");
658: if (!dev_init(devpath, mode, &dipar, &dopar, bufsz, round)) {
1.58 ratchov 659: errx(1, "%s: can't open device",
660: devpath ? devpath : "<default>");
661: }
1.51 ratchov 662: }
1.52 ratchov 663:
1.15 ratchov 664: /*
665: * Create buffers for all input and output pipes.
666: */
1.83 ratchov 667: while (!SLIST_EMPTY(&qfiles)) {
668: fa = SLIST_FIRST(&qfiles);
669: SLIST_REMOVE_HEAD(&qfiles, entry);
670: if (!dev_thruadd(fa->name, 1, 1))
671: errx(1, "%s: can't open device", fa->name);
672: free(fa);
673: }
1.26 ratchov 674: while (!SLIST_EMPTY(&ifiles)) {
675: fa = SLIST_FIRST(&ifiles);
676: SLIST_REMOVE_HEAD(&ifiles, entry);
1.83 ratchov 677: if (!wav_new_in(&wav_ops, fa->mode, fa->name,
1.84 ratchov 678: fa->hdr, &fa->ipar, fa->xrun, fa->vol, fa->mmc,
679: fa->join))
1.77 ratchov 680: exit(1);
1.26 ratchov 681: free(fa);
682: }
683: while (!SLIST_EMPTY(&ofiles)) {
684: fa = SLIST_FIRST(&ofiles);
685: SLIST_REMOVE_HEAD(&ofiles, entry);
1.83 ratchov 686: if (!wav_new_out(&wav_ops, fa->mode, fa->name,
1.84 ratchov 687: fa->hdr, &fa->opar, fa->xrun, fa->mmc,
688: fa->join))
1.42 ratchov 689: free(fa);
690: }
691: while (!SLIST_EMPTY(&sfiles)) {
692: fa = SLIST_FIRST(&sfiles);
693: SLIST_REMOVE_HEAD(&sfiles, entry);
1.74 ratchov 694: opt_new(fa->name, &fa->opar, &fa->ipar,
1.84 ratchov 695: MIDI_TO_ADATA(fa->vol), fa->mmc, fa->join, fa->mode);
1.26 ratchov 696: free(fa);
1.56 ratchov 697: }
1.61 ratchov 698: if (l_flag) {
699: snprintf(path, sizeof(path), "%s/%s%u", base,
700: DEFAULT_SOFTAUDIO, unit);
701: listen_new(&listen_ops, path);
1.86 ratchov 702: if (geteuid() == 0)
703: privdrop();
1.69 ratchov 704: if (!d_flag && daemon(0, 0) < 0)
1.56 ratchov 705: err(1, "daemon");
1.15 ratchov 706: }
1.83 ratchov 707: if (autostart) {
708: /*
709: * inject artificial mmc start
710: */
711: ctl_start(dev_midi);
712: }
713: if (l_flag)
714: dev_prime();
1.13 uwe 715:
1.15 ratchov 716: /*
1.62 ratchov 717: * Loop, start audio.
1.15 ratchov 718: */
1.28 ratchov 719: for (;;) {
720: if (quit_flag) {
721: break;
722: }
1.87 ratchov 723: if ((APROC_OK(dev_mix) && LIST_EMPTY(&dev_mix->outs)) ||
724: (APROC_OK(dev_sub) && LIST_EMPTY(&dev_sub->ins))) {
1.75 deraadt 725: fprintf(stderr, "device disappeared, terminating\n");
1.50 ratchov 726: break;
1.38 ratchov 727: }
1.83 ratchov 728: if (!l_flag && ctl_idle(dev_midi))
729: break;
1.50 ratchov 730: if (!file_poll())
731: break;
1.83 ratchov 732: if ((!APROC_OK(dev_mix) || dev_mix->u.mix.idle > 2 * dev_bufsz) &&
733: (!APROC_OK(dev_sub) || dev_sub->u.sub.idle > 2 * dev_bufsz) &&
734: (!APROC_OK(dev_submon) || dev_submon->u.sub.idle > 2 * dev_bufsz) &&
735: (!APROC_OK(dev_midi) || dev_midi->u.ctl.tstate != CTL_RUN)) {
736: if (dev_pstate == DEV_RUN) {
737: dev_pstate = DEV_INIT;
1.34 ratchov 738: dev_stop();
739: dev_clear();
1.83 ratchov 740: /*
741: * priming buffer in non-server mode is not
742: * ok, because it will insert silence and
743: * break synchronization
744: */
745: if (l_flag)
746: dev_prime();
1.34 ratchov 747: }
748: }
1.83 ratchov 749: /*
750: * move device state machine
751: * XXX: move this to dev.c
752: */
753: if (dev_pstate == DEV_START) {
754: dev_pstate = DEV_RUN;
755: dev_start();
1.34 ratchov 756: }
757: }
1.89 ! ratchov 758: stopall();
1.83 ratchov 759: dev_done();
760: filelist_done();
1.55 ratchov 761: if (l_flag) {
1.86 ratchov 762: if (rmdir(base) < 0 && errno != ENOTEMPTY && errno != EPERM)
1.55 ratchov 763: warn("rmdir(\"%s\")", base);
764: }
1.61 ratchov 765: unsetsig();
766: return 0;
767: }
768:
769: void
770: midicat_usage(void)
771: {
1.83 ratchov 772: (void)fputs("usage: " PROG_MIDICAT " [-dl] "
773: "[-i file] [-o file] [-q device] [-U unit]\n",
1.61 ratchov 774: stderr);
775: }
776: int
777: midicat_main(int argc, char **argv)
778: {
1.69 ratchov 779: int c, d_flag, l_flag, unit, fd;
1.64 ratchov 780: struct farglist dfiles, ifiles, ofiles;
1.61 ratchov 781: char base[PATH_MAX], path[PATH_MAX];
1.63 ratchov 782: struct farg *fa;
1.65 ratchov 783: struct file *stdx;
1.63 ratchov 784: struct aproc *p;
1.61 ratchov 785: struct abuf *buf;
1.76 ratchov 786: const char *str;
1.61 ratchov 787:
1.69 ratchov 788: d_flag = 0;
1.61 ratchov 789: l_flag = 0;
790: unit = -1;
1.63 ratchov 791: SLIST_INIT(&dfiles);
1.64 ratchov 792: SLIST_INIT(&ifiles);
793: SLIST_INIT(&ofiles);
1.61 ratchov 794:
1.83 ratchov 795: while ((c = getopt(argc, argv, "di:o:lf:q:U:")) != -1) {
1.61 ratchov 796: switch (c) {
1.69 ratchov 797: case 'd':
1.78 ratchov 798: #ifdef DEBUG
799: if (d_flag)
800: debug_level++;
801: #endif
1.69 ratchov 802: d_flag = 1;
803: break;
1.61 ratchov 804: case 'i':
1.64 ratchov 805: farg_add(&ifiles, &aparams_none, &aparams_none,
1.84 ratchov 806: 0, HDR_RAW, 0, 0, 0, 0, optarg);
1.61 ratchov 807: break;
808: case 'o':
1.64 ratchov 809: farg_add(&ofiles, &aparams_none, &aparams_none,
1.84 ratchov 810: 0, HDR_RAW, 0, 0, 0, 0, optarg);
1.61 ratchov 811: break;
1.83 ratchov 812: /* XXX: backward compat, remove this */
813: case 'f':
814: case 'q':
1.63 ratchov 815: farg_add(&dfiles, &aparams_none, &aparams_none,
1.84 ratchov 816: 0, HDR_RAW, 0, 0, 0, 0, optarg);
1.61 ratchov 817: break;
818: case 'l':
819: l_flag = 1;
820: break;
821: case 'U':
1.76 ratchov 822: unit = strtonum(optarg, 0, MIDI_MAXCTL, &str);
823: if (str)
824: errx(1, "%s: device number is %s", optarg, str);
1.61 ratchov 825: break;
826: default:
827: midicat_usage();
828: exit(1);
829: }
830: }
831: argc -= optind;
832: argv += optind;
833:
1.64 ratchov 834: if (argc > 0 || (SLIST_EMPTY(&ifiles) && SLIST_EMPTY(&ofiles) &&
835: !l_flag)) {
1.61 ratchov 836: midicat_usage();
837: exit(1);
838: }
839: if (!l_flag && unit >= 0)
840: errx(1, "can't use -U without -l");
841: if (l_flag) {
1.64 ratchov 842: if (!SLIST_EMPTY(&ifiles) || !SLIST_EMPTY(&ofiles))
1.61 ratchov 843: errx(1, "can't use -i or -o with -l");
844: getbasepath(base, sizeof(path));
845: if (unit < 0)
846: unit = 0;
847: }
848: setsig();
849: filelist_init();
850:
1.65 ratchov 851: dev_thruinit();
1.83 ratchov 852: if (!l_flag && APROC_OK(dev_midi))
1.74 ratchov 853: dev_midi->flags |= APROC_QUIT;
1.64 ratchov 854: if ((!SLIST_EMPTY(&ifiles) || !SLIST_EMPTY(&ofiles)) &&
855: SLIST_EMPTY(&dfiles)) {
1.63 ratchov 856: farg_add(&dfiles, &aparams_none, &aparams_none,
1.84 ratchov 857: 0, HDR_RAW, 0, 0, 0, 0, NULL);
1.63 ratchov 858: }
859: while (!SLIST_EMPTY(&dfiles)) {
860: fa = SLIST_FIRST(&dfiles);
861: SLIST_REMOVE_HEAD(&dfiles, entry);
1.77 ratchov 862: if (!dev_thruadd(fa->name,
863: !SLIST_EMPTY(&ofiles) || l_flag,
864: !SLIST_EMPTY(&ifiles) || l_flag)) {
865: errx(1, "%s: can't open device",
866: fa->name ? fa->name : "<default>");
867: }
1.63 ratchov 868: free(fa);
869: }
1.61 ratchov 870: if (l_flag) {
871: snprintf(path, sizeof(path), "%s/%s%u", base,
872: DEFAULT_MIDITHRU, unit);
873: listen_new(&listen_ops, path);
1.86 ratchov 874: if (geteuid() == 0)
875: privdrop();
1.69 ratchov 876: if (!d_flag && daemon(0, 0) < 0)
1.61 ratchov 877: err(1, "daemon");
878: }
1.64 ratchov 879: while (!SLIST_EMPTY(&ifiles)) {
880: fa = SLIST_FIRST(&ifiles);
881: SLIST_REMOVE_HEAD(&ifiles, entry);
882: if (strcmp(fa->name, "-") == 0) {
1.61 ratchov 883: fd = STDIN_FILENO;
884: if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
885: warn("stdin");
886: } else {
1.64 ratchov 887: fd = open(fa->name, O_RDONLY | O_NONBLOCK, 0666);
1.61 ratchov 888: if (fd < 0)
1.64 ratchov 889: err(1, "%s", fa->name);
1.61 ratchov 890: }
1.64 ratchov 891: stdx = (struct file *)pipe_new(&pipe_ops, fd, fa->name);
1.77 ratchov 892: p = rfile_new(stdx);
1.64 ratchov 893: buf = abuf_new(MIDI_BUFSZ, &aparams_none);
1.61 ratchov 894: aproc_setout(p, buf);
1.65 ratchov 895: dev_midiattach(buf, NULL);
1.64 ratchov 896: free(fa);
1.63 ratchov 897: }
1.64 ratchov 898: while (!SLIST_EMPTY(&ofiles)) {
1.66 ratchov 899: fa = SLIST_FIRST(&ofiles);
900: SLIST_REMOVE_HEAD(&ofiles, entry);
1.64 ratchov 901: if (strcmp(fa->name, "-") == 0) {
1.61 ratchov 902: fd = STDOUT_FILENO;
903: if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
904: warn("stdout");
905: } else {
1.64 ratchov 906: fd = open(fa->name,
1.61 ratchov 907: O_WRONLY | O_TRUNC | O_CREAT | O_NONBLOCK, 0666);
908: if (fd < 0)
1.64 ratchov 909: err(1, "%s", fa->name);
1.61 ratchov 910: }
1.64 ratchov 911: stdx = (struct file *)pipe_new(&pipe_ops, fd, fa->name);
1.77 ratchov 912: p = wfile_new(stdx);
1.64 ratchov 913: buf = abuf_new(MIDI_BUFSZ, &aparams_none);
1.61 ratchov 914: aproc_setin(p, buf);
1.65 ratchov 915: dev_midiattach(NULL, buf);
1.64 ratchov 916: free(fa);
1.63 ratchov 917: }
1.61 ratchov 918: /*
919: * loop, start processing
920: */
921: for (;;) {
922: if (quit_flag) {
923: break;
924: }
925: if (!file_poll())
926: break;
927: }
1.89 ! ratchov 928: stopall();
1.83 ratchov 929: dev_done();
930: filelist_done();
1.61 ratchov 931: if (l_flag) {
1.86 ratchov 932: if (rmdir(base) < 0 && errno != ENOTEMPTY && errno != EPERM)
1.61 ratchov 933: warn("rmdir(\"%s\")", base);
934: }
935: unsetsig();
1.15 ratchov 936: return 0;
1.61 ratchov 937: }
938:
939:
940: int
941: main(int argc, char **argv)
942: {
943: char *prog;
944:
945: prog = strrchr(argv[0], '/');
946: if (prog == NULL)
947: prog = argv[0];
948: else
949: prog++;
950: if (strcmp(prog, PROG_AUCAT) == 0) {
951: return aucat_main(argc, argv);
952: } else if (strcmp(prog, PROG_MIDICAT) == 0) {
953: return midicat_main(argc, argv);
954: } else {
955: fprintf(stderr, "%s: can't determine program to run\n", prog);
956: }
957: return 1;
1.1 kstailey 958: }