1 | #include "esd-server.h" |
---|
2 | |
---|
3 | #include <arpa/inet.h> |
---|
4 | #include <sys/types.h> |
---|
5 | #include <sys/stat.h> |
---|
6 | #include <sys/un.h> |
---|
7 | #include <errno.h> |
---|
8 | #include <signal.h> |
---|
9 | #include <time.h> |
---|
10 | |
---|
11 | #ifndef HAVE_NANOSLEEP |
---|
12 | #include <sys/time.h> |
---|
13 | #include <sys/types.h> |
---|
14 | #include <unistd.h> |
---|
15 | #endif |
---|
16 | |
---|
17 | #include <netdb.h> |
---|
18 | |
---|
19 | /* Older resolvers don't have gethostbyname2() */ |
---|
20 | #ifndef HAVE_GETHOSTBYNAME2 |
---|
21 | #define gethostbyname2(host, family) gethostbyname((host)) |
---|
22 | #endif /* HAVE_GETHOSTBYNAME2 */ |
---|
23 | |
---|
24 | /*******************************************************************/ |
---|
25 | /* esd.c - prototypes */ |
---|
26 | void set_audio_buffer( void *buf, esd_format_t format, int magl, int magr, |
---|
27 | int freq, int speed, int length, long offset ); |
---|
28 | void clean_exit( int signum ); |
---|
29 | void reset_signal( int signum ); |
---|
30 | void reset_daemon( int signum ); |
---|
31 | int open_listen_socket( const char *hostname, int port ); |
---|
32 | |
---|
33 | /*******************************************************************/ |
---|
34 | /* globals */ |
---|
35 | |
---|
36 | int esd_is_owned = 0; /* start unowned, first client claims it */ |
---|
37 | int esd_is_locked = 1; /* if owned, will prohibit foreign sources */ |
---|
38 | char esd_owner_key[ESD_KEY_LEN]; /* the key that locks the daemon */ |
---|
39 | |
---|
40 | int esd_on_standby = 0; /* set to route ignore incoming audio data */ |
---|
41 | int esdbg_trace = 0; /* show warm fuzzy debug messages */ |
---|
42 | int esdbg_comms = 0; /* show protocol level debug messages */ |
---|
43 | int esdbg_mixer = 0; /* show mixer engine debug messages */ |
---|
44 | |
---|
45 | int esd_buf_size_octets = 0; /* size of audio buffer in bytes */ |
---|
46 | int esd_buf_size_samples = 0; /* size of audio buffer in samples */ |
---|
47 | int esd_sample_size = 0; /* size of sample in bytes */ |
---|
48 | |
---|
49 | int esd_beeps = 1; /* whether or not to beep on startup */ |
---|
50 | int listen_socket = -1; /* socket to accept connections on */ |
---|
51 | int esd_trustval = -1; /* -1 be paranoic, 0 trust to owner of ESD_UNIX_SOCKET_DIR */ |
---|
52 | |
---|
53 | int esd_autostandby_secs = -1; /* timeout to release audio device, disabled <0 */ |
---|
54 | time_t esd_last_activity = 0; /* seconds since last activity */ |
---|
55 | int esd_on_autostandby = 0; /* set when auto paused for auto reawaken */ |
---|
56 | |
---|
57 | int esd_use_tcpip = 0; /* use tcp/ip sockets instead of unix domain */ |
---|
58 | int esd_terminate = 0; /* terminate after the last client exits */ |
---|
59 | int esd_public = 0; /* allow connects from hosts other than localhost */ |
---|
60 | int esd_spawnpid = 0; /* The PID of the process that spawned us (for use by esdlib only) */ |
---|
61 | int esd_spawnfd = 0; /* The PID of the process that spawned us (for use by esdlib only) */ |
---|
62 | |
---|
63 | static char *programname = NULL; |
---|
64 | |
---|
65 | /*******************************************************************/ |
---|
66 | /* just to create the startup tones for the fun of it */ |
---|
67 | void set_audio_buffer( void *buf, esd_format_t format, |
---|
68 | int magl, int magr, |
---|
69 | int freq, int speed, int length, long offset ) |
---|
70 | { |
---|
71 | int i; |
---|
72 | float sample; |
---|
73 | float kf = 2.0 * 3.14 * (float)freq / (float)speed; |
---|
74 | |
---|
75 | unsigned char *uc_buf = (unsigned char *)buf; |
---|
76 | signed short *ss_buf = (signed short *)buf; |
---|
77 | |
---|
78 | /* printf( "fmt=%d, ml=%d, mr=%d, freq=%d, speed=%d, len=%ld\n", |
---|
79 | format, magl, magr, freq, speed, length ); */ |
---|
80 | |
---|
81 | switch ( format & ESD_MASK_BITS ) |
---|
82 | { |
---|
83 | case ESD_BITS8: |
---|
84 | for ( i = 0 ; i < length ; i+=2 ) { |
---|
85 | sample = sin( (float)(i+offset) * kf ); |
---|
86 | uc_buf[i] = 127 + magl * sample; |
---|
87 | uc_buf[i+1] = 127 + magr * sample; |
---|
88 | } |
---|
89 | break; |
---|
90 | case ESD_BITS16: /* assume same endian */ |
---|
91 | for ( i = 0 ; i < length ; i+=2 ) { |
---|
92 | sample = sin( (float)(i+offset) * kf ); |
---|
93 | ss_buf[i] = magl * sample; |
---|
94 | ss_buf[i+1] = magr * sample; |
---|
95 | } |
---|
96 | break; |
---|
97 | default: |
---|
98 | fprintf( stderr, |
---|
99 | "unsupported format for set_audio_buffer: 0x%08x\n", |
---|
100 | format ); |
---|
101 | exit( 1 ); |
---|
102 | } |
---|
103 | |
---|
104 | |
---|
105 | return; |
---|
106 | } |
---|
107 | |
---|
108 | /*******************************************************************/ |
---|
109 | /* to properly handle signals */ |
---|
110 | |
---|
111 | void reset_daemon( int signum ) |
---|
112 | { |
---|
113 | int tumbler; |
---|
114 | |
---|
115 | ESDBG_TRACE( |
---|
116 | printf( "(ca) resetting sound daemon on signal %d\n", |
---|
117 | signum ); ); |
---|
118 | |
---|
119 | /* reset the access rights */ |
---|
120 | esd_is_owned = 0; |
---|
121 | esd_is_locked = 1; |
---|
122 | |
---|
123 | /* scramble the stored key */ |
---|
124 | srand( time(NULL) ); |
---|
125 | for ( tumbler = 0 ; tumbler < ESD_KEY_LEN ; tumbler++ ) { |
---|
126 | esd_owner_key[ tumbler ] = rand() % 256; |
---|
127 | } |
---|
128 | |
---|
129 | /* close the clients */ |
---|
130 | while ( esd_clients_list != NULL ) |
---|
131 | { |
---|
132 | erase_client( esd_clients_list ); |
---|
133 | } |
---|
134 | |
---|
135 | /* free samples */ |
---|
136 | while ( esd_samples_list != NULL ) |
---|
137 | { |
---|
138 | erase_sample( esd_samples_list->sample_id, 1 ); |
---|
139 | /* TODO: kill_sample, so it stops playing */ |
---|
140 | /* a looping sample will get stuck */ |
---|
141 | } |
---|
142 | |
---|
143 | /* reset next sample id */ |
---|
144 | esd_next_sample_id = 1; |
---|
145 | |
---|
146 | /* reset signal handler, if not called from a signal, no effect */ |
---|
147 | signal( SIGHUP, reset_daemon ); |
---|
148 | } |
---|
149 | |
---|
150 | void clean_exit(int signum) { |
---|
151 | /* just clean up as best we can and terminate from here */ |
---|
152 | esd_client_t * client = esd_clients_list; |
---|
153 | |
---|
154 | /* fprintf( stderr, "received signal %d: terminating...\n", signum );*/ |
---|
155 | |
---|
156 | /* free the sound device */ |
---|
157 | esd_audio_close(); |
---|
158 | |
---|
159 | /* close the listening socket */ |
---|
160 | close( listen_socket ); |
---|
161 | |
---|
162 | /* close the clients */ |
---|
163 | while ( client != NULL ) |
---|
164 | { |
---|
165 | close( client->fd ); |
---|
166 | client = client->next; |
---|
167 | } |
---|
168 | if (!esd_use_tcpip) |
---|
169 | { |
---|
170 | unlink(ESD_UNIX_SOCKET_NAME); |
---|
171 | rmdir(ESD_UNIX_SOCKET_DIR); |
---|
172 | } |
---|
173 | |
---|
174 | |
---|
175 | /* trust the os to clean up the memory for the samples and such */ |
---|
176 | exit( 0 ); |
---|
177 | } |
---|
178 | |
---|
179 | void reset_signal(int signum) { |
---|
180 | /* fprintf( stderr, "received signal %d: resetting...\n", signum );*/ |
---|
181 | signal( signum, reset_signal); |
---|
182 | |
---|
183 | return; |
---|
184 | } |
---|
185 | |
---|
186 | static int |
---|
187 | esd_connect_unix(void) |
---|
188 | { |
---|
189 | struct sockaddr_un socket_unix; |
---|
190 | int socket_out = -1; |
---|
191 | int curstate = 1; |
---|
192 | |
---|
193 | /* create the socket, and set for non-blocking */ |
---|
194 | socket_out = socket( AF_UNIX, SOCK_STREAM, 0 ); |
---|
195 | if ( socket_out < 0 ) |
---|
196 | return -1; |
---|
197 | /* this was borrowed blindly from the Tcl socket stuff */ |
---|
198 | if ( fcntl( socket_out, F_SETFD, FD_CLOEXEC ) < 0 ) |
---|
199 | return -1; |
---|
200 | if ( setsockopt( socket_out, SOL_SOCKET, SO_REUSEADDR, |
---|
201 | &curstate, sizeof(curstate) ) < 0 ) |
---|
202 | return -1; |
---|
203 | /* set the connect information */ |
---|
204 | socket_unix.sun_family = AF_UNIX; |
---|
205 | strncpy(socket_unix.sun_path, ESD_UNIX_SOCKET_NAME, sizeof(socket_unix.sun_path)); |
---|
206 | if ( connect( socket_out, |
---|
207 | (struct sockaddr *) &socket_unix, SUN_LEN(&socket_unix) ) < 0 ) |
---|
208 | return -1; |
---|
209 | return socket_out; |
---|
210 | } |
---|
211 | |
---|
212 | |
---|
213 | /*******************************************************************/ |
---|
214 | /* safely create directory for socket |
---|
215 | Code inspired by trans_mkdir from XFree86 source code |
---|
216 | For more credits see xc/lib/xtrans/Xtransutil.c. */ |
---|
217 | int |
---|
218 | safe_mksocketdir(void) |
---|
219 | { |
---|
220 | struct stat dir_stats; |
---|
221 | |
---|
222 | #if defined(S_ISVTX) |
---|
223 | #define ESD_UNIX_SOCKET_DIR_MODE (S_IRUSR|S_IWUSR|S_IXUSR|\ |
---|
224 | S_IRGRP|S_IWGRP|S_IXGRP|\ |
---|
225 | S_IROTH|S_IWOTH|S_IXOTH|S_ISVTX) |
---|
226 | #else |
---|
227 | #define ESD_UNIX_SOCKET_DIR_MODE (S_IRUSR|S_IWUSR|S_IXUSR|\ |
---|
228 | S_IRGRP|S_IWGRP|S_IXGRP|\ |
---|
229 | S_IROTH|S_IWOTH|S_IXOTH) |
---|
230 | #endif |
---|
231 | |
---|
232 | if (mkdir(ESD_UNIX_SOCKET_DIR, ESD_UNIX_SOCKET_DIR_MODE) == 0) { |
---|
233 | if (chmod(ESD_UNIX_SOCKET_DIR, ESD_UNIX_SOCKET_DIR_MODE) != 0) { |
---|
234 | return -1; |
---|
235 | } |
---|
236 | return 0; |
---|
237 | } |
---|
238 | /* If mkdir failed with EEXIST, test if it is a directory with |
---|
239 | the right modes, else fail */ |
---|
240 | if (errno == EEXIST) { |
---|
241 | #if !defined(S_IFLNK) && !defined(S_ISLNK) |
---|
242 | #define lstat(a,b) stat(a,b) |
---|
243 | #endif |
---|
244 | if (lstat(ESD_UNIX_SOCKET_DIR, &dir_stats) != 0) { |
---|
245 | return -1; |
---|
246 | } |
---|
247 | if (S_ISDIR(dir_stats.st_mode)) { |
---|
248 | int updateOwner = 0; |
---|
249 | int updateMode = 0; |
---|
250 | int updatedOwner = 0; |
---|
251 | int updatedMode = 0; |
---|
252 | /* Check if the directory's ownership is OK. */ |
---|
253 | if ((dir_stats.st_uid != 0) && (dir_stats.st_uid != getuid())) |
---|
254 | updateOwner = 1; |
---|
255 | /* |
---|
256 | * Check if the directory's mode is OK. An exact match isn't |
---|
257 | * required, just a mode that isn't more permissive than the |
---|
258 | * one requested. |
---|
259 | */ |
---|
260 | if ( ~ESD_UNIX_SOCKET_DIR_MODE & (dir_stats.st_mode & ~S_IFMT)) |
---|
261 | updateMode = 1; |
---|
262 | #if defined(S_ISVTX) |
---|
263 | if ((dir_stats.st_mode & S_IWOTH) && !(dir_stats.st_mode & S_ISVTX)) |
---|
264 | updateMode = 1; |
---|
265 | #endif |
---|
266 | #if defined(HAVE_FCHOWN) && defined(HAVE_FCHMOD) |
---|
267 | /* |
---|
268 | * If fchown(2) and fchmod(2) are available, try to correct the |
---|
269 | * directory's owner and mode. Otherwise it isn't safe to attempt |
---|
270 | * to do this. |
---|
271 | */ |
---|
272 | if (updateMode || updateOwner) { |
---|
273 | int fd = -1; |
---|
274 | struct stat fdir_stats; |
---|
275 | if ((fd = open(ESD_UNIX_SOCKET_DIR, O_RDONLY)) != -1) { |
---|
276 | if (fstat(fd, &fdir_stats) == -1) { |
---|
277 | return esd_trustval; |
---|
278 | } |
---|
279 | /* |
---|
280 | * Verify that we've opened the same directory as was |
---|
281 | * checked above. |
---|
282 | */ |
---|
283 | if (!S_ISDIR(fdir_stats.st_mode) || |
---|
284 | dir_stats.st_dev != fdir_stats.st_dev || |
---|
285 | dir_stats.st_ino != fdir_stats.st_ino) { |
---|
286 | return esd_trustval; |
---|
287 | } |
---|
288 | if (updateOwner && fchown(fd, getuid(), getgid()) == 0) |
---|
289 | updatedOwner = 1; |
---|
290 | if (updateMode && fchmod(fd, ESD_UNIX_SOCKET_DIR_MODE) == 0) |
---|
291 | updatedMode = 1; |
---|
292 | close(fd); |
---|
293 | } |
---|
294 | } |
---|
295 | #endif |
---|
296 | if (updateOwner && !updatedOwner) { |
---|
297 | fprintf(stderr, |
---|
298 | "esd: Failed to fix owner of %s.\n", |
---|
299 | ESD_UNIX_SOCKET_DIR); |
---|
300 | if (esd_trustval) fprintf(stderr, "Try -trust to force esd to start.\n"); |
---|
301 | return esd_trustval; |
---|
302 | } |
---|
303 | if (updateMode && !updatedMode) { |
---|
304 | fprintf(stderr, "esd: Failed to fix mode of %s to %04o.\n", |
---|
305 | ESD_UNIX_SOCKET_DIR, ESD_UNIX_SOCKET_DIR_MODE); |
---|
306 | if (esd_trustval) fprintf(stderr, "Try -trust to force esd to start.\n"); |
---|
307 | return esd_trustval; |
---|
308 | } |
---|
309 | return 0; |
---|
310 | } |
---|
311 | } |
---|
312 | /* In all other cases, fail */ |
---|
313 | return -1; |
---|
314 | } |
---|
315 | |
---|
316 | /*******************************************************************/ |
---|
317 | /* returns the listening socket descriptor */ |
---|
318 | int open_listen_socket(const char *hostname, int port ) |
---|
319 | { |
---|
320 | /*********************/ |
---|
321 | /* socket test setup */ |
---|
322 | struct sockaddr_in socket_addr; |
---|
323 | struct sockaddr_un socket_unix; |
---|
324 | int socket_listen = -1; |
---|
325 | struct linger lin; |
---|
326 | |
---|
327 | struct hostent *resolved; |
---|
328 | |
---|
329 | |
---|
330 | /* create the socket, and set for non-blocking */ |
---|
331 | if (esd_use_tcpip) |
---|
332 | socket_listen=socket(AF_INET, SOCK_STREAM, 0); |
---|
333 | else |
---|
334 | { |
---|
335 | if (safe_mksocketdir()) |
---|
336 | { |
---|
337 | fprintf(stderr, |
---|
338 | "esd: Esound sound daemon unable to create unix domain socket:\n" |
---|
339 | "%s\n" |
---|
340 | "The socket is not accessible by esd.\n" |
---|
341 | "Exiting...\n", ESD_UNIX_SOCKET_NAME); |
---|
342 | exit(1); |
---|
343 | } |
---|
344 | else |
---|
345 | { |
---|
346 | if (esd_connect_unix() >= 0) |
---|
347 | { |
---|
348 | /* not allowed access */ |
---|
349 | fprintf(stderr, |
---|
350 | "esd: Esound sound daemon already running or stale UNIX socket\n" |
---|
351 | "%s\n" |
---|
352 | "This socket already exists indicating esd is already running.\n" |
---|
353 | "Exiting...\n", ESD_UNIX_SOCKET_NAME); |
---|
354 | exit(1); |
---|
355 | } |
---|
356 | } |
---|
357 | unlink(ESD_UNIX_SOCKET_NAME); |
---|
358 | socket_listen=socket(AF_UNIX, SOCK_STREAM, 0); |
---|
359 | } |
---|
360 | if (socket_listen < 0) |
---|
361 | { |
---|
362 | fprintf(stderr,"Unable to create socket\n"); |
---|
363 | return( -1 ); |
---|
364 | } |
---|
365 | if (fcntl(socket_listen, F_SETFL, O_NONBLOCK) < 0) |
---|
366 | { |
---|
367 | fprintf(stderr,"Unable to set socket to non-blocking\n"); |
---|
368 | return( -1 ); |
---|
369 | } |
---|
370 | |
---|
371 | /* set socket for linger? */ |
---|
372 | lin.l_onoff=1; /* block a closing socket for 1 second */ |
---|
373 | lin.l_linger=100; /* if data is waiting to be sent */ |
---|
374 | if ( setsockopt( socket_listen, SOL_SOCKET, SO_LINGER, |
---|
375 | &lin, sizeof(struct linger) ) < 0 ) |
---|
376 | { |
---|
377 | fprintf(stderr,"Unable to set socket linger value to %d\n", |
---|
378 | lin.l_linger); |
---|
379 | return( -1 ); |
---|
380 | } |
---|
381 | { |
---|
382 | int n = 1; |
---|
383 | setsockopt(socket_listen, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)); |
---|
384 | /* if it fails, so what */ |
---|
385 | } |
---|
386 | |
---|
387 | if (esd_use_tcpip) |
---|
388 | { |
---|
389 | /* set the listening information */ |
---|
390 | memset(&socket_addr, 0, sizeof(struct sockaddr_in)); |
---|
391 | socket_addr.sin_family = AF_INET; |
---|
392 | socket_addr.sin_port = htons( port ); |
---|
393 | |
---|
394 | /* if hostname is set, bind to its first address */ |
---|
395 | if (hostname) |
---|
396 | { |
---|
397 | if (!(resolved=gethostbyname2(hostname, AF_INET))) |
---|
398 | { |
---|
399 | herror(programname); |
---|
400 | return -1; |
---|
401 | } |
---|
402 | memcpy(&(socket_addr.sin_addr), resolved->h_addr_list[0], resolved->h_length); |
---|
403 | } else if (esd_public) |
---|
404 | socket_addr.sin_addr.s_addr = htonl( INADDR_ANY ); |
---|
405 | else |
---|
406 | socket_addr.sin_addr.s_addr = htonl( INADDR_LOOPBACK ); |
---|
407 | |
---|
408 | if ( bind( socket_listen, |
---|
409 | (struct sockaddr *) &socket_addr, |
---|
410 | sizeof(struct sockaddr_in) ) < 0 ) |
---|
411 | { |
---|
412 | fprintf(stderr,"Unable to bind port %d\n", port ); |
---|
413 | exit(1); |
---|
414 | } |
---|
415 | } |
---|
416 | else |
---|
417 | { |
---|
418 | mode_t old_umask; |
---|
419 | |
---|
420 | old_umask = umask(0); |
---|
421 | socket_unix.sun_family=AF_UNIX; |
---|
422 | strncpy(socket_unix.sun_path, ESD_UNIX_SOCKET_NAME, sizeof(socket_unix.sun_path)); |
---|
423 | if ( bind( socket_listen, |
---|
424 | (struct sockaddr *) &socket_unix, SUN_LEN(&socket_unix) ) < 0 ) |
---|
425 | { |
---|
426 | fprintf(stderr,"Unable to connect to UNIX socket %s\n", ESD_UNIX_SOCKET_NAME); |
---|
427 | if (!esd_use_tcpip) |
---|
428 | { |
---|
429 | unlink(ESD_UNIX_SOCKET_NAME); |
---|
430 | rmdir(ESD_UNIX_SOCKET_DIR); |
---|
431 | } |
---|
432 | exit(1); |
---|
433 | } |
---|
434 | umask(old_umask); |
---|
435 | } |
---|
436 | if (listen(socket_listen,16)<0) |
---|
437 | { |
---|
438 | fprintf(stderr,"Unable to set socket listen buffer length\n"); |
---|
439 | if (!esd_use_tcpip) |
---|
440 | { |
---|
441 | unlink(ESD_UNIX_SOCKET_NAME); |
---|
442 | rmdir(ESD_UNIX_SOCKET_DIR); |
---|
443 | } |
---|
444 | exit(1); |
---|
445 | } |
---|
446 | |
---|
447 | return socket_listen; |
---|
448 | } |
---|
449 | |
---|
450 | /*******************************************************************/ |
---|
451 | /* daemon eats sound data, without playing anything, return boolean ok */ |
---|
452 | int esd_server_standby(void) |
---|
453 | { |
---|
454 | int ok = 1; |
---|
455 | |
---|
456 | /* only bother if we're not already on standby */ |
---|
457 | if ( !esd_on_standby ) { |
---|
458 | ESDBG_TRACE( printf( "setting sound daemon to standby\n" ); ); |
---|
459 | |
---|
460 | /* TODO: close down any recorders, too */ |
---|
461 | esd_on_standby = 1; |
---|
462 | esd_audio_close(); |
---|
463 | } |
---|
464 | |
---|
465 | return ok; |
---|
466 | } |
---|
467 | |
---|
468 | /*******************************************************************/ |
---|
469 | /* daemon goes back to playing sound data, returns boolean ok */ |
---|
470 | int esd_server_resume(void) |
---|
471 | { |
---|
472 | int ok = 1; |
---|
473 | |
---|
474 | /* only bother if we're already on standby */ |
---|
475 | if ( esd_on_standby ) { |
---|
476 | |
---|
477 | ESDBG_TRACE( printf( "resuming sound daemon\n" ); ); |
---|
478 | |
---|
479 | /* reclaim the audio device */ |
---|
480 | if ( esd_audio_open() < 0 ) { |
---|
481 | /* device was busy or something, return error, try later */ |
---|
482 | ok = 0; |
---|
483 | } else { |
---|
484 | /* turn ourselves back on */ |
---|
485 | esd_on_standby = 0; |
---|
486 | esd_on_autostandby = 0; |
---|
487 | esd_forced_standby = 0; |
---|
488 | } |
---|
489 | } |
---|
490 | |
---|
491 | return ok; |
---|
492 | } |
---|
493 | |
---|
494 | /*******************************************************************/ |
---|
495 | int main ( int argc, char *argv[] ) |
---|
496 | { |
---|
497 | /***************************/ |
---|
498 | /* Enlightened sound Daemon */ |
---|
499 | |
---|
500 | int esd_port = ESD_DEFAULT_PORT; |
---|
501 | int length = 0; |
---|
502 | int arg = 0; |
---|
503 | int itmp; |
---|
504 | |
---|
505 | void *output_buffer = NULL; |
---|
506 | |
---|
507 | char *hostname=NULL; |
---|
508 | |
---|
509 | /* begin test scaffolding parameters */ |
---|
510 | /* int format = AFMT_U8; AFMT_S16_LE; */ |
---|
511 | /* int stereo = 0; */ /* 0=mono, 1=stereo */ |
---|
512 | int default_rate = ESD_DEFAULT_RATE, default_buf_size = ESD_BUF_SIZE; |
---|
513 | int i, j, freq=440; |
---|
514 | int magl, magr; |
---|
515 | |
---|
516 | int first = 1; |
---|
517 | |
---|
518 | int default_format = ESD_BITS16 | ESD_STEREO; |
---|
519 | /* end test scaffolding parameters */ |
---|
520 | |
---|
521 | programname = *argv; |
---|
522 | |
---|
523 | /* parse the command line args */ |
---|
524 | for ( arg = 1 ; arg < argc ; arg++ ) { |
---|
525 | if ( !strcmp( argv[ arg ], "-d" ) ) { |
---|
526 | if ( ++arg != argc ) { |
---|
527 | esd_audio_device = argv[ arg ]; |
---|
528 | if ( !esd_audio_device ) { |
---|
529 | esd_port = ESD_DEFAULT_PORT; |
---|
530 | fprintf( stderr, "- could not read device: %s\n", |
---|
531 | argv[ arg ] ); |
---|
532 | } |
---|
533 | fprintf( stderr, "- using device %s\n", |
---|
534 | esd_audio_device ); |
---|
535 | } |
---|
536 | } else if ( !strcmp( argv[ arg ], "-port" ) ) { |
---|
537 | if ( ++arg != argc ) { |
---|
538 | esd_port = atoi( argv[ arg ] ); |
---|
539 | if ( !esd_port ) { |
---|
540 | esd_port = ESD_DEFAULT_PORT; |
---|
541 | fprintf( stderr, "- could not read port: %s\n", |
---|
542 | argv[ arg ] ); |
---|
543 | } |
---|
544 | fprintf( stderr, "- accepting connections on port %d\n", |
---|
545 | esd_port ); |
---|
546 | } |
---|
547 | |
---|
548 | } else if ( !strcmp( argv[ arg ], "-bind" ) ) { |
---|
549 | if ( ++arg != argc ) |
---|
550 | { |
---|
551 | hostname = argv[ arg ]; |
---|
552 | } |
---|
553 | fprintf( stderr, "- accepting connections on port %d\n", |
---|
554 | esd_port ); |
---|
555 | } else if ( !strcmp( argv[ arg ], "-b" ) ) { |
---|
556 | fprintf( stderr, "- server format: 8 bit samples\n" ); |
---|
557 | default_format &= ~ESD_MASK_BITS; default_format |= ESD_BITS8; |
---|
558 | } else if ( !strcmp( argv[ arg ], "-r" ) ) { |
---|
559 | if ( ++arg != argc ) { |
---|
560 | default_rate = atoi( argv[ arg ] ); |
---|
561 | if ( !default_rate ) { |
---|
562 | default_rate = ESD_DEFAULT_RATE; |
---|
563 | fprintf( stderr, "- could not read rate: %s\n", |
---|
564 | argv[ arg ] ); |
---|
565 | } |
---|
566 | fprintf( stderr, "- server format: sample rate = %d Hz\n", |
---|
567 | default_rate ); |
---|
568 | } |
---|
569 | } else if ( !strcmp( argv[ arg ], "-as" ) ) { |
---|
570 | if ( ++arg != argc ) { |
---|
571 | esd_autostandby_secs = atoi( argv[ arg ] ); |
---|
572 | if ( !esd_autostandby_secs ) { |
---|
573 | esd_autostandby_secs = ESD_DEFAULT_AUTOSTANDBY_SECS; |
---|
574 | fprintf( stderr, "- could not read autostandby timeout: %s\n", |
---|
575 | argv[ arg ] ); |
---|
576 | } |
---|
577 | /* fprintf( stderr, "- autostandby timeout: %d seconds\n", |
---|
578 | esd_autostandby_secs );*/ |
---|
579 | } |
---|
580 | #ifdef ESDBG |
---|
581 | } else if ( !strcmp( argv[ arg ], "-vt" ) ) { |
---|
582 | esdbg_trace = 1; |
---|
583 | fprintf( stderr, "- enabling trace diagnostic info\n" ); |
---|
584 | } else if ( !strcmp( argv[ arg ], "-vc" ) ) { |
---|
585 | esdbg_comms = 1; |
---|
586 | fprintf( stderr, "- enabling comms diagnostic info\n" ); |
---|
587 | } else if ( !strcmp( argv[ arg ], "-vm" ) ) { |
---|
588 | esdbg_mixer = 1; |
---|
589 | fprintf( stderr, "- enabling mixer diagnostic info\n" ); |
---|
590 | #endif |
---|
591 | } else if ( !strcmp( argv[ arg ], "-nobeeps" ) ) { |
---|
592 | esd_beeps = 0; |
---|
593 | /* fprintf( stderr, "- disabling startup beeps\n" );*/ |
---|
594 | } else if ( !strcmp( argv[ arg ], "-unix" ) ) { |
---|
595 | esd_use_tcpip = 0; |
---|
596 | } else if ( !strcmp( argv[ arg ], "-tcp" ) ) { |
---|
597 | esd_use_tcpip = 1; |
---|
598 | } else if ( !strcmp( argv[ arg ], "-public" ) ) { |
---|
599 | esd_public = 1; |
---|
600 | } else if ( !strcmp( argv[ arg ], "-promiscuous" ) ) { |
---|
601 | esd_is_owned = 1; |
---|
602 | esd_is_locked = 0; |
---|
603 | } else if ( !strcmp( argv[ arg ], "-terminate" ) ) { |
---|
604 | esd_terminate = 1; |
---|
605 | } else if ( !strcmp( argv[ arg ], "-spawnpid" ) ) { |
---|
606 | if ( ++arg < argc ) |
---|
607 | esd_spawnpid = atoi( argv[ arg ] ); |
---|
608 | } else if ( !strcmp( argv[ arg ], "-spawnfd" ) ) { |
---|
609 | if ( ++arg < argc ) |
---|
610 | esd_spawnfd = atoi( argv[ arg ] ); |
---|
611 | } else if ( !strcmp( argv[ arg ], "-trust" ) ) { |
---|
612 | esd_trustval = 0; |
---|
613 | } else if ( !strcmp( argv[ arg ], "-v" ) || !strcmp( argv[ arg ], "--version" ) ) { |
---|
614 | fprintf(stderr, "Esound version " VERSION "\n"); |
---|
615 | exit (0); |
---|
616 | } else if ( !strcmp( argv[ arg ], "-h" ) || !strcmp( argv[ arg ], "--help" ) ) { |
---|
617 | fprintf( stderr, "Esound version " VERSION "\n\n"); |
---|
618 | fprintf( stderr, "Usage: esd [options]\n\n" ); |
---|
619 | fprintf( stderr, " -v --version print version information\n" ); |
---|
620 | fprintf( stderr, " -d DEVICE force esd to use sound device DEVICE\n" ); |
---|
621 | fprintf( stderr, " -b run server in 8 bit sound mode\n" ); |
---|
622 | fprintf( stderr, " -r RATE run server at sample rate of RATE\n" ); |
---|
623 | fprintf( stderr, " -as SECS free audio device after SECS of inactivity\n" ); |
---|
624 | fprintf( stderr, " -unix use unix domain sockets instead of tcp/ip\n" ); |
---|
625 | fprintf( stderr, " -tcp use tcp/ip sockets instead of unix domain\n" ); |
---|
626 | fprintf( stderr, " -public make tcp/ip access public (other than localhost)\n" ); |
---|
627 | fprintf( stderr, " -promiscuous start unlocked and owned (disable authenticaton) NOT RECOMMENDED\n" ); |
---|
628 | fprintf( stderr, " -terminate terminate esd daemone after last client exits\n" ); |
---|
629 | fprintf( stderr, " -nobeeps disable startup beeps\n" ); |
---|
630 | fprintf( stderr, " -trust start esd even if use of %s can be insecure\n", |
---|
631 | ESD_UNIX_SOCKET_DIR ); |
---|
632 | #ifdef ESDBG |
---|
633 | fprintf( stderr, " -vt enable trace diagnostic info\n" ); |
---|
634 | fprintf( stderr, " -vc enable comms diagnostic info\n" ); |
---|
635 | fprintf( stderr, " -vm enable mixer diagnostic info\n" ); |
---|
636 | #endif |
---|
637 | fprintf( stderr, " -port PORT listen for connections at PORT (only for tcp/ip)\n" ); |
---|
638 | fprintf( stderr, " -bind ADDRESS binds to ADDRESS (only for tcp/ip)\n" ); |
---|
639 | fprintf( stderr, "\nPossible devices are: %s\n", esd_audio_devices() ); |
---|
640 | exit( 0 ); |
---|
641 | } else { |
---|
642 | fprintf( stderr, "unrecognized option: %s\n", argv[ arg ] ); |
---|
643 | } |
---|
644 | } |
---|
645 | |
---|
646 | /* open the listening socket */ |
---|
647 | listen_socket = open_listen_socket(hostname, esd_port ); |
---|
648 | if ( listen_socket < 0 ) { |
---|
649 | fprintf( stderr, "fatal error opening socket\n" ); |
---|
650 | if (!esd_use_tcpip) |
---|
651 | { |
---|
652 | unlink(ESD_UNIX_SOCKET_NAME); |
---|
653 | rmdir(ESD_UNIX_SOCKET_DIR); |
---|
654 | } |
---|
655 | exit( 1 ); |
---|
656 | } |
---|
657 | |
---|
658 | #define ESD_AUDIO_STUFF \ |
---|
659 | esd_sample_size = ( (esd_audio_format & ESD_MASK_BITS) == ESD_BITS16 ) \ |
---|
660 | ? sizeof(signed short) : sizeof(unsigned char); \ |
---|
661 | esd_buf_size_samples = default_buf_size / 2; \ |
---|
662 | esd_buf_size_octets = esd_buf_size_samples * esd_sample_size; |
---|
663 | |
---|
664 | /* start the initializatin process */ |
---|
665 | /* printf( "ESound ESD daemon initializing...\n" );*/ |
---|
666 | |
---|
667 | /* set the data size parameters */ |
---|
668 | esd_audio_format = default_format; |
---|
669 | esd_audio_rate = default_rate; |
---|
670 | ESD_AUDIO_STUFF; |
---|
671 | |
---|
672 | /* open and initialize the audio device, /dev/dsp */ |
---|
673 | itmp = esd_audio_open(); |
---|
674 | if (itmp == -2) { /* Special return value indicates open of device failed, don't bother |
---|
675 | trying */ |
---|
676 | if(esd_spawnpid) |
---|
677 | kill(esd_spawnpid, SIGALRM); /* Startup failed */ |
---|
678 | |
---|
679 | if(esd_spawnfd) { |
---|
680 | char c = 0; /* Startup failed */ |
---|
681 | write (esd_spawnfd, &c, 1); |
---|
682 | } |
---|
683 | |
---|
684 | if (!esd_use_tcpip) { |
---|
685 | unlink(ESD_UNIX_SOCKET_NAME); |
---|
686 | rmdir(ESD_UNIX_SOCKET_DIR); |
---|
687 | } |
---|
688 | exit (2); |
---|
689 | } else if ( itmp < 0 ) { |
---|
690 | fprintf(stderr, "Audio device open for 44.1Khz, stereo, 16bit failed\n" |
---|
691 | "Trying 44.1Khz, 8bit stereo.\n"); |
---|
692 | /* cant do defaults ... try 44.1 kkz 8bit stereo */ |
---|
693 | esd_audio_format = ESD_BITS8 | ESD_STEREO; |
---|
694 | esd_audio_rate = 44100; |
---|
695 | ESD_AUDIO_STUFF; |
---|
696 | if ( esd_audio_open() < 0 ) { |
---|
697 | fprintf(stderr, "Audio device open for 44.1Khz, stereo, 8bit failed\n" |
---|
698 | |
---|
699 | "Trying 48Khz, 16bit stereo.\n"); |
---|
700 | /* cant do defaults ... try 48 kkz 16bit stereo */ |
---|
701 | esd_audio_format = ESD_BITS16 | ESD_STEREO; |
---|
702 | esd_audio_rate = 48000; |
---|
703 | ESD_AUDIO_STUFF; |
---|
704 | if ( esd_audio_open() < 0 ) { |
---|
705 | fprintf(stderr, "Audio device open for 48Khz, stereo,16bit failed\n" |
---|
706 | "Trying 22.05Khz, 8bit stereo.\n"); |
---|
707 | /* cant do defaults ... try 22.05 kkz 8bit stereo */ |
---|
708 | esd_audio_format = ESD_BITS8 | ESD_STEREO; |
---|
709 | esd_audio_rate = 22050; |
---|
710 | ESD_AUDIO_STUFF; |
---|
711 | if ( esd_audio_open() < 0 ) { |
---|
712 | fprintf(stderr, "Audio device open for 22.05Khz, stereo, 8bit failed\n" |
---|
713 | "Trying 44.1Khz, 16bit mono.\n"); |
---|
714 | /* cant do defaults ... try 44.1Khz kkz 16bit mono */ |
---|
715 | esd_audio_format = ESD_BITS16; |
---|
716 | esd_audio_rate = 44100; |
---|
717 | ESD_AUDIO_STUFF; |
---|
718 | if ( esd_audio_open() < 0 ) { |
---|
719 | fprintf(stderr, "Audio device open for 44.1Khz, mono, 8bit failed\n" |
---|
720 | "Trying 22.05Khz, 8bit mono.\n"); |
---|
721 | /* cant do defaults ... try 22.05 kkz 8bit mono */ |
---|
722 | esd_audio_format = ESD_BITS8; |
---|
723 | esd_audio_rate = 22050; |
---|
724 | ESD_AUDIO_STUFF; |
---|
725 | if ( esd_audio_open() < 0 ) { |
---|
726 | fprintf(stderr, "Audio device open for 22.05Khz, mono, 8bit failed\n" |
---|
727 | "Trying 11.025Khz, 8bit stereo.\n"); |
---|
728 | /* cant to defaults ... try 11.025 kkz 8bit stereo */ |
---|
729 | esd_audio_format = ESD_BITS8 | ESD_STEREO; |
---|
730 | esd_audio_rate = 11025; |
---|
731 | ESD_AUDIO_STUFF; |
---|
732 | if ( esd_audio_open() < 0 ) { |
---|
733 | fprintf(stderr, "Audio device open for 11.025Khz, stereo, 8bit failed\n" |
---|
734 | "Trying 11.025Khz, 8bit mono.\n"); |
---|
735 | /* cant to defaults ... try 11.025 kkz 8bit mono */ |
---|
736 | esd_audio_format = ESD_BITS8; |
---|
737 | esd_audio_rate = 11025; |
---|
738 | ESD_AUDIO_STUFF; |
---|
739 | if ( esd_audio_open() < 0 ) { |
---|
740 | fprintf(stderr, "Audio device open for 11.025Khz, mono, 8bit failed\n" |
---|
741 | "Trying 8.192Khz, 8bit mono.\n"); |
---|
742 | /* cant to defaults ... try 8.192 kkz 8bit mono */ |
---|
743 | esd_audio_format = ESD_BITS8; |
---|
744 | esd_audio_rate = 8192; |
---|
745 | ESD_AUDIO_STUFF; |
---|
746 | if ( esd_audio_open() < 0 ) { |
---|
747 | fprintf(stderr, "Audio device open for 8.192Khz, mono, 8bit failed\n" |
---|
748 | "Trying 8Khz, 8bit mono.\n"); |
---|
749 | /* cant to defaults ... try 8 kkz 8bit mono */ |
---|
750 | esd_audio_format = ESD_BITS8; |
---|
751 | esd_audio_rate = 8000; |
---|
752 | ESD_AUDIO_STUFF; |
---|
753 | if ( esd_audio_open() < 0 ) { |
---|
754 | fprintf(stderr, "Sound device inadequate for Esound. Fatal.\n"); |
---|
755 | if (!esd_use_tcpip) |
---|
756 | { |
---|
757 | unlink(ESD_UNIX_SOCKET_NAME); |
---|
758 | rmdir(ESD_UNIX_SOCKET_DIR); |
---|
759 | } |
---|
760 | if(esd_spawnpid) |
---|
761 | kill(esd_spawnpid, SIGALRM); /* Startup failed */ |
---|
762 | |
---|
763 | if(esd_spawnfd) { |
---|
764 | char c = 0; /* Startup failed */ |
---|
765 | write (esd_spawnfd, &c, 1); |
---|
766 | } |
---|
767 | |
---|
768 | exit( 1 ); |
---|
769 | } |
---|
770 | } |
---|
771 | } |
---|
772 | } |
---|
773 | } |
---|
774 | } |
---|
775 | } |
---|
776 | } |
---|
777 | } |
---|
778 | } |
---|
779 | |
---|
780 | /* allocate and zero out buffer */ |
---|
781 | output_buffer = (void *) malloc( esd_buf_size_octets ); |
---|
782 | memset( output_buffer, 0, esd_buf_size_octets ); |
---|
783 | |
---|
784 | |
---|
785 | /* install signal handlers for program integrity */ |
---|
786 | signal( SIGINT, clean_exit ); /* for ^C */ |
---|
787 | signal( SIGTERM, clean_exit ); /* for default kill */ |
---|
788 | signal( SIGPIPE, reset_signal ); /* for closed rec/mon clients */ |
---|
789 | signal( SIGHUP, reset_daemon ); /* kill -HUP clear ownership */ |
---|
790 | |
---|
791 | /* send some sine waves just to check the sound connection */ |
---|
792 | i = 0; |
---|
793 | if ( esd_beeps ) { |
---|
794 | magl = magr = ( (esd_audio_format & ESD_MASK_BITS) == ESD_BITS16) |
---|
795 | ? 30000 : 100; |
---|
796 | |
---|
797 | for ( freq = 55 ; freq < esd_audio_rate/2 ; freq *= 2, i++ ) { |
---|
798 | /* repeat the freq for a few buffer lengths */ |
---|
799 | for ( j = 0 ; j < esd_audio_rate / 4 / esd_buf_size_samples ; j++ ) { |
---|
800 | set_audio_buffer( output_buffer, esd_audio_format, |
---|
801 | ( (i%2) ? magl : 0 ), ( (i%2) ? 0 : magr ), |
---|
802 | freq, esd_audio_rate, esd_buf_size_samples, |
---|
803 | j * esd_buf_size_samples ); |
---|
804 | esd_audio_write( output_buffer, esd_buf_size_octets ); |
---|
805 | } |
---|
806 | } |
---|
807 | } |
---|
808 | |
---|
809 | /* put some stuff in sound driver before pausing */ |
---|
810 | esd_audio_write( NULL, 0); |
---|
811 | |
---|
812 | /* pause the sound output */ |
---|
813 | esd_audio_pause(); |
---|
814 | |
---|
815 | /* Startup succeeded */ |
---|
816 | if(esd_spawnpid) |
---|
817 | kill(esd_spawnpid, SIGUSR1); |
---|
818 | |
---|
819 | if(esd_spawnfd) { |
---|
820 | char c = 1; /* Startup succeeded */ |
---|
821 | write (esd_spawnfd, &c, 1); |
---|
822 | } |
---|
823 | |
---|
824 | /* until we kill the daemon */ |
---|
825 | while ( 1 ) |
---|
826 | { |
---|
827 | /* block while waiting for more clients and new data */ |
---|
828 | wait_for_clients_and_data( listen_socket ); |
---|
829 | |
---|
830 | /* accept new connections */ |
---|
831 | get_new_clients( listen_socket ); |
---|
832 | |
---|
833 | |
---|
834 | if ((esd_clients_list == NULL) && (!first) && (esd_terminate)) { |
---|
835 | /* fprintf(stderr, "No clients!\n");*/ |
---|
836 | clean_exit(0); |
---|
837 | exit(0); |
---|
838 | } |
---|
839 | |
---|
840 | /* check for new protocol requests */ |
---|
841 | poll_client_requests(); |
---|
842 | first = 0; |
---|
843 | |
---|
844 | /* mix new requests, and output to device */ |
---|
845 | refresh_mix_funcs(); /* TODO: set a flag to cue when to do this */ |
---|
846 | length = mix_players( output_buffer, esd_buf_size_octets ); |
---|
847 | |
---|
848 | /* awaken if on autostandby and doing anything */ |
---|
849 | if ( esd_on_autostandby && length && !esd_forced_standby ) { |
---|
850 | ESDBG_TRACE( printf( "stuff to play, waking up.\n" ); ); |
---|
851 | esd_server_resume(); |
---|
852 | } |
---|
853 | |
---|
854 | /* we handle this even when length == 0 because a filter could have |
---|
855 | * closed, and we don't want to eat the processor if one did.. */ |
---|
856 | if ( esd_filter_list && !esd_on_standby ) { |
---|
857 | length = filter_write( output_buffer, length, |
---|
858 | esd_audio_format, esd_audio_rate ); |
---|
859 | } |
---|
860 | |
---|
861 | if ( length > 0 /* || esd_monitor */ ) { |
---|
862 | /* do_sleep = 0; */ |
---|
863 | if ( !esd_on_standby ) { |
---|
864 | /* standby check goes in here, so esd will eat sound data */ |
---|
865 | /* TODO: eat a round of data with a better algorithm */ |
---|
866 | /* this will cause guaranteed timing issues */ |
---|
867 | /* TODO: on monitor, why isn't this a buffer of zeroes? */ |
---|
868 | /* esd_audio_write( output_buffer, esd_buf_size_octets ); */ |
---|
869 | esd_audio_write( output_buffer, length ); |
---|
870 | /* esd_audio_flush(); */ /* this is overkill */ |
---|
871 | esd_last_activity = time( NULL ); |
---|
872 | } |
---|
873 | } else { |
---|
874 | /* should be pausing just fine within wait_for_clients_and_data */ |
---|
875 | /* if so, this isn't really needed */ |
---|
876 | |
---|
877 | /* be very quiet, and wait for a wabbit to come along */ |
---|
878 | #if 0 |
---|
879 | if ( !do_sleep ) { ESDBG_TRACE( printf( "pausing in esd.c\n" ); ); } |
---|
880 | do_sleep = 1; |
---|
881 | esd_audio_pause(); |
---|
882 | #endif |
---|
883 | } |
---|
884 | |
---|
885 | /* if someone's monitoring the sound stream, send them data */ |
---|
886 | /* mix_players, above, forces buffer to zero if no players */ |
---|
887 | /* this clears out any leftovers from recording, below */ |
---|
888 | if ( esd_monitor_list && !esd_on_standby && length ) { |
---|
889 | /* if ( esd_monitor_list && !esd_on_standby ) { */ |
---|
890 | monitor_write( output_buffer, length ); |
---|
891 | } |
---|
892 | |
---|
893 | /* if someone's recording the sound stream, send them data */ |
---|
894 | if ( esd_recorder_list && !esd_on_standby ) { |
---|
895 | length = esd_audio_read( output_buffer, esd_buf_size_octets ); |
---|
896 | if ( length ) { |
---|
897 | length = recorder_write( output_buffer, length ); |
---|
898 | esd_last_activity = time( NULL ); |
---|
899 | } |
---|
900 | } |
---|
901 | |
---|
902 | if ( esd_on_standby ) { |
---|
903 | #ifdef HAVE_NANOSLEEP |
---|
904 | struct timespec restrain; |
---|
905 | restrain.tv_sec = 0; |
---|
906 | /* funky math to make sure a long can hold it all, calulate in ms */ |
---|
907 | restrain.tv_nsec = (long) esd_buf_size_samples * 1000L |
---|
908 | / (long) esd_audio_rate / 4L; /* divide by two for stereo */ |
---|
909 | restrain.tv_nsec *= 1000000L; /* convert to nanoseconds */ |
---|
910 | nanosleep( &restrain, NULL ); |
---|
911 | #else |
---|
912 | struct timeval restrain; |
---|
913 | restrain.tv_sec = 0; |
---|
914 | /* funky math to make sure a long can hold it all, calulate in ms */ |
---|
915 | restrain.tv_usec = (long) esd_buf_size_samples * 1000L |
---|
916 | / (long) esd_audio_rate / 4L; /* divide by two for stereo */ |
---|
917 | restrain.tv_usec *= 1000L; /* convert to microseconds */ |
---|
918 | select( 0, 0, 0, 0, &restrain ); |
---|
919 | #endif |
---|
920 | } |
---|
921 | } /* while ( 1 ) */ |
---|
922 | |
---|
923 | /* how we'd get here, i have no idea, should only exit on signal */ |
---|
924 | clean_exit( -1 ); |
---|
925 | exit( 0 ); |
---|
926 | } |
---|