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