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