sphinx: handle both known message origin formats.
[profile/ivi/speech-recognition.git] / src / plugins / speech-to-text / sphinx / logger.c
1 #include <stdio.h>
2 #include <unistd.h>
3 #define __USE_GNU                        /* F_SETPIPE_SZ */
4 #include <fcntl.h>
5 #include <string.h>
6 #include <errno.h>
7
8 #include <murphy/common/log.h>
9 #include <murphy/common/mainloop.h>
10
11 #include "sphinx-plugin.h"
12
13 #define SPHINX_DEBUG  "DEBUG: "
14 #define SPHINX_INFO   "INFO: "
15 #define SPHINX_ERROR  "ERROR: "
16 #define SPHINX_WARN   "WARNING: "
17 #define SPHINX_SYSERR "SYSTEM_ERROR: "
18 #define SPHINX_FATAL  "FATAL_ERROR: "
19
20 #define RD 0
21 #define WR 1
22
23 typedef struct {
24     int             fd[2];               /* log message pipe */
25     FILE           *fp;                  /* log write stream */
26     mrp_io_watch_t *w;                   /* log I/O watch */
27     char            buf[4096];           /* log line buffer */
28     ssize_t         n;
29 } logger_t;
30
31
32 static ssize_t pull_log(logger_t *logger)
33 {
34     char    *p;
35     ssize_t  n, l;
36
37     p = logger->buf + logger->n;
38     l = sizeof(logger->buf) - logger->n - 1;
39
40     n = read(logger->fd[RD], p, l);
41
42     if (n <= 0)
43         return n;
44
45     p[n] = '\0';
46     logger->n += n;
47
48     return logger->n;
49 }
50
51
52 static char *dig_origin(char *msg, char *e, char *name, size_t size,
53                         char **file, int *line)
54 {
55     char *nb, *ne, *lb, *le;
56     int   l, nlen;
57
58     nb = msg;
59
60     /*
61      * Sphinx can prefix messages with a location header (file/line number)
62      * in at least two different formats:
63      *
64      *  1) file-name(line-number) message
65      *  2) "file-name", line line-number: message
66      *
67      * We assume we get a message with a location header in either of the
68      * above formats, try to dig out the location information and set the
69      * message pointer past the header. If we fail we pass the message
70      * back as such.
71      */
72
73     if (*nb == '"') {
74         ne = strchr(nb + 1, '"');
75
76         if (ne == NULL || (ne > e && e != NULL)) {
77         parse_error:
78             if (*file == NULL)
79                 *file = "<unknown file>";
80             return msg;
81         }
82
83         if (strncmp(ne + 1, ", line ", 7))
84             goto parse_error;
85
86         lb = ne + 1 + 7;
87         l  = (int)strtoul(lb + 1, &le, 10);
88
89         if (*le != ':')
90             goto parse_error;
91
92         nlen = ne - nb - 1;
93         snprintf(name, size - 1, "%*.*s", nlen, nlen, nb + 1);
94         *file = name;
95         *line = l;
96
97         msg = le + 1;
98     }
99     else {
100         lb = strchr(msg, '(');
101
102         if (lb == NULL || lb > e)
103             goto parse_error;
104
105         l = (int)strtoul(lb + 1, &le, 10);
106
107         if (le[0] != ')' || le[1] != ':' || le[2] != ' ')
108             goto parse_error;
109
110         nb   = msg;
111         ne   = lb;
112         nlen = ne - nb;
113
114         snprintf(name, size - 1, "%*.*s", nlen, nlen, nb);
115         *file = name;
116         *line = l;
117
118         msg = le + 2;
119     }
120
121     if (*msg == ' ' || *msg == '\t') /* strip single leading space */
122         msg++;
123
124     return msg;
125 }
126
127
128 static void push_log(logger_t *logger)
129 {
130     char    *b, *e, *lb, *le, *file, lvl, name[1024], *msg;
131     int      line, len, nlen;
132     ssize_t  n;
133
134     name[sizeof(name) - 1] = '\0';
135
136     lvl  = 0;
137     file = NULL;
138     line = 0;
139
140     while (logger->n > 0) {
141         b = logger->buf;
142
143         if (!strncmp(b, SPHINX_INFO  , len = sizeof(SPHINX_INFO  ) - 1) ||
144             !strncmp(b, SPHINX_WARN  , len = sizeof(SPHINX_WARN  ) - 1) ||
145             !strncmp(b, SPHINX_ERROR , len = sizeof(SPHINX_ERROR ) - 1) ||
146             !strncmp(b, SPHINX_SYSERR, len = sizeof(SPHINX_SYSERR) - 1) ||
147             !strncmp(b, SPHINX_FATAL , len = sizeof(SPHINX_FATAL ) - 1)) {
148             lvl  = *b;
149             b   += len;
150         }
151         else
152             lvl = 0;
153
154         if ((e = strchr(b, '\n')) == NULL) {
155             if (logger->n >= sizeof(logger->buf) - 1) {
156                 mrp_log_warning("Discarding too long sphinx log buffer.");
157                 logger->n = 0;
158             }
159             return;
160         }
161
162         mrp_debug("got log message '%s'", b);
163
164         if (lvl != 0)
165             msg = dig_origin(b, e, name, sizeof(name), &file, &line);
166         else
167             msg = b;
168
169         mrp_debug("stripped message '%s'", msg);
170
171         n = e - msg;
172
173         switch (lvl) {
174         case 'I':
175         default:
176             if (mrp_debug_check(file, "sphinx", line))
177                 mrp_debug_msg("sphinx", line, file,
178                               "%*.*s", n, n, msg);
179             break;
180         case 'W':
181             mrp_log_msg(MRP_LOG_WARNING, file, line, "sphinx",
182                         "%*.*s", n, n, msg);
183             break;
184         case 'E':
185         case 'S':
186         case 'F':
187             mrp_log_msg(MRP_LOG_ERROR, file, line, "sphinx",
188                         "%*.*s", n, n, msg);
189             break;
190         }
191
192         b = e + 1;
193
194         while (*b == '\n')
195             b++;
196
197         n = logger->n - (b - logger->buf);
198
199         if (n <= 0)
200             logger->n = 0;
201         else {
202             memmove(logger->buf, b, n);
203             logger->n = n;
204             logger->buf[n] = '\0';
205         }
206     }
207 }
208
209
210 static void log_cb(mrp_io_watch_t *w, int fd, mrp_io_event_t events,
211                    void *user_data)
212 {
213     logger_t *logger = (logger_t *)user_data;
214
215     MRP_UNUSED(fd);
216
217     if (events & MRP_IO_EVENT_IN)
218         while (pull_log(logger) > 0)
219             push_log(logger);
220
221     if (events & MRP_IO_EVENT_HUP)
222         mrp_del_io_watch(w);
223 }
224
225
226 FILE *logger_create(context_t *ctx)
227 {
228     static logger_t  logger = { { -1, -1 }, NULL, 0 };
229     mrp_mainloop_t  *ml     = plugin_get_mainloop(ctx->plugin);
230     mrp_io_event_t   events = MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP;
231
232     if (logger.fp != NULL)
233         return logger.fp;
234
235     if (pipe(logger.fd) < 0) {
236         mrp_log_error("Failed to create sphinx logging pipe (error %d: %s).",
237                       errno, strerror(errno));
238         goto fail;
239     }
240
241     if ((logger.fp = fdopen(logger.fd[WR], "w")) == NULL)
242         goto fail;
243
244     setvbuf(logger.fp, NULL, _IOLBF, 0);
245
246     fcntl(logger.fd[WR], F_SETPIPE_SZ, 512 * 1024);
247     fcntl(logger.fd[WR], F_SETFL, O_NONBLOCK);
248     fcntl(logger.fd[RD], F_SETFL, O_NONBLOCK);
249
250     logger.w = mrp_add_io_watch(ml, logger.fd[RD], events, log_cb, &logger);
251
252     if (logger.w != NULL)
253         return logger.fp;
254
255     /* fallthru */
256
257  fail:
258     close(logger.fd[0]);
259     close(logger.fd[1]);
260     if (logger.fp != NULL)
261         fclose(logger.fp);
262
263     logger.fd[0] = -1;
264     logger.fd[1] = -1;
265     logger.fp = NULL;
266
267     return NULL;
268 }