log.c revision 05b261ec
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 OsCleanup(TRUE); 405 CloseDownDevices(); 406 AbortDDX(); 407 fflush(stderr); 408 if (CoreDump) 409 abort(); 410 exit (1); 411} 412 413#ifndef AUDIT_PREFIX 414#define AUDIT_PREFIX "AUDIT: %s: %ld %s: " 415#endif 416#ifndef AUDIT_TIMEOUT 417#define AUDIT_TIMEOUT ((CARD32)(120 * 1000)) /* 2 mn */ 418#endif 419 420static int nrepeat = 0; 421static int oldlen = -1; 422static OsTimerPtr auditTimer = NULL; 423 424void 425FreeAuditTimer(void) 426{ 427 if (auditTimer != NULL) { 428 /* Force output of pending messages */ 429 TimerForce(auditTimer); 430 TimerFree(auditTimer); 431 auditTimer = NULL; 432 } 433} 434 435static char * 436AuditPrefix(void) 437{ 438 time_t tm; 439 char *autime, *s; 440 char *tmpBuf; 441 int len; 442 443 time(&tm); 444 autime = ctime(&tm); 445 if ((s = strchr(autime, '\n'))) 446 *s = '\0'; 447 if ((s = strrchr(argvGlobal[0], '/'))) 448 s++; 449 else 450 s = argvGlobal[0]; 451 len = strlen(AUDIT_PREFIX) + strlen(autime) + 10 + strlen(s) + 1; 452 tmpBuf = malloc(len); 453 if (!tmpBuf) 454 return NULL; 455 snprintf(tmpBuf, len, AUDIT_PREFIX, autime, (unsigned long)getpid(), s); 456 return tmpBuf; 457} 458 459void 460AuditF(const char * f, ...) 461{ 462 va_list args; 463 464 va_start(args, f); 465 466 VAuditF(f, args); 467 va_end(args); 468} 469 470static CARD32 471AuditFlush(OsTimerPtr timer, CARD32 now, pointer arg) 472{ 473 char *prefix; 474 475 if (nrepeat > 0) { 476 prefix = AuditPrefix(); 477 ErrorF("%slast message repeated %d times\n", 478 prefix != NULL ? prefix : "", nrepeat); 479 nrepeat = 0; 480 if (prefix != NULL) 481 free(prefix); 482 return AUDIT_TIMEOUT; 483 } else { 484 /* if the timer expires without anything to print, flush the message */ 485 oldlen = -1; 486 return 0; 487 } 488} 489 490void 491VAuditF(const char *f, va_list args) 492{ 493 char *prefix; 494 char buf[1024]; 495 int len; 496 static char oldbuf[1024]; 497 498 prefix = AuditPrefix(); 499 len = vsnprintf(buf, sizeof(buf), f, args); 500 501#if 1 502 /* XXX Compressing duplicated messages is temporarily disabled to 503 * work around bugzilla 964: 504 * https://freedesktop.org/bugzilla/show_bug.cgi?id=964 505 */ 506 ErrorF("%s%s", prefix != NULL ? prefix : "", buf); 507 oldlen = -1; 508 nrepeat = 0; 509#else 510 if (len == oldlen && strcmp(buf, oldbuf) == 0) { 511 /* Message already seen */ 512 nrepeat++; 513 } else { 514 /* new message */ 515 if (auditTimer != NULL) 516 TimerForce(auditTimer); 517 ErrorF("%s%s", prefix != NULL ? prefix : "", buf); 518 strlcpy(oldbuf, buf, sizeof(oldbuf)); 519 oldlen = len; 520 nrepeat = 0; 521 auditTimer = TimerSet(auditTimer, 0, AUDIT_TIMEOUT, AuditFlush, NULL); 522 } 523#endif 524 if (prefix != NULL) 525 free(prefix); 526} 527 528_X_EXPORT void 529FatalError(const char *f, ...) 530{ 531 va_list args; 532 static Bool beenhere = FALSE; 533 534 if (beenhere) 535 ErrorF("\nFatalError re-entered, aborting\n"); 536 else 537 ErrorF("\nFatal server error:\n"); 538 539 va_start(args, f); 540 VErrorF(f, args); 541 va_end(args); 542 ErrorF("\n"); 543#ifdef DDXOSFATALERROR 544 if (!beenhere) 545 OsVendorFatalError(); 546#endif 547#ifdef ABORTONFATALERROR 548 abort(); 549#endif 550 if (!beenhere) { 551 beenhere = TRUE; 552 AbortServer(); 553 } else 554 abort(); 555 /*NOTREACHED*/ 556} 557 558_X_EXPORT void 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 571_X_EXPORT void 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 583#ifndef NEED_STRERROR 584#ifdef SYSV 585#if !defined(ISC) || defined(ISC202) || defined(ISC22) 586#define NEED_STRERROR 587#endif 588#endif 589#endif 590 591#if defined(NEED_STRERROR) && !defined(strerror) 592extern char *sys_errlist[]; 593extern int sys_nerr; 594#define strerror(n) \ 595 ((n) >= 0 && (n) < sys_nerr) ? sys_errlist[(n)] : "unknown error" 596#endif 597 598_X_EXPORT void 599Error(char *str) 600{ 601 char *err = NULL; 602 int saveErrno = errno; 603 604 if (str) { 605 err = malloc(strlen(strerror(saveErrno)) + strlen(str) + 2 + 1); 606 if (!err) 607 return; 608 sprintf(err, "%s: ", str); 609 strcat(err, strerror(saveErrno)); 610 LogWrite(-1, err); 611 } else 612 LogWrite(-1, strerror(saveErrno)); 613} 614 615void 616LogPrintMarkers(void) 617{ 618 /* Show what the message marker symbols mean. */ 619 ErrorF("Markers: "); 620 LogMessageVerb(X_PROBED, -1, "probed, "); 621 LogMessageVerb(X_CONFIG, -1, "from config file, "); 622 LogMessageVerb(X_DEFAULT, -1, "default setting,\n\t"); 623 LogMessageVerb(X_CMDLINE, -1, "from command line, "); 624 LogMessageVerb(X_NOTICE, -1, "notice, "); 625 LogMessageVerb(X_INFO, -1, "informational,\n\t"); 626 LogMessageVerb(X_WARNING, -1, "warning, "); 627 LogMessageVerb(X_ERROR, -1, "error, "); 628 LogMessageVerb(X_NOT_IMPLEMENTED, -1, "not implemented, "); 629 LogMessageVerb(X_UNKNOWN, -1, "unknown.\n"); 630} 631 632