sad

simple audio daemon
git clone git://git.2f30.org/sad
Log | Files | Refs | LICENSE

sad.c (3296B)


      1 #include <sys/select.h>
      2 #include <sys/socket.h>
      3 #include <sys/types.h>
      4 #include <sys/un.h>
      5 
      6 #include <err.h>
      7 #include <errno.h>
      8 #include <fcntl.h>
      9 #include <limits.h>
     10 #include <signal.h>
     11 #include <stdio.h>
     12 #include <stdlib.h>
     13 #include <string.h>
     14 #include <unistd.h>
     15 
     16 #include "arg.h"
     17 #include "sad.h"
     18 
     19 fd_set master;
     20 fd_set rfds;
     21 int fdmax;
     22 char *argv0;
     23 
     24 static int listenfd = -1;
     25 static volatile sig_atomic_t isrunning = 1;
     26 static char *socketpath = "/tmp/sad-sock";
     27 
     28 static int servlisten(const char *name) {
     29   struct sockaddr_un sun;
     30   int fd, r;
     31   socklen_t len;
     32 
     33   fd = socket(AF_UNIX, SOCK_STREAM, 0);
     34   if (fd < 0)
     35     err(1, "socket");
     36 
     37   unlink(name);
     38 
     39   memset(&sun, 0, sizeof(sun));
     40   sun.sun_family = AF_UNIX;
     41   strlcpy(sun.sun_path, name, sizeof(sun.sun_path));
     42 
     43   len = sizeof(sun);
     44   r = bind(fd, (struct sockaddr *)&sun, len);
     45   if (r < 0)
     46     err(1, "bind");
     47 
     48   r = listen(fd, 5);
     49   if (r < 0)
     50     err(1, "listen");
     51 
     52   return fd;
     53 }
     54 
     55 static int servaccept(int listenfd) {
     56   struct sockaddr_un sun;
     57   int clifd;
     58   socklen_t len;
     59 
     60   len = sizeof(sun);
     61   clifd = accept(listenfd, (struct sockaddr *)&sun, &len);
     62   if (clifd < 0)
     63     err(1, "accept");
     64   return clifd;
     65 }
     66 
     67 static void playaudio(void) {
     68   Song *s;
     69   unsigned char buf[4096];
     70   int nbytes;
     71 
     72   s = getcursong();
     73   if (!s)
     74     return;
     75 
     76   switch (s->state) {
     77   case PREPARE:
     78     if (s->decoder->open(&s->fmt, s->path) < 0) {
     79       s->state = NONE;
     80       return;
     81     }
     82     s->state = PLAYING;
     83     break;
     84   case PLAYING:
     85     if ((nbytes = s->decoder->decode(buf, sizeof(buf))) <= 0) {
     86       playsong(picknextsong());
     87       notify(EVSONGFINISHED);
     88     } else {
     89       playoutputs(&s->fmt, buf, nbytes);
     90     }
     91     break;
     92   }
     93 }
     94 
     95 static void sighandler(int signum) {
     96   if (signum == SIGINT || signum == SIGTERM)
     97     isrunning = 0;
     98 }
     99 
    100 static void usage(void) {
    101   fprintf(stderr, "usage: %s [-f sock]\n", argv0);
    102   exit(1);
    103 }
    104 
    105 int main(int argc, char *argv[]) {
    106   struct sigaction sa;
    107   struct timeval tv;
    108   int clifd, n, i;
    109 
    110   ARGBEGIN {
    111   case 'f':
    112     socketpath = EARGF(usage());
    113     break;
    114   default:
    115     usage();
    116   }
    117   ARGEND;
    118 
    119   memset(&sa, 0, sizeof(sa));
    120   sa.sa_flags = SA_RESTART;
    121   sa.sa_handler = sighandler;
    122   sigaction(SIGTERM, &sa, NULL);
    123   sigaction(SIGINT, &sa, NULL);
    124 
    125   FD_ZERO(&master);
    126   FD_ZERO(&rfds);
    127 
    128   listenfd = servlisten(socketpath);
    129   FD_SET(listenfd, &master);
    130   fdmax = listenfd;
    131 
    132   initoutputs();
    133   openoutputs();
    134   initnotifier();
    135 
    136   playlistmode(REPEAT);
    137 
    138   while (isrunning) {
    139     rfds = master;
    140     tv.tv_sec = 0;
    141     tv.tv_usec = 10000;
    142     n = select(fdmax + 1, &rfds, NULL, NULL, &tv);
    143     if (n < 0) {
    144       if (errno == EINTR)
    145         goto fini;
    146       err(1, "select");
    147     }
    148 
    149     playaudio();
    150 
    151     for (i = 0; i <= fdmax; i++) {
    152       if (!FD_ISSET(i, &rfds))
    153         continue;
    154       if (i == listenfd) {
    155         clifd = servaccept(listenfd);
    156         FD_SET(clifd, &master);
    157         if (clifd > fdmax)
    158           fdmax = clifd;
    159       } else {
    160         if (docmd(i) < 0) {
    161           close(i);
    162           FD_CLR(i, &master);
    163           removesubscriber(i);
    164         }
    165       }
    166     }
    167   }
    168 fini:
    169   for (i = 0; i <= fdmax; i++) {
    170     if (FD_ISSET(i, &master)) {
    171       close(i);
    172       FD_CLR(i, &master);
    173       removesubscriber(i);
    174     }
    175   }
    176   if (listenfd != -1)
    177     unlink(socketpath);
    178   return 0;
    179 }