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