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