X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;ds=sidebyside;f=src%2Fmisc%2Fmessages.c;h=e22e4fe742fb4587c6c394eb561b150ea8a130d4;hb=31268f2df54a433f2b50440bb504546bd19ed16a;hp=0b5a3fd147e6f744e0a9960e6a1d536d7ca05e0d;hpb=46cd3b97940b60ff3e54b9405a50a8b4564093bd;p=vlc diff --git a/src/misc/messages.c b/src/misc/messages.c index 0b5a3fd147..e22e4fe742 100644 --- a/src/misc/messages.c +++ b/src/misc/messages.c @@ -34,42 +34,47 @@ #include #include /* va_list for BSD */ -#ifdef __APPLE__ -# include -#elif defined(HAVE_LOCALE_H) -# include -#endif -#include /* errno */ -#include #include +#include #include #include -#ifdef WIN32 -# include /* 'net_strerror' and 'WSAGetLastError' */ -#endif #include +#include #include "../libvlc.h" -/** - * Emit a log message. - * \param obj VLC object emitting the message or NULL - * \param type VLC_MSG_* message type (info, error, warning or debug) - * \param module name of module from which the message come - * (normally MODULE_STRING) - * \param format printf-like message format - */ -void vlc_Log (vlc_object_t *obj, int type, const char *module, - const char *format, ... ) +struct vlc_logger_t +{ + VLC_COMMON_MEMBERS + vlc_rwlock_t lock; + vlc_log_cb log; + void *sys; + module_t *module; +}; + +static void vlc_vaLogCallback(libvlc_int_t *vlc, int type, + const vlc_log_t *item, const char *format, + va_list ap) { - va_list args; + vlc_logger_t *logger = libvlc_priv(vlc)->logger; - va_start (args, format); - vlc_vaLog (obj, type, module, format, args); - va_end (args); + assert(logger != NULL); + vlc_rwlock_rdlock(&logger->lock); + logger->log(logger->sys, type, item, format, ap); + vlc_rwlock_unlock(&logger->lock); } -#ifdef WIN32 +static void vlc_LogCallback(libvlc_int_t *vlc, int type, const vlc_log_t *item, + const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vlc_vaLogCallback(vlc, type, item, format, ap); + va_end(ap); +} + +#ifdef _WIN32 static void Win32DebugOutputMsg (void *, int , const vlc_log_t *, const char *, va_list); #endif @@ -79,70 +84,26 @@ static void Win32DebugOutputMsg (void *, int , const vlc_log_t *, * to vlc_Log(). */ void vlc_vaLog (vlc_object_t *obj, int type, const char *module, + const char *file, unsigned line, const char *func, const char *format, va_list args) { if (obj != NULL && obj->i_flags & OBJECT_FLAGS_QUIET) return; - /* C locale to get error messages in English in the logs */ - locale_t c = newlocale (LC_MESSAGES_MASK, "C", (locale_t)0); - locale_t locale = uselocale (c); - -#ifndef __GLIBC__ - /* Expand %m to strerror(errno) - only once */ - char buf[strlen(format) + 2001], *ptr; - strcpy (buf, format); - ptr = (char*)buf; - format = (const char*) buf; + /* Get basename from the module filename */ + char *p = strrchr(module, '/'); + if (p != NULL) + module = p; + p = strchr(module, '.'); - for( ;; ) + size_t modlen = (p != NULL) ? (p - module) : 0; + char modulebuf[modlen + 1]; + if (p != NULL) { - ptr = strchr( ptr, '%' ); - if( ptr == NULL ) - break; - - if( ptr[1] == 'm' ) - { - char errbuf[2001]; - size_t errlen; - -#ifndef WIN32 - strerror_r( errno, errbuf, 1001 ); -#else - int sockerr = WSAGetLastError( ); - if( sockerr ) - { - strncpy( errbuf, net_strerror( sockerr ), 1001 ); - WSASetLastError( sockerr ); - } - if ((sockerr == 0) - || (strcmp ("Unknown network stack error", errbuf) == 0)) - strncpy( errbuf, strerror( errno ), 1001 ); -#endif - errbuf[1000] = 0; - - /* Escape '%' from the error string */ - for( char *percent = strchr( errbuf, '%' ); - percent != NULL; - percent = strchr( percent + 2, '%' ) ) - { - memmove( percent + 1, percent, strlen( percent ) + 1 ); - } - - errlen = strlen( errbuf ); - memmove( ptr + errlen, ptr + 2, strlen( ptr + 2 ) + 1 ); - memcpy( ptr, errbuf, errlen ); - break; /* Only once, so we don't overflow */ - } - - /* Looks for conversion specifier... */ - do - ptr++; - while( *ptr && ( strchr( "diouxXeEfFgGaAcspn%", *ptr ) == NULL ) ); - if( *ptr ) - ptr++; /* ...and skip it */ + memcpy(modulebuf, module, modlen); + modulebuf[modlen] = '\0'; + module = modulebuf; } -#endif /* Fill message information fields */ vlc_log_t msg; @@ -151,6 +112,9 @@ void vlc_vaLog (vlc_object_t *obj, int type, const char *module, msg.psz_object_type = (obj != NULL) ? obj->psz_object_type : "generic"; msg.psz_module = module; msg.psz_header = NULL; + msg.file = file; + msg.line = line; + msg.func = func; for (vlc_object_t *o = obj; o != NULL; o = o->p_parent) if (o->psz_header != NULL) @@ -159,89 +123,44 @@ void vlc_vaLog (vlc_object_t *obj, int type, const char *module, break; } - /* Pass message to the callback */ - libvlc_priv_t *priv = obj ? libvlc_priv (obj->p_libvlc) : NULL; - -#ifdef WIN32 +#ifdef _WIN32 va_list ap; va_copy (ap, args); - Win32DebugOutputMsg (priv ? &priv->log.verbose : NULL, type, &msg, format, ap); + Win32DebugOutputMsg (NULL, type, &msg, format, ap); va_end (ap); #endif - if (priv) { - vlc_rwlock_rdlock (&priv->log.lock); - priv->log.cb (priv->log.opaque, type, &msg, format, args); - vlc_rwlock_unlock (&priv->log.lock); - } - - uselocale (locale); - freelocale (c); + /* Pass message to the callback */ + if (obj != NULL) + vlc_vaLogCallback(obj->p_libvlc, type, &msg, format, args); } -static const char msg_type[4][9] = { "", " error", " warning", " debug" }; -#define COL(x,y) "\033[" #x ";" #y "m" -#define RED COL(31,1) -#define GREEN COL(32,1) -#define YELLOW COL(0,33) -#define WHITE COL(0,1) -#define GRAY "\033[0m" -static const char msg_color[4][8] = { WHITE, RED, YELLOW, GRAY }; - -static void PrintColorMsg (void *d, int type, const vlc_log_t *p_item, - const char *format, va_list ap) +/** + * Emit a log message. + * \param obj VLC object emitting the message or NULL + * \param type VLC_MSG_* message type (info, error, warning or debug) + * \param module name of module from which the message come + * (normally MODULE_STRING) + * \param file source module file name (normally __FILE__) or NULL + * \param line function call source line number (normally __LINE__) or 0 + * \param func calling function name (normally __func__) or NULL + * \param format printf-like message format + */ +void vlc_Log(vlc_object_t *obj, int type, const char *module, + const char *file, unsigned line, const char *func, + const char *format, ... ) { - FILE *stream = stderr; - int verbose = (intptr_t)d; - - if (verbose < 0 || verbose < (type - VLC_MSG_ERR)) - return; + va_list ap; - int canc = vlc_savecancel (); - - flockfile (stream); - fprintf (stream, "["GREEN"%p"GRAY"] ", (void *)p_item->i_object_id); - if (p_item->psz_header != NULL) - utf8_fprintf (stream, "[%s] ", p_item->psz_header); - utf8_fprintf (stream, "%s %s%s: %s", p_item->psz_module, - p_item->psz_object_type, msg_type[type], msg_color[type]); - utf8_vfprintf (stream, format, ap); - fputs (GRAY"\n", stream); -#if defined (WIN32) || defined (__OS2__) - fflush (stream); -#endif - funlockfile (stream); - vlc_restorecancel (canc); + va_start(ap, format); + vlc_vaLog(obj, type, module, file, line, func, format, ap); + va_end(ap); } -static void PrintMsg (void *d, int type, const vlc_log_t *p_item, - const char *format, va_list ap) -{ - FILE *stream = stderr; - int verbose = (intptr_t)d; - - if (verbose < 0 || verbose < (type - VLC_MSG_ERR)) - return; - - int canc = vlc_savecancel (); - - flockfile (stream); - fprintf (stream, "[%p] ", (void *)p_item->i_object_id); - if (p_item->psz_header != NULL) - utf8_fprintf (stream, "[%s] ", p_item->psz_header); - utf8_fprintf (stream, "%s %s%s: ", p_item->psz_module, - p_item->psz_object_type, msg_type[type]); - utf8_vfprintf (stream, format, ap); - putc_unlocked ('\n', stream); -#if defined (WIN32) || defined (__OS2__) - fflush (stream); -#endif - funlockfile (stream); - vlc_restorecancel (canc); -} +#ifdef _WIN32 +static const char msg_type[4][9] = { "", " error", " warning", " debug" }; -#ifdef WIN32 static void Win32DebugOutputMsg (void* d, int type, const vlc_log_t *p_item, const char *format, va_list dol) { @@ -282,30 +201,214 @@ static void Win32DebugOutputMsg (void* d, int type, const vlc_log_t *p_item, } #endif +typedef struct vlc_log_early_t +{ + struct vlc_log_early_t *next; + int type; + vlc_log_t meta; + char *msg; +} vlc_log_early_t; + +typedef struct +{ + vlc_mutex_t lock; + vlc_log_early_t *head; + vlc_log_early_t **tailp; +} vlc_logger_early_t; + +static void vlc_vaLogEarly(void *d, int type, const vlc_log_t *item, + const char *format, va_list ap) +{ + vlc_logger_early_t *sys = d; + + vlc_log_early_t *log = malloc(sizeof (*log)); + if (unlikely(log == NULL)) + return; + + log->next = NULL; + log->type = type; + log->meta.i_object_id = item->i_object_id; + /* NOTE: Object types MUST be static constant - no need to copy them. */ + log->meta.psz_object_type = item->psz_object_type; + log->meta.psz_module = item->psz_module; /* Ditto. */ + log->meta.psz_header = item->psz_header ? strdup(item->psz_header) : NULL; + log->meta.file = item->file; + log->meta.line = item->line; + log->meta.func = item->func; + + int canc = vlc_savecancel(); /* XXX: needed for vasprintf() ? */ + if (vasprintf(&log->msg, format, ap) == -1) + log->msg = NULL; + vlc_restorecancel(canc); + + vlc_mutex_lock(&sys->lock); + assert(sys->tailp != NULL); + assert(*(sys->tailp) == NULL); + *(sys->tailp) = log; + sys->tailp = &log->next; + vlc_mutex_unlock(&sys->lock); +} + +static int vlc_LogEarlyOpen(vlc_logger_t *logger) +{ + vlc_logger_early_t *sys = malloc(sizeof (*sys)); + + if (unlikely(sys == NULL)) + return -1; + + vlc_mutex_init(&sys->lock); + sys->head = NULL; + sys->tailp = &sys->head; + + logger->log = vlc_vaLogEarly; + logger->sys = sys; + return 0; +} + +static void vlc_LogEarlyClose(vlc_logger_t *logger, void *d) +{ + libvlc_int_t *vlc = logger->p_libvlc; + vlc_logger_early_t *sys = d; + + /* Drain early log messages */ + for (vlc_log_early_t *log = sys->head, *next; log != NULL; log = next) + { + vlc_LogCallback(vlc, log->type, &log->meta, "%s", + (log->msg != NULL) ? log->msg : "message lost"); + free(log->msg); + next = log->next; + free(log); + } + + vlc_mutex_destroy(&sys->lock); + free(sys); +} + +static void vlc_vaLogDiscard(void *d, int type, const vlc_log_t *item, + const char *format, va_list ap) +{ + (void) d; (void) type; (void) item; (void) format; (void) ap; +} + +static int vlc_logger_load(void *func, va_list ap) +{ + vlc_log_cb (*activate)(vlc_object_t *, void **) = func; + vlc_logger_t *logger = va_arg(ap, vlc_logger_t *); + vlc_log_cb *cb = va_arg(ap, vlc_log_cb *); + void **sys = va_arg(ap, void **); + + *cb = activate(VLC_OBJECT(logger), sys); + return (*cb != NULL) ? VLC_SUCCESS : VLC_EGENERIC; +} + +static void vlc_logger_unload(void *func, va_list ap) +{ + void (*deactivate)(vlc_logger_t *) = func; + void *sys = va_arg(ap, void *); + + deactivate(sys); +} + +/** + * Performs preinitialization of the messages logging subsystem. + * + * Early log messages will be stored in memory until the subsystem is fully + * initialized with vlc_LogInit(). This enables logging before the + * configuration and modules bank are ready. + * + * \return 0 on success, -1 on error. + */ +int vlc_LogPreinit(libvlc_int_t *vlc) +{ + vlc_logger_t *logger = vlc_custom_create(vlc, sizeof (*logger), "logger"); + + libvlc_priv(vlc)->logger = logger; + + if (unlikely(logger == NULL)) + return -1; + + vlc_rwlock_init(&logger->lock); + + if (vlc_LogEarlyOpen(logger)) + { + logger->log = vlc_vaLogDiscard; + return -1; + } + + /* Announce who we are */ + msg_Dbg(vlc, "VLC media player - %s", VERSION_MESSAGE); + msg_Dbg(vlc, "%s", COPYRIGHT_MESSAGE); + msg_Dbg(vlc, "revision %s", psz_vlc_changeset); + msg_Dbg(vlc, "configured with %s", CONFIGURE_LINE); + return 0; +} + +/** + * Initializes the messages logging subsystem and drain the early messages to + * the configured log. + * + * \return 0 on success, -1 on error. + */ +int vlc_LogInit(libvlc_int_t *vlc) +{ + vlc_logger_t *logger = libvlc_priv(vlc)->logger; + if (unlikely(logger == NULL)) + return -1; + + vlc_log_cb cb; + void *sys, *early_sys = NULL; + + /* TODO: module configuration item */ + module_t *module = vlc_module_load(logger, "logger", NULL, false, + vlc_logger_load, logger, &cb, &sys); + if (module == NULL) + cb = vlc_vaLogDiscard; + + vlc_rwlock_wrlock(&logger->lock); + if (logger->log == vlc_vaLogEarly) + early_sys = logger->sys; + + logger->log = cb; + logger->sys = sys; + assert(logger->module == NULL); /* Only one call to vlc_LogInit()! */ + logger->module = module; + vlc_rwlock_unlock(&logger->lock); + + if (early_sys != NULL) + vlc_LogEarlyClose(logger, early_sys); + + return 0; +} + /** * Sets the message logging callback. - * \param cb message callback, or NULL to reset + * \param cb message callback, or NULL to clear * \param data data pointer for the message callback */ -void vlc_LogSet (libvlc_int_t *vlc, vlc_log_cb cb, void *opaque) +void vlc_LogSet(libvlc_int_t *vlc, vlc_log_cb cb, void *opaque) { - libvlc_priv_t *priv = libvlc_priv (vlc); + vlc_logger_t *logger = libvlc_priv(vlc)->logger; + + if (unlikely(logger == NULL)) + return; + + module_t *module; + void *sys; if (cb == NULL) - { -#if defined (HAVE_ISATTY) && !defined (WIN32) - if (isatty (STDERR_FILENO) && var_InheritBool (vlc, "color")) - cb = PrintColorMsg; - else -#endif - cb = PrintMsg; - opaque = (void *)(intptr_t)priv->log.verbose; - } + cb = vlc_vaLogDiscard; + + vlc_rwlock_wrlock(&logger->lock); + sys = logger->sys; + module = logger->module; - vlc_rwlock_wrlock (&priv->log.lock); - priv->log.cb = cb; - priv->log.opaque = opaque; - vlc_rwlock_unlock (&priv->log.lock); + logger->log = cb; + logger->sys = opaque; + logger->module = NULL; + vlc_rwlock_unlock(&logger->lock); + + if (module != NULL) + vlc_module_unload(module, vlc_logger_unload, sys); /* Announce who we are */ msg_Dbg (vlc, "VLC media player - %s", VERSION_MESSAGE); @@ -314,26 +417,24 @@ void vlc_LogSet (libvlc_int_t *vlc, vlc_log_cb cb, void *opaque) msg_Dbg (vlc, "configured with %s", CONFIGURE_LINE); } -void vlc_LogInit (libvlc_int_t *vlc) +void vlc_LogDeinit(libvlc_int_t *vlc) { - libvlc_priv_t *priv = libvlc_priv (vlc); - const char *str; - - if (var_InheritBool (vlc, "quiet")) - priv->log.verbose = -1; - else - if ((str = getenv ("VLC_VERBOSE")) != NULL) - priv->log.verbose = atoi (str); - else - priv->log.verbose = var_InheritInteger (vlc, "verbose"); + vlc_logger_t *logger = libvlc_priv(vlc)->logger; - vlc_rwlock_init (&priv->log.lock); - vlc_LogSet (vlc, NULL, NULL); -} + if (unlikely(logger == NULL)) + return; -void vlc_LogDeinit (libvlc_int_t *vlc) -{ - libvlc_priv_t *priv = libvlc_priv (vlc); + if (logger->module != NULL) + vlc_module_unload(logger->module, vlc_logger_unload, logger->sys); + else + /* Flush early log messages (corner case: no call to vlc_LogInit()) */ + if (logger->log == vlc_vaLogEarly) + { + logger->log = vlc_vaLogDiscard; + vlc_LogEarlyClose(logger, logger->sys); + } - vlc_rwlock_destroy (&priv->log.lock); + vlc_rwlock_destroy(&logger->lock); + vlc_object_release(logger); + libvlc_priv(vlc)->logger = NULL; }