[BACK]Return to sndiod.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / sndiod

Annotation of src/usr.bin/sndiod/sndiod.c, Revision 1.2

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