]> granicus.if.org Git - imagemagick/blob - MagickCore/log.c
...
[imagemagick] / MagickCore / log.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                             L       OOO    GGGG                             %
7 %                             L      O   O  G                                 %
8 %                             L      O   O  G GG                              %
9 %                             L      O   O  G   G                             %
10 %                             LLLLL   OOO    GGG                              %
11 %                                                                             %
12 %                                                                             %
13 %                             MagickCore Log Events                           %
14 %                                                                             %
15 %                               Software Design                               %
16 %                                    Cristy                                   %
17 %                                September 2002                               %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2017 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    https://www.imagemagick.org/script/license.php                           %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/blob.h"
44 #include "MagickCore/client.h"
45 #include "MagickCore/configure.h"
46 #include "MagickCore/configure-private.h"
47 #include "MagickCore/exception.h"
48 #include "MagickCore/exception-private.h"
49 #include "MagickCore/linked-list.h"
50 #include "MagickCore/log.h"
51 #include "MagickCore/log-private.h"
52 #include "MagickCore/memory_.h"
53 #include "MagickCore/nt-base-private.h"
54 #include "MagickCore/option.h"
55 #include "MagickCore/semaphore.h"
56 #include "MagickCore/timer.h"
57 #include "MagickCore/string_.h"
58 #include "MagickCore/string-private.h"
59 #include "MagickCore/token.h"
60 #include "MagickCore/thread_.h"
61 #include "MagickCore/thread-private.h"
62 #include "MagickCore/utility.h"
63 #include "MagickCore/utility-private.h"
64 #include "MagickCore/version.h"
65 #include "MagickCore/xml-tree.h"
66 #include "MagickCore/xml-tree-private.h"
67 \f
68 /*
69   Define declarations.
70 */
71 #define LogFilename  "log.xml"
72 \f
73 /*
74   Typedef declarations.
75 */
76 typedef enum
77 {
78   UndefinedHandler = 0x0000,
79   NoHandler = 0x0000,
80   ConsoleHandler = 0x0001,
81   StdoutHandler = 0x0002,
82   StderrHandler = 0x0004,
83   FileHandler = 0x0008,
84   DebugHandler = 0x0010,
85   EventHandler = 0x0020,
86   MethodHandler = 0x0040
87 } LogHandlerType;
88
89 typedef struct _EventInfo
90 {
91   char
92     *name;
93
94   LogEventType
95     event;
96 } EventInfo;
97
98 typedef struct _HandlerInfo
99 {
100   const char
101     *name;
102
103   LogHandlerType
104     handler;
105 } HandlerInfo;
106
107 struct _LogInfo
108 {
109   LogEventType
110     event_mask;
111
112   LogHandlerType
113     handler_mask;
114
115   char
116     *path,
117     *name,
118     *filename,
119     *format;
120
121   size_t
122     generations,
123     limit;
124
125   FILE
126     *file;
127
128   size_t
129     generation;
130
131   MagickBooleanType
132     append,
133     stealth;
134
135   TimerInfo
136     timer;
137
138   size_t
139     signature;
140
141   MagickLogMethod
142     method;
143 };
144
145 typedef struct _LogMapInfo
146 {
147   const LogEventType
148     event_mask;
149
150   const LogHandlerType
151     handler_mask;
152
153   const char
154     *filename,
155     *format;
156 } LogMapInfo;
157 \f
158 /*
159   Static declarations.
160 */
161 static const HandlerInfo
162   LogHandlers[32] =
163   {
164     { "Console", ConsoleHandler },
165     { "Debug", DebugHandler },
166     { "Event", EventHandler },
167     { "File", FileHandler },
168     { "None", NoHandler },
169     { "Stderr", StderrHandler },
170     { "Stdout", StdoutHandler },
171     { (char *) NULL, UndefinedHandler },
172     { (char *) NULL, UndefinedHandler },
173     { (char *) NULL, UndefinedHandler },
174     { (char *) NULL, UndefinedHandler },
175     { (char *) NULL, UndefinedHandler },
176     { (char *) NULL, UndefinedHandler },
177     { (char *) NULL, UndefinedHandler },
178     { (char *) NULL, UndefinedHandler },
179     { (char *) NULL, UndefinedHandler },
180     { (char *) NULL, UndefinedHandler },
181     { (char *) NULL, UndefinedHandler },
182     { (char *) NULL, UndefinedHandler },
183     { (char *) NULL, UndefinedHandler },
184     { (char *) NULL, UndefinedHandler },
185     { (char *) NULL, UndefinedHandler },
186     { (char *) NULL, UndefinedHandler },
187     { (char *) NULL, UndefinedHandler },
188     { (char *) NULL, UndefinedHandler },
189     { (char *) NULL, UndefinedHandler },
190     { (char *) NULL, UndefinedHandler },
191     { (char *) NULL, UndefinedHandler },
192     { (char *) NULL, UndefinedHandler },
193     { (char *) NULL, UndefinedHandler },
194     { (char *) NULL, UndefinedHandler },
195     { (char *) NULL, UndefinedHandler }
196   };
197
198 static const LogMapInfo
199   LogMap[] =
200   {
201     { NoEvents, ConsoleHandler, "Magick-%g.log",
202       "%t %r %u %v %d %c[%p]: %m/%f/%l/%d\\n  %e" }
203   };
204
205 static char
206   log_name[MagickPathExtent] = "Magick";
207
208 static LinkedListInfo
209   *log_cache = (LinkedListInfo *) NULL;
210
211 static SemaphoreInfo
212   *event_semaphore = (SemaphoreInfo *) NULL,
213   *log_semaphore = (SemaphoreInfo *) NULL;
214 \f
215 /*
216   Forward declarations.
217 */
218 static LogHandlerType
219   ParseLogHandlers(const char *);
220
221 static LogInfo
222   *GetLogInfo(const char *,ExceptionInfo *);
223
224 static MagickBooleanType
225   IsLogCacheInstantiated(ExceptionInfo *),
226   LoadLogCache(LinkedListInfo *,const char *,const char *,const size_t,
227     ExceptionInfo *);
228 \f
229 /*
230 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
231 %                                                                             %
232 %                                                                             %
233 %                                                                             %
234 %  A c q u i r e L o g C a c h e                                              %
235 %                                                                             %
236 %                                                                             %
237 %                                                                             %
238 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
239 %
240 %  AcquireLogCache() caches one or more log configurations which provides a
241 %  mapping between log attributes and log name.
242 %
243 %  The format of the AcquireLogCache method is:
244 %
245 %      LinkedListInfo *AcquireLogCache(const char *filename,
246 %        ExceptionInfo *exception)
247 %
248 %  A description of each parameter follows:
249 %
250 %    o filename: the log configuration filename.
251 %
252 %    o exception: return any errors or warnings in this structure.
253 %
254 */
255 static LinkedListInfo *AcquireLogCache(const char *filename,
256   ExceptionInfo *exception)
257 {
258   LinkedListInfo
259     *cache;
260
261   MagickStatusType
262     status;
263
264   register ssize_t
265     i;
266
267   /*
268     Load external log map.
269   */
270   cache=NewLinkedList(0);
271   if (cache == (LinkedListInfo *) NULL)
272     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
273   status=MagickTrue;
274 #if !defined(MAGICKCORE_ZERO_CONFIGURATION_SUPPORT)
275   {
276     const StringInfo
277       *option;
278
279     LinkedListInfo
280       *options;
281
282     options=GetConfigureOptions(filename,exception);
283     option=(const StringInfo *) GetNextValueInLinkedList(options);
284     while (option != (const StringInfo *) NULL)
285     {
286       status&=LoadLogCache(cache,(const char *) GetStringInfoDatum(option),
287         GetStringInfoPath(option),0,exception);
288       option=(const StringInfo *) GetNextValueInLinkedList(options);
289     }
290     options=DestroyConfigureOptions(options);
291   }
292 #endif
293   /*
294     Load built-in log map.
295   */
296   for (i=0; i < (ssize_t) (sizeof(LogMap)/sizeof(*LogMap)); i++)
297   {
298     LogInfo
299       *log_info;
300
301     register const LogMapInfo
302       *p;
303
304     p=LogMap+i;
305     log_info=(LogInfo *) AcquireMagickMemory(sizeof(*log_info));
306     if (log_info == (LogInfo *) NULL)
307       {
308         (void) ThrowMagickException(exception,GetMagickModule(),
309           ResourceLimitError,"MemoryAllocationFailed","`%s'",p->filename);
310         continue;
311       }
312     (void) ResetMagickMemory(log_info,0,sizeof(*log_info));
313     log_info->path=ConstantString("[built-in]");
314     GetTimerInfo((TimerInfo *) &log_info->timer);
315     log_info->event_mask=p->event_mask;
316     log_info->handler_mask=p->handler_mask;
317     log_info->filename=ConstantString(p->filename);
318     log_info->format=ConstantString(p->format);
319     log_info->signature=MagickCoreSignature;
320     status&=AppendValueToLinkedList(cache,log_info);
321     if (status == MagickFalse)
322       (void) ThrowMagickException(exception,GetMagickModule(),
323         ResourceLimitError,"MemoryAllocationFailed","`%s'",log_info->name);
324   }
325   return(cache);
326 }
327 \f
328 /*
329 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
330 %                                                                             %
331 %                                                                             %
332 %                                                                             %
333 %   C l o s e M a g i c k L o g                                               %
334 %                                                                             %
335 %                                                                             %
336 %                                                                             %
337 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
338 %
339 %  CloseMagickLog() closes the Magick log.
340 %
341 %  The format of the CloseMagickLog method is:
342 %
343 %      CloseMagickLog(void)
344 %
345 */
346 MagickExport void CloseMagickLog(void)
347 {
348   ExceptionInfo
349     *exception;
350
351   LogInfo
352     *log_info;
353
354   if (IsEventLogging() == MagickFalse)
355     return;
356   exception=AcquireExceptionInfo();
357   log_info=GetLogInfo("*",exception);
358   exception=DestroyExceptionInfo(exception);
359   LockSemaphoreInfo(log_semaphore);
360   if (log_info->file != (FILE *) NULL)
361     {
362       (void) FormatLocaleFile(log_info->file,"</log>\n");
363       (void) fclose(log_info->file);
364       log_info->file=(FILE *) NULL;
365     }
366   UnlockSemaphoreInfo(log_semaphore);
367 }
368 \f
369 /*
370 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
371 %                                                                             %
372 %                                                                             %
373 %                                                                             %
374 +   G e t L o g I n f o                                                       %
375 %                                                                             %
376 %                                                                             %
377 %                                                                             %
378 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
379 %
380 %  GetLogInfo() searches the log list for the specified name and if found
381 %  returns attributes for that log.
382 %
383 %  The format of the GetLogInfo method is:
384 %
385 %      LogInfo *GetLogInfo(const char *name,ExceptionInfo *exception)
386 %
387 %  A description of each parameter follows:
388 %
389 %    o name: the log name.
390 %
391 %    o exception: return any errors or warnings in this structure.
392 %
393 */
394 static LogInfo *GetLogInfo(const char *name,ExceptionInfo *exception)
395 {
396   register LogInfo
397     *p;
398
399   assert(exception != (ExceptionInfo *) NULL);
400   if (IsLogCacheInstantiated(exception) == MagickFalse)
401     return((LogInfo *) NULL);
402   /*
403     Search for log tag.
404   */
405   LockSemaphoreInfo(log_semaphore);
406   ResetLinkedListIterator(log_cache);
407   p=(LogInfo *) GetNextValueInLinkedList(log_cache);
408   if ((name == (const char *) NULL) || (LocaleCompare(name,"*") == 0))
409     {
410       UnlockSemaphoreInfo(log_semaphore);
411       return(p);
412     }
413   while (p != (LogInfo *) NULL)
414   {
415     if (LocaleCompare(name,p->name) == 0)
416       break;
417     p=(LogInfo *) GetNextValueInLinkedList(log_cache);
418   }
419   if (p != (LogInfo *) NULL)
420     (void) InsertValueInLinkedList(log_cache,0,
421       RemoveElementByValueFromLinkedList(log_cache,p));
422   UnlockSemaphoreInfo(log_semaphore);
423   return(p);
424 }
425 \f
426 /*
427 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
428 %                                                                             %
429 %                                                                             %
430 %                                                                             %
431 %   G e t L o g I n f o L i s t                                               %
432 %                                                                             %
433 %                                                                             %
434 %                                                                             %
435 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
436 %
437 %  GetLogInfoList() returns any logs that match the specified pattern.
438 %
439 %  The format of the GetLogInfoList function is:
440 %
441 %      const LogInfo **GetLogInfoList(const char *pattern,
442 %        size_t *number_preferences,ExceptionInfo *exception)
443 %
444 %  A description of each parameter follows:
445 %
446 %    o pattern: Specifies a pointer to a text string containing a pattern.
447 %
448 %    o number_preferences:  This integer returns the number of logs in the list.
449 %
450 %    o exception: return any errors or warnings in this structure.
451 %
452 */
453 #if defined(__cplusplus) || defined(c_plusplus)
454 extern "C" {
455 #endif
456
457 static int LogInfoCompare(const void *x,const void *y)
458 {
459   const LogInfo
460     **p,
461     **q;
462
463   p=(const LogInfo **) x,
464   q=(const LogInfo **) y;
465   if (LocaleCompare((*p)->path,(*q)->path) == 0)
466     return(LocaleCompare((*p)->name,(*q)->name));
467   return(LocaleCompare((*p)->path,(*q)->path));
468 }
469
470 #if defined(__cplusplus) || defined(c_plusplus)
471 }
472 #endif
473
474 MagickExport const LogInfo **GetLogInfoList(const char *pattern,
475   size_t *number_preferences,ExceptionInfo *exception)
476 {
477   const LogInfo
478     **preferences;
479
480   register const LogInfo
481     *p;
482
483   register ssize_t
484     i;
485
486   /*
487     Allocate log list.
488   */
489   assert(pattern != (char *) NULL);
490   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
491   assert(number_preferences != (size_t *) NULL);
492   *number_preferences=0;
493   p=GetLogInfo("*",exception);
494   if (p == (const LogInfo *) NULL)
495     return((const LogInfo **) NULL);
496   preferences=(const LogInfo **) AcquireQuantumMemory((size_t)
497     GetNumberOfElementsInLinkedList(log_cache)+1UL,sizeof(*preferences));
498   if (preferences == (const LogInfo **) NULL)
499     return((const LogInfo **) NULL);
500   /*
501     Generate log list.
502   */
503   LockSemaphoreInfo(log_semaphore);
504   ResetLinkedListIterator(log_cache);
505   p=(const LogInfo *) GetNextValueInLinkedList(log_cache);
506   for (i=0; p != (const LogInfo *) NULL; )
507   {
508     if ((p->stealth == MagickFalse) &&
509         (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
510       preferences[i++]=p;
511     p=(const LogInfo *) GetNextValueInLinkedList(log_cache);
512   }
513   UnlockSemaphoreInfo(log_semaphore);
514   qsort((void *) preferences,(size_t) i,sizeof(*preferences),LogInfoCompare);
515   preferences[i]=(LogInfo *) NULL;
516   *number_preferences=(size_t) i;
517   return(preferences);
518 }
519 \f
520 /*
521 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
522 %                                                                             %
523 %                                                                             %
524 %                                                                             %
525 %   G e t L o g L i s t                                                       %
526 %                                                                             %
527 %                                                                             %
528 %                                                                             %
529 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
530 %
531 %  GetLogList() returns any logs that match the specified pattern.
532 %
533 %  The format of the GetLogList function is:
534 %
535 %      char **GetLogList(const char *pattern,size_t *number_preferences,
536 %        ExceptionInfo *exception)
537 %
538 %  A description of each parameter follows:
539 %
540 %    o pattern: Specifies a pointer to a text string containing a pattern.
541 %
542 %    o number_preferences:  This integer returns the number of logs in the list.
543 %
544 %    o exception: return any errors or warnings in this structure.
545 %
546 */
547
548 #if defined(__cplusplus) || defined(c_plusplus)
549 extern "C" {
550 #endif
551
552 static int LogCompare(const void *x,const void *y)
553 {
554   register const char
555     **p,
556     **q;
557
558   p=(const char **) x;
559   q=(const char **) y;
560   return(LocaleCompare(*p,*q));
561 }
562
563 #if defined(__cplusplus) || defined(c_plusplus)
564 }
565 #endif
566
567 MagickExport char **GetLogList(const char *pattern,size_t *number_preferences,
568   ExceptionInfo *exception)
569 {
570   char
571     **preferences;
572
573   register const LogInfo
574     *p;
575
576   register ssize_t
577     i;
578
579   /*
580     Allocate log list.
581   */
582   assert(pattern != (char *) NULL);
583   (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern);
584   assert(number_preferences != (size_t *) NULL);
585   *number_preferences=0;
586   p=GetLogInfo("*",exception);
587   if (p == (const LogInfo *) NULL)
588     return((char **) NULL);
589   preferences=(char **) AcquireQuantumMemory((size_t)
590     GetNumberOfElementsInLinkedList(log_cache)+1UL,sizeof(*preferences));
591   if (preferences == (char **) NULL)
592     return((char **) NULL);
593   /*
594     Generate log list.
595   */
596   LockSemaphoreInfo(log_semaphore);
597   ResetLinkedListIterator(log_cache);
598   p=(const LogInfo *) GetNextValueInLinkedList(log_cache);
599   for (i=0; p != (const LogInfo *) NULL; )
600   {
601     if ((p->stealth == MagickFalse) &&
602         (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse))
603       preferences[i++]=ConstantString(p->name);
604     p=(const LogInfo *) GetNextValueInLinkedList(log_cache);
605   }
606   UnlockSemaphoreInfo(log_semaphore);
607   qsort((void *) preferences,(size_t) i,sizeof(*preferences),LogCompare);
608   preferences[i]=(char *) NULL;
609   *number_preferences=(size_t) i;
610   return(preferences);
611 }
612 \f
613 /*
614 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
615 %                                                                             %
616 %                                                                             %
617 %                                                                             %
618 %   G e t L o g N a m e                                                       %
619 %                                                                             %
620 %                                                                             %
621 %                                                                             %
622 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
623 %
624 %  GetLogName() returns the current log name.
625 %
626 %  The format of the GetLogName method is:
627 %
628 %      const char *GetLogName(void)
629 %
630 */
631 MagickExport const char *GetLogName(void)
632 {
633   return(log_name);
634 }
635 \f
636 /*
637 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
638 %                                                                             %
639 %                                                                             %
640 %                                                                             %
641 +   I s L o g C a c h e I n s t a n t i a t e d                               %
642 %                                                                             %
643 %                                                                             %
644 %                                                                             %
645 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
646 %
647 %  IsLogCacheInstantiated() determines if the log list is instantiated.  If
648 %  not, it instantiates the list and returns it.
649 %
650 %  The format of the IsLogInstantiated method is:
651 %
652 %      MagickBooleanType IsLogCacheInstantiated(ExceptionInfo *exception)
653 %
654 %  A description of each parameter follows.
655 %
656 %    o exception: return any errors or warnings in this structure.
657 %
658 */
659 static MagickBooleanType IsLogCacheInstantiated(ExceptionInfo *exception)
660 {
661   if (log_cache == (LinkedListInfo *) NULL)
662     {
663       if (log_semaphore == (SemaphoreInfo *) NULL)
664         ActivateSemaphoreInfo(&log_semaphore);
665       LockSemaphoreInfo(log_semaphore);
666       if (log_cache == (LinkedListInfo *) NULL)
667         log_cache=AcquireLogCache(LogFilename,exception);
668       UnlockSemaphoreInfo(log_semaphore);
669     }
670   return(log_cache != (LinkedListInfo *) NULL ? MagickTrue : MagickFalse);
671 }
672 \f
673 /*
674 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
675 %                                                                             %
676 %                                                                             %
677 %                                                                             %
678 %  I s E v e n t L o g g i n g                                                %
679 %                                                                             %
680 %                                                                             %
681 %                                                                             %
682 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
683 %
684 %  IsEventLogging() returns MagickTrue if debug of events is enabled otherwise
685 %  MagickFalse.
686 %
687 %  The format of the IsEventLogging method is:
688 %
689 %      MagickBooleanType IsEventLogging(void)
690 %
691 */
692 MagickExport MagickBooleanType IsEventLogging(void)
693 {
694   const LogInfo
695     *log_info;
696
697   ExceptionInfo
698     *exception;
699
700   if ((log_cache == (LinkedListInfo *) NULL) ||
701       (IsLinkedListEmpty(log_cache) != MagickFalse))
702     return(MagickFalse);
703   exception=AcquireExceptionInfo();
704   log_info=GetLogInfo("*",exception);
705   exception=DestroyExceptionInfo(exception);
706   return(log_info->event_mask != NoEvents ? MagickTrue : MagickFalse);
707 }
708 /*
709 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
710 %                                                                             %
711 %                                                                             %
712 %                                                                             %
713 %  L i s t L o g I n f o                                                      %
714 %                                                                             %
715 %                                                                             %
716 %                                                                             %
717 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
718 %
719 %  ListLogInfo() lists the log info to a file.
720 %
721 %  The format of the ListLogInfo method is:
722 %
723 %      MagickBooleanType ListLogInfo(FILE *file,ExceptionInfo *exception)
724 %
725 %  A description of each parameter follows.
726 %
727 %    o file:  An pointer to a FILE.
728 %
729 %    o exception: return any errors or warnings in this structure.
730 %
731 */
732 MagickExport MagickBooleanType ListLogInfo(FILE *file,ExceptionInfo *exception)
733 {
734 #define MegabytesToBytes(value) ((MagickSizeType) (value)*1024*1024)
735
736   const char
737     *path;
738
739   const LogInfo
740     **log_info;
741
742   register ssize_t
743     i;
744
745   size_t
746     number_aliases;
747
748   ssize_t
749     j;
750
751   if (file == (const FILE *) NULL)
752     file=stdout;
753   log_info=GetLogInfoList("*",&number_aliases,exception);
754   if (log_info == (const LogInfo **) NULL)
755     return(MagickFalse);
756   j=0;
757   path=(const char *) NULL;
758   for (i=0; i < (ssize_t) number_aliases; i++)
759   {
760     if (log_info[i]->stealth != MagickFalse)
761       continue;
762     if ((path == (const char *) NULL) ||
763         (LocaleCompare(path,log_info[i]->path) != 0))
764       {
765         size_t
766           length;
767
768         if (log_info[i]->path != (char *) NULL)
769           (void) FormatLocaleFile(file,"\nPath: %s\n\n",log_info[i]->path);
770         length=0;
771         for (j=0; j < (ssize_t) (8*sizeof(LogHandlerType)); j++)
772         {
773           size_t
774             mask;
775
776           if (LogHandlers[j].name == (const char *) NULL)
777             break;
778           mask=1;
779           mask<<=j;
780           if ((log_info[i]->handler_mask & mask) != 0)
781             {
782               (void) FormatLocaleFile(file,"%s ",LogHandlers[j].name);
783               length+=strlen(LogHandlers[j].name);
784             }
785         }
786         for (j=(ssize_t) length; j <= 12; j++)
787           (void) FormatLocaleFile(file," ");
788         (void) FormatLocaleFile(file," Generations     Limit  Format\n");
789         (void) FormatLocaleFile(file,"-----------------------------------------"
790           "--------------------------------------\n");
791       }
792     path=log_info[i]->path;
793     if (log_info[i]->filename != (char *) NULL)
794       {
795         (void) FormatLocaleFile(file,"%s",log_info[i]->filename);
796         for (j=(ssize_t) strlen(log_info[i]->filename); j <= 16; j++)
797           (void) FormatLocaleFile(file," ");
798       }
799     (void) FormatLocaleFile(file,"%9g  ",(double) log_info[i]->generations);
800     (void) FormatLocaleFile(file,"%8g   ",(double) log_info[i]->limit);
801     if (log_info[i]->format != (char *) NULL)
802       (void) FormatLocaleFile(file,"%s",log_info[i]->format);
803     (void) FormatLocaleFile(file,"\n");
804   }
805   (void) fflush(file);
806   log_info=(const LogInfo **) RelinquishMagickMemory((void *) log_info);
807   return(MagickTrue);
808 }
809 \f
810 /*
811 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
812 %                                                                             %
813 %                                                                             %
814 %                                                                             %
815 +   L o g C o m p o n e n t G e n e s i s                                     %
816 %                                                                             %
817 %                                                                             %
818 %                                                                             %
819 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
820 %
821 %  LogComponentGenesis() instantiates the log component.
822 %
823 %  The format of the LogComponentGenesis method is:
824 %
825 %      MagickBooleanType LogComponentGenesis(void)
826 %
827 */
828 MagickPrivate MagickBooleanType LogComponentGenesis(void)
829 {
830   ExceptionInfo
831     *exception;
832
833   if (log_semaphore == (SemaphoreInfo *) NULL)
834     log_semaphore=AcquireSemaphoreInfo();
835   exception=AcquireExceptionInfo();
836   (void) GetLogInfo("*",exception);
837   exception=DestroyExceptionInfo(exception);
838   event_semaphore=AcquireSemaphoreInfo();
839   return(MagickTrue);
840 }
841 \f
842 /*
843 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
844 %                                                                             %
845 %                                                                             %
846 %                                                                             %
847 +   L o g C o m p o n e n t T e r m i n u s                                   %
848 %                                                                             %
849 %                                                                             %
850 %                                                                             %
851 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
852 %
853 %  LogComponentTerminus() destroys the logging component.
854 %
855 %  The format of the LogComponentTerminus method is:
856 %
857 %      LogComponentTerminus(void)
858 %
859 */
860
861 static void *DestroyLogElement(void *log_info)
862 {
863   register LogInfo
864     *p;
865
866   p=(LogInfo *) log_info;
867   if (p->file != (FILE *) NULL)
868     {
869       (void) FormatLocaleFile(p->file,"</log>\n");
870       (void) fclose(p->file);
871       p->file=(FILE *) NULL;
872     }
873   if (p->format != (char *) NULL)
874     p->format=DestroyString(p->format);
875   if (p->path != (char *) NULL)
876     p->path=DestroyString(p->path);
877   if (p->filename != (char *) NULL)
878     p->filename=DestroyString(p->filename);
879   p=(LogInfo *) RelinquishMagickMemory(p);
880   return((void *) NULL);
881 }
882
883 MagickPrivate void LogComponentTerminus(void)
884 {
885   if (event_semaphore == (SemaphoreInfo *) NULL)
886     ActivateSemaphoreInfo(&event_semaphore);
887   LockSemaphoreInfo(event_semaphore);
888   UnlockSemaphoreInfo(event_semaphore);
889   RelinquishSemaphoreInfo(&event_semaphore);
890   if (log_semaphore == (SemaphoreInfo *) NULL)
891     ActivateSemaphoreInfo(&log_semaphore);
892   LockSemaphoreInfo(log_semaphore);
893   if (log_cache != (LinkedListInfo *) NULL)
894     log_cache=DestroyLinkedList(log_cache,DestroyLogElement);
895   UnlockSemaphoreInfo(log_semaphore);
896   RelinquishSemaphoreInfo(&log_semaphore);
897 }
898 \f
899 /*
900 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
901 %                                                                             %
902 %                                                                             %
903 %                                                                             %
904 %   L o g M a g i c k E v e n t                                               %
905 %                                                                             %
906 %                                                                             %
907 %                                                                             %
908 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
909 %
910 %  LogMagickEvent() logs an event as determined by the log configuration file.
911 %  If an error occurs, MagickFalse is returned otherwise MagickTrue.
912 %
913 %  The format of the LogMagickEvent method is:
914 %
915 %      MagickBooleanType LogMagickEvent(const LogEventType type,
916 %        const char *module,const char *function,const size_t line,
917 %        const char *format,...)
918 %
919 %  A description of each parameter follows:
920 %
921 %    o type: the event type.
922 %
923 %    o filename: the source module filename.
924 %
925 %    o function: the function name.
926 %
927 %    o line: the line number of the source module.
928 %
929 %    o format: the output format.
930 %
931 */
932 static char *TranslateEvent(const char *module,const char *function,
933   const size_t line,const char *domain,const char *event)
934 {
935   char
936     *text;
937
938   double
939     elapsed_time,
940     user_time;
941
942   ExceptionInfo
943     *exception;
944
945   LogInfo
946     *log_info;
947
948   register char
949     *q;
950
951   register const char
952     *p;
953
954   size_t
955     extent;
956
957   time_t
958     seconds;
959
960   exception=AcquireExceptionInfo();
961   log_info=(LogInfo *) GetLogInfo("*",exception);
962   exception=DestroyExceptionInfo(exception);
963   seconds=time((time_t *) NULL);
964   elapsed_time=GetElapsedTime(&log_info->timer);
965   user_time=GetUserTime(&log_info->timer);
966   text=AcquireString(event);
967   if (log_info->format == (char *) NULL)
968     return(text);
969   extent=strlen(event)+MagickPathExtent;
970   if (LocaleCompare(log_info->format,"xml") == 0)
971     {
972       char
973         timestamp[MagickPathExtent];
974
975       /*
976         Translate event in "XML" format.
977       */
978       (void) FormatMagickTime(seconds,extent,timestamp);
979       (void) FormatLocaleString(text,extent,
980         "<entry>\n"
981         "  <timestamp>%s</timestamp>\n"
982         "  <elapsed-time>%lu:%02lu.%03lu</elapsed-time>\n"
983         "  <user-time>%0.3f</user-time>\n"
984         "  <process-id>%.20g</process-id>\n"
985         "  <thread-id>%.20g</thread-id>\n"
986         "  <module>%s</module>\n"
987         "  <function>%s</function>\n"
988         "  <line>%.20g</line>\n"
989         "  <domain>%s</domain>\n"
990         "  <event>%s</event>\n"
991         "</entry>",timestamp,(unsigned long) (elapsed_time/60.0),
992         (unsigned long) floor(fmod(elapsed_time,60.0)),(unsigned long)
993         (1000.0*(elapsed_time-floor(elapsed_time))+0.5),user_time,
994         (double) getpid(),(double) GetMagickThreadSignature(),module,function,
995         (double) line,domain,event);
996       return(text);
997     }
998   /*
999     Translate event in "human readable" format.
1000   */
1001   q=text;
1002   for (p=log_info->format; *p != '\0'; p++)
1003   {
1004     *q='\0';
1005     if ((size_t) (q-text+MagickPathExtent) >= extent)
1006       {
1007         extent+=MagickPathExtent;
1008         text=(char *) ResizeQuantumMemory(text,extent+MagickPathExtent,
1009           sizeof(*text));
1010         if (text == (char *) NULL)
1011           return((char *) NULL);
1012         q=text+strlen(text);
1013       }
1014     /*
1015       The format of the log is defined by embedding special format characters:
1016
1017         %c   client name
1018         %d   domain
1019         %e   event
1020         %f   function
1021         %g   generation
1022         %l   line
1023         %m   module
1024         %n   log name
1025         %p   process id
1026         %r   real CPU time
1027         %t   wall clock time
1028         %u   user CPU time
1029         %v   version
1030         %%   percent sign
1031         \n   newline
1032         \r   carriage return
1033     */
1034     if ((*p == '\\') && (*(p+1) == 'r'))
1035       {
1036         *q++='\r';
1037         p++;
1038         continue;
1039       }
1040     if ((*p == '\\') && (*(p+1) == 'n'))
1041       {
1042         *q++='\n';
1043         p++;
1044         continue;
1045       }
1046     if (*p != '%')
1047       {
1048         *q++=(*p);
1049         continue;
1050       }
1051     p++;
1052     switch (*p)
1053     {
1054       case 'c':
1055       {
1056         q+=CopyMagickString(q,GetClientName(),extent);
1057         break;
1058       }
1059       case 'd':
1060       {
1061         q+=CopyMagickString(q,domain,extent);
1062         break;
1063       }
1064       case 'e':
1065       {
1066         q+=CopyMagickString(q,event,extent);
1067         break;
1068       }
1069       case 'f':
1070       {
1071         q+=CopyMagickString(q,function,extent);
1072         break;
1073       }
1074       case 'g':
1075       {
1076         if (log_info->generations == 0)
1077           {
1078             (void) CopyMagickString(q,"0",extent);
1079             q++;
1080             break;
1081           }
1082         q+=FormatLocaleString(q,extent,"%.20g",(double) (log_info->generation %
1083           log_info->generations));
1084         break;
1085       }
1086       case 'l':
1087       {
1088         q+=FormatLocaleString(q,extent,"%.20g",(double) line);
1089         break;
1090       }
1091       case 'm':
1092       {
1093         register const char
1094           *p;
1095
1096         for (p=module+strlen(module)-1; p > module; p--)
1097           if (*p == *DirectorySeparator)
1098             {
1099               p++;
1100               break;
1101             }
1102         q+=CopyMagickString(q,p,extent);
1103         break;
1104       }
1105       case 'n':
1106       {
1107         q+=CopyMagickString(q,GetLogName(),extent);
1108         break;
1109       }
1110       case 'p':
1111       {
1112         q+=FormatLocaleString(q,extent,"%.20g",(double) getpid());
1113         break;
1114       }
1115       case 'r':
1116       {
1117         q+=FormatLocaleString(q,extent,"%lu:%02lu.%03lu",(unsigned long)
1118           (elapsed_time/60.0),(unsigned long) floor(fmod(elapsed_time,60.0)),
1119           (unsigned long) (1000.0*(elapsed_time-floor(elapsed_time))+0.5));
1120         break;
1121       }
1122       case 't':
1123       {
1124         q+=FormatMagickTime(seconds,extent,q);
1125         break;
1126       }
1127       case 'u':
1128       {
1129         q+=FormatLocaleString(q,extent,"%0.3fu",user_time);
1130         break;
1131       }
1132       case 'v':
1133       {
1134         q+=CopyMagickString(q,MagickLibVersionText,extent);
1135         break;
1136       }
1137       case '%':
1138       {
1139         *q++=(*p);
1140         break;
1141       }
1142       default:
1143       {
1144         *q++='%';
1145         *q++=(*p);
1146         break;
1147       }
1148     }
1149   }
1150   *q='\0';
1151   return(text);
1152 }
1153
1154 static char *TranslateFilename(const LogInfo *log_info)
1155 {
1156   char
1157     *filename;
1158
1159   register char
1160     *q;
1161
1162   register const char
1163     *p;
1164
1165   size_t
1166     extent;
1167
1168   /*
1169     Translate event in "human readable" format.
1170   */
1171   assert(log_info != (LogInfo *) NULL);
1172   assert(log_info->filename != (char *) NULL);
1173   filename=AcquireString((char *) NULL);
1174   extent=MagickPathExtent;
1175   q=filename;
1176   for (p=log_info->filename; *p != '\0'; p++)
1177   {
1178     *q='\0';
1179     if ((size_t) (q-filename+MagickPathExtent) >= extent)
1180       {
1181         extent+=MagickPathExtent;
1182         filename=(char *) ResizeQuantumMemory(filename,extent+MagickPathExtent,
1183           sizeof(*filename));
1184         if (filename == (char *) NULL)
1185           return((char *) NULL);
1186         q=filename+strlen(filename);
1187       }
1188     /*
1189       The format of the filename is defined by embedding special format
1190       characters:
1191
1192         %c   client name
1193         %n   log name
1194         %p   process id
1195         %v   version
1196         %%   percent sign
1197     */
1198     if (*p != '%')
1199       {
1200         *q++=(*p);
1201         continue;
1202       }
1203     p++;
1204     switch (*p)
1205     {
1206       case 'c':
1207       {
1208         q+=CopyMagickString(q,GetClientName(),extent);
1209         break;
1210       }
1211       case 'g':
1212       {
1213         if (log_info->generations == 0)
1214           {
1215             (void) CopyMagickString(q,"0",extent);
1216             q++;
1217             break;
1218           }
1219         q+=FormatLocaleString(q,extent,"%.20g",(double) (log_info->generation %
1220           log_info->generations));
1221         break;
1222       }
1223       case 'n':
1224       {
1225         q+=CopyMagickString(q,GetLogName(),extent);
1226         break;
1227       }
1228       case 'p':
1229       {
1230         q+=FormatLocaleString(q,extent,"%.20g",(double) getpid());
1231         break;
1232       }
1233       case 'v':
1234       {
1235         q+=CopyMagickString(q,MagickLibVersionText,extent);
1236         break;
1237       }
1238       case '%':
1239       {
1240         *q++=(*p);
1241         break;
1242       }
1243       default:
1244       {
1245         *q++='%';
1246         *q++=(*p);
1247         break;
1248       }
1249     }
1250   }
1251   *q='\0';
1252   return(filename);
1253 }
1254
1255 MagickBooleanType LogMagickEventList(const LogEventType type,const char *module,
1256   const char *function,const size_t line,const char *format,va_list operands)
1257 {
1258   char
1259     event[MagickPathExtent],
1260     *text;
1261
1262   const char
1263     *domain;
1264
1265   ExceptionInfo
1266     *exception;
1267
1268   int
1269     n;
1270
1271   LogInfo
1272     *log_info;
1273
1274   if (IsEventLogging() == MagickFalse)
1275     return(MagickFalse);
1276   exception=AcquireExceptionInfo();
1277   log_info=(LogInfo *) GetLogInfo("*",exception);
1278   exception=DestroyExceptionInfo(exception);
1279   if (event_semaphore == (SemaphoreInfo *) NULL)
1280     ActivateSemaphoreInfo(&event_semaphore);
1281   LockSemaphoreInfo(event_semaphore);
1282   if ((log_info->event_mask & type) == 0)
1283     {
1284       UnlockSemaphoreInfo(event_semaphore);
1285       return(MagickTrue);
1286     }
1287   domain=CommandOptionToMnemonic(MagickLogEventOptions,type);
1288 #if defined(MAGICKCORE_HAVE_VSNPRINTF)
1289   n=vsnprintf(event,MagickPathExtent,format,operands);
1290 #else
1291   n=vsprintf(event,format,operands);
1292 #endif
1293   if (n < 0)
1294     event[MagickPathExtent-1]='\0';
1295   text=TranslateEvent(module,function,line,domain,event);
1296   if (text == (char *) NULL)
1297     {
1298       (void) ContinueTimer((TimerInfo *) &log_info->timer);
1299       UnlockSemaphoreInfo(event_semaphore);
1300       return(MagickFalse);
1301     }
1302   if ((log_info->handler_mask & ConsoleHandler) != 0)
1303     {
1304       (void) FormatLocaleFile(stderr,"%s\n",text);
1305       (void) fflush(stderr);
1306     }
1307   if ((log_info->handler_mask & DebugHandler) != 0)
1308     {
1309 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
1310       OutputDebugString(text);
1311       OutputDebugString("\n");
1312 #endif
1313     }
1314   if ((log_info->handler_mask & EventHandler) != 0)
1315     {
1316 #if defined(MAGICKCORE_WINDOWS_SUPPORT)
1317       (void) NTReportEvent(text,MagickFalse);
1318 #endif
1319     }
1320   if ((log_info->handler_mask & FileHandler) != 0)
1321     {
1322       struct stat
1323         file_info;
1324
1325       file_info.st_size=0;
1326       if (log_info->file != (FILE *) NULL)
1327         (void) fstat(fileno(log_info->file),&file_info);
1328       if (file_info.st_size > (ssize_t) (1024*1024*log_info->limit))
1329         {
1330           (void) FormatLocaleFile(log_info->file,"</log>\n");
1331           (void) fclose(log_info->file);
1332           log_info->file=(FILE *) NULL;
1333         }
1334       if (log_info->file == (FILE *) NULL)
1335         {
1336           char
1337             *filename;
1338
1339           filename=TranslateFilename(log_info);
1340           if (filename == (char *) NULL)
1341             {
1342               (void) ContinueTimer((TimerInfo *) &log_info->timer);
1343               UnlockSemaphoreInfo(event_semaphore);
1344               return(MagickFalse);
1345             }
1346           log_info->append=IsPathAccessible(filename);
1347           log_info->file=fopen_utf8(filename,"ab");
1348           filename=(char  *) RelinquishMagickMemory(filename);
1349           if (log_info->file == (FILE *) NULL)
1350             {
1351               UnlockSemaphoreInfo(event_semaphore);
1352               return(MagickFalse);
1353             }
1354           log_info->generation++;
1355           if (log_info->append == MagickFalse)
1356             (void) FormatLocaleFile(log_info->file,"<?xml version=\"1.0\" "
1357               "encoding=\"UTF-8\" standalone=\"yes\"?>\n");
1358           (void) FormatLocaleFile(log_info->file,"<log>\n");
1359         }
1360       (void) FormatLocaleFile(log_info->file,"  <event>%s</event>\n",text);
1361       (void) fflush(log_info->file);
1362     }
1363   if ((log_info->handler_mask & MethodHandler) != 0)
1364     {
1365       if (log_info->method != (MagickLogMethod) NULL)
1366         log_info->method(type,text);
1367     }
1368   if ((log_info->handler_mask & StdoutHandler) != 0)
1369     {
1370       (void) FormatLocaleFile(stdout,"%s\n",text);
1371       (void) fflush(stdout);
1372     }
1373   if ((log_info->handler_mask & StderrHandler) != 0)
1374     {
1375       (void) FormatLocaleFile(stderr,"%s\n",text);
1376       (void) fflush(stderr);
1377     }
1378   text=(char  *) RelinquishMagickMemory(text);
1379   (void) ContinueTimer((TimerInfo *) &log_info->timer);
1380   UnlockSemaphoreInfo(event_semaphore);
1381   return(MagickTrue);
1382 }
1383
1384 MagickBooleanType LogMagickEvent(const LogEventType type,const char *module,
1385   const char *function,const size_t line,const char *format,...)
1386 {
1387   va_list
1388     operands;
1389
1390   MagickBooleanType
1391     status;
1392
1393   va_start(operands,format);
1394   status=LogMagickEventList(type,module,function,line,format,operands);
1395   va_end(operands);
1396   return(status);
1397 }
1398 \f
1399 /*
1400 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1401 %                                                                             %
1402 %                                                                             %
1403 %                                                                             %
1404 +   L o a d L o g C a c h e                                                   %
1405 %                                                                             %
1406 %                                                                             %
1407 %                                                                             %
1408 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1409 %
1410 %  LoadLogCache() loads the log configurations which provides a
1411 %  mapping between log attributes and log name.
1412 %
1413 %  The format of the LoadLogCache method is:
1414 %
1415 %      MagickBooleanType LoadLogCache(LinkedListInfo *cache,const char *xml,
1416 %        const char *filename,const size_t depth,ExceptionInfo *exception)
1417 %
1418 %  A description of each parameter follows:
1419 %
1420 %    o xml:  The log list in XML format.
1421 %
1422 %    o filename:  The log list filename.
1423 %
1424 %    o depth: depth of <include /> statements.
1425 %
1426 %    o exception: return any errors or warnings in this structure.
1427 %
1428 */
1429 static MagickBooleanType LoadLogCache(LinkedListInfo *cache,const char *xml,
1430   const char *filename,const size_t depth,ExceptionInfo *exception)
1431 {
1432   char
1433     keyword[MagickPathExtent],
1434     *token;
1435
1436   const char
1437     *q;
1438
1439   LogInfo
1440     *log_info = (LogInfo *) NULL;
1441
1442   MagickStatusType
1443     status;
1444
1445   size_t
1446     extent;
1447
1448   /*
1449     Load the log map file.
1450   */
1451   if (xml == (const char *) NULL)
1452     return(MagickFalse);
1453   status=MagickTrue;
1454   token=AcquireString(xml);
1455   extent=strlen(token)+MagickPathExtent;
1456   for (q=(const char *) xml; *q != '\0'; )
1457   {
1458     /*
1459       Interpret XML.
1460     */
1461     GetNextToken(q,&q,extent,token);
1462     if (*token == '\0')
1463       break;
1464     (void) CopyMagickString(keyword,token,MagickPathExtent);
1465     if (LocaleNCompare(keyword,"<!DOCTYPE",9) == 0)
1466       {
1467         /*
1468           Doctype element.
1469         */
1470         while ((LocaleNCompare(q,"]>",2) != 0) && (*q != '\0'))
1471           GetNextToken(q,&q,extent,token);
1472         continue;
1473       }
1474     if (LocaleNCompare(keyword,"<!--",4) == 0)
1475       {
1476         /*
1477           Comment element.
1478         */
1479         while ((LocaleNCompare(q,"->",2) != 0) && (*q != '\0'))
1480           GetNextToken(q,&q,extent,token);
1481         continue;
1482       }
1483     if (LocaleCompare(keyword,"<include") == 0)
1484       {
1485         /*
1486           Include element.
1487         */
1488         while (((*token != '/') && (*(token+1) != '>')) && (*q != '\0'))
1489         {
1490           (void) CopyMagickString(keyword,token,MagickPathExtent);
1491           GetNextToken(q,&q,extent,token);
1492           if (*token != '=')
1493             continue;
1494           GetNextToken(q,&q,extent,token);
1495           if (LocaleCompare(keyword,"file") == 0)
1496             {
1497               if (depth > 200)
1498                 (void) ThrowMagickException(exception,GetMagickModule(),
1499                   ConfigureError,"IncludeElementNestedTooDeeply","`%s'",token);
1500               else
1501                 {
1502                   char
1503                     path[MagickPathExtent],
1504                     *file_xml;
1505
1506                   GetPathComponent(filename,HeadPath,path);
1507                   if (*path != '\0')
1508                     (void) ConcatenateMagickString(path,DirectorySeparator,
1509                       MagickPathExtent);
1510                   if (*token == *DirectorySeparator)
1511                     (void) CopyMagickString(path,token,MagickPathExtent);
1512                   else
1513                     (void) ConcatenateMagickString(path,token,MagickPathExtent);
1514                   file_xml=FileToXML(path,~0UL);
1515                   if (file_xml != (char *) NULL)
1516                     {
1517                       status&=LoadLogCache(cache,file_xml,path,depth+1,
1518                         exception);
1519                       file_xml=DestroyString(file_xml);
1520                     }
1521                 }
1522             }
1523         }
1524         continue;
1525       }
1526     if (LocaleCompare(keyword,"<logmap>") == 0)
1527       {
1528         /*
1529           Allocate memory for the log list.
1530         */
1531         log_info=(LogInfo *) AcquireMagickMemory(sizeof(*log_info));
1532         if (log_info == (LogInfo *) NULL)
1533           ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1534         (void) ResetMagickMemory(log_info,0,sizeof(*log_info));
1535         log_info->path=ConstantString(filename);
1536         GetTimerInfo((TimerInfo *) &log_info->timer);
1537         log_info->signature=MagickCoreSignature;
1538         continue;
1539       }
1540     if (log_info == (LogInfo *) NULL)
1541       continue;
1542     if (LocaleCompare(keyword,"</logmap>") == 0)
1543       {
1544         status=AppendValueToLinkedList(cache,log_info);
1545         if (status == MagickFalse)
1546           (void) ThrowMagickException(exception,GetMagickModule(),
1547             ResourceLimitError,"MemoryAllocationFailed","`%s'",filename);
1548         log_info=(LogInfo *) NULL;
1549         continue;
1550       }
1551     GetNextToken(q,(const char **) NULL,extent,token);
1552     if (*token != '=')
1553       continue;
1554     GetNextToken(q,&q,extent,token);
1555     GetNextToken(q,&q,extent,token);
1556     switch (*keyword)
1557     {
1558       case 'E':
1559       case 'e':
1560       {
1561         if (LocaleCompare((char *) keyword,"events") == 0)
1562           {
1563             log_info->event_mask=(LogEventType) (log_info->event_mask |
1564               ParseCommandOption(MagickLogEventOptions,MagickTrue,token));
1565             break;
1566           }
1567         break;
1568       }
1569       case 'F':
1570       case 'f':
1571       {
1572         if (LocaleCompare((char *) keyword,"filename") == 0)
1573           {
1574             if (log_info->filename != (char *) NULL)
1575               log_info->filename=(char *)
1576                 RelinquishMagickMemory(log_info->filename);
1577             log_info->filename=ConstantString(token);
1578             break;
1579           }
1580         if (LocaleCompare((char *) keyword,"format") == 0)
1581           {
1582             if (log_info->format != (char *) NULL)
1583               log_info->format=(char *)
1584                 RelinquishMagickMemory(log_info->format);
1585             log_info->format=ConstantString(token);
1586             break;
1587           }
1588         break;
1589       }
1590       case 'G':
1591       case 'g':
1592       {
1593         if (LocaleCompare((char *) keyword,"generations") == 0)
1594           {
1595             if (LocaleCompare(token,"unlimited") == 0)
1596               {
1597                 log_info->generations=(~0UL);
1598                 break;
1599               }
1600             log_info->generations=StringToUnsignedLong(token);
1601             break;
1602           }
1603         break;
1604       }
1605       case 'L':
1606       case 'l':
1607       {
1608         if (LocaleCompare((char *) keyword,"limit") == 0)
1609           {
1610             if (LocaleCompare(token,"unlimited") == 0)
1611               {
1612                 log_info->limit=(~0UL);
1613                 break;
1614               }
1615             log_info->limit=StringToUnsignedLong(token);
1616             break;
1617           }
1618         break;
1619       }
1620       case 'O':
1621       case 'o':
1622       {
1623         if (LocaleCompare((char *) keyword,"output") == 0)
1624           {
1625             log_info->handler_mask=(LogHandlerType)
1626               (log_info->handler_mask | ParseLogHandlers(token));
1627             break;
1628           }
1629         break;
1630       }
1631       default:
1632         break;
1633     }
1634   }
1635   token=DestroyString(token);
1636   if (cache == (LinkedListInfo *) NULL)
1637     return(MagickFalse);
1638   return(status != 0 ? MagickTrue : MagickFalse);
1639 }
1640 \f
1641 /*
1642 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1643 %                                                                             %
1644 %                                                                             %
1645 %                                                                             %
1646 +   P a r s e L o g H a n d l e r s                                           %
1647 %                                                                             %
1648 %                                                                             %
1649 %                                                                             %
1650 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1651 %
1652 %  ParseLogHandlers() parses a string defining which handlers takes a log
1653 %  message and exports them.
1654 %
1655 %  The format of the ParseLogHandlers method is:
1656 %
1657 %      LogHandlerType ParseLogHandlers(const char *handlers)
1658 %
1659 %  A description of each parameter follows:
1660 %
1661 %    o handlers: one or more handlers separated by commas.
1662 %
1663 */
1664 static LogHandlerType ParseLogHandlers(const char *handlers)
1665 {
1666   LogHandlerType
1667     handler_mask;
1668
1669   register const char
1670     *p;
1671
1672   register ssize_t
1673     i;
1674
1675   size_t
1676     length;
1677
1678   handler_mask=NoHandler;
1679   for (p=handlers; p != (char *) NULL; p=strchr(p,','))
1680   {
1681     while ((*p != '\0') && ((isspace((int) ((unsigned char) *p)) != 0) ||
1682            (*p == ',')))
1683       p++;
1684     for (i=0; LogHandlers[i].name != (char *) NULL; i++)
1685     {
1686       length=strlen(LogHandlers[i].name);
1687       if (LocaleNCompare(p,LogHandlers[i].name,length) == 0)
1688         {
1689           handler_mask=(LogHandlerType) (handler_mask | LogHandlers[i].handler);
1690           break;
1691         }
1692     }
1693     if (LogHandlers[i].name == (char *) NULL)
1694       return(UndefinedHandler);
1695   }
1696   return(handler_mask);
1697 }
1698 \f
1699 /*
1700 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1701 %                                                                             %
1702 %                                                                             %
1703 %                                                                             %
1704 %   S e t L o g E v e n t M a s k                                             %
1705 %                                                                             %
1706 %                                                                             %
1707 %                                                                             %
1708 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1709 %
1710 %  SetLogEventMask() accepts a list that determines which events to log.  All
1711 %  other events are ignored.  By default, no debug is enabled.  This method
1712 %  returns the previous log event mask.
1713 %
1714 %  The format of the SetLogEventMask method is:
1715 %
1716 %      LogEventType SetLogEventMask(const char *events)
1717 %
1718 %  A description of each parameter follows:
1719 %
1720 %    o events: log these events.
1721 %
1722 */
1723 MagickExport LogEventType SetLogEventMask(const char *events)
1724 {
1725   ExceptionInfo
1726     *exception;
1727
1728   LogInfo
1729     *log_info;
1730
1731   ssize_t
1732     option;
1733
1734   exception=AcquireExceptionInfo();
1735   log_info=(LogInfo *) GetLogInfo("*",exception);
1736   exception=DestroyExceptionInfo(exception);
1737   option=ParseCommandOption(MagickLogEventOptions,MagickTrue,events);
1738   LockSemaphoreInfo(log_semaphore);
1739   log_info=(LogInfo *) GetValueFromLinkedList(log_cache,0);
1740   log_info->event_mask=(LogEventType) option;
1741   if (option == -1)
1742     log_info->event_mask=UndefinedEvents;
1743   UnlockSemaphoreInfo(log_semaphore);
1744   return(log_info->event_mask);
1745 }
1746 \f
1747 /*
1748 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1749 %                                                                             %
1750 %                                                                             %
1751 %                                                                             %
1752 %   S e t L o g F o r m a t                                                   %
1753 %                                                                             %
1754 %                                                                             %
1755 %                                                                             %
1756 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1757 %
1758 %  SetLogFormat() sets the format for the "human readable" log record.
1759 %
1760 %  The format of the LogMagickFormat method is:
1761 %
1762 %      SetLogFormat(const char *format)
1763 %
1764 %  A description of each parameter follows:
1765 %
1766 %    o format: the log record format.
1767 %
1768 */
1769 MagickExport void SetLogFormat(const char *format)
1770 {
1771   LogInfo
1772     *log_info;
1773
1774   ExceptionInfo
1775     *exception;
1776
1777   exception=AcquireExceptionInfo();
1778   log_info=(LogInfo *) GetLogInfo("*",exception);
1779   exception=DestroyExceptionInfo(exception);
1780   LockSemaphoreInfo(log_semaphore);
1781   if (log_info->format != (char *) NULL)
1782     log_info->format=DestroyString(log_info->format);
1783   log_info->format=ConstantString(format);
1784   UnlockSemaphoreInfo(log_semaphore);
1785 }
1786
1787 /*
1788 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1789 %                                                                             %
1790 %                                                                             %
1791 %                                                                             %
1792 %   S e t L o g M e t h o d                                                   %
1793 %                                                                             %
1794 %                                                                             %
1795 %                                                                             %
1796 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1797 %
1798 %  SetLogMethod() sets the method that will be called when an event is logged.
1799 %
1800 %  The format of the SetLogMethod method is:
1801 %
1802 %      void SetLogMethod(MagickLogMethod method)
1803 %
1804 %  A description of each parameter follows:
1805 %
1806 %    o method: pointer to a method that will be called when LogMagickEvent is
1807 %      being called.
1808 %
1809 */
1810 MagickExport void SetLogMethod(MagickLogMethod method)
1811 {
1812   ExceptionInfo
1813     *exception;
1814
1815   LogInfo
1816     *log_info;
1817
1818   exception=AcquireExceptionInfo();
1819   log_info=(LogInfo *) GetLogInfo("*",exception);
1820   exception=DestroyExceptionInfo(exception);
1821   LockSemaphoreInfo(log_semaphore);
1822   log_info=(LogInfo *) GetValueFromLinkedList(log_cache,0);
1823   log_info->handler_mask=(LogHandlerType) (log_info->handler_mask |
1824     MethodHandler);
1825   log_info->method=method;
1826   UnlockSemaphoreInfo(log_semaphore);
1827 }
1828 \f
1829 /*
1830 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1831 %                                                                             %
1832 %                                                                             %
1833 %                                                                             %
1834 %   S e t L o g N a m e                                                       %
1835 %                                                                             %
1836 %                                                                             %
1837 %                                                                             %
1838 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1839 %
1840 %  SetLogName() sets the log name and returns it.
1841 %
1842 %  The format of the SetLogName method is:
1843 %
1844 %      const char *SetLogName(const char *name)
1845 %
1846 %  A description of each parameter follows:
1847 %
1848 %    o log_name: SetLogName() returns the current client name.
1849 %
1850 %    o name: Specifies the new client name.
1851 %
1852 */
1853 MagickExport const char *SetLogName(const char *name)
1854 {
1855   if ((name != (char *) NULL) && (*name != '\0'))
1856     (void) CopyMagickString(log_name,name,MagickPathExtent);
1857   return(log_name);
1858 }