#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 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;
#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) {
logFileName = malloc(strlen(fname) + strlen(display) + 1);
if (!logFileName)
FatalError("Cannot allocate space for the log file name\n");
sprintf(logFileName, fname, display);
if (backup && *backup) {
struct stat buf;
if (!stat(logFileName, &buf) && S_ISREG(buf.st_mode)) {
char *suffix;
char *oldLog;
oldLog = malloc(strlen(logFileName) + strlen(backup) +
strlen(display) + 1);
suffix = malloc(strlen(backup) + strlen(display) + 1);
if (!oldLog || !suffix)
FatalError("Cannot allocate space for the log file name\n");
sprintf(suffix, backup, display);
sprintf(oldLog, "%s%s", logFileName, suffix);
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;
}
}
_X_EXPORT void
LogVWrite(int verb, const char *f, va_list args)
{
static char tmpBuffer[1024];
int len = 0;
if (verb < 0 || logFileVerbosity >= verb || logVerbosity >= verb) {
vsnprintf(tmpBuffer, sizeof(tmpBuffer), f, args);
len = strlen(tmpBuffer);
}
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;
if (saveBuffer)
saveBuffer = realloc(saveBuffer, bufferSize);
else
saveBuffer = malloc(bufferSize);
if (!saveBuffer)
FatalError("realloc() failed while saving log messages\n");
}
bufferUnused -= len;
memcpy(saveBuffer + bufferPos, tmpBuffer, len);
bufferPos += len;
}
}
}
_X_EXPORT void
LogWrite(int verb, const char *f, ...)
{
va_list args;
va_start(args, f);
LogVWrite(verb, f, args);
va_end(args);
}
_X_EXPORT void
LogVMessageVerb(MessageType type, int verb, const char *format, va_list args)
{
const char *s = X_UNKNOWN_STRING;
char *tmpBuf = NULL;
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;
}
if (s) {
tmpBuf = malloc(strlen(format) + strlen(s) + 1 + 1);
if (!tmpBuf)
return;
sprintf(tmpBuf, "%s ", s);
strcat(tmpBuf, format);
LogVWrite(verb, tmpBuf, args);
free(tmpBuf);
} else
LogVWrite(verb, format, args);
}
}
_X_EXPORT void
LogMessageVerb(MessageType type, int verb, const char *format, ...)
{
va_list ap;
va_start(ap, format);
LogVMessageVerb(type, verb, format, ap);
va_end(ap);
}
_X_EXPORT void
LogMessage(MessageType type, const char *format, ...)
{
va_list ap;
va_start(ap, format);
LogVMessageVerb(type, 1, format, ap);
va_end(ap);
}
#ifdef __GNUC__
void AbortServer(void) __attribute__((noreturn));
#endif
void
AbortServer(void)
{
OsCleanup(TRUE);
CloseDownDevices();
AbortDDX();
fflush(stderr);
if (CoreDump)
abort();
exit (1);
}
#ifndef AUDIT_PREFIX
#define AUDIT_PREFIX "AUDIT: %s: %ld %s: "
#endif
#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';
if ((s = strrchr(argvGlobal[0], '/')))
s++;
else
s = argvGlobal[0];
len = strlen(AUDIT_PREFIX) + strlen(autime) + 10 + strlen(s) + 1;
tmpBuf = malloc(len);
if (!tmpBuf)
return NULL;
snprintf(tmpBuf, len, AUDIT_PREFIX, autime, (unsigned long)getpid(), s);
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;
if (prefix != NULL)
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 1
ErrorF("%s%s", prefix != NULL ? prefix : "", buf);
oldlen = -1;
nrepeat = 0;
#else
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);
}
#endif
if (prefix != NULL)
free(prefix);
}
_X_EXPORT void
FatalError(const char *f, ...)
{
va_list args;
static Bool beenhere = FALSE;
if (beenhere)
ErrorF("\nFatalError re-entered, aborting\n");
else
ErrorF("\nFatal server error:\n");
va_start(args, f);
VErrorF(f, args);
va_end(args);
ErrorF("\n");
#ifdef DDXOSFATALERROR
if (!beenhere)
OsVendorFatalError();
#endif
#ifdef ABORTONFATALERROR
abort();
#endif
if (!beenhere) {
beenhere = TRUE;
AbortServer();
} else
abort();
}
_X_EXPORT 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
}
_X_EXPORT void
ErrorF(const char * f, ...)
{
va_list args;
va_start(args, f);
VErrorF(f, args);
va_end(args);
}
#ifndef NEED_STRERROR
#ifdef SYSV
#if !defined(ISC) || defined(ISC202) || defined(ISC22)
#define NEED_STRERROR
#endif
#endif
#endif
#if defined(NEED_STRERROR) && !defined(strerror)
extern char *sys_errlist[];
extern int sys_nerr;
#define strerror(n) \
((n) >= 0 && (n) < sys_nerr) ? sys_errlist[(n)] : "unknown error"
#endif
_X_EXPORT void
Error(char *str)
{
char *err = NULL;
int saveErrno = errno;
if (str) {
err = malloc(strlen(strerror(saveErrno)) + strlen(str) + 2 + 1);
if (!err)
return;
sprintf(err, "%s: ", str);
strcat(err, strerror(saveErrno));
LogWrite(-1, err);
} else
LogWrite(-1, strerror(saveErrno));
}
void
LogPrintMarkers(void)
{
ErrorF("Markers: ");
LogMessageVerb(X_PROBED, -1, "probed, ");
LogMessageVerb(X_CONFIG, -1, "from config file, ");
LogMessageVerb(X_DEFAULT, -1, "default setting,\n\t");
LogMessageVerb(X_CMDLINE, -1, "from command line, ");
LogMessageVerb(X_NOTICE, -1, "notice, ");
LogMessageVerb(X_INFO, -1, "informational,\n\t");
LogMessageVerb(X_WARNING, -1, "warning, ");
LogMessageVerb(X_ERROR, -1, "error, ");
LogMessageVerb(X_NOT_IMPLEMENTED, -1, "not implemented, ");
LogMessageVerb(X_UNKNOWN, -1, "unknown.\n");
}