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