1 /* _XOPEN_SOURCE >= 500 for vsnprintf() */
3 #define _XOPEN_SOURCE 700
11 #include <sys/types.h>
16 #include <stdio.h> /* vsnprintf() */
17 #include <stdlib.h> /* malloc() free() */
24 #ifndef HAVE_CLOCK_GETTIME
25 #ifdef HAVE_SYS_TIME_H
26 # include <sys/time.h> /* gettimeofday() */
30 int log_clock_gettime_realtime (struct timespec
*ts
) {
31 #ifdef HAVE_CLOCK_GETTIME
32 return clock_gettime(CLOCK_REALTIME
, ts
);
34 /* Mac OSX does not provide clock_gettime()
35 * e.g. defined(__APPLE__) && defined(__MACH__) */
37 gettimeofday(&tv
, NULL
);
38 ts
->tv_sec
= tv
.tv_sec
;
39 ts
->tv_nsec
= tv
.tv_usec
* 1000;
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
) {
49 ssize_t r
= write(fd
, buf
, count
);
56 /* fail - repeating probably won't help */
60 /* really shouldn't happen... */
64 force_assert(r
<= (ssize_t
) count
);
66 buf
= r
+ (char const*) buf
;
74 /* lowercase: append space, uppercase: don't */
75 static void log_buffer_append_printf(buffer
*out
, const char *fmt
, va_list ap
) {
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);
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
));
96 buffer_append_int(out
, d
);
100 o
= va_arg(ap
, off_t
);
101 buffer_append_int(out
, o
);
103 case 'X': /* int (hex) */
104 case 'x': /* int (hex) */
106 buffer_append_string_len(out
, CONST_STR_LEN("0x"));
107 buffer_append_uint_hex(out
, d
);
115 buffer_append_string_len(out
, fmt
, 1);
119 if (*fmt
>= 'a') { /* 's' 'b' 'd' 'o' 'x' */
120 buffer_append_string_len(out
, CONST_STR_LEN(" "));
125 static int log_buffer_prepare(const log_error_st
*errh
, const char *filename
, unsigned int line
, buffer
*b
) {
126 switch(errh
->errorlog_mode
) {
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
));
138 buffer_copy_buffer(b
, errh
->tb
);
139 buffer_append_string_len(b
, CONST_STR_LEN(": ("));
141 case ERRORLOG_SYSLOG
:
142 /* syslog is generating its own timestamps */
143 buffer_copy_string_len(b
, CONST_STR_LEN("("));
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(") "));
155 static void log_write(const log_error_st
*errh
, buffer
*b
) {
156 switch(errh
->errorlog_mode
) {
160 buffer_append_string_len(b
, CONST_STR_LEN("\n"));
161 write_all(errh
->errorlog_fd
, CONST_BUF_LEN(b
));
163 case ERRORLOG_SYSLOG
:
164 syslog(LOG_ERR
, "%s", b
->ptr
);
169 int log_error_write(server
*srv
, const char *filename
, unsigned int line
, const char *fmt
, ...) {
170 const log_error_st
*errh
= srv
->errh
;
172 if (-1 == log_buffer_prepare(errh
, filename
, line
, b
)) return 0;
176 log_buffer_append_printf(b
, fmt
, ap
);
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
;
189 char *pos
, *end
, *current_line
;
191 if (buffer_string_is_empty(multiline
)) return 0;
193 if (-1 == log_buffer_prepare(errh
, filename
, line
, b
)) return 0;
196 log_buffer_append_printf(b
, fmt
, ap
);
199 prefix_len
= buffer_string_length(b
);
201 current_line
= pos
= multiline
->ptr
;
202 end
= multiline
->ptr
+ buffer_string_length(multiline
);
204 for ( ; pos
<= end
; ++pos
) {
208 case '\0': /* handles end of string */
209 if (current_line
< pos
) {
210 /* truncate to prefix */
211 buffer_string_set_length(b
, prefix_len
);
213 buffer_append_string_len(b
, current_line
, pos
- current_line
);
216 current_line
= pos
+ 1;
228 log_buffer_vprintf (buffer
* const b
,
229 const char * const fmt
, va_list ap
)
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
;
241 n
= (size_t)vsnprintf(s
, bsp
, fmt
, aptry
);
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
);
250 for (i
= 0; i
< n
&& ' ' <= s
[i
] && s
[i
] <= '~'; ++i
) ;/*(ASCII isprint())*/
252 buffer_string_set_length(b
, blen
+ n
);
253 return; /* common case; nothing to encode */
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
);
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
,
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
);
277 buffer_append_string_len(b
, CONST_STR_LEN(": "));
278 buffer_append_string(b
, strerror(errnum
));
286 log_error(const log_error_st
* const errh
,
287 const char * const filename
, const unsigned int line
,
288 const char *fmt
, ...)
292 log_error_va_list_impl(errh
, filename
, line
, fmt
, ap
, 0);
298 log_perror (const log_error_st
* const errh
,
299 const char * const filename
, const unsigned int line
,
300 const char * const fmt
, ...)
304 log_error_va_list_impl(errh
, filename
, line
, fmt
, ap
, 1);
310 log_error_st_init (time_t *cur_ts_ptr
, time_t *last_ts_ptr
)
312 log_error_st
*errh
= calloc(1, sizeof(log_error_st
));
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
;
325 log_error_st_free (log_error_st
*errh
)
327 if (NULL
== errh
) return;
328 buffer_free(errh
->tb
);
329 buffer_free(errh
->b
);