log.c revision 35c4bbdf
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
27Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts,
28Copyright 1994 Quarterdeck Office Systems.
29
30                        All Rights Reserved
31
32Permission to use, copy, modify, and distribute this software and its
33documentation for any purpose and without fee is hereby granted,
34provided that the above copyright notice appear in all copies and that
35both that copyright notice and this permission notice appear in
36supporting documentation, and that the names of Digital and
37Quarterdeck not be used in advertising or publicity pertaining to
38distribution of the software without specific, written prior
39permission.
40
41DIGITAL AND QUARTERDECK DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
42SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
43FITNESS, IN NO EVENT SHALL DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT
44OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
45OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
46OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
47OR PERFORMANCE OF THIS SOFTWARE.
48
49*/
50
51/*
52 * Copyright (c) 1997-2003 by The XFree86 Project, Inc.
53 *
54 * Permission is hereby granted, free of charge, to any person obtaining a
55 * copy of this software and associated documentation files (the "Software"),
56 * to deal in the Software without restriction, including without limitation
57 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
58 * and/or sell copies of the Software, and to permit persons to whom the
59 * Software is furnished to do so, subject to the following conditions:
60 *
61 * The above copyright notice and this permission notice shall be included in
62 * all copies or substantial portions of the Software.
63 *
64 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
65 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
66 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
67 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
68 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
69 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
70 * OTHER DEALINGS IN THE SOFTWARE.
71 *
72 * Except as contained in this notice, the name of the copyright holder(s)
73 * and author(s) shall not be used in advertising or otherwise to promote
74 * the sale, use or other dealings in this Software without prior written
75 * authorization from the copyright holder(s) and author(s).
76 */
77
78#ifdef HAVE_DIX_CONFIG_H
79#include <dix-config.h>
80#endif
81
82#include <X11/Xos.h>
83#include <stdio.h>
84#include <time.h>
85#include <sys/stat.h>
86#include <stdarg.h>
87#include <stdlib.h>             /* for malloc() */
88#include <errno.h>
89
90#include "input.h"
91#include "site.h"
92#include "opaque.h"
93
94#ifdef WIN32
95#include <process.h>
96#define getpid(x) _getpid(x)
97#endif
98
99#ifdef XF86BIGFONT
100#include "xf86bigfontsrv.h"
101#endif
102
103#ifdef __clang__
104#pragma clang diagnostic ignored "-Wformat-nonliteral"
105#endif
106
107#ifdef DDXOSVERRORF
108void (*OsVendorVErrorFProc) (const char *, va_list args) = NULL;
109#endif
110
111static FILE *logFile = NULL;
112static int logFileFd = -1;
113static Bool logFlush = FALSE;
114static Bool logSync = FALSE;
115static int logVerbosity = DEFAULT_LOG_VERBOSITY;
116static int logFileVerbosity = DEFAULT_LOG_FILE_VERBOSITY;
117
118/* Buffer to information logged before the log file is opened. */
119static char *saveBuffer = NULL;
120static int bufferSize = 0, bufferUnused = 0, bufferPos = 0;
121static Bool needBuffer = TRUE;
122
123#ifdef __APPLE__
124#include <AvailabilityMacros.h>
125
126static char __crashreporter_info_buff__[4096] = { 0 };
127
128static const char *__crashreporter_info__ __attribute__ ((__used__)) =
129    &__crashreporter_info_buff__[0];
130#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
131// This is actually a toolchain requirement, but I'm not sure the correct check,
132// but it should be fine to just only include it for Leopard and later.  This line
133// just tells the linker to never strip this symbol (such as for space optimization)
134asm(".desc ___crashreporter_info__, 0x10");
135#endif
136#endif
137
138/* Prefix strings for log messages. */
139#ifndef X_UNKNOWN_STRING
140#define X_UNKNOWN_STRING		"(\?\?)"
141#endif
142#ifndef X_PROBE_STRING
143#define X_PROBE_STRING			"(--)"
144#endif
145#ifndef X_CONFIG_STRING
146#define X_CONFIG_STRING			"(**)"
147#endif
148#ifndef X_DEFAULT_STRING
149#define X_DEFAULT_STRING		"(==)"
150#endif
151#ifndef X_CMDLINE_STRING
152#define X_CMDLINE_STRING		"(++)"
153#endif
154#ifndef X_NOTICE_STRING
155#define X_NOTICE_STRING			"(!!)"
156#endif
157#ifndef X_ERROR_STRING
158#define X_ERROR_STRING			"(EE)"
159#endif
160#ifndef X_WARNING_STRING
161#define X_WARNING_STRING		"(WW)"
162#endif
163#ifndef X_INFO_STRING
164#define X_INFO_STRING			"(II)"
165#endif
166#ifndef X_NOT_IMPLEMENTED_STRING
167#define X_NOT_IMPLEMENTED_STRING	"(NI)"
168#endif
169#ifndef X_DEBUG_STRING
170#define X_DEBUG_STRING			"(DB)"
171#endif
172#ifndef X_NONE_STRING
173#define X_NONE_STRING			""
174#endif
175
176static size_t
177strlen_sigsafe(const char *s)
178{
179    size_t len;
180    for (len = 0; s[len]; len++);
181    return len;
182}
183
184/*
185 * LogFilePrep is called to setup files for logging, including getting
186 * an old file out of the way, but it doesn't actually open the file,
187 * since it may be used for renaming a file we're already logging to.
188 */
189#pragma GCC diagnostic push
190#pragma GCC diagnostic ignored "-Wformat-nonliteral"
191
192static char *
193LogFilePrep(const char *fname, const char *backup, const char *idstring)
194{
195    char *logFileName = NULL;
196
197    if (asprintf(&logFileName, fname, idstring) == -1)
198        FatalError("Cannot allocate space for the log file name\n");
199
200    if (backup && *backup) {
201        struct stat buf;
202
203        if (!stat(logFileName, &buf) && S_ISREG(buf.st_mode)) {
204            char *suffix;
205            char *oldLog;
206
207            if ((asprintf(&suffix, backup, idstring) == -1) ||
208                (asprintf(&oldLog, "%s%s", logFileName, suffix) == -1)) {
209                FatalError("Cannot allocate space for the log file name\n");
210            }
211            free(suffix);
212
213            if (rename(logFileName, oldLog) == -1) {
214                FatalError("Cannot move old log file \"%s\" to \"%s\"\n",
215                           logFileName, oldLog);
216            }
217            free(oldLog);
218        }
219    }
220    else {
221        if (remove(logFileName) != 0 && errno != ENOENT) {
222            FatalError("Cannot remove old log file \"%s\": %s\n",
223                       logFileName, strerror(errno));
224        }
225    }
226
227    return logFileName;
228}
229#pragma GCC diagnostic pop
230
231/*
232 * LogInit is called to start logging to a file.  It is also called (with
233 * NULL arguments) when logging to a file is not wanted.  It must always be
234 * called, otherwise log messages will continue to accumulate in a buffer.
235 *
236 * %s, if present in the fname or backup strings, is expanded to the display
237 * string (or to a string containing the pid if the display is not yet set).
238 */
239
240static char *saved_log_fname;
241static char *saved_log_backup;
242static char *saved_log_tempname;
243
244const char *
245LogInit(const char *fname, const char *backup)
246{
247    char *logFileName = NULL;
248
249    if (fname && *fname) {
250        if (displayfd != -1) {
251            /* Display isn't set yet, so we can't use it in filenames yet. */
252            char pidstring[32];
253            snprintf(pidstring, sizeof(pidstring), "pid-%ld",
254                     (unsigned long) getpid());
255            logFileName = LogFilePrep(fname, backup, pidstring);
256            saved_log_tempname = logFileName;
257
258            /* Save the patterns for use when the display is named. */
259            saved_log_fname = strdup(fname);
260            if (backup == NULL)
261                saved_log_backup = NULL;
262            else
263                saved_log_backup = strdup(backup);
264        } else
265            logFileName = LogFilePrep(fname, backup, display);
266        if ((logFile = fopen(logFileName, "w")) == NULL)
267            FatalError("Cannot open log file \"%s\"\n", logFileName);
268        setvbuf(logFile, NULL, _IONBF, 0);
269
270        logFileFd = fileno(logFile);
271
272        /* Flush saved log information. */
273        if (saveBuffer && bufferSize > 0) {
274            fwrite(saveBuffer, bufferPos, 1, logFile);
275            fflush(logFile);
276#ifndef WIN32
277            fsync(fileno(logFile));
278#endif
279        }
280    }
281
282    /*
283     * Unconditionally free the buffer, and flag that the buffer is no longer
284     * needed.
285     */
286    if (saveBuffer && bufferSize > 0) {
287        free(saveBuffer);
288        saveBuffer = NULL;
289        bufferSize = 0;
290    }
291    needBuffer = FALSE;
292
293    return logFileName;
294}
295
296void
297LogSetDisplay(void)
298{
299    if (saved_log_fname) {
300        char *logFileName;
301
302        logFileName = LogFilePrep(saved_log_fname, saved_log_backup, display);
303
304        if (rename(saved_log_tempname, logFileName) == 0) {
305            LogMessageVerb(X_PROBED, 0,
306                           "Log file renamed from \"%s\" to \"%s\"\n",
307                           saved_log_tempname, logFileName);
308
309            if (strlen(saved_log_tempname) >= strlen(logFileName))
310                strncpy(saved_log_tempname, logFileName,
311                        strlen(saved_log_tempname));
312        }
313        else {
314            ErrorF("Failed to rename log file \"%s\" to \"%s\": %s\n",
315                   saved_log_tempname, logFileName, strerror(errno));
316        }
317
318        /* free newly allocated string - can't free old one since existing
319           pointers to it may exist in DDX callers. */
320        free(logFileName);
321        free(saved_log_fname);
322        free(saved_log_backup);
323    }
324}
325
326void
327LogClose(enum ExitCode error)
328{
329    if (logFile) {
330        int msgtype = (error == EXIT_NO_ERROR) ? X_INFO : X_ERROR;
331        LogMessageVerbSigSafe(msgtype, -1,
332                "Server terminated %s (%d). Closing log file.\n",
333                (error == EXIT_NO_ERROR) ? "successfully" : "with error",
334                error);
335        fclose(logFile);
336        logFile = NULL;
337        logFileFd = -1;
338    }
339}
340
341Bool
342LogSetParameter(LogParameter param, int value)
343{
344    switch (param) {
345    case XLOG_FLUSH:
346        logFlush = value ? TRUE : FALSE;
347        return TRUE;
348    case XLOG_SYNC:
349        logSync = value ? TRUE : FALSE;
350        return TRUE;
351    case XLOG_VERBOSITY:
352        logVerbosity = value;
353        return TRUE;
354    case XLOG_FILE_VERBOSITY:
355        logFileVerbosity = value;
356        return TRUE;
357    default:
358        return FALSE;
359    }
360}
361
362enum {
363    LMOD_LONG     = 0x1,
364    LMOD_LONGLONG = 0x2,
365    LMOD_SHORT    = 0x4,
366    LMOD_SIZET    = 0x8,
367};
368
369/**
370 * Parse non-digit length modifiers and set the corresponding flag in
371 * flags_return.
372 *
373 * @return the number of bytes parsed
374 */
375static int parse_length_modifier(const char *format, size_t len, int *flags_return)
376{
377    int idx = 0;
378    int length_modifier = 0;
379
380    while (idx < len) {
381        switch (format[idx]) {
382            case 'l':
383                BUG_RETURN_VAL(length_modifier & LMOD_SHORT, 0);
384
385                if (length_modifier & LMOD_LONG)
386                    length_modifier |= LMOD_LONGLONG;
387                else
388                    length_modifier |= LMOD_LONG;
389                break;
390            case 'h':
391                BUG_RETURN_VAL(length_modifier & (LMOD_LONG|LMOD_LONGLONG), 0);
392                length_modifier |= LMOD_SHORT;
393                /* gcc says 'short int' is promoted to 'int' when
394                 * passed through '...', so ignored during
395                 * processing */
396                break;
397            case 'z':
398                length_modifier |= LMOD_SIZET;
399                break;
400            default:
401                goto out;
402        }
403        idx++;
404    }
405
406out:
407    *flags_return = length_modifier;
408    return idx;
409}
410
411/**
412 * Signal-safe snprintf, with some limitations over snprintf. Be careful
413 * which directives you use.
414 */
415static int
416vpnprintf(char *string, int size_in, const char *f, va_list args)
417{
418    int f_idx = 0;
419    int s_idx = 0;
420    int f_len = strlen_sigsafe(f);
421    char *string_arg;
422    char number[21];
423    int p_len;
424    int i;
425    uint64_t ui;
426    int64_t si;
427    size_t size = size_in;
428    int precision;
429
430    for (; f_idx < f_len && s_idx < size - 1; f_idx++) {
431        int length_modifier = 0;
432        if (f[f_idx] != '%') {
433            string[s_idx++] = f[f_idx];
434            continue;
435        }
436
437        f_idx++;
438
439        /* silently swallow minimum field width */
440        if (f[f_idx] == '*') {
441            f_idx++;
442            va_arg(args, int);
443        } else {
444            while (f_idx < f_len && ((f[f_idx] >= '0' && f[f_idx] <= '9')))
445                f_idx++;
446        }
447
448        /* is there a precision? */
449        precision = size;
450        if (f[f_idx] == '.') {
451            f_idx++;
452            if (f[f_idx] == '*') {
453                f_idx++;
454                /* precision is supplied in an int argument */
455                precision = va_arg(args, int);
456            } else {
457                /* silently swallow precision digits */
458                while (f_idx < f_len && ((f[f_idx] >= '0' && f[f_idx] <= '9')))
459                    f_idx++;
460            }
461        }
462
463        /* non-digit length modifiers */
464        if (f_idx < f_len) {
465            int parsed_bytes = parse_length_modifier(&f[f_idx], f_len - f_idx, &length_modifier);
466            if (parsed_bytes < 0)
467                return 0;
468            f_idx += parsed_bytes;
469        }
470
471        if (f_idx >= f_len)
472            break;
473
474        switch (f[f_idx]) {
475        case 's':
476            string_arg = va_arg(args, char*);
477
478            for (i = 0; string_arg[i] != 0 && s_idx < size - 1 && s_idx < precision; i++)
479                string[s_idx++] = string_arg[i];
480            break;
481
482        case 'u':
483            if (length_modifier & LMOD_LONGLONG)
484                ui = va_arg(args, unsigned long long);
485            else if (length_modifier & LMOD_LONG)
486                ui = va_arg(args, unsigned long);
487            else if (length_modifier & LMOD_SIZET)
488                ui = va_arg(args, size_t);
489            else
490                ui = va_arg(args, unsigned);
491
492            FormatUInt64(ui, number);
493            p_len = strlen_sigsafe(number);
494
495            for (i = 0; i < p_len && s_idx < size - 1; i++)
496                string[s_idx++] = number[i];
497            break;
498        case 'i':
499        case 'd':
500            if (length_modifier & LMOD_LONGLONG)
501                si = va_arg(args, long long);
502            else if (length_modifier & LMOD_LONG)
503                si = va_arg(args, long);
504            else if (length_modifier & LMOD_SIZET)
505                si = va_arg(args, ssize_t);
506            else
507                si = va_arg(args, int);
508
509            FormatInt64(si, number);
510            p_len = strlen_sigsafe(number);
511
512            for (i = 0; i < p_len && s_idx < size - 1; i++)
513                string[s_idx++] = number[i];
514            break;
515
516        case 'p':
517            string[s_idx++] = '0';
518            if (s_idx < size - 1)
519                string[s_idx++] = 'x';
520            ui = (uintptr_t)va_arg(args, void*);
521            FormatUInt64Hex(ui, number);
522            p_len = strlen_sigsafe(number);
523
524            for (i = 0; i < p_len && s_idx < size - 1; i++)
525                string[s_idx++] = number[i];
526            break;
527
528        case 'x':
529            if (length_modifier & LMOD_LONGLONG)
530                ui = va_arg(args, unsigned long long);
531            else if (length_modifier & LMOD_LONG)
532                ui = va_arg(args, unsigned long);
533            else if (length_modifier & LMOD_SIZET)
534                ui = va_arg(args, size_t);
535            else
536                ui = va_arg(args, unsigned);
537
538            FormatUInt64Hex(ui, number);
539            p_len = strlen_sigsafe(number);
540
541            for (i = 0; i < p_len && s_idx < size - 1; i++)
542                string[s_idx++] = number[i];
543            break;
544        case 'f':
545            {
546                double d = va_arg(args, double);
547                FormatDouble(d, number);
548                p_len = strlen_sigsafe(number);
549
550                for (i = 0; i < p_len && s_idx < size - 1; i++)
551                    string[s_idx++] = number[i];
552            }
553            break;
554        case 'c':
555            {
556                char c = va_arg(args, int);
557                if (s_idx < size - 1)
558                    string[s_idx++] = c;
559            }
560            break;
561        case '%':
562            string[s_idx++] = '%';
563            break;
564        default:
565            BUG_WARN_MSG(f[f_idx], "Unsupported printf directive '%c'\n", f[f_idx]);
566            va_arg(args, char*);
567            string[s_idx++] = '%';
568            if (s_idx < size - 1)
569                string[s_idx++] = f[f_idx];
570            break;
571        }
572    }
573
574    string[s_idx] = '\0';
575
576    return s_idx;
577}
578
579static int
580pnprintf(char *string, int size, const char *f, ...)
581{
582    int rc;
583    va_list args;
584
585    va_start(args, f);
586    rc = vpnprintf(string, size, f, args);
587    va_end(args);
588
589    return rc;
590}
591
592/* This function does the actual log message writes. It must be signal safe.
593 * When attempting to call non-signal-safe functions, guard them with a check
594 * of the inSignalContext global variable. */
595static void
596LogSWrite(int verb, const char *buf, size_t len, Bool end_line)
597{
598    static Bool newline = TRUE;
599    int ret;
600
601    if (verb < 0 || logVerbosity >= verb)
602        ret = write(2, buf, len);
603
604    if (verb < 0 || logFileVerbosity >= verb) {
605        if (inSignalContext && logFileFd >= 0) {
606            ret = write(logFileFd, buf, len);
607#ifndef WIN32
608            if (logFlush && logSync)
609                fsync(logFileFd);
610#endif
611        }
612        else if (!inSignalContext && logFile) {
613            if (newline)
614                fprintf(logFile, "[%10.3f] ", GetTimeInMillis() / 1000.0);
615            newline = end_line;
616            fwrite(buf, len, 1, logFile);
617            if (logFlush) {
618                fflush(logFile);
619#ifndef WIN32
620                if (logSync)
621                    fsync(fileno(logFile));
622#endif
623            }
624        }
625        else if (!inSignalContext && needBuffer) {
626            if (len > bufferUnused) {
627                bufferSize += 1024;
628                bufferUnused += 1024;
629                saveBuffer = realloc(saveBuffer, bufferSize);
630                if (!saveBuffer)
631                    FatalError("realloc() failed while saving log messages\n");
632            }
633            bufferUnused -= len;
634            memcpy(saveBuffer + bufferPos, buf, len);
635            bufferPos += len;
636        }
637    }
638
639    /* There's no place to log an error message if the log write
640     * fails...
641     */
642    (void) ret;
643}
644
645void
646LogVWrite(int verb, const char *f, va_list args)
647{
648    return LogVMessageVerb(X_NONE, verb, f, args);
649}
650
651void
652LogWrite(int verb, const char *f, ...)
653{
654    va_list args;
655
656    va_start(args, f);
657    LogVWrite(verb, f, args);
658    va_end(args);
659}
660
661/* Returns the Message Type string to prepend to a logging message, or NULL
662 * if the message will be dropped due to insufficient verbosity. */
663static const char *
664LogMessageTypeVerbString(MessageType type, int verb)
665{
666    if (type == X_ERROR)
667        verb = 0;
668
669    if (logVerbosity < verb && logFileVerbosity < verb)
670        return NULL;
671
672    switch (type) {
673    case X_PROBED:
674        return X_PROBE_STRING;
675    case X_CONFIG:
676        return X_CONFIG_STRING;
677    case X_DEFAULT:
678        return X_DEFAULT_STRING;
679    case X_CMDLINE:
680        return X_CMDLINE_STRING;
681    case X_NOTICE:
682        return X_NOTICE_STRING;
683    case X_ERROR:
684        return X_ERROR_STRING;
685    case X_WARNING:
686        return X_WARNING_STRING;
687    case X_INFO:
688        return X_INFO_STRING;
689    case X_NOT_IMPLEMENTED:
690        return X_NOT_IMPLEMENTED_STRING;
691    case X_UNKNOWN:
692        return X_UNKNOWN_STRING;
693    case X_NONE:
694        return X_NONE_STRING;
695    case X_DEBUG:
696        return X_DEBUG_STRING;
697    default:
698        return X_UNKNOWN_STRING;
699    }
700}
701
702void
703LogVMessageVerb(MessageType type, int verb, const char *format, va_list args)
704{
705    const char *type_str;
706    char buf[1024];
707    const size_t size = sizeof(buf);
708    Bool newline;
709    size_t len = 0;
710
711    if (inSignalContext) {
712        LogVMessageVerbSigSafe(type, verb, format, args);
713        return;
714    }
715
716    type_str = LogMessageTypeVerbString(type, verb);
717    if (!type_str)
718        return;
719
720    /* if type_str is not "", prepend it and ' ', to message */
721    if (type_str[0] != '\0')
722        len += Xscnprintf(&buf[len], size - len, "%s ", type_str);
723
724    if (size - len > 1)
725        len += Xvscnprintf(&buf[len], size - len, format, args);
726
727    /* Force '\n' at end of truncated line */
728    if (size - len == 1)
729        buf[len - 1] = '\n';
730
731    newline = (buf[len - 1] == '\n');
732    LogSWrite(verb, buf, len, newline);
733}
734
735/* Log message with verbosity level specified. */
736void
737LogMessageVerb(MessageType type, int verb, const char *format, ...)
738{
739    va_list ap;
740
741    va_start(ap, format);
742    LogVMessageVerb(type, verb, format, ap);
743    va_end(ap);
744}
745
746/* Log a message with the standard verbosity level of 1. */
747void
748LogMessage(MessageType type, const char *format, ...)
749{
750    va_list ap;
751
752    va_start(ap, format);
753    LogVMessageVerb(type, 1, format, ap);
754    va_end(ap);
755}
756
757/* Log a message using only signal safe functions. */
758void
759LogMessageVerbSigSafe(MessageType type, int verb, const char *format, ...)
760{
761    va_list ap;
762    va_start(ap, format);
763    LogVMessageVerbSigSafe(type, verb, format, ap);
764    va_end(ap);
765}
766
767void
768LogVMessageVerbSigSafe(MessageType type, int verb, const char *format, va_list args)
769{
770    const char *type_str;
771    char buf[1024];
772    int len;
773    Bool newline;
774
775    type_str = LogMessageTypeVerbString(type, verb);
776    if (!type_str)
777        return;
778
779    /* if type_str is not "", prepend it and ' ', to message */
780    if (type_str[0] != '\0') {
781        LogSWrite(verb, type_str, strlen_sigsafe(type_str), FALSE);
782        LogSWrite(verb, " ", 1, FALSE);
783    }
784
785    len = vpnprintf(buf, sizeof(buf), format, args);
786
787    /* Force '\n' at end of truncated line */
788    if (sizeof(buf) - len == 1)
789        buf[len - 1] = '\n';
790
791    newline = (len > 0 && buf[len - 1] == '\n');
792    LogSWrite(verb, buf, len, newline);
793}
794
795void
796LogVHdrMessageVerb(MessageType type, int verb, const char *msg_format,
797                   va_list msg_args, const char *hdr_format, va_list hdr_args)
798{
799    const char *type_str;
800    char buf[1024];
801    const size_t size = sizeof(buf);
802    Bool newline;
803    size_t len = 0;
804    int (*vprintf_func)(char *, int, const char* _X_RESTRICT_KYWD f, va_list args)
805            _X_ATTRIBUTE_PRINTF(3, 0);
806    int (*printf_func)(char *, int, const char* _X_RESTRICT_KYWD f, ...)
807            _X_ATTRIBUTE_PRINTF(3, 4);
808
809    type_str = LogMessageTypeVerbString(type, verb);
810    if (!type_str)
811        return;
812
813    if (inSignalContext) {
814        vprintf_func = vpnprintf;
815        printf_func = pnprintf;
816    } else {
817        vprintf_func = Xvscnprintf;
818        printf_func = Xscnprintf;
819    }
820
821    /* if type_str is not "", prepend it and ' ', to message */
822    if (type_str[0] != '\0')
823        len += printf_func(&buf[len], size - len, "%s ", type_str);
824
825    if (hdr_format && size - len > 1)
826        len += vprintf_func(&buf[len], size - len, hdr_format, hdr_args);
827
828    if (msg_format && size - len > 1)
829        len += vprintf_func(&buf[len], size - len, msg_format, msg_args);
830
831    /* Force '\n' at end of truncated line */
832    if (size - len == 1)
833        buf[len - 1] = '\n';
834
835    newline = (buf[len - 1] == '\n');
836    LogSWrite(verb, buf, len, newline);
837}
838
839void
840LogHdrMessageVerb(MessageType type, int verb, const char *msg_format,
841                  va_list msg_args, const char *hdr_format, ...)
842{
843    va_list hdr_args;
844
845    va_start(hdr_args, hdr_format);
846    LogVHdrMessageVerb(type, verb, msg_format, msg_args, hdr_format, hdr_args);
847    va_end(hdr_args);
848}
849
850void
851LogHdrMessage(MessageType type, const char *msg_format, va_list msg_args,
852              const char *hdr_format, ...)
853{
854    va_list hdr_args;
855
856    va_start(hdr_args, hdr_format);
857    LogVHdrMessageVerb(type, 1, msg_format, msg_args, hdr_format, hdr_args);
858    va_end(hdr_args);
859}
860
861void
862AbortServer(void)
863    _X_NORETURN;
864
865void
866AbortServer(void)
867{
868#ifdef XF86BIGFONT
869    XF86BigfontCleanup();
870#endif
871    CloseWellKnownConnections();
872    OsCleanup(TRUE);
873    AbortDevices();
874    AbortDDX(EXIT_ERR_ABORT);
875    fflush(stderr);
876    if (CoreDump)
877        OsAbort();
878    exit(1);
879}
880
881#define AUDIT_PREFIX "AUDIT: %s: %ld: "
882#ifndef AUDIT_TIMEOUT
883#define AUDIT_TIMEOUT ((CARD32)(120 * 1000))    /* 2 mn */
884#endif
885
886static int nrepeat = 0;
887static int oldlen = -1;
888static OsTimerPtr auditTimer = NULL;
889
890void
891FreeAuditTimer(void)
892{
893    if (auditTimer != NULL) {
894        /* Force output of pending messages */
895        TimerForce(auditTimer);
896        TimerFree(auditTimer);
897        auditTimer = NULL;
898    }
899}
900
901static char *
902AuditPrefix(void)
903{
904    time_t tm;
905    char *autime, *s;
906    char *tmpBuf;
907    int len;
908
909    time(&tm);
910    autime = ctime(&tm);
911    if ((s = strchr(autime, '\n')))
912        *s = '\0';
913    len = strlen(AUDIT_PREFIX) + strlen(autime) + 10 + 1;
914    tmpBuf = malloc(len);
915    if (!tmpBuf)
916        return NULL;
917    snprintf(tmpBuf, len, AUDIT_PREFIX, autime, (unsigned long) getpid());
918    return tmpBuf;
919}
920
921void
922AuditF(const char *f, ...)
923{
924    va_list args;
925
926    va_start(args, f);
927
928    VAuditF(f, args);
929    va_end(args);
930}
931
932static CARD32
933AuditFlush(OsTimerPtr timer, CARD32 now, void *arg)
934{
935    char *prefix;
936
937    if (nrepeat > 0) {
938        prefix = AuditPrefix();
939        ErrorF("%slast message repeated %d times\n",
940               prefix != NULL ? prefix : "", nrepeat);
941        nrepeat = 0;
942        free(prefix);
943        return AUDIT_TIMEOUT;
944    }
945    else {
946        /* if the timer expires without anything to print, flush the message */
947        oldlen = -1;
948        return 0;
949    }
950}
951
952void
953VAuditF(const char *f, va_list args)
954{
955    char *prefix;
956    char buf[1024];
957    int len;
958    static char oldbuf[1024];
959
960    prefix = AuditPrefix();
961    len = vsnprintf(buf, sizeof(buf), f, args);
962
963    if (len == oldlen && strcmp(buf, oldbuf) == 0) {
964        /* Message already seen */
965        nrepeat++;
966    }
967    else {
968        /* new message */
969        if (auditTimer != NULL)
970            TimerForce(auditTimer);
971        ErrorF("%s%s", prefix != NULL ? prefix : "", buf);
972        strlcpy(oldbuf, buf, sizeof(oldbuf));
973        oldlen = len;
974        nrepeat = 0;
975        auditTimer = TimerSet(auditTimer, 0, AUDIT_TIMEOUT, AuditFlush, NULL);
976    }
977    free(prefix);
978}
979
980void
981FatalError(const char *f, ...)
982{
983    va_list args;
984    va_list args2;
985    static Bool beenhere = FALSE;
986
987    if (beenhere)
988        ErrorFSigSafe("\nFatalError re-entered, aborting\n");
989    else
990        ErrorFSigSafe("\nFatal server error:\n");
991
992    va_start(args, f);
993
994    /* Make a copy for OsVendorFatalError */
995    va_copy(args2, args);
996
997#ifdef __APPLE__
998    {
999        va_list apple_args;
1000
1001        va_copy(apple_args, args);
1002        (void)vsnprintf(__crashreporter_info_buff__,
1003                        sizeof(__crashreporter_info_buff__), f, apple_args);
1004        va_end(apple_args);
1005    }
1006#endif
1007    VErrorFSigSafe(f, args);
1008    va_end(args);
1009    ErrorFSigSafe("\n");
1010    if (!beenhere)
1011        OsVendorFatalError(f, args2);
1012    va_end(args2);
1013    if (!beenhere) {
1014        beenhere = TRUE;
1015        AbortServer();
1016    }
1017    else
1018        OsAbort();
1019 /*NOTREACHED*/}
1020
1021void
1022VErrorF(const char *f, va_list args)
1023{
1024#ifdef DDXOSVERRORF
1025    if (OsVendorVErrorFProc)
1026        OsVendorVErrorFProc(f, args);
1027    else
1028        LogVWrite(-1, f, args);
1029#else
1030    LogVWrite(-1, f, args);
1031#endif
1032}
1033
1034void
1035ErrorF(const char *f, ...)
1036{
1037    va_list args;
1038
1039    va_start(args, f);
1040    VErrorF(f, args);
1041    va_end(args);
1042}
1043
1044void
1045VErrorFSigSafe(const char *f, va_list args)
1046{
1047    LogVMessageVerbSigSafe(X_ERROR, -1, f, args);
1048}
1049
1050void
1051ErrorFSigSafe(const char *f, ...)
1052{
1053    va_list args;
1054
1055    va_start(args, f);
1056    VErrorFSigSafe(f, args);
1057    va_end(args);
1058}
1059
1060void
1061LogPrintMarkers(void)
1062{
1063    /* Show what the message marker symbols mean. */
1064    LogWrite(0, "Markers: ");
1065    LogMessageVerb(X_PROBED, 0, "probed, ");
1066    LogMessageVerb(X_CONFIG, 0, "from config file, ");
1067    LogMessageVerb(X_DEFAULT, 0, "default setting,\n\t");
1068    LogMessageVerb(X_CMDLINE, 0, "from command line, ");
1069    LogMessageVerb(X_NOTICE, 0, "notice, ");
1070    LogMessageVerb(X_INFO, 0, "informational,\n\t");
1071    LogMessageVerb(X_WARNING, 0, "warning, ");
1072    LogMessageVerb(X_ERROR, 0, "error, ");
1073    LogMessageVerb(X_NOT_IMPLEMENTED, 0, "not implemented, ");
1074    LogMessageVerb(X_UNKNOWN, 0, "unknown.\n");
1075}
1076