1 /* $OpenBSD: sndiod.c,v 1.48 2022/03/07 08:58:33 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 void 258 setsig(void) 259 { 260 struct sigaction sa; 261 262 quit_flag = 0; 263 reopen_flag = 0; 264 sigfillset(&sa.sa_mask); 265 sa.sa_flags = SA_RESTART; 266 sa.sa_handler = sigint; 267 if (sigaction(SIGINT, &sa, NULL) == -1) 268 err(1, "sigaction(int) failed"); 269 if (sigaction(SIGTERM, &sa, NULL) == -1) 270 err(1, "sigaction(term) failed"); 271 sa.sa_handler = sighup; 272 if (sigaction(SIGHUP, &sa, NULL) == -1) 273 err(1, "sigaction(hup) failed"); 274 } 275 276 void 277 unsetsig(void) 278 { 279 struct sigaction sa; 280 281 sigfillset(&sa.sa_mask); 282 sa.sa_flags = SA_RESTART; 283 sa.sa_handler = SIG_DFL; 284 if (sigaction(SIGHUP, &sa, NULL) == -1) 285 err(1, "unsetsig(hup): sigaction failed"); 286 if (sigaction(SIGTERM, &sa, NULL) == -1) 287 err(1, "unsetsig(term): sigaction failed"); 288 if (sigaction(SIGINT, &sa, NULL) == -1) 289 err(1, "unsetsig(int): sigaction failed"); 290 } 291 292 void 293 getbasepath(char *base) 294 { 295 uid_t uid; 296 struct stat sb; 297 mode_t mask, omask; 298 299 uid = geteuid(); 300 if (uid == 0) { 301 mask = 022; 302 snprintf(base, SOCKPATH_MAX, SOCKPATH_DIR); 303 } else { 304 mask = 077; 305 snprintf(base, SOCKPATH_MAX, SOCKPATH_DIR "-%u", uid); 306 } 307 omask = umask(mask); 308 if (mkdir(base, 0777) == -1) { 309 if (errno != EEXIST) 310 err(1, "mkdir(\"%s\")", base); 311 } 312 umask(omask); 313 if (stat(base, &sb) == -1) 314 err(1, "stat(\"%s\")", base); 315 if (!S_ISDIR(sb.st_mode)) 316 errx(1, "%s is not a directory", base); 317 if (sb.st_uid != uid || (sb.st_mode & mask) != 0) 318 errx(1, "%s has wrong permissions", base); 319 } 320 321 struct dev * 322 mkdev(char *path, struct aparams *par, 323 int mode, int bufsz, int round, int rate, int hold, int autovol) 324 { 325 struct dev *d; 326 327 for (d = dev_list; d != NULL; d = d->next) { 328 if (strcmp(d->path, path) == 0) 329 return d; 330 } 331 if (!bufsz && !round) { 332 round = DEFAULT_ROUND; 333 bufsz = DEFAULT_BUFSZ; 334 } else if (!bufsz) { 335 bufsz = round * 2; 336 } else if (!round) 337 round = bufsz / 2; 338 d = dev_new(path, par, mode, bufsz, round, rate, hold, autovol); 339 if (d == NULL) 340 exit(1); 341 return d; 342 } 343 344 struct port * 345 mkport(char *path, int hold) 346 { 347 struct port *c; 348 349 for (c = port_list; c != NULL; c = c->next) { 350 if (strcmp(c->path, path) == 0) 351 return c; 352 } 353 c = port_new(path, MODE_MIDIMASK, hold); 354 if (c == NULL) 355 exit(1); 356 return c; 357 } 358 359 struct opt * 360 mkopt(char *path, struct dev *d, 361 int pmin, int pmax, int rmin, int rmax, 362 int mode, int vol, int mmc, int dup) 363 { 364 struct opt *o; 365 366 o = opt_new(d, path, pmin, pmax, rmin, rmax, 367 MIDI_TO_ADATA(vol), mmc, dup, mode); 368 if (o == NULL) 369 return NULL; 370 dev_adjpar(d, o->mode, o->pmax, o->rmax); 371 return o; 372 } 373 374 static void 375 dounveil(char *name, char *prefix, char *path_prefix) 376 { 377 size_t prefix_len; 378 char path[PATH_MAX]; 379 380 prefix_len = strlen(prefix); 381 382 if (strncmp(name, prefix, prefix_len) != 0) 383 errx(1, "%s: unsupported device or port format", name); 384 snprintf(path, sizeof(path), "%s%s", path_prefix, name + prefix_len); 385 if (unveil(path, "rw") == -1) 386 err(1, "unveil %s", path); 387 } 388 389 static int 390 start_helper(int background) 391 { 392 struct dev *d; 393 struct port *p; 394 struct passwd *pw; 395 int s[2]; 396 pid_t pid; 397 398 if (geteuid() == 0) { 399 if ((pw = getpwnam(SNDIO_PRIV_USER)) == NULL) 400 errx(1, "unknown user %s", SNDIO_PRIV_USER); 401 } else 402 pw = NULL; 403 if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == -1) { 404 perror("socketpair"); 405 return 0; 406 } 407 pid = fork(); 408 if (pid == -1) { 409 log_puts("can't fork\n"); 410 return 0; 411 } 412 if (pid == 0) { 413 setproctitle("helper"); 414 close(s[0]); 415 if (fdpass_new(s[1], &helper_fileops) == NULL) 416 return 0; 417 if (background) { 418 log_flush(); 419 log_level = 0; 420 if (daemon(0, 0) == -1) 421 err(1, "daemon"); 422 } 423 if (pw != NULL) { 424 if (setgroups(1, &pw->pw_gid) || 425 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 426 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 427 err(1, "cannot drop privileges"); 428 } 429 for (d = dev_list; d != NULL; d = d->next) { 430 dounveil(d->path, "rsnd/", "/dev/audio"); 431 dounveil(d->path, "rsnd/", "/dev/audioctl"); 432 } 433 for (p = port_list; p != NULL; p = p->next) { 434 dounveil(p->path, "rmidi/", "/dev/rmidi"); 435 } 436 if (pledge("stdio sendfd rpath wpath", NULL) == -1) 437 err(1, "pledge"); 438 while (file_poll()) 439 ; /* nothing */ 440 exit(0); 441 } else { 442 close(s[1]); 443 if (fdpass_new(s[0], &worker_fileops) == NULL) 444 return 0; 445 } 446 return 1; 447 } 448 449 static void 450 stop_helper(void) 451 { 452 if (fdpass_peer) 453 fdpass_close(fdpass_peer); 454 } 455 456 int 457 main(int argc, char **argv) 458 { 459 int c, i, background, unit; 460 int pmin, pmax, rmin, rmax; 461 char base[SOCKPATH_MAX], path[SOCKPATH_MAX]; 462 unsigned int mode, dup, mmc, vol; 463 unsigned int hold, autovol, bufsz, round, rate; 464 unsigned int reopen_list; 465 const char *str; 466 struct aparams par; 467 struct opt *o; 468 struct dev *d, *dev_first, *dev_next; 469 struct port *p, *port_first, *port_next; 470 struct listen *l; 471 struct passwd *pw; 472 struct tcpaddr { 473 char *host; 474 struct tcpaddr *next; 475 } *tcpaddr_list, *ta; 476 477 atexit(log_flush); 478 479 /* 480 * global options defaults 481 */ 482 vol = 127; 483 dup = 1; 484 mmc = 0; 485 hold = 0; 486 autovol = 0; 487 bufsz = 0; 488 round = 0; 489 rate = DEFAULT_RATE; 490 unit = 0; 491 background = 1; 492 pmin = 0; 493 pmax = 1; 494 rmin = 0; 495 rmax = 1; 496 par.bits = DEFAULT_BITS; 497 par.bps = APARAMS_BPS(par.bits); 498 par.le = ADATA_LE; 499 par.sig = 1; 500 par.msb = 0; 501 mode = MODE_PLAY | MODE_REC; 502 dev_first = dev_next = NULL; 503 port_first = port_next = NULL; 504 tcpaddr_list = NULL; 505 d = NULL; 506 p = NULL; 507 508 slot_array_init(); 509 510 while ((c = getopt(argc, argv, 511 "a:b:c:C:de:F:f:j:L:m:Q:q:r:s:t:U:v:w:x:z:")) != -1) { 512 switch (c) { 513 case 'd': 514 log_level++; 515 background = 0; 516 break; 517 case 'U': 518 unit = strtonum(optarg, 0, 15, &str); 519 if (str) 520 errx(1, "%s: unit number is %s", optarg, str); 521 break; 522 case 'L': 523 ta = xmalloc(sizeof(struct tcpaddr)); 524 ta->host = optarg; 525 ta->next = tcpaddr_list; 526 tcpaddr_list = ta; 527 break; 528 case 'm': 529 mode = opt_mode(); 530 break; 531 case 'j': 532 dup = opt_onoff(); 533 break; 534 case 't': 535 mmc = opt_mmc(); 536 break; 537 case 'c': 538 opt_ch(&pmin, &pmax); 539 break; 540 case 'C': 541 opt_ch(&rmin, &rmax); 542 break; 543 case 'e': 544 opt_enc(&par); 545 break; 546 case 'r': 547 rate = strtonum(optarg, RATE_MIN, RATE_MAX, &str); 548 if (str) 549 errx(1, "%s: rate is %s", optarg, str); 550 break; 551 case 'v': 552 vol = strtonum(optarg, 0, MIDI_MAXCTL, &str); 553 if (str) 554 errx(1, "%s: volume is %s", optarg, str); 555 break; 556 case 's': 557 if (d == NULL) { 558 for (i = 0; default_devs[i] != NULL; i++) { 559 mkdev(default_devs[i], &par, 0, 560 bufsz, round, rate, 0, autovol); 561 } 562 d = dev_list; 563 } 564 if (mkopt(optarg, d, pmin, pmax, rmin, rmax, 565 mode, vol, mmc, dup) == NULL) 566 return 1; 567 break; 568 case 'q': 569 p = mkport(optarg, hold); 570 /* create new circulate list */ 571 port_first = port_next = p; 572 break; 573 case 'Q': 574 if (p == NULL) 575 errx(1, "-Q %s: no ports defined", optarg); 576 p = mkport(optarg, hold); 577 /* add to circulate list */ 578 p->alt_next = port_next; 579 port_first->alt_next = p; 580 port_next = p; 581 break; 582 case 'a': 583 hold = opt_onoff(); 584 break; 585 case 'w': 586 autovol = opt_onoff(); 587 break; 588 case 'b': 589 bufsz = strtonum(optarg, 1, RATE_MAX, &str); 590 if (str) 591 errx(1, "%s: buffer size is %s", optarg, str); 592 break; 593 case 'z': 594 round = strtonum(optarg, 1, SHRT_MAX, &str); 595 if (str) 596 errx(1, "%s: block size is %s", optarg, str); 597 break; 598 case 'f': 599 d = mkdev(optarg, &par, 0, bufsz, round, 600 rate, hold, autovol); 601 /* create new circulate list */ 602 dev_first = dev_next = d; 603 break; 604 case 'F': 605 if (d == NULL) 606 errx(1, "-F %s: no devices defined", optarg); 607 d = mkdev(optarg, &par, 0, bufsz, round, 608 rate, hold, autovol); 609 /* add to circulate list */ 610 d->alt_next = dev_next; 611 dev_first->alt_next = d; 612 dev_next = d; 613 break; 614 default: 615 fputs(usagestr, stderr); 616 return 1; 617 } 618 } 619 argc -= optind; 620 argv += optind; 621 if (argc > 0) { 622 fputs(usagestr, stderr); 623 return 1; 624 } 625 if (port_list == NULL) { 626 for (i = 0; default_ports[i] != NULL; i++) 627 mkport(default_ports[i], 0); 628 } 629 if (dev_list == NULL) { 630 for (i = 0; default_devs[i] != NULL; i++) { 631 mkdev(default_devs[i], &par, 0, 632 bufsz, round, rate, 0, autovol); 633 } 634 } 635 636 /* 637 * Add default sub-device (if none) backed by the last device 638 */ 639 o = opt_byname("default"); 640 if (o == NULL) { 641 o = mkopt("default", dev_list, pmin, pmax, rmin, rmax, 642 mode, vol, 0, dup); 643 if (o == NULL) 644 return 1; 645 } 646 647 /* 648 * For each device create an anonymous sub-device using 649 * the "default" sub-device as template 650 */ 651 for (d = dev_list; d != NULL; d = d->next) { 652 if (opt_new(d, NULL, o->pmin, o->pmax, o->rmin, o->rmax, 653 o->maxweight, o->mtc != NULL, o->dup, o->mode) == NULL) 654 return 1; 655 dev_adjpar(d, o->mode, o->pmax, o->rmax); 656 } 657 658 setsig(); 659 filelist_init(); 660 661 if (!start_helper(background)) 662 return 1; 663 664 if (geteuid() == 0) { 665 if ((pw = getpwnam(SNDIO_USER)) == NULL) 666 errx(1, "unknown user %s", SNDIO_USER); 667 } else 668 pw = NULL; 669 getbasepath(base); 670 snprintf(path, SOCKPATH_MAX, "%s/" SOCKPATH_FILE "%u", base, unit); 671 if (!listen_new_un(path)) 672 return 1; 673 for (ta = tcpaddr_list; ta != NULL; ta = ta->next) { 674 if (!listen_new_tcp(ta->host, AUCAT_PORT + unit)) 675 return 1; 676 } 677 for (l = listen_list; l != NULL; l = l->next) { 678 if (!listen_init(l)) 679 return 1; 680 } 681 midi_init(); 682 for (p = port_list; p != NULL; p = p->next) { 683 if (!port_init(p)) 684 return 1; 685 } 686 for (d = dev_list; d != NULL; d = d->next) { 687 if (!dev_init(d)) 688 return 1; 689 } 690 for (o = opt_list; o != NULL; o = o->next) 691 opt_init(o); 692 if (background) { 693 log_flush(); 694 log_level = 0; 695 if (daemon(0, 0) == -1) 696 err(1, "daemon"); 697 } 698 if (pw != NULL) { 699 if (setpriority(PRIO_PROCESS, 0, SNDIO_PRIO) == -1) 700 err(1, "setpriority"); 701 if (chroot(pw->pw_dir) == -1 || chdir("/") == -1) 702 err(1, "cannot chroot to %s", pw->pw_dir); 703 if (setgroups(1, &pw->pw_gid) == -1 || 704 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 || 705 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1 ) 706 err(1, "cannot drop privileges"); 707 } 708 if (tcpaddr_list) { 709 if (pledge("stdio audio recvfd unix inet", NULL) == -1) 710 err(1, "pledge"); 711 } else { 712 if (pledge("stdio audio recvfd unix", NULL) == -1) 713 err(1, "pledge"); 714 } 715 for (;;) { 716 if (quit_flag) 717 break; 718 if (reopen_flag) { 719 reopen_flag = 0; 720 721 reopen_list = 0; 722 for (d = dev_list; d != NULL; d = d->next) { 723 if (d->pstate != DEV_CFG) 724 reopen_list |= (1 << d->num); 725 } 726 for (d = dev_list; d != NULL; d = d->next) { 727 if (reopen_list & (1 << d->num)) 728 dev_migrate(d); 729 } 730 731 reopen_list = 0; 732 for (p = port_list; p != NULL; p = p->next) { 733 if (p->state != PORT_CFG) 734 reopen_list |= (1 << p->num); 735 } 736 for (p = port_list; p != NULL; p = p->next) { 737 if (reopen_list & (1 << p->num)) { 738 if (port_migrate(p) != p) 739 port_close(p); 740 } 741 } 742 } 743 if (!fdpass_peer) 744 break; 745 if (!file_poll()) 746 break; 747 } 748 stop_helper(); 749 while (listen_list != NULL) 750 listen_close(listen_list); 751 while (sock_list != NULL) 752 sock_close(sock_list); 753 for (o = opt_list; o != NULL; o = o->next) 754 opt_done(o); 755 for (d = dev_list; d != NULL; d = d->next) 756 dev_done(d); 757 for (p = port_list; p != NULL; p = p->next) 758 port_done(p); 759 while (file_poll()) 760 ; /* nothing */ 761 midi_done(); 762 763 while (opt_list) 764 opt_del(opt_list); 765 while (dev_list) 766 dev_del(dev_list); 767 while (port_list) 768 port_del(port_list); 769 while (tcpaddr_list) { 770 ta = tcpaddr_list; 771 tcpaddr_list = ta->next; 772 xfree(ta); 773 } 774 filelist_done(); 775 unsetsig(); 776 return 0; 777 } 778