Annotation of src/usr.bin/aucat/aucat.c, Revision 1.103
1.103 ! ratchov 1: /* $OpenBSD: aucat.c,v 1.102 2010/07/31 08:48:01 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: cmax = strtol(++next, &end, 10);
112: if (end == next || *end != '\0')
113: goto failed;
114: if (cmin < 0 || cmax < cmin || cmax > NCHAN_MAX)
115: goto failed;
116: par->cmin = cmin;
117: par->cmax = cmax;
118: return;
119: failed:
120: errx(1, "%s: bad channel range", optarg);
1.15 ratchov 121: }
1.13 uwe 122:
1.15 ratchov 123: void
124: opt_enc(struct aparams *par)
125: {
1.28 ratchov 126: int len;
127:
128: len = aparams_strtoenc(par, optarg);
129: if (len == 0 || optarg[len] != '\0')
130: errx(1, "%s: bad encoding", optarg);
1.15 ratchov 131: }
1.4 millert 132:
1.15 ratchov 133: int
134: opt_hdr(void)
135: {
136: if (strcmp("auto", optarg) == 0)
137: return HDR_AUTO;
138: if (strcmp("raw", optarg) == 0)
139: return HDR_RAW;
140: if (strcmp("wav", optarg) == 0)
141: return HDR_WAV;
1.35 ratchov 142: errx(1, "%s: bad header specification", optarg);
1.1 kstailey 143: }
144:
1.22 ratchov 145: int
1.74 ratchov 146: opt_mmc(void)
147: {
148: if (strcmp("off", optarg) == 0)
149: return 0;
150: if (strcmp("slave", optarg) == 0)
151: return 1;
152: errx(1, "%s: bad MMC mode", optarg);
153: }
154:
155: int
1.90 ratchov 156: opt_onoff(void)
1.84 ratchov 157: {
158: if (strcmp("off", optarg) == 0)
159: return 0;
160: if (strcmp("on", optarg) == 0)
161: return 1;
162: errx(1, "%s: bad join/expand setting", optarg);
163: }
164:
165: int
1.22 ratchov 166: opt_xrun(void)
167: {
168: if (strcmp("ignore", optarg) == 0)
169: return XRUN_IGNORE;
170: if (strcmp("sync", optarg) == 0)
171: return XRUN_SYNC;
172: if (strcmp("error", optarg) == 0)
173: return XRUN_ERROR;
1.73 ratchov 174: errx(1, "%s: bad underrun/overrun policy", optarg);
1.22 ratchov 175: }
176:
1.83 ratchov 177: unsigned
1.43 ratchov 178: opt_mode(void)
179: {
1.83 ratchov 180: unsigned mode = 0;
181: char *p = optarg;
182: size_t len;
183:
184: for (p = optarg; *p != NULL; p++) {
185: len = strcspn(p, ",");
186: if (strncmp("play", p, len) == 0) {
187: mode |= MODE_PLAY;
188: } else if (strncmp("rec", p, len) == 0) {
189: mode |= MODE_REC;
190: } else if (strncmp("mon", p, len) == 0) {
191: mode |= MODE_MON;
192: } else if (strncmp("duplex", p, len) == 0) {
193: /* XXX: backward compat, remove this */
194: mode |= MODE_REC | MODE_PLAY;
195: } else
196: errx(1, "%s: bad mode", optarg);
197: p += len;
198: if (*p == '\0')
199: break;
200: }
201: if (mode == 0)
202: errx(1, "empty mode");
203: return mode;
1.43 ratchov 204: }
205:
1.13 uwe 206: /*
1.92 ratchov 207: * stream configuration
208: */
209: struct cfstr {
210: SLIST_ENTRY(cfstr) entry;
211: unsigned mode; /* bitmap of MODE_XXX */
212: struct aparams ipar; /* input (read) parameters */
213: struct aparams opar; /* output (write) parameters */
214: unsigned vol; /* last requested volume */
215: int hdr; /* header format */
216: int xrun; /* overrun/underrun policy */
217: int mmc; /* MMC mode */
218: int join; /* join/expand enabled */
219: char *path; /* static path (no need to copy it) */
220: };
221:
222: SLIST_HEAD(cfstrlist, cfstr);
223:
224: /*
225: * midi device (control stream)
1.13 uwe 226: */
1.92 ratchov 227: struct cfmid {
228: SLIST_ENTRY(cfmid) entry;
229: char *path; /* static path (no need to copy it) */
1.15 ratchov 230: };
1.13 uwe 231:
1.92 ratchov 232: SLIST_HEAD(cfmidlist, cfmid);
1.13 uwe 233:
1.15 ratchov 234: /*
1.92 ratchov 235: * audio device configuration
1.15 ratchov 236: */
1.92 ratchov 237: struct cfdev {
238: SLIST_ENTRY(cfdev) entry;
239: struct cfstrlist ins; /* files to play */
240: struct cfstrlist outs; /* files to record */
241: struct cfstrlist opts; /* subdevices to expose */
242: struct cfmidlist mids; /* midi ports to subscribe */
243: struct aparams ipar; /* input (read) parameters */
244: struct aparams opar; /* output (write) parameters */
1.93 ratchov 245: unsigned hold; /* open immediately */
1.92 ratchov 246: unsigned bufsz; /* par.bufsz for sio device */
247: unsigned round; /* par.round for sio device */
248: unsigned mode; /* bitmap of MODE_XXX */
249: char *path; /* static path (no need to copy it) */
250: };
251:
252: SLIST_HEAD(cfdevlist, cfdev);
253:
1.15 ratchov 254: void
1.92 ratchov 255: cfdev_add(struct cfdevlist *list, struct cfdev *templ, char *path)
256: {
257: struct cfdev *cd;
258:
259: cd = malloc(sizeof(struct cfdev));
260: if (cd == NULL) {
261: perror("malloc");
262: abort();
263: }
264: *cd = *templ;
265: cd->path = path;
266: SLIST_INSERT_HEAD(list, cd, entry);
267: SLIST_INIT(&templ->ins);
268: SLIST_INIT(&templ->outs);
269: SLIST_INIT(&templ->opts);
270: SLIST_INIT(&templ->mids);
271: }
272:
273: void
274: cfstr_add(struct cfstrlist *list, struct cfstr *templ, char *path)
275: {
276: size_t len;
277: struct cfstr *cs;
278: unsigned hdr;
279:
1.103 ! ratchov 280: if (templ->hdr == HDR_AUTO) {
1.92 ratchov 281: len = strlen(path);
282: if (len >= 4 && strcasecmp(path + len - 4, ".wav") == 0)
283: hdr = HDR_WAV;
284: else
285: hdr = HDR_RAW;
1.52 ratchov 286: } else
1.92 ratchov 287: hdr = templ->hdr;
288: cs = malloc(sizeof(struct cfstr));
289: if (cs == NULL) {
290: perror("malloc");
291: abort();
292: }
293: *cs = *templ;
294: cs->path = path;
295: cs->hdr = hdr;
296: SLIST_INSERT_HEAD(list, cs, entry);
297: }
298:
299: void
300: cfmid_add(struct cfmidlist *list, char *path)
301: {
302: struct cfmid *cm;
303:
304: cm = malloc(sizeof(struct cfmid));
305: if (cm == NULL) {
306: perror("malloc");
307: abort();
308: }
309: cm->path = path;
310: SLIST_INSERT_HEAD(list, cm, entry);
1.15 ratchov 311: }
1.13 uwe 312:
1.61 ratchov 313: void
314: setsig(void)
315: {
316: struct sigaction sa;
317:
318: quit_flag = 0;
319: sigfillset(&sa.sa_mask);
320: sa.sa_flags = SA_RESTART;
321: sa.sa_handler = sigint;
322: if (sigaction(SIGINT, &sa, NULL) < 0)
1.68 ratchov 323: err(1, "sigaction(int) failed");
1.61 ratchov 324: if (sigaction(SIGTERM, &sa, NULL) < 0)
1.68 ratchov 325: err(1, "sigaction(term) failed");
1.61 ratchov 326: if (sigaction(SIGHUP, &sa, NULL) < 0)
1.68 ratchov 327: err(1, "sigaction(hup) failed");
1.78 ratchov 328: #ifdef DEBUG
329: sa.sa_handler = sigusr1;
330: if (sigaction(SIGUSR1, &sa, NULL) < 0)
331: err(1, "sigaction(usr1) failed");
332: sa.sa_handler = sigusr2;
333: if (sigaction(SIGUSR2, &sa, NULL) < 0)
334: err(1, "sigaction(usr2) failed1n");
335: #endif
1.61 ratchov 336: }
337:
338: void
339: unsetsig(void)
340: {
341: struct sigaction sa;
342:
343: sigfillset(&sa.sa_mask);
344: sa.sa_flags = SA_RESTART;
345: sa.sa_handler = SIG_DFL;
1.78 ratchov 346: #ifdef DEBUG
347: if (sigaction(SIGUSR2, &sa, NULL) < 0)
348: err(1, "unsetsig(usr2): sigaction failed");
349: if (sigaction(SIGUSR1, &sa, NULL) < 0)
350: err(1, "unsetsig(usr1): sigaction failed");
351: #endif
1.61 ratchov 352: if (sigaction(SIGHUP, &sa, NULL) < 0)
1.68 ratchov 353: err(1, "unsetsig(hup): sigaction failed\n");
1.61 ratchov 354: if (sigaction(SIGTERM, &sa, NULL) < 0)
1.68 ratchov 355: err(1, "unsetsig(term): sigaction failed\n");
1.61 ratchov 356: if (sigaction(SIGINT, &sa, NULL) < 0)
1.68 ratchov 357: err(1, "unsetsig(int): sigaction failed\n");
1.61 ratchov 358: }
359:
360: void
361: getbasepath(char *base, size_t size)
362: {
363: uid_t uid;
364: struct stat sb;
1.86 ratchov 365: mode_t mask;
1.61 ratchov 366:
367: uid = geteuid();
1.86 ratchov 368: if (uid == 0) {
369: mask = 022;
370: snprintf(base, PATH_MAX, "/tmp/aucat");
371: } else {
372: mask = 077;
373: snprintf(base, PATH_MAX, "/tmp/aucat-%u", uid);
374: }
375: if (mkdir(base, 0777 & ~mask) < 0) {
1.61 ratchov 376: if (errno != EEXIST)
377: err(1, "mkdir(\"%s\")", base);
378: }
379: if (stat(base, &sb) < 0)
380: err(1, "stat(\"%s\")", base);
1.86 ratchov 381: if (sb.st_uid != uid || (sb.st_mode & mask) != 0)
1.61 ratchov 382: errx(1, "%s has wrong permissions", base);
383: }
384:
385: void
1.86 ratchov 386: privdrop(void)
387: {
388: struct passwd *pw;
389: struct stat sb;
390:
391: if ((pw = getpwnam(SNDIO_USER)) == NULL)
392: err(1, "getpwnam");
393: if (stat(pw->pw_dir, &sb) < 0)
394: err(1, "stat(\"%s\")", pw->pw_dir);
395: if (sb.st_uid != 0 || (sb.st_mode & 022) != 0)
396: errx(1, "%s has wrong permissions", pw->pw_dir);
397: if (setpriority(PRIO_PROCESS, 0, SNDIO_PRIO) < 0)
398: err(1, "setpriority");
399: if (setgroups(1, &pw->pw_gid) ||
400: setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
401: setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
402: err(1, "cannot drop privileges");
403: }
404:
405: void
1.61 ratchov 406: aucat_usage(void)
407: {
1.92 ratchov 408: (void)fputs("usage: " PROG_AUCAT " [-dlnu] [-a flag] [-b nframes] "
1.83 ratchov 409: "[-C min:max] [-c min:max] [-e enc]\n\t"
1.84 ratchov 410: "[-f device] [-h fmt] [-i file] [-j flag] [-m mode]"
1.83 ratchov 411: "[-o file] [-q device]\n\t"
412: "[-r rate] [-s name] [-t mode] [-U unit] "
413: "[-v volume] [-x policy]\n\t"
414: "[-z nframes]\n",
1.61 ratchov 415: stderr);
416: }
417:
1.1 kstailey 418: int
1.61 ratchov 419: aucat_main(int argc, char **argv)
1.1 kstailey 420: {
1.92 ratchov 421: struct cfdevlist cfdevs;
422: struct cfmid *cm;
423: struct cfstr *cs;
424: struct cfdev *cd;
425: struct listen *listen = NULL;
426: int c, u_flag, d_flag, l_flag, n_flag, unit;
427: char base[PATH_MAX], path[PATH_MAX];
428: unsigned mode, rate;
1.76 ratchov 429: const char *str;
1.102 ratchov 430: int autostart;
1.92 ratchov 431: struct dev *d, *dnext;
432: unsigned active;
1.98 ratchov 433: unsigned nsock, nfile;
1.19 ratchov 434:
1.92 ratchov 435: /*
436: * global options defaults
437: */
438: unit = -1;
1.26 ratchov 439: u_flag = 0;
1.69 ratchov 440: d_flag = 0;
1.28 ratchov 441: l_flag = 0;
1.51 ratchov 442: n_flag = 0;
1.92 ratchov 443: SLIST_INIT(&cfdevs);
1.98 ratchov 444: nfile = nsock = 0;
1.92 ratchov 445:
446: /*
447: * default stream params
448: */
449: cs = malloc(sizeof(struct cfstr));
450: if (cs == NULL) {
451: perror("malloc");
452: exit(1);
453: }
454: aparams_init(&cs->ipar, 0, 1, 44100);
455: aparams_init(&cs->opar, 0, 1, 44100);
456: cs->mmc = 0;
457: cs->hdr = HDR_AUTO;
458: cs->xrun = XRUN_IGNORE;
459: cs->vol = MIDI_MAXCTL;
460: cs->mode = MODE_PLAY | MODE_REC;
461: cs->join = 1;
462:
463: /*
464: * default device
465: */
466: cd = malloc(sizeof(struct cfdev));
467: if (cd == NULL) {
468: perror("malloc");
469: exit(1);
470: }
471: aparams_init(&cd->ipar, 0, 1, 44100);
472: aparams_init(&cd->opar, 0, 1, 44100);
473: SLIST_INIT(&cd->ins);
474: SLIST_INIT(&cd->outs);
475: SLIST_INIT(&cd->opts);
476: SLIST_INIT(&cd->mids);
477: cd->path = NULL;
478: cd->bufsz = 0;
479: cd->round = 0;
480: cd->hold = 1;
1.15 ratchov 481:
1.92 ratchov 482: while ((c = getopt(argc, argv, "a:dnb:c:C:e:r:h:x:v:i:o:f:m:luq:s:U:t:j:z:")) != -1) {
1.15 ratchov 483: switch (c) {
1.69 ratchov 484: case 'd':
1.78 ratchov 485: #ifdef DEBUG
486: if (d_flag)
487: debug_level++;
488: #endif
1.69 ratchov 489: d_flag = 1;
490: break;
1.51 ratchov 491: case 'n':
492: n_flag = 1;
493: break;
1.92 ratchov 494: case 'u':
495: u_flag = 1;
496: break;
497: case 'U':
498: unit = strtonum(optarg, 0, MIDI_MAXCTL, &str);
499: if (str)
500: errx(1, "%s: unit number is %s", optarg, str);
501: break;
1.43 ratchov 502: case 'm':
1.92 ratchov 503: cs->mode = opt_mode();
504: cd->mode = cs->mode;
1.43 ratchov 505: break;
1.15 ratchov 506: case 'h':
1.92 ratchov 507: cs->hdr = opt_hdr();
1.15 ratchov 508: break;
1.22 ratchov 509: case 'x':
1.92 ratchov 510: cs->xrun = opt_xrun();
1.22 ratchov 511: break;
1.84 ratchov 512: case 'j':
1.92 ratchov 513: cs->join = opt_onoff();
1.84 ratchov 514: break;
1.74 ratchov 515: case 't':
1.92 ratchov 516: cs->mmc = opt_mmc();
1.74 ratchov 517: break;
1.15 ratchov 518: case 'c':
1.92 ratchov 519: opt_ch(&cs->ipar);
520: cd->opar.cmin = cs->ipar.cmin;
521: cd->opar.cmax = cs->ipar.cmax;
1.15 ratchov 522: break;
523: case 'C':
1.92 ratchov 524: opt_ch(&cs->opar);
525: cd->ipar.cmin = cs->opar.cmin;
526: cd->ipar.cmax = cs->opar.cmax;
1.15 ratchov 527: break;
528: case 'e':
1.92 ratchov 529: opt_enc(&cs->ipar);
530: aparams_copyenc(&cs->opar, &cs->ipar);
1.15 ratchov 531: break;
532: case 'r':
1.92 ratchov 533: rate = strtonum(optarg, RATE_MIN, RATE_MAX, &str);
1.76 ratchov 534: if (str)
535: errx(1, "%s: rate is %s", optarg, str);
1.92 ratchov 536: cs->opar.rate = cs->ipar.rate = rate;
537: cd->ipar.rate = cd->opar.rate = rate;
1.15 ratchov 538: break;
1.35 ratchov 539: case 'v':
1.92 ratchov 540: cs->vol = strtonum(optarg, 0, MIDI_MAXCTL, &str);
1.76 ratchov 541: if (str)
542: errx(1, "%s: volume is %s", optarg, str);
1.35 ratchov 543: break;
1.15 ratchov 544: case 'i':
1.92 ratchov 545: cfstr_add(&cd->ins, cs, optarg);
1.98 ratchov 546: nfile++;
1.15 ratchov 547: break;
548: case 'o':
1.92 ratchov 549: cfstr_add(&cd->outs, cs, optarg);
1.98 ratchov 550: nfile++;
1.42 ratchov 551: break;
552: case 's':
1.92 ratchov 553: cfstr_add(&cd->opts, cs, optarg);
1.98 ratchov 554: nsock++;
1.92 ratchov 555: break;
556: case 'a':
557: cd->hold = opt_onoff();
1.83 ratchov 558: break;
559: case 'q':
1.92 ratchov 560: cfmid_add(&cd->mids, optarg);
1.4 millert 561: break;
1.28 ratchov 562: case 'b':
1.92 ratchov 563: cd->bufsz = strtonum(optarg, 1, RATE_MAX * 5, &str);
1.76 ratchov 564: if (str)
565: errx(1, "%s: buffer size is %s", optarg, str);
1.28 ratchov 566: break;
1.74 ratchov 567: case 'z':
1.92 ratchov 568: cd->round = strtonum(optarg, 1, SHRT_MAX, &str);
1.76 ratchov 569: if (str)
570: errx(1, "%s: block size is %s", optarg, str);
1.74 ratchov 571: break;
1.92 ratchov 572: case 'f':
1.98 ratchov 573: if (SLIST_EMPTY(&cd->opts) &&
574: SLIST_EMPTY(&cd->ins) &&
575: SLIST_EMPTY(&cd->outs)) {
576: cfstr_add(&cd->opts, cs, DEFAULT_OPT);
577: nsock++;
578: }
1.92 ratchov 579: cfdev_add(&cfdevs, cd, optarg);
580: break;
581: case 'l':
582: l_flag = 1;
583: autostart = 0;
584: break;
1.11 jaredy 585: default:
1.61 ratchov 586: aucat_usage();
1.15 ratchov 587: exit(1);
1.4 millert 588: }
589: }
590: argc -= optind;
591: argv += optind;
592:
1.92 ratchov 593: #ifdef DEBUG
594: if (debug_level == 0)
595: debug_level = 1;
596: #endif
597: if (argc > 0) {
1.102 ratchov 598: aucat_usage();
599: exit(1);
1.15 ratchov 600: }
601:
1.98 ratchov 602: /*
603: * Check constraints specific to -n option
604: */
1.51 ratchov 605: if (n_flag) {
1.98 ratchov 606: if (!SLIST_EMPTY(&cfdevs) ||
607: !SLIST_EMPTY(&cd->mids) ||
608: !SLIST_EMPTY(&cd->opts))
609: errx(1, "-f, -s, and -q not allowed in loopback mode");
1.92 ratchov 610: if (SLIST_EMPTY(&cd->ins) || SLIST_EMPTY(&cd->outs))
1.98 ratchov 611: errx(1, "-i and -o are required in loopback mode");
1.51 ratchov 612: }
1.15 ratchov 613:
1.46 ratchov 614: /*
1.98 ratchov 615: * If there's no device specified, do as if the default
616: * device is specified as last argument.
1.92 ratchov 617: */
618: if (SLIST_EMPTY(&cfdevs)) {
1.98 ratchov 619: if (SLIST_EMPTY(&cd->opts) &&
620: SLIST_EMPTY(&cd->ins) &&
621: SLIST_EMPTY(&cd->outs)) {
622: cfstr_add(&cd->opts, cs, DEFAULT_OPT);
623: nsock++;
624: }
1.92 ratchov 625: if (!cd->hold)
1.98 ratchov 626: errx(1, "-a off not compatible with default device");
1.92 ratchov 627: cfdev_add(&cfdevs, cd, "default");
628: }
1.98 ratchov 629: if ((cs = SLIST_FIRST(&cd->opts)) ||
630: (cs = SLIST_FIRST(&cd->ins)) ||
631: (cs = SLIST_FIRST(&cd->outs)))
632: errx(1, "%s: no device to attach the stream to", cs->path);
1.46 ratchov 633:
1.83 ratchov 634: /*
635: * Check modes and calculate "best" device parameters. Iterate over all
636: * inputs and outputs and find the maximum sample rate and channel
637: * number.
638: */
1.92 ratchov 639: SLIST_FOREACH(cd, &cfdevs, entry) {
640: mode = 0;
641: SLIST_FOREACH(cs, &cd->ins, entry) {
642: if (cs->mode == 0)
643: errx(1, "%s: not in play mode", cs->path);
644: mode |= (cs->mode & MODE_PLAY);
645: if (!u_flag)
646: aparams_grow(&cd->opar, &cs->ipar);
647: }
648: SLIST_FOREACH(cs, &cd->outs, entry) {
649: if (cs->mode == 0)
650: errx(1, "%s: not in rec/mon mode", cs->path);
651: if ((cs->mode & MODE_REC) && (cs->mode & MODE_MON))
652: errx(1, "%s: can't rec and mon", cs->path);
653: mode |= (cs->mode & MODE_RECMASK);
654: if (!u_flag)
655: aparams_grow(&cd->ipar, &cs->opar);
656: }
657: SLIST_FOREACH(cs, &cd->opts, entry) {
658: if ((cs->mode & MODE_REC) && (cs->mode & MODE_MON))
659: errx(1, "%s: can't rec and mon", cs->path);
660: mode |= (cs->mode & (MODE_RECMASK | MODE_PLAY));
661: if (!u_flag) {
662: aparams_grow(&cd->opar, &cs->ipar);
663: aparams_grow(&cd->ipar, &cs->opar);
664: }
665: }
666: if ((mode & MODE_MON) && !(mode & MODE_PLAY))
667: errx(1, "no playback stream to monitor");
1.98 ratchov 668: if (n_flag && (mode & MODE_MON))
669: errx(1, "-m mon not allowed in loopback mode");
1.92 ratchov 670: rate = (mode & MODE_REC) ? cd->ipar.rate : cd->opar.rate;
671: if (!cd->round)
672: cd->round = rate / 15;
673: if (!cd->bufsz)
674: cd->bufsz = rate / 15 * 4;
675: cd->mode = mode;
1.42 ratchov 676: }
1.98 ratchov 677: if (nsock > 0) {
1.61 ratchov 678: getbasepath(base, sizeof(base));
679: if (unit < 0)
680: unit = 0;
1.55 ratchov 681: }
1.61 ratchov 682: setsig();
1.28 ratchov 683: filelist_init();
1.51 ratchov 684:
1.15 ratchov 685: /*
1.92 ratchov 686: * Open devices
1.15 ratchov 687: */
1.92 ratchov 688: while (!SLIST_EMPTY(&cfdevs)) {
689: cd = SLIST_FIRST(&cfdevs);
690: SLIST_REMOVE_HEAD(&cfdevs, entry);
691:
692: if (n_flag) {
693: d = dev_new_loop(&cd->ipar, &cd->opar, cd->bufsz);
694: } else {
695: d = dev_new_sio(cd->path, cd->mode,
696: &cd->ipar, &cd->opar, cd->bufsz, cd->round,
1.94 ratchov 697: cd->hold);
1.92 ratchov 698: }
699: if (d == NULL)
700: errx(1, "%s: can't open device", cd->path);
701:
702: /*
703: * register midi devices
704: */
705: while (!SLIST_EMPTY(&cd->mids)) {
706: cm = SLIST_FIRST(&cd->mids);
707: SLIST_REMOVE_HEAD(&cd->mids, entry);
708: if (!dev_thruadd(d, cm->path, 1, 1))
709: errx(1, "%s: can't open device", cm->path);
710: free(cm);
711: }
1.52 ratchov 712:
1.92 ratchov 713: /*
714: * register files
715: */
716: autostart = 0;
717: while (!SLIST_EMPTY(&cd->ins)) {
718: cs = SLIST_FIRST(&cd->ins);
719: SLIST_REMOVE_HEAD(&cd->ins, entry);
720: if (!cs->mmc)
721: autostart = 1;
1.103 ! ratchov 722: if (strcmp(cs->path, "-") == 0)
! 723: cs->path = NULL;
1.92 ratchov 724: if (!wav_new_in(&wav_ops, d, cs->mode & MODE_PLAY,
725: cs->path, cs->hdr, &cs->ipar, cs->xrun,
726: cs->vol, cs->mmc, cs->join))
727: exit(1);
728: free(cs);
729: }
730: while (!SLIST_EMPTY(&cd->outs)) {
731: cs = SLIST_FIRST(&cd->outs);
732: SLIST_REMOVE_HEAD(&cd->outs, entry);
733: if (!cs->mmc)
734: autostart = 1;
1.103 ! ratchov 735: if (strcmp(cs->path, "-") == 0)
! 736: cs->path = NULL;
1.92 ratchov 737: if (!wav_new_out(&wav_ops, d, cs->mode & MODE_RECMASK,
738: cs->path, cs->hdr, &cs->opar, cs->xrun,
739: cs->mmc, cs->join))
740: exit(1);
741: free(cs);
742: }
743: while (!SLIST_EMPTY(&cd->opts)) {
744: cs = SLIST_FIRST(&cd->opts);
745: SLIST_REMOVE_HEAD(&cd->opts, entry);
746: opt_new(cs->path, d, &cs->opar, &cs->ipar,
747: MIDI_TO_ADATA(cs->vol), cs->mmc,
748: cs->join, cs->mode);
749: free(cs);
750: }
751: free(cd);
752: if (autostart) {
753: /*
754: * inject artificial mmc start
755: */
756: ctl_start(d->midi);
757: }
1.56 ratchov 758: }
1.98 ratchov 759: if (nsock > 0) {
1.61 ratchov 760: snprintf(path, sizeof(path), "%s/%s%u", base,
761: DEFAULT_SOFTAUDIO, unit);
1.92 ratchov 762: listen = listen_new(&listen_ops, path);
1.98 ratchov 763: if (listen == NULL)
1.92 ratchov 764: exit(1);
1.98 ratchov 765: }
766: if (geteuid() == 0)
767: privdrop();
768: if (l_flag) {
769: debug_level = 0;
770: dbg_flush();
771: if (daemon(0, 0) < 0)
1.56 ratchov 772: err(1, "daemon");
1.15 ratchov 773: }
1.13 uwe 774:
1.15 ratchov 775: /*
1.62 ratchov 776: * Loop, start audio.
1.15 ratchov 777: */
1.28 ratchov 778: for (;;) {
1.90 ratchov 779: if (quit_flag)
1.28 ratchov 780: break;
1.92 ratchov 781: active = 0;
782: for (d = dev_list; d != NULL; d = dnext) {
783: dnext = d->next;
784: if (!dev_run(d))
785: goto fatal;
1.97 jakemsr 786: if (d->pstate != DEV_CLOSED && !ctl_idle(d->midi))
1.92 ratchov 787: active = 1;
788: }
789: if (dev_list == NULL)
1.50 ratchov 790: break;
1.98 ratchov 791: if (nsock == 0 && !active)
1.83 ratchov 792: break;
1.50 ratchov 793: if (!file_poll())
794: break;
1.34 ratchov 795: }
1.92 ratchov 796: fatal:
1.98 ratchov 797: if (nsock > 0)
1.92 ratchov 798: file_close(&listen->file);
1.90 ratchov 799: /*
800: * give a chance to drain
801: */
1.96 ratchov 802: for (d = dev_list; d != NULL; d = d->next)
803: dev_drain(d);
1.90 ratchov 804: while (file_poll())
805: ; /* nothing */
1.96 ratchov 806:
807: while (dev_list)
808: dev_del(dev_list);
1.83 ratchov 809: filelist_done();
1.98 ratchov 810: if (nsock > 0) {
1.86 ratchov 811: if (rmdir(base) < 0 && errno != ENOTEMPTY && errno != EPERM)
1.55 ratchov 812: warn("rmdir(\"%s\")", base);
813: }
1.61 ratchov 814: unsetsig();
815: return 0;
816: }
817:
818: void
819: midicat_usage(void)
820: {
1.83 ratchov 821: (void)fputs("usage: " PROG_MIDICAT " [-dl] "
1.99 jmc 822: "[-i file] [-o file] [-q port] [-s name] [-U unit]\n",
1.61 ratchov 823: stderr);
824: }
1.92 ratchov 825:
1.61 ratchov 826: int
827: midicat_main(int argc, char **argv)
828: {
1.98 ratchov 829: struct cfdevlist cfdevs;
1.92 ratchov 830: struct cfmid *cm;
1.98 ratchov 831: struct cfstr *cs;
832: struct cfdev *cd;
1.92 ratchov 833: struct listen *listen = NULL;
1.69 ratchov 834: int c, d_flag, l_flag, unit, fd;
1.61 ratchov 835: char base[PATH_MAX], path[PATH_MAX];
1.65 ratchov 836: struct file *stdx;
1.63 ratchov 837: struct aproc *p;
1.61 ratchov 838: struct abuf *buf;
1.76 ratchov 839: const char *str;
1.98 ratchov 840: struct dev *d, *dnext;
841: unsigned nsock;
1.61 ratchov 842:
1.92 ratchov 843: /*
844: * global options defaults
845: */
846: unit = -1;
1.69 ratchov 847: d_flag = 0;
1.61 ratchov 848: l_flag = 0;
1.98 ratchov 849: SLIST_INIT(&cfdevs);
850: nsock = 0;
851:
852: /*
853: * default stream params
854: */
855: cs = malloc(sizeof(struct cfstr));
856: if (cs == NULL) {
857: perror("malloc");
858: exit(1);
859: }
860: cs->hdr = HDR_RAW;
861:
862: /*
863: * default device
864: */
865: cd = malloc(sizeof(struct cfdev));
866: if (cd == NULL) {
867: perror("malloc");
868: exit(1);
869: }
870: SLIST_INIT(&cd->ins);
871: SLIST_INIT(&cd->outs);
872: SLIST_INIT(&cd->opts);
873: SLIST_INIT(&cd->mids);
874: cd->path = NULL;
875:
1.61 ratchov 876:
1.98 ratchov 877: while ((c = getopt(argc, argv, "di:o:ls:q:U:")) != -1) {
1.61 ratchov 878: switch (c) {
1.69 ratchov 879: case 'd':
1.78 ratchov 880: #ifdef DEBUG
881: if (d_flag)
882: debug_level++;
883: #endif
1.69 ratchov 884: d_flag = 1;
885: break;
1.61 ratchov 886: case 'i':
1.98 ratchov 887: cfstr_add(&cd->ins, cs, optarg);
1.61 ratchov 888: break;
889: case 'o':
1.98 ratchov 890: cfstr_add(&cd->outs, cs, optarg);
1.61 ratchov 891: break;
1.83 ratchov 892: case 'q':
1.98 ratchov 893: cfmid_add(&cd->mids, optarg);
894: break;
895: case 's':
896: cfstr_add(&cd->opts, cs, optarg);
897: cfdev_add(&cfdevs, cd, optarg);
898: nsock++;
1.61 ratchov 899: break;
900: case 'l':
901: l_flag = 1;
902: break;
903: case 'U':
1.76 ratchov 904: unit = strtonum(optarg, 0, MIDI_MAXCTL, &str);
905: if (str)
1.92 ratchov 906: errx(1, "%s: unit number is %s", optarg, str);
1.61 ratchov 907: break;
908: default:
909: midicat_usage();
910: exit(1);
911: }
912: }
913: argc -= optind;
914: argv += optind;
915:
1.98 ratchov 916: #ifdef DEBUG
917: if (debug_level == 0)
918: debug_level = 1;
919: #endif
920: if (argc > 0) {
1.61 ratchov 921: midicat_usage();
922: exit(1);
923: }
1.98 ratchov 924:
925: /*
926: * If there's no device specified (-s), then create one with
927: * reasonable defaults:
928: *
929: * - if there are no streams (-ioq) defined, assume server mode
930: * and expose the "defaut" option
931: *
932: * - if there are files (-io) but no ports (-q) to send/receive
933: * from, add the default sndio(7) MIDI port
934: */
935: if (SLIST_EMPTY(&cfdevs)) {
936: if (SLIST_EMPTY(&cd->mids)) {
1.100 ratchov 937: if (!SLIST_EMPTY(&cd->ins) || !SLIST_EMPTY(&cd->outs))
1.98 ratchov 938: cfmid_add(&cd->mids, "default");
939: else {
940: cfstr_add(&cd->opts, cs, DEFAULT_OPT);
941: nsock++;
942: }
943: }
944: cfdev_add(&cfdevs, cd, "default");
945: }
946: if (nsock > 0) {
1.61 ratchov 947: getbasepath(base, sizeof(path));
948: if (unit < 0)
949: unit = 0;
950: }
951: setsig();
952: filelist_init();
953:
1.98 ratchov 954: while (!SLIST_EMPTY(&cfdevs)) {
955: cd = SLIST_FIRST(&cfdevs);
956: SLIST_REMOVE_HEAD(&cfdevs, entry);
957:
958: d = dev_new_thru();
959: if (d == NULL)
960: errx(1, "%s: can't open device", cd->path);
961: if (!dev_ref(d))
962: errx(1, "couldn't open midi thru box");
1.101 ratchov 963: if (SLIST_EMPTY(&cd->opts) && APROC_OK(d->midi))
1.98 ratchov 964: d->midi->flags |= APROC_QUIT;
965:
966: /*
967: * register midi ports
968: */
969: while (!SLIST_EMPTY(&cd->mids)) {
970: cm = SLIST_FIRST(&cd->mids);
971: SLIST_REMOVE_HEAD(&cd->mids, entry);
972: if (!dev_thruadd(d, cm->path, 1, 1))
973: errx(1, "%s: can't open device", cm->path);
974: free(cm);
975: }
976:
977: /*
978: * register files
979: */
980: while (!SLIST_EMPTY(&cd->ins)) {
981: cs = SLIST_FIRST(&cd->ins);
982: SLIST_REMOVE_HEAD(&cd->ins, entry);
983: if (strcmp(cs->path, "-") == 0) {
984: fd = STDIN_FILENO;
985: if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
986: warn("stdin");
987: } else {
988: fd = open(cs->path, O_RDONLY | O_NONBLOCK, 0666);
989: if (fd < 0)
990: err(1, "%s", cs->path);
991: }
992: stdx = (struct file *)pipe_new(&pipe_ops, fd, cs->path);
993: p = rfile_new(stdx);
994: buf = abuf_new(MIDI_BUFSZ, &aparams_none);
995: aproc_setout(p, buf);
996: dev_midiattach(d, buf, NULL);
997: free(cs);
1.77 ratchov 998: }
1.98 ratchov 999: while (!SLIST_EMPTY(&cd->outs)) {
1000: cs = SLIST_FIRST(&cd->outs);
1001: SLIST_REMOVE_HEAD(&cd->outs, entry);
1002: if (strcmp(cs->path, "-") == 0) {
1003: fd = STDOUT_FILENO;
1004: if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
1005: warn("stdout");
1006: } else {
1007: fd = open(cs->path,
1008: O_WRONLY | O_TRUNC | O_CREAT | O_NONBLOCK, 0666);
1009: if (fd < 0)
1010: err(1, "%s", cs->path);
1011: }
1012: stdx = (struct file *)pipe_new(&pipe_ops, fd, cs->path);
1013: p = wfile_new(stdx);
1014: buf = abuf_new(MIDI_BUFSZ, &aparams_none);
1015: aproc_setin(p, buf);
1016: dev_midiattach(d, NULL, buf);
1017: free(cs);
1018: }
1019: while (!SLIST_EMPTY(&cd->opts)) {
1020: cs = SLIST_FIRST(&cd->opts);
1021: SLIST_REMOVE_HEAD(&cd->opts, entry);
1022: opt_new(cs->path, d, NULL, NULL, 0, 0, 0, 0);
1023: free(cs);
1024: }
1025: free(cd);
1.63 ratchov 1026: }
1.98 ratchov 1027: if (nsock > 0) {
1.61 ratchov 1028: snprintf(path, sizeof(path), "%s/%s%u", base,
1029: DEFAULT_MIDITHRU, unit);
1.92 ratchov 1030: listen = listen_new(&listen_ops, path);
1.98 ratchov 1031: if (listen == NULL)
1032: exit(1);
1033: }
1034: if (geteuid() == 0)
1035: privdrop();
1036: if (l_flag) {
1037: debug_level = 0;
1038: dbg_flush();
1039: if (daemon(0, 0) < 0)
1.61 ratchov 1040: err(1, "daemon");
1041: }
1.98 ratchov 1042:
1.61 ratchov 1043: /*
1044: * loop, start processing
1045: */
1046: for (;;) {
1.90 ratchov 1047: if (quit_flag)
1048: break;
1.98 ratchov 1049: for (d = dev_list; d != NULL; d = dnext) {
1050: dnext = d->next;
1051: if (!dev_run(d))
1052: goto fatal;
1053: }
1.61 ratchov 1054: if (!file_poll())
1055: break;
1056: }
1.98 ratchov 1057: fatal:
1058: if (nsock > 0)
1.92 ratchov 1059: file_close(&listen->file);
1.90 ratchov 1060: /*
1.98 ratchov 1061: * give a chance to drain
1.90 ratchov 1062: */
1.98 ratchov 1063: for (d = dev_list; d != NULL; d = d->next)
1064: dev_drain(d);
1.90 ratchov 1065: while (file_poll())
1066: ; /* nothing */
1.98 ratchov 1067:
1068: while (dev_list)
1069: dev_del(dev_list);
1.83 ratchov 1070: filelist_done();
1.98 ratchov 1071: if (nsock > 0) {
1.86 ratchov 1072: if (rmdir(base) < 0 && errno != ENOTEMPTY && errno != EPERM)
1.61 ratchov 1073: warn("rmdir(\"%s\")", base);
1074: }
1075: unsetsig();
1.15 ratchov 1076: return 0;
1.61 ratchov 1077: }
1078:
1079: int
1080: main(int argc, char **argv)
1081: {
1082: char *prog;
1083:
1.90 ratchov 1084: #ifdef DEBUG
1085: atexit(dbg_flush);
1086: #endif
1.61 ratchov 1087: prog = strrchr(argv[0], '/');
1088: if (prog == NULL)
1089: prog = argv[0];
1090: else
1091: prog++;
1092: if (strcmp(prog, PROG_AUCAT) == 0) {
1093: return aucat_main(argc, argv);
1094: } else if (strcmp(prog, PROG_MIDICAT) == 0) {
1095: return midicat_main(argc, argv);
1096: } else {
1097: fprintf(stderr, "%s: can't determine program to run\n", prog);
1098: }
1099: return 1;
1.1 kstailey 1100: }