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