chiark / gitweb /
add with-lock-ex to new chiark-utils-bin package
[chiark-utils.git] / cprogs / trivsoundd.c
1 /*
2  * triv-sound-d.c
3  * writebuffer adapted for sound-playing
4  *
5  * readbuffer and writebuffer are:
6  *  Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
7  *
8  * readbuffer is part of chiark backup, a system for backing up GNU/Linux and
9  * other UN*X-compatible machines, as used on chiark.greenend.org.uk.
10  * chiark backup is:
11  *  Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
12  *  Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
13  *
14  * This is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as
16  * published by the Free Software Foundation; either version 2,
17  * or (at your option) any later version.
18  *
19  * This is distributed in the hope that it will be useful, but
20  * WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public
25  * License along with this file; if not, write to the Free Software
26  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27  *
28  */
29
30 #include "rwbuffer.h"
31
32 const char *progname= "trivsoundd";
33
34 static int maxstartdelay=60, maxbadaccept=10;
35
36 struct inqnode {
37   struct inqnode *next, *back;
38   time_t accepted;
39   int fd;
40 };
41
42 static struct { struct inqnode *head, *tail; } inq;
43 static int master, sdev;
44 static time_t now;
45
46 static void usageerr(const char *m) {
47   fprintf(stderr,"bad usage: %s\n",m);
48   exit(12);
49 }
50
51 static void bindmaster(const char *bindname) {
52   union {
53     struct sockaddr sa;
54     struct sockaddr_in sin;
55     struct sockaddr_un sun;
56   } su;
57   socklen_t sulen;
58   const char *colon;
59   char *copy, *ep;
60   int r;
61   unsigned long portul;
62   struct hostent *he;
63   struct servent *se;
64
65   memset(&su,0,sizeof(su));
66
67   if (bindname[0]=='/' || bindname[0]=='.') {
68
69     if (strlen(bindname) >= sizeof(su.sun.sun_path))
70       usageerr("AF_UNIX bind path too long");
71     sulen= sizeof(su.sun);
72     su.sun.sun_family= AF_UNIX;
73     strcpy(su.sun.sun_path, bindname);
74
75   } else if (bindname[0] != ':' && (colon= strrchr(bindname,':'))) {
76
77     sulen= sizeof(su.sin);
78     su.sin.sin_family= AF_INET;
79
80     copy= xmalloc(colon - bindname + 1);
81     memcpy(copy,bindname, colon - bindname + 1);
82     copy[colon - bindname]= 0;
83     portul= strtoul(colon+1,&ep,0);
84
85     if (!*ep) {
86       if (!portul || portul>=65536) usageerr("invalid port number");
87       su.sin.sin_port= htons(portul);
88     } else {
89       se= getservbyname(colon+1, "tcp");
90       if (!se) { fprintf(stderr,"unknown service `%s'\n",colon+1); exit(4); }
91       su.sin.sin_port= htons(se->s_port);
92     }
93
94     if (!strcmp(copy,"any")) {
95       su.sin.sin_addr.s_addr= INADDR_ANY;
96     } else if (!inet_aton(copy,&su.sin.sin_addr)) {
97       he= gethostbyname(copy);
98       if (!he) { herror(copy); exit(4); }
99       if (he->h_addrtype != AF_INET ||
100           he->h_length != sizeof(su.sin.sin_addr) ||
101           !he->h_addr_list[0] ||
102           he->h_addr_list[1]) {
103         fprintf(stderr,"hostname lookup `%s' did not yield"
104                 " exactly one IPv4 address\n",copy);
105         exit(4);
106       }
107       memcpy(&su.sin.sin_addr, he->h_addr_list[0], sizeof(su.sin.sin_addr));
108     }
109
110   } else {
111     usageerr("unknown bind name");
112     exit(12);
113   }
114
115   master= socket(su.sa.sa_family,SOCK_STREAM,0);
116   if (master<0) { perror("socket"); exit(8); }
117
118   r= bind(master, &su.sa, sulen);
119   if (r) { perror("bind"); exit(8); }
120
121   r= listen(master, 5);
122   if (r) { perror("listen"); exit(8); }
123 }
124
125 static void opensounddevice(void) {
126   int r;
127   char cbuf[200];
128   
129   sdev= open("/dev/dsp", O_WRONLY);
130   if (sdev<0) { perror("open sound device"); exit(8); }
131
132   snprintf(cbuf, sizeof(cbuf), "sox -t raw -s -w -r 44100 -c 2"
133            " - </dev/null -t ossdsp - >&%d", sdev);
134   r= system(cbuf);  if (r) { fprintf(stderr,"sox gave %d\n",r); exit(5); }
135 }
136
137 void wrbuf_report(const char *m) {
138   printf("writing %s\n", m);
139 }
140
141 static void selectcopy(void) {
142   int slave= inq.head ? inq.head->fd : -1;
143   wrbufcore_prepselect(slave, sdev);
144   fdsetset(master,&readfds);
145   callselect();
146   wrbufcore_afterselect(slave, sdev);
147 }
148
149 static void expireoldconns(void) {
150   struct inqnode *searchold, *nextsearchold;
151       
152   for (searchold= inq.head ? inq.head->next : 0;
153        searchold;
154        searchold= nextsearchold) {
155     nextsearchold= searchold->next;
156     if (searchold->accepted < now-maxstartdelay) {
157       printf("expired %p\n",searchold);
158       LIST_UNLINK(inq,searchold);
159       free(searchold);
160     }
161   }
162 }
163
164 static void acceptnewconns(void) {
165   static int bad;
166   
167   int slave;
168   struct inqnode *new;
169
170   if (!FD_ISSET(master,&readfds)) return;
171
172   slave= accept(master,0,0);
173   if (slave < 0) {
174     if (!(errno == EINTR ||
175           errno == EAGAIN ||
176           errno == EWOULDBLOCK)) {
177       perror("accept");
178       bad++;
179       if (bad > maxbadaccept) {
180         fprintf(stderr,"accept failures repeating\n");
181         exit(4);
182       }
183     }
184     /* any transient error will just send us round again via select */
185     return;
186   }
187
188   bad= 0;
189   new= xmalloc(sizeof(struct inqnode));
190   new->accepted= now;
191   new->fd= slave;
192   LIST_LINK_TAIL(inq,new);
193
194   printf("accepted %p\n",new);
195 }
196
197 static void switchinput(void) {
198   struct inqnode *old;
199   if (!seeneof) return;
200   old= inq.head;
201   assert(old);
202   printf("finished %p\n",old);
203   close(old->fd);
204   LIST_UNLINK(inq,old);
205   free(old);
206   seeneof= 0;
207 }  
208
209 int main(int argc, const char *const *argv) {
210   assert(argv[0]);
211   if (!argv[1] || argv[2] || argv[1][0]=='-')
212     usageerr("no options allowed, must have one argument (bindname)");
213
214   buffersize= 44100*4* 5/*seconds*/;
215
216   opensounddevice();
217   bindmaster(argv[1]);
218   nonblock(sdev,1);
219   nonblock(master,1);
220
221   startupcore();
222   wrbufcore_startup();
223   
224   printf("started\n");
225   for (;;) {
226     selectcopy();
227     if (time(&now)==(time_t)-1) { perror("time(2)"); exit(4); }
228     expireoldconns();
229     acceptnewconns();
230     switchinput();
231   }
232 }