xref: /lighttpd1.4/src/log.c (revision 3392e8fb)
1 /* _XOPEN_SOURCE >= 500 for vsnprintf() */
2 #ifndef _XOPEN_SOURCE
3 #define _XOPEN_SOURCE 700
4 #endif
5 
6 #include "first.h"
7 
8 #include "log.h"
9 
10 #include <sys/types.h>
11 #include "sys-time.h"
12 #include <errno.h>
13 #include <string.h>
14 #include <stdarg.h>
15 #include <stdio.h>      /* vsnprintf() */
16 #include <stdlib.h>     /* malloc() free() */
17 #include <unistd.h>
18 
19 #ifdef HAVE_SYSLOG_H
20 # include <syslog.h>
21 #endif
22 
23 #ifndef HAVE_CLOCK_GETTIME
24 #ifdef HAVE_SYS_TIME_H
25 # include <sys/time.h>  /* gettimeofday() */
26 #endif
27 #endif
28 
29 time_t log_epoch_secs = 0;
30 time_t log_monotonic_secs = 0;
31 
32 int log_clock_gettime_realtime (struct timespec *ts) {
33       #ifdef HAVE_CLOCK_GETTIME
34 	return clock_gettime(CLOCK_REALTIME, ts);
35       #else
36 	/* Mac OSX does not provide clock_gettime()
37 	 * e.g. defined(__APPLE__) && defined(__MACH__) */
38 	struct timeval tv;
39 	gettimeofday(&tv, NULL);
40 	ts->tv_sec  = tv.tv_sec;
41 	ts->tv_nsec = tv.tv_usec * 1000;
42 	return 0;
43       #endif
44 }
45 
46 int log_clock_gettime_monotonic (struct timespec *ts) {
47       #ifdef HAVE_CLOCK_GETTIME
48 	return clock_gettime(CLOCK_MONOTONIC, ts);
49       #else
50 	return log_clock_gettime_realtime(ts); /*(fallback)*/
51       #endif
52 }
53 
54 /* retry write on EINTR or when not all data was written */
55 ssize_t write_all(int fd, const void * const buf, size_t count) {
56     ssize_t written = 0;
57 
58     for (ssize_t wr; count > 0; count -= wr, written += wr) {
59         wr = write(fd, (const char *)buf + written, count);
60         if (wr > 0) continue;
61 
62         if (wr < 0 && errno == EINTR) { wr = 0; continue; } /* try again */
63         if (0 == wr) errno = EIO; /* really shouldn't happen... */
64         return -1; /* fail - repeating probably won't help */
65     }
66 
67     return written;
68 }
69 
70 static int log_buffer_prepare(const log_error_st *errh, const char *filename, unsigned int line, buffer *b) {
71 	static time_t tlast;
72 	static uint32_t tlen;
73 	static char tstr[24]; /* 20 "%F %T" incl '\0' +3 ": (" */
74 	switch(errh->errorlog_mode) {
75 	case ERRORLOG_PIPE:
76 	case ERRORLOG_FILE:
77 	case ERRORLOG_FD:
78 		if (-1 == errh->errorlog_fd) return -1;
79 		/* cache the generated timestamp */
80 		if (__builtin_expect( (tlast != log_epoch_secs), 0)) {
81 			struct tm tm;
82 			tlast = log_epoch_secs;
83 			tlen = (uint32_t)
84 			  strftime(tstr, sizeof(tstr), "%F %T",
85 			           localtime_r(&tlast, &tm));
86 			tstr[  tlen] = ':';
87 			tstr[++tlen] = ' ';
88 			tstr[++tlen] = '(';
89 			/*tstr[++tlen] = '\0';*//*(not necessary for our use)*/
90 		}
91 
92 		buffer_copy_string_len(b, tstr, tlen);
93 		break;
94 	case ERRORLOG_SYSLOG:
95 		/* syslog is generating its own timestamps */
96 		buffer_copy_string_len(b, CONST_STR_LEN("("));
97 		break;
98 	}
99 
100 	buffer_append_string(b, filename);
101 	buffer_append_string_len(b, CONST_STR_LEN("."));
102 	buffer_append_int(b, line);
103 	buffer_append_string_len(b, CONST_STR_LEN(") "));
104 
105 	return 0;
106 }
107 
108 static void log_write(const log_error_st *errh, buffer *b) {
109 	switch(errh->errorlog_mode) {
110 	case ERRORLOG_PIPE:
111 	case ERRORLOG_FILE:
112 	case ERRORLOG_FD:
113 		buffer_append_string_len(b, CONST_STR_LEN("\n"));
114 		write_all(errh->errorlog_fd, CONST_BUF_LEN(b));
115 		break;
116 	case ERRORLOG_SYSLOG:
117 		syslog(LOG_ERR, "%s", b->ptr);
118 		break;
119 	}
120 }
121 
122 
123 static void
124 log_buffer_append_encoded (buffer * const b,
125                            const char * const s, const size_t n)
126 {
127     size_t i;
128     for (i = 0; i < n && ' ' <= s[i] && s[i] <= '~'; ++i) ;/*(ASCII isprint())*/
129     if (i == n)
130         buffer_append_string_len(b, s, n);  /* common case; nothing to encode */
131     else
132         buffer_append_string_c_escaped(b, s, n);
133 }
134 
135 
136 __attribute_format__((__printf__, 2, 0))
137 static void
138 log_buffer_vprintf (buffer * const b,
139                     const char * const fmt, va_list ap)
140 {
141     /* NOTE: log_buffer_prepare() ensures 0 != b->used */
142     /*assert(0 != b->used);*//*(only because code calcs below assume this)*/
143     /*assert(0 != b->size);*//*(errh->b should not have 0 size here)*/
144     size_t blen = buffer_string_length(b);
145     size_t bsp  = buffer_string_space(b)+1;
146     char *s = b->ptr + blen;
147     size_t n;
148 
149     va_list aptry;
150     va_copy(aptry, ap);
151     n = (size_t)vsnprintf(s, bsp, fmt, aptry);
152     va_end(aptry);
153 
154     if (n >= bsp) {
155         s = buffer_extend(b, n);
156         vsnprintf(s, n+1, fmt, ap);
157     }
158 
159     size_t i;
160     for (i = 0; i < n && ' ' <= s[i] && s[i] <= '~'; ++i) ;/*(ASCII isprint())*/
161     if (i == n) {
162         buffer_string_set_length(b, blen + n);
163         return; /* common case; nothing to encode */
164     }
165 
166     /* need to encode log line
167      * copy original line fragment, append encoded line to buffer, free copy */
168     char * const src = (char *)malloc(n);
169     memcpy(src, s, n); /*(note: not '\0'-terminated)*/
170     buffer_append_string_c_escaped(b, src, n);
171     free(src);
172 }
173 
174 
175 __attribute_format__((__printf__, 4, 0))
176 static void
177 log_error_va_list_impl (log_error_st * const errh,
178                         const char * const filename,
179                         const unsigned int line,
180                         const char * const fmt, va_list ap,
181                         const int perr)
182 {
183     const int errnum = errno;
184     buffer * const b = &errh->b;
185     if (-1 == log_buffer_prepare(errh, filename, line, b)) return;
186     log_buffer_vprintf(b, fmt, ap);
187     if (perr) {
188         buffer_append_string_len(b, CONST_STR_LEN(": "));
189         buffer_append_string(b, strerror(errnum));
190     }
191     log_write(errh, b);
192     errno = errnum;
193 }
194 
195 
196 void
197 log_error(log_error_st * const errh,
198           const char * const filename, const unsigned int line,
199           const char *fmt, ...)
200 {
201     va_list ap;
202     va_start(ap, fmt);
203     log_error_va_list_impl(errh, filename, line, fmt, ap, 0);
204     va_end(ap);
205 }
206 
207 
208 void
209 log_perror (log_error_st * const errh,
210             const char * const filename, const unsigned int line,
211             const char * const fmt, ...)
212 {
213     va_list ap;
214     va_start(ap, fmt);
215     log_error_va_list_impl(errh, filename, line, fmt, ap, 1);
216     va_end(ap);
217 }
218 
219 
220 void
221 log_error_multiline_buffer (log_error_st * const restrict errh,
222                             const char * const restrict filename,
223                             const unsigned int line,
224                             const buffer * const restrict multiline,
225                             const char * const restrict fmt, ...)
226 {
227     if (multiline->used < 2) return;
228 
229     const int errnum = errno;
230     buffer * const b = &errh->b;
231     if (-1 == log_buffer_prepare(errh, filename, line, b)) return;
232 
233     va_list ap;
234     va_start(ap, fmt);
235     log_buffer_vprintf(b, fmt, ap);
236     va_end(ap);
237 
238     const size_t prefix_len = buffer_string_length(b);
239     const char * const end = multiline->ptr + multiline->used - 2;
240     const char *pos = multiline->ptr-1, *current_line;
241     do {
242         pos = strchr(current_line = pos+1, '\n');
243         if (!pos)
244             pos = end;
245         buffer_string_set_length(b, prefix_len); /* truncate to prefix */
246         log_buffer_append_encoded(b, current_line, pos - current_line);
247         log_write(errh, b);
248     } while (pos < end);
249 
250     errno = errnum;
251 }
252 
253 
254 log_error_st *
255 log_error_st_init (void)
256 {
257     log_error_st *errh = calloc(1, sizeof(log_error_st));
258     force_assert(errh);
259     errh->errorlog_fd = STDERR_FILENO;
260     errh->errorlog_mode = ERRORLOG_FD;
261     return errh;
262 }
263 
264 
265 void
266 log_error_st_free (log_error_st *errh)
267 {
268     if (NULL == errh) return;
269     free(errh->b.ptr);
270     free(errh);
271 }
272