1 /* $OpenBSD: sndiod.c,v 1.50 2024/12/20 07:35:56 ratchov Exp $ */ 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> 20 #include <sys/socket.h> 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" 38 #include "fdpass.h" 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 /* 54 * privileged user name 55 */ 56 #ifndef SNDIO_PRIV_USER 57 #define SNDIO_PRIV_USER "_sndiop" 58 #endif 59 60 /* 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 78 #define DEFAULT_ROUND 480 79 #endif 80 81 /* 82 * buffer size if neither ``-z'' nor ``-b'' is used 83 */ 84 #ifndef DEFAULT_BUFSZ 85 #define DEFAULT_BUFSZ 7680 86 #endif 87 88 /* 89 * default device precision 90 */ 91 #ifndef DEFAULT_BITS 92 #define DEFAULT_BITS 16 93 #endif 94 95 void sigint(int); 96 void sighup(int); 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); 103 void getbasepath(char *); 104 void setsig(void); 105 void unsetsig(void); 106 struct dev *mkdev(char *, struct aparams *, 107 int, int, int, int, int, int); 108 struct port *mkport(char *, int); 109 struct opt *mkopt(char *, struct dev *, 110 int, int, int, int, int, int, int, int); 111 112 unsigned int log_level = 0; 113 volatile sig_atomic_t quit_flag = 0, reopen_flag = 0; 114 115 char usagestr[] = "usage: sndiod [-d] [-a flag] [-b nframes] " 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"; 120 121 /* 122 * default audio devices 123 */ 124 static char *default_devs[] = { 125 "rsnd/0", "rsnd/1", "rsnd/2", "rsnd/3", 126 NULL 127 }; 128 129 /* 130 * default MIDI ports 131 */ 132 static char *default_ports[] = { 133 "rmidi/0", "rmidi/1", "rmidi/2", "rmidi/3", 134 "rmidi/4", "rmidi/5", "rmidi/6", "rmidi/7", 135 NULL 136 }; 137 138 /* 139 * SIGINT handler, it raises the quit flag. If the flag is already set, 140 * that means that the last SIGINT was not handled, because the process 141 * is blocked somewhere, so exit. 142 */ 143 void 144 sigint(int s) 145 { 146 if (quit_flag) 147 _exit(1); 148 quit_flag = 1; 149 } 150 151 /* 152 * SIGHUP handler, it raises the reopen flag, which requests devices 153 * to be reopened. 154 */ 155 void 156 sighup(int s) 157 { 158 reopen_flag = 1; 159 } 160 161 void 162 opt_ch(int *rcmin, int *rcmax) 163 { 164 char *next, *end; 165 long cmin, cmax; 166 167 errno = 0; 168 cmin = strtol(optarg, &next, 10); 169 if (next == optarg || *next != ':') 170 goto failed; 171 cmax = strtol(++next, &end, 10); 172 if (end == next || *end != '\0') 173 goto failed; 174 if (cmin < 0 || cmax < cmin || cmax >= NCHAN_MAX) 175 goto failed; 176 *rcmin = cmin; 177 *rcmax = cmax; 178 return; 179 failed: 180 errx(1, "%s: bad channel range", optarg); 181 } 182 183 void 184 opt_enc(struct aparams *par) 185 { 186 int len; 187 188 len = aparams_strtoenc(par, optarg); 189 if (len == 0 || optarg[len] != '\0') 190 errx(1, "%s: bad encoding", optarg); 191 } 192 193 int 194 opt_mmc(void) 195 { 196 if (strcmp("off", optarg) == 0) 197 return 0; 198 if (strcmp("slave", optarg) == 0) 199 return 1; 200 errx(1, "%s: off/slave expected", optarg); 201 } 202 203 int 204 opt_onoff(void) 205 { 206 if (strcmp("off", optarg) == 0) 207 return 0; 208 if (strcmp("on", optarg) == 0) 209 return 1; 210 errx(1, "%s: on/off expected", optarg); 211 } 212 213 int 214 getword(char *word, char **str) 215 { 216 char *p = *str; 217 218 for (;;) { 219 if (*word == '\0') 220 break; 221 if (*word++ != *p++) 222 return 0; 223 } 224 if (*p == ',' || *p == '\0') { 225 *str = p; 226 return 1; 227 } 228 return 0; 229 } 230 231 unsigned int 232 opt_mode(void) 233 { 234 unsigned int mode = 0; 235 char *p = optarg; 236 237 for (;;) { 238 if (getword("play", &p)) { 239 mode |= MODE_PLAY; 240 } else if (getword("rec", &p)) { 241 mode |= MODE_REC; 242 } else if (getword("mon", &p)) { 243 mode |= MODE_MON; 244 } else if (getword("midi", &p)) { 245 mode |= MODE_MIDIMASK; 246 } else 247 errx(1, "%s: bad mode", optarg); 248 if (*p == '\0') 249 break; 250 p++; 251 } 252 if (mode == 0) 253 errx(1, "empty mode"); 254 return mode; 255 } 256 257 /* 258 * Open all devices. Possibly switch to the new devices if they have higher 259 * priorities than the current ones. 260 */ 261 static void 262 reopen_devs(void) 263 { 264 struct opt *o; 265 struct dev *d, *a; 266 267 for (o = opt_list; o != NULL; o = o->next) { 268 269 /* skip unused logical devices and ones with fixed hardware */ 270 if (o->refcnt == 0 || strcmp(o->name, o->dev->name) == 0) 271 continue; 272 273 /* circulate to the device with the highest prio */ 274 a = o->alt_first; 275 for (d = a; d->alt_next != a; d = d->alt_next) { 276 if (d->num > o->alt_first->num) 277 o->alt_first = d; 278 } 279 280 /* switch to the first working one, in pririty order */ 281 d = o->alt_first; 282 while (d != o->dev) { 283 if (opt_setdev(o, d)) 284 break; 285 d = d->alt_next; 286 } 287 } 288 289 /* 290 * retry to open the remaining devices that are not used but need 291 * to stay open (ex. '-a on') 292 */ 293 for (d = dev_list; d != NULL; d = d->next) { 294 if (d->refcnt > 0 && d->pstate == DEV_CFG) 295 dev_open(d); 296 } 297 } 298 299 /* 300 * For each port, open the alt with the highest priority and switch to it 301 */ 302 static void 303 reopen_ports(void) 304 { 305 struct port *p, *a, *apri; 306 int inuse; 307 308 for (p = port_list; p != NULL; p = a->next) { 309 310 /* skip unused ports */ 311 inuse = 0; 312 a = p; 313 while (1) { 314 if (midi_rxmask(a->midi) || a->midi->txmask) 315 inuse = 1; 316 if (a->alt_next == p) 317 break; 318 a = a->alt_next; 319 } 320 if (!inuse) 321 continue; 322 323 /* open the alt with the highest prio */ 324 apri = port_alt_ref(p->num); 325 326 /* switch to it */ 327 a = p; 328 while (1) { 329 if (a != apri) { 330 midi_migrate(a->midi, apri->midi); 331 port_unref(a); 332 } 333 if (a->alt_next == p) 334 break; 335 a = a->alt_next; 336 } 337 } 338 } 339 340 void 341 setsig(void) 342 { 343 struct sigaction sa; 344 345 quit_flag = 0; 346 reopen_flag = 0; 347 sigfillset(&sa.sa_mask); 348 sa.sa_flags = SA_RESTART; 349 sa.sa_handler = sigint; 350 if (sigaction(SIGINT, &sa, NULL) == -1) 351 err(1, "sigaction(int) failed"); 352 if (sigaction(SIGTERM, &sa, NULL) == -1) 353 err(1, "sigaction(term) failed"); 354 sa.sa_handler = sighup; 355 if (sigaction(SIGHUP, &sa, NULL) == -1) 356 err(1, "sigaction(hup) failed"); 357 } 358 359 void 360 unsetsig(void) 361 { 362 struct sigaction sa; 363 364 sigfillset(&sa.sa_mask); 365 sa.sa_flags = SA_RESTART; 366 sa.sa_handler = SIG_DFL; 367 if (sigaction(SIGHUP, &sa, NULL) == -1) 368 err(1, "unsetsig(hup): sigaction failed"); 369 if (sigaction(SIGTERM, &sa, NULL) == -1) 370 err(1, "unsetsig(term): sigaction failed"); 371 if (sigaction(SIGINT, &sa, NULL) == -1) 372 err(1, "unsetsig(int): sigaction failed"); 373 } 374 375 void 376 getbasepath(char *base) 377 { 378 uid_t uid; 379 struct stat sb; 380 mode_t mask, omask; 381 382 uid = geteuid(); 383 if (uid == 0) { 384 mask = 022; 385 snprintf(base, SOCKPATH_MAX, SOCKPATH_DIR); 386 } else { 387 mask = 077; 388 snprintf(base, SOCKPATH_MAX, SOCKPATH_DIR "-%u", uid); 389 } 390 omask = umask(mask); 391 if (mkdir(base, 0777) == -1) { 392 if (errno != EEXIST) 393 err(1, "mkdir(\"%s\")", base); 394 } 395 umask(omask); 396 if (stat(base, &sb) == -1) 397 err(1, "stat(\"%s\")", base); 398 if (!S_ISDIR(sb.st_mode)) 399 errx(1, "%s is not a directory", base); 400 if (sb.st_uid != uid || (sb.st_mode & mask) != 0) 401 errx(1, "%s has wrong permissions", base); 402 } 403 404 struct dev * 405 mkdev(char *path, struct aparams *par, 406 int mode, int bufsz, int round, int rate, int hold, int autovol) 407 { 408 struct dev *d; 409 410 for (d = dev_list; d != NULL; d = d->next) { 411 if (strcmp(d->path, path) == 0) 412 return d; 413 } 414 if (!bufsz && !round) { 415 round = DEFAULT_ROUND; 416 bufsz = DEFAULT_BUFSZ; 417 } else if (!bufsz) { 418 bufsz = round * 2; 419 } else if (!round) 420 round = bufsz / 2; 421 d = dev_new(path, par, mode, bufsz, round, rate, hold, autovol); 422 if (d == NULL) 423 exit(1); 424 return d; 425 } 426 427 struct port * 428 mkport(char *path, int hold) 429 { 430 struct port *c; 431 432 for (c = port_list; c != NULL; c = c->next) { 433 if (strcmp(c->path, path) == 0) 434 return c; 435 } 436 c = port_new(path, MODE_MIDIMASK, hold); 437 if (c == NULL) 438 exit(1); 439 return c; 440 } 441 442 struct opt * 443 mkopt(char *path, struct dev *d, 444 int pmin, int pmax, int rmin, int rmax, 445 int mode, int vol, int mmc, int dup) 446 { 447 struct opt *o; 448 449 o = opt_new(d, path, pmin, pmax, rmin, rmax, 450 MIDI_TO_ADATA(vol), mmc, dup, mode); 451 if (o == NULL) 452 return NULL; 453 dev_adjpar(d, o->mode, o->pmax, o->rmax); 454 return o; 455 } 456 457 static void 458 dounveil(char *name, char *prefix, char *path_prefix) 459 { 460 size_t prefix_len; 461 char path[PATH_MAX]; 462 463 prefix_len = strlen(prefix); 464 465 if (strncmp(name, prefix, prefix_len) != 0) 466 errx(1, "%s: unsupported device or port format", name); 467 snprintf(path, sizeof(path), "%s%s", path_prefix, name + prefix_len); 468 if (unveil(path, "rw") == -1) 469 err(1, "unveil %s", path); 470 } 471 472 static int 473 start_helper(int background) 474 { 475 struct dev *d; 476 struct port *p; 477 struct passwd *pw; 478 int s[2]; 479 pid_t pid; 480 481 if (geteuid() == 0) { 482 if ((pw = getpwnam(SNDIO_PRIV_USER)) == NULL) 483 errx(1, "unknown user %s", SNDIO_PRIV_USER); 484 } else 485 pw = NULL; 486 if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == -1) { 487 perror("socketpair"); 488 return 0; 489 } 490 pid = fork(); 491 if (pid == -1) { 492 perror("fork"); 493 return 0; 494 } 495 if (pid == 0) { 496 setproctitle("helper"); 497 close(s[0]); 498 if (fdpass_new(s[1], &helper_fileops) == NULL) 499 return 0; 500 if (background) { 501 log_flush(); 502 log_level = 0; 503 if (daemon(0, 0) == -1) 504 err(1, "daemon"); 505 } 506 if (pw != NULL) { 507 if (setgroups(1, &pw->pw_gid) || 508 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 509 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 510 err(1, "cannot drop privileges"); 511 } 512 for (d = dev_list; d != NULL; d = d->next) { 513 dounveil(d->path, "rsnd/", "/dev/audio"); 514 dounveil(d->path, "rsnd/", "/dev/audioctl"); 515 } 516 for (p = port_list; p != NULL; p = p->next) { 517 dounveil(p->path, "rmidi/", "/dev/rmidi"); 518 } 519 if (pledge("stdio sendfd rpath wpath", NULL) == -1) 520 err(1, "pledge"); 521 while (file_poll()) 522 ; /* nothing */ 523 exit(0); 524 } else { 525 close(s[1]); 526 if (fdpass_new(s[0], &worker_fileops) == NULL) 527 return 0; 528 } 529 return 1; 530 } 531 532 static void 533 stop_helper(void) 534 { 535 if (fdpass_peer) 536 fdpass_close(fdpass_peer); 537 } 538 539 int 540 main(int argc, char **argv) 541 { 542 int c, i, background, unit; 543 int pmin, pmax, rmin, rmax; 544 char base[SOCKPATH_MAX], path[SOCKPATH_MAX]; 545 unsigned int mode, dup, mmc, vol; 546 unsigned int hold, autovol, bufsz, round, rate; 547 const char *str; 548 struct aparams par; 549 struct opt *o; 550 struct dev *d, *dev_first, *dev_next; 551 struct port *p, *port_first, *port_next; 552 struct listen *l; 553 struct passwd *pw; 554 struct tcpaddr { 555 char *host; 556 struct tcpaddr *next; 557 } *tcpaddr_list, *ta; 558 559 atexit(log_flush); 560 561 /* 562 * global options defaults 563 */ 564 vol = 127; 565 dup = 1; 566 mmc = 0; 567 hold = 0; 568 autovol = 0; 569 bufsz = 0; 570 round = 0; 571 rate = DEFAULT_RATE; 572 unit = 0; 573 background = 1; 574 pmin = 0; 575 pmax = 1; 576 rmin = 0; 577 rmax = 1; 578 par.bits = DEFAULT_BITS; 579 par.bps = APARAMS_BPS(par.bits); 580 par.le = ADATA_LE; 581 par.sig = 1; 582 par.msb = 0; 583 mode = MODE_PLAY | MODE_REC; 584 dev_first = dev_next = NULL; 585 port_first = port_next = NULL; 586 tcpaddr_list = NULL; 587 d = NULL; 588 p = NULL; 589 590 slot_array_init(); 591 592 while ((c = getopt(argc, argv, 593 "a:b:c:C:de:F:f:j:L:m:Q:q:r:s:t:U:v:w:x:z:")) != -1) { 594 switch (c) { 595 case 'd': 596 log_level++; 597 background = 0; 598 break; 599 case 'U': 600 unit = strtonum(optarg, 0, 15, &str); 601 if (str) 602 errx(1, "%s: unit number is %s", optarg, str); 603 break; 604 case 'L': 605 ta = xmalloc(sizeof(struct tcpaddr)); 606 ta->host = optarg; 607 ta->next = tcpaddr_list; 608 tcpaddr_list = ta; 609 break; 610 case 'm': 611 mode = opt_mode(); 612 break; 613 case 'j': 614 dup = opt_onoff(); 615 break; 616 case 't': 617 mmc = opt_mmc(); 618 break; 619 case 'c': 620 opt_ch(&pmin, &pmax); 621 break; 622 case 'C': 623 opt_ch(&rmin, &rmax); 624 break; 625 case 'e': 626 opt_enc(&par); 627 break; 628 case 'r': 629 rate = strtonum(optarg, RATE_MIN, RATE_MAX, &str); 630 if (str) 631 errx(1, "%s: rate is %s", optarg, str); 632 break; 633 case 'v': 634 vol = strtonum(optarg, 0, MIDI_MAXCTL, &str); 635 if (str) 636 errx(1, "%s: volume is %s", optarg, str); 637 break; 638 case 's': 639 if (d == NULL) { 640 for (i = 0; default_devs[i] != NULL; i++) { 641 mkdev(default_devs[i], &par, 0, 642 bufsz, round, rate, 0, autovol); 643 } 644 d = dev_list; 645 } 646 if (mkopt(optarg, d, pmin, pmax, rmin, rmax, 647 mode, vol, mmc, dup) == NULL) 648 return 1; 649 break; 650 case 'q': 651 p = mkport(optarg, hold); 652 /* create new circulate list */ 653 port_first = port_next = p; 654 break; 655 case 'Q': 656 if (p == NULL) 657 errx(1, "-Q %s: no ports defined", optarg); 658 p = mkport(optarg, hold); 659 /* add to circulate list */ 660 p->alt_next = port_next; 661 port_first->alt_next = p; 662 port_next = p; 663 break; 664 case 'a': 665 hold = opt_onoff(); 666 break; 667 case 'w': 668 autovol = opt_onoff(); 669 break; 670 case 'b': 671 bufsz = strtonum(optarg, 1, RATE_MAX, &str); 672 if (str) 673 errx(1, "%s: buffer size is %s", optarg, str); 674 break; 675 case 'z': 676 round = strtonum(optarg, 1, SHRT_MAX, &str); 677 if (str) 678 errx(1, "%s: block size is %s", optarg, str); 679 break; 680 case 'f': 681 d = mkdev(optarg, &par, 0, bufsz, round, 682 rate, hold, autovol); 683 /* create new circulate list */ 684 dev_first = dev_next = d; 685 break; 686 case 'F': 687 if (d == NULL) 688 errx(1, "-F %s: no devices defined", optarg); 689 d = mkdev(optarg, &par, 0, bufsz, round, 690 rate, hold, autovol); 691 /* add to circulate list */ 692 d->alt_next = dev_next; 693 dev_first->alt_next = d; 694 dev_next = d; 695 break; 696 default: 697 fputs(usagestr, stderr); 698 return 1; 699 } 700 } 701 argc -= optind; 702 argv += optind; 703 if (argc > 0) { 704 fputs(usagestr, stderr); 705 return 1; 706 } 707 if (port_list == NULL) { 708 for (i = 0; default_ports[i] != NULL; i++) 709 mkport(default_ports[i], 0); 710 } 711 if (dev_list == NULL) { 712 for (i = 0; default_devs[i] != NULL; i++) { 713 mkdev(default_devs[i], &par, 0, 714 bufsz, round, rate, 0, autovol); 715 } 716 } 717 718 /* 719 * Add default sub-device (if none) backed by the last device 720 */ 721 o = opt_byname("default"); 722 if (o == NULL) { 723 o = mkopt("default", dev_list, pmin, pmax, rmin, rmax, 724 mode, vol, 0, dup); 725 if (o == NULL) 726 return 1; 727 } 728 729 /* 730 * For each device create an anonymous sub-device using 731 * the "default" sub-device as template 732 */ 733 for (d = dev_list; d != NULL; d = d->next) { 734 if (opt_new(d, NULL, o->pmin, o->pmax, o->rmin, o->rmax, 735 o->maxweight, o->mtc != NULL, o->dup, o->mode) == NULL) 736 return 1; 737 dev_adjpar(d, o->mode, o->pmax, o->rmax); 738 } 739 740 setsig(); 741 filelist_init(); 742 743 if (!start_helper(background)) 744 return 1; 745 746 if (geteuid() == 0) { 747 if ((pw = getpwnam(SNDIO_USER)) == NULL) 748 errx(1, "unknown user %s", SNDIO_USER); 749 } else 750 pw = NULL; 751 getbasepath(base); 752 snprintf(path, SOCKPATH_MAX, "%s/" SOCKPATH_FILE "%u", base, unit); 753 if (!listen_new_un(path)) 754 return 1; 755 for (ta = tcpaddr_list; ta != NULL; ta = ta->next) { 756 if (!listen_new_tcp(ta->host, AUCAT_PORT + unit)) 757 return 1; 758 } 759 for (l = listen_list; l != NULL; l = l->next) { 760 if (!listen_init(l)) 761 return 1; 762 } 763 midi_init(); 764 for (p = port_list; p != NULL; p = p->next) { 765 if (!port_init(p)) 766 return 1; 767 } 768 for (d = dev_list; d != NULL; d = d->next) { 769 if (!dev_init(d)) 770 return 1; 771 } 772 for (o = opt_list; o != NULL; o = o->next) 773 opt_init(o); 774 if (background) { 775 log_flush(); 776 log_level = 0; 777 if (daemon(0, 0) == -1) 778 err(1, "daemon"); 779 } 780 if (pw != NULL) { 781 if (setpriority(PRIO_PROCESS, 0, SNDIO_PRIO) == -1) 782 err(1, "setpriority"); 783 if (chroot(pw->pw_dir) == -1 || chdir("/") == -1) 784 err(1, "cannot chroot to %s", pw->pw_dir); 785 if (setgroups(1, &pw->pw_gid) == -1 || 786 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 || 787 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1 ) 788 err(1, "cannot drop privileges"); 789 } 790 if (tcpaddr_list) { 791 if (pledge("stdio audio recvfd unix inet", NULL) == -1) 792 err(1, "pledge"); 793 } else { 794 if (pledge("stdio audio recvfd unix", NULL) == -1) 795 err(1, "pledge"); 796 } 797 798 for (;;) { 799 if (quit_flag) 800 break; 801 if (reopen_flag) { 802 reopen_flag = 0; 803 reopen_devs(); 804 reopen_ports(); 805 } 806 if (!fdpass_peer) 807 break; 808 if (!file_poll()) 809 break; 810 } 811 stop_helper(); 812 while (listen_list != NULL) 813 listen_close(listen_list); 814 while (sock_list != NULL) 815 sock_close(sock_list); 816 for (o = opt_list; o != NULL; o = o->next) 817 opt_done(o); 818 for (d = dev_list; d != NULL; d = d->next) 819 dev_done(d); 820 for (p = port_list; p != NULL; p = p->next) 821 port_done(p); 822 while (file_poll()) 823 ; /* nothing */ 824 midi_done(); 825 826 while (opt_list) 827 opt_del(opt_list); 828 while (dev_list) 829 dev_del(dev_list); 830 while (port_list) 831 port_del(port_list); 832 while (tcpaddr_list) { 833 ta = tcpaddr_list; 834 tcpaddr_list = ta->next; 835 xfree(ta); 836 } 837 filelist_done(); 838 unsetsig(); 839 return 0; 840 } 841