#ifdef HAVE_DIX_CONFIG_H
#include <dix-config.h>
#endif
#include <X11/Xos.h>
#include <stdio.h>
#include <time.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <stdlib.h>
#include <errno.h>
#include "input.h"
#include "site.h"
#include "opaque.h"
#ifdef WIN32
#include <process.h>
#define getpid(x) _getpid(x)
#endif
#ifdef XF86BIGFONT
#include "xf86bigfontsrv.h"
#endif
#ifdef __clang__
#pragma clang diagnostic ignored "-Wformat-nonliteral"
#endif
#ifdef DDXOSVERRORF
void (*OsVendorVErrorFProc)(const char *, va_list args) = NULL;
#endif
static FILE *logFile = NULL;
static Bool logFlush = FALSE;
static Bool logSync = FALSE;
static int logVerbosity = DEFAULT_LOG_VERBOSITY;
static int logFileVerbosity = DEFAULT_LOG_FILE_VERBOSITY;
static char *saveBuffer = NULL;
static int bufferSize = 0, bufferUnused = 0, bufferPos = 0;
static Bool needBuffer = TRUE;
#ifdef __APPLE__
#include <AvailabilityMacros.h>
static char __crashreporter_info_buff__[4096] = {0};
static const char *__crashreporter_info__ __attribute__((__used__)) = &__crashreporter_info_buff__[0];
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
asm (".desc ___crashreporter_info__, 0x10");
#endif
#endif
#ifndef X_UNKNOWN_STRING
#define X_UNKNOWN_STRING "(\?\?)"
#endif
#ifndef X_PROBE_STRING
#define X_PROBE_STRING "(--)"
#endif
#ifndef X_CONFIG_STRING
#define X_CONFIG_STRING "(**)"
#endif
#ifndef X_DEFAULT_STRING
#define X_DEFAULT_STRING "(==)"
#endif
#ifndef X_CMDLINE_STRING
#define X_CMDLINE_STRING "(++)"
#endif
#ifndef X_NOTICE_STRING
#define X_NOTICE_STRING "(!!)"
#endif
#ifndef X_ERROR_STRING
#define X_ERROR_STRING "(EE)"
#endif
#ifndef X_WARNING_STRING
#define X_WARNING_STRING "(WW)"
#endif
#ifndef X_INFO_STRING
#define X_INFO_STRING "(II)"
#endif
#ifndef X_NOT_IMPLEMENTED_STRING
#define X_NOT_IMPLEMENTED_STRING "(NI)"
#endif
const char *
LogInit(const char *fname, const char *backup)
{
char *logFileName = NULL;
if (fname && *fname) {
if (asprintf(&logFileName, fname, display) == -1)
FatalError("Cannot allocate space for the log file name\n");
if (backup && *backup) {
struct stat buf;
if (!stat(logFileName, &buf) && S_ISREG(buf.st_mode)) {
char *suffix;
char *oldLog;
if ((asprintf(&suffix, backup, display) == -1) ||
(asprintf(&oldLog, "%s%s", logFileName, suffix) == -1))
FatalError("Cannot allocate space for the log file name\n");
free(suffix);
if (rename(logFileName, oldLog) == -1) {
FatalError("Cannot move old log file \"%s\" to \"%s\"\n",
logFileName, oldLog);
}
free(oldLog);
}
}
if ((logFile = fopen(logFileName, "w")) == NULL)
FatalError("Cannot open log file \"%s\"\n", logFileName);
setvbuf(logFile, NULL, _IONBF, 0);
if (saveBuffer && bufferSize > 0) {
fwrite(saveBuffer, bufferPos, 1, logFile);
fflush(logFile);
#ifndef WIN32
fsync(fileno(logFile));
#endif
}
}
if (saveBuffer && bufferSize > 0) {
free(saveBuffer);
saveBuffer = NULL;
bufferSize = 0;
}
needBuffer = FALSE;
return logFileName;
}
void
LogClose(void)
{
if (logFile) {
fclose(logFile);
logFile = NULL;
}
}
Bool
LogSetParameter(LogParameter param, int value)
{
switch (param) {
case XLOG_FLUSH:
logFlush = value ? TRUE : FALSE;
return TRUE;
case XLOG_SYNC:
logSync = value ? TRUE : FALSE;
return TRUE;
case XLOG_VERBOSITY:
logVerbosity = value;
return TRUE;
case XLOG_FILE_VERBOSITY:
logFileVerbosity = value;
return TRUE;
default:
return FALSE;
}
}
void
LogVWrite(int verb, const char *f, va_list args)
{
static char tmpBuffer[1024];
int len = 0;
static Bool newline = TRUE;
if (newline) {
sprintf(tmpBuffer, "[%10.3f] ", GetTimeInMillis() / 1000.0);
len = strlen(tmpBuffer);
if (logFile)
fwrite(tmpBuffer, len, 1, logFile);
}
if (verb < 0 || logFileVerbosity >= verb || logVerbosity >= verb) {
vsnprintf(tmpBuffer, sizeof(tmpBuffer), f, args);
len = strlen(tmpBuffer);
}
newline = (tmpBuffer[len-1] == '\n');
if ((verb < 0 || logVerbosity >= verb) && len > 0)
fwrite(tmpBuffer, len, 1, stderr);
if ((verb < 0 || logFileVerbosity >= verb) && len > 0) {
if (logFile) {
fwrite(tmpBuffer, len, 1, logFile);
if (logFlush) {
fflush(logFile);
#ifndef WIN32
if (logSync)
fsync(fileno(logFile));
#endif
}
} else if (needBuffer) {
if (len > bufferUnused) {
bufferSize += 1024;
bufferUnused += 1024;
saveBuffer = realloc(saveBuffer, bufferSize);
if (!saveBuffer)
FatalError("realloc() failed while saving log messages\n");
}
bufferUnused -= len;
memcpy(saveBuffer + bufferPos, tmpBuffer, len);
bufferPos += len;
}
}
}
void
LogWrite(int verb, const char *f, ...)
{
va_list args;
va_start(args, f);
LogVWrite(verb, f, args);
va_end(args);
}
void
LogVMessageVerb(MessageType type, int verb, const char *format, va_list args)
{
const char *s = X_UNKNOWN_STRING;
char tmpBuf[1024];
if (logVerbosity >= verb || logFileVerbosity >= verb || type == X_ERROR) {
switch (type) {
case X_PROBED:
s = X_PROBE_STRING;
break;
case X_CONFIG:
s = X_CONFIG_STRING;
break;
case X_DEFAULT:
s = X_DEFAULT_STRING;
break;
case X_CMDLINE:
s = X_CMDLINE_STRING;
break;
case X_NOTICE:
s = X_NOTICE_STRING;
break;
case X_ERROR:
s = X_ERROR_STRING;
if (verb > 0)
verb = 0;
break;
case X_WARNING:
s = X_WARNING_STRING;
break;
case X_INFO:
s = X_INFO_STRING;
break;
case X_NOT_IMPLEMENTED:
s = X_NOT_IMPLEMENTED_STRING;
break;
case X_UNKNOWN:
s = X_UNKNOWN_STRING;
break;
case X_NONE:
s = NULL;
break;
}
snprintf(tmpBuf, sizeof(tmpBuf), "%s%s%s", s ? s : "",
s ? " " : "",
format);
LogVWrite(verb, tmpBuf, args);
}
}
void
LogMessageVerb(MessageType type, int verb, const char *format, ...)
{
va_list ap;
va_start(ap, format);
LogVMessageVerb(type, verb, format, ap);
va_end(ap);
}
void
LogMessage(MessageType type, const char *format, ...)
{
va_list ap;
va_start(ap, format);
LogVMessageVerb(type, 1, format, ap);
va_end(ap);
}
void
AbortServer(void) _X_NORETURN;
void
AbortServer(void)
{
#ifdef XF86BIGFONT
XF86BigfontCleanup();
#endif
CloseWellKnownConnections();
OsCleanup(TRUE);
CloseDownDevices();
AbortDDX();
fflush(stderr);
if (CoreDump)
OsAbort();
exit (1);
}
#define AUDIT_PREFIX "AUDIT: %s: %ld: "
#ifndef AUDIT_TIMEOUT
#define AUDIT_TIMEOUT ((CARD32)(120 * 1000))
#endif
static int nrepeat = 0;
static int oldlen = -1;
static OsTimerPtr auditTimer = NULL;
void
FreeAuditTimer(void)
{
if (auditTimer != NULL) {
TimerForce(auditTimer);
TimerFree(auditTimer);
auditTimer = NULL;
}
}
static char *
AuditPrefix(void)
{
time_t tm;
char *autime, *s;
char *tmpBuf;
int len;
time(&tm);
autime = ctime(&tm);
if ((s = strchr(autime, '\n')))
*s = '\0';
len = strlen(AUDIT_PREFIX) + strlen(autime) + 10 + 1;
tmpBuf = malloc(len);
if (!tmpBuf)
return NULL;
snprintf(tmpBuf, len, AUDIT_PREFIX, autime, (unsigned long)getpid());
return tmpBuf;
}
void
AuditF(const char * f, ...)
{
va_list args;
va_start(args, f);
VAuditF(f, args);
va_end(args);
}
static CARD32
AuditFlush(OsTimerPtr timer, CARD32 now, pointer arg)
{
char *prefix;
if (nrepeat > 0) {
prefix = AuditPrefix();
ErrorF("%slast message repeated %d times\n",
prefix != NULL ? prefix : "", nrepeat);
nrepeat = 0;
free(prefix);
return AUDIT_TIMEOUT;
} else {
oldlen = -1;
return 0;
}
}
void
VAuditF(const char *f, va_list args)
{
char *prefix;
char buf[1024];
int len;
static char oldbuf[1024];
prefix = AuditPrefix();
len = vsnprintf(buf, sizeof(buf), f, args);
if (len == oldlen && strcmp(buf, oldbuf) == 0) {
nrepeat++;
} else {
if (auditTimer != NULL)
TimerForce(auditTimer);
ErrorF("%s%s", prefix != NULL ? prefix : "", buf);
strlcpy(oldbuf, buf, sizeof(oldbuf));
oldlen = len;
nrepeat = 0;
auditTimer = TimerSet(auditTimer, 0, AUDIT_TIMEOUT, AuditFlush, NULL);
}
free(prefix);
}
void
FatalError(const char *f, ...)
{
va_list args;
va_list args2;
static Bool beenhere = FALSE;
if (beenhere)
ErrorF("\nFatalError re-entered, aborting\n");
else
ErrorF("\nFatal server error:\n");
va_copy(args2, args);
#ifdef __APPLE__
{
va_list apple_args;
va_copy(apple_args, args);
(void)vsnprintf(__crashreporter_info_buff__, sizeof(__crashreporter_info_buff__), f, apple_args);
va_end(apple_args);
}
#endif
VErrorF(f, args);
va_end(args);
ErrorF("\n");
if (!beenhere)
OsVendorFatalError(f, args2);
va_end(args2);
if (!beenhere) {
beenhere = TRUE;
AbortServer();
} else
OsAbort();
}
void
VErrorF(const char *f, va_list args)
{
#ifdef DDXOSVERRORF
if (OsVendorVErrorFProc)
OsVendorVErrorFProc(f, args);
else
LogVWrite(-1, f, args);
#else
LogVWrite(-1, f, args);
#endif
}
void
ErrorF(const char * f, ...)
{
va_list args;
va_start(args, f);
VErrorF(f, args);
va_end(args);
}
void
Error(const char *str)
{
const char *err = strerror(errno);
if (str)
LogWrite(-1, "%s: %s", str, err);
else
LogWrite(-1, "%s", err);
}
void
LogPrintMarkers(void)
{
LogWrite(0, "Markers: ");
LogMessageVerb(X_PROBED, 0, "probed, ");
LogMessageVerb(X_CONFIG, 0, "from config file, ");
LogMessageVerb(X_DEFAULT, 0, "default setting,\n\t");
LogMessageVerb(X_CMDLINE, 0, "from command line, ");
LogMessageVerb(X_NOTICE, 0, "notice, ");
LogMessageVerb(X_INFO, 0, "informational,\n\t");
LogMessageVerb(X_WARNING, 0, "warning, ");
LogMessageVerb(X_ERROR, 0, "error, ");
LogMessageVerb(X_NOT_IMPLEMENTED, 0, "not implemented, ");
LogMessageVerb(X_UNKNOWN, 0, "unknown.\n");
}