97f3507309df638e428a424595161b7ac2668b70
[profile/ivi/speech-recognition.git] / src / plugins / text-to-speech / festival / festival-loader.c
1 /*
2  * Copyright (c) 2012, 2013, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *   * Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *   * Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *   * Neither the name of Intel Corporation nor the names of its contributors
14  *     may be used to endorse or promote products derived from this software
15  *     without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <unistd.h>
31 #include <errno.h>
32 #include <limits.h>
33 #include <dirent.h>
34 #include <dlfcn.h>
35 #include <regex.h>
36 #include <dirent.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39
40 #include <murphy/common/macros.h>
41 #include <murphy/common/debug.h>
42 #include <murphy/common/mainloop.h>
43
44 #include "srs/daemon/plugin.h"
45 #include "srs/daemon/voice.h"
46
47 #define PLUGIN_NAME    "festival-loader"
48 #define PLUGIN_AUTHORS "Krisztian Litkey <kli@iki.fi>"
49 #define PLUGIN_VERSION "0.0.1"
50 #define PLUGIN_DESCR                                                         \
51     "A plugin to load libFestival.so. This loader works around a bug in "    \
52     "festival caused by a symbol conflict with glibc that causes a SIGSEGV " \
53     "and a crash during library initialization."
54
55 #ifndef PATH_MAX
56 #    define PATH_MAX 1024
57 #endif
58
59 #define MAX_LIBS 8                       /* max libs to preload */
60 #define MAX_DIRS 8                       /* max dirs to search */
61
62 #define CONFIG_DIRS "SRS_FESTIVAL_DIRS"  /* env. var. for dirs to search */
63 #define CONFIG_LIBS "SRS_FESTIVAL_LIBS"  /* env. var. for libs to preload */
64
65 #define DEFAULT_DIRS "/usr/lib64, /usr/lib, /lib64, /lib"
66 #define DEFAULT_LIBS "libeststring.so*, libestbase.so*, libestools.so*, " \
67     "libFestival.so*"
68
69
70 typedef struct {
71     const char *dirs[MAX_DIRS];          /* directories to search */
72     int         ndir;                    /* number of directories */
73     const char *libs[MAX_LIBS];          /* libraries to preload */
74     int         nlib;                    /* number of libraries */
75     void       *handles[MAX_LIBS];       /* library DSO handles */
76 } loader_t;
77
78 static loader_t loader;
79
80
81 static int check_config(loader_t *l)
82 {
83     static char buf[PATH_MAX * 16];
84     const char *evdir, *evlib, *s;
85     char       *b;
86
87     if ((evdir = getenv(CONFIG_DIRS)) == NULL)
88         evdir = DEFAULT_DIRS;
89
90     if ((evlib = getenv(CONFIG_LIBS)) == NULL)
91         evlib = DEFAULT_LIBS;
92
93     mrp_log_info("Directories to search: %s.", evdir);
94     mrp_log_info("Libraries to preaload: %s.", evlib);
95
96     b = buf;
97
98     s = evdir;
99     while (*s) {
100         while (*s == ' ' || *s == '\t' || *s == ',' || *s == ':')
101             s++;
102
103         l->dirs[l->ndir++] = b;
104
105         while (*s && *s != ' ' && *s != '\t' && *s != ',' && *s != ':') {
106             *b++ = *s++;
107
108             if (b - buf >= sizeof(buf) - 1) {
109                 errno = ENOBUFS;
110                 return -1;
111             }
112         }
113
114         if (b - buf >= sizeof(buf) - 1) {
115             errno = ENOBUFS;
116             return -1;
117         }
118
119         *b++ = '\0';
120         mrp_debug("added preload search dir '%s'...", l->dirs[l->ndir - 1]);
121     }
122
123
124     s = evlib;
125     while (*s) {
126         while (*s == ' ' || *s == '\t' || *s == ',' || *s == ':')
127             s++;
128
129         l->libs[l->nlib++] = b;
130
131         while (*s && *s != ' ' && *s != '\t' && *s != ',' && *s != ':') {
132             *b++ = *s++;
133
134             if (b - buf >= sizeof(buf) - 1) {
135                 errno = ENOBUFS;
136                 return -1;
137             }
138         }
139
140         if (b - buf >= sizeof(buf) - 1) {
141             errno = ENOBUFS;
142             return -1;
143         }
144
145         *b++ = '\0';
146
147         mrp_debug("added preload lib '%s'...", l->libs[l->nlib - 1]);
148     }
149
150     return 0;
151 }
152
153
154 static char *find_matching(char *buf, size_t size, const char *dir,
155                            const char *lib)
156 {
157     char           pattern[PATH_MAX], path[PATH_MAX], *p;
158     const char    *l;
159     int            len, prfx, match, ok;
160     DIR           *dp;
161     struct dirent *de;
162     regex_t        re;
163     regmatch_t     rm;
164
165     if (strchr(lib, '*') == NULL && strchr(lib, '?') == NULL) {
166         if (snprintf(buf, size, "%s/%s", dir, lib) >= size)
167             return NULL;
168         if (access(buf, R_OK) == 0)
169             return buf;
170         else
171             return NULL;
172     }
173
174     if ((dp = opendir(dir)) == NULL)
175         return NULL;
176
177     p    = pattern;
178     l    = lib;
179     len  = size;
180     prfx = -1;
181     while (len > 1 && *l) {
182         switch (*l) {
183         case '?':
184         case '*':
185             if (len <= 3)
186                 goto fail;
187
188             if (prfx < 0)
189                 prfx = (p - pattern);
190
191             *p++ = '.';
192             *p++ = *l++;
193             len -= 2;
194             break;
195
196         case '.':
197             if (len <= 3)
198                 goto fail;
199
200             if (prfx < 0)
201                 prfx = (p - pattern);
202
203             *p++ = '\\';
204             *p++ = *l++;
205             len -= 2;
206             break;
207
208         default:
209             *p++ = *l++;
210             len--;
211         }
212     }
213
214     *p = '\0';
215
216     mrp_debug("regex pattern to match: '%s'", pattern);
217
218     if (regcomp(&re, pattern, REG_NOSUB) != 0)
219         goto fail;
220
221     while ((de = readdir(dp)) != NULL) {
222         if (de->d_type == DT_REG) {
223         check_match:
224             if (prfx > 0 && strncmp(de->d_name, pattern, prfx) != 0)
225                 continue;
226
227             if (regexec(&re, de->d_name, 1, &rm, 0) == 0) {
228                 ok = snprintf(buf, size, "%s/%s", dir, de->d_name) < size;
229
230                 closedir(dp);
231                 regfree(&re);
232
233                 return ok ? buf : NULL;
234             }
235         }
236         else if (de->d_type == DT_LNK) {
237             struct stat st;
238
239             snprintf(path, sizeof(path), "%s/%s", dir, de->d_name);
240
241             if (stat(path, &st) == 0 && S_ISREG(st.st_mode))
242                 goto check_match;
243         }
244     }
245
246     closedir(dp);
247     regfree(&re);
248
249     return NULL;
250
251  fail:
252     if (dp != NULL)
253         closedir(dp);
254     return NULL;
255 }
256
257
258 static const char *load_libs(loader_t *l)
259 {
260     char  path[PATH_MAX], *err;
261     void *h;
262     int   i, j;
263
264     for (i = 0; i < l->nlib; i++) {
265         for (j = 0; j < l->ndir; j++) {
266             mrp_log_info("Looking for %s in %s...", l->libs[i], l->dirs[j]);
267
268             if (find_matching(path, sizeof(path), l->dirs[j], l->libs[i])) {
269                 h = dlopen(path, RTLD_NOW | RTLD_GLOBAL | RTLD_DEEPBIND);
270
271                 if (h != NULL) {
272                     mrp_log_info("Preloaded %s.", path);
273                     l->handles[i] = h;
274                     break;
275                 }
276                 else {
277                     err = dlerror();
278                     mrp_log_warning("Failed to load %s (error: %s).",
279                                     l->libs[i], err ? err : "unknown");
280                 }
281             }
282         }
283
284         if (l->handles[i] == NULL) {
285             mrp_log_error("Failed to preload %s.", l->libs[i]);
286             return l->libs[i];
287         }
288     }
289
290     return NULL;
291 }
292
293
294 static void unload_libs(loader_t *l)
295 {
296     int i;
297
298     for (i = 0; i < l->nlib; i++)
299         if (l->handles[i] != NULL) {
300             dlclose(l->handles[i]);
301             l->handles[i] = NULL;
302         }
303 }
304
305
306 static int create_loader(srs_plugin_t *plugin)
307 {
308     const char *failed;
309
310     MRP_UNUSED(plugin);
311
312     mrp_clear(&loader);
313
314     if (check_config(&loader) != 0) {
315         mrp_log_error("Failed to get configuration (%d: %s).", errno,
316                       strerror(errno));
317         return FALSE;
318     }
319
320     if ((failed = load_libs(&loader)) != NULL) {
321         mrp_log_error("Failed to preload library '%s'.", failed);
322         return FALSE;
323     }
324     else
325         return TRUE;
326 }
327
328
329 static int config_loader(srs_plugin_t *plugin, srs_cfg_t *settings)
330 {
331     MRP_UNUSED(plugin);
332     MRP_UNUSED(settings);
333
334     return TRUE;
335 }
336
337
338 static int start_loader(srs_plugin_t *plugin)
339 {
340     return TRUE;
341 }
342
343
344 static void stop_loader(srs_plugin_t *plugin)
345 {
346     MRP_UNUSED(plugin);
347 }
348
349
350 static void destroy_loader(srs_plugin_t *plugin)
351 {
352     MRP_UNUSED(plugin);
353
354     unload_libs(&loader);
355 }
356
357
358 SRS_DECLARE_PLUGIN(PLUGIN_NAME, PLUGIN_DESCR, PLUGIN_AUTHORS, PLUGIN_VERSION,
359                    create_loader, config_loader, start_loader, stop_loader,
360                    destroy_loader);