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