1 /* $OpenBSD: sndiod.c,v 1.6 2014/03/05 20:24:16 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/param.h> 18 #include <sys/queue.h> 19 #include <sys/stat.h> 20 #include <sys/types.h> 21 #include <sys/resource.h> 22 23 #include <err.h> 24 #include <errno.h> 25 #include <fcntl.h> 26 #include <grp.h> 27 #include <limits.h> 28 #include <pwd.h> 29 #include <signal.h> 30 #include <sndio.h> 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <unistd.h> 35 36 #include "amsg.h" 37 #include "defs.h" 38 #include "dev.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 * priority when run as root 55 */ 56 #ifndef SNDIO_PRIO 57 #define SNDIO_PRIO (-20) 58 #endif 59 60 /* 61 * sample rate if no ``-r'' is used 62 */ 63 #ifndef DEFAULT_RATE 64 #define DEFAULT_RATE 48000 65 #endif 66 67 /* 68 * block size if neither ``-z'' nor ``-b'' is used 69 */ 70 #ifndef DEFAULT_ROUND 71 #define DEFAULT_ROUND 960 72 #endif 73 74 /* 75 * buffer size if neither ``-z'' nor ``-b'' is used 76 */ 77 #ifndef DEFAULT_BUFSZ 78 #define DEFAULT_BUFSZ 7860 79 #endif 80 81 /* 82 * default device in server mode 83 */ 84 #ifndef DEFAULT_DEV 85 #define DEFAULT_DEV "rsnd/0" 86 #endif 87 88 void sigint(int); 89 void opt_ch(int *, int *); 90 void opt_enc(struct aparams *); 91 int opt_mmc(void); 92 int opt_onoff(void); 93 int getword(char *, char **); 94 unsigned int opt_mode(void); 95 void getbasepath(char *, size_t); 96 void setsig(void); 97 void unsetsig(void); 98 void privdrop(void); 99 struct dev *mkdev(char *, struct aparams *, 100 int, int, int, int, int, int); 101 struct opt *mkopt(char *, struct dev *, 102 int, int, int, int, int, int, int, int); 103 104 unsigned int log_level = 0; 105 volatile sig_atomic_t quit_flag = 0; 106 107 char usagestr[] = "usage: sndiod [-d] [-a flag] [-b nframes] " 108 "[-C min:max] [-c min:max] [-e enc]\n\t" 109 "[-f device] [-j flag] [-L addr] [-m mode] [-q port] [-r rate]\n\t" 110 "[-s name] [-t mode] [-U unit] [-v volume] [-w flag] [-z nframes]\n"; 111 112 /* 113 * SIGINT handler, it raises the quit flag. If the flag is already set, 114 * that means that the last SIGINT was not handled, because the process 115 * is blocked somewhere, so exit. 116 */ 117 void 118 sigint(int s) 119 { 120 if (quit_flag) 121 _exit(1); 122 quit_flag = 1; 123 } 124 125 void 126 opt_ch(int *rcmin, int *rcmax) 127 { 128 char *next, *end; 129 long cmin, cmax; 130 131 errno = 0; 132 cmin = strtol(optarg, &next, 10); 133 if (next == optarg || *next != ':') 134 goto failed; 135 cmax = strtol(++next, &end, 10); 136 if (end == next || *end != '\0') 137 goto failed; 138 if (cmin < 0 || cmax < cmin || cmax >= NCHAN_MAX) 139 goto failed; 140 *rcmin = cmin; 141 *rcmax = cmax; 142 return; 143 failed: 144 errx(1, "%s: bad channel range", optarg); 145 } 146 147 void 148 opt_enc(struct aparams *par) 149 { 150 int len; 151 152 len = aparams_strtoenc(par, optarg); 153 if (len == 0 || optarg[len] != '\0') 154 errx(1, "%s: bad encoding", optarg); 155 } 156 157 int 158 opt_mmc(void) 159 { 160 if (strcmp("off", optarg) == 0) 161 return 0; 162 if (strcmp("slave", optarg) == 0) 163 return 1; 164 errx(1, "%s: off/slave expected", optarg); 165 } 166 167 int 168 opt_onoff(void) 169 { 170 if (strcmp("off", optarg) == 0) 171 return 0; 172 if (strcmp("on", optarg) == 0) 173 return 1; 174 errx(1, "%s: on/off expected", optarg); 175 } 176 177 int 178 getword(char *word, char **str) 179 { 180 char *p = *str; 181 182 for (;;) { 183 if (*word == '\0') 184 break; 185 if (*word++ != *p++) 186 return 0; 187 } 188 if (*p == ',' || *p == '\0') { 189 *str = p; 190 return 1; 191 } 192 return 0; 193 } 194 195 unsigned int 196 opt_mode(void) 197 { 198 unsigned int mode = 0; 199 char *p = optarg; 200 201 for (;;) { 202 if (getword("play", &p)) { 203 mode |= MODE_PLAY; 204 } else if (getword("rec", &p)) { 205 mode |= MODE_REC; 206 } else if (getword("mon", &p)) { 207 mode |= MODE_MON; 208 } else if (getword("midi", &p)) { 209 mode |= MODE_MIDIMASK; 210 } else 211 errx(1, "%s: bad mode", optarg); 212 if (*p == '\0') 213 break; 214 p++; 215 } 216 if (mode == 0) 217 errx(1, "empty mode"); 218 return mode; 219 } 220 221 void 222 setsig(void) 223 { 224 struct sigaction sa; 225 226 quit_flag = 0; 227 sigfillset(&sa.sa_mask); 228 sa.sa_flags = SA_RESTART; 229 sa.sa_handler = sigint; 230 if (sigaction(SIGINT, &sa, NULL) < 0) 231 err(1, "sigaction(int) failed"); 232 if (sigaction(SIGTERM, &sa, NULL) < 0) 233 err(1, "sigaction(term) failed"); 234 if (sigaction(SIGHUP, &sa, NULL) < 0) 235 err(1, "sigaction(hup) failed"); 236 } 237 238 void 239 unsetsig(void) 240 { 241 struct sigaction sa; 242 243 sigfillset(&sa.sa_mask); 244 sa.sa_flags = SA_RESTART; 245 sa.sa_handler = SIG_DFL; 246 if (sigaction(SIGHUP, &sa, NULL) < 0) 247 err(1, "unsetsig(hup): sigaction failed\n"); 248 if (sigaction(SIGTERM, &sa, NULL) < 0) 249 err(1, "unsetsig(term): sigaction failed\n"); 250 if (sigaction(SIGINT, &sa, NULL) < 0) 251 err(1, "unsetsig(int): sigaction failed\n"); 252 } 253 254 void 255 getbasepath(char *base, size_t size) 256 { 257 uid_t uid; 258 struct stat sb; 259 mode_t mask; 260 261 uid = geteuid(); 262 if (uid == 0) { 263 mask = 022; 264 snprintf(base, PATH_MAX, "/tmp/aucat"); 265 } else { 266 mask = 077; 267 snprintf(base, PATH_MAX, "/tmp/aucat-%u", uid); 268 } 269 if (mkdir(base, 0777 & ~mask) < 0) { 270 if (errno != EEXIST) 271 err(1, "mkdir(\"%s\")", base); 272 } 273 if (stat(base, &sb) < 0) 274 err(1, "stat(\"%s\")", base); 275 if (sb.st_uid != uid || (sb.st_mode & mask) != 0) 276 errx(1, "%s has wrong permissions", base); 277 } 278 279 void 280 privdrop(void) 281 { 282 struct passwd *pw; 283 284 if ((pw = getpwnam(SNDIO_USER)) == NULL) 285 errx(1, "unknown user %s", SNDIO_USER); 286 if (setpriority(PRIO_PROCESS, 0, SNDIO_PRIO) < 0) 287 err(1, "setpriority"); 288 if (setgroups(1, &pw->pw_gid) || 289 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 290 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 291 err(1, "cannot drop privileges"); 292 } 293 294 struct dev * 295 mkdev(char *path, struct aparams *par, 296 int mode, int bufsz, int round, int rate, int hold, int autovol) 297 { 298 struct dev *d; 299 300 for (d = dev_list; d != NULL; d = d->next) { 301 if (strcmp(d->path, path) == 0) 302 return d; 303 } 304 if (!bufsz && !round) { 305 round = DEFAULT_ROUND; 306 bufsz = DEFAULT_BUFSZ; 307 } else if (!bufsz) { 308 bufsz = round * 2; 309 } else if (!round) 310 round = bufsz / 2; 311 d = dev_new(path, par, mode, bufsz, round, rate, hold, autovol); 312 if (d == NULL) 313 exit(1); 314 return d; 315 } 316 317 struct opt * 318 mkopt(char *path, struct dev *d, 319 int pmin, int pmax, int rmin, int rmax, 320 int mode, int vol, int mmc, int dup) 321 { 322 struct opt *o; 323 324 o = opt_new(path, d, pmin, pmax, rmin, rmax, 325 MIDI_TO_ADATA(vol), mmc, dup, mode); 326 if (o == NULL) 327 errx(1, "%s: couldn't create subdev", path); 328 dev_adjpar(d, o->mode, o->pmin, o->pmax, o->rmin, o->rmax); 329 return o; 330 } 331 332 int 333 main(int argc, char **argv) 334 { 335 int c, background, unit; 336 int pmin, pmax, rmin, rmax; 337 char base[PATH_MAX], path[PATH_MAX]; 338 unsigned int mode, dup, mmc, vol; 339 unsigned int hold, autovol, bufsz, round, rate; 340 const char *str; 341 struct aparams par; 342 struct dev *d; 343 struct port *p; 344 struct listen *l; 345 346 atexit(log_flush); 347 348 /* 349 * global options defaults 350 */ 351 vol = 118; 352 dup = 1; 353 mmc = 0; 354 hold = 0; 355 autovol = 1; 356 bufsz = 0; 357 round = 0; 358 rate = DEFAULT_RATE; 359 unit = 0; 360 background = 1; 361 pmin = 0; 362 pmax = 1; 363 rmin = 0; 364 rmax = 1; 365 aparams_init(&par); 366 mode = MODE_PLAY | MODE_REC; 367 368 setsig(); 369 filelist_init(); 370 371 while ((c = getopt(argc, argv, "a:b:c:C:de:f:j:L:m:Mq:r:s:t:U:v:w:x:z:")) != -1) { 372 switch (c) { 373 case 'd': 374 log_level++; 375 background = 0; 376 break; 377 case 'U': 378 if (listen_list) 379 errx(1, "-U must come before -L"); 380 unit = strtonum(optarg, 0, 15, &str); 381 if (str) 382 errx(1, "%s: unit number is %s", optarg, str); 383 break; 384 case 'L': 385 listen_new_tcp(optarg, AUCAT_PORT + unit); 386 break; 387 case 'm': 388 mode = opt_mode(); 389 break; 390 case 'j': 391 dup = opt_onoff(); 392 break; 393 case 't': 394 mmc = opt_mmc(); 395 break; 396 case 'c': 397 opt_ch(&pmin, &pmax); 398 break; 399 case 'C': 400 opt_ch(&rmin, &rmax); 401 break; 402 case 'e': 403 opt_enc(&par); 404 break; 405 case 'r': 406 rate = strtonum(optarg, RATE_MIN, RATE_MAX, &str); 407 if (str) 408 errx(1, "%s: rate is %s", optarg, str); 409 break; 410 case 'v': 411 vol = strtonum(optarg, 0, MIDI_MAXCTL, &str); 412 if (str) 413 errx(1, "%s: volume is %s", optarg, str); 414 break; 415 case 's': 416 if ((d = dev_list) == NULL) { 417 d = mkdev(DEFAULT_DEV, &par, 0, bufsz, round, rate, 418 hold, autovol); 419 } 420 mkopt(optarg, d, pmin, pmax, rmin, rmax, 421 mode, vol, mmc, dup); 422 break; 423 case 'q': 424 p = port_new(optarg, MODE_MIDIMASK, hold); 425 if (!p) 426 errx(1, "%s: can't open port", optarg); 427 break; 428 case 'a': 429 hold = opt_onoff(); 430 break; 431 case 'w': 432 autovol = opt_onoff(); 433 break; 434 case 'b': 435 bufsz = strtonum(optarg, 1, RATE_MAX, &str); 436 if (str) 437 errx(1, "%s: buffer size is %s", optarg, str); 438 break; 439 case 'z': 440 round = strtonum(optarg, 1, SHRT_MAX, &str); 441 if (str) 442 errx(1, "%s: block size is %s", optarg, str); 443 break; 444 case 'f': 445 mkdev(optarg, &par, 0, bufsz, round, rate, hold, autovol); 446 break; 447 case 'M': 448 /* XXX: for compatibility with aucat, remove this */ 449 break; 450 default: 451 fputs(usagestr, stderr); 452 return 1; 453 } 454 } 455 argc -= optind; 456 argv += optind; 457 if (argc > 0) { 458 fputs(usagestr, stderr); 459 return 1; 460 } 461 if (dev_list == NULL) 462 mkdev(DEFAULT_DEV, &par, 0, bufsz, round, rate, hold, autovol); 463 for (d = dev_list; d != NULL; d = d->next) { 464 if (opt_byname("default", d->num)) 465 continue; 466 mkopt("default", d, pmin, pmax, rmin, rmax, 467 mode, vol, mmc, dup); 468 } 469 getbasepath(base, sizeof(base)); 470 snprintf(path, PATH_MAX, "%s/%s%u", base, AUCAT_PATH, unit); 471 listen_new_un(path); 472 if (geteuid() == 0) 473 privdrop(); 474 midi_init(); 475 for (p = port_list; p != NULL; p = p->next) { 476 if (!port_init(p)) 477 return 1; 478 } 479 for (d = dev_list; d != NULL; d = d->next) { 480 if (!dev_init(d)) 481 return 1; 482 } 483 for (l = listen_list; l != NULL; l = l->next) { 484 if (!listen_init(l)) 485 return 1; 486 } 487 if (background) { 488 log_flush(); 489 log_level = 0; 490 if (daemon(0, 0) < 0) 491 err(1, "daemon"); 492 } 493 494 /* 495 * Loop, start audio. 496 */ 497 for (;;) { 498 if (quit_flag) 499 break; 500 if (!file_poll()) 501 break; 502 } 503 while (listen_list != NULL) 504 listen_close(listen_list); 505 while (sock_list != NULL) 506 sock_close(sock_list); 507 while (opt_list != NULL) 508 opt_del(opt_list); 509 for (d = dev_list; d != NULL; d = d->next) 510 dev_done(d); 511 for (p = port_list; p != NULL; p = p->next) 512 port_done(p); 513 midi_done(); 514 while (file_poll()) 515 ; /* nothing */ 516 while (dev_list) 517 dev_del(dev_list); 518 while (port_list) 519 port_del(port_list); 520 filelist_done(); 521 rmdir(base); 522 unsetsig(); 523 return 0; 524 } 525