*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2,
+ * published by the Free Software Foundation; either version 3,
* or (at your option) any later version.
*
* This is distributed in the hope that it will be useful, but
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
- * License along with this file; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * License along with this file; if not, consult the Free Software
+ * Foundation's website at www.fsf.org, or the GNU Project website at
+ * www.gnu.org.
*
*/
-#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;
+struct inqnode {
+ struct inqnode *next, *back;
+ time_t accepted;
+ int fd;
+};
+
+static struct { struct inqnode *head, *tail; } inq;
+static int master, sdev;
+static time_t now;
+
+static void usageerr(const char *m) {
+ fprintf(stderr,"bad usage: %s\n",m);
+ exit(12);
+}
+
+static void bindmaster(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);
+ const char *colon;
+ char *copy, *ep;
+ int r;
+ unsigned long portul;
+ struct hostent *he;
+ struct servent *se;
memset(&su,0,sizeof(su));
- if (bindname[0]=='/' || bindname[0]='.') {
+ 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;
+
+ } else if (bindname[0] != ':' && (colon= strrchr(bindname,':'))) {
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);
+ portul= strtoul(colon+1,&ep,0);
+
if (!*ep) {
if (!portul || portul>=65536) usageerr("invalid port number");
su.sin.sin_port= htons(portul);
} else {
- struct servent *se= getservbyname(colon+1, "tcp");
+ 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);
}
- if (!inet_aton(copy,&su.sin.sin_addr)) {
- struct hostent *he= gethostbyname(copy);
+ if (!strcmp(copy,"any")) {
+ su.sin.sin_addr.s_addr= INADDR_ANY;
+ } else if (!inet_aton(copy,&su.sin.sin_addr)) {
+ 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]) {
+ !he->h_addr_list[0] ||
+ he->h_addr_list[1]) {
fprintf(stderr,"hostname lookup `%s' did not yield"
" exactly one IPv4 address\n",copy);
exit(4);
}
- memcpy(su.sin.sin_addr, he->h_addrs[0], sizeof(su.sin.sin_addr));
+ memcpy(&su.sin.sin_addr, he->h_addr_list[0], sizeof(su.sin.sin_addr));
}
+
+ } else {
+ usageerr("unknown bind name");
+ exit(12);
}
master= socket(su.sa.sa_family,SOCK_STREAM,0);
if (master<0) { perror("socket"); exit(8); }
+ r= bind(master, &su.sa, sulen);
+ if (r) { perror("bind"); exit(8); }
+
+ r= listen(master, 5);
+ if (r) { perror("listen"); exit(8); }
+}
+
+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); }
+}
+
+void wrbuf_report(const char *m) {
+ printf("writing %s\n", m);
+}
+
+static void selectcopy(void) {
+ int slave= inq.head ? inq.head->fd : -1;
+ wrbufcore_prepselect(slave, sdev);
+ fdsetset(master,&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->next;
+ if (searchold->accepted < now-maxstartdelay) {
+ printf("expired %p\n",searchold);
+ LIST_UNLINK(inq,searchold);
+ free(searchold);
+ }
+ }
+}
+
+static void acceptnewconns(void) {
+ static int bad;
- if (
+ int slave;
+ struct inqnode *new;
+
+ if (!FD_ISSET(master,&readfds)) return;
+
+ slave= accept(master,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);
+ }
+ }
+ /* any transient error will just send us round again via select */
+ return;
+ }
+
+ bad= 0;
+ new= xmalloc(sizeof(struct inqnode));
+ new->accepted= now;
+ new->fd= slave;
+ LIST_LINK_TAIL(inq,new);
+
+ printf("accepted %p\n",new);
+}
+
+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;
+}
- sdigit((unsigned char)bindname[0])) {
-
+int main(int argc, const char *const *argv) {
+ assert(argv[0]);
+ if (!argv[1] || argv[2] || argv[1][0]=='-')
+ usageerr("no options allowed, must have one argument (bindname)");
+
+ buffersize= 44100*4* 5/*seconds*/;
+
+ opensounddevice();
+ bindmaster(argv[1]);
+ nonblock(sdev,1);
+ nonblock(master,1);
- if (argv[0]
-
- startup(argv);
- writebuf_startup();
- writebuf_mainloop();
- exit(0);
+ startupcore();
+ wrbufcore_startup();
+
+ printf("started\n");
+ for (;;) {
+ selectcopy();
+ if (time(&now)==(time_t)-1) { perror("time(2)"); exit(4); }
+ expireoldconns();
+ acceptnewconns();
+ switchinput();
+ }
}