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