log.c revision 6747b715
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#ifdef XF86BIGFONT
102#include "xf86bigfontsrv.h"
103#endif
104
105#ifdef DDXOSVERRORF
106void (*OsVendorVErrorFProc)(const char *, va_list args) = NULL;
107#endif
108
109static FILE *logFile = NULL;
110static Bool logFlush = FALSE;
111static Bool logSync = FALSE;
112static int logVerbosity = DEFAULT_LOG_VERBOSITY;
113static int logFileVerbosity = DEFAULT_LOG_FILE_VERBOSITY;
114
115/* Buffer to information logged before the log file is opened. */
116static char *saveBuffer = NULL;
117static int bufferSize = 0, bufferUnused = 0, bufferPos = 0;
118static Bool needBuffer = TRUE;
119
120#ifdef __APPLE__
121#include <AvailabilityMacros.h>
122
123static char __crashreporter_info_buff__[4096] = {0};
124static const char *__crashreporter_info__ = &__crashreporter_info_buff__[0];
125#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
126// This is actually a toolchain requirement, but I'm not sure the correct check,
127// but it should be fine to just only include it for Leopard and later.  This line
128// just tells the linker to never strip this symbol (such as for space optimization)
129asm (".desc ___crashreporter_info__, 0x10");
130#endif
131#endif
132
133/* Prefix strings for log messages. */
134#ifndef X_UNKNOWN_STRING
135#define X_UNKNOWN_STRING		"(\?\?)"
136#endif
137#ifndef X_PROBE_STRING
138#define X_PROBE_STRING			"(--)"
139#endif
140#ifndef X_CONFIG_STRING
141#define X_CONFIG_STRING			"(**)"
142#endif
143#ifndef X_DEFAULT_STRING
144#define X_DEFAULT_STRING		"(==)"
145#endif
146#ifndef X_CMDLINE_STRING
147#define X_CMDLINE_STRING		"(++)"
148#endif
149#ifndef X_NOTICE_STRING
150#define X_NOTICE_STRING			"(!!)"
151#endif
152#ifndef X_ERROR_STRING
153#define X_ERROR_STRING			"(EE)"
154#endif
155#ifndef X_WARNING_STRING
156#define X_WARNING_STRING		"(WW)"
157#endif
158#ifndef X_INFO_STRING
159#define X_INFO_STRING			"(II)"
160#endif
161#ifndef X_NOT_IMPLEMENTED_STRING
162#define X_NOT_IMPLEMENTED_STRING	"(NI)"
163#endif
164
165/*
166 * LogInit is called to start logging to a file.  It is also called (with
167 * NULL arguments) when logging to a file is not wanted.  It must always be
168 * called, otherwise log messages will continue to accumulate in a buffer.
169 *
170 * %s, if present in the fname or backup strings, is expanded to the display
171 * string.
172 */
173
174const char *
175LogInit(const char *fname, const char *backup)
176{
177    char *logFileName = NULL;
178
179    if (fname && *fname) {
180	/* malloc() can't be used yet. */
181	logFileName = malloc(strlen(fname) + strlen(display) + 1);
182	if (!logFileName)
183	    FatalError("Cannot allocate space for the log file name\n");
184	sprintf(logFileName, fname, display);
185
186	if (backup && *backup) {
187	    struct stat buf;
188
189	    if (!stat(logFileName, &buf) && S_ISREG(buf.st_mode)) {
190		char *suffix;
191		char *oldLog;
192
193		oldLog = malloc(strlen(logFileName) + strlen(backup) +
194				strlen(display) + 1);
195		suffix = malloc(strlen(backup) + strlen(display) + 1);
196		if (!oldLog || !suffix)
197		    FatalError("Cannot allocate space for the log file name\n");
198		sprintf(suffix, backup, display);
199		sprintf(oldLog, "%s%s", logFileName, suffix);
200		free(suffix);
201		if (rename(logFileName, oldLog) == -1) {
202		    FatalError("Cannot move old log file \"%s\" to \"%s\"\n",
203			       logFileName, oldLog);
204		}
205		free(oldLog);
206	    }
207	}
208	if ((logFile = fopen(logFileName, "w")) == NULL)
209	    FatalError("Cannot open log file \"%s\"\n", logFileName);
210	setvbuf(logFile, NULL, _IONBF, 0);
211
212	/* Flush saved log information. */
213	if (saveBuffer && bufferSize > 0) {
214	    fwrite(saveBuffer, bufferPos, 1, logFile);
215	    fflush(logFile);
216#ifndef WIN32
217	    fsync(fileno(logFile));
218#endif
219	}
220    }
221
222    /*
223     * Unconditionally free the buffer, and flag that the buffer is no longer
224     * needed.
225     */
226    if (saveBuffer && bufferSize > 0) {
227	free(saveBuffer);	/* Must be free(), not free() */
228	saveBuffer = NULL;
229	bufferSize = 0;
230    }
231    needBuffer = FALSE;
232
233    return logFileName;
234}
235
236void
237LogClose(void)
238{
239    if (logFile) {
240	fclose(logFile);
241	logFile = NULL;
242    }
243}
244
245Bool
246LogSetParameter(LogParameter param, int value)
247{
248    switch (param) {
249    case XLOG_FLUSH:
250	logFlush = value ? TRUE : FALSE;
251	return TRUE;
252    case XLOG_SYNC:
253	logSync = value ? TRUE : FALSE;
254	return TRUE;
255    case XLOG_VERBOSITY:
256	logVerbosity = value;
257	return TRUE;
258    case XLOG_FILE_VERBOSITY:
259	logFileVerbosity = value;
260	return TRUE;
261    default:
262	return FALSE;
263    }
264}
265
266/* This function does the actual log message writes. */
267
268void
269LogVWrite(int verb, const char *f, va_list args)
270{
271    static char tmpBuffer[1024];
272    int len = 0;
273    static Bool newline = TRUE;
274
275    if (newline) {
276	sprintf(tmpBuffer, "[%10.3f] ", GetTimeInMillis() / 1000.0);
277	len = strlen(tmpBuffer);
278	if (logFile)
279	    fwrite(tmpBuffer, len, 1, logFile);
280    }
281
282    /*
283     * Since a va_list can only be processed once, write the string to a
284     * buffer, and then write the buffer out to the appropriate output
285     * stream(s).
286     */
287    if (verb < 0 || logFileVerbosity >= verb || logVerbosity >= verb) {
288	vsnprintf(tmpBuffer, sizeof(tmpBuffer), f, args);
289	len = strlen(tmpBuffer);
290    }
291    newline = (tmpBuffer[len-1] == '\n');
292    if ((verb < 0 || logVerbosity >= verb) && len > 0)
293	fwrite(tmpBuffer, len, 1, stderr);
294    if ((verb < 0 || logFileVerbosity >= verb) && len > 0) {
295	if (logFile) {
296	    fwrite(tmpBuffer, len, 1, logFile);
297	    if (logFlush) {
298		fflush(logFile);
299#ifndef WIN32
300		if (logSync)
301		    fsync(fileno(logFile));
302#endif
303	    }
304	} else if (needBuffer) {
305	    /*
306	     * Note, this code is used before OsInit() has been called, so
307	     * malloc() and friends can't be used.
308	     */
309	    if (len > bufferUnused) {
310		bufferSize += 1024;
311		bufferUnused += 1024;
312		if (saveBuffer)
313		    saveBuffer = realloc(saveBuffer, bufferSize);
314		else
315		    saveBuffer = malloc(bufferSize);
316		if (!saveBuffer)
317		    FatalError("realloc() failed while saving log messages\n");
318	    }
319	    bufferUnused -= len;
320	    memcpy(saveBuffer + bufferPos, tmpBuffer, len);
321	    bufferPos += len;
322	}
323    }
324}
325
326void
327LogWrite(int verb, const char *f, ...)
328{
329    va_list args;
330
331    va_start(args, f);
332    LogVWrite(verb, f, args);
333    va_end(args);
334}
335
336void
337LogVMessageVerb(MessageType type, int verb, const char *format, va_list args)
338{
339    const char *s  = X_UNKNOWN_STRING;
340    char tmpBuf[1024];
341
342    /* Ignore verbosity for X_ERROR */
343    if (logVerbosity >= verb || logFileVerbosity >= verb || type == X_ERROR) {
344	switch (type) {
345	case X_PROBED:
346	    s = X_PROBE_STRING;
347	    break;
348	case X_CONFIG:
349	    s = X_CONFIG_STRING;
350	    break;
351	case X_DEFAULT:
352	    s = X_DEFAULT_STRING;
353	    break;
354	case X_CMDLINE:
355	    s = X_CMDLINE_STRING;
356	    break;
357	case X_NOTICE:
358	    s = X_NOTICE_STRING;
359	    break;
360	case X_ERROR:
361	    s = X_ERROR_STRING;
362	    if (verb > 0)
363		verb = 0;
364	    break;
365	case X_WARNING:
366	    s = X_WARNING_STRING;
367	    break;
368	case X_INFO:
369	    s = X_INFO_STRING;
370	    break;
371	case X_NOT_IMPLEMENTED:
372	    s = X_NOT_IMPLEMENTED_STRING;
373	    break;
374	case X_UNKNOWN:
375	    s = X_UNKNOWN_STRING;
376	    break;
377	case X_NONE:
378	    s = NULL;
379	    break;
380	}
381
382        /* if s is not NULL we need a space before format */
383        snprintf(tmpBuf, sizeof(tmpBuf), "%s%s%s", s ? s : "",
384                                                   s ? " " : "",
385                                                   format);
386        LogVWrite(verb, tmpBuf, args);
387    }
388}
389
390/* Log message with verbosity level specified. */
391void
392LogMessageVerb(MessageType type, int verb, const char *format, ...)
393{
394    va_list ap;
395
396    va_start(ap, format);
397    LogVMessageVerb(type, verb, format, ap);
398    va_end(ap);
399}
400
401/* Log a message with the standard verbosity level of 1. */
402void
403LogMessage(MessageType type, const char *format, ...)
404{
405    va_list ap;
406
407    va_start(ap, format);
408    LogVMessageVerb(type, 1, format, ap);
409    va_end(ap);
410}
411
412void
413AbortServer(void) _X_NORETURN;
414
415void
416AbortServer(void)
417{
418#ifdef XF86BIGFONT
419    XF86BigfontCleanup();
420#endif
421    CloseWellKnownConnections();
422    OsCleanup(TRUE);
423    CloseDownDevices();
424    AbortDDX();
425    fflush(stderr);
426    if (CoreDump)
427	OsAbort();
428    exit (1);
429}
430
431#define AUDIT_PREFIX "AUDIT: %s: %ld: "
432#ifndef AUDIT_TIMEOUT
433#define AUDIT_TIMEOUT ((CARD32)(120 * 1000)) /* 2 mn */
434#endif
435
436static int nrepeat = 0;
437static int oldlen = -1;
438static OsTimerPtr auditTimer = NULL;
439
440void
441FreeAuditTimer(void)
442{
443    if (auditTimer != NULL) {
444	/* Force output of pending messages */
445	TimerForce(auditTimer);
446	TimerFree(auditTimer);
447	auditTimer = NULL;
448    }
449}
450
451static char *
452AuditPrefix(void)
453{
454    time_t tm;
455    char *autime, *s;
456    char *tmpBuf;
457    int len;
458
459    time(&tm);
460    autime = ctime(&tm);
461    if ((s = strchr(autime, '\n')))
462	*s = '\0';
463    len = strlen(AUDIT_PREFIX) + strlen(autime) + 10 + 1;
464    tmpBuf = malloc(len);
465    if (!tmpBuf)
466	return NULL;
467    snprintf(tmpBuf, len, AUDIT_PREFIX, autime, (unsigned long)getpid());
468    return tmpBuf;
469}
470
471void
472AuditF(const char * f, ...)
473{
474    va_list args;
475
476    va_start(args, f);
477
478    VAuditF(f, args);
479    va_end(args);
480}
481
482static CARD32
483AuditFlush(OsTimerPtr timer, CARD32 now, pointer arg)
484{
485    char *prefix;
486
487    if (nrepeat > 0) {
488	prefix = AuditPrefix();
489	ErrorF("%slast message repeated %d times\n",
490	       prefix != NULL ? prefix : "", nrepeat);
491	nrepeat = 0;
492	if (prefix != NULL)
493	    free(prefix);
494	return AUDIT_TIMEOUT;
495    } else {
496	/* if the timer expires without anything to print, flush the message */
497	oldlen = -1;
498	return 0;
499    }
500}
501
502void
503VAuditF(const char *f, va_list args)
504{
505    char *prefix;
506    char buf[1024];
507    int len;
508    static char oldbuf[1024];
509
510    prefix = AuditPrefix();
511    len = vsnprintf(buf, sizeof(buf), f, args);
512
513    if (len == oldlen && strcmp(buf, oldbuf) == 0) {
514	/* Message already seen */
515	nrepeat++;
516    } else {
517	/* new message */
518	if (auditTimer != NULL)
519	    TimerForce(auditTimer);
520	ErrorF("%s%s", prefix != NULL ? prefix : "", buf);
521	strlcpy(oldbuf, buf, sizeof(oldbuf));
522	oldlen = len;
523	nrepeat = 0;
524	auditTimer = TimerSet(auditTimer, 0, AUDIT_TIMEOUT, AuditFlush, NULL);
525    }
526    if (prefix != NULL)
527	free(prefix);
528}
529
530void
531FatalError(const char *f, ...)
532{
533    va_list args;
534    static Bool beenhere = FALSE;
535
536    if (beenhere)
537	ErrorF("\nFatalError re-entered, aborting\n");
538    else
539	ErrorF("\nFatal server error:\n");
540
541    va_start(args, f);
542#ifdef __APPLE__
543    (void)vsnprintf(__crashreporter_info_buff__, sizeof(__crashreporter_info_buff__), f, args);
544#endif
545    VErrorF(f, args);
546    va_end(args);
547    ErrorF("\n");
548    if (!beenhere)
549	OsVendorFatalError();
550    if (!beenhere) {
551	beenhere = TRUE;
552	AbortServer();
553    } else
554	OsAbort();
555    /*NOTREACHED*/
556}
557
558void
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
571void
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
583void
584Error(char *str)
585{
586    char *err = NULL;
587    int saveErrno = errno;
588
589    if (str) {
590	err = malloc(strlen(strerror(saveErrno)) + strlen(str) + 2 + 1);
591	if (!err)
592	    return;
593	sprintf(err, "%s: ", str);
594	strcat(err, strerror(saveErrno));
595	LogWrite(-1, "%s", err);
596	free(err);
597    } else
598	LogWrite(-1, "%s", strerror(saveErrno));
599}
600
601void
602LogPrintMarkers(void)
603{
604    /* Show what the message marker symbols mean. */
605    LogWrite(0, "Markers: ");
606    LogMessageVerb(X_PROBED, 0, "probed, ");
607    LogMessageVerb(X_CONFIG, 0, "from config file, ");
608    LogMessageVerb(X_DEFAULT, 0, "default setting,\n\t");
609    LogMessageVerb(X_CMDLINE, 0, "from command line, ");
610    LogMessageVerb(X_NOTICE, 0, "notice, ");
611    LogMessageVerb(X_INFO, 0, "informational,\n\t");
612    LogMessageVerb(X_WARNING, 0, "warning, ");
613    LogMessageVerb(X_ERROR, 0, "error, ");
614    LogMessageVerb(X_NOT_IMPLEMENTED, 0, "not implemented, ");
615    LogMessageVerb(X_UNKNOWN, 0, "unknown.\n");
616}
617
618