xref: /php-src/sapi/fpm/fpm/zlog.c (revision a19267d4)
1 	/* (c) 2004-2007 Andrei Nigmatulin */
2 
3 #include "fpm_config.h"
4 
5 #include <stdio.h>
6 #include <unistd.h>
7 #include <time.h>
8 #include <string.h>
9 #include <stdarg.h>
10 #include <sys/time.h>
11 #include <errno.h>
12 
13 #include "php_syslog.h"
14 
15 #include "zlog.h"
16 #include "fpm.h"
17 #include "zend_portability.h"
18 
19 /* buffer is used for fmt result and it should never be over 2048 */
20 #define MAX_BUF_LENGTH 2048
21 
22 /* maximal length for wrapping prefix */
23 #define MAX_WRAPPING_PREFIX_LENGTH 512
24 
25 #define EXTRA_SPACE_FOR_PREFIX 128
26 
27 static int zlog_fd = -1;
28 static bool zlog_fd_is_stderr = false;
29 static int zlog_level = ZLOG_NOTICE;
30 static int zlog_limit = ZLOG_DEFAULT_LIMIT;
31 static zlog_bool zlog_buffering = ZLOG_DEFAULT_BUFFERING;
32 static int launched = 0;
33 static void (*external_logger)(int, char *, size_t) = NULL;
34 
35 static const char *level_names[] = {
36 	[ZLOG_DEBUG]   = "DEBUG",
37 	[ZLOG_NOTICE]  = "NOTICE",
38 	[ZLOG_WARNING] = "WARNING",
39 	[ZLOG_ERROR]   = "ERROR",
40 	[ZLOG_ALERT]   = "ALERT",
41 };
42 
43 #ifdef HAVE_SYSLOG_H
44 const int syslog_priorities[] = {
45 	[ZLOG_DEBUG]   = LOG_DEBUG,
46 	[ZLOG_NOTICE]  = LOG_NOTICE,
47 	[ZLOG_WARNING] = LOG_WARNING,
48 	[ZLOG_ERROR]   = LOG_ERR,
49 	[ZLOG_ALERT]   = LOG_ALERT,
50 };
51 #endif
52 
zlog_set_external_logger(void (* logger)(int,char *,size_t))53 void zlog_set_external_logger(void (*logger)(int, char *, size_t)) /* {{{ */
54 {
55 	external_logger = logger;
56 }
57 /* }}} */
58 
zlog_get_level_name(int log_level)59 const char *zlog_get_level_name(int log_level) /* {{{ */
60 {
61 	if (log_level < 0) {
62 		log_level = zlog_level;
63 	} else if (log_level < ZLOG_DEBUG || log_level > ZLOG_ALERT) {
64 		return "unknown value";
65 	}
66 
67 	return level_names[log_level];
68 }
69 /* }}} */
70 
zlog_set_launched(void)71 void zlog_set_launched(void) /* {{{ */
72 {
73 	launched = 1;
74 }
75 /* }}} */
76 
zlog_print_time(struct timeval * tv,char * timebuf,size_t timebuf_len)77 size_t zlog_print_time(struct timeval *tv, char *timebuf, size_t timebuf_len) /* {{{ */
78 {
79 	struct tm t;
80 	size_t len;
81 
82 	len = strftime(timebuf, timebuf_len, "[%d-%b-%Y %H:%M:%S",
83 			localtime_r((const time_t *) &tv->tv_sec, &t));
84 	if (zlog_level == ZLOG_DEBUG) {
85 		len += snprintf(timebuf + len, timebuf_len - len, ".%06d", (int) tv->tv_usec);
86 	}
87 	len += snprintf(timebuf + len, timebuf_len - len, "] ");
88 	return len;
89 }
90 /* }}} */
91 
zlog_set_fd(int new_fd,zlog_bool is_stderr)92 int zlog_set_fd(int new_fd, zlog_bool is_stderr) /* {{{ */
93 {
94 	int old_fd = zlog_fd;
95 
96 	zlog_fd = new_fd;
97 	zlog_fd_is_stderr = is_stderr;
98 
99 	return old_fd;
100 }
101 /* }}} */
102 
zlog_set_level(int new_value)103 int zlog_set_level(int new_value) /* {{{ */
104 {
105 	int old_value = zlog_level;
106 
107 	if (new_value < ZLOG_DEBUG || new_value > ZLOG_ALERT) return old_value;
108 
109 	zlog_level = new_value;
110 	return old_value;
111 }
112 /* }}} */
113 
zlog_set_limit(int new_value)114 int zlog_set_limit(int new_value) /* {{{ */
115 {
116 	int old_value = zlog_limit;
117 
118 	zlog_limit = new_value;
119 	return old_value;
120 }
121 /* }}} */
122 
zlog_set_buffering(zlog_bool buffering)123 int zlog_set_buffering(zlog_bool buffering) /* {{{ */
124 {
125 	int old_value = zlog_buffering;
126 
127 	zlog_buffering = buffering;
128 	return old_value;
129 }
130 /* }}} */
131 
zlog_truncate_buf(char * buf,size_t buf_size,size_t space_left)132 static inline size_t zlog_truncate_buf(char *buf, size_t buf_size, size_t space_left) /* {{{ */
133 {
134 	memcpy(buf + buf_size - sizeof("...") + 1 - space_left, "...", sizeof("...") - 1);
135 	return buf_size - space_left;
136 }
137 /* }}} */
138 
zlog_external(int flags,char * buf,size_t buf_size,const char * fmt,va_list args)139 static inline void zlog_external(
140 		int flags, char *buf, size_t buf_size, const char *fmt, va_list args) /* {{{ */
141 {
142 	va_list ap;
143 	size_t len;
144 
145 	va_copy(ap, args);
146 	len = vsnprintf(buf, buf_size, fmt, ap);
147 	va_end(ap);
148 
149 	if (len >= buf_size) {
150 		len = zlog_truncate_buf(buf, buf_size, 0);
151 	}
152 	external_logger(flags & ZLOG_LEVEL_MASK, buf, len);
153 }
154 /* }}} */
155 
zlog_buf_prefix(const char * function,int line,int flags,char * buf,size_t buf_size,int use_syslog)156 static size_t zlog_buf_prefix(
157 		const char *function, int line, int flags,
158 		char *buf, size_t buf_size, int use_syslog) /* {{{ */
159 {
160 	struct timeval tv;
161 	size_t len = 0;
162 
163 #ifdef HAVE_SYSLOG_H
164 	if (use_syslog /* && !fpm_globals.is_child */) {
165 		if (zlog_level == ZLOG_DEBUG) {
166 			len += snprintf(buf, buf_size, "[%s] %s(), line %d: ",
167 					level_names[flags & ZLOG_LEVEL_MASK], function, line);
168 		} else {
169 			len += snprintf(buf, buf_size, "[%s] ", level_names[flags & ZLOG_LEVEL_MASK]);
170 		}
171 	} else
172 #endif
173 	{
174 		if (!fpm_globals.is_child) {
175 			gettimeofday(&tv, 0);
176 			len = zlog_print_time(&tv, buf, buf_size);
177 		}
178 		if (zlog_level == ZLOG_DEBUG) {
179 			if (!fpm_globals.is_child) {
180 				len += snprintf(buf + len, buf_size - len, "%s: pid %d, %s(), line %d: ",
181 						level_names[flags & ZLOG_LEVEL_MASK], getpid(), function, line);
182 			} else {
183 				len += snprintf(buf + len, buf_size - len, "%s: %s(), line %d: ",
184 						level_names[flags & ZLOG_LEVEL_MASK], function, line);
185 			}
186 		} else {
187 			len += snprintf(buf + len, buf_size - len, "%s: ",
188 					level_names[flags & ZLOG_LEVEL_MASK]);
189 		}
190 	}
191 
192 	return len;
193 }
194 /* }}} */
195 
vzlog(const char * function,int line,int flags,const char * fmt,va_list args)196 void vzlog(const char *function, int line, int flags, const char *fmt, va_list args) /* {{{ */
197 {
198 	char buf[MAX_BUF_LENGTH];
199 	size_t buf_size = MAX_BUF_LENGTH;
200 	size_t len = 0;
201 	int truncated = 0;
202 	int saved_errno;
203 
204 	if (external_logger) {
205 		zlog_external(flags, buf, buf_size, fmt, args);
206 	}
207 
208 	if ((flags & ZLOG_LEVEL_MASK) < zlog_level) {
209 		return;
210 	}
211 
212 	saved_errno = errno;
213 	len = zlog_buf_prefix(function, line, flags, buf, buf_size, zlog_fd == ZLOG_SYSLOG);
214 
215 	if (len > buf_size - 1) {
216 		truncated = 1;
217 	} else {
218 		len += vsnprintf(buf + len, buf_size - len, fmt, args);
219 		if (len >= buf_size) {
220 			truncated = 1;
221 		}
222 	}
223 
224 	if (!truncated) {
225 		if (flags & ZLOG_HAVE_ERRNO) {
226 			len += snprintf(buf + len, buf_size - len,
227 					": %s (%d)", strerror(saved_errno), saved_errno);
228 			if (len >= zlog_limit) {
229 				truncated = 1;
230 			}
231 		}
232 	}
233 
234 	if (truncated) {
235 		len = zlog_truncate_buf(buf, zlog_limit < buf_size ? zlog_limit : buf_size, 1);
236 	}
237 
238 #ifdef HAVE_SYSLOG_H
239 	if (zlog_fd == ZLOG_SYSLOG) {
240 		buf[len] = '\0';
241 		php_syslog(syslog_priorities[zlog_level], "%s", buf);
242 		buf[len++] = '\n';
243 	} else
244 #endif
245 	{
246 		buf[len++] = '\n';
247 		zend_quiet_write(zlog_fd > -1 ? zlog_fd : STDERR_FILENO, buf, len);
248 	}
249 
250 	if (!zlog_fd_is_stderr && zlog_fd != -1 &&
251 			!launched && (flags & ZLOG_LEVEL_MASK) >= ZLOG_NOTICE) {
252 		zend_quiet_write(STDERR_FILENO, buf, len);
253 	}
254 }
255 /* }}} */
256 
zlog_ex(const char * function,int line,int flags,const char * fmt,...)257 void zlog_ex(const char *function, int line, int flags, const char *fmt, ...) /* {{{ */
258 {
259 	va_list args;
260 	va_start(args, fmt);
261 	vzlog(function, line, flags, fmt, args);
262 	va_end(args);
263 }
264 /* }}} */
265 
266 /* predefine stream init that is used by zlog_msg_ex */
267 static inline void zlog_stream_init_internal(
268 		struct zlog_stream *stream, int flags, size_t capacity, int fd);
269 
zlog_msg_ex(const char * function,int line,int flags,const char * prefix,const char * msg)270 void zlog_msg_ex(const char *function, int line, int flags,
271 		const char *prefix, const char *msg) /* {{{ */
272 {
273 	struct zlog_stream stream;
274 	size_t prefix_len = strlen(prefix);
275 	size_t msg_len = strlen(msg);
276 
277 	zlog_stream_init_internal(&stream, flags, msg_len + prefix_len, 0);
278 	zlog_stream_prefix_ex(&stream, function, line);
279 	zlog_stream_str(&stream, prefix, prefix_len);
280 	zlog_stream_str(&stream, msg, msg_len);
281 	zlog_stream_finish(&stream);
282 	zlog_stream_destroy(&stream);
283 }
284 /* }}} */
285 
286 /* STREAM OPS */
287 
zlog_stream_buf_alloc_ex(struct zlog_stream * stream,size_t needed)288 static zlog_bool zlog_stream_buf_alloc_ex(struct zlog_stream *stream, size_t needed)  /* {{{ */
289 {
290 	char *buf;
291 	size_t size = stream->buf.size ?: stream->buf_init_size;
292 	size = MIN(zlog_limit, MAX((stream->buf.data ? (size << 1) : size), needed));
293 	buf = realloc(stream->buf.data, size);
294 
295 	if (buf == NULL) {
296 		return 0;
297 	}
298 
299 	stream->buf.data = buf;
300 	stream->buf.size = size;
301 
302 	return 1;
303 }
304 /* }}} */
305 
zlog_stream_buf_alloc(struct zlog_stream * stream)306 inline static zlog_bool zlog_stream_buf_alloc(struct zlog_stream *stream)  /* {{{ */
307 {
308 	/* if there is enough space in the buffer, we do not need to reallocate */
309 	if (stream->buf.data && stream->buf.size >= MIN(zlog_limit, stream->buf_init_size)) {
310 		return 1;
311 	}
312 	return zlog_stream_buf_alloc_ex(stream, 0);
313 }
314 /* }}} */
315 
zlog_stream_direct_write_ex(struct zlog_stream * stream,const char * buf,size_t len,const char * append,size_t append_len)316 static inline ssize_t zlog_stream_direct_write_ex(
317 		struct zlog_stream *stream, const char *buf, size_t len,
318 		const char *append, size_t append_len) /* {{{ */
319 {
320 	if (stream->use_fd) {
321 		zend_quiet_write(stream->fd, buf, len);
322 		if (append_len > 0) {
323 			zend_quiet_write(stream->fd, append, append_len);
324 		}
325 	}
326 
327 	if (stream->use_stderr) {
328 		zend_quiet_write(STDERR_FILENO, buf, len);
329 		if (append_len > 0) {
330 			zend_quiet_write(STDERR_FILENO, append, append_len);
331 		}
332 	}
333 
334 	return len;
335 }
336 /* }}} */
337 
zlog_stream_direct_write(struct zlog_stream * stream,const char * buf,size_t len)338 static ssize_t zlog_stream_direct_write(
339 		struct zlog_stream *stream, const char *buf, size_t len) /* {{{ */
340 {
341 	return zlog_stream_direct_write_ex(stream, buf, len, NULL, 0);
342 }
343 /* }}} */
344 
zlog_stream_unbuffered_write(struct zlog_stream * stream,const char * buf,size_t len)345 static inline ssize_t zlog_stream_unbuffered_write(
346 		struct zlog_stream *stream, const char *buf, size_t len) /* {{{ */
347 {
348 	const char *append = NULL;
349 	size_t append_len = 0, required_len, reserved_len;
350 	ssize_t written;
351 
352 	if (stream->len == 0) {
353 		stream->len = zlog_stream_prefix_ex(stream, stream->function, stream->line);
354 	}
355 
356 	/* msg_suffix_len and msg_quote are used only for wrapping */
357 	reserved_len = stream->len + stream->msg_suffix_len + stream->msg_quote;
358 	required_len = reserved_len + len;
359 	if (required_len >= zlog_limit) {
360 		if (stream->wrap) {
361 			size_t available_len;
362 			if (required_len == zlog_limit) {
363 				append = NULL;
364 				append_len = 0;
365 			} else {
366 				append = "\n";
367 				append_len = 1;
368 			}
369 			available_len = zlog_limit - reserved_len - 1;
370 			zlog_stream_direct_write(stream, buf, available_len);
371 			if (append != NULL) {
372 				if (stream->msg_quote) {
373 					zlog_stream_direct_write(stream, "\"", 1);
374 				}
375 				if (stream->msg_suffix) {
376 					zlog_stream_direct_write(stream, stream->msg_suffix, stream->msg_suffix_len);
377 				}
378 				zlog_stream_direct_write(stream, append, append_len);
379 			}
380 			stream->len = 0;
381 			written = zlog_stream_unbuffered_write(
382 					stream, buf + available_len, len - available_len);
383 			if (written > 0) {
384 				return available_len + written;
385 			}
386 
387 			return written;
388 		}
389 		/* this would be used in case of an option for disabling wrapping in direct write */
390 		stream->full = 1;
391 		if (required_len == zlog_limit) {
392 			append = NULL;
393 		} else {
394 			append = "...";
395 			append_len = sizeof("...") - 1;
396 			len = zlog_limit - stream->len - append_len;
397 		}
398 	}
399 
400 	written = zlog_stream_direct_write_ex(stream, buf, len, append, append_len);
401 	if (written > 0) {
402 		/* currently written will be always len as the write is blocking
403 		 * - this should be address if we change to non-blocking write */
404 		stream->len += written;
405 	}
406 
407 	return written;
408 }
409 /* }}} */
410 
zlog_stream_buf_copy_cstr(struct zlog_stream * stream,const char * str,size_t str_len)411 static inline ssize_t zlog_stream_buf_copy_cstr(
412 		struct zlog_stream *stream, const char *str, size_t str_len) /* {{{ */
413 {
414 	if (stream->buf.size - stream->len <= str_len &&
415 			!zlog_stream_buf_alloc_ex(stream, str_len + stream->len)) {
416 		return -1;
417 	}
418 
419 	memcpy(stream->buf.data + stream->len, str, str_len);
420 	stream->len += str_len;
421 
422 	return str_len;
423 }
424 /* }}} */
425 
zlog_stream_buf_copy_char(struct zlog_stream * stream,char c)426 static inline ssize_t zlog_stream_buf_copy_char(struct zlog_stream *stream, char c) /* {{{ */
427 {
428 	if (stream->buf.size - stream->len < 1 && !zlog_stream_buf_alloc_ex(stream, 1)) {
429 		return -1;
430 	}
431 
432 	stream->buf.data[stream->len++] = c;
433 
434 	return 1;
435 }
436 /* }}} */
437 
zlog_stream_buf_flush(struct zlog_stream * stream)438 static ssize_t zlog_stream_buf_flush(struct zlog_stream *stream) /* {{{ */
439 {
440 	ssize_t written;
441 
442 #ifdef HAVE_SYSLOG_H
443 	if (stream->use_syslog) {
444 		zlog_stream_buf_copy_char(stream, '\0');
445 		php_syslog(syslog_priorities[zlog_level], "%s", stream->buf.data);
446 		--stream->len;
447 	}
448 #endif
449 
450 	if (external_logger != NULL) {
451 		external_logger(stream->flags & ZLOG_LEVEL_MASK,
452 				stream->buf.data + stream->prefix_len, stream->len - stream->prefix_len);
453 	}
454 	zlog_stream_buf_copy_char(stream, '\n');
455 	written = zlog_stream_direct_write(stream, stream->buf.data, stream->len);
456 	stream->len = 0;
457 
458 	return written;
459 }
460 /* }}} */
461 
zlog_stream_buf_append(struct zlog_stream * stream,const char * str,size_t str_len)462 static ssize_t zlog_stream_buf_append(
463 		struct zlog_stream *stream, const char *str, size_t str_len)  /* {{{ */
464 {
465 	int over_limit = 0;
466 	size_t available_len, required_len, reserved_len;
467 
468 	if (stream->len == 0) {
469 		stream->len = zlog_stream_prefix_ex(stream, stream->function, stream->line);
470 	}
471 
472 	/* msg_suffix_len and msg_quote are used only for wrapping */
473 	reserved_len = stream->len + stream->msg_suffix_len + stream->msg_quote;
474 	required_len = reserved_len + str_len;
475 	if (required_len >= zlog_limit) {
476 		over_limit = 1;
477 		available_len = zlog_limit - reserved_len - 1;
478 	} else {
479 		available_len = str_len;
480 	}
481 
482 	if (zlog_stream_buf_copy_cstr(stream, str, available_len) < 0) {
483 		return -1;
484 	}
485 
486 	if (!over_limit) {
487 		return available_len;
488 	}
489 
490 	if (stream->wrap) {
491 		if (stream->msg_quote) {
492 			zlog_stream_buf_copy_char(stream, '"');
493 		}
494 		if (stream->msg_suffix != NULL) {
495 			zlog_stream_buf_copy_cstr(stream, stream->msg_suffix, stream->msg_suffix_len);
496 		}
497 		zlog_stream_buf_flush(stream);
498 		zlog_stream_prefix_ex(stream, stream->function, stream->line);
499 		return available_len + zlog_stream_buf_append(
500 				stream, str + available_len, str_len - available_len);
501 	}
502 
503 	stream->len = zlog_truncate_buf(stream->buf.data, stream->len, 0);
504 	stream->full = 1;
505 	return available_len;
506 }
507 /* }}} */
508 
zlog_stream_init_internal(struct zlog_stream * stream,int flags,size_t capacity,int fd)509 static inline void zlog_stream_init_internal(
510 		struct zlog_stream *stream, int flags, size_t capacity, int fd) /* {{{ */
511 {
512 	if (fd == 0) {
513 		fd = zlog_fd;
514 	}
515 
516 	memset(stream, 0, sizeof(struct zlog_stream));
517 	stream->flags = flags;
518 	stream->use_syslog = fd == ZLOG_SYSLOG;
519 	stream->use_fd = fd > 0;
520 	stream->use_buffer = zlog_buffering || external_logger != NULL || stream->use_syslog;
521 	stream->buf_init_size = capacity;
522 	stream->use_stderr = fd < 0 ||
523 			(
524 				fd != STDERR_FILENO && fd != STDOUT_FILENO && !launched &&
525 				(flags & ZLOG_LEVEL_MASK) >= ZLOG_NOTICE
526 			);
527 	stream->prefix_buffer = (flags & ZLOG_LEVEL_MASK) >= zlog_level &&
528 			(stream->use_fd || stream->use_stderr || stream->use_syslog);
529 	stream->fd = fd > -1 ? fd : STDERR_FILENO;
530 }
531 /* }}} */
532 
zlog_stream_init(struct zlog_stream * stream,int flags)533 void zlog_stream_init(struct zlog_stream *stream, int flags) /* {{{ */
534 {
535 	zlog_stream_init_internal(stream, flags, 1024, 0);
536 }
537 /* }}} */
538 
zlog_stream_init_ex(struct zlog_stream * stream,int flags,int fd)539 void zlog_stream_init_ex(struct zlog_stream *stream, int flags, int fd) /* {{{ */
540 {
541 	zlog_stream_init_internal(stream, flags, 1024, fd);
542 	stream->wrap = 1;
543 }
544 /* }}} */
545 
zlog_stream_set_decorating(struct zlog_stream * stream,zlog_bool decorate)546 void zlog_stream_set_decorating(struct zlog_stream *stream, zlog_bool decorate) /* {{{ */
547 {
548 	if (decorate) {
549 		stream->decorate = 1;
550 	} else {
551 		stream->decorate = 0;
552 		stream->msg_quote = 0;
553 		stream->prefix_buffer = 0;
554 	}
555 }
556 /* }}} */
557 
zlog_stream_set_wrapping(struct zlog_stream * stream,zlog_bool wrap)558 void zlog_stream_set_wrapping(struct zlog_stream *stream, zlog_bool wrap) /* {{{ */
559 {
560 	stream->wrap = wrap ? 1 : 0;
561 }
562 /* }}} */
563 
zlog_stream_set_is_stdout(struct zlog_stream * stream,zlog_bool is_stdout)564 void zlog_stream_set_is_stdout(struct zlog_stream *stream, zlog_bool is_stdout) /* {{{ */
565 {
566 	stream->is_stdout = is_stdout ? 1 : 0;
567 }
568 /* }}} */
569 
zlog_stream_set_child_pid(struct zlog_stream * stream,int child_pid)570 void zlog_stream_set_child_pid(struct zlog_stream *stream, int child_pid) /* {{{ */
571 {
572 	stream->child_pid = child_pid;
573 }
574 /* }}} */
575 
zlog_stream_set_msg_quoting(struct zlog_stream * stream,zlog_bool quote)576 void zlog_stream_set_msg_quoting(struct zlog_stream *stream, zlog_bool quote) /* {{{ */
577 {
578 	stream->msg_quote = quote && stream->decorate ? 1 : 0;
579 }
580 /* }}} */
581 
zlog_stream_set_msg_prefix(struct zlog_stream * stream,const char * fmt,...)582 zlog_bool zlog_stream_set_msg_prefix(struct zlog_stream *stream, const char *fmt, ...) /* {{{ */
583 {
584 	char buf[MAX_WRAPPING_PREFIX_LENGTH];
585 	size_t len;
586 	va_list args;
587 
588 	if (!stream->decorate) {
589 		return ZLOG_TRUE;
590 	}
591 
592 	va_start(args, fmt);
593 	len = vsnprintf(buf, MAX_WRAPPING_PREFIX_LENGTH - 1, fmt, args);
594 	va_end(args);
595 
596 	if (stream->msg_prefix_len < len) {
597 		stream->msg_prefix = stream->msg_prefix_len ? realloc(stream->msg_prefix, len + 1) : malloc(len + 1);
598 		if (stream->msg_prefix == NULL) {
599 			return ZLOG_FALSE;
600 		}
601 	}
602 	memcpy(stream->msg_prefix, buf, len);
603 	stream->msg_prefix[len] = 0;
604 	stream->msg_prefix_len = len;
605 
606 	return len;
607 }
608 /* }}} */
609 
zlog_stream_set_msg_suffix(struct zlog_stream * stream,const char * suffix,const char * final_suffix)610 zlog_bool zlog_stream_set_msg_suffix(
611 		struct zlog_stream *stream, const char *suffix, const char *final_suffix)  /* {{{ */
612 {
613 	size_t len;
614 	if (!stream->wrap || !stream->decorate) {
615 		return ZLOG_TRUE;
616 	}
617 
618 	if (suffix != NULL && final_suffix != NULL) {
619 		stream->msg_suffix_len = strlen(suffix);
620 		stream->msg_final_suffix_len = strlen(final_suffix);
621 		len = stream->msg_suffix_len + stream->msg_final_suffix_len + 2;
622 		if (stream->msg_suffix != NULL) {
623 			free(stream->msg_suffix);
624 		}
625 		stream->msg_suffix = malloc(len);
626 		if (stream->msg_suffix == NULL) {
627 			return ZLOG_FALSE;
628 		}
629 		stream->msg_final_suffix = stream->msg_suffix + stream->msg_suffix_len + 1;
630 		memcpy(stream->msg_suffix, suffix, stream->msg_suffix_len + 1);
631 		memcpy(stream->msg_final_suffix, final_suffix, stream->msg_final_suffix_len + 1);
632 		return ZLOG_TRUE;
633 	}
634 	if (suffix != NULL) {
635 		stream->msg_suffix_len = strlen(suffix);
636 		len = stream->msg_suffix_len + 1;
637 		if (stream->msg_suffix != NULL) {
638 			free(stream->msg_suffix);
639 		}
640 		stream->msg_suffix = malloc(len);
641 		if (stream->msg_suffix == NULL) {
642 			return ZLOG_FALSE;
643 		}
644 		memcpy(stream->msg_suffix, suffix, len);
645 		return ZLOG_TRUE;
646 	}
647 	if (final_suffix != NULL) {
648 		stream->msg_final_suffix_len = strlen(final_suffix);
649 		len = stream->msg_final_suffix_len + 1;
650 		if (stream->msg_final_suffix != NULL) {
651 			free(stream->msg_final_suffix);
652 		}
653 		stream->msg_final_suffix = malloc(len);
654 		if (stream->msg_final_suffix == NULL) {
655 			return ZLOG_FALSE;
656 		}
657 		memcpy(stream->msg_final_suffix, final_suffix, len);
658 		return ZLOG_TRUE;
659 	}
660 
661 	return ZLOG_TRUE;
662 }
663 /* }}} */
664 
zlog_stream_prefix_ex(struct zlog_stream * stream,const char * function,int line)665 ssize_t zlog_stream_prefix_ex(struct zlog_stream *stream, const char *function, int line) /* {{{ */
666 {
667 	size_t len;
668 
669 	if (!stream->prefix_buffer) {
670 		return 0;
671 	}
672 	if (stream->wrap && stream->function == NULL) {
673 		stream->function = function;
674 		stream->line = line;
675 	}
676 
677 	if (stream->use_buffer) {
678 		if (!zlog_stream_buf_alloc(stream)) {
679 			return -1;
680 		}
681 		len = zlog_buf_prefix(
682 				function, line, stream->flags,
683 				stream->buf.data, stream->buf.size, stream->use_syslog);
684 		stream->len = stream->prefix_len = len;
685 		if (stream->msg_prefix != NULL) {
686 			zlog_stream_buf_copy_cstr(stream, stream->msg_prefix, stream->msg_prefix_len);
687 		}
688 		if (stream->msg_quote) {
689 			zlog_stream_buf_copy_char(stream, '"');
690 		}
691 		return stream->len;
692 	} else {
693 		char sbuf[1024];
694 		ssize_t written;
695 		len = zlog_buf_prefix(function, line, stream->flags, sbuf, 1024, stream->use_syslog);
696 		written = zlog_stream_direct_write(stream, sbuf, len);
697 		if (stream->msg_prefix != NULL) {
698 			written += zlog_stream_direct_write(
699 					stream, stream->msg_prefix, stream->msg_prefix_len);
700 		}
701 		if (stream->msg_quote) {
702 			written += zlog_stream_direct_write(stream, "\"", 1);
703 		}
704 		return written;
705 	}
706 }
707 /* }}} */
708 
zlog_stream_vformat(struct zlog_stream * stream,const char * fmt,va_list args)709 ssize_t zlog_stream_vformat(struct zlog_stream *stream, const char *fmt, va_list args) /* {{{ */
710 {
711 	char sbuf[1024];
712 	size_t len;
713 
714 	len = vsnprintf(sbuf, 1024, fmt, args);
715 
716 	return zlog_stream_str(stream, sbuf, len);
717 }
718 /* }}} */
719 
zlog_stream_format(struct zlog_stream * stream,const char * fmt,...)720 ssize_t zlog_stream_format(struct zlog_stream *stream, const char *fmt, ...) /* {{{ */
721 {
722 	ssize_t len;
723 
724 	va_list args;
725 	va_start(args, fmt);
726 	len = zlog_stream_vformat(stream, fmt, args);
727 	va_end(args);
728 
729 	return len;
730 }
731 /* }}} */
732 
zlog_stream_str(struct zlog_stream * stream,const char * str,size_t str_len)733 ssize_t zlog_stream_str(struct zlog_stream *stream, const char *str, size_t str_len) /* {{{ */
734 {
735 	/* do not write anything if the stream is full or str is empty */
736 	if (str_len == 0 || stream->full) {
737 		return 0;
738 	}
739 
740 	/* reset stream if it is finished */
741 	if (stream->finished) {
742 		stream->finished = 0;
743 		stream->len = 0;
744 		stream->full = 0;
745 	}
746 
747 	if (stream->use_buffer) {
748 		return zlog_stream_buf_append(stream, str, str_len);
749 	}
750 
751 	return zlog_stream_unbuffered_write(stream, str, str_len);
752 }
753 /* }}} */
754 
zlog_stream_finish_buffer_suffix(struct zlog_stream * stream)755 static inline void zlog_stream_finish_buffer_suffix(struct zlog_stream *stream) /* {{{ */
756 {
757 	if (stream->msg_quote) {
758 		zlog_stream_buf_copy_char(stream, '"');
759 	}
760 	if (stream->msg_suffix != NULL) {
761 		zlog_stream_buf_copy_cstr(stream, stream->msg_suffix, stream->msg_suffix_len);
762 	}
763 	if (stream->msg_final_suffix != NULL) {
764 		if (stream->len + stream->msg_final_suffix_len >= zlog_limit) {
765 			zlog_bool quoting = stream->msg_quote;
766 			size_t final_suffix_wrap = stream->len + stream->msg_final_suffix_len + 1 - zlog_limit;
767 			zlog_stream_buf_copy_cstr(
768 					stream, stream->msg_final_suffix,
769 					stream->msg_final_suffix_len - final_suffix_wrap);
770 			zlog_stream_buf_copy_char(stream, '\n');
771 			zlog_stream_buf_flush(stream);
772 			stream->msg_quote = 0;
773 			zlog_stream_prefix_ex(stream, stream->function, stream->line);
774 			stream->msg_quote = quoting;
775 			zlog_stream_buf_copy_cstr(
776 					stream,
777 					stream->msg_final_suffix + (stream->msg_final_suffix_len - final_suffix_wrap),
778 					final_suffix_wrap);
779 			zlog_stream_buf_copy_char(stream, '\n');
780 		} else {
781 			zlog_stream_buf_copy_cstr(
782 					stream, stream->msg_final_suffix, stream->msg_final_suffix_len);
783 		}
784 	}
785 }
786 /* }}} */
787 
zlog_stream_finish_direct_suffix(struct zlog_stream * stream)788 static inline void zlog_stream_finish_direct_suffix(struct zlog_stream *stream) /* {{{ */
789 {
790 	if (stream->msg_quote) {
791 		zlog_stream_direct_write(stream, "\"", 1);
792 		++stream->len;
793 	}
794 	if (stream->msg_suffix != NULL) {
795 		/* we should always have space for wrap suffix so we don't have to check it */
796 		zlog_stream_direct_write(stream, stream->msg_suffix, stream->msg_suffix_len);
797 		stream->len += stream->msg_suffix_len;
798 	}
799 	if (stream->msg_final_suffix != NULL) {
800 		if (stream->len + stream->msg_final_suffix_len >= zlog_limit) {
801 			zlog_bool quoting = stream->msg_quote;
802 			size_t final_suffix_wrap = stream->len + stream->msg_final_suffix_len + 1 - zlog_limit;
803 			zlog_stream_direct_write_ex(
804 					stream, stream->msg_final_suffix,
805 					stream->msg_final_suffix_len - final_suffix_wrap, "\n", 1);
806 			stream->msg_quote = 0;
807 			zlog_stream_prefix_ex(stream, stream->function, stream->line);
808 			stream->msg_quote = quoting;
809 			zlog_stream_direct_write_ex(
810 					stream,
811 					stream->msg_final_suffix + (stream->msg_final_suffix_len - final_suffix_wrap),
812 					final_suffix_wrap, "\n", 1);
813 		} else {
814 			zlog_stream_direct_write_ex(
815 					stream, stream->msg_final_suffix, stream->msg_final_suffix_len, "\n", 1);
816 		}
817 	} else {
818 		zlog_stream_direct_write(stream, "\n", 1);
819 	}
820 }
821 /* }}} */
822 
zlog_stream_finish(struct zlog_stream * stream)823 zlog_bool zlog_stream_finish(struct zlog_stream *stream) /* {{{ */
824 {
825 	if (stream->finished || stream->len == 0) {
826 		return ZLOG_TRUE;
827 	}
828 
829 	if (stream->use_buffer) {
830 		if (stream->decorate) {
831 			zlog_stream_finish_buffer_suffix(stream);
832 		}
833 		zlog_stream_buf_flush(stream);
834 	} else {
835 		if (stream->decorate) {
836 			zlog_stream_finish_direct_suffix(stream);
837 		} else {
838 			zlog_stream_direct_write(stream, "\n", 1);
839 		}
840 	}
841 	stream->finished = 1;
842 
843 	return ZLOG_TRUE;
844 }
845 /* }}} */
846 
zlog_stream_destroy(struct zlog_stream * stream)847 void zlog_stream_destroy(struct zlog_stream *stream) /* {{{ */
848 {
849 	if (stream->buf.data != NULL) {
850 		free(stream->buf.data);
851 	}
852 	if (stream->msg_prefix != NULL) {
853 		free(stream->msg_prefix);
854 	}
855 	if (stream->msg_suffix != NULL) {
856 		free(stream->msg_suffix);
857 	} else if (stream->msg_final_suffix != NULL) {
858 		free(stream->msg_final_suffix);
859 	}
860 }
861 /* }}} */
862 
zlog_stream_close(struct zlog_stream * stream)863 zlog_bool zlog_stream_close(struct zlog_stream *stream) /* {{{ */
864 {
865 	zlog_bool finished = zlog_stream_finish(stream);
866 	zlog_stream_destroy(stream);
867 
868 	return finished;
869 }
870 /* }}} */
871