1 /* $OpenBSD: sndiod.c,v 1.41 2020/06/18 05:11:13 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 (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"); 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, devindex; 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 = 118; 481 dup = 1; 482 mmc = 0; 483 hold = 0; 484 autovol = 1; 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 devindex = 0; 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 = dev_list) == NULL) { 547 d = mkdev(default_devs[devindex++], &par, 0, 548 bufsz, round, rate, hold, autovol); 549 } 550 if (mkopt(optarg, d, pmin, pmax, rmin, rmax, 551 mode, vol, mmc, dup) == NULL) 552 return 1; 553 break; 554 case 'q': 555 mkport(optarg, hold); 556 break; 557 case 'Q': 558 if (port_list == NULL) 559 errx(1, "-Q %s: no ports defined", optarg); 560 namelist_add(&port_list->path_list, optarg); 561 break; 562 case 'a': 563 hold = opt_onoff(); 564 break; 565 case 'w': 566 autovol = opt_onoff(); 567 break; 568 case 'b': 569 bufsz = strtonum(optarg, 1, RATE_MAX, &str); 570 if (str) 571 errx(1, "%s: buffer size is %s", optarg, str); 572 break; 573 case 'z': 574 round = strtonum(optarg, 1, SHRT_MAX, &str); 575 if (str) 576 errx(1, "%s: block size is %s", optarg, str); 577 break; 578 case 'f': 579 mkdev(optarg, &par, 0, bufsz, round, 580 rate, hold, autovol); 581 devindex = -1; 582 break; 583 case 'F': 584 if ((d = dev_list) == NULL) 585 errx(1, "-F %s: no devices defined", optarg); 586 if (!dev_addname(d, optarg)) 587 exit(1); 588 break; 589 default: 590 fputs(usagestr, stderr); 591 return 1; 592 } 593 } 594 argc -= optind; 595 argv += optind; 596 if (argc > 0) { 597 fputs(usagestr, stderr); 598 return 1; 599 } 600 if (port_list == NULL) { 601 for (i = 0; default_ports[i] != NULL; i++) 602 mkport(default_ports[i], 0); 603 } 604 if (devindex != -1) { 605 for (i = devindex; default_devs[i] != NULL; i++) { 606 mkdev(default_devs[i], &par, 0, 607 bufsz, round, rate, 0, autovol); 608 } 609 } 610 for (d = dev_list; d != NULL; d = d->next) { 611 if (opt_byname(d, "default")) 612 continue; 613 if (mkopt("default", d, pmin, pmax, rmin, rmax, 614 mode, vol, mmc, dup) == NULL) 615 return 1; 616 } 617 618 setsig(); 619 filelist_init(); 620 621 if (!start_helper(background)) 622 return 1; 623 624 if (geteuid() == 0) { 625 if ((pw = getpwnam(SNDIO_USER)) == NULL) 626 errx(1, "unknown user %s", SNDIO_USER); 627 } else 628 pw = NULL; 629 getbasepath(base); 630 snprintf(path, SOCKPATH_MAX, "%s/" SOCKPATH_FILE "%u", base, unit); 631 if (!listen_new_un(path)) 632 return 1; 633 for (ta = tcpaddr_list; ta != NULL; ta = ta->next) { 634 if (!listen_new_tcp(ta->host, AUCAT_PORT + unit)) 635 return 1; 636 } 637 for (l = listen_list; l != NULL; l = l->next) { 638 if (!listen_init(l)) 639 return 1; 640 } 641 midi_init(); 642 for (p = port_list; p != NULL; p = p->next) { 643 if (!port_init(p)) 644 return 1; 645 } 646 for (d = dev_list; d != NULL; d = d->next) { 647 if (!dev_init(d)) 648 return 1; 649 } 650 if (background) { 651 log_flush(); 652 log_level = 0; 653 if (daemon(0, 0) == -1) 654 err(1, "daemon"); 655 } 656 if (pw != NULL) { 657 if (setpriority(PRIO_PROCESS, 0, SNDIO_PRIO) == -1) 658 err(1, "setpriority"); 659 if (chroot(pw->pw_dir) == -1 || chdir("/") == -1) 660 err(1, "cannot chroot to %s", pw->pw_dir); 661 if (setgroups(1, &pw->pw_gid) == -1 || 662 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 || 663 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1 ) 664 err(1, "cannot drop privileges"); 665 } 666 if (tcpaddr_list) { 667 if (pledge("stdio audio recvfd unix inet", NULL) == -1) 668 err(1, "pledge"); 669 } else { 670 if (pledge("stdio audio recvfd unix", NULL) == -1) 671 err(1, "pledge"); 672 } 673 for (;;) { 674 if (quit_flag) 675 break; 676 if (reopen_flag) { 677 reopen_flag = 0; 678 for (d = dev_list; d != NULL; d = d->next) 679 dev_reopen(d); 680 for (p = port_list; p != NULL; p = p->next) 681 port_reopen(p); 682 } 683 if (!fdpass_peer) 684 break; 685 if (!file_poll()) 686 break; 687 } 688 stop_helper(); 689 while (listen_list != NULL) 690 listen_close(listen_list); 691 while (sock_list != NULL) 692 sock_close(sock_list); 693 for (d = dev_list; d != NULL; d = d->next) 694 dev_done(d); 695 for (p = port_list; p != NULL; p = p->next) 696 port_done(p); 697 while (file_poll()) 698 ; /* nothing */ 699 midi_done(); 700 701 while (dev_list) 702 dev_del(dev_list); 703 while (port_list) 704 port_del(port_list); 705 while (tcpaddr_list) { 706 ta = tcpaddr_list; 707 tcpaddr_list = ta->next; 708 xfree(ta); 709 } 710 filelist_done(); 711 unsetsig(); 712 return 0; 713 } 714