log.c revision 4642e01f
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    CloseWellKnownConnections();
405    OsCleanup(TRUE);
406    CloseDownDevices();
407    AbortDDX();
408    fflush(stderr);
409    if (CoreDump)
410	abort();
411    exit (1);
412}
413
414#define AUDIT_PREFIX "AUDIT: %s: %ld: "
415#ifndef AUDIT_TIMEOUT
416#define AUDIT_TIMEOUT ((CARD32)(120 * 1000)) /* 2 mn */
417#endif
418
419static int nrepeat = 0;
420static int oldlen = -1;
421static OsTimerPtr auditTimer = NULL;
422
423void
424FreeAuditTimer(void)
425{
426    if (auditTimer != NULL) {
427	/* Force output of pending messages */
428	TimerForce(auditTimer);
429	TimerFree(auditTimer);
430	auditTimer = NULL;
431    }
432}
433
434static char *
435AuditPrefix(void)
436{
437    time_t tm;
438    char *autime, *s;
439    char *tmpBuf;
440    int len;
441
442    time(&tm);
443    autime = ctime(&tm);
444    if ((s = strchr(autime, '\n')))
445	*s = '\0';
446    len = strlen(AUDIT_PREFIX) + strlen(autime) + 10 + 1;
447    tmpBuf = malloc(len);
448    if (!tmpBuf)
449	return NULL;
450    snprintf(tmpBuf, len, AUDIT_PREFIX, autime, (unsigned long)getpid());
451    return tmpBuf;
452}
453
454void
455AuditF(const char * f, ...)
456{
457    va_list args;
458
459    va_start(args, f);
460
461    VAuditF(f, args);
462    va_end(args);
463}
464
465static CARD32
466AuditFlush(OsTimerPtr timer, CARD32 now, pointer arg)
467{
468    char *prefix;
469
470    if (nrepeat > 0) {
471	prefix = AuditPrefix();
472	ErrorF("%slast message repeated %d times\n",
473	       prefix != NULL ? prefix : "", nrepeat);
474	nrepeat = 0;
475	if (prefix != NULL)
476	    free(prefix);
477	return AUDIT_TIMEOUT;
478    } else {
479	/* if the timer expires without anything to print, flush the message */
480	oldlen = -1;
481	return 0;
482    }
483}
484
485void
486VAuditF(const char *f, va_list args)
487{
488    char *prefix;
489    char buf[1024];
490    int len;
491    static char oldbuf[1024];
492
493    prefix = AuditPrefix();
494    len = vsnprintf(buf, sizeof(buf), f, args);
495
496    if (len == oldlen && strcmp(buf, oldbuf) == 0) {
497	/* Message already seen */
498	nrepeat++;
499    } else {
500	/* new message */
501	if (auditTimer != NULL)
502	    TimerForce(auditTimer);
503	ErrorF("%s%s", prefix != NULL ? prefix : "", buf);
504	strlcpy(oldbuf, buf, sizeof(oldbuf));
505	oldlen = len;
506	nrepeat = 0;
507	auditTimer = TimerSet(auditTimer, 0, AUDIT_TIMEOUT, AuditFlush, NULL);
508    }
509    if (prefix != NULL)
510	free(prefix);
511}
512
513_X_EXPORT void
514FatalError(const char *f, ...)
515{
516    va_list args;
517    static Bool beenhere = FALSE;
518
519    if (beenhere)
520	ErrorF("\nFatalError re-entered, aborting\n");
521    else
522	ErrorF("\nFatal server error:\n");
523
524    va_start(args, f);
525    VErrorF(f, args);
526    va_end(args);
527    ErrorF("\n");
528    if (!beenhere)
529	OsVendorFatalError();
530    if (!beenhere) {
531	beenhere = TRUE;
532	AbortServer();
533    } else
534	abort();
535    /*NOTREACHED*/
536}
537
538_X_EXPORT void
539VErrorF(const char *f, va_list args)
540{
541#ifdef DDXOSVERRORF
542    if (OsVendorVErrorFProc)
543	OsVendorVErrorFProc(f, args);
544    else
545	LogVWrite(-1, f, args);
546#else
547    LogVWrite(-1, f, args);
548#endif
549}
550
551_X_EXPORT void
552ErrorF(const char * f, ...)
553{
554    va_list args;
555
556    va_start(args, f);
557    VErrorF(f, args);
558    va_end(args);
559}
560
561/* A perror() workalike. */
562
563_X_EXPORT void
564Error(char *str)
565{
566    char *err = NULL;
567    int saveErrno = errno;
568
569    if (str) {
570	err = malloc(strlen(strerror(saveErrno)) + strlen(str) + 2 + 1);
571	if (!err)
572	    return;
573	sprintf(err, "%s: ", str);
574	strcat(err, strerror(saveErrno));
575	LogWrite(-1, "%s", err);
576	free(err);
577    } else
578	LogWrite(-1, "%s", strerror(saveErrno));
579}
580
581void
582LogPrintMarkers(void)
583{
584    /* Show what the message marker symbols mean. */
585    ErrorF("Markers: ");
586    LogMessageVerb(X_PROBED, -1, "probed, ");
587    LogMessageVerb(X_CONFIG, -1, "from config file, ");
588    LogMessageVerb(X_DEFAULT, -1, "default setting,\n\t");
589    LogMessageVerb(X_CMDLINE, -1, "from command line, ");
590    LogMessageVerb(X_NOTICE, -1, "notice, ");
591    LogMessageVerb(X_INFO, -1, "informational,\n\t");
592    LogMessageVerb(X_WARNING, -1, "warning, ");
593    LogMessageVerb(X_ERROR, -1, "error, ");
594    LogMessageVerb(X_NOT_IMPLEMENTED, -1, "not implemented, ");
595    LogMessageVerb(X_UNKNOWN, -1, "unknown.\n");
596}
597
598