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