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