]> git.sesse.net Git - vlc/blob - src/misc/messages.c
45666108e1ab4e727d59162a3c01fd9aea63574a
[vlc] / src / misc / messages.c
1 /*****************************************************************************
2  * messages.c: messages interface
3  * This library provides an interface to the message queue to be used by other
4  * modules, especially intf modules. See vlc_config.h for output configuration.
5  *****************************************************************************
6  * Copyright (C) 1998-2005 VLC authors and VideoLAN
7  * $Id$
8  *
9  * Authors: Vincent Seguin <seguin@via.ecp.fr>
10  *          Samuel Hocevar <sam@zoy.org>
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU Lesser General Public License as published by
14  * the Free Software Foundation; either version 2.1 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public License
23  * along with this program; if not, write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30
31 #ifdef HAVE_CONFIG_H
32 # include "config.h"
33 #endif
34
35 #include <stdlib.h>
36 #include <stdarg.h>                                       /* va_list for BSD */
37 #include <unistd.h>
38 #include <assert.h>
39
40 #include <vlc_common.h>
41 #include <vlc_interface.h>
42 #include <vlc_charset.h>
43 #include "../libvlc.h"
44
45 #ifdef __ANDROID__
46 #include <android/log.h>
47 #endif
48
49 struct vlc_logger_t
50 {
51     VLC_COMMON_MEMBERS
52     vlc_rwlock_t lock;
53     vlc_log_cb log;
54     void *sys;
55 };
56
57 static void vlc_vaLogCallback(libvlc_int_t *vlc, int type,
58                               const vlc_log_t *item, const char *format,
59                               va_list ap)
60 {
61     vlc_logger_t *logger = libvlc_priv(vlc)->logger;
62
63     assert(logger != NULL);
64     vlc_rwlock_rdlock(&logger->lock);
65     logger->log(logger->sys, type, item, format, ap);
66     vlc_rwlock_unlock(&logger->lock);
67 }
68
69 static void vlc_LogCallback(libvlc_int_t *vlc, int type, const vlc_log_t *item,
70                             const char *format, ...)
71 {
72     va_list ap;
73
74     va_start(ap, format);
75     vlc_vaLogCallback(vlc, type, item, format, ap);
76     va_end(ap);
77 }
78
79 #ifdef _WIN32
80 static void Win32DebugOutputMsg (void *, int , const vlc_log_t *,
81                                  const char *, va_list);
82 #endif
83
84 /**
85  * Emit a log message. This function is the variable argument list equivalent
86  * to vlc_Log().
87  */
88 void vlc_vaLog (vlc_object_t *obj, int type, const char *module,
89                 const char *format, va_list args)
90 {
91     if (obj != NULL && obj->i_flags & OBJECT_FLAGS_QUIET)
92         return;
93
94     /* Get basename from the module filename */
95     char *p = strrchr(module, '/');
96     if (p != NULL)
97         module = p;
98     p = strchr(module, '.');
99
100     size_t modlen = (p != NULL) ? (p - module) : 0;
101     char modulebuf[modlen + 1];
102     if (p != NULL)
103     {
104         memcpy(modulebuf, module, modlen);
105         modulebuf[modlen] = '\0';
106         module = modulebuf;
107     }
108
109     /* Fill message information fields */
110     vlc_log_t msg;
111
112     msg.i_object_id = (uintptr_t)obj;
113     msg.psz_object_type = (obj != NULL) ? obj->psz_object_type : "generic";
114     msg.psz_module = module;
115     msg.psz_header = NULL;
116
117     for (vlc_object_t *o = obj; o != NULL; o = o->p_parent)
118         if (o->psz_header != NULL)
119         {
120             msg.psz_header = o->psz_header;
121             break;
122         }
123
124 #ifdef _WIN32
125     va_list ap;
126
127     va_copy (ap, args);
128     Win32DebugOutputMsg (NULL, type, &msg, format, ap);
129     va_end (ap);
130 #endif
131
132     /* Pass message to the callback */
133     if (obj != NULL)
134         vlc_vaLogCallback(obj->p_libvlc, type, &msg, format, args);
135 }
136
137 /**
138  * Emit a log message.
139  * \param obj VLC object emitting the message or NULL
140  * \param type VLC_MSG_* message type (info, error, warning or debug)
141  * \param module name of module from which the message come
142  *               (normally MODULE_STRING)
143  * \param format printf-like message format
144  */
145 void vlc_Log(vlc_object_t *obj, int type, const char *module,
146              const char *format, ... )
147 {
148     va_list ap;
149
150     va_start(ap, format);
151     vlc_vaLog(obj, type, module, format, ap);
152     va_end(ap);
153 }
154
155 static const char msg_type[4][9] = { "", " error", " warning", " debug" };
156 #define COL(x,y)  "\033[" #x ";" #y "m"
157 #define RED     COL(31,1)
158 #define GREEN   COL(32,1)
159 #define YELLOW  COL(0,33)
160 #define WHITE   COL(0,1)
161 #define GRAY    "\033[0m"
162 static const char msg_color[4][8] = { WHITE, RED, YELLOW, GRAY };
163
164 /* Display size of a pointer */
165 static const int ptr_width = 2 * /* hex digits */ sizeof(uintptr_t);
166
167 static void PrintColorMsg (void *d, int type, const vlc_log_t *p_item,
168                            const char *format, va_list ap)
169 {
170     FILE *stream = stderr;
171     int verbose = (intptr_t)d;
172
173     if (verbose < 0 || verbose < (type - VLC_MSG_ERR))
174         return;
175
176     int canc = vlc_savecancel ();
177
178     flockfile (stream);
179     utf8_fprintf (stream, "["GREEN"%0*"PRIxPTR""GRAY"] ", ptr_width, p_item->i_object_id);
180     if (p_item->psz_header != NULL)
181         utf8_fprintf (stream, "[%s] ", p_item->psz_header);
182     utf8_fprintf (stream, "%s %s%s: %s", p_item->psz_module,
183                   p_item->psz_object_type, msg_type[type], msg_color[type]);
184     utf8_vfprintf (stream, format, ap);
185     fputs (GRAY"\n", stream);
186 #if defined (_WIN32) || defined (__OS2__)
187     fflush (stream);
188 #endif
189     funlockfile (stream);
190     vlc_restorecancel (canc);
191 }
192
193 static void PrintMsg (void *d, int type, const vlc_log_t *p_item,
194                       const char *format, va_list ap)
195 {
196     FILE *stream = stderr;
197     int verbose = (intptr_t)d;
198
199     if (verbose < 0 || verbose < (type - VLC_MSG_ERR))
200         return;
201
202     int canc = vlc_savecancel ();
203
204     flockfile (stream);
205     utf8_fprintf (stream, "[%0*"PRIxPTR"] ", ptr_width, p_item->i_object_id);
206     if (p_item->psz_header != NULL)
207         utf8_fprintf (stream, "[%s] ", p_item->psz_header);
208     utf8_fprintf (stream, "%s %s%s: ", p_item->psz_module,
209                   p_item->psz_object_type, msg_type[type]);
210     utf8_vfprintf (stream, format, ap);
211     putc_unlocked ('\n', stream);
212 #if defined (_WIN32) || defined (__OS2__)
213     fflush (stream);
214 #endif
215     funlockfile (stream);
216     vlc_restorecancel (canc);
217 }
218
219 #ifdef _WIN32
220 static void Win32DebugOutputMsg (void* d, int type, const vlc_log_t *p_item,
221                                  const char *format, va_list dol)
222 {
223     VLC_UNUSED(p_item);
224
225     const signed char *pverbose = d;
226     if (pverbose && (*pverbose < 0 || *pverbose < (type - VLC_MSG_ERR)))
227         return;
228
229     va_list dol2;
230     va_copy (dol2, dol);
231     int msg_len = vsnprintf(NULL, 0, format, dol2);
232     va_end (dol2);
233
234     if(msg_len <= 0)
235         return;
236
237     char *msg = malloc(msg_len + 1 + 1);
238     if (!msg)
239         return;
240
241     msg_len = vsnprintf(msg, msg_len+1, format, dol);
242     if (msg_len > 0){
243         if(msg[msg_len-1] != '\n'){
244             msg[msg_len] = '\n';
245             msg[msg_len + 1] = '\0';
246         }
247         char* psz_msg = NULL;
248         if(asprintf(&psz_msg, "%s %s%s: %s", p_item->psz_module,
249                     p_item->psz_object_type, msg_type[type], msg) > 0) {
250             wchar_t* wmsg = ToWide(psz_msg);
251             OutputDebugStringW(wmsg);
252             free(wmsg);
253             free(psz_msg);
254         }
255     }
256     free(msg);
257 }
258 #endif
259
260 #ifdef __ANDROID__
261 static void AndroidPrintMsg (void *d, int type, const vlc_log_t *p_item,
262                              const char *format, va_list ap)
263 {
264     int verbose = (intptr_t)d;
265     int prio;
266
267     if (verbose < 0 || verbose < (type - VLC_MSG_ERR))
268         return;
269
270     int canc = vlc_savecancel ();
271
272     char *format2;
273     if (asprintf (&format2, "[%0*"PRIxPTR"] %s %s: %s",
274                   ptr_width, p_item->i_object_id, p_item->psz_module,
275                   p_item->psz_object_type, format) < 0)
276         return;
277     switch (type) {
278         case VLC_MSG_INFO:
279             prio = ANDROID_LOG_INFO;
280             break;
281         case VLC_MSG_ERR:
282             prio = ANDROID_LOG_ERROR;
283             break;
284         case VLC_MSG_WARN:
285             prio = ANDROID_LOG_WARN;
286             break;
287         default:
288         case VLC_MSG_DBG:
289             prio = ANDROID_LOG_DEBUG;
290     }
291     __android_log_vprint (prio, "VLC", format2, ap);
292     free (format2);
293     vlc_restorecancel (canc);
294 }
295 #endif
296
297 typedef struct vlc_log_early_t
298 {
299     struct vlc_log_early_t *next;
300     int type;
301     vlc_log_t meta;
302     char *msg;
303 } vlc_log_early_t;
304
305 typedef struct
306 {
307     vlc_mutex_t lock;
308     vlc_log_early_t *head;
309     vlc_log_early_t **tailp;
310 } vlc_logger_early_t;
311
312 static void vlc_vaLogEarly(void *d, int type, const vlc_log_t *item,
313                            const char *format, va_list ap)
314 {
315     vlc_logger_early_t *sys = d;
316
317     vlc_log_early_t *log = malloc(sizeof (*log));
318     if (unlikely(log == NULL))
319         return;
320
321     log->next = NULL;
322     log->type = type;
323     log->meta.i_object_id = item->i_object_id;
324     /* NOTE: Object types MUST be static constant - no need to copy them. */
325     log->meta.psz_object_type = item->psz_object_type;
326     log->meta.psz_module = item->psz_module; /* Ditto. */
327     log->meta.psz_header = item->psz_header ? strdup(item->psz_header) : NULL;
328
329     int canc = vlc_savecancel(); /* XXX: needed for vasprintf() ? */
330     if (vasprintf(&log->msg, format, ap) == -1)
331         log->msg = NULL;
332     vlc_restorecancel(canc);
333
334     vlc_mutex_lock(&sys->lock);
335     assert(sys->tailp != NULL);
336     assert(*(sys->tailp) == NULL);
337     *(sys->tailp) = log;
338     sys->tailp = &log->next;
339     vlc_mutex_unlock(&sys->lock);
340 }
341
342 static int vlc_LogEarlyOpen(vlc_logger_t *logger)
343 {
344     vlc_logger_early_t *sys = malloc(sizeof (*sys));
345
346     if (unlikely(sys == NULL))
347         return -1;
348
349     vlc_mutex_init(&sys->lock);
350     sys->head = NULL;
351     sys->tailp = &sys->head;
352
353     logger->log = vlc_vaLogEarly;
354     logger->sys = sys;
355     return 0;
356 }
357
358 static void vlc_LogEarlyClose(libvlc_int_t *vlc, void *d)
359 {
360     vlc_logger_early_t *sys = d;
361
362     /* Drain early log messages */
363     for (vlc_log_early_t *log = sys->head, *next; log != NULL; log = next)
364     {
365         vlc_LogCallback(vlc, log->type, &log->meta, "%s",
366                         (log->msg != NULL) ? log->msg : "message lost");
367         free(log->msg);
368         next = log->next;
369         free(log);
370     }
371
372     vlc_mutex_destroy(&sys->lock);
373     free(sys);
374 }
375
376 static void vlc_vaLogDiscard(void *d, int type, const vlc_log_t *item,
377                              const char *format, va_list ap)
378 {
379     (void) d; (void) type; (void) item; (void) format; (void) ap;
380 }
381
382 /**
383  * Performs preinitialization of the messages logging subsystem.
384  *
385  * Early log messages will be stored in memory until the subsystem is fully
386  * initialized with vlc_LogInit(). This enables logging before the
387  * configuration and modules bank are ready.
388  *
389  * \return 0 on success, -1 on error.
390  */
391 int vlc_LogPreinit(libvlc_int_t *vlc)
392 {
393     vlc_logger_t *logger = vlc_custom_create(vlc, sizeof (*logger), "logger");
394
395     libvlc_priv(vlc)->logger = logger;
396
397     if (unlikely(logger == NULL))
398         return -1;
399
400     vlc_rwlock_init(&logger->lock);
401
402     if (vlc_LogEarlyOpen(logger))
403     {
404         logger->log = vlc_vaLogDiscard;
405         return -1;
406     }
407
408     /* Announce who we are */
409     msg_Dbg(vlc, "VLC media player - %s", VERSION_MESSAGE);
410     msg_Dbg(vlc, "%s", COPYRIGHT_MESSAGE);
411     msg_Dbg(vlc, "revision %s", psz_vlc_changeset);
412     msg_Dbg(vlc, "configured with %s", CONFIGURE_LINE);
413     return 0;
414 }
415
416 /**
417  * Initializes the messages logging subsystem and drain the early messages to
418  * the configured log.
419  *
420  * \return 0 on success, -1 on error.
421  */
422 int vlc_LogInit(libvlc_int_t *vlc)
423 {
424     vlc_logger_t *logger = libvlc_priv(vlc)->logger;
425     void *early_sys = NULL;
426     vlc_log_cb cb = PrintMsg;
427     signed char verbosity;
428
429     if (unlikely(logger == NULL))
430         return -1;
431
432 #ifdef __ANDROID__
433     cb = AndroidPrintMsg;
434 #elif defined (HAVE_ISATTY) && !defined (_WIN32)
435     if (isatty(STDERR_FILENO) && var_InheritBool(vlc, "color"))
436         cb = PrintColorMsg;
437 #endif
438
439     if (var_InheritBool(vlc, "quiet"))
440         verbosity = -1;
441     else
442     {
443         const char *str = getenv("VLC_VERBOSE");
444
445         if (str == NULL || sscanf(str, "%hhd", &verbosity) < 1)
446             verbosity = var_InheritInteger(vlc, "verbose");
447     }
448
449     vlc_rwlock_wrlock(&logger->lock);
450
451     if (logger->log == vlc_vaLogEarly)
452         early_sys = logger->sys;
453
454     logger->log = cb;
455     logger->sys = (void *)(intptr_t)verbosity;
456     vlc_rwlock_unlock(&logger->lock);
457
458     if (early_sys != NULL)
459         vlc_LogEarlyClose(vlc, early_sys);
460
461     return 0;
462 }
463
464 /**
465  * Sets the message logging callback.
466  * \param cb message callback, or NULL to clear
467  * \param data data pointer for the message callback
468  */
469 void vlc_LogSet(libvlc_int_t *vlc, vlc_log_cb cb, void *opaque)
470 {
471     vlc_logger_t *logger = libvlc_priv(vlc)->logger;
472
473     if (unlikely(logger == NULL))
474         return;
475
476     if (cb == NULL)
477         cb = vlc_vaLogDiscard;
478
479     vlc_rwlock_wrlock(&logger->lock);
480     logger->log = cb;
481     logger->sys = opaque;
482     vlc_rwlock_unlock(&logger->lock);
483
484     /* Announce who we are */
485     msg_Dbg (vlc, "VLC media player - %s", VERSION_MESSAGE);
486     msg_Dbg (vlc, "%s", COPYRIGHT_MESSAGE);
487     msg_Dbg (vlc, "revision %s", psz_vlc_changeset);
488     msg_Dbg (vlc, "configured with %s", CONFIGURE_LINE);
489 }
490
491 void vlc_LogDeinit(libvlc_int_t *vlc)
492 {
493     vlc_logger_t *logger = libvlc_priv(vlc)->logger;
494
495     if (unlikely(logger == NULL))
496         return;
497
498     /* Flush early log messages (corner case: no call to vlc_LogInit()) */
499     if (logger->log == vlc_vaLogEarly)
500     {
501         logger->log = vlc_vaLogDiscard;
502         vlc_LogEarlyClose(vlc, logger->sys);
503     }
504
505     vlc_rwlock_destroy(&logger->lock);
506     vlc_object_release(logger);
507 }