1 /*
2 * output.c - Output layer for Speech Dispatcher
3 *
4 * Copyright (C) 2001, 2002, 2003, 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: output.c,v 1.38 2008-06-27 12:28:48 hanke Exp $
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <fdsetconv.h>
27 #include <safe_io.h>
28 #include <spd_utils.h>
29 #include "output.h"
30 #include "parse.h"
31
32 #ifndef HAVE_STRNDUP
33 /*
34 * Added by Willie Walker - strndup was a GNU libc extension, later adopted
35 * in the POSIX.1-2008 standard, but not yet found on all systems.
36 */
strndup(const char * s,size_t n)37 char *strndup(const char *s, size_t n)
38 {
39 size_t nAvail;
40 char *p;
41
42 if (!s)
43 return 0;
44
45 if (strlen(s) > n)
46 nAvail = n + 1;
47 else
48 nAvail = strlen(s) + 1;
49 p = g_malloc(nAvail);
50 memcpy(p, s, nAvail);
51 p[nAvail - 1] = '\0';
52
53 return p;
54 }
55 #endif /* HAVE_STRNDUP */
56
output_set_speaking_monitor(TSpeechDMessage * msg,OutputModule * output)57 void output_set_speaking_monitor(TSpeechDMessage * msg, OutputModule * output)
58 {
59 /* Set the speaking-monitor so that we know who is speaking */
60 speaking_module = output;
61 speaking_uid = msg->settings.uid;
62 speaking_gid = msg->settings.reparted;
63 }
64
get_output_module_by_name(char * name)65 OutputModule *get_output_module_by_name(char *name)
66 {
67 OutputModule *output;
68 int i;
69
70 for (i = 0; i < g_list_length(output_modules); i++) {
71 output = g_list_nth_data(output_modules, i);
72 if (!strcmp(output->name, name)) {
73 if (output->working)
74 return output;
75 else
76 return NULL;
77 }
78 }
79
80 return NULL;
81 }
82
83 /* get_output_module tries to return a pointer to the
84 appropriate output module according to message context.
85 If it is not possible to find the required module,
86 it will subsequently try to get the default module,
87 any of the other remaining modules except dummy and
88 at last, the dummy output module.
89
90 Only if not even dummy output module is working
91 (serious issues), it will log an error message and return
92 a NULL pointer.
93
94 */
95
get_output_module(const TSpeechDMessage * message)96 OutputModule *get_output_module(const TSpeechDMessage * message)
97 {
98 OutputModule *output = NULL;
99 int i, len;
100
101 if (message->settings.output_module != NULL) {
102 MSG(5, "Desired output module is %s",
103 message->settings.output_module);
104 output =
105 get_output_module_by_name(message->settings.output_module);
106 if ((output != NULL) && output->working)
107 return output;
108 }
109
110 MSG(3, "Warning: Didn't find preferred output module, using default");
111 // If the requested module was not found or is not working,
112 // first try to use the default output module
113 if (GlobalFDSet.output_module != NULL)
114 output = get_output_module_by_name(GlobalFDSet.output_module);
115
116 if (output != NULL && output->working)
117 return output;
118
119 MSG(3, "Couldn't load default output module, trying other modules");
120
121 /* Try all other output modules other than dummy */
122 len = g_list_length(output_modules);
123 for (i = 0; i < len; i++) {
124 output = g_list_nth_data(output_modules, i);
125 if (0 == strcmp(output->name, "dummy"))
126 continue;
127
128 if (output->working) {
129 MSG(3, "Output module %s seems to be working, using it",
130 output->name);
131 return output;
132 }
133 }
134
135 // if we get here there are no good modules use the dummy
136 // a pre-synthesized error message with some hints over and over).
137 if (output == NULL || !output->working)
138 output = get_output_module_by_name("dummy");
139
140 // Give up....
141 if (output == NULL)
142 MSG(1,
143 "Error: No output module working, not even dummy, no sound produced!\n");
144
145 return output;
146 }
147
148 void
output_lock(void)149 static output_lock(void)
150 {
151 pthread_mutex_lock(&output_layer_mutex);
152 }
153
154 void
output_unlock(void)155 static output_unlock(void)
156 {
157 pthread_mutex_unlock(&output_layer_mutex);
158 }
159
160 #define OL_RET(value) \
161 { output_unlock(); \
162 return (value); }
163
output_read_reply(OutputModule * output)164 GString *output_read_reply(OutputModule * output)
165 {
166 GString *rstr;
167 int bytes;
168 char *line = NULL;
169 size_t N = 0;
170 gboolean errors = FALSE;
171
172 rstr = g_string_new("");
173
174 /* Wait for activity on the socket, when there is some,
175 read all the message line by line */
176 do {
177 bytes = spd_getline(&line, &N, output->stream_out);
178 if (bytes == -1) {
179 MSG(2, "Error: Broken pipe to module.");
180 output->working = 0;
181 speaking_module = NULL;
182 output_check_module(output);
183 errors = TRUE; /* Broken pipe */
184 } else {
185 MSG(5, "Got %d bytes from output module over socket",
186 bytes);
187 g_string_append(rstr, line);
188 }
189 /* terminate if we reached the last line (without '-' after numcode) */
190 } while (!errors && !((strlen(line) < 4) || (line[3] == ' ')));
191
192 if (line != NULL)
193 g_free(line);
194
195 if (errors) {
196 g_string_free(rstr, TRUE);
197 rstr = NULL;
198 }
199
200 return rstr;
201 }
202
output_send_data(char * cmd,OutputModule * output,int wfr)203 int output_send_data(char *cmd, OutputModule * output, int wfr)
204 {
205 int ret;
206 GString *response;
207
208 if (output == NULL)
209 return -1;
210 if (cmd == NULL)
211 return -1;
212
213 ret = safe_write(output->pipe_in[1], cmd, strlen(cmd));
214 fflush(NULL);
215 if (ret == -1) {
216 MSG(2, "Error: Broken pipe to module.");
217 output->working = 0;
218 speaking_module = NULL;
219 output_check_module(output);
220 return -1; /* Broken pipe */
221 }
222 MSG2(5, "output_module", "Command sent to output module: |%s| (%d)",
223 cmd, wfr);
224
225 if (wfr) { /* wait for reply? */
226 int ret = 0;
227 response = output_read_reply(output);
228 if (response == NULL)
229 return -1;
230
231 MSG2(5, "output_module", "Reply from output module: |%s|",
232 response->str);
233
234 switch (response->str[0]) {
235 case '3':
236 MSG(2,
237 "Error: Module reported error in request from speechd (code 3xx): %s.",
238 response->str);
239 ret = -2; /* User (speechd) side error */
240 break;
241
242 case '4':
243 MSG(2,
244 "Error: Module reported error in itself (code 4xx): %s",
245 response->str);
246 ret = -3; /* Module side error */
247 break;
248
249 case '2':
250 ret = 0;
251 break;
252 default: /* unknown response */
253 MSG(3, "Unknown response from output module!");
254 ret = -3;
255 break;
256 }
257 g_string_free(response, TRUE);
258 return ret;
259 }
260
261 return 0;
262 }
263
free_voice(gpointer data)264 static void free_voice(gpointer data)
265 {
266 SPDVoice *voice = (SPDVoice *)data;
267
268 if (voice != NULL) {
269 if (voice->name != NULL)
270 g_free(voice->name);
271 if (voice->language != NULL)
272 g_free(voice->language);
273 if (voice->variant != NULL)
274 g_free(voice->variant);
275
276 g_free(voice);
277 }
278 }
279
output_get_voices(OutputModule * module)280 static SPDVoice **output_get_voices(OutputModule * module)
281 {
282 SPDVoice **voice_dscr;
283 SPDVoice *voice;
284 GString *reply;
285 gchar **lines;
286 gchar **atoms;
287 GQueue *voices;
288 int i;
289 int numvoices = 0;
290 gboolean errors = FALSE;
291
292 output_lock();
293
294 if (module == NULL) {
295 MSG(1, "ERROR: Can't list voices for broken output module");
296 OL_RET(NULL);
297 }
298 output_send_data("LIST VOICES\n", module, 0);
299 reply = output_read_reply(module);
300
301 if (reply == NULL) {
302 output_unlock();
303 return NULL;
304 }
305
306 lines = g_strsplit(reply->str, "\n", -1);
307 g_string_free(reply, TRUE);
308 voices = g_queue_new();
309 for (i = 0; !errors && (lines[i] != NULL); i++) {
310 MSG(1, "LINE here:|%s|", lines[i]);
311 if (strlen(lines[i]) <= 4) {
312 MSG(1,
313 "ERROR: Bad communication from driver in synth_voices");
314 errors = TRUE;
315 } else if (lines[i][3] == ' ')
316 break;
317 else if (lines[i][3] == '-') {
318 atoms = g_strsplit(&lines[i][4], "\t", 0);
319 // Name, language, variant
320 if ((atoms[0] == NULL) || (atoms[1] == NULL)
321 || (atoms[2] == NULL)) {
322 errors = TRUE;
323 } else {
324 //Fill in VoiceDescription
325 voice = g_malloc(sizeof(SPDVoice));
326 voice->name = g_strdup(atoms[0]);
327 voice->language = g_strdup(atoms[1]);
328 voice->variant = g_strdup(atoms[2]);
329 g_queue_push_tail(voices, voice);
330 }
331 g_strfreev(atoms);
332 }
333 /* Should we do something in a final "else" branch? */
334
335 }
336
337 numvoices = g_queue_get_length(voices);
338
339 if (errors == TRUE) {
340 g_queue_free_full(voices, (GDestroyNotify)free_voice);
341 g_strfreev(lines);
342 output_unlock();
343 return NULL;
344 }
345
346 voice_dscr = g_malloc((numvoices + 1) * sizeof(SPDVoice *));
347
348 for (i = 0; i < numvoices; i++) {
349 voice_dscr[i] = g_queue_pop_head(voices);
350 }
351
352 voice_dscr[i] = NULL;
353 g_queue_free(voices);
354 g_strfreev(lines);
355
356 output_unlock();
357 return voice_dscr;
358 }
359
output_list_voices(char * module_name)360 SPDVoice **output_list_voices(char *module_name)
361 {
362 OutputModule *module;
363 if (module_name == NULL)
364 return NULL;
365 module = get_output_module_by_name(module_name);
366 if (module == NULL) {
367 MSG(1, "ERROR: Can't list voices for module %s", module_name);
368 return NULL;
369 }
370 return output_get_voices(module);
371 }
372
373 #define SEND_CMD_N(cmd) \
374 { err = output_send_data(cmd"\n", output, 1); \
375 if (err < 0) return (err); }
376
377 #define SEND_CMD(cmd) \
378 { err = output_send_data(cmd"\n", output, 1); \
379 if (err < 0) OL_RET(err)}
380
381 #define SEND_DATA_N(data) \
382 { err = output_send_data(data, output, 0); \
383 if (err < 0) return (err); }
384
385 #define SEND_DATA(data) \
386 { err = output_send_data(data, output, 0); \
387 if (err < 0) OL_RET(err); }
388
389 #define SEND_CMD_GET_VALUE(data) \
390 { err = output_send_data(data"\n", output, 1); \
391 OL_RET(err); }
392
393 #define ADD_SET_INT(name) \
394 g_string_append_printf(set_str, #name"=%d\n", msg->settings.name);
395 #define ADD_SET_STR(name) \
396 if (msg->settings.name != NULL){ \
397 g_string_append_printf(set_str, #name"=%s\n", msg->settings.name); \
398 }else{ \
399 g_string_append_printf(set_str, #name"=NULL\n"); \
400 }
401 #define ADD_SET_STR_C(name, fconv) \
402 val = fconv(msg->settings.msg_settings.name); \
403 if (val != NULL){ \
404 g_string_append_printf(set_str, #name"=%s\n", val); \
405 } \
406 g_free(val);
407
output_send_settings(TSpeechDMessage * msg,OutputModule * output)408 int output_send_settings(TSpeechDMessage * msg, OutputModule * output)
409 {
410 GString *set_str;
411 char *val;
412 int err;
413
414 MSG(4, "Module set parameters.");
415 set_str = g_string_new("");
416 g_string_append_printf(set_str, "pitch=%d\n",
417 msg->settings.msg_settings.pitch);
418 g_string_append_printf(set_str, "pitch_range=%d\n",
419 msg->settings.msg_settings.pitch_range);
420 g_string_append_printf(set_str, "rate=%d\n",
421 msg->settings.msg_settings.rate);
422 g_string_append_printf(set_str, "volume=%d\n",
423 msg->settings.msg_settings.volume);
424 ADD_SET_STR_C(punctuation_mode, EPunctMode2str);
425 ADD_SET_STR_C(spelling_mode, ESpellMode2str);
426 ADD_SET_STR_C(cap_let_recogn, ECapLetRecogn2str);
427 val = EVoice2str(msg->settings.msg_settings.voice_type);
428 if (val != NULL) {
429 g_string_append_printf(set_str, "voice=%s\n", val);
430 }
431 g_free(val);
432 if (msg->settings.msg_settings.voice.language != NULL) {
433 g_string_append_printf(set_str, "language=%s\n",
434 msg->settings.msg_settings.voice.
435 language);
436 } else {
437 g_string_append_printf(set_str, "language=NULL\n");
438 }
439 if (msg->settings.msg_settings.voice.name != NULL) {
440 g_string_append_printf(set_str, "synthesis_voice=%s\n",
441 msg->settings.msg_settings.voice.name);
442 } else {
443 g_string_append_printf(set_str, "synthesis_voice=NULL\n");
444 }
445
446 SEND_CMD_N("SET");
447 SEND_DATA_N(set_str->str);
448 SEND_CMD_N(".");
449
450 g_string_free(set_str, 1);
451
452 return 0;
453 }
454
455 #undef ADD_SET_INT
456 #undef ADD_SET_STR
457
458 #define ADD_SET_INT(name) \
459 g_string_append_printf(set_str, #name"=%d\n", GlobalFDSet.name);
460 #define ADD_SET_STR(name) \
461 if (GlobalFDSet.name != NULL){ \
462 g_string_append_printf(set_str, #name"=%s\n", GlobalFDSet.name); \
463 }else{ \
464 g_string_append_printf(set_str, #name"=NULL\n"); \
465 }
466
output_send_audio_settings(OutputModule * output)467 int output_send_audio_settings(OutputModule * output)
468 {
469 GString *set_str;
470 int err;
471
472 MSG(4, "Module set parameters.");
473 set_str = g_string_new("");
474 ADD_SET_STR(audio_output_method);
475 ADD_SET_STR(audio_oss_device);
476 ADD_SET_STR(audio_alsa_device);
477 ADD_SET_STR(audio_nas_server);
478 // TODO: restore AudioPulseServer option
479 //ADD_SET_STR(audio_pulse_server);
480 ADD_SET_STR(audio_pulse_device);
481 ADD_SET_INT(audio_pulse_min_length);
482
483 SEND_CMD_N("AUDIO");
484 SEND_DATA_N(set_str->str);
485 SEND_CMD_N(".");
486
487 g_string_free(set_str, 1);
488
489 return 0;
490 }
491
output_send_loglevel_setting(OutputModule * output)492 int output_send_loglevel_setting(OutputModule * output)
493 {
494 GString *set_str;
495 int err;
496
497 MSG(4, "Module set parameters.");
498 set_str = g_string_new("");
499 ADD_SET_INT(log_level);
500
501 SEND_CMD_N("LOGLEVEL");
502 SEND_DATA_N(set_str->str);
503 SEND_CMD_N(".");
504
505 g_string_free(set_str, 1);
506
507 return 0;
508 }
509
510 #undef ADD_SET_INT
511 #undef ADD_SET_STR
512
output_send_debug(OutputModule * output,int flag,char * log_path)513 int output_send_debug(OutputModule * output, int flag, char *log_path)
514 {
515 char *cmd_str;
516 int err;
517
518 MSG(4, "Module sending debug flag %d with file %s", flag, log_path);
519
520 output_lock();
521 if (flag) {
522 cmd_str = g_strdup_printf("DEBUG ON %s \n", log_path);
523 err = output_send_data(cmd_str, output, 1);
524 g_free(cmd_str);
525 if (err) {
526 MSG(3,
527 "ERROR: Can't set debugging on for output module %s",
528 output->name);
529 OL_RET(-1);
530 }
531 } else {
532 err = output_send_data("DEBUG OFF \n", output, 1);
533 if (err) {
534 MSG(3,
535 "ERROR: Can't switch debugging off for output module %s",
536 output->name);
537 OL_RET(-1);
538 }
539
540 }
541
542 OL_RET(0);
543 }
544
output_speak(TSpeechDMessage * msg,OutputModule * output)545 int output_speak(TSpeechDMessage * msg, OutputModule *output)
546 {
547 int err;
548 int ret;
549
550 if (msg == NULL)
551 return -1;
552
553 output_lock();
554
555 msg->buf = escape_dot(msg->buf);
556 msg->bytes = -1;
557
558 output_set_speaking_monitor(msg, output);
559
560 ret = output_send_settings(msg, output);
561 if (ret != 0)
562 OL_RET(ret);
563
564 MSG(4, "Module speak!");
565
566 switch (msg->settings.type) {
567 case SPD_MSGTYPE_TEXT:
568 SEND_CMD("SPEAK") break;
569 case SPD_MSGTYPE_SOUND_ICON:
570 SEND_CMD("SOUND_ICON");
571 break;
572 case SPD_MSGTYPE_CHAR:
573 SEND_CMD("CHAR");
574 break;
575 case SPD_MSGTYPE_KEY:
576 SEND_CMD("KEY");
577 break;
578 default:
579 MSG(2, "Invalid message type in output_speak()!");
580 }
581
582 SEND_DATA(msg->buf)
583 SEND_CMD("\n.")
584
585 OL_RET(0)
586 }
587
output_stop()588 int output_stop()
589 {
590 int err;
591 OutputModule *output;
592
593 output_lock();
594
595 if (speaking_module == NULL)
596 OL_RET(0)
597 else
598 output = speaking_module;
599
600 MSG(4, "Module stop!");
601 SEND_DATA("STOP\n");
602
603 OL_RET(0)
604 }
605
output_pause()606 size_t output_pause()
607 {
608 static int err;
609 static OutputModule *output;
610
611 output_lock();
612
613 if (speaking_module == NULL)
614 OL_RET(0)
615 else
616 output = speaking_module;
617
618 MSG(4, "Module pause!");
619 SEND_DATA("PAUSE\n");
620
621 OL_RET(0)
622 }
623
output_module_is_speaking(OutputModule * output,char ** index_mark)624 int output_module_is_speaking(OutputModule * output, char **index_mark)
625 {
626 GString *response;
627 int retcode = -1;
628
629 output_lock();
630
631 MSG(5, "output_module_is_speaking()");
632
633 if (output == NULL) {
634 MSG(5, "output==NULL in output_module_is_speaking()");
635 OL_RET(-1);
636 }
637
638 response = output_read_reply(output);
639 if (response == NULL) {
640 *index_mark = NULL;
641 OL_RET(-1);
642 }
643
644 MSG2(5, "output_module", "Reply from output module: |%s|",
645 response->str);
646
647 if (response->len < 4) {
648 MSG2(2, "output_module",
649 "Error: Wrong communication from output module! Reply less than four bytes.");
650 g_string_free(response, TRUE);
651 OL_RET(-1);
652 }
653
654 switch (response->str[0]) {
655 case '3':
656 MSG(2,
657 "Error: Module reported error in request from speechd (code 3xx).");
658 retcode = -2; /* User (speechd) side error */
659 break;
660
661 case '4':
662 MSG(2, "Error: Module reported error in itself (code 4xx).");
663 retcode = -3; /* Module side error */
664 break;
665
666 case '2':
667 retcode = 0;
668 if (response->len > 4) {
669 if (response->str[3] == '-') {
670 char *p;
671 p = strchr(response->str, '\n');
672 *index_mark =
673 (char *)strndup(response->str + 4,
674 p - response->str - 4);
675 MSG2(5, "output_module",
676 "Detected INDEX MARK: %s", *index_mark);
677 } else {
678 MSG2(2, "output_module",
679 "Error: Wrong communication from output module!"
680 "Reply on SPEAKING not multi-line.");
681 retcode = -1;
682 }
683 }
684 break;
685
686 case '7':
687 retcode = 0;
688 MSG2(5, "output_module", "Received event:\n %s", response->str);
689 if (!strncmp(response->str, "701", 3))
690 *index_mark = (char *)g_strdup("__spd_begin");
691 else if (!strncmp(response->str, "702", 3))
692 *index_mark = (char *)g_strdup("__spd_end");
693 else if (!strncmp(response->str, "703", 3))
694 *index_mark = (char *)g_strdup("__spd_stopped");
695 else if (!strncmp(response->str, "704", 3))
696 *index_mark = (char *)g_strdup("__spd_paused");
697 else if (!strncmp(response->str, "700", 3)) {
698 char *p;
699 p = strchr(response->str, '\n');
700 MSG2(5, "output_module", "response:|%s|\n p:|%s|",
701 response->str, p);
702 *index_mark =
703 (char *)strndup(response->str + 4,
704 p - response->str - 4);
705 MSG2(5, "output_module", "Detected INDEX MARK: %s",
706 *index_mark);
707 } else {
708 MSG2(2, "output_module",
709 "ERROR: Unknown event received from output module");
710 retcode = -5;
711 }
712 break;
713
714 default: /* unknown response */
715 MSG(3, "Unknown response from output module!");
716 retcode = -3;
717 break;
718
719 }
720
721 g_string_free(response, TRUE);
722 OL_RET(retcode)
723 }
724
output_is_speaking(char ** index_mark)725 int output_is_speaking(char **index_mark)
726 {
727 int err;
728 OutputModule *output;
729
730 output = speaking_module;
731
732 err = output_module_is_speaking(output, index_mark);
733 if (err < 0) {
734 *index_mark = NULL;
735 }
736
737 return err;
738 }
739
740 /* Wait until the child _pid_ returns with timeout. Calls waitpid() each 100ms
741 until timeout is exceeded. This is not exact and you should not rely on the
742 exact time waited. */
743 int
waitpid_with_timeout(pid_t pid,int * status_ptr,int options,size_t timeout)744 waitpid_with_timeout(pid_t pid, int *status_ptr, int options, size_t timeout)
745 {
746 size_t i;
747 int ret;
748 for (i = 0; i <= timeout; i += 100) {
749 ret = waitpid(pid, status_ptr, options | WNOHANG);
750 if (ret > 0)
751 return ret;
752 if (ret < 0)
753 return ret;
754 usleep(100 * 1000); /* Sleep 100 ms */
755 }
756 return 0;
757 }
758
output_close(OutputModule * module)759 int output_close(OutputModule * module)
760 {
761 int err;
762 int ret;
763 OutputModule *output;
764 output = module;
765
766 if (output == NULL)
767 return -1;
768
769 output_lock();
770
771 assert(output->name != NULL);
772 MSG(3, "Closing module \"%s\"...", output->name);
773 if (output->working) {
774 SEND_DATA("STOP\n");
775 SEND_CMD("QUIT");
776 usleep(100);
777 /* So that the module has some time to exit() correctly */
778 }
779
780 MSG(4, "Waiting for module pid %d", module->pid);
781 ret = waitpid_with_timeout(module->pid, NULL, 0, 1000);
782 if (ret > 0) {
783 MSG(4, "Ok, module closed successfully.");
784 } else if (ret == 0) {
785 int ret2;
786 MSG(1, "ERROR: Timed out when waiting for child cancellation");
787 MSG(3, "Killing the module");
788 kill(module->pid, SIGKILL);
789 MSG(4, "Waiting until the child terminates.");
790 ret2 = waitpid_with_timeout(module->pid, NULL, 0, 1000);
791 if (ret2 > 0) {
792 MSG(3, "Module terminated");
793 } else {
794 MSG(1,
795 "ERROR: Module is not able to terminate, giving up.");
796 }
797 } else {
798 MSG(1,
799 "ERROR: waitpid() failed when waiting for child (module).");
800 }
801
802 OL_RET(0)
803 }
804
805 #undef SEND_CMD
806 #undef SEND_DATA
807
output_check_module(OutputModule * output)808 int output_check_module(OutputModule * output)
809 {
810 int ret;
811 int err;
812 int status;
813
814 if (output == NULL)
815 return -1;
816
817 MSG(4, "Output module working status: %d (pid:%d)", output->working,
818 output->pid);
819
820 if (output->working == 0) {
821 /* Investigate on why it crashed */
822 ret = waitpid(output->pid, &status, WNOHANG);
823 if (ret == 0) {
824 MSG(2, "Output module not running.");
825 return 0;
826 }
827 ret = WIFEXITED(status);
828
829 /* TODO: Linux kernel implementation of threads is not very good :( */
830 // if (ret == 0){
831 if (1) {
832 /* Module terminated abnormally */
833 MSG(2,
834 "Output module terminated abnormally, probably crashed.");
835 } else {
836 /* Module terminated normally, check status */
837 err = WEXITSTATUS(status);
838 if (err == 0)
839 MSG(2, "Module exited normally");
840 if (err == 1)
841 MSG(2, "Internal error in output module!");
842 if (err == 2) {
843 MSG(2,
844 "Output device not working. For software devices, this can mean"
845 "that they are not running or they are not accessible due to wrong"
846 "acces permissions.");
847 }
848 if (err > 2)
849 MSG(2,
850 "Unknown error happened in output module, exit status: %d !",
851 err);
852 }
853 }
854 return 0;
855 }
856
escape_dot(char * otext)857 char *escape_dot(char *otext)
858 {
859 char *seq;
860 GString *ntext;
861 char *ootext;
862 char *ret = NULL;
863 int len;
864
865 if (otext == NULL)
866 return NULL;
867
868 MSG2(5, "escaping", "Incoming text: |%s|", otext);
869
870 ootext = otext;
871
872 ntext = g_string_new("");
873
874 if (strlen(otext) == 1) {
875 if (!strcmp(otext, ".")) {
876 g_string_append(ntext, "..");
877 otext += 1;
878 }
879 }
880
881 if (strlen(otext) >= 2) {
882 if ((otext[0] == '.') && (otext[1] == '\n')) {
883 g_string_append(ntext, "..\n");
884 otext = otext + 2;
885 }
886 }
887
888 MSG2(6, "escaping", "Altering text (I): |%s|", ntext->str);
889
890 while ((seq = strstr(otext, "\n.\n"))) {
891 *seq = 0;
892 g_string_append(ntext, otext);
893 g_string_append(ntext, "\n..\n");
894 otext = seq + 3;
895 }
896
897 MSG2(6, "escaping", "Altering text (II): |%s|", ntext->str);
898
899 len = strlen(otext);
900 if (len >= 2) {
901 if ((otext[len - 2] == '\n') && (otext[len - 1] == '.')) {
902 g_string_append(ntext, otext);
903 g_string_append(ntext, ".");
904 otext = otext + len;
905 MSG2(6, "escaping", "Altering text (II-b): |%s|",
906 ntext->str);
907 }
908 }
909
910 if (otext == ootext) {
911 g_string_free(ntext, 1);
912 ret = otext;
913 } else {
914 g_string_append(ntext, otext);
915 g_free(ootext);
916 ret = ntext->str;
917 g_string_free(ntext, 0);
918 }
919
920 MSG2(6, "escaping", "Altered text: |%s|", ret);
921
922 return ret;
923 }
924