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