1 | #include "esd-server.h" |
---|
2 | |
---|
3 | #ifdef HAVE_SYS_IOCTL_H |
---|
4 | # include <sys/ioctl.h> |
---|
5 | #endif |
---|
6 | #ifdef HAVE_SYS_FILIO_H |
---|
7 | # include <sys/filio.h> |
---|
8 | #endif |
---|
9 | |
---|
10 | #ifdef USE_LIBWRAP |
---|
11 | #include <tcpd.h> |
---|
12 | #include <syslog.h> |
---|
13 | |
---|
14 | int allow_severity = LOG_INFO; |
---|
15 | int deny_severity = LOG_WARNING; |
---|
16 | #endif |
---|
17 | |
---|
18 | /*******************************************************************/ |
---|
19 | /* globals */ |
---|
20 | |
---|
21 | /* the list of the currently connected clients */ |
---|
22 | esd_client_t *esd_clients_list; |
---|
23 | |
---|
24 | /*******************************************************************/ |
---|
25 | /* prototypes */ |
---|
26 | void dump_clients(void); |
---|
27 | void free_client( esd_client_t *client ); |
---|
28 | |
---|
29 | |
---|
30 | /*******************************************************************/ |
---|
31 | /* for debugging purposes, dump the list of the clients and data */ |
---|
32 | |
---|
33 | |
---|
34 | void dump_clients() |
---|
35 | { |
---|
36 | long addr; |
---|
37 | short port; |
---|
38 | esd_client_t *clients = esd_clients_list; |
---|
39 | |
---|
40 | if ( !esdbg_trace ) return; |
---|
41 | |
---|
42 | while ( clients != NULL ) { |
---|
43 | port = ntohs( clients->source.sin_port ); |
---|
44 | addr = ntohl( clients->source.sin_addr.s_addr ); |
---|
45 | |
---|
46 | printf( "(%02d) client from: %03u.%03u.%03u.%03u:%05d [%p]\n", |
---|
47 | clients->fd, (unsigned int) addr >> 24, |
---|
48 | (unsigned int) (addr >> 16) % 256, |
---|
49 | (unsigned int) (addr >> 8) % 256, |
---|
50 | (unsigned int) addr % 256, port, clients ); |
---|
51 | |
---|
52 | clients = clients->next; |
---|
53 | } |
---|
54 | return; |
---|
55 | } |
---|
56 | |
---|
57 | /*******************************************************************/ |
---|
58 | /* deallocate memory for the client */ |
---|
59 | void free_client( esd_client_t *client ) |
---|
60 | { |
---|
61 | /* free the client memory */ |
---|
62 | free( client ); |
---|
63 | return; |
---|
64 | } |
---|
65 | |
---|
66 | /*******************************************************************/ |
---|
67 | /* add a complete new client into the list of clients at head */ |
---|
68 | void add_new_client( esd_client_t *new_client ) |
---|
69 | { |
---|
70 | /* printf ( "adding client 0x%08x\n", new_client ); */ |
---|
71 | new_client->next = esd_clients_list; |
---|
72 | esd_clients_list = new_client; |
---|
73 | return; |
---|
74 | } |
---|
75 | |
---|
76 | /*******************************************************************/ |
---|
77 | /* erase a client from the client list */ |
---|
78 | void erase_client( esd_client_t *client ) |
---|
79 | { |
---|
80 | esd_client_t *previous = NULL; |
---|
81 | esd_client_t *current = esd_clients_list; |
---|
82 | |
---|
83 | /* iterate until we hit a NULL */ |
---|
84 | while ( current != NULL ) |
---|
85 | { |
---|
86 | /* see if we hit the target client */ |
---|
87 | if ( current == client ) { |
---|
88 | if( previous != NULL ){ |
---|
89 | /* we are deleting in the middle of the list */ |
---|
90 | previous->next = current->next; |
---|
91 | } else { |
---|
92 | /* we are deleting the head of the list */ |
---|
93 | esd_clients_list = current->next; |
---|
94 | } |
---|
95 | |
---|
96 | ESDBG_TRACE( printf ( "(%02d) closing client connection\n", |
---|
97 | client->fd ); ); |
---|
98 | |
---|
99 | close( client->fd ); |
---|
100 | free_client( client ); |
---|
101 | |
---|
102 | return; |
---|
103 | } |
---|
104 | |
---|
105 | /* iterate through the list */ |
---|
106 | previous = current; |
---|
107 | current = current->next; |
---|
108 | } |
---|
109 | |
---|
110 | /* hmm, we didn't find the desired client, just get on with life */ |
---|
111 | ESDBG_TRACE( printf( "(%02d) client not found\n", client->fd ); ); |
---|
112 | return; |
---|
113 | } |
---|
114 | |
---|
115 | |
---|
116 | /*******************************************************************/ |
---|
117 | /* checks for new connections at listener - zero when done */ |
---|
118 | int get_new_clients( int listen ) |
---|
119 | { |
---|
120 | int fd, nbl; |
---|
121 | struct sockaddr_in incoming; |
---|
122 | size_t size_in = sizeof(struct sockaddr_in); |
---|
123 | esd_client_t *new_client = NULL; |
---|
124 | |
---|
125 | unsigned long addr; |
---|
126 | short port; |
---|
127 | |
---|
128 | /* see who awakened us */ |
---|
129 | do { |
---|
130 | fd = accept( listen, (struct sockaddr*) &incoming, &size_in ); |
---|
131 | if ( fd > 0 ) { |
---|
132 | port = ntohs( incoming.sin_port ); |
---|
133 | addr = ntohl( incoming.sin_addr.s_addr ); |
---|
134 | |
---|
135 | ESDBG_TRACE( |
---|
136 | printf( "(%02d) new client from: %03u.%03u.%03u.%03u:%05d\n", |
---|
137 | fd, (unsigned int) addr >> 24, |
---|
138 | (unsigned int) (addr >> 16) % 256, |
---|
139 | (unsigned int) (addr >> 8) % 256, |
---|
140 | (unsigned int) addr % 256, port ); ); |
---|
141 | |
---|
142 | #ifdef USE_LIBWRAP |
---|
143 | { |
---|
144 | struct request_info req; |
---|
145 | struct servent *serv; |
---|
146 | |
---|
147 | request_init( &req, RQ_DAEMON, "esound", RQ_FILE, fd, NULL ); |
---|
148 | fromhost( &req ); |
---|
149 | |
---|
150 | if ( !hosts_access( &req )) { |
---|
151 | ESDBG_TRACE( |
---|
152 | printf( "connection from %s refused by tcp_wrappers\n", |
---|
153 | eval_client( &req ) ); ); |
---|
154 | |
---|
155 | close( fd ); |
---|
156 | continue; |
---|
157 | } |
---|
158 | } |
---|
159 | #endif |
---|
160 | |
---|
161 | ESDBG_COMMS( printf( "================================\n" ); ); |
---|
162 | |
---|
163 | /* make sure we have the memory to save the client... */ |
---|
164 | new_client = (esd_client_t*) malloc( sizeof(esd_client_t) ); |
---|
165 | if ( new_client == NULL ) { |
---|
166 | close( fd ); |
---|
167 | return -1; |
---|
168 | } |
---|
169 | |
---|
170 | /* It appears that not all systems construct the new socket in |
---|
171 | * a blocking mode, if the listening socket is non-blocking, so |
---|
172 | * let's set that here... |
---|
173 | */ |
---|
174 | nbl = 0; |
---|
175 | if ( ioctl( fd, FIONBIO, &nbl ) < 0 ) |
---|
176 | { |
---|
177 | ESDBG_TRACE( printf( "(%02d) couldn't turn on blocking for client\n", |
---|
178 | fd ); ); |
---|
179 | close( fd ); |
---|
180 | return -1; |
---|
181 | } |
---|
182 | |
---|
183 | /* Reduce buffers on sockets to the minimum needed */ |
---|
184 | esd_set_socket_buffers( fd, ESD_BITS16, 44100, esd_audio_rate ); |
---|
185 | |
---|
186 | /* fill in the new_client structure - sockaddr = works!? */ |
---|
187 | new_client->next = NULL; |
---|
188 | new_client->state = ESD_NEEDS_REQDATA; |
---|
189 | new_client->request = ESD_PROTO_CONNECT; |
---|
190 | new_client->fd = fd; |
---|
191 | new_client->source = incoming; |
---|
192 | new_client->proto_data_length = 0; |
---|
193 | |
---|
194 | add_new_client( new_client ); |
---|
195 | } |
---|
196 | } while ( fd > 0 ); |
---|
197 | |
---|
198 | return 0; |
---|
199 | } |
---|
200 | |
---|
201 | static int is_paused_here = 0; |
---|
202 | /*******************************************************************/ |
---|
203 | /* blocks waiting for data from the listener, and client conns. */ |
---|
204 | int wait_for_clients_and_data( int listen ) |
---|
205 | { |
---|
206 | fd_set rd_fds; |
---|
207 | struct timeval timeout; |
---|
208 | struct timeval *timeout_ptr = NULL; |
---|
209 | esd_client_t *client = esd_clients_list; |
---|
210 | int max_fd = listen, ready; |
---|
211 | |
---|
212 | /* add the listener to the file descriptor list */ |
---|
213 | FD_ZERO( &rd_fds ); |
---|
214 | FD_SET( listen, &rd_fds ); |
---|
215 | |
---|
216 | /* add the clients to the list, too */ |
---|
217 | while ( client != NULL ) |
---|
218 | { |
---|
219 | /* add this client, but only if it's not monitoring */ |
---|
220 | if ( client->state == ESD_STREAMING_DATA && |
---|
221 | client->request == ESD_PROTO_STREAM_MON ) |
---|
222 | { |
---|
223 | client = client->next; |
---|
224 | continue; |
---|
225 | } |
---|
226 | |
---|
227 | FD_SET( client->fd, &rd_fds ); |
---|
228 | |
---|
229 | /* update the maximum fd for the select() */ |
---|
230 | if ( client->fd > max_fd ) |
---|
231 | max_fd = client->fd; |
---|
232 | |
---|
233 | /* next client */ |
---|
234 | client = client->next; |
---|
235 | } |
---|
236 | |
---|
237 | /* if we're doing something useful, make sure we return immediately */ |
---|
238 | if ( esd_recorder_list || esd_playing_samples ) { |
---|
239 | timeout.tv_sec = 0; |
---|
240 | timeout.tv_usec = 0; |
---|
241 | timeout_ptr = &timeout; |
---|
242 | } else { |
---|
243 | |
---|
244 | /* TODO: any reason not to pause indefinitely here? */ |
---|
245 | /* sample players that's why, if no players, can pause indefinitely */ |
---|
246 | /* if ( esd_on_autostandby |
---|
247 | || (esd_autostandby_secs < 0 && !esd_playing_samples ) ) |
---|
248 | timeout_ptr = NULL; else { ... } */ |
---|
249 | |
---|
250 | if ( is_paused_here ) { |
---|
251 | |
---|
252 | ESDBG_TRACE( printf( "paused, awaiting instructions.\n" ); ); |
---|
253 | timeout_ptr = NULL; |
---|
254 | |
---|
255 | } else { |
---|
256 | |
---|
257 | timeout.tv_sec = 0; |
---|
258 | /* funky math to make sure a long can hold it all, calulate in ms */ |
---|
259 | timeout.tv_usec = (long) esd_buf_size_samples * 1000L |
---|
260 | / (long) esd_audio_rate / 4L; /* divide by two for stereo */ |
---|
261 | timeout.tv_usec *= 1000L; /* convert to microseconds */ |
---|
262 | timeout_ptr = &timeout; |
---|
263 | |
---|
264 | } |
---|
265 | } |
---|
266 | |
---|
267 | ready = select( max_fd+1, &rd_fds, NULL, NULL, timeout_ptr ); |
---|
268 | |
---|
269 | ESDBG_COMMS( printf( |
---|
270 | "paused=%d, samples=%d, auto=%d, standby=%d, record=%d, ready=%d\n", |
---|
271 | is_paused_here, esd_playing_samples, |
---|
272 | esd_autostandby_secs, esd_on_standby, |
---|
273 | (esd_recorder != 0), ready ); ); |
---|
274 | |
---|
275 | /* TODO: return ready, and do this in esd.c */ |
---|
276 | if ( ready <= 0 ) { |
---|
277 | /* if < 0, something horrible happened: |
---|
278 | EBADF invalid file descriptor - let individual read sort it out |
---|
279 | EINTR non blocked signal caught - o well, no big deal |
---|
280 | EINVAL n is negative - not bloody likely |
---|
281 | ENOMEM unable to allocate internal tables - o well, no big deal */ |
---|
282 | |
---|
283 | if ( !is_paused_here && !esd_playing_samples && (esd_autostandby_secs<0) ) { |
---|
284 | ESDBG_TRACE( printf( "doing nothing, pausing server.\n" ); ); |
---|
285 | esd_audio_flush(); |
---|
286 | esd_audio_pause(); |
---|
287 | esd_last_activity = time( NULL ); |
---|
288 | is_paused_here = 1; |
---|
289 | } |
---|
290 | |
---|
291 | if ( !is_paused_here && !esd_playing_samples && !esd_recorder_list ) { |
---|
292 | |
---|
293 | if ( esd_autostandby_secs >= 0 |
---|
294 | && ( time(NULL) > esd_last_activity + esd_autostandby_secs ) ) { |
---|
295 | ESDBG_TRACE( printf( "bored, going to standby mode.\n" ); ); |
---|
296 | esd_server_standby(); |
---|
297 | esd_on_autostandby = 1; |
---|
298 | is_paused_here = 1; |
---|
299 | } |
---|
300 | |
---|
301 | } |
---|
302 | |
---|
303 | } else { |
---|
304 | |
---|
305 | is_paused_here = 0; |
---|
306 | |
---|
307 | } |
---|
308 | |
---|
309 | return ready; |
---|
310 | } |
---|