log.c revision 05b261ec
1/*
2
3Copyright 1987, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included
12in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
18OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall
23not be used in advertising or otherwise to promote the sale, use or
24other dealings in this Software without prior written authorization
25from The Open Group.
26
27
28Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
29Copyright 1994 Quarterdeck Office Systems.
30
31                        All Rights Reserved
32
33Permission to use, copy, modify, and distribute this software and its
34documentation for any purpose and without fee is hereby granted,
35provided that the above copyright notice appear in all copies and that
36both that copyright notice and this permission notice appear in
37supporting documentation, and that the names of Digital and
38Quarterdeck not be used in advertising or publicity pertaining to
39distribution of the software without specific, written prior
40permission.
41
42DIGITAL AND QUARTERDECK DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
43SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
44FITNESS, IN NO EVENT SHALL DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT
45OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
46OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
47OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
48OR PERFORMANCE OF THIS SOFTWARE.
49
50*/
51
52/*
53 * Copyright (c) 1997-2003 by The XFree86 Project, Inc.
54 *
55 * Permission is hereby granted, free of charge, to any person obtaining a
56 * copy of this software and associated documentation files (the "Software"),
57 * to deal in the Software without restriction, including without limitation
58 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
59 * and/or sell copies of the Software, and to permit persons to whom the
60 * Software is furnished to do so, subject to the following conditions:
61 *
62 * The above copyright notice and this permission notice shall be included in
63 * all copies or substantial portions of the Software.
64 *
65 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
66 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
67 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
68 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
69 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
70 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
71 * OTHER DEALINGS IN THE SOFTWARE.
72 *
73 * Except as contained in this notice, the name of the copyright holder(s)
74 * and author(s) shall not be used in advertising or otherwise to promote
75 * the sale, use or other dealings in this Software without prior written
76 * authorization from the copyright holder(s) and author(s).
77 */
78
79
80#ifdef HAVE_DIX_CONFIG_H
81#include <dix-config.h>
82#endif
83
84#include <X11/Xos.h>
85#include <stdio.h>
86#include <time.h>
87#include <sys/stat.h>
88#include <stdarg.h>
89#include <stdlib.h>	/* for malloc() */
90#include <errno.h>
91
92#include "input.h"
93#include "site.h"
94#include "opaque.h"
95
96#ifdef WIN32
97#include <process.h>
98#define getpid(x) _getpid(x)
99#endif
100
101
102#ifdef DDXOSVERRORF
103void (*OsVendorVErrorFProc)(const char *, va_list args) = NULL;
104#endif
105
106static FILE *logFile = NULL;
107static Bool logFlush = FALSE;
108static Bool logSync = FALSE;
109static int logVerbosity = DEFAULT_LOG_VERBOSITY;
110static int logFileVerbosity = DEFAULT_LOG_FILE_VERBOSITY;
111
112/* Buffer to information logged before the log file is opened. */
113static char *saveBuffer = NULL;
114static int bufferSize = 0, bufferUnused = 0, bufferPos = 0;
115static Bool needBuffer = TRUE;
116
117/* Prefix strings for log messages. */
118#ifndef X_UNKNOWN_STRING
119#define X_UNKNOWN_STRING		"(\?\?)"
120#endif
121#ifndef X_PROBE_STRING
122#define X_PROBE_STRING			"(--)"
123#endif
124#ifndef X_CONFIG_STRING
125#define X_CONFIG_STRING			"(**)"
126#endif
127#ifndef X_DEFAULT_STRING
128#define X_DEFAULT_STRING		"(==)"
129#endif
130#ifndef X_CMDLINE_STRING
131#define X_CMDLINE_STRING		"(++)"
132#endif
133#ifndef X_NOTICE_STRING
134#define X_NOTICE_STRING			"(!!)"
135#endif
136#ifndef X_ERROR_STRING
137#define X_ERROR_STRING			"(EE)"
138#endif
139#ifndef X_WARNING_STRING
140#define X_WARNING_STRING		"(WW)"
141#endif
142#ifndef X_INFO_STRING
143#define X_INFO_STRING			"(II)"
144#endif
145#ifndef X_NOT_IMPLEMENTED_STRING
146#define X_NOT_IMPLEMENTED_STRING	"(NI)"
147#endif
148
149/*
150 * LogInit is called to start logging to a file.  It is also called (with
151 * NULL arguments) when logging to a file is not wanted.  It must always be
152 * called, otherwise log messages will continue to accumulate in a buffer.
153 *
154 * %s, if present in the fname or backup strings, is expanded to the display
155 * string.
156 */
157
158const char *
159LogInit(const char *fname, const char *backup)
160{
161    char *logFileName = NULL;
162
163    if (fname && *fname) {
164	/* xalloc() can't be used yet. */
165	logFileName = malloc(strlen(fname) + strlen(display) + 1);
166	if (!logFileName)
167	    FatalError("Cannot allocate space for the log file name\n");
168	sprintf(logFileName, fname, display);
169
170	if (backup && *backup) {
171	    struct stat buf;
172
173	    if (!stat(logFileName, &buf) && S_ISREG(buf.st_mode)) {
174		char *suffix;
175		char *oldLog;
176
177		oldLog = malloc(strlen(logFileName) + strlen(backup) +
178				strlen(display) + 1);
179		suffix = malloc(strlen(backup) + strlen(display) + 1);
180		if (!oldLog || !suffix)
181		    FatalError("Cannot allocate space for the log file name\n");
182		sprintf(suffix, backup, display);
183		sprintf(oldLog, "%s%s", logFileName, suffix);
184		free(suffix);
185		if (rename(logFileName, oldLog) == -1) {
186		    FatalError("Cannot move old log file (\"%s\" to \"%s\"\n",
187			       logFileName, oldLog);
188		}
189		free(oldLog);
190	    }
191	}
192	if ((logFile = fopen(logFileName, "w")) == NULL)
193	    FatalError("Cannot open log file \"%s\"\n", logFileName);
194	setvbuf(logFile, NULL, _IONBF, 0);
195
196	/* Flush saved log information. */
197	if (saveBuffer && bufferSize > 0) {
198	    fwrite(saveBuffer, bufferPos, 1, logFile);
199	    fflush(logFile);
200#ifndef WIN32
201	    fsync(fileno(logFile));
202#endif
203	}
204    }
205
206    /*
207     * Unconditionally free the buffer, and flag that the buffer is no longer
208     * needed.
209     */
210    if (saveBuffer && bufferSize > 0) {
211	free(saveBuffer);	/* Must be free(), not xfree() */
212	saveBuffer = NULL;
213	bufferSize = 0;
214    }
215    needBuffer = FALSE;
216
217    return logFileName;
218}
219
220void
221LogClose(void)
222{
223    if (logFile) {
224	fclose(logFile);
225	logFile = NULL;
226    }
227}
228
229Bool
230LogSetParameter(LogParameter param, int value)
231{
232    switch (param) {
233    case XLOG_FLUSH:
234	logFlush = value ? TRUE : FALSE;
235	return TRUE;
236    case XLOG_SYNC:
237	logSync = value ? TRUE : FALSE;
238	return TRUE;
239    case XLOG_VERBOSITY:
240	logVerbosity = value;
241	return TRUE;
242    case XLOG_FILE_VERBOSITY:
243	logFileVerbosity = value;
244	return TRUE;
245    default:
246	return FALSE;
247    }
248}
249
250/* This function does the actual log message writes. */
251
252_X_EXPORT void
253LogVWrite(int verb, const char *f, va_list args)
254{
255    static char tmpBuffer[1024];
256    int len = 0;
257
258    /*
259     * Since a va_list can only be processed once, write the string to a
260     * buffer, and then write the buffer out to the appropriate output
261     * stream(s).
262     */
263    if (verb < 0 || logFileVerbosity >= verb || logVerbosity >= verb) {
264	vsnprintf(tmpBuffer, sizeof(tmpBuffer), f, args);
265	len = strlen(tmpBuffer);
266    }
267    if ((verb < 0 || logVerbosity >= verb) && len > 0)
268	fwrite(tmpBuffer, len, 1, stderr);
269    if ((verb < 0 || logFileVerbosity >= verb) && len > 0) {
270	if (logFile) {
271	    fwrite(tmpBuffer, len, 1, logFile);
272	    if (logFlush) {
273		fflush(logFile);
274#ifndef WIN32
275		if (logSync)
276		    fsync(fileno(logFile));
277#endif
278	    }
279	} else if (needBuffer) {
280	    /*
281	     * Note, this code is used before OsInit() has been called, so
282	     * xalloc() and friends can't be used.
283	     */
284	    if (len > bufferUnused) {
285		bufferSize += 1024;
286		bufferUnused += 1024;
287		if (saveBuffer)
288		    saveBuffer = realloc(saveBuffer, bufferSize);
289		else
290		    saveBuffer = malloc(bufferSize);
291		if (!saveBuffer)
292		    FatalError("realloc() failed while saving log messages\n");
293	    }
294	    bufferUnused -= len;
295	    memcpy(saveBuffer + bufferPos, tmpBuffer, len);
296	    bufferPos += len;
297	}
298    }
299}
300
301_X_EXPORT void
302LogWrite(int verb, const char *f, ...)
303{
304    va_list args;
305
306    va_start(args, f);
307    LogVWrite(verb, f, args);
308    va_end(args);
309}
310
311_X_EXPORT void
312LogVMessageVerb(MessageType type, int verb, const char *format, va_list args)
313{
314    const char *s  = X_UNKNOWN_STRING;
315    char *tmpBuf = NULL;
316
317    /* Ignore verbosity for X_ERROR */
318    if (logVerbosity >= verb || logFileVerbosity >= verb || type == X_ERROR) {
319	switch (type) {
320	case X_PROBED:
321	    s = X_PROBE_STRING;
322	    break;
323	case X_CONFIG:
324	    s = X_CONFIG_STRING;
325	    break;
326	case X_DEFAULT:
327	    s = X_DEFAULT_STRING;
328	    break;
329	case X_CMDLINE:
330	    s = X_CMDLINE_STRING;
331	    break;
332	case X_NOTICE:
333	    s = X_NOTICE_STRING;
334	    break;
335	case X_ERROR:
336	    s = X_ERROR_STRING;
337	    if (verb > 0)
338		verb = 0;
339	    break;
340	case X_WARNING:
341	    s = X_WARNING_STRING;
342	    break;
343	case X_INFO:
344	    s = X_INFO_STRING;
345	    break;
346	case X_NOT_IMPLEMENTED:
347	    s = X_NOT_IMPLEMENTED_STRING;
348	    break;
349	case X_UNKNOWN:
350	    s = X_UNKNOWN_STRING;
351	    break;
352	case X_NONE:
353	    s = NULL;
354	    break;
355	}
356
357	/*
358	 * Prefix the format string with the message type.  We do it this way
359	 * so that LogVWrite() is only called once per message.
360	 */
361	if (s) {
362	    tmpBuf = malloc(strlen(format) + strlen(s) + 1 + 1);
363	    /* Silently return if malloc fails here. */
364	    if (!tmpBuf)
365		return;
366	    sprintf(tmpBuf, "%s ", s);
367	    strcat(tmpBuf, format);
368	    LogVWrite(verb, tmpBuf, args);
369	    free(tmpBuf);
370	} else
371	    LogVWrite(verb, format, args);
372    }
373}
374
375/* Log message with verbosity level specified. */
376_X_EXPORT void
377LogMessageVerb(MessageType type, int verb, const char *format, ...)
378{
379    va_list ap;
380
381    va_start(ap, format);
382    LogVMessageVerb(type, verb, format, ap);
383    va_end(ap);
384}
385
386/* Log a message with the standard verbosity level of 1. */
387_X_EXPORT void
388LogMessage(MessageType type, const char *format, ...)
389{
390    va_list ap;
391
392    va_start(ap, format);
393    LogVMessageVerb(type, 1, format, ap);
394    va_end(ap);
395}
396
397#ifdef __GNUC__
398void AbortServer(void) __attribute__((noreturn));
399#endif
400
401void
402AbortServer(void)
403{
404    OsCleanup(TRUE);
405    CloseDownDevices();
406    AbortDDX();
407    fflush(stderr);
408    if (CoreDump)
409	abort();
410    exit (1);
411}
412
413#ifndef AUDIT_PREFIX
414#define AUDIT_PREFIX "AUDIT: %s: %ld %s: "
415#endif
416#ifndef AUDIT_TIMEOUT
417#define AUDIT_TIMEOUT ((CARD32)(120 * 1000)) /* 2 mn */
418#endif
419
420static int nrepeat = 0;
421static int oldlen = -1;
422static OsTimerPtr auditTimer = NULL;
423
424void
425FreeAuditTimer(void)
426{
427    if (auditTimer != NULL) {
428	/* Force output of pending messages */
429	TimerForce(auditTimer);
430	TimerFree(auditTimer);
431	auditTimer = NULL;
432    }
433}
434
435static char *
436AuditPrefix(void)
437{
438    time_t tm;
439    char *autime, *s;
440    char *tmpBuf;
441    int len;
442
443    time(&tm);
444    autime = ctime(&tm);
445    if ((s = strchr(autime, '\n')))
446	*s = '\0';
447    if ((s = strrchr(argvGlobal[0], '/')))
448	s++;
449    else
450	s = argvGlobal[0];
451    len = strlen(AUDIT_PREFIX) + strlen(autime) + 10 + strlen(s) + 1;
452    tmpBuf = malloc(len);
453    if (!tmpBuf)
454	return NULL;
455    snprintf(tmpBuf, len, AUDIT_PREFIX, autime, (unsigned long)getpid(), s);
456    return tmpBuf;
457}
458
459void
460AuditF(const char * f, ...)
461{
462    va_list args;
463
464    va_start(args, f);
465
466    VAuditF(f, args);
467    va_end(args);
468}
469
470static CARD32
471AuditFlush(OsTimerPtr timer, CARD32 now, pointer arg)
472{
473    char *prefix;
474
475    if (nrepeat > 0) {
476	prefix = AuditPrefix();
477	ErrorF("%slast message repeated %d times\n",
478	       prefix != NULL ? prefix : "", nrepeat);
479	nrepeat = 0;
480	if (prefix != NULL)
481	    free(prefix);
482	return AUDIT_TIMEOUT;
483    } else {
484	/* if the timer expires without anything to print, flush the message */
485	oldlen = -1;
486	return 0;
487    }
488}
489
490void
491VAuditF(const char *f, va_list args)
492{
493    char *prefix;
494    char buf[1024];
495    int len;
496    static char oldbuf[1024];
497
498    prefix = AuditPrefix();
499    len = vsnprintf(buf, sizeof(buf), f, args);
500
501#if 1
502    /* XXX Compressing duplicated messages is temporarily disabled to
503     * work around bugzilla 964:
504     *     https://freedesktop.org/bugzilla/show_bug.cgi?id=964
505     */
506    ErrorF("%s%s", prefix != NULL ? prefix : "", buf);
507    oldlen = -1;
508    nrepeat = 0;
509#else
510    if (len == oldlen && strcmp(buf, oldbuf) == 0) {
511	/* Message already seen */
512	nrepeat++;
513    } else {
514	/* new message */
515	if (auditTimer != NULL)
516	    TimerForce(auditTimer);
517	ErrorF("%s%s", prefix != NULL ? prefix : "", buf);
518	strlcpy(oldbuf, buf, sizeof(oldbuf));
519	oldlen = len;
520	nrepeat = 0;
521	auditTimer = TimerSet(auditTimer, 0, AUDIT_TIMEOUT, AuditFlush, NULL);
522    }
523#endif
524    if (prefix != NULL)
525	free(prefix);
526}
527
528_X_EXPORT void
529FatalError(const char *f, ...)
530{
531    va_list args;
532    static Bool beenhere = FALSE;
533
534    if (beenhere)
535	ErrorF("\nFatalError re-entered, aborting\n");
536    else
537	ErrorF("\nFatal server error:\n");
538
539    va_start(args, f);
540    VErrorF(f, args);
541    va_end(args);
542    ErrorF("\n");
543#ifdef DDXOSFATALERROR
544    if (!beenhere)
545	OsVendorFatalError();
546#endif
547#ifdef ABORTONFATALERROR
548    abort();
549#endif
550    if (!beenhere) {
551	beenhere = TRUE;
552	AbortServer();
553    } else
554	abort();
555    /*NOTREACHED*/
556}
557
558_X_EXPORT void
559VErrorF(const char *f, va_list args)
560{
561#ifdef DDXOSVERRORF
562    if (OsVendorVErrorFProc)
563	OsVendorVErrorFProc(f, args);
564    else
565	LogVWrite(-1, f, args);
566#else
567    LogVWrite(-1, f, args);
568#endif
569}
570
571_X_EXPORT void
572ErrorF(const char * f, ...)
573{
574    va_list args;
575
576    va_start(args, f);
577    VErrorF(f, args);
578    va_end(args);
579}
580
581/* A perror() workalike. */
582
583#ifndef NEED_STRERROR
584#ifdef SYSV
585#if !defined(ISC) || defined(ISC202) || defined(ISC22)
586#define NEED_STRERROR
587#endif
588#endif
589#endif
590
591#if defined(NEED_STRERROR) && !defined(strerror)
592extern char *sys_errlist[];
593extern int sys_nerr;
594#define strerror(n) \
595	((n) >= 0 && (n) < sys_nerr) ? sys_errlist[(n)] : "unknown error"
596#endif
597
598_X_EXPORT void
599Error(char *str)
600{
601    char *err = NULL;
602    int saveErrno = errno;
603
604    if (str) {
605	err = malloc(strlen(strerror(saveErrno)) + strlen(str) + 2 + 1);
606	if (!err)
607	    return;
608	sprintf(err, "%s: ", str);
609	strcat(err, strerror(saveErrno));
610	LogWrite(-1, err);
611    } else
612	LogWrite(-1, strerror(saveErrno));
613}
614
615void
616LogPrintMarkers(void)
617{
618    /* Show what the message marker symbols mean. */
619    ErrorF("Markers: ");
620    LogMessageVerb(X_PROBED, -1, "probed, ");
621    LogMessageVerb(X_CONFIG, -1, "from config file, ");
622    LogMessageVerb(X_DEFAULT, -1, "default setting,\n\t");
623    LogMessageVerb(X_CMDLINE, -1, "from command line, ");
624    LogMessageVerb(X_NOTICE, -1, "notice, ");
625    LogMessageVerb(X_INFO, -1, "informational,\n\t");
626    LogMessageVerb(X_WARNING, -1, "warning, ");
627    LogMessageVerb(X_ERROR, -1, "error, ");
628    LogMessageVerb(X_NOT_IMPLEMENTED, -1, "not implemented, ");
629    LogMessageVerb(X_UNKNOWN, -1, "unknown.\n");
630}
631
632