chiark / gitweb /
working on trivsoundd
[chiark-utils.git] / cprogs / trivsoundd.c
index e3d1acf06a9769d1703ff946fbb511315ec499a0..5eda4f7aebd098427c30a439fa8348d60ee9b23a 100644 (file)
  *
  */
 
-#include <sys/time.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <assert.h>
-#include <ctype.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <sys/mman.h>
-
 #include "rwbuffer.h"
 
-const char *progname= "triv-sound-d";
+const char *progname= "trivsoundd";
 
-static int master;
+static int maxstartdelay=60, maxbadaccept=10;
 
-int main(int argc, const char *const *argv) {
-  const char *bindname;
-  union {
-    struct sockaddr sa;
-    struct sockaddr_in sin;
-    struct sockaddr_un sun;
-  } su;
-  socklen_t sulen;
-
-  bindname= *++argv;
-  if (!bindname) usageerr("need bind argument");
-  startup(argv);
-
-  memset(&su,0,sizeof(su));
-
-  if (bindname[0]=='/' || bindname[0]='.') {
-    if (strlen(bindname) >= sizeof(su.sun.sun_path))
-      usageerr("AF_UNIX bind path too long");
-    sulen= sizeof(su.sun);
-    su.sun.sun_family= AF_UNIX;
-    strcpy(su.sun.sun_path, bindname);
-  } else if ((colon= strrchr(bindname,':'))) {
-    char *copy, *ep;
-    unsigned long portul;
-
-    sulen= sizeof(su.sin);
-    su.sin.sin_family= AF_INET;
-
-    copy= xmalloc(colon - bindname + 1);
-    memcpy(copy,bindname, colon - bindname + 1);
-    copy[colon - bindname]= 0;
-    portul= strtoul(colon+1,0,&ep);
-    if (!*ep) {
-      if (!portul || portul>=65536) usageerr("invalid port number");
-      su.sin.sin_port= htons(portul);
-    } else {
-      struct servent *se= getservbyname(colon+1, "tcp");
-      if (!se) { fprintf(stderr,"unknown service `%s'\n",colon+1); exit(4); }
-      su.sin.sin_port= htons(se->s_port);
+struct inqnode {
+  struct inqnode *next, *back;
+  time_t accepted;
+  int fd;
+};
+
+static struct { struct inqnode *head, *tail; } inq;
+static int sdev;
+static time_t now;
+
+static void opensounddevice(void) {
+  int r;
+  char cbuf[200];
+  
+  sdev= open("/dev/dsp", O_WRONLY);
+  if (sdev<0) { perror("open sound device"); exit(8); }
+
+  snprintf(cbuf, sizeof(cbuf), "sox -t raw -s -w -r 44100 -c 2"
+          " - </dev/null -t ossdsp - >&%d", sdev);
+  r= system(cbuf);  if (r) { fprintf(stderr,"sox gave %d\n",r); exit(5); }
+}
+
+static void selectcopy(void) {
+  int slave= inq.head ? inq.head->fd : -1;
+  wrbufcore_prepselect(slave, sdev);
+  FD_SET(0,&readfds);
+  callselect();
+  wrbufcore_afterselect(slave, sdev);
+}
+
+static void expireoldconns(void) {
+  struct inqnode *searchold, *nextsearchold;
+      
+  for (searchold= inq.head ? inq.head->next : 0;
+       searchold;
+       searchold= nextsearchold) {
+    nextsearchold= searchold;
+    if (searchold->accepted < now-maxstartdelay) {
+      printf("expired %p\n",searchold);
+      LIST_UNLINK(inq,searchold);
+      free(searchold);
     }
+  }
+}
 
-    if (!inet_aton(copy,&su.sin.sin_addr)) {
-      struct hostent *he= gethostbyname(copy);
-      if (!he) { herror(copy); exit(4); }
-      if (he->h_addrtype != AF_INET ||
-         he->h_length != sizeof(su.sin.sin_addr) ||
-         !he->h_addrs[0] ||
-         he->h_addrs[1]) {
-       fprintf(stderr,"hostname lookup `%s' did not yield"
-               " exactly one IPv4 address\n",copy);
+static void acceptnewconns(void) {
+  static int bad;
+  
+  int slave;
+  struct inqnode *new;
+
+  if (!FD_ISSET(0,&readfds)) return;
+
+  slave= accept(0,0,0);
+  if (slave < 0) {
+    if (!(errno == EINTR ||
+         errno == EAGAIN ||
+         errno == EWOULDBLOCK)) {
+      perror("accept");
+      bad++;
+      if (bad > maxbadaccept) {
+       fprintf(stderr,"accept failures repeating\n");
        exit(4);
       }
-      memcpy(su.sin.sin_addr, he->h_addrs[0], sizeof(su.sin.sin_addr));
     }
+    /* any transient error will just send us round again via select */
+    return;
   }
 
-  master= socket(su.sa.sa_family,SOCK_STREAM,0);
-  if (master<0) { perror("socket"); exit(8); }
+  bad= 0;
+  new= xmalloc(sizeof(struct inqnode));
+  new->accepted= now;
+  new->fd= slave;
+  LIST_LINK_TAIL(inq,new);
 
-  
-  
-    if (
+  printf("accepted %p\n",new);
+}
 
-      sdigit((unsigned char)bindname[0])) {
-    
+static void switchinput(void) {
+  struct inqnode *old;
+  if (!seeneof) return;
+  old= inq.head;
+  assert(old);
+  printf("finished %p\n",old);
+  close(old->fd);
+  LIST_UNLINK(inq,old);
+  free(old);
+  seeneof= 0;
+}  
 
-  if (argv[0]
-    
-  startup(argv);
-  writebuf_startup();
-  writebuf_mainloop();
-  exit(0);
+int main(int argc, const char *const *argv) {
+  assert(argv[0]);
+  if (argv[1]) { fputs("no arguments allowed\n",stderr); exit(12); }
+
+  buffersize= 44100*4* 5/*seconds*/;
+
+  opensounddevice();
+  nonblock(sdev,1);
+  nonblock(0,1);
+
+  startupcore();
+  wrbufcore_startup();
+  
+  printf("started\n");
+  for (;;) {
+    selectcopy();
+    if (time(&now)==(time_t)-1) { perror("time(2)"); exit(4); }
+    expireoldconns();
+    acceptnewconns();
+    switchinput();
+  }
 }