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