Annotation of src/usr.bin/sndiod/sndiod.c, Revision 1.39
1.39 ! ratchov 1: /* $OpenBSD: sndiod.c,v 1.38 2020/02/26 13:53:58 ratchov Exp $ */
1.1 ratchov 2: /*
3: * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org>
4: *
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/stat.h>
18: #include <sys/types.h>
19: #include <sys/resource.h>
1.18 ratchov 20: #include <sys/socket.h>
1.1 ratchov 21:
22: #include <err.h>
23: #include <errno.h>
24: #include <fcntl.h>
25: #include <grp.h>
26: #include <limits.h>
27: #include <pwd.h>
28: #include <signal.h>
29: #include <sndio.h>
30: #include <stdio.h>
31: #include <stdlib.h>
32: #include <string.h>
33: #include <unistd.h>
34:
35: #include "amsg.h"
36: #include "defs.h"
37: #include "dev.h"
1.18 ratchov 38: #include "fdpass.h"
1.1 ratchov 39: #include "file.h"
40: #include "listen.h"
41: #include "midi.h"
42: #include "opt.h"
43: #include "sock.h"
44: #include "utils.h"
45:
46: /*
47: * unprivileged user name
48: */
49: #ifndef SNDIO_USER
50: #define SNDIO_USER "_sndio"
51: #endif
52:
53: /*
1.18 ratchov 54: * privileged user name
55: */
56: #ifndef SNDIO_PRIV_USER
57: #define SNDIO_PRIV_USER "_sndiop"
58: #endif
59:
60: /*
1.1 ratchov 61: * priority when run as root
62: */
63: #ifndef SNDIO_PRIO
64: #define SNDIO_PRIO (-20)
65: #endif
66:
67: /*
68: * sample rate if no ``-r'' is used
69: */
70: #ifndef DEFAULT_RATE
71: #define DEFAULT_RATE 48000
72: #endif
73:
74: /*
75: * block size if neither ``-z'' nor ``-b'' is used
76: */
77: #ifndef DEFAULT_ROUND
1.37 ratchov 78: #define DEFAULT_ROUND 480
1.1 ratchov 79: #endif
80:
81: /*
82: * buffer size if neither ``-z'' nor ``-b'' is used
83: */
84: #ifndef DEFAULT_BUFSZ
1.8 dcoppa 85: #define DEFAULT_BUFSZ 7680
1.1 ratchov 86: #endif
87:
88: /*
89: * default device in server mode
90: */
91: #ifndef DEFAULT_DEV
92: #define DEFAULT_DEV "rsnd/0"
93: #endif
1.5 ratchov 94:
95: void sigint(int);
1.36 ratchov 96: void sighup(int);
1.5 ratchov 97: void opt_ch(int *, int *);
98: void opt_enc(struct aparams *);
99: int opt_mmc(void);
100: int opt_onoff(void);
101: int getword(char *, char **);
102: unsigned int opt_mode(void);
1.31 ratchov 103: void getbasepath(char *);
1.5 ratchov 104: void setsig(void);
105: void unsetsig(void);
106: struct dev *mkdev(char *, struct aparams *,
107: int, int, int, int, int, int);
1.27 ratchov 108: struct port *mkport(char *, int);
1.5 ratchov 109: struct opt *mkopt(char *, struct dev *,
110: int, int, int, int, int, int, int, int);
1.1 ratchov 111:
112: unsigned int log_level = 0;
1.36 ratchov 113: volatile sig_atomic_t quit_flag = 0, reopen_flag = 0;
1.1 ratchov 114:
115: char usagestr[] = "usage: sndiod [-d] [-a flag] [-b nframes] "
1.36 ratchov 116: "[-C min:max] [-c min:max]\n\t"
117: "[-e enc] [-F device] [-f device] [-j flag] [-L addr] [-m mode]\n\t"
118: "[-Q port] [-q port] [-r rate] [-s name] [-t mode] [-U unit]\n\t"
119: "[-v volume] [-w flag] [-z nframes]\n";
1.1 ratchov 120:
121: /*
1.39 ! ratchov 122: * default MIDI ports
! 123: */
! 124: static char *default_ports[] = {
! 125: "rmidi/0", "rmidi/1", "rmidi/2", "rmidi/3",
! 126: "rmidi/4", "rmidi/5", "rmidi/6", "rmidi/7",
! 127: NULL
! 128: };
! 129:
! 130: /*
1.1 ratchov 131: * SIGINT handler, it raises the quit flag. If the flag is already set,
132: * that means that the last SIGINT was not handled, because the process
133: * is blocked somewhere, so exit.
134: */
135: void
136: sigint(int s)
137: {
138: if (quit_flag)
139: _exit(1);
140: quit_flag = 1;
141: }
142:
1.36 ratchov 143: /*
144: * SIGHUP handler, it raises the reopen flag, which requests devices
145: * to be reopened.
146: */
147: void
148: sighup(int s)
149: {
150: reopen_flag = 1;
151: }
152:
1.1 ratchov 153: void
154: opt_ch(int *rcmin, int *rcmax)
155: {
156: char *next, *end;
157: long cmin, cmax;
158:
159: errno = 0;
160: cmin = strtol(optarg, &next, 10);
161: if (next == optarg || *next != ':')
162: goto failed;
163: cmax = strtol(++next, &end, 10);
164: if (end == next || *end != '\0')
165: goto failed;
166: if (cmin < 0 || cmax < cmin || cmax >= NCHAN_MAX)
167: goto failed;
168: *rcmin = cmin;
169: *rcmax = cmax;
170: return;
171: failed:
172: errx(1, "%s: bad channel range", optarg);
173: }
174:
175: void
176: opt_enc(struct aparams *par)
177: {
178: int len;
179:
180: len = aparams_strtoenc(par, optarg);
181: if (len == 0 || optarg[len] != '\0')
182: errx(1, "%s: bad encoding", optarg);
183: }
184:
185: int
186: opt_mmc(void)
187: {
188: if (strcmp("off", optarg) == 0)
189: return 0;
190: if (strcmp("slave", optarg) == 0)
191: return 1;
192: errx(1, "%s: off/slave expected", optarg);
193: }
194:
195: int
196: opt_onoff(void)
197: {
198: if (strcmp("off", optarg) == 0)
199: return 0;
200: if (strcmp("on", optarg) == 0)
201: return 1;
202: errx(1, "%s: on/off expected", optarg);
203: }
204:
1.4 ratchov 205: int
206: getword(char *word, char **str)
207: {
208: char *p = *str;
209:
210: for (;;) {
211: if (*word == '\0')
212: break;
213: if (*word++ != *p++)
214: return 0;
215: }
216: if (*p == ',' || *p == '\0') {
217: *str = p;
218: return 1;
219: }
220: return 0;
221: }
222:
1.1 ratchov 223: unsigned int
224: opt_mode(void)
225: {
226: unsigned int mode = 0;
227: char *p = optarg;
228:
1.4 ratchov 229: for (;;) {
230: if (getword("play", &p)) {
1.1 ratchov 231: mode |= MODE_PLAY;
1.4 ratchov 232: } else if (getword("rec", &p)) {
1.1 ratchov 233: mode |= MODE_REC;
1.4 ratchov 234: } else if (getword("mon", &p)) {
1.1 ratchov 235: mode |= MODE_MON;
1.4 ratchov 236: } else if (getword("midi", &p)) {
1.1 ratchov 237: mode |= MODE_MIDIMASK;
1.4 ratchov 238: } else
1.1 ratchov 239: errx(1, "%s: bad mode", optarg);
240: if (*p == '\0')
241: break;
1.4 ratchov 242: p++;
1.1 ratchov 243: }
244: if (mode == 0)
245: errx(1, "empty mode");
246: return mode;
247: }
248:
249: void
250: setsig(void)
251: {
252: struct sigaction sa;
253:
254: quit_flag = 0;
1.36 ratchov 255: reopen_flag = 0;
1.1 ratchov 256: sigfillset(&sa.sa_mask);
257: sa.sa_flags = SA_RESTART;
258: sa.sa_handler = sigint;
1.35 ratchov 259: if (sigaction(SIGINT, &sa, NULL) == -1)
1.1 ratchov 260: err(1, "sigaction(int) failed");
1.35 ratchov 261: if (sigaction(SIGTERM, &sa, NULL) == -1)
1.1 ratchov 262: err(1, "sigaction(term) failed");
1.36 ratchov 263: sa.sa_handler = sighup;
1.35 ratchov 264: if (sigaction(SIGHUP, &sa, NULL) == -1)
1.1 ratchov 265: err(1, "sigaction(hup) failed");
266: }
267:
268: void
269: unsetsig(void)
270: {
271: struct sigaction sa;
272:
273: sigfillset(&sa.sa_mask);
274: sa.sa_flags = SA_RESTART;
275: sa.sa_handler = SIG_DFL;
1.35 ratchov 276: if (sigaction(SIGHUP, &sa, NULL) == -1)
1.29 ratchov 277: err(1, "unsetsig(hup): sigaction failed");
1.35 ratchov 278: if (sigaction(SIGTERM, &sa, NULL) == -1)
1.29 ratchov 279: err(1, "unsetsig(term): sigaction failed");
1.35 ratchov 280: if (sigaction(SIGINT, &sa, NULL) == -1)
1.29 ratchov 281: err(1, "unsetsig(int): sigaction failed");
1.1 ratchov 282: }
283:
284: void
1.31 ratchov 285: getbasepath(char *base)
1.1 ratchov 286: {
287: uid_t uid;
288: struct stat sb;
1.14 ratchov 289: mode_t mask, omask;
1.1 ratchov 290:
291: uid = geteuid();
292: if (uid == 0) {
293: mask = 022;
1.10 ratchov 294: snprintf(base, SOCKPATH_MAX, SOCKPATH_DIR);
1.1 ratchov 295: } else {
296: mask = 077;
1.10 ratchov 297: snprintf(base, SOCKPATH_MAX, SOCKPATH_DIR "-%u", uid);
1.1 ratchov 298: }
1.14 ratchov 299: omask = umask(mask);
1.35 ratchov 300: if (mkdir(base, 0777) == -1) {
1.1 ratchov 301: if (errno != EEXIST)
302: err(1, "mkdir(\"%s\")", base);
303: }
1.28 ratchov 304: umask(omask);
1.35 ratchov 305: if (stat(base, &sb) == -1)
1.1 ratchov 306: err(1, "stat(\"%s\")", base);
1.30 ratchov 307: if (!S_ISDIR(sb.st_mode))
308: errx(1, "%s is not a directory", base);
1.1 ratchov 309: if (sb.st_uid != uid || (sb.st_mode & mask) != 0)
310: errx(1, "%s has wrong permissions", base);
311: }
312:
313: struct dev *
314: mkdev(char *path, struct aparams *par,
315: int mode, int bufsz, int round, int rate, int hold, int autovol)
316: {
317: struct dev *d;
318:
319: for (d = dev_list; d != NULL; d = d->next) {
1.36 ratchov 320: if (d->path_list->next == NULL &&
321: strcmp(d->path_list->str, path) == 0)
1.1 ratchov 322: return d;
323: }
324: if (!bufsz && !round) {
325: round = DEFAULT_ROUND;
326: bufsz = DEFAULT_BUFSZ;
327: } else if (!bufsz) {
328: bufsz = round * 2;
329: } else if (!round)
330: round = bufsz / 2;
331: d = dev_new(path, par, mode, bufsz, round, rate, hold, autovol);
332: if (d == NULL)
333: exit(1);
334: return d;
335: }
336:
1.27 ratchov 337: struct port *
338: mkport(char *path, int hold)
339: {
340: struct port *c;
341:
342: for (c = port_list; c != NULL; c = c->next) {
1.36 ratchov 343: if (c->path_list->next == NULL &&
344: strcmp(c->path_list->str, path) == 0)
1.27 ratchov 345: return c;
346: }
347: c = port_new(path, MODE_MIDIMASK, hold);
348: if (c == NULL)
349: exit(1);
350: return c;
351: }
352:
1.1 ratchov 353: struct opt *
354: mkopt(char *path, struct dev *d,
355: int pmin, int pmax, int rmin, int rmax,
356: int mode, int vol, int mmc, int dup)
357: {
358: struct opt *o;
359:
1.33 ratchov 360: o = opt_new(d, path, pmin, pmax, rmin, rmax,
1.1 ratchov 361: MIDI_TO_ADATA(vol), mmc, dup, mode);
362: if (o == NULL)
1.26 ratchov 363: return NULL;
1.31 ratchov 364: dev_adjpar(d, o->mode, o->pmax, o->rmax);
1.1 ratchov 365: return o;
366: }
367:
1.34 ratchov 368: static void
369: dounveil(char *name, char *prefix, char *path_prefix)
370: {
371: size_t prefix_len;
372: char path[PATH_MAX];
373:
374: prefix_len = strlen(prefix);
375:
376: if (strncmp(name, prefix, prefix_len) != 0)
377: errx(1, "%s: unsupported device or port format", name);
378: snprintf(path, sizeof(path), "%s%s", path_prefix, name + prefix_len);
1.35 ratchov 379: if (unveil(path, "rw") == -1)
1.34 ratchov 380: err(1, "unveil");
381: }
382:
1.32 ratchov 383: static int
384: start_helper(int background)
385: {
1.34 ratchov 386: struct dev *d;
387: struct port *p;
1.32 ratchov 388: struct passwd *pw;
1.36 ratchov 389: struct name *n;
1.32 ratchov 390: int s[2];
391: pid_t pid;
392:
393: if (geteuid() == 0) {
394: if ((pw = getpwnam(SNDIO_PRIV_USER)) == NULL)
395: errx(1, "unknown user %s", SNDIO_PRIV_USER);
396: } else
397: pw = NULL;
1.35 ratchov 398: if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == -1) {
1.32 ratchov 399: perror("socketpair");
400: return 0;
401: }
402: pid = fork();
403: if (pid == -1) {
404: log_puts("can't fork\n");
405: return 0;
406: }
407: if (pid == 0) {
408: setproctitle("helper");
409: close(s[0]);
410: if (fdpass_new(s[1], &helper_fileops) == NULL)
411: return 0;
412: if (background) {
413: log_flush();
414: log_level = 0;
1.35 ratchov 415: if (daemon(0, 0) == -1)
1.32 ratchov 416: err(1, "daemon");
417: }
418: if (pw != NULL) {
419: if (setgroups(1, &pw->pw_gid) ||
420: setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
421: setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
422: err(1, "cannot drop privileges");
423: }
1.36 ratchov 424: for (d = dev_list; d != NULL; d = d->next) {
1.38 ratchov 425: for (n = d->path_list; n != NULL; n = n->next) {
1.36 ratchov 426: dounveil(n->str, "rsnd/", "/dev/audio");
1.38 ratchov 427: dounveil(n->str, "rsnd/", "/dev/audioctl");
428: }
1.36 ratchov 429: }
430: for (p = port_list; p != NULL; p = p->next) {
431: for (n = p->path_list; n != NULL; n = n->next)
432: dounveil(n->str, "rmidi/", "/dev/rmidi");
433: }
1.35 ratchov 434: if (pledge("stdio sendfd rpath wpath", NULL) == -1)
1.32 ratchov 435: err(1, "pledge");
436: while (file_poll())
437: ; /* nothing */
438: exit(0);
439: } else {
440: close(s[1]);
441: if (fdpass_new(s[0], &worker_fileops) == NULL)
442: return 0;
443: }
444: return 1;
445: }
446:
447: static void
448: stop_helper(void)
449: {
450: if (fdpass_peer)
451: fdpass_close(fdpass_peer);
452: }
453:
1.1 ratchov 454: int
455: main(int argc, char **argv)
456: {
1.39 ! ratchov 457: int c, i, background, unit;
1.1 ratchov 458: int pmin, pmax, rmin, rmax;
1.25 ratchov 459: char base[SOCKPATH_MAX], path[SOCKPATH_MAX];
1.1 ratchov 460: unsigned int mode, dup, mmc, vol;
461: unsigned int hold, autovol, bufsz, round, rate;
462: const char *str;
463: struct aparams par;
464: struct dev *d;
465: struct port *p;
466: struct listen *l;
1.17 ratchov 467: struct passwd *pw;
1.25 ratchov 468: struct tcpaddr {
469: char *host;
470: struct tcpaddr *next;
471: } *tcpaddr_list, *ta;
1.1 ratchov 472:
473: atexit(log_flush);
474:
475: /*
476: * global options defaults
477: */
478: vol = 118;
479: dup = 1;
480: mmc = 0;
481: hold = 0;
482: autovol = 1;
483: bufsz = 0;
484: round = 0;
485: rate = DEFAULT_RATE;
486: unit = 0;
487: background = 1;
488: pmin = 0;
489: pmax = 1;
490: rmin = 0;
491: rmax = 1;
492: aparams_init(&par);
493: mode = MODE_PLAY | MODE_REC;
1.25 ratchov 494: tcpaddr_list = NULL;
1.1 ratchov 495:
1.36 ratchov 496: while ((c = getopt(argc, argv,
497: "a:b:c:C:de:F:f:j:L:m:Q:q:r:s:t:U:v:w:x:z:")) != -1) {
1.1 ratchov 498: switch (c) {
499: case 'd':
500: log_level++;
501: background = 0;
502: break;
503: case 'U':
504: unit = strtonum(optarg, 0, 15, &str);
505: if (str)
506: errx(1, "%s: unit number is %s", optarg, str);
507: break;
508: case 'L':
1.25 ratchov 509: ta = xmalloc(sizeof(struct tcpaddr));
510: ta->host = optarg;
511: ta->next = tcpaddr_list;
512: tcpaddr_list = ta;
1.1 ratchov 513: break;
514: case 'm':
515: mode = opt_mode();
516: break;
517: case 'j':
518: dup = opt_onoff();
519: break;
520: case 't':
521: mmc = opt_mmc();
522: break;
523: case 'c':
524: opt_ch(&pmin, &pmax);
525: break;
526: case 'C':
527: opt_ch(&rmin, &rmax);
528: break;
529: case 'e':
530: opt_enc(&par);
531: break;
532: case 'r':
533: rate = strtonum(optarg, RATE_MIN, RATE_MAX, &str);
534: if (str)
535: errx(1, "%s: rate is %s", optarg, str);
536: break;
537: case 'v':
538: vol = strtonum(optarg, 0, MIDI_MAXCTL, &str);
539: if (str)
540: errx(1, "%s: volume is %s", optarg, str);
541: break;
542: case 's':
543: if ((d = dev_list) == NULL) {
1.28 ratchov 544: d = mkdev(DEFAULT_DEV, &par, 0, bufsz, round,
545: rate, hold, autovol);
1.1 ratchov 546: }
1.26 ratchov 547: if (mkopt(optarg, d, pmin, pmax, rmin, rmax,
548: mode, vol, mmc, dup) == NULL)
549: return 1;
1.1 ratchov 550: break;
551: case 'q':
1.27 ratchov 552: mkport(optarg, hold);
1.1 ratchov 553: break;
1.36 ratchov 554: case 'Q':
555: if (port_list == NULL)
556: errx(1, "-Q %s: no ports defined", optarg);
557: namelist_add(&port_list->path_list, optarg);
558: break;
1.1 ratchov 559: case 'a':
560: hold = opt_onoff();
561: break;
562: case 'w':
563: autovol = opt_onoff();
564: break;
565: case 'b':
566: bufsz = strtonum(optarg, 1, RATE_MAX, &str);
567: if (str)
568: errx(1, "%s: buffer size is %s", optarg, str);
569: break;
570: case 'z':
571: round = strtonum(optarg, 1, SHRT_MAX, &str);
572: if (str)
573: errx(1, "%s: block size is %s", optarg, str);
574: break;
575: case 'f':
1.28 ratchov 576: mkdev(optarg, &par, 0, bufsz, round,
577: rate, hold, autovol);
1.1 ratchov 578: break;
1.36 ratchov 579: case 'F':
580: if (dev_list == NULL)
581: errx(1, "-F %s: no devices defined", optarg);
582: namelist_add(&dev_list->path_list, optarg);
583: break;
1.1 ratchov 584: default:
585: fputs(usagestr, stderr);
586: return 1;
587: }
588: }
589: argc -= optind;
590: argv += optind;
591: if (argc > 0) {
592: fputs(usagestr, stderr);
593: return 1;
1.39 ! ratchov 594: }
! 595: if (port_list == NULL) {
! 596: for (i = 0; default_ports[i] != NULL; i++)
! 597: mkport(default_ports[i], 0);
1.1 ratchov 598: }
599: if (dev_list == NULL)
600: mkdev(DEFAULT_DEV, &par, 0, bufsz, round, rate, hold, autovol);
601: for (d = dev_list; d != NULL; d = d->next) {
1.33 ratchov 602: if (opt_byname(d, "default"))
1.1 ratchov 603: continue;
1.26 ratchov 604: if (mkopt("default", d, pmin, pmax, rmin, rmax,
605: mode, vol, mmc, dup) == NULL)
606: return 1;
1.1 ratchov 607: }
1.17 ratchov 608:
609: setsig();
610: filelist_init();
611:
1.32 ratchov 612: if (!start_helper(background))
613: return 1;
614:
615: if (geteuid() == 0) {
1.20 ratchov 616: if ((pw = getpwnam(SNDIO_USER)) == NULL)
617: errx(1, "unknown user %s", SNDIO_USER);
1.32 ratchov 618: } else
619: pw = NULL;
620: getbasepath(base);
621: snprintf(path, SOCKPATH_MAX, "%s/" SOCKPATH_FILE "%u", base, unit);
622: if (!listen_new_un(path))
623: return 1;
624: for (ta = tcpaddr_list; ta != NULL; ta = ta->next) {
625: if (!listen_new_tcp(ta->host, AUCAT_PORT + unit))
626: return 1;
1.20 ratchov 627: }
1.32 ratchov 628: for (l = listen_list; l != NULL; l = l->next) {
629: if (!listen_init(l))
630: return 1;
1.13 ratchov 631: }
1.32 ratchov 632: midi_init();
633: for (p = port_list; p != NULL; p = p->next) {
634: if (!port_init(p))
635: return 1;
1.1 ratchov 636: }
1.32 ratchov 637: for (d = dev_list; d != NULL; d = d->next) {
638: if (!dev_init(d))
1.1 ratchov 639: return 1;
1.32 ratchov 640: }
641: if (background) {
642: log_flush();
643: log_level = 0;
1.35 ratchov 644: if (daemon(0, 0) == -1)
1.32 ratchov 645: err(1, "daemon");
646: }
647: if (pw != NULL) {
1.35 ratchov 648: if (setpriority(PRIO_PROCESS, 0, SNDIO_PRIO) == -1)
1.32 ratchov 649: err(1, "setpriority");
1.35 ratchov 650: if (chroot(pw->pw_dir) == -1 || chdir("/") == -1)
1.32 ratchov 651: err(1, "cannot chroot to %s", pw->pw_dir);
1.35 ratchov 652: if (setgroups(1, &pw->pw_gid) == -1 ||
653: setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
654: setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1 )
1.32 ratchov 655: err(1, "cannot drop privileges");
656: }
657: if (tcpaddr_list) {
658: if (pledge("stdio audio recvfd unix inet", NULL) == -1)
1.22 ratchov 659: err(1, "pledge");
1.18 ratchov 660: } else {
1.32 ratchov 661: if (pledge("stdio audio recvfd unix", NULL) == -1)
662: err(1, "pledge");
663: }
664: for (;;) {
665: if (quit_flag)
666: break;
1.36 ratchov 667: if (reopen_flag) {
668: reopen_flag = 0;
669: for (d = dev_list; d != NULL; d = d->next)
670: dev_reopen(d);
671: for (p = port_list; p != NULL; p = p->next)
672: port_reopen(p);
673: }
1.32 ratchov 674: if (!fdpass_peer)
675: break;
676: if (!file_poll())
677: break;
678: }
679: stop_helper();
680: while (listen_list != NULL)
681: listen_close(listen_list);
682: while (sock_list != NULL)
683: sock_close(sock_list);
684: for (d = dev_list; d != NULL; d = d->next)
685: dev_done(d);
686: for (p = port_list; p != NULL; p = p->next)
687: port_done(p);
688: while (file_poll())
689: ; /* nothing */
690: midi_done();
1.18 ratchov 691:
1.1 ratchov 692: while (dev_list)
693: dev_del(dev_list);
694: while (port_list)
695: port_del(port_list);
1.25 ratchov 696: while (tcpaddr_list) {
697: ta = tcpaddr_list;
698: tcpaddr_list = ta->next;
699: xfree(ta);
700: }
1.1 ratchov 701: filelist_done();
702: unsetsig();
703: return 0;
704: }