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 the VideoLAN team
9 * Authors: Vincent Seguin <seguin@via.ecp.fr>
10 * Samuel Hocevar <sam@zoy.org>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
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 General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
35 #include <vlc_common.h>
37 #include <stdarg.h> /* va_list for BSD */
40 #elif defined(HAVE_LOCALE_H)
43 #include <errno.h> /* errno */
46 # include <vlc_network.h> /* 'net_strerror' and 'WSAGetLastError' */
50 # include <unistd.h> /* close(), write() */
55 #include <vlc_charset.h>
56 #include "../libvlc.h"
64 static void cleanup_msg_context (void *data
)
66 msg_context_t
*ctx
= data
;
67 free (ctx
->psz_message
);
71 static vlc_threadvar_t msg_context
;
72 static uintptr_t banks
= 0;
74 /*****************************************************************************
76 *****************************************************************************/
77 #if defined(HAVE_VA_COPY)
78 # define vlc_va_copy(dest,src) va_copy(dest,src)
79 #elif defined(HAVE___VA_COPY)
80 # define vlc_va_copy(dest,src) __va_copy(dest,src)
82 # define vlc_va_copy(dest,src) (dest)=(src)
85 static inline msg_bank_t
*libvlc_bank (libvlc_int_t
*inst
)
87 return (libvlc_priv (inst
))->msg_bank
;
90 /*****************************************************************************
92 *****************************************************************************/
93 static void PrintMsg ( vlc_object_t
*, msg_item_t
* );
95 static vlc_mutex_t msg_stack_lock
= VLC_STATIC_MUTEX
;
98 * Store all data required by messages interfaces.
102 /** Message queue lock */
107 msg_subscription_t
**pp_sub
;
109 locale_t locale
; /**< C locale for error messages */
110 vlc_dictionary_t enabled_objects
; ///< Enabled objects
111 bool all_objects_enabled
; ///< Should we print all objects?
115 * Initialize messages queues
116 * This function initializes all message queues
118 msg_bank_t
*msg_Create (void)
120 msg_bank_t
*bank
= malloc (sizeof (*bank
));
122 vlc_rwlock_init (&bank
->lock
);
123 vlc_dictionary_init (&bank
->enabled_objects
, 0);
124 bank
->all_objects_enabled
= true;
129 /* C locale to get error messages in English in the logs */
130 bank
->locale
= newlocale (LC_MESSAGES_MASK
, "C", (locale_t
)0);
132 vlc_mutex_lock( &msg_stack_lock
);
134 vlc_threadvar_create( &msg_context
, cleanup_msg_context
);
135 vlc_mutex_unlock( &msg_stack_lock
);
140 * Object Printing selection
142 static void const * kObjectPrintingEnabled
= &kObjectPrintingEnabled
;
143 static void const * kObjectPrintingDisabled
= &kObjectPrintingDisabled
;
145 #undef msg_EnableObjectPrinting
146 void msg_EnableObjectPrinting (vlc_object_t
*obj
, const char * psz_object
)
148 msg_bank_t
*bank
= libvlc_bank (obj
->p_libvlc
);
150 vlc_rwlock_wrlock (&bank
->lock
);
151 if( !strcmp(psz_object
, "all") )
152 bank
->all_objects_enabled
= true;
154 vlc_dictionary_insert (&bank
->enabled_objects
, psz_object
,
155 (void *)kObjectPrintingEnabled
);
156 vlc_rwlock_unlock (&bank
->lock
);
159 #undef msg_DisableObjectPrinting
160 void msg_DisableObjectPrinting (vlc_object_t
*obj
, const char * psz_object
)
162 msg_bank_t
*bank
= libvlc_bank (obj
->p_libvlc
);
164 vlc_rwlock_wrlock (&bank
->lock
);
165 if( !strcmp(psz_object
, "all") )
166 bank
->all_objects_enabled
= false;
168 vlc_dictionary_insert (&bank
->enabled_objects
, psz_object
,
169 (void *)kObjectPrintingDisabled
);
170 vlc_rwlock_unlock (&bank
->lock
);
174 * Destroy the message queues
176 * This functions prints all messages remaining in the queues,
177 * then frees all the allocated resources
178 * No other messages interface functions should be called after this one.
180 void msg_Destroy (msg_bank_t
*bank
)
182 if (unlikely(bank
->i_sub
!= 0))
183 fputs ("stale interface subscribers (LibVLC might crash)\n", stderr
);
185 vlc_mutex_lock( &msg_stack_lock
);
188 vlc_threadvar_delete( &msg_context
);
189 vlc_mutex_unlock( &msg_stack_lock
);
191 if (bank
->locale
!= (locale_t
)0)
192 freelocale (bank
->locale
);
194 vlc_dictionary_clear (&bank
->enabled_objects
, NULL
, NULL
);
196 vlc_rwlock_destroy (&bank
->lock
);
200 struct msg_subscription_t
202 libvlc_int_t
*instance
;
204 msg_cb_data_t
*opaque
;
208 * Subscribe to the message queue.
209 * Whenever a message is emitted, a callback will be called.
210 * Callback invocation are serialized within a subscription.
212 * @param instance LibVLC instance to get messages from
213 * @param cb callback function
214 * @param opaque data for the callback function
215 * @return a subscription pointer, or NULL in case of failure
217 msg_subscription_t
*msg_Subscribe (libvlc_int_t
*instance
, msg_callback_t cb
,
218 msg_cb_data_t
*opaque
)
220 msg_subscription_t
*sub
= malloc (sizeof (*sub
));
224 sub
->instance
= instance
;
226 sub
->opaque
= opaque
;
228 msg_bank_t
*bank
= libvlc_bank (instance
);
229 vlc_rwlock_wrlock (&bank
->lock
);
230 TAB_APPEND (bank
->i_sub
, bank
->pp_sub
, sub
);
231 vlc_rwlock_unlock (&bank
->lock
);
237 * Unsubscribe from the message queue.
238 * This function waits for the message callback to return if needed.
240 void msg_Unsubscribe (msg_subscription_t
*sub
)
242 msg_bank_t
*bank
= libvlc_bank (sub
->instance
);
244 vlc_rwlock_wrlock (&bank
->lock
);
245 TAB_REMOVE (bank
->i_sub
, bank
->pp_sub
, sub
);
246 vlc_rwlock_unlock (&bank
->lock
);
250 /*****************************************************************************
251 * msg_*: print a message
252 *****************************************************************************
253 * These functions queue a message for later printing.
254 *****************************************************************************/
255 void msg_Generic( vlc_object_t
*p_this
, int i_type
, const char *psz_module
,
256 const char *psz_format
, ... )
260 va_start( args
, psz_format
);
261 msg_GenericVa (p_this
, i_type
, psz_module
, psz_format
, args
);
266 * Destroys a message.
268 static void msg_Free (gc_object_t
*gc
)
270 msg_item_t
*msg
= vlc_priv (gc
, msg_item_t
);
272 free (msg
->psz_module
);
274 free (msg
->psz_header
);
280 * Add a message to a queue
282 * This function provides basic functionnalities to other msg_* functions.
283 * It adds a message to a queue (after having printed all stored messages if it
284 * is full). If the message can't be converted to string in memory, it issues
287 void msg_GenericVa (vlc_object_t
*p_this
, int i_type
,
288 const char *psz_module
,
289 const char *psz_format
, va_list _args
)
291 size_t i_header_size
; /* Size of the additionnal header */
293 char * psz_str
= NULL
; /* formatted message string */
294 char * psz_header
= NULL
;
299 if( p_this
->i_flags
& OBJECT_FLAGS_QUIET
||
300 (p_this
->i_flags
& OBJECT_FLAGS_NODBG
&& i_type
== VLC_MSG_DBG
) )
303 msg_bank_t
*bank
= libvlc_bank (p_this
->p_libvlc
);
304 locale_t locale
= uselocale (bank
->locale
);
307 /* Expand %m to strerror(errno) - only once */
308 char buf
[strlen( psz_format
) + 2001], *ptr
;
309 strcpy( buf
, psz_format
);
311 psz_format
= (const char*) buf
;
315 ptr
= strchr( ptr
, '%' );
325 strerror_r( errno
, errbuf
, 1001 );
327 int sockerr
= WSAGetLastError( );
330 strncpy( errbuf
, net_strerror( sockerr
), 1001 );
331 WSASetLastError( sockerr
);
334 || (strcmp ("Unknown network stack error", errbuf
) == 0))
335 strncpy( errbuf
, strerror( errno
), 1001 );
339 /* Escape '%' from the error string */
340 for( char *percent
= strchr( errbuf
, '%' );
342 percent
= strchr( percent
+ 2, '%' ) )
344 memmove( percent
+ 1, percent
, strlen( percent
) + 1 );
347 errlen
= strlen( errbuf
);
348 memmove( ptr
+ errlen
, ptr
+ 2, strlen( ptr
+ 2 ) + 1 );
349 memcpy( ptr
, errbuf
, errlen
);
350 break; /* Only once, so we don't overflow */
353 /* Looks for conversion specifier... */
356 while( *ptr
&& ( strchr( "diouxXeEfFgGaAcspn%", *ptr
) == NULL
) );
358 ptr
++; /* ...and skip it */
362 /* Convert message to string */
363 vlc_va_copy( args
, _args
);
364 if( vasprintf( &psz_str
, psz_format
, args
) == -1 )
368 if( psz_str
== NULL
)
370 int canc
= vlc_savecancel (); /* Do not print half of a message... */
372 fprintf( stderr
, "main warning: can't store message (%m): " );
376 /* we're not using GLIBC, so we are sure that the error description
377 * will be stored in the buffer we provide to strerror_r() */
378 strerror_r( errno
, psz_err
, 1001 );
380 strncpy( psz_err
, strerror( errno
), 1001 );
382 psz_err
[1000] = '\0';
383 fprintf( stderr
, "main warning: can't store message (%s): ", psz_err
);
385 vlc_va_copy( args
, _args
);
386 /* We should use utf8_vfprintf - but it calls malloc()... */
387 vfprintf( stderr
, psz_format
, args
);
389 fputs( "\n", stderr
);
390 vlc_restorecancel (canc
);
396 msg_item_t
* p_item
= malloc (sizeof (*p_item
));
400 vlc_gc_init (p_item
, msg_Free
);
401 p_item
->psz_module
= p_item
->psz_msg
= p_item
->psz_header
= NULL
;
407 while( p_obj
!= NULL
)
409 char *psz_old
= NULL
;
410 if( p_obj
->psz_header
)
412 i_header_size
+= strlen( p_obj
->psz_header
) + 4;
415 psz_old
= strdup( psz_header
);
416 psz_header
= xrealloc( psz_header
, i_header_size
);
417 snprintf( psz_header
, i_header_size
, "[%s] %s",
418 p_obj
->psz_header
, psz_old
);
422 psz_header
= xmalloc( i_header_size
);
423 snprintf( psz_header
, i_header_size
, "[%s]",
428 p_obj
= p_obj
->p_parent
;
431 /* Fill message information fields */
432 p_item
->i_type
= i_type
;
433 p_item
->i_object_id
= (uintptr_t)p_this
;
434 p_item
->psz_object_type
= p_this
->psz_object_type
;
435 p_item
->psz_module
= strdup( psz_module
);
436 p_item
->psz_msg
= psz_str
;
437 p_item
->psz_header
= psz_header
;
439 PrintMsg( p_this
, p_item
);
441 vlc_rwlock_rdlock (&bank
->lock
);
442 for (int i
= 0; i
< bank
->i_sub
; i
++)
444 msg_subscription_t
*sub
= bank
->pp_sub
[i
];
445 sub
->func (sub
->opaque
, p_item
, 0);
447 vlc_rwlock_unlock (&bank
->lock
);
448 msg_Release (p_item
);
451 /*****************************************************************************
452 * PrintMsg: output a standard message item to stderr
453 *****************************************************************************
454 * Print a message to stderr, with colour formatting if needed.
455 *****************************************************************************/
456 static void PrintMsg ( vlc_object_t
* p_this
, msg_item_t
* p_item
)
458 # define COL(x) "\033[" #x ";1m"
460 # define GREEN COL(32)
461 # define YELLOW COL(33)
462 # define WHITE COL(0)
463 # define GRAY "\033[0m"
465 static const char ppsz_type
[4][9] = { "", " error", " warning", " debug" };
466 static const char ppsz_color
[4][8] = { WHITE
, RED
, YELLOW
, GRAY
};
467 const char *psz_object
;
468 libvlc_priv_t
*priv
= libvlc_priv (p_this
->p_libvlc
);
469 msg_bank_t
*bank
= priv
->msg_bank
;
470 int i_type
= p_item
->i_type
;
475 if( priv
->i_verbose
< 0 ) return;
478 if( priv
->i_verbose
< 0 ) return;
481 if( priv
->i_verbose
< 1 ) return;
484 if( priv
->i_verbose
< 2 ) return;
488 psz_object
= p_item
->psz_object_type
;
489 void * val
= vlc_dictionary_value_for_key (&bank
->enabled_objects
,
491 if( val
== kObjectPrintingDisabled
)
493 if( val
== kObjectPrintingEnabled
)
497 val
= vlc_dictionary_value_for_key (&bank
->enabled_objects
,
499 if( val
== kObjectPrintingDisabled
)
501 if( val
== kObjectPrintingEnabled
)
503 else if( !bank
->all_objects_enabled
)
507 int canc
= vlc_savecancel ();
508 /* Send the message to stderr */
509 utf8_fprintf( stderr
, "[%s%p%s] %s%s%s %s%s: %s%s%s\n",
510 priv
->b_color
? GREEN
: "",
511 (void *)p_item
->i_object_id
,
512 priv
->b_color
? GRAY
: "",
513 p_item
->psz_header
? p_item
->psz_header
: "",
514 p_item
->psz_header
? " " : "",
515 p_item
->psz_module
, psz_object
,
517 priv
->b_color
? ppsz_color
[i_type
] : "",
519 priv
->b_color
? GRAY
: "" );
524 vlc_restorecancel (canc
);
527 static msg_context_t
* GetContext(void)
529 msg_context_t
*p_ctx
= vlc_threadvar_get( msg_context
);
532 p_ctx
= malloc( sizeof( msg_context_t
) );
535 p_ctx
->psz_message
= NULL
;
536 vlc_threadvar_set( msg_context
, p_ctx
);
541 void msg_StackSet( int i_code
, const char *psz_message
, ... )
544 msg_context_t
*p_ctx
= GetContext();
548 free( p_ctx
->psz_message
);
550 va_start( ap
, psz_message
);
551 if( vasprintf( &p_ctx
->psz_message
, psz_message
, ap
) == -1 )
552 p_ctx
->psz_message
= NULL
;
555 p_ctx
->i_code
= i_code
;
558 void msg_StackAdd( const char *psz_message
, ... )
562 msg_context_t
*p_ctx
= GetContext();
567 va_start( ap
, psz_message
);
568 if( vasprintf( &psz_tmp
, psz_message
, ap
) == -1 )
572 if( !p_ctx
->psz_message
)
573 p_ctx
->psz_message
= psz_tmp
;
577 if( asprintf( &psz_new
, "%s: %s", psz_tmp
, p_ctx
->psz_message
) == -1 )
580 free( p_ctx
->psz_message
);
581 p_ctx
->psz_message
= psz_new
;
586 const char* msg_StackMsg( void )
588 msg_context_t
*p_ctx
= GetContext();
590 return p_ctx
->psz_message
;