]> git.sesse.net Git - vlc/blob - src/misc/messages.c
Memleak
[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 config.h for output configuration.
5  *****************************************************************************
6  * Copyright (C) 1998-2005 the VideoLAN team
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
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.
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 General Public License for more details.
21  *
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  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include <stdio.h>                                               /* required */
31 #include <stdarg.h>                                       /* va_list for BSD */
32 #include <stdlib.h>                                              /* malloc() */
33 #include <string.h>                                            /* strerror() */
34
35 #include <vlc/vlc.h>
36
37 #ifdef HAVE_FCNTL_H
38 #   include <fcntl.h>                  /* O_CREAT, O_TRUNC, O_WRONLY, O_SYNC */
39 #endif
40
41 #include <errno.h>                                                  /* errno */
42
43 #ifdef HAVE_UNISTD_H
44 #   include <unistd.h>                                   /* close(), write() */
45 #endif
46
47 #include "vlc_interface.h"
48
49 /*****************************************************************************
50  * Local macros
51  *****************************************************************************/
52 #if defined(HAVE_VA_COPY)
53 #   define vlc_va_copy(dest,src) va_copy(dest,src)
54 #elif defined(HAVE___VA_COPY)
55 #   define vlc_va_copy(dest,src) __va_copy(dest,src)
56 #else
57 #   define vlc_va_copy(dest,src) (dest)=(src)
58 #endif
59
60 /*****************************************************************************
61  * Local prototypes
62  *****************************************************************************/
63 static void QueueMsg ( vlc_object_t *, int, int , const char *,
64                        const char *, va_list );
65 static void FlushMsg ( msg_queue_t * );
66 static void PrintMsg ( vlc_object_t *, msg_item_t * );
67 static void CreateMsgQueue( vlc_object_t *p_this, int i_queue );
68
69 /**
70  * Initialize messages queues
71  * This function initializes all message queues
72  */
73 void __msg_Create( vlc_object_t *p_this )
74 {
75     vlc_mutex_init( p_this, &(p_this->p_libvlc->msg_bank.lock) );
76
77     CreateMsgQueue( p_this, MSG_QUEUE_NORMAL );
78     CreateMsgQueue( p_this, MSG_QUEUE_HTTPD_ACCESS );
79
80 #ifdef UNDER_CE
81     p_this->p_libvlc->msg_bank.pp_queues[MSG_QUEUE_NORMAL]->logfile =
82         CreateFile( L"vlc-log.txt", GENERIC_WRITE,
83                     FILE_SHARE_READ|FILE_SHARE_WRITE, NULL,
84                     CREATE_ALWAYS, 0, NULL );
85     SetFilePointer( p_this->p_libvlc->msg_bank.pp_queues[MSG_QUEUE_NORMAL]->
86                                      logfile, 0, NULL, FILE_END );
87 #endif
88
89 }
90
91 static void CreateMsgQueue( vlc_object_t *p_this, int i_queue )
92 {
93     msg_queue_t *p_queue = (msg_queue_t *)malloc( sizeof( msg_queue_t ) );
94
95     vlc_mutex_init( p_this, &p_queue->lock );
96
97     p_queue->b_overflow = VLC_FALSE;
98     p_queue->i_id = i_queue;
99     p_queue->i_start = 0;
100     p_queue->i_stop = 0;
101
102     p_queue->i_sub = 0;
103     p_queue->pp_sub = NULL;
104
105     INSERT_ELEM( p_this->p_libvlc->msg_bank.pp_queues,
106                  p_this->p_libvlc->msg_bank.i_queues,
107                  i_queue,
108                  p_queue );
109 }
110
111 /**
112  * Flush all message queues
113  */
114 void __msg_Flush( vlc_object_t *p_this )
115 {
116     int i;
117
118     for( i = 0 ; i < p_this->p_libvlc->msg_bank.i_queues; i++ )
119     {
120         vlc_mutex_lock( &p_this->p_libvlc->msg_bank.pp_queues[i]->lock );
121         FlushMsg( p_this->p_libvlc->msg_bank.pp_queues[i] );
122         vlc_mutex_unlock( &p_this->p_libvlc->msg_bank.pp_queues[i]->lock );
123     }
124 }
125
126 /**
127  * Free resources allocated by msg_Create
128  *
129  * This functions prints all messages remaining in the normal queue,
130  * then frees all the allocated ressources
131  * No other messages interface functions should be called after this one.
132  */
133 void __msg_Destroy( vlc_object_t *p_this )
134 {
135     int i;
136     for( i = p_this->p_libvlc->msg_bank.i_queues -1 ; i >= 0;  i-- )
137     {
138         msg_queue_t *p_queue = p_this->p_libvlc->msg_bank.pp_queues[i];
139         if( p_queue->i_sub )
140         {
141             msg_Err( p_this, "stale interface subscribers" );
142         }
143         FlushMsg( p_queue );
144
145 #ifdef UNDER_CE
146         if( i == MSG_QUEUE_NORMAL )
147             CloseHandle( p_this->p_libvlc->msg_bank.pp_queues[MSG_QUEUE_NORMAL]->logfile );
148 #endif
149         /* Destroy lock */
150         vlc_mutex_destroy( &p_queue->lock );
151         REMOVE_ELEM( p_this->p_libvlc->msg_bank.pp_queues,
152                      p_this->p_libvlc->msg_bank.i_queues, i );
153         free( p_queue );
154     }
155     vlc_mutex_destroy( &(p_this->p_libvlc->msg_bank.lock) );
156 }
157
158 /**
159  * Subscribe to the message queue.
160  */
161 msg_subscription_t *__msg_Subscribe( vlc_object_t *p_this, int i_queue )
162 {
163     msg_bank_t *p_bank = &p_this->p_libvlc->msg_bank;
164     msg_subscription_t *p_sub = malloc( sizeof( msg_subscription_t ) );
165     msg_queue_t *p_queue = NULL;
166     int i;
167
168     vlc_mutex_lock( &p_bank->lock );
169
170     for( i = 0 ; i <p_bank->i_queues ;i++ )
171     {
172         if( p_bank->pp_queues[i]->i_id == i_queue )
173         {
174             p_queue = p_bank->pp_queues[i];
175         }
176     }
177
178     if( p_queue == NULL )
179     {
180         vlc_mutex_unlock( &p_bank->lock );
181         return NULL;
182     }
183
184     vlc_mutex_lock( &p_queue->lock );
185
186     /* Add subscription to the list */
187     INSERT_ELEM( p_bank->pp_queues[i_queue]->pp_sub,
188                  p_bank->pp_queues[i_queue]->i_sub,
189                  p_bank->pp_queues[i_queue]->i_sub,
190                  p_sub );
191
192     p_sub->i_start = p_queue->i_start;
193     p_sub->pi_stop = &p_queue->i_stop;
194
195     p_sub->p_msg   = p_queue->msg;
196     p_sub->p_lock  = &p_queue->lock;
197
198     vlc_mutex_unlock( &p_queue->lock );
199     vlc_mutex_unlock( &p_bank->lock );
200
201     return p_sub;
202 }
203
204 /**
205  * Unsubscribe from the message queue.
206  */
207 void __msg_Unsubscribe( vlc_object_t *p_this, msg_subscription_t *p_sub )
208 {
209     msg_bank_t *p_bank = &p_this->p_libvlc->msg_bank;
210     int i,j;
211
212     vlc_mutex_lock( &p_bank->lock );
213
214     for( i = 0 ; i< p_bank->i_queues ; i++ )
215     {
216         vlc_mutex_lock( & p_bank->pp_queues[i]->lock );
217         for( j = 0 ; j< p_bank->pp_queues[i]->i_sub ; j++ )
218         {
219             if( p_bank->pp_queues[i]->pp_sub[j] == p_sub )
220             {
221                 REMOVE_ELEM( p_bank->pp_queues[i]->pp_sub,
222                              p_bank->pp_queues[i]->i_sub,
223                              j );
224                 if( p_sub ) free( p_sub );
225             }
226         }
227         vlc_mutex_unlock( & p_bank->pp_queues[i]->lock );
228     }
229
230     vlc_mutex_unlock( &p_bank->lock );
231 }
232
233 /*****************************************************************************
234  * __msg_*: print a message
235  *****************************************************************************
236  * These functions queue a message for later printing.
237  *****************************************************************************/
238 void __msg_Generic( vlc_object_t *p_this, int i_queue_id, int i_type,
239                     const char *psz_module,
240                     const char *psz_format, ... )
241 {
242     va_list args;
243
244     va_start( args, psz_format );
245     QueueMsg( p_this, i_queue_id, i_type, psz_module, psz_format, args );
246     va_end( args );
247 }
248
249 void __msg_GenericVa( vlc_object_t *p_this, int i_queue_id,
250                       int i_type, const char *psz_module,
251                       const char *psz_format, va_list args )
252 {
253     QueueMsg( p_this, i_queue_id, i_type, psz_module, psz_format, args );
254 }
255
256 /* Generic functions used when variadic macros are not available. */
257 #define DECLARE_MSG_FN( FN_NAME, FN_TYPE ) \
258     void FN_NAME( vlc_object_t *p_this, const char *psz_format, ... ) \
259     { \
260         va_list args; \
261         va_start( args, psz_format ); \
262         QueueMsg( (vlc_object_t *)p_this,MSG_QUEUE_NORMAL, FN_TYPE, "unknown", \
263                   psz_format, args ); \
264         va_end( args ); \
265     } \
266     struct _
267 /**
268  * Output an informational message.
269  * \note Do not use this for debug messages
270  * \see input_AddInfo
271  */
272 DECLARE_MSG_FN( __msg_Info, VLC_MSG_INFO );
273 /**
274  * Output an error message.
275  */
276 DECLARE_MSG_FN( __msg_Err,  VLC_MSG_ERR );
277 /**
278  * Output a waring message
279  */
280 DECLARE_MSG_FN( __msg_Warn, VLC_MSG_WARN );
281 /**
282  * Output a debug message
283  */
284 DECLARE_MSG_FN( __msg_Dbg,  VLC_MSG_DBG );
285
286 /**
287  * Add a message to a queue
288  *
289  * This function provides basic functionnalities to other msg_* functions.
290  * It adds a message to a queue (after having printed all stored messages if it
291  * is full). If the message can't be converted to string in memory, it issues
292  * a warning.
293  */
294 static void QueueMsg( vlc_object_t *p_this, int i_queue_id, int i_type,
295                       const char *psz_module,
296                       const char *psz_format, va_list _args )
297 {
298     int         i_header_size;               /* Size of the additionnal header */
299     vlc_object_t *p_obj;
300     msg_bank_t * p_bank = &p_this->p_libvlc->msg_bank;       /* message bank */
301     msg_queue_t *p_queue = NULL;
302     char *       psz_str = NULL;                 /* formatted message string */
303     char *       psz_header = NULL;
304     va_list      args;
305     msg_item_t * p_item = NULL;                        /* pointer to message */
306     msg_item_t   item;                    /* message in case of a full queue */
307
308 #if !defined(HAVE_VASPRINTF) || defined(__APPLE__) || defined(SYS_BEOS)
309     int          i_size = strlen(psz_format) + INTF_MAX_MSG_SIZE;
310 #endif
311     int i;
312
313     if( p_this->i_flags & OBJECT_FLAGS_QUIET ||
314         (p_this->i_flags & OBJECT_FLAGS_NODBG &&
315          i_type == VLC_MSG_DBG ) )
316     {
317         return;
318     }
319
320     /*
321      * Convert message to string
322      */
323 #if defined(HAVE_VASPRINTF) && !defined(__APPLE__) && !defined( SYS_BEOS )
324     vlc_va_copy( args, _args );
325     vasprintf( &psz_str, psz_format, args );
326     va_end( args );
327 #else
328     psz_str = (char*) malloc( i_size * sizeof(char) );
329 #endif
330
331     if( psz_str == NULL )
332     {
333         fprintf( stderr, "main warning: can't store message (%s): ",
334                  strerror(errno) );
335         vlc_va_copy( args, _args );
336         vfprintf( stderr, psz_format, args );
337         va_end( args );
338         fprintf( stderr, "\n" );
339         return;
340     }
341
342     i_header_size = 0;
343     p_obj = p_this;
344     while( p_obj != NULL )
345     {
346         char *psz_old = NULL;
347         if( p_obj == NULL ) break;
348         if( p_obj->psz_header )
349         {
350             i_header_size += strlen( p_obj->psz_header ) + 4;
351             if( psz_header )
352             {
353                 psz_old = strdup( psz_header );
354                 psz_header = (char*)realloc( psz_header, i_header_size );
355                 snprintf( psz_header, i_header_size , "[%s] %s",
356                           p_obj->psz_header, psz_old );
357             }
358             else
359             {
360                 psz_header = (char *)malloc( i_header_size );
361                 snprintf( psz_header, i_header_size, "[%s]",
362                           p_obj->psz_header );
363             }
364         }
365         if( psz_old ) free( psz_old );
366         p_obj = p_obj->p_parent;
367     }
368
369 #if !defined(HAVE_VASPRINTF) || defined(__APPLE__) || defined(SYS_BEOS)
370     vlc_va_copy( args, _args );
371     vsnprintf( psz_str, i_size, psz_format, args );
372     va_end( args );
373     psz_str[ i_size - 1 ] = 0; /* Just in case */
374 #endif
375
376     /* Put message in queue */
377     vlc_mutex_lock( &p_bank->lock );
378     for( i = 0 ; i <p_bank->i_queues ;i++ )
379     {
380         if( p_bank->pp_queues[i]->i_id == i_queue_id )
381         {
382             p_queue = p_bank->pp_queues[i];
383         }
384     }
385
386     if( p_queue == NULL )
387     {
388         vlc_mutex_unlock( &p_bank->lock );
389         if( psz_str ) free( psz_str );
390         if( psz_header ) free( psz_header );
391         return;
392     }
393
394     vlc_mutex_lock( &p_queue->lock );
395
396     /* Check there is room in the queue for our message */
397     if( p_queue->b_overflow )
398     {
399         FlushMsg( p_queue );
400
401         if( ((p_queue->i_stop - p_queue->i_start + 1) % VLC_MSG_QSIZE) == 0 )
402         {
403             /* Still in overflow mode, print from a dummy item */
404             p_item = &item;
405         }
406         else
407         {
408             /* Pheeew, at last, there is room in the queue! */
409             p_queue->b_overflow = VLC_FALSE;
410         }
411     }
412     else if( ((p_queue->i_stop - p_queue->i_start + 2) % VLC_MSG_QSIZE) == 0 )
413     {
414         FlushMsg( p_queue );
415
416         if( ((p_queue->i_stop - p_queue->i_start + 2) % VLC_MSG_QSIZE) == 0 )
417         {
418             p_queue->b_overflow = VLC_TRUE;
419
420             if( p_queue->i_id == MSG_QUEUE_NORMAL )
421             {
422                /* Put the overflow message in the queue */
423                 p_item = p_queue->msg + p_queue->i_stop;
424                 p_queue->i_stop = (p_queue->i_stop + 1) % VLC_MSG_QSIZE;
425
426                 p_item->i_type =        VLC_MSG_WARN;
427                 p_item->i_object_id =   p_this->i_object_id;
428                 p_item->i_object_type = p_this->i_object_type;
429                 p_item->psz_module =    strdup( "message" );
430                 p_item->psz_msg =       strdup( "message queue overflowed" );
431                 p_item->psz_header =    NULL;
432
433                PrintMsg( p_this, p_item );
434                /* We print from a dummy item */
435                p_item = &item;
436             }
437         }
438     }
439
440     if( !p_queue->b_overflow )
441     {
442         /* Put the message in the queue */
443         p_item = p_queue->msg + p_queue->i_stop;
444         p_queue->i_stop = (p_queue->i_stop + 1) % VLC_MSG_QSIZE;
445     }
446
447     /* Fill message information fields */
448     p_item->i_type =        i_type;
449     p_item->i_object_id =   p_this->i_object_id;
450     p_item->i_object_type = p_this->i_object_type;
451     p_item->psz_module =    strdup( psz_module );
452     p_item->psz_msg =       psz_str;
453     p_item->psz_header =    psz_header;
454
455     if( p_queue->i_id == MSG_QUEUE_NORMAL )
456         PrintMsg( p_this, p_item );
457
458     if( p_queue->b_overflow )
459     {
460         if( p_item->psz_module )
461             free( p_item->psz_module );
462         if( p_item->psz_msg )
463             free( p_item->psz_msg );
464         if( p_item->psz_header )
465             free( p_item->psz_header );
466     }
467
468     vlc_mutex_unlock ( &p_queue->lock );
469     vlc_mutex_unlock( &p_bank->lock );
470 }
471
472 /* following functions are local */
473
474 /*****************************************************************************
475  * FlushMsg
476  *****************************************************************************
477  * Print all messages remaining in queue. MESSAGE QUEUE MUST BE LOCKED, since
478  * this function does not check the lock.
479  *****************************************************************************/
480 static void FlushMsg ( msg_queue_t *p_queue )
481 {
482     int i_index, i_start, i_stop;
483
484     /* Get the maximum message index that can be freed */
485     i_stop = p_queue->i_stop;
486
487     /* Check until which value we can free messages */
488     for( i_index = 0; i_index < p_queue->i_sub; i_index++ )
489     {
490         i_start = p_queue->pp_sub[ i_index ]->i_start;
491
492         /* If this subscriber is late, we don't free messages before
493          * his i_start value, otherwise he'll miss messages */
494         if(   ( i_start < i_stop
495                && (p_queue->i_stop <= i_start || i_stop <= p_queue->i_stop) )
496            || ( i_stop < i_start
497                && (i_stop <= p_queue->i_stop && p_queue->i_stop <= i_start) ) )
498         {
499             i_stop = i_start;
500         }
501     }
502
503     /* Free message data */
504     for( i_index = p_queue->i_start;
505          i_index != i_stop;
506          i_index = (i_index+1) % VLC_MSG_QSIZE )
507     {
508         if( p_queue->msg[i_index].psz_msg )
509             free( p_queue->msg[i_index].psz_msg );
510         if( p_queue->msg[i_index].psz_module )
511             free( p_queue->msg[i_index].psz_module );
512         if( p_queue->msg[i_index].psz_header )
513             free( p_queue->msg[i_index].psz_header );
514     }
515
516     /* Update the new start value */
517     p_queue->i_start = i_index;
518 }
519
520 /*****************************************************************************
521  * PrintMsg: output a standard message item to stderr
522  *****************************************************************************
523  * Print a message to stderr, with colour formatting if needed.
524  *****************************************************************************/
525 static void PrintMsg ( vlc_object_t * p_this, msg_item_t * p_item )
526 {
527 #   define COL(x)  "\033[" #x ";1m"
528 #   define RED     COL(31)
529 #   define GREEN   COL(32)
530 #   define YELLOW  COL(33)
531 #   define WHITE   COL(37)
532 #   define GRAY    "\033[0m"
533
534 #ifdef UNDER_CE
535     int i_dummy;
536 #endif
537     static const char * ppsz_type[4] = { "", " error", " warning", " debug" };
538     static const char *ppsz_color[4] = { WHITE, RED, YELLOW, GRAY };
539     char *psz_object = "private";
540     int i_type = p_item->i_type;
541
542     switch( i_type )
543     {
544         case VLC_MSG_ERR:
545             if( p_this->p_libvlc->i_verbose < 0 ) return;
546             break;
547         case VLC_MSG_INFO:
548             if( p_this->p_libvlc->i_verbose < 0 ) return;
549             break;
550         case VLC_MSG_WARN:
551             if( p_this->p_libvlc->i_verbose < 1 ) return;
552             break;
553         case VLC_MSG_DBG:
554             if( p_this->p_libvlc->i_verbose < 2 ) return;
555             break;
556     }
557
558     switch( p_item->i_object_type )
559     {
560         case VLC_OBJECT_ROOT: psz_object = "root"; break;
561         case VLC_OBJECT_VLC: psz_object = "vlc"; break;
562         case VLC_OBJECT_MODULE: psz_object = "module"; break;
563         case VLC_OBJECT_INTF: psz_object = "interface"; break;
564         case VLC_OBJECT_PLAYLIST: psz_object = "playlist"; break;
565         case VLC_OBJECT_ITEM: psz_object = "item"; break;
566         case VLC_OBJECT_INPUT: psz_object = "input"; break;
567         case VLC_OBJECT_DECODER: psz_object = "decoder"; break;
568         case VLC_OBJECT_PACKETIZER: psz_object = "packetizer"; break;
569         case VLC_OBJECT_ENCODER: psz_object = "encoder"; break;
570         case VLC_OBJECT_VOUT: psz_object = "video output"; break;
571         case VLC_OBJECT_AOUT: psz_object = "audio output"; break;
572         case VLC_OBJECT_SOUT: psz_object = "stream output"; break;
573         case VLC_OBJECT_HTTPD: psz_object = "http daemon"; break;
574         case VLC_OBJECT_DIALOGS: psz_object = "dialogs provider"; break;
575         case VLC_OBJECT_VLM: psz_object = "vlm"; break;
576         case VLC_OBJECT_ANNOUNCE: psz_object = "announce handler"; break;
577         case VLC_OBJECT_DEMUX: psz_object = "demuxer"; break;
578         case VLC_OBJECT_ACCESS: psz_object = "access"; break;
579     }
580
581 #ifdef UNDER_CE
582 #   define CE_WRITE(str) WriteFile( p_this->p_libvlc->msg_bank.pp_queues[MSG_QUEUE_NORMAL]->logfile, \
583                                     str, strlen(str), &i_dummy, NULL );
584     CE_WRITE( p_item->psz_module );
585     CE_WRITE( " " );
586     CE_WRITE( psz_object );
587     CE_WRITE( ppsz_type[i_type] );
588     CE_WRITE( ": " );
589     CE_WRITE( p_item->psz_msg );
590     CE_WRITE( "\r\n" );
591     FlushFileBuffers( p_this->p_libvlc->msg_bank.pp_queues[MSG_QUEUE_NORMAL]->logfile );
592
593 #else
594     /* Send the message to stderr */
595     if( p_this->p_libvlc->b_color )
596     {
597         if( p_item->psz_header )
598         {
599             fprintf( stderr, "[" GREEN "%.8i" GRAY "] %s %s %s%s: %s%s" GRAY
600                               "\n",
601                          p_item->i_object_id, p_item->psz_header,
602                          p_item->psz_module, psz_object,
603                          ppsz_type[i_type], ppsz_color[i_type],
604                          p_item->psz_msg );
605         }
606         else
607         {
608              fprintf( stderr, "[" GREEN "%.8i" GRAY "] %s %s%s: %s%s" GRAY "\n",
609                          p_item->i_object_id, p_item->psz_module, psz_object,
610                          ppsz_type[i_type], ppsz_color[i_type],
611                          p_item->psz_msg );
612         }
613     }
614     else
615     {
616         if( p_item->psz_header )
617         {
618             fprintf( stderr, "[%.8i] %s %s %s%s: %s\n", p_item->i_object_id,
619                          p_item->psz_header, p_item->psz_module,
620                          psz_object, ppsz_type[i_type], p_item->psz_msg );
621         }
622         else
623         {
624             fprintf( stderr, "[%.8i] %s %s%s: %s\n", p_item->i_object_id,
625                          p_item->psz_module, psz_object, ppsz_type[i_type],
626                          p_item->psz_msg );
627         }
628     }
629
630 #   if defined(WIN32)
631     fflush( stderr );
632 #   endif
633 #endif
634 }