xref: /lighttpd1.4/src/log.c (revision 49e9f0ac)
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 "base.h"
9 #include "log.h"
10 
11 #include <sys/types.h>
12 #include <errno.h>
13 #include <time.h>
14 #include <string.h>
15 #include <stdarg.h>
16 #include <stdio.h>      /* vsnprintf() */
17 #include <stdlib.h>     /* malloc() free() */
18 #include <unistd.h>
19 
20 #ifdef HAVE_SYSLOG_H
21 # include <syslog.h>
22 #endif
23 
24 #ifndef HAVE_CLOCK_GETTIME
25 #ifdef HAVE_SYS_TIME_H
26 # include <sys/time.h>  /* gettimeofday() */
27 #endif
28 #endif
29 
30 int log_clock_gettime_realtime (struct timespec *ts) {
31       #ifdef HAVE_CLOCK_GETTIME
32 	return clock_gettime(CLOCK_REALTIME, ts);
33       #else
34 	/* Mac OSX does not provide clock_gettime()
35 	 * e.g. defined(__APPLE__) && defined(__MACH__) */
36 	struct timeval tv;
37 	gettimeofday(&tv, NULL);
38 	ts->tv_sec  = tv.tv_sec;
39 	ts->tv_nsec = tv.tv_usec * 1000;
40 	return 0;
41       #endif
42 }
43 
44 /* retry write on EINTR or when not all data was written */
45 ssize_t write_all(int fd, const void* buf, size_t count) {
46 	ssize_t written = 0;
47 
48 	while (count > 0) {
49 		ssize_t r = write(fd, buf, count);
50 		if (r < 0) {
51 			switch (errno) {
52 			case EINTR:
53 				/* try again */
54 				break;
55 			default:
56 				/* fail - repeating probably won't help */
57 				return -1;
58 			}
59 		} else if (0 == r) {
60 			/* really shouldn't happen... */
61 			errno = EIO;
62 			return -1;
63 		} else {
64 			force_assert(r <= (ssize_t) count);
65 			written += r;
66 			buf = r + (char const*) buf;
67 			count -= r;
68 		}
69 	}
70 
71 	return written;
72 }
73 
74 /* lowercase: append space, uppercase: don't */
75 static void log_buffer_append_printf(buffer *out, const char *fmt, va_list ap) {
76 	for(; *fmt; fmt++) {
77 		int d;
78 		char *s;
79 		buffer *b;
80 		off_t o;
81 
82 		switch(*fmt) {
83 		case 'S':           /* string */
84 		case 's':           /* string */
85 			s = va_arg(ap, char *);
86 			buffer_append_string_c_escaped(out, s, (NULL != s) ? strlen(s) : 0);
87 			break;
88 		case 'B':           /* buffer */
89 		case 'b':           /* buffer */
90 			b = va_arg(ap, buffer *);
91 			buffer_append_string_c_escaped(out, CONST_BUF_LEN(b));
92 			break;
93 		case 'D':           /* int */
94 		case 'd':           /* int */
95 			d = va_arg(ap, int);
96 			buffer_append_int(out, d);
97 			break;
98 		case 'O':           /* off_t */
99 		case 'o':           /* off_t */
100 			o = va_arg(ap, off_t);
101 			buffer_append_int(out, o);
102 			break;
103 		case 'X':           /* int (hex) */
104 		case 'x':           /* int (hex) */
105 			d = va_arg(ap, int);
106 			buffer_append_string_len(out, CONST_STR_LEN("0x"));
107 			buffer_append_uint_hex(out, d);
108 			break;
109 		case '(':
110 		case ')':
111 		case '<':
112 		case '>':
113 		case ',':
114 		case ' ':
115 			buffer_append_string_len(out, fmt, 1);
116 			break;
117 		}
118 
119 		if (*fmt >= 'a') { /* 's' 'b' 'd' 'o' 'x' */
120 			buffer_append_string_len(out, CONST_STR_LEN(" "));
121 		}
122 	}
123 }
124 
125 static int log_buffer_prepare(const log_error_st *errh, const char *filename, unsigned int line, buffer *b) {
126 	switch(errh->errorlog_mode) {
127 	case ERRORLOG_PIPE:
128 	case ERRORLOG_FILE:
129 	case ERRORLOG_FD:
130 		if (-1 == errh->errorlog_fd) return -1;
131 		/* cache the generated timestamp */
132 		if (*errh->last_ts != *errh->cur_ts) {
133 			*errh->last_ts = *errh->cur_ts;
134 			buffer_clear(errh->tb);
135 			buffer_append_strftime(errh->tb, "%Y-%m-%d %H:%M:%S", localtime(errh->cur_ts));
136 		}
137 
138 		buffer_copy_buffer(b, errh->tb);
139 		buffer_append_string_len(b, CONST_STR_LEN(": ("));
140 		break;
141 	case ERRORLOG_SYSLOG:
142 		/* syslog is generating its own timestamps */
143 		buffer_copy_string_len(b, CONST_STR_LEN("("));
144 		break;
145 	}
146 
147 	buffer_append_string(b, filename);
148 	buffer_append_string_len(b, CONST_STR_LEN("."));
149 	buffer_append_int(b, line);
150 	buffer_append_string_len(b, CONST_STR_LEN(") "));
151 
152 	return 0;
153 }
154 
155 static void log_write(const log_error_st *errh, buffer *b) {
156 	switch(errh->errorlog_mode) {
157 	case ERRORLOG_PIPE:
158 	case ERRORLOG_FILE:
159 	case ERRORLOG_FD:
160 		buffer_append_string_len(b, CONST_STR_LEN("\n"));
161 		write_all(errh->errorlog_fd, CONST_BUF_LEN(b));
162 		break;
163 	case ERRORLOG_SYSLOG:
164 		syslog(LOG_ERR, "%s", b->ptr);
165 		break;
166 	}
167 }
168 
169 int log_error_write(server *srv, const char *filename, unsigned int line, const char *fmt, ...) {
170 	const log_error_st *errh = srv->errh;
171 	buffer *b = errh->b;
172 	if (-1 == log_buffer_prepare(errh, filename, line, b)) return 0;
173 
174 	va_list ap;
175 	va_start(ap, fmt);
176 	log_buffer_append_printf(b, fmt, ap);
177 	va_end(ap);
178 
179 	log_write(errh, b);
180 
181 	return 0;
182 }
183 
184 int log_error_write_multiline_buffer(server *srv, const char *filename, unsigned int line, buffer *multiline, const char *fmt, ...) {
185 	const log_error_st *errh = srv->errh;
186 	buffer *b = errh->b;
187 	va_list ap;
188 	size_t prefix_len;
189 	char *pos, *end, *current_line;
190 
191 	if (buffer_string_is_empty(multiline)) return 0;
192 
193 	if (-1 == log_buffer_prepare(errh, filename, line, b)) return 0;
194 
195 	va_start(ap, fmt);
196 	log_buffer_append_printf(b, fmt, ap);
197 	va_end(ap);
198 
199 	prefix_len = buffer_string_length(b);
200 
201 	current_line = pos = multiline->ptr;
202 	end = multiline->ptr + buffer_string_length(multiline);
203 
204 	for ( ; pos <= end ; ++pos) {
205 		switch (*pos) {
206 		case '\n':
207 		case '\r':
208 		case '\0': /* handles end of string */
209 			if (current_line < pos) {
210 				/* truncate to prefix */
211 				buffer_string_set_length(b, prefix_len);
212 
213 				buffer_append_string_len(b, current_line, pos - current_line);
214 				log_write(errh, b);
215 			}
216 			current_line = pos + 1;
217 			break;
218 		default:
219 			break;
220 		}
221 	}
222 
223 	return 0;
224 }
225 
226 
227 static void
228 log_buffer_vprintf (buffer * const b,
229                     const char * const fmt, va_list ap)
230 {
231     /* NOTE: log_buffer_prepare() ensures 0 != b->used */
232     /*assert(0 != b->used);*//*(only because code calcs below assume this)*/
233     /*assert(0 != b->size);*//*(errh->b should not have 0 size here)*/
234     size_t blen = buffer_string_length(b);
235     size_t bsp  = buffer_string_space(b)+1;
236     char *s = b->ptr + blen;
237     size_t n;
238 
239     va_list aptry;
240     va_copy(aptry, ap);
241     n = (size_t)vsnprintf(s, bsp, fmt, aptry);
242     va_end(aptry);
243 
244     if (n >= bsp) {
245         buffer_string_prepare_append(b, n); /*(must re-read s after realloc)*/
246         vsnprintf((s = b->ptr + blen), buffer_string_space(b)+1, fmt, ap);
247     }
248 
249     size_t i;
250     for (i = 0; i < n && ' ' <= s[i] && s[i] <= '~'; ++i) ;/*(ASCII isprint())*/
251     if (i == n) {
252         buffer_string_set_length(b, blen + n);
253         return; /* common case; nothing to encode */
254     }
255 
256     /* need to encode log line
257      * copy original line fragment, append encoded line to buffer, free copy */
258     char * const src = (char *)malloc(n);
259     memcpy(src, s, n); /*(note: not '\0'-terminated)*/
260     buffer_append_string_c_escaped(b, src, n);
261     free(src);
262 }
263 
264 
265 static void
266 log_error_va_list_impl (const log_error_st * const errh,
267                         const char * const filename,
268                         const unsigned int line,
269                         const char * const fmt, va_list ap,
270                         const int perr)
271 {
272     const int errnum = errno;
273     buffer * const b = errh->b;
274     if (-1 == log_buffer_prepare(errh, filename, line, b)) return;
275     log_buffer_vprintf(b, fmt, ap);
276     if (perr) {
277         buffer_append_string_len(b, CONST_STR_LEN(": "));
278         buffer_append_string(b, strerror(errnum));
279     }
280     log_write(errh, b);
281     errno = errnum;
282 }
283 
284 
285 void
286 log_error(const log_error_st * const errh,
287           const char * const filename, const unsigned int line,
288           const char *fmt, ...)
289 {
290     va_list ap;
291     va_start(ap, fmt);
292     log_error_va_list_impl(errh, filename, line, fmt, ap, 0);
293     va_end(ap);
294 }
295 
296 
297 void
298 log_perror (const log_error_st * const errh,
299             const char * const filename, const unsigned int line,
300             const char * const fmt, ...)
301 {
302     va_list ap;
303     va_start(ap, fmt);
304     log_error_va_list_impl(errh, filename, line, fmt, ap, 1);
305     va_end(ap);
306 }
307 
308 
309 log_error_st *
310 log_error_st_init (time_t *cur_ts_ptr, time_t *last_ts_ptr)
311 {
312     log_error_st *errh = calloc(1, sizeof(log_error_st));
313     force_assert(errh);
314     errh->errorlog_fd = STDERR_FILENO;
315     errh->errorlog_mode = ERRORLOG_FD;
316     errh->b = buffer_init();
317     errh->tb = buffer_init();
318     errh->cur_ts = cur_ts_ptr;
319     errh->last_ts = last_ts_ptr;
320     return errh;
321 }
322 
323 
324 void
325 log_error_st_free (log_error_st *errh)
326 {
327     if (NULL == errh) return;
328     buffer_free(errh->tb);
329     buffer_free(errh->b);
330     free(errh);
331 }
332