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