1 /*
2  * speechd.c - Speech Dispatcher server program
3  *
4  * Copyright (C) 2001, 2002, 2003, 2006, 2007 Brailcom, o.p.s.
5  *
6  * This is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * This software is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
18  *
19  * $Id: speechd.c,v 1.81 2008-07-10 15:36:49 hanke Exp $
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include <glib.h>
27 #include <gmodule.h>
28 #include <glib/gstdio.h>
29 #include <sys/stat.h>
30 #include <sys/socket.h>
31 #include <sys/un.h>
32 
33 #ifdef HAVE_SYS_FILIO_H
34 #include <sys/filio.h>	/* Needed for FIONREAD on Solaris */
35 #endif
36 
37 #include "speechd.h"
38 
39 /* Declare dotconf functions and data structures*/
40 #include "configuration.h"
41 
42 /* Declare functions to allocate and create important data
43  * structures */
44 #include "alloc.h"
45 #include "sem_functions.h"
46 #include "speaking.h"
47 #include "set.h"
48 #include "options.h"
49 #include "server.h"
50 
51 #include <i18n.h>
52 
53 /* list of output modules */
54 GList *output_modules;
55 
56 /* Manipulating pid files */
57 int create_pid_file();
58 void destroy_pid_file();
59 
60 /* Server socket file descriptor */
61 int server_socket;
62 
63 GMainLoop *main_loop = NULL;
64 gint server_timeout_source = -1;
65 
66 int client_count = 0;
67 
68 struct SpeechdOptions SpeechdOptions;
69 struct SpeechdStatus SpeechdStatus;
70 
71 pthread_t speak_thread;
72 pthread_mutex_t logging_mutex;
73 pthread_mutex_t element_free_mutex;
74 pthread_mutex_t output_layer_mutex;
75 pthread_mutex_t socket_com_mutex;
76 
77 GHashTable *fd_settings;
78 GHashTable *language_default_modules;
79 GHashTable *fd_uid;
80 
81 TSpeechDQueue *MessageQueue;
82 GList *MessagePausedList;
83 
84 GList *client_specific_settings;
85 
86 GList *last_p5_block;
87 
88 TFDSetElement GlobalFDSet;
89 
90 int speaking_pipe[2];
91 
92 GHashTable *speechd_sockets_status;
93 
94 FILE *logfile;
95 FILE *custom_logfile;
96 char *custom_log_kind;
97 FILE *debug_logfile;
98 
99 TSpeechDMode spd_mode;
100 
101 static gboolean speechd_client_terminate(gpointer key, gpointer value, gpointer user);
102 static gboolean speechd_reload_dead_modules(gpointer user_data);
103 static gboolean speechd_load_configuration(gpointer user_data);
104 static gboolean speechd_quit(gpointer user_data);
105 
106 static gboolean server_process_incoming (gint          fd,
107 				  GIOCondition  condition,
108 				  gpointer      data);
109 
110 static gboolean client_process_incoming (gint          fd,
111 				  GIOCondition  condition,
112 				  gpointer      data);
113 
114 void check_client_count(void);
115 
116 #ifndef HAVE_DAEMON
117 /* Added by Willie Walker - daemon is a common, but not universal, extension.
118  */
daemon(int nochdir,int noclose)119 static int daemon(int nochdir, int noclose)
120 {
121 	int fd, i;
122 
123 	switch (fork()) {
124 	case 0:
125 		break;
126 	case -1:
127 		return -1;
128 	default:
129 		_exit(0);
130 	}
131 
132 	if (!nochdir) {
133 		chdir("/");
134 	}
135 
136 	if (setsid() < 0) {
137 		return -1;
138 	}
139 
140 	if (!noclose) {
141 		if (fd = open("/dev/null", O_RDWR) >= 0) {
142 			for (i = 0; i < 3; i++) {
143 				dup2(fd, i);
144 			}
145 			if (fd > 2) {
146 				close(fd);
147 			}
148 		}
149 	}
150 	return 0;
151 }
152 #endif /* HAVE_DAEMON */
153 
154 /* --- DEBUGGING --- */
155 
156 /* Just to be able to set breakpoints */
fatal_error(void)157 void fatal_error(void)
158 {
159 	int i = 0;
160 	i++;
161 }
162 
163 /* Logging messages, level of verbosity is defined between 1 and 5,
164  * see documentation */
MSG2(int level,char * kind,char * format,...)165 void MSG2(int level, char *kind, char *format, ...)
166 {
167 	int std_log = level <= SpeechdOptions.log_level;
168 	int custom_log = (kind != NULL && custom_log_kind != NULL &&
169 			  !strcmp(kind, custom_log_kind) &&
170 			  custom_logfile != NULL);
171 
172 	if (std_log || custom_log) {
173 		va_list args;
174 		int i;
175 
176 		pthread_mutex_lock(&logging_mutex);
177 
178 		{
179 			{
180 				/* Print timestamp */
181 				time_t t;
182 				char *tstr;
183 				struct timeval tv;
184 				t = time(NULL);
185 				tstr = g_strdup(ctime(&t));
186 				gettimeofday(&tv, NULL);
187 				assert(tstr);
188 				/* Remove the trailing \n */
189 				assert(strlen(tstr) > 1);
190 				tstr[strlen(tstr) - 1] = 0;
191 				if (std_log) {
192 					fprintf(logfile, "[%s : %d] speechd: ",
193 						tstr, (int)tv.tv_usec);
194 					//            fprintf(logfile, "[test : %d] speechd: ",
195 					//                     (int) tv.tv_usec);
196 				}
197 				if (custom_log) {
198 					fprintf(custom_logfile,
199 						"[%s : %d] speechd: ", tstr,
200 						(int)tv.tv_usec);
201 				}
202 				if (SpeechdOptions.debug) {
203 					fprintf(debug_logfile,
204 						"[%s : %d] speechd: ", tstr,
205 						(int)tv.tv_usec);
206 				}
207 				g_free(tstr);
208 			}
209 			for (i = 1; i < level; i++) {
210 				if (std_log) {
211 					fprintf(logfile, " ");
212 				}
213 				if (custom_log) {
214 					fprintf(custom_logfile, " ");
215 				}
216 			}
217 			if (std_log) {
218 				va_start(args, format);
219 				vfprintf(logfile, format, args);
220 				va_end(args);
221 				fprintf(logfile, "\n");
222 				fflush(logfile);
223 			}
224 			if (custom_log) {
225 				va_start(args, format);
226 				vfprintf(custom_logfile, format, args);
227 				va_end(args);
228 				fprintf(custom_logfile, "\n");
229 				fflush(custom_logfile);
230 			}
231 			if (SpeechdOptions.debug) {
232 				va_start(args, format);
233 				vfprintf(debug_logfile, format, args);
234 				va_end(args);
235 				fprintf(debug_logfile, "\n");
236 				fflush(debug_logfile);
237 			}
238 		}
239 		pthread_mutex_unlock(&logging_mutex);
240 	}
241 }
242 
243 /* The main logging function for Speech Dispatcher,
244    level is between -1 and 5. 1 means the most important,
245    5 less important. Loglevels after 4 can contain private
246    data. -1 logs also to stderr. See Speech Dispatcher
247    documentation */
248 /* TODO: Define this in terms of MSG somehow. I don't
249    know how to pass '...' arguments to another C function */
MSG(int level,char * format,...)250 void MSG(int level, char *format, ...)
251 {
252 
253 	if ((level <= SpeechdOptions.log_level)
254 	    || (SpeechdOptions.debug)) {
255 		va_list args;
256 		int i;
257 		pthread_mutex_lock(&logging_mutex);
258 		{
259 			/* Print timestamp */
260 			{
261 				time_t t;
262 				char *tstr;
263 				struct timeval tv;
264 				t = time(NULL);
265 				tstr = g_strdup(ctime(&t));
266 				gettimeofday(&tv, NULL);
267 				/* Remove the trailing \n */
268 				assert(tstr);
269 				assert(strlen(tstr) > 1);
270 				assert((level >= -1) && (level <= 5));
271 				tstr[strlen(tstr) - 1] = 0;
272 				/* Write timestamps */
273 				if (level <= SpeechdOptions.log_level)
274 					fprintf(logfile, "[%s : %d] speechd: ",
275 						tstr, (int)tv.tv_usec);
276 				if (SpeechdOptions.debug)
277 					fprintf(debug_logfile,
278 						"[%s : %d] speechd: ", tstr,
279 						(int)tv.tv_usec);
280 				/*                fprintf(logfile, "[%s : %d] speechd: ",
281 				   tstr, (int) tv.tv_usec); */
282 				g_free(tstr);
283 			}
284 
285 			for (i = 1; i < level; i++) {
286 				fprintf(logfile, " ");
287 			}
288 			/* Log to ordinary logfile */
289 			if (level <= SpeechdOptions.log_level) {
290 				va_start(args, format);
291 				vfprintf(logfile, format, args);
292 				va_end(args);
293 				fprintf(logfile, "\n");
294 				fflush(logfile);
295 			}
296 			/* Log into debug logfile */
297 			if (SpeechdOptions.debug) {
298 				va_start(args, format);
299 				vfprintf(debug_logfile, format, args);
300 				va_end(args);
301 				fprintf(debug_logfile, "\n");
302 				fflush(debug_logfile);
303 			}
304 			/* Log also into stderr for loglevel -1 */
305 			if (level == -1) {
306 				va_start(args, format);
307 				vfprintf(stderr, format, args);
308 				va_end(args);
309 				fprintf(stderr, "\n");
310 				fflush(stderr);
311 			}
312 		}
313 		pthread_mutex_unlock(&logging_mutex);
314 	}
315 }
316 
317 /* --- CLIENTS / CONNECTIONS MANAGING --- */
318 
319 /* Initialize sockets status table */
speechd_sockets_status_init(void)320 int speechd_sockets_status_init(void)
321 {
322 	speechd_sockets_status = g_hash_table_new_full(g_int_hash, g_int_equal,
323 						       (GDestroyNotify) g_free,
324 						       (GDestroyNotify)
325 						       speechd_socket_free);
326 	if (speechd_sockets_status)
327 		return 0;
328 	else
329 		return 1;
330 }
331 
332 /* Register a new socket for SSIP connection */
speechd_socket_register(int fd)333 int speechd_socket_register(int fd)
334 {
335 	int *fd_key;
336 	TSpeechDSock *speechd_socket;
337 	speechd_socket = g_malloc(sizeof(TSpeechDSock));
338 	speechd_socket->o_buf = NULL;
339 	speechd_socket->o_bytes = 0;
340 	speechd_socket->awaiting_data = 0;
341 	speechd_socket->inside_block = 0;
342 	fd_key = g_malloc(sizeof(int));
343 	*fd_key = fd;
344 	g_hash_table_insert(speechd_sockets_status, fd_key, speechd_socket);
345 	return 0;
346 }
347 
348 /* Free a TSpeechDSock structure including it's data */
speechd_socket_free(TSpeechDSock * speechd_socket)349 void speechd_socket_free(TSpeechDSock * speechd_socket)
350 {
351 	if (speechd_socket->o_buf)
352 		g_string_free(speechd_socket->o_buf, 1);
353 	g_free(speechd_socket);
354 }
355 
356 /* Unregister a socket for SSIP communication */
speechd_socket_unregister(int fd)357 int speechd_socket_unregister(int fd)
358 {
359 	return (!g_hash_table_remove(speechd_sockets_status, &fd));
360 }
361 
362 /* Get a pointer to the TSpeechDSock structure for a given file descriptor */
speechd_socket_get_by_fd(int fd)363 TSpeechDSock *speechd_socket_get_by_fd(int fd)
364 {
365 	return g_hash_table_lookup(speechd_sockets_status, &fd);
366 }
367 
368 /* activity is on server_socket (request for a new connection) */
speechd_connection_new(int server_socket)369 int speechd_connection_new(int server_socket)
370 {
371 	TFDSetElement *new_fd_set;
372 	struct sockaddr_in client_address;
373 	unsigned int client_len = sizeof(client_address);
374 	int client_socket;
375 	int *p_client_socket, *p_client_uid, *p_client_uid2;
376 
377 	client_socket =
378 	    accept(server_socket, (struct sockaddr *)&client_address,
379 		   &client_len);
380 
381 	if (client_socket == -1) {
382 		MSG(2,
383 		    "Error: Can't handle connection request of a new client");
384 		return -1;
385 	}
386 
387 	/* We add the associated client_socket to the descriptor set. */
388 	if (client_socket > SpeechdStatus.max_fd)
389 		SpeechdStatus.max_fd = client_socket;
390 	MSG(4, "Adding client on fd %d", client_socket);
391 
392 	speechd_socket_register(client_socket);
393 
394 	/* Create a record in fd_settings */
395 	new_fd_set = (TFDSetElement *) default_fd_set();
396 	if (new_fd_set == NULL) {
397 		MSG(2,
398 		    "Error: Failed to create a record in fd_settings for the new client");
399 		if (SpeechdStatus.max_fd == client_socket)
400 			SpeechdStatus.max_fd--;
401 		return -1;
402 	}
403 	new_fd_set->fd = client_socket;
404 	new_fd_set->uid = ++SpeechdStatus.max_uid;
405 	p_client_socket = (int *)g_malloc(sizeof(int));
406 	p_client_uid = (int *)g_malloc(sizeof(int));
407 	p_client_uid2 = (int *)g_malloc(sizeof(int));
408 	*p_client_socket = client_socket;
409 	*p_client_uid = SpeechdStatus.max_uid;
410 	*p_client_uid2 = SpeechdStatus.max_uid;
411 
412 	g_hash_table_insert(fd_settings, p_client_uid, new_fd_set);
413 	g_hash_table_insert(fd_uid, p_client_socket, p_client_uid2);
414 
415 	new_fd_set->fd_source = g_unix_fd_add(client_socket, G_IO_IN, client_process_incoming, NULL);
416 
417 	MSG(4, "Data structures for client on fd %d created", client_socket);
418 
419 	client_count++;
420 	check_client_count();
421 
422 	return 0;
423 
424 }
425 
speechd_connection_destroy(int fd)426 int speechd_connection_destroy(int fd)
427 {
428 	TFDSetElement *fdset_element;
429 
430 	/* Client has gone away and we remove it from the descriptor set. */
431 	MSG(4, "Removing client on fd %d", fd);
432 
433 	MSG(4, "Tagging client as inactive in settings");
434 	fdset_element = get_client_settings_by_fd(fd);
435 	if (fdset_element != NULL) {
436 		fdset_element->fd = -1;
437 		fdset_element->active = 0;
438 		g_source_remove(fdset_element->fd_source);
439 		/* The fdset_element will be freed and removed from the
440 		   hash table as soon as the client no longer has any
441 		   message in the queues, check out the speak() function */
442 	} else if (SPEECHD_DEBUG) {
443 		DIE("Can't find settings for this client\n");
444 	}
445 
446 	MSG(4, "Removing client from the fd->uid table.");
447 
448 	g_hash_table_remove(fd_uid, &fd);
449 
450 	speechd_socket_unregister(fd);
451 
452 	MSG(4, "Closing clients file descriptor %d", fd);
453 
454 	if (close(fd) != 0)
455 		if (SPEECHD_DEBUG)
456 			DIE("Can't close file descriptor associated to this client");
457 
458 	if (fd == SpeechdStatus.max_fd)
459 		SpeechdStatus.max_fd--;
460 
461 	MSG(4, "Connection closed");
462 
463 	client_count--;
464 	check_client_count();
465 
466 	return 0;
467 }
468 
speechd_client_terminate(gpointer key,gpointer value,gpointer user)469 static gboolean speechd_client_terminate(gpointer key, gpointer value, gpointer user)
470 {
471 	TFDSetElement *set;
472 
473 	set = (TFDSetElement *) value;
474 	if (set == NULL) {
475 		MSG(2, "Error: Empty connection, internal error");
476 		if (SPEECHD_DEBUG)
477 			FATAL("Internal error");
478 		return TRUE;
479 	}
480 
481 	if (set->fd > 0) {
482 		MSG(4, "Closing connection on fd %d\n", set->fd);
483 		speechd_connection_destroy(set->fd);
484 	}
485 	mem_free_fdset(set);
486 	g_free(set);
487 	return TRUE;
488 }
489 
490 /* --- OUTPUT MODULES MANAGING --- */
491 
speechd_modules_terminate(gpointer data,gpointer user_data)492 void speechd_modules_terminate(gpointer data, gpointer user_data)
493 {
494 	OutputModule *module;
495 
496 	module = (OutputModule *) data;
497 	if (module == NULL) {
498 		MSG(2, "Error: Empty module, internal error");
499 		return;
500 	}
501 	unload_output_module(module);
502 
503 	return;
504 }
505 
speechd_modules_reload(gpointer data,gpointer user_data)506 void speechd_modules_reload(gpointer data, gpointer user_data)
507 {
508 	OutputModule *module;
509 
510 	module = (OutputModule *) data;
511 	if (module == NULL) {
512 		MSG(2, "Empty module, internal error");
513 		return;
514 	}
515 
516 	reload_output_module(module);
517 
518 	return;
519 }
520 
speechd_module_debug(gpointer data,gpointer user_data)521 void speechd_module_debug(gpointer data, gpointer user_data)
522 {
523 	OutputModule *module;
524 
525 	module = (OutputModule *) data;
526 	if (module == NULL) {
527 		MSG(2, "Empty module, internal error");
528 		return;
529 	}
530 
531 	output_module_debug(module);
532 
533 	return;
534 }
535 
speechd_module_nodebug(gpointer data,gpointer user_data)536 void speechd_module_nodebug(gpointer data, gpointer user_data)
537 {
538 	OutputModule *module;
539 
540 	module = (OutputModule *) data;
541 	if (module == NULL) {
542 		MSG(2, "Empty module, internal error");
543 		return;
544 	}
545 
546 	output_module_nodebug(module);
547 
548 	return;
549 }
550 
speechd_reload_dead_modules(gpointer user_data)551 static gboolean speechd_reload_dead_modules(gpointer user_data)
552 {
553 	/* Reload dead modules */
554 	g_list_foreach(output_modules, speechd_modules_reload, NULL);
555 
556 	/* Make sure there aren't any more child processes left */
557 	while (waitpid(-1, NULL, WNOHANG) > 0) ;
558 	return TRUE;
559 }
560 
speechd_modules_debug(void)561 void speechd_modules_debug(void)
562 {
563 	/* Redirect output to debug for all modules */
564 	g_list_foreach(output_modules, speechd_module_debug, NULL);
565 
566 }
567 
speechd_modules_nodebug(void)568 void speechd_modules_nodebug(void)
569 {
570 	/* Redirect output to normal for all modules */
571 	g_list_foreach(output_modules, speechd_module_nodebug, NULL);
572 }
573 
574 /* --- SPEECHD START/EXIT FUNCTIONS --- */
575 
speechd_options_init(void)576 void speechd_options_init(void)
577 {
578 	SpeechdOptions.spawn = FALSE;
579 	SpeechdOptions.log_level_set = 0;
580 	SpeechdOptions.communication_method = NULL;
581 	SpeechdOptions.socket_path = NULL;
582 	SpeechdOptions.port_set = 0;
583 	SpeechdOptions.localhost_access_only_set = 0;
584 	SpeechdOptions.pid_file = NULL;
585 	SpeechdOptions.conf_file = NULL;
586 	SpeechdOptions.module_dir = MODULEBINDIR;
587 	SpeechdOptions.runtime_speechd_dir = NULL;
588 	SpeechdOptions.log_dir = NULL;
589 	SpeechdOptions.log_dir_set = 0;
590 	SpeechdOptions.debug = 0;
591 	SpeechdOptions.debug_destination = NULL;
592 	debug_logfile = NULL;
593 	spd_mode = SPD_MODE_DAEMON;
594 }
595 
speechd_init()596 void speechd_init()
597 {
598 	int ret;
599 
600 	SpeechdStatus.max_uid = 0;
601 	SpeechdStatus.max_gid = 0;
602 
603 	/* Initialize inter-thread comm pipe */
604 	if (pipe(speaking_pipe)) {
605 		MSG(1, "Speaking pipe creation failed (%s)", strerror(errno));
606 		FATAL("Can't create pipe");
607 	}
608 
609 	/* Initialize Speech Dispatcher priority queue */
610 	MessageQueue = g_malloc0(sizeof(TSpeechDQueue));
611 	if (MessageQueue == NULL)
612 		FATAL("Couldn't allocate memory for MessageQueue.");
613 
614 	/* Initialize lists */
615 	MessagePausedList = NULL;
616 
617 	/* Initialize hash tables */
618 	fd_settings = g_hash_table_new_full(g_int_hash, g_int_equal,
619 					    (GDestroyNotify) g_free, NULL);
620 	assert(fd_settings != NULL);
621 
622 	fd_uid = g_hash_table_new_full(g_int_hash, g_int_equal,
623 				       (GDestroyNotify) g_free,
624 				       (GDestroyNotify) g_free);
625 	assert(fd_uid != NULL);
626 
627 	language_default_modules = g_hash_table_new(g_str_hash, g_str_equal);
628 	assert(language_default_modules != NULL);
629 
630 	speechd_sockets_status_init();
631 
632 	pause_requested = 0;
633 	resume_requested = 0;
634 
635 	/* Perform some functionality tests */
636 	if (g_module_supported() == FALSE)
637 		DIE("Loadable modules not supported by current platform.\n");
638 
639 	if (_POSIX_VERSION < 199506L)
640 		DIE("This system doesn't support POSIX.1c threads\n");
641 
642 	/* Fill GlobalFDSet with default values */
643 	GlobalFDSet.min_delay_progress = 2000;
644 
645 	/* Initialize list of different client specific settings entries */
646 	client_specific_settings = NULL;
647 
648 	/* Initialize mutexes, semaphores and synchronization */
649 	ret = pthread_mutex_init(&element_free_mutex, NULL);
650 	if (ret != 0)
651 		DIE("Mutex initialization failed");
652 
653 	ret = pthread_mutex_init(&output_layer_mutex, NULL);
654 	if (ret != 0)
655 		DIE("Mutex initialization failed");
656 
657 	ret = pthread_mutex_init(&socket_com_mutex, NULL);
658 	if (ret != 0)
659 		DIE("Mutex initialization failed");
660 
661 	if (SpeechdOptions.log_dir == NULL) {
662 		SpeechdOptions.log_dir =
663 		    g_strdup_printf("%s/log/",
664 				    SpeechdOptions.runtime_speechd_dir);
665 		mkdir(SpeechdOptions.log_dir, S_IRWXU);
666 		if (!SpeechdOptions.debug_destination) {
667 			SpeechdOptions.debug_destination =
668 			    g_strdup_printf("%s/log/debug",
669 					    SpeechdOptions.runtime_speechd_dir);
670 			mkdir(SpeechdOptions.debug_destination, S_IRWXU);
671 		}
672 	}
673 
674 	/* Load configuration from the config file */
675 	MSG(4, "Reading Speech Dispatcher configuration from %s",
676 	    SpeechdOptions.conf_file);
677 	speechd_load_configuration(NULL);
678 
679 	logging_init();
680 
681 	/* Check for output modules */
682 	if (g_list_length(output_modules) == 0) {
683 		DIE("No speech output modules were loaded - aborting...");
684 	} else {
685 		MSG(3, "Speech Dispatcher started with %d output module%s",
686 		    g_list_length(output_modules),
687 		    g_list_length(output_modules) > 1 ? "s" : "");
688 	}
689 
690 	last_p5_block = NULL;
691 }
692 
speechd_load_configuration(gpointer user_data)693 static gboolean speechd_load_configuration(gpointer user_data)
694 {
695 	configfile_t *configfile = NULL;
696 	GList *detected_modules = NULL;
697 
698 	/* Clean previous configuration */
699 	if (output_modules != NULL) {
700 		g_list_foreach(output_modules, speechd_modules_terminate, NULL);
701 		g_list_free(output_modules);
702 		output_modules = NULL;
703 	}
704 
705 	/* Make sure there aren't any more child processes left */
706 	while (waitpid(-1, NULL, WNOHANG) > 0) ;
707 
708 	/* Load new configuration */
709 	load_default_global_set_options();
710 
711 	spd_num_options = 0;
712 	spd_options = load_config_options(&spd_num_options);
713 
714 	/* Add the LAST option */
715 	spd_options = add_config_option(spd_options, &spd_num_options, "", 0,
716 					NULL, NULL, 0);
717 
718 	configfile =
719 	    dotconf_create(SpeechdOptions.conf_file, spd_options, 0,
720 			   CASE_INSENSITIVE);
721 	if (configfile) {
722 		configfile->includepath = g_strdup(SpeechdOptions.conf_dir);
723 		MSG(5, "Config file include path is: %s",
724 		    configfile->includepath);
725 		if (dotconf_command_loop(configfile) == 0)
726 			DIE("Error reading config file\n");
727 		dotconf_cleanup(configfile);
728 		MSG(2, "Configuration has been read from \"%s\"",
729 		    SpeechdOptions.conf_file);
730 
731 		/* We need to load modules here, since this is called both by speechd_init
732 		 * and to handle SIGHUP. */
733 		if (module_number_of_requested_modules() < 1) {
734 			detected_modules = detect_output_modules(SpeechdOptions.module_dir,
735 								 SpeechdOptions.conf_dir);
736 			while (detected_modules != NULL) {
737 				char **parameters = detected_modules->data;
738 				module_add_load_request(parameters[0],
739 							parameters[1],
740 							parameters[2],
741 							parameters[3]);
742 				g_free(detected_modules->data);
743 				detected_modules->data = NULL;
744 				detected_modules =
745 				    g_list_delete_link(detected_modules,
746 						       detected_modules);
747 			}
748 		}
749 
750 		module_load_requested_modules();
751 	} else {
752 		MSG(1, "Can't open %s", SpeechdOptions.conf_file);
753 	}
754 
755 	free_config_options(spd_options, &spd_num_options);
756 
757 	return TRUE;
758 }
759 
speechd_quit(gpointer user_data)760 static gboolean speechd_quit(gpointer user_data)
761 {
762 	g_main_loop_quit(main_loop);
763 	return FALSE;
764 }
765 
766 /* --- PID FILES --- */
767 
create_pid_file()768 int create_pid_file()
769 {
770 	FILE *pid_file;
771 	int pid_fd;
772 	struct flock lock;
773 	int ret;
774 
775 	/* If the file exists, examine it's lock */
776 	pid_file = fopen(SpeechdOptions.pid_file, "r");
777 	if (pid_file != NULL) {
778 		pid_fd = fileno(pid_file);
779 
780 		lock.l_type = F_WRLCK;
781 		lock.l_whence = SEEK_SET;
782 		lock.l_start = 1;
783 		lock.l_len = 3;
784 
785 		/* If there is a lock, exit, otherwise remove the old file */
786 		ret = fcntl(pid_fd, F_GETLK, &lock);
787 		if (ret == -1) {
788 			MSG(-1,
789 			    "Can't check lock status of an existing pid file.\n");
790 			return -1;
791 		}
792 
793 		fclose(pid_file);
794 		if (lock.l_type != F_UNLCK) {
795 			MSG(-1, "Speech Dispatcher already running.\n");
796 			return -1;
797 		}
798 
799 		unlink(SpeechdOptions.pid_file);
800 	}
801 
802 	/* Create a new pid file and lock it */
803 	pid_file = fopen(SpeechdOptions.pid_file, "w");
804 	if (pid_file == NULL) {
805 		MSG(-1, "Can't create pid file in %s, wrong permissions?\n",
806 		    SpeechdOptions.pid_file);
807 		return -1;
808 	}
809 	fprintf(pid_file, "%d\n", getpid());
810 	fflush(pid_file);
811 
812 	pid_fd = fileno(pid_file);
813 	lock.l_type = F_WRLCK;
814 	lock.l_whence = SEEK_SET;
815 	lock.l_start = 1;
816 	lock.l_len = 3;
817 
818 	ret = fcntl(pid_fd, F_SETLK, &lock);
819 	if (ret == -1) {
820 		MSG(-1, "Can't set lock on pid file.\n");
821 		return -1;
822 	}
823 
824 	return 0;
825 }
826 
destroy_pid_file(void)827 void destroy_pid_file(void)
828 {
829 	unlink(SpeechdOptions.pid_file);
830 }
831 
logging_init(void)832 void logging_init(void)
833 {
834 	char *file_name =
835 	    g_strdup_printf("%s/speech-dispatcher.log", SpeechdOptions.log_dir);
836 	assert(file_name != NULL);
837 	if (!strncmp(file_name, "stdout", 6)) {
838 		logfile = stdout;
839 	} else if (!strncmp(file_name, "stderr", 6)) {
840 		logfile = stderr;
841 	} else {
842 		logfile = fopen(file_name, "a");
843 		if (logfile == NULL) {
844 			fprintf(stderr,
845 				"Error: can't open logging file %s! Using stdout.\n",
846 				file_name);
847 			logfile = stdout;
848 		} else {
849 			MSG(3, "Speech Dispatcher Logging to file %s",
850 			    file_name);
851 		}
852 	}
853 
854 	if (!debug_logfile)
855 		debug_logfile = stdout;
856 
857 	g_free(file_name);
858 	return;
859 }
860 
861 /* --- Sockets --- */
862 
make_local_socket(const char * filename)863 int make_local_socket(const char *filename)
864 {
865 	struct sockaddr_un name;
866 	int sock;
867 	size_t size;
868 
869 	/* Create the socket. */
870 	sock = socket(AF_UNIX, SOCK_STREAM, 0);
871 	if (sock < 0) {
872 		FATAL("Can't create local socket");
873 	}
874 
875 	/* Bind a name to the socket. */
876 	name.sun_family = AF_UNIX;
877 	strncpy(name.sun_path, filename, sizeof(name.sun_path));
878 	name.sun_path[sizeof(name.sun_path) - 1] = '\0';
879 	size = SUN_LEN(&name);
880 
881 	if (bind(sock, (struct sockaddr *)&name, size) < 0) {
882 		FATAL("Can't bind local socket");
883 	}
884 
885 	if (listen(sock, 50) == -1) {
886 		MSG(2, "listen failed: ERRNO:%s", strerror(errno));
887 		FATAL("listen() failed for local socket");
888 	}
889 
890 	return sock;
891 }
892 
make_inet_socket(const int port)893 int make_inet_socket(const int port)
894 {
895 	struct sockaddr_in server_address;
896 	int server_socket;
897 
898 	/* Create an inet socket */
899 	server_socket = socket(AF_INET, SOCK_STREAM, 0);
900 	if (server_socket < 0) {
901 		FATAL("Can't create inet socket");
902 	}
903 
904 	/* Set REUSEADDR flag */
905 	const int flag = 1;
906 	if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &flag,
907 		       sizeof(int)))
908 		MSG(2, "Error: Setting socket option failed!");
909 
910 	server_address.sin_family = AF_INET;
911 
912 	/* Enable access only to localhost or for any address
913 	   based on LocalhostAccessOnly configuration option. */
914 	if (SpeechdOptions.localhost_access_only)
915 		server_address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
916 	else
917 		server_address.sin_addr.s_addr = htonl(INADDR_ANY);
918 
919 	server_address.sin_port = htons(port);
920 
921 	MSG(4, "Opening inet socket connection");
922 	if (bind(server_socket, (struct sockaddr *)&server_address,
923 		 sizeof(server_address)) == -1) {
924 		MSG(-1, "bind() failed: %s", strerror(errno));
925 		FATAL("Couldn't open inet socket, try a few minutes later.");
926 	}
927 
928 	if (listen(server_socket, 50) == -1) {
929 		MSG(2, "ERRNO:%s", strerror(errno));
930 		FATAL
931 		    ("listen() failed for inet socket, another Speech Dispatcher running?");
932 	}
933 
934 	return server_socket;
935 }
936 
server_process_incoming(gint fd,GIOCondition condition,gpointer data)937 gboolean server_process_incoming (gint          fd,
938 				  GIOCondition  condition,
939 				  gpointer      data)
940 {
941 	int ret;
942 
943 	ret = speechd_connection_new(fd);
944 	if (ret != 0) {
945 		MSG(2, "Error: Failed to add new client!");
946 		if (SPEECHD_DEBUG) {
947 			FATAL("Failed to add new client");
948 		}
949 	}
950 
951 	return TRUE;
952 }
953 
client_process_incoming(gint fd,GIOCondition condition,gpointer data)954 gboolean client_process_incoming (gint          fd,
955 				  GIOCondition  condition,
956 				  gpointer      data)
957 {
958 	int ret;
959 	int nread;
960 
961 	ioctl(fd, FIONREAD, &nread);
962 
963 	if (nread == 0) {
964 		/* client has gone */
965 		ret = speechd_connection_destroy(fd);
966 		if (ret != 0) {
967 			MSG(2, "Error: Failed to close the client!");
968 		}
969 		return FALSE;
970 	}
971 
972 	/* client sends some commands or data */
973 	if (serve(fd) == -1) {
974 		MSG(2, "Error: Failed to serve client on fd %d!", fd);
975 	}
976 
977 	return TRUE;
978 }
979 
check_client_count(void)980 void check_client_count(void)
981 {
982 	if (client_count <= 0
983 	    && SpeechdOptions.server_timeout > 0) {
984 		MSG(4, "Currently no clients connected, enabling shutdown timer.");
985 		server_timeout_source = g_timeout_add_seconds(
986 		                        SpeechdOptions.server_timeout,
987 		                        speechd_quit, NULL);
988 	} else {
989 		if (server_timeout_source >= 0) {
990 			MSG(4, "Clients connected, disabling shutdown timer.");
991 			g_source_remove(server_timeout_source);
992 			server_timeout_source = -1;
993 		}
994 	}
995 }
996 
997 /* --- MAIN --- */
998 
main(int argc,char * argv[])999 int main(int argc, char *argv[])
1000 {
1001 	int ret;
1002 	/* Autospawn helper variables */
1003 	char *spawn_communication_method = NULL;
1004 	int spawn_port = 0;
1005 	char *spawn_socket_path = NULL;
1006 
1007 	/* Strip all permisions for 'others' from the files created */
1008 	umask(007);
1009 
1010 	/* Initialize logging */
1011 	logfile = stdout;
1012 	SpeechdOptions.log_level = 1;
1013 	custom_logfile = NULL;
1014 	custom_log_kind = NULL;
1015 
1016 	/* initialize i18n support */
1017 	i18n_init();
1018 
1019 	speechd_options_init();
1020 
1021 	options_parse(argc, argv);
1022 
1023 	if (SpeechdOptions.spawn) {
1024 		/* In case of --spawn, copy the host port and socket_path
1025 		   parameters into temporary spawn_ variables for later comparison
1026 		   with the config file and unset them */
1027 		if (SpeechdOptions.communication_method_set) {
1028 			spawn_communication_method =
1029 			    g_strdup(SpeechdOptions.communication_method);
1030 			g_free(SpeechdOptions.communication_method);
1031 			SpeechdOptions.communication_method_set = 0;
1032 		}
1033 		if (SpeechdOptions.port_set) {
1034 			spawn_port = SpeechdOptions.port;
1035 			SpeechdOptions.port_set = 0;
1036 		}
1037 		if (SpeechdOptions.socket_path_set) {
1038 			spawn_socket_path =
1039 			    g_strdup(SpeechdOptions.socket_path);
1040 			g_free(SpeechdOptions.socket_path);
1041 			SpeechdOptions.socket_path_set = 0;
1042 		}
1043 	}
1044 
1045 	MSG(1, "Speech Dispatcher " VERSION " starting");
1046 
1047 	/* By default, search for configuration options in $XDG_CONFIG_HOME/speech-dispatcher
1048 	   and sockets and pid files in $XDG_RUNTIME_DIR/speech-dispatcher */
1049 	{
1050 		const char *user_runtime_dir;
1051 		const char *user_config_dir;
1052 		char *test_speechd_conf_file = NULL;
1053 
1054 		user_runtime_dir = g_get_user_runtime_dir();
1055 		user_config_dir = g_get_user_config_dir();
1056 
1057 		/* Setup a speechd-dispatcher directory or create a new one */
1058 		SpeechdOptions.runtime_speechd_dir =
1059 		    g_strdup_printf("%s/speech-dispatcher", user_runtime_dir);
1060 		MSG(4, "Trying to find %s", SpeechdOptions.runtime_speechd_dir);
1061 		g_mkdir_with_parents(SpeechdOptions.runtime_speechd_dir,
1062 				     S_IRWXU);
1063 		MSG(4, "Using directory: %s for pidfile and logging",
1064 		    SpeechdOptions.runtime_speechd_dir);
1065 		/* Pidfile */
1066 		if (SpeechdOptions.pid_file == NULL) {
1067 			/* If no pidfile path specified on command line, use default local dir */
1068 			SpeechdOptions.pid_file =
1069 			    g_strdup_printf("%s/pid/speech-dispatcher.pid",
1070 					    SpeechdOptions.runtime_speechd_dir);
1071 			g_mkdir(g_path_get_dirname(SpeechdOptions.pid_file),
1072 				S_IRWXU);
1073 		}
1074 		/* Config file */
1075 		if (SpeechdOptions.conf_dir == NULL) {
1076 			/* If no conf_dir was specified on command line, try default local config dir */
1077 			SpeechdOptions.conf_dir =
1078 			    g_build_filename(user_config_dir,
1079 					     "speech-dispatcher", NULL);
1080 			test_speechd_conf_file =
1081 			    g_build_filename(SpeechdOptions.conf_dir,
1082 					     "speechd.conf", NULL);
1083 			if (!g_file_test
1084 			    (test_speechd_conf_file, G_FILE_TEST_IS_REGULAR)) {
1085 				/* If the local configuration file doesn't exist, read the global configuration */
1086 				if (strcmp(SYS_CONF, ""))
1087 					SpeechdOptions.conf_dir =
1088 					    g_strdup(SYS_CONF);
1089 				else
1090 					SpeechdOptions.conf_dir =
1091 					    g_strdup("/etc/speech-dispatcher/");
1092 			}
1093 			g_free(test_speechd_conf_file);
1094 		}
1095 		SpeechdOptions.conf_file =
1096 		    g_strdup_printf("%s/speechd.conf", SpeechdOptions.conf_dir);
1097 	}
1098 
1099 	/* Check for PID file or create a new one or exit if Speech Dispatcher
1100 	   is already running */
1101 	if (create_pid_file() != 0)
1102 		exit(1);
1103 
1104 	/* Handle --spawn request */
1105 	if (SpeechdOptions.spawn) {
1106 		/* Check whether spawning is not disabled */
1107 		gchar *config_contents;
1108 		int err;
1109 		GRegex *regexp;
1110 		int result;
1111 
1112 		err =
1113 		    g_file_get_contents(SpeechdOptions.conf_file,
1114 					&config_contents, NULL, NULL);
1115 		if (err == FALSE) {
1116 			MSG(-1, "Error opening %s", SpeechdOptions.conf_file);
1117 			FATAL("Can't open conf file");
1118 		}
1119 		regexp =
1120 		    g_regex_new("^[ ]*DisableAutoSpawn", G_REGEX_MULTILINE, 0,
1121 				NULL);
1122 		result = g_regex_match(regexp, config_contents, 0, NULL);
1123 		if (result) {
1124 			MSG(-1,
1125 			    "Autospawn requested but disabled in configuration");
1126 			exit(1);
1127 		}
1128 		g_free(config_contents);
1129 		g_regex_unref(regexp);
1130 		MSG(2, "Starting Speech Dispatcher due to auto-spawn");
1131 	}
1132 
1133 	/* Initialize logging mutex to workaround ctime threading bug */
1134 	/* Must be done no later than here */
1135 	ret = pthread_mutex_init(&logging_mutex, NULL);
1136 	if (ret != 0) {
1137 		fprintf(stderr, "Mutex initialization failed");
1138 		exit(1);
1139 	}
1140 
1141 	speechd_init();
1142 
1143 	/* Handle socket_path 'default' */
1144 	// TODO: This is a hack, we should do that at appropriate places...
1145 	if (!strcmp(SpeechdOptions.socket_path, "default")) {
1146 		/* This code cannot be moved above next to conf_dir and pidpath resolution because
1147 		   we need to also consider the DotConf configuration, which is read in speechd_init() */
1148 		GString *socket_filename;
1149 		socket_filename = g_string_new("");
1150 		if (SpeechdOptions.runtime_speechd_dir) {
1151 			g_string_printf(socket_filename, "%s/speechd.sock",
1152 					SpeechdOptions.runtime_speechd_dir);
1153 		} else {
1154 			FATAL
1155 			    ("Socket name file not set and user has no runtime directory");
1156 		}
1157 		g_free(SpeechdOptions.socket_path);
1158 		SpeechdOptions.socket_path = g_strdup(socket_filename->str);
1159 		g_string_free(socket_filename, 1);
1160 	}
1161 
1162 	/* Check if the communication method corresponds to the spawn request */
1163 	/* TODO: This should preferably be done much sooner, but the current
1164 	   configuration mechanism doesn't allow it */
1165 	if (SpeechdOptions.spawn) {
1166 		if (spawn_communication_method) {
1167 			if (strcmp
1168 			    (spawn_communication_method,
1169 			     SpeechdOptions.communication_method)) {
1170 				MSG(-1,
1171 				    "Autospawn failed: Mismatch in communication methods. Client "
1172 				    "requests %s, most probably due to its configuration or the value of "
1173 				    "the SPEECHD_ADDRESS environment variable, but the server is configured "
1174 				    "to provide the %s method.",
1175 				    spawn_communication_method,
1176 				    SpeechdOptions.communication_method);
1177 				exit(1);
1178 			} else {
1179 				if (!strcmp
1180 				    (SpeechdOptions.communication_method,
1181 				     "inet_socket")) {
1182 					/* Check port */
1183 					if (spawn_port != 0)
1184 						if (spawn_port !=
1185 						    SpeechdOptions.port) {
1186 							MSG(-1,
1187 							    "Autospawn failed: Mismatch in port numbers. Server "
1188 							    "is configured to use the inet_socket method on the port %d "
1189 							    "while the client requests port %d, most probably due to its "
1190 							    "configuration or the value of the SPEECHD_ADDRESS environment "
1191 							    "variable.",
1192 							    SpeechdOptions.port,
1193 							    spawn_port);
1194 							exit(1);
1195 						}
1196 				} else if (!strcmp
1197 					   (SpeechdOptions.communication_method,
1198 					    "unix_socket")) {
1199 					/* Check socket name */
1200 					if (spawn_socket_path)
1201 						if (strcmp
1202 						    (spawn_socket_path,
1203 						     SpeechdOptions.socket_path))
1204 						{
1205 							MSG(-1,
1206 							    "Autospawn failed: Mismatch in socket names. The server "
1207 							    "is configured to provide a socket interface in %s, but the "
1208 							    "client requests a different path: %s. This is most probably "
1209 							    "due to the client application configuration or the value of "
1210 							    "the SPEECHD_ADDRESS environment variable.",
1211 							    SpeechdOptions.socket_path,
1212 							    spawn_socket_path);
1213 							exit(1);
1214 						}
1215 				} else
1216 					assert(0);
1217 			}
1218 		}
1219 		g_free(spawn_communication_method);
1220 		g_free(spawn_socket_path);
1221 	}
1222 
1223 	if (!strcmp(SpeechdOptions.communication_method, "inet_socket")) {
1224 		MSG(4, "Speech Dispatcher will use inet port %d",
1225 		    SpeechdOptions.port);
1226 		/* Connect and start listening on inet socket */
1227 		server_socket = make_inet_socket(SpeechdOptions.port);
1228 	} else if (!strcmp(SpeechdOptions.communication_method, "unix_socket")) {
1229 		/* Determine appropariate socket file name */
1230 		MSG(4, "Speech Dispatcher will use local unix socket: %s",
1231 		    SpeechdOptions.socket_path);
1232 		/* Delete an old socket file if it exists */
1233 		if (g_file_test(SpeechdOptions.socket_path, G_FILE_TEST_EXISTS))
1234 			if (g_unlink(SpeechdOptions.socket_path) == -1)
1235 				FATAL
1236 				    ("Local socket file exists but impossible to delete. Wrong permissions?");
1237 		/* Connect and start listening on local unix socket */
1238 		server_socket = make_local_socket(SpeechdOptions.socket_path);
1239 	} else {
1240 		FATAL("Unknown communication method");
1241 	}
1242 
1243 	/* Fork, set uid, chdir, etc. */
1244 	if (spd_mode == SPD_MODE_DAEMON) {
1245 		if (daemon(0, 0)) {
1246 			FATAL("Can't fork child process");
1247 		}
1248 		/* Re-create the pid file under this process */
1249 		unlink(SpeechdOptions.pid_file);
1250 		if (create_pid_file() == -1)
1251 			return -1;
1252 	}
1253 
1254 	/* Set up the main loop and register signals */
1255         main_loop = g_main_loop_new(g_main_context_default(), FALSE);
1256 	g_unix_signal_add(SIGINT, speechd_quit, NULL);
1257 	g_unix_signal_add(SIGTERM, speechd_quit, NULL);
1258 	g_unix_signal_add(SIGHUP, speechd_load_configuration, NULL);
1259 	g_unix_signal_add(SIGUSR1, speechd_reload_dead_modules, NULL);
1260 	(void)signal(SIGPIPE, SIG_IGN);
1261 
1262 	MSG(4, "Creating new thread for speak()");
1263 	ret = pthread_create(&speak_thread, NULL, speak, NULL);
1264 	if (ret != 0)
1265 		FATAL("Speak thread failed!\n");
1266 
1267 	SpeechdStatus.max_fd = server_socket;
1268 
1269 	g_unix_fd_add(server_socket, G_IO_IN,
1270 		      server_process_incoming, NULL);
1271 
1272 	/* Now wait for clients and requests. */
1273 	MSG(1, "Speech Dispatcher started and waiting for clients ...");
1274 
1275 	check_client_count();
1276 
1277 	g_main_loop_run(main_loop);
1278 
1279 	MSG(1, "Terminating...");
1280 
1281 	MSG(2, "Closing open connections...");
1282 	/* We will browse through all the connections and close them. */
1283 	g_hash_table_foreach_remove(fd_settings, speechd_client_terminate,
1284 				    NULL);
1285 	g_hash_table_destroy(fd_settings);
1286 
1287 	MSG(4, "Closing speak() thread...");
1288 	ret = pthread_cancel(speak_thread);
1289 	if (ret != 0)
1290 		FATAL("Speak thread failed to cancel!\n");
1291 
1292 	ret = pthread_join(speak_thread, NULL);
1293 	if (ret != 0)
1294 		FATAL("Speak thread failed to join!\n");
1295 
1296 	MSG(2, "Closing open output modules...");
1297 	/*  Call the close() function of each registered output module. */
1298 	g_list_foreach(output_modules, speechd_modules_terminate, NULL);
1299 	g_list_free(output_modules);
1300 
1301 	MSG(2, "Closing server connection...");
1302 	if (close(server_socket) == -1)
1303 		MSG(2, "close() failed: %s", strerror(errno));
1304 
1305 	MSG(4, "Removing pid file");
1306 	destroy_pid_file();
1307 
1308 	fflush(NULL);
1309 
1310 	g_main_loop_unref(main_loop);
1311 	main_loop = NULL;
1312 
1313 	MSG(2, "Speech Dispatcher terminated correctly");
1314 
1315 	exit(0);
1316 }
1317 
check_locked(pthread_mutex_t * lock)1318 void check_locked(pthread_mutex_t * lock)
1319 {
1320 	if (pthread_mutex_trylock(lock) == 0) {
1321 		MSG(1,
1322 		    "CRITICAL ERROR: Not locked but accessing structure data!");
1323 		fprintf(stderr, "WARNING! WARNING! MUTEX CHECK FAILED!\n");
1324 		fflush(stderr);
1325 		exit(0);
1326 	}
1327 }
1328