[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.1

1.1     ! ratchov     1: /*     $OpenBSD$       */
        !             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:
        !           339:        while ((c = getopt(argc, argv, "a:b:c:C:de:f:j:L:m:q:r:s:t:U:v:w:x:z:")) != -1) {
        !           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);
        !           414:                        break;
        !           415:                default:
        !           416:                        fputs(usagestr, stderr);
        !           417:                        return 1;
        !           418:                }
        !           419:        }
        !           420:        argc -= optind;
        !           421:        argv += optind;
        !           422:        if (argc > 0) {
        !           423:                fputs(usagestr, stderr);
        !           424:                return 1;
        !           425:        }
        !           426:        if (dev_list == NULL)
        !           427:                mkdev(DEFAULT_DEV, &par, 0, bufsz, round, rate, hold, autovol);
        !           428:        for (d = dev_list; d != NULL; d = d->next) {
        !           429:                if (opt_byname("default", d->num))
        !           430:                        continue;
        !           431:                mkopt("default", d, pmin, pmax, rmin, rmax,
        !           432:                    mode, vol, mmc, dup);
        !           433:        }
        !           434:        getbasepath(base, sizeof(base));
        !           435:        snprintf(path, PATH_MAX, "%s/%s%u", base, AUCAT_PATH, unit);
        !           436:        listen_new_un(path);
        !           437:        if (geteuid() == 0)
        !           438:                privdrop();
        !           439:        midi_init();
        !           440:        for (p = port_list; p != NULL; p = p->next) {
        !           441:                if (!port_init(p))
        !           442:                        return 1;
        !           443:        }
        !           444:        for (d = dev_list; d != NULL; d = d->next) {
        !           445:                if (!dev_init(d))
        !           446:                        return 1;
        !           447:                if (d->autostart && (d->mode & MODE_AUDIOMASK))
        !           448:                        dev_mmcstart(d);
        !           449:        }
        !           450:        for (l = listen_list; l != NULL; l = l->next) {
        !           451:                if (!listen_init(l))
        !           452:                        return 1;
        !           453:        }
        !           454:        if (background) {
        !           455:                log_flush();
        !           456:                log_level = 0;
        !           457:                if (daemon(0, 0) < 0)
        !           458:                        err(1, "daemon");
        !           459:        }
        !           460:
        !           461:        /*
        !           462:         * Loop, start audio.
        !           463:         */
        !           464:        for (;;) {
        !           465:                if (quit_flag)
        !           466:                        break;
        !           467:                if (!file_poll())
        !           468:                        break;
        !           469:        }
        !           470:        while (listen_list != NULL)
        !           471:                listen_close(listen_list);
        !           472:        while (sock_list != NULL)
        !           473:                sock_close(sock_list);
        !           474:        while (opt_list != NULL)
        !           475:                opt_del(opt_list);
        !           476:        for (d = dev_list; d != NULL; d = d->next)
        !           477:                dev_done(d);
        !           478:        for (p = port_list; p != NULL; p = p->next)
        !           479:                port_done(p);
        !           480:        midi_done();
        !           481:        while (file_poll())
        !           482:                ; /* nothing */
        !           483:        while (dev_list)
        !           484:                dev_del(dev_list);
        !           485:        while (port_list)
        !           486:                port_del(port_list);
        !           487:        filelist_done();
        !           488:        rmdir(base);
        !           489:        unsetsig();
        !           490:        return 0;
        !           491: }