1 /* $OpenBSD: sndiod.c,v 1.46 2021/07/12 15:09:20 beck 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 (d->alt_list->next == NULL && 322 strcmp(d->alt_list->name, path) == 0) 323 return d; 324 } 325 if (!bufsz && !round) { 326 round = DEFAULT_ROUND; 327 bufsz = DEFAULT_BUFSZ; 328 } else if (!bufsz) { 329 bufsz = round * 2; 330 } else if (!round) 331 round = bufsz / 2; 332 d = dev_new(path, par, mode, bufsz, round, rate, hold, autovol); 333 if (d == NULL) 334 exit(1); 335 return d; 336 } 337 338 struct port * 339 mkport(char *path, int hold) 340 { 341 struct port *c; 342 343 for (c = port_list; c != NULL; c = c->next) { 344 if (c->path_list->next == NULL && 345 strcmp(c->path_list->str, path) == 0) 346 return c; 347 } 348 c = port_new(path, MODE_MIDIMASK, hold); 349 if (c == NULL) 350 exit(1); 351 return c; 352 } 353 354 struct opt * 355 mkopt(char *path, struct dev *d, 356 int pmin, int pmax, int rmin, int rmax, 357 int mode, int vol, int mmc, int dup) 358 { 359 struct opt *o; 360 361 o = opt_new(d, path, pmin, pmax, rmin, rmax, 362 MIDI_TO_ADATA(vol), mmc, dup, mode); 363 if (o == NULL) 364 return NULL; 365 dev_adjpar(d, o->mode, o->pmax, o->rmax); 366 return o; 367 } 368 369 static void 370 dounveil(char *name, char *prefix, char *path_prefix) 371 { 372 size_t prefix_len; 373 char path[PATH_MAX]; 374 375 prefix_len = strlen(prefix); 376 377 if (strncmp(name, prefix, prefix_len) != 0) 378 errx(1, "%s: unsupported device or port format", name); 379 snprintf(path, sizeof(path), "%s%s", path_prefix, name + prefix_len); 380 if (unveil(path, "rw") == -1) 381 err(1, "unveil %s", path); 382 } 383 384 static int 385 start_helper(int background) 386 { 387 struct dev *d; 388 struct dev_alt *da; 389 struct port *p; 390 struct passwd *pw; 391 struct name *n; 392 int s[2]; 393 pid_t pid; 394 395 if (geteuid() == 0) { 396 if ((pw = getpwnam(SNDIO_PRIV_USER)) == NULL) 397 errx(1, "unknown user %s", SNDIO_PRIV_USER); 398 } else 399 pw = NULL; 400 if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == -1) { 401 perror("socketpair"); 402 return 0; 403 } 404 pid = fork(); 405 if (pid == -1) { 406 log_puts("can't fork\n"); 407 return 0; 408 } 409 if (pid == 0) { 410 setproctitle("helper"); 411 close(s[0]); 412 if (fdpass_new(s[1], &helper_fileops) == NULL) 413 return 0; 414 if (background) { 415 log_flush(); 416 log_level = 0; 417 if (daemon(0, 0) == -1) 418 err(1, "daemon"); 419 } 420 if (pw != NULL) { 421 if (setgroups(1, &pw->pw_gid) || 422 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 423 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 424 err(1, "cannot drop privileges"); 425 } 426 for (d = dev_list; d != NULL; d = d->next) { 427 for (da = d->alt_list; da != NULL; da = da->next) { 428 dounveil(da->name, "rsnd/", "/dev/audio"); 429 dounveil(da->name, "rsnd/", "/dev/audioctl"); 430 } 431 } 432 for (p = port_list; p != NULL; p = p->next) { 433 for (n = p->path_list; n != NULL; n = n->next) 434 dounveil(n->str, "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 const char *str; 465 struct aparams par; 466 struct dev *d; 467 struct port *p; 468 struct listen *l; 469 struct passwd *pw; 470 struct tcpaddr { 471 char *host; 472 struct tcpaddr *next; 473 } *tcpaddr_list, *ta; 474 475 atexit(log_flush); 476 477 /* 478 * global options defaults 479 */ 480 vol = 127; 481 dup = 1; 482 mmc = 0; 483 hold = 0; 484 autovol = 0; 485 bufsz = 0; 486 round = 0; 487 rate = DEFAULT_RATE; 488 unit = 0; 489 background = 1; 490 pmin = 0; 491 pmax = 1; 492 rmin = 0; 493 rmax = 1; 494 aparams_init(&par); 495 mode = MODE_PLAY | MODE_REC; 496 tcpaddr_list = NULL; 497 d = NULL; 498 p = NULL; 499 500 slot_array_init(); 501 502 while ((c = getopt(argc, argv, 503 "a:b:c:C:de:F:f:j:L:m:Q:q:r:s:t:U:v:w:x:z:")) != -1) { 504 switch (c) { 505 case 'd': 506 log_level++; 507 background = 0; 508 break; 509 case 'U': 510 unit = strtonum(optarg, 0, 15, &str); 511 if (str) 512 errx(1, "%s: unit number is %s", optarg, str); 513 break; 514 case 'L': 515 ta = xmalloc(sizeof(struct tcpaddr)); 516 ta->host = optarg; 517 ta->next = tcpaddr_list; 518 tcpaddr_list = ta; 519 break; 520 case 'm': 521 mode = opt_mode(); 522 break; 523 case 'j': 524 dup = opt_onoff(); 525 break; 526 case 't': 527 mmc = opt_mmc(); 528 break; 529 case 'c': 530 opt_ch(&pmin, &pmax); 531 break; 532 case 'C': 533 opt_ch(&rmin, &rmax); 534 break; 535 case 'e': 536 opt_enc(&par); 537 break; 538 case 'r': 539 rate = strtonum(optarg, RATE_MIN, RATE_MAX, &str); 540 if (str) 541 errx(1, "%s: rate is %s", optarg, str); 542 break; 543 case 'v': 544 vol = strtonum(optarg, 0, MIDI_MAXCTL, &str); 545 if (str) 546 errx(1, "%s: volume is %s", optarg, str); 547 break; 548 case 's': 549 if (d == NULL) { 550 for (i = 0; default_devs[i] != NULL; i++) { 551 mkdev(default_devs[i], &par, 0, 552 bufsz, round, rate, 0, autovol); 553 } 554 d = dev_list; 555 } 556 if (mkopt(optarg, d, pmin, pmax, rmin, rmax, 557 mode, vol, mmc, dup) == NULL) 558 return 1; 559 break; 560 case 'q': 561 p = mkport(optarg, hold); 562 break; 563 case 'Q': 564 if (p == NULL) 565 errx(1, "-Q %s: no ports defined", optarg); 566 namelist_add(&p->path_list, optarg); 567 break; 568 case 'a': 569 hold = opt_onoff(); 570 break; 571 case 'w': 572 autovol = opt_onoff(); 573 break; 574 case 'b': 575 bufsz = strtonum(optarg, 1, RATE_MAX, &str); 576 if (str) 577 errx(1, "%s: buffer size is %s", optarg, str); 578 break; 579 case 'z': 580 round = strtonum(optarg, 1, SHRT_MAX, &str); 581 if (str) 582 errx(1, "%s: block size is %s", optarg, str); 583 break; 584 case 'f': 585 d = mkdev(optarg, &par, 0, bufsz, round, 586 rate, hold, autovol); 587 break; 588 case 'F': 589 if (d == NULL) 590 errx(1, "-F %s: no devices defined", optarg); 591 if (!dev_addname(d, optarg)) 592 exit(1); 593 break; 594 default: 595 fputs(usagestr, stderr); 596 return 1; 597 } 598 } 599 argc -= optind; 600 argv += optind; 601 if (argc > 0) { 602 fputs(usagestr, stderr); 603 return 1; 604 } 605 if (port_list == NULL) { 606 for (i = 0; default_ports[i] != NULL; i++) 607 mkport(default_ports[i], 0); 608 } 609 if (dev_list == NULL) { 610 for (i = 0; default_devs[i] != NULL; i++) { 611 mkdev(default_devs[i], &par, 0, 612 bufsz, round, rate, 0, autovol); 613 } 614 } 615 for (d = dev_list; d != NULL; d = d->next) { 616 if (opt_byname(d, "default")) 617 continue; 618 if (mkopt("default", d, pmin, pmax, rmin, rmax, 619 mode, vol, mmc, dup) == NULL) 620 return 1; 621 } 622 623 setsig(); 624 filelist_init(); 625 626 if (!start_helper(background)) 627 return 1; 628 629 if (geteuid() == 0) { 630 if ((pw = getpwnam(SNDIO_USER)) == NULL) 631 errx(1, "unknown user %s", SNDIO_USER); 632 } else 633 pw = NULL; 634 getbasepath(base); 635 snprintf(path, SOCKPATH_MAX, "%s/" SOCKPATH_FILE "%u", base, unit); 636 if (!listen_new_un(path)) 637 return 1; 638 for (ta = tcpaddr_list; ta != NULL; ta = ta->next) { 639 if (!listen_new_tcp(ta->host, AUCAT_PORT + unit)) 640 return 1; 641 } 642 for (l = listen_list; l != NULL; l = l->next) { 643 if (!listen_init(l)) 644 return 1; 645 } 646 midi_init(); 647 for (p = port_list; p != NULL; p = p->next) { 648 if (!port_init(p)) 649 return 1; 650 } 651 for (d = dev_list; d != NULL; d = d->next) { 652 if (!dev_init(d)) 653 return 1; 654 } 655 if (background) { 656 log_flush(); 657 log_level = 0; 658 if (daemon(0, 0) == -1) 659 err(1, "daemon"); 660 } 661 if (pw != NULL) { 662 if (setpriority(PRIO_PROCESS, 0, SNDIO_PRIO) == -1) 663 err(1, "setpriority"); 664 if (chroot(pw->pw_dir) == -1 || chdir("/") == -1) 665 err(1, "cannot chroot to %s", pw->pw_dir); 666 if (setgroups(1, &pw->pw_gid) == -1 || 667 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 || 668 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1 ) 669 err(1, "cannot drop privileges"); 670 } 671 if (tcpaddr_list) { 672 if (pledge("stdio audio recvfd unix inet", NULL) == -1) 673 err(1, "pledge"); 674 } else { 675 if (pledge("stdio audio recvfd unix", NULL) == -1) 676 err(1, "pledge"); 677 } 678 for (;;) { 679 if (quit_flag) 680 break; 681 if (reopen_flag) { 682 reopen_flag = 0; 683 for (d = dev_list; d != NULL; d = d->next) 684 dev_reopen(d); 685 for (p = port_list; p != NULL; p = p->next) 686 port_reopen(p); 687 } 688 if (!fdpass_peer) 689 break; 690 if (!file_poll()) 691 break; 692 } 693 stop_helper(); 694 while (listen_list != NULL) 695 listen_close(listen_list); 696 while (sock_list != NULL) 697 sock_close(sock_list); 698 for (d = dev_list; d != NULL; d = d->next) 699 dev_done(d); 700 for (p = port_list; p != NULL; p = p->next) 701 port_done(p); 702 while (file_poll()) 703 ; /* nothing */ 704 midi_done(); 705 706 while (opt_list) 707 opt_del(opt_list); 708 while (dev_list) 709 dev_del(dev_list); 710 while (port_list) 711 port_del(port_list); 712 while (tcpaddr_list) { 713 ta = tcpaddr_list; 714 tcpaddr_list = ta->next; 715 xfree(ta); 716 } 717 filelist_done(); 718 unsetsig(); 719 return 0; 720 } 721