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