xref: /PHP-7.4/sapi/fpm/fpm/zlog.c (revision 0bc6a66a)
1d77d6153SAntony Dovgal 	/* (c) 2004-2007 Andrei Nigmatulin */
2d77d6153SAntony Dovgal 
3d77d6153SAntony Dovgal #include "fpm_config.h"
4d77d6153SAntony Dovgal 
5d77d6153SAntony Dovgal #include <stdio.h>
6d77d6153SAntony Dovgal #include <unistd.h>
7d77d6153SAntony Dovgal #include <time.h>
8d77d6153SAntony Dovgal #include <string.h>
9d77d6153SAntony Dovgal #include <stdarg.h>
10d77d6153SAntony Dovgal #include <sys/time.h>
11d77d6153SAntony Dovgal #include <errno.h>
12d77d6153SAntony Dovgal 
135842a414SJérôme Loyet #include "php_syslog.h"
145842a414SJérôme Loyet 
15d77d6153SAntony Dovgal #include "zlog.h"
16e63f5919SJérôme Loyet #include "fpm.h"
17bc30206bSBob Weinand #include "zend_portability.h"
18d77d6153SAntony Dovgal 
193e5afbf0SJakub Zelenka /* buffer is used for fmt result and it should never be over 2048 */
203e5afbf0SJakub Zelenka #define MAX_BUF_LENGTH 2048
213e5afbf0SJakub Zelenka 
223e5afbf0SJakub Zelenka /* maximal length for wrapping prefix */
233e5afbf0SJakub Zelenka #define MAX_WRAPPING_PREFIX_LENGTH 512
243e5afbf0SJakub Zelenka 
253e5afbf0SJakub Zelenka #define EXTRA_SPACE_FOR_PREFIX 128
26d77d6153SAntony Dovgal 
27d77d6153SAntony Dovgal static int zlog_fd = -1;
28d77d6153SAntony Dovgal static int zlog_level = ZLOG_NOTICE;
293e5afbf0SJakub Zelenka static int zlog_limit = ZLOG_DEFAULT_LIMIT;
303e5afbf0SJakub Zelenka static zlog_bool zlog_buffering = ZLOG_DEFAULT_BUFFERING;
31b72460a4SJérôme Loyet static int launched = 0;
32dce25909SJerome Loyet static void (*external_logger)(int, char *, size_t) = NULL;
33d77d6153SAntony Dovgal 
34d77d6153SAntony Dovgal static const char *level_names[] = {
355842a414SJérôme Loyet 	[ZLOG_DEBUG]   = "DEBUG",
365842a414SJérôme Loyet 	[ZLOG_NOTICE]  = "NOTICE",
375842a414SJérôme Loyet 	[ZLOG_WARNING] = "WARNING",
385842a414SJérôme Loyet 	[ZLOG_ERROR]   = "ERROR",
395842a414SJérôme Loyet 	[ZLOG_ALERT]   = "ALERT",
405842a414SJérôme Loyet };
415842a414SJérôme Loyet 
425842a414SJérôme Loyet #ifdef HAVE_SYSLOG_H
435842a414SJérôme Loyet const int syslog_priorities[] = {
445842a414SJérôme Loyet 	[ZLOG_DEBUG]   = LOG_DEBUG,
455842a414SJérôme Loyet 	[ZLOG_NOTICE]  = LOG_NOTICE,
465842a414SJérôme Loyet 	[ZLOG_WARNING] = LOG_WARNING,
475842a414SJérôme Loyet 	[ZLOG_ERROR]   = LOG_ERR,
485842a414SJérôme Loyet 	[ZLOG_ALERT]   = LOG_ALERT,
49d77d6153SAntony Dovgal };
505842a414SJérôme Loyet #endif
51d77d6153SAntony Dovgal 
zlog_set_external_logger(void (* logger)(int,char *,size_t))52dce25909SJerome Loyet void zlog_set_external_logger(void (*logger)(int, char *, size_t)) /* {{{ */
53dce25909SJerome Loyet {
54dce25909SJerome Loyet 	external_logger = logger;
55dce25909SJerome Loyet }
56dce25909SJerome Loyet /* }}} */
57dce25909SJerome Loyet 
zlog_get_level_name(int log_level)5881579a35SJérôme Loyet const char *zlog_get_level_name(int log_level) /* {{{ */
5969ba9c82SJérôme Loyet {
6081579a35SJérôme Loyet 	if (log_level < 0) {
6181579a35SJérôme Loyet 		log_level = zlog_level;
6281579a35SJérôme Loyet 	} else if (log_level < ZLOG_DEBUG || log_level > ZLOG_ALERT) {
6381579a35SJérôme Loyet 		return "unknown value";
6481579a35SJérôme Loyet 	}
6581579a35SJérôme Loyet 
6681579a35SJérôme Loyet 	return level_names[log_level];
6769ba9c82SJérôme Loyet }
6869ba9c82SJérôme Loyet /* }}} */
6969ba9c82SJérôme Loyet 
zlog_set_launched(void)703e5afbf0SJakub Zelenka void zlog_set_launched(void) /* {{{ */
713e5afbf0SJakub Zelenka {
72b72460a4SJérôme Loyet 	launched = 1;
73b72460a4SJérôme Loyet }
743e5afbf0SJakub Zelenka /* }}} */
75b72460a4SJérôme Loyet 
zlog_print_time(struct timeval * tv,char * timebuf,size_t timebuf_len)76d77d6153SAntony Dovgal size_t zlog_print_time(struct timeval *tv, char *timebuf, size_t timebuf_len) /* {{{ */
77d77d6153SAntony Dovgal {
78d77d6153SAntony Dovgal 	struct tm t;
79d77d6153SAntony Dovgal 	size_t len;
80d77d6153SAntony Dovgal 
813e5afbf0SJakub Zelenka 	len = strftime(timebuf, timebuf_len, "[%d-%b-%Y %H:%M:%S",
823e5afbf0SJakub Zelenka 			localtime_r((const time_t *) &tv->tv_sec, &t));
83ca3126f1SJérôme Loyet 	if (zlog_level == ZLOG_DEBUG) {
84ca3126f1SJérôme Loyet 		len += snprintf(timebuf + len, timebuf_len - len, ".%06d", (int) tv->tv_usec);
85ca3126f1SJérôme Loyet 	}
86e63f5919SJérôme Loyet 	len += snprintf(timebuf + len, timebuf_len - len, "] ");
87d77d6153SAntony Dovgal 	return len;
88d77d6153SAntony Dovgal }
89d77d6153SAntony Dovgal /* }}} */
90d77d6153SAntony Dovgal 
zlog_set_fd(int new_fd)91d77d6153SAntony Dovgal int zlog_set_fd(int new_fd) /* {{{ */
92d77d6153SAntony Dovgal {
93d77d6153SAntony Dovgal 	int old_fd = zlog_fd;
94d77d6153SAntony Dovgal 
95d77d6153SAntony Dovgal 	zlog_fd = new_fd;
96d77d6153SAntony Dovgal 	return old_fd;
97d77d6153SAntony Dovgal }
98d77d6153SAntony Dovgal /* }}} */
99d77d6153SAntony Dovgal 
zlog_set_level(int new_value)100d77d6153SAntony Dovgal int zlog_set_level(int new_value) /* {{{ */
101d77d6153SAntony Dovgal {
102d77d6153SAntony Dovgal 	int old_value = zlog_level;
103d77d6153SAntony Dovgal 
104257a8dacSJérôme Loyet 	if (new_value < ZLOG_DEBUG || new_value > ZLOG_ALERT) return old_value;
105257a8dacSJérôme Loyet 
106d77d6153SAntony Dovgal 	zlog_level = new_value;
107d77d6153SAntony Dovgal 	return old_value;
108d77d6153SAntony Dovgal }
109d77d6153SAntony Dovgal /* }}} */
110d77d6153SAntony Dovgal 
zlog_set_limit(int new_value)1113e5afbf0SJakub Zelenka int zlog_set_limit(int new_value) /* {{{ */
112d77d6153SAntony Dovgal {
1133e5afbf0SJakub Zelenka 	int old_value = zlog_limit;
114d77d6153SAntony Dovgal 
1153e5afbf0SJakub Zelenka 	zlog_limit = new_value;
1163e5afbf0SJakub Zelenka 	return old_value;
1173e5afbf0SJakub Zelenka }
1183e5afbf0SJakub Zelenka /* }}} */
1193e5afbf0SJakub Zelenka 
zlog_set_buffering(zlog_bool buffering)1203e5afbf0SJakub Zelenka int zlog_set_buffering(zlog_bool buffering) /* {{{ */
1213e5afbf0SJakub Zelenka {
1223e5afbf0SJakub Zelenka 	int old_value = zlog_buffering;
1233e5afbf0SJakub Zelenka 
1243e5afbf0SJakub Zelenka 	zlog_buffering = buffering;
1253e5afbf0SJakub Zelenka 	return old_value;
1263e5afbf0SJakub Zelenka }
1273e5afbf0SJakub Zelenka /* }}} */
1283e5afbf0SJakub Zelenka 
zlog_truncate_buf(char * buf,size_t buf_size,size_t space_left)1293e5afbf0SJakub Zelenka static inline size_t zlog_truncate_buf(char *buf, size_t buf_size, size_t space_left) /* {{{ */
1303e5afbf0SJakub Zelenka {
1313e5afbf0SJakub Zelenka 	memcpy(buf + buf_size - sizeof("...") + 1 - space_left, "...", sizeof("...") - 1);
1323e5afbf0SJakub Zelenka 	return buf_size - space_left;
1333e5afbf0SJakub Zelenka }
1343e5afbf0SJakub Zelenka /* }}} */
1353e5afbf0SJakub Zelenka 
zlog_external(int flags,char * buf,size_t buf_size,const char * fmt,va_list args)1363e5afbf0SJakub Zelenka static inline void zlog_external(
1373e5afbf0SJakub Zelenka 		int flags, char *buf, size_t buf_size, const char *fmt, va_list args) /* {{{ */
1383e5afbf0SJakub Zelenka {
1393e5afbf0SJakub Zelenka 	va_list ap;
1403e5afbf0SJakub Zelenka 	size_t len;
1413e5afbf0SJakub Zelenka 
1423e5afbf0SJakub Zelenka 	va_copy(ap, args);
1433e5afbf0SJakub Zelenka 	len = vsnprintf(buf, buf_size, fmt, ap);
1443e5afbf0SJakub Zelenka 	va_end(ap);
1453e5afbf0SJakub Zelenka 
1463e5afbf0SJakub Zelenka 	if (len >= buf_size) {
1473e5afbf0SJakub Zelenka 		len = zlog_truncate_buf(buf, buf_size, 0);
148d77d6153SAntony Dovgal 	}
1493e5afbf0SJakub Zelenka 	external_logger(flags & ZLOG_LEVEL_MASK, buf, len);
1503e5afbf0SJakub Zelenka }
1513e5afbf0SJakub Zelenka /* }}} */
1523e5afbf0SJakub Zelenka 
zlog_buf_prefix(const char * function,int line,int flags,char * buf,size_t buf_size,int use_syslog)1533e5afbf0SJakub Zelenka static size_t zlog_buf_prefix(
1543e5afbf0SJakub Zelenka 		const char *function, int line, int flags,
1553e5afbf0SJakub Zelenka 		char *buf, size_t buf_size, int use_syslog) /* {{{ */
1563e5afbf0SJakub Zelenka {
1573e5afbf0SJakub Zelenka 	struct timeval tv;
1583e5afbf0SJakub Zelenka 	size_t len = 0;
159d77d6153SAntony Dovgal 
1605842a414SJérôme Loyet #ifdef HAVE_SYSLOG_H
1613e5afbf0SJakub Zelenka 	if (use_syslog /* && !fpm_globals.is_child */) {
1625842a414SJérôme Loyet 		if (zlog_level == ZLOG_DEBUG) {
1633e5afbf0SJakub Zelenka 			len += snprintf(buf, buf_size, "[%s] %s(), line %d: ",
1643e5afbf0SJakub Zelenka 					level_names[flags & ZLOG_LEVEL_MASK], function, line);
1655842a414SJérôme Loyet 		} else {
1665842a414SJérôme Loyet 			len += snprintf(buf, buf_size, "[%s] ", level_names[flags & ZLOG_LEVEL_MASK]);
1675842a414SJérôme Loyet 		}
1685842a414SJérôme Loyet 	} else
1695842a414SJérôme Loyet #endif
1705842a414SJérôme Loyet 	{
1710f19777bSJérôme Loyet 		if (!fpm_globals.is_child) {
1725842a414SJérôme Loyet 			gettimeofday(&tv, 0);
1735842a414SJérôme Loyet 			len = zlog_print_time(&tv, buf, buf_size);
1745842a414SJérôme Loyet 		}
1755842a414SJérôme Loyet 		if (zlog_level == ZLOG_DEBUG) {
1765842a414SJérôme Loyet 			if (!fpm_globals.is_child) {
1773e5afbf0SJakub Zelenka 				len += snprintf(buf + len, buf_size - len, "%s: pid %d, %s(), line %d: ",
1783e5afbf0SJakub Zelenka 						level_names[flags & ZLOG_LEVEL_MASK], getpid(), function, line);
1795842a414SJérôme Loyet 			} else {
1803e5afbf0SJakub Zelenka 				len += snprintf(buf + len, buf_size - len, "%s: %s(), line %d: ",
1813e5afbf0SJakub Zelenka 						level_names[flags & ZLOG_LEVEL_MASK], function, line);
1825842a414SJérôme Loyet 			}
1830f19777bSJérôme Loyet 		} else {
1843e5afbf0SJakub Zelenka 			len += snprintf(buf + len, buf_size - len, "%s: ",
1853e5afbf0SJakub Zelenka 					level_names[flags & ZLOG_LEVEL_MASK]);
1860f19777bSJérôme Loyet 		}
187d77d6153SAntony Dovgal 	}
188d77d6153SAntony Dovgal 
1893e5afbf0SJakub Zelenka 	return len;
1903e5afbf0SJakub Zelenka }
1913e5afbf0SJakub Zelenka /* }}} */
1923e5afbf0SJakub Zelenka 
vzlog(const char * function,int line,int flags,const char * fmt,va_list args)1933e5afbf0SJakub Zelenka void vzlog(const char *function, int line, int flags, const char *fmt, va_list args) /* {{{ */
1943e5afbf0SJakub Zelenka {
1953e5afbf0SJakub Zelenka 	char buf[MAX_BUF_LENGTH];
1963e5afbf0SJakub Zelenka 	size_t buf_size = MAX_BUF_LENGTH;
1973e5afbf0SJakub Zelenka 	size_t len = 0;
1983e5afbf0SJakub Zelenka 	int truncated = 0;
1993e5afbf0SJakub Zelenka 	int saved_errno;
2003e5afbf0SJakub Zelenka 
2013e5afbf0SJakub Zelenka 	if (external_logger) {
2023e5afbf0SJakub Zelenka 		zlog_external(flags, buf, buf_size, fmt, args);
203d77d6153SAntony Dovgal 	}
204d77d6153SAntony Dovgal 
2053e5afbf0SJakub Zelenka 	if ((flags & ZLOG_LEVEL_MASK) < zlog_level) {
2063e5afbf0SJakub Zelenka 		return;
2073e5afbf0SJakub Zelenka 	}
2083e5afbf0SJakub Zelenka 
2093e5afbf0SJakub Zelenka 	saved_errno = errno;
2103e5afbf0SJakub Zelenka 	len = zlog_buf_prefix(function, line, flags, buf, buf_size, zlog_fd == ZLOG_SYSLOG);
2113e5afbf0SJakub Zelenka 
2123e5afbf0SJakub Zelenka 	if (len > buf_size - 1) {
2133e5afbf0SJakub Zelenka 		truncated = 1;
2143e5afbf0SJakub Zelenka 	} else {
215d77d6153SAntony Dovgal 		len += vsnprintf(buf + len, buf_size - len, fmt, args);
216d77d6153SAntony Dovgal 		if (len >= buf_size) {
217d77d6153SAntony Dovgal 			truncated = 1;
218d77d6153SAntony Dovgal 		}
219d77d6153SAntony Dovgal 	}
220d77d6153SAntony Dovgal 
221d77d6153SAntony Dovgal 	if (!truncated) {
222d77d6153SAntony Dovgal 		if (flags & ZLOG_HAVE_ERRNO) {
2233e5afbf0SJakub Zelenka 			len += snprintf(buf + len, buf_size - len,
2243e5afbf0SJakub Zelenka 					": %s (%d)", strerror(saved_errno), saved_errno);
2253e5afbf0SJakub Zelenka 			if (len >= zlog_limit) {
226d77d6153SAntony Dovgal 				truncated = 1;
227d77d6153SAntony Dovgal 			}
228d77d6153SAntony Dovgal 		}
229d77d6153SAntony Dovgal 	}
230d77d6153SAntony Dovgal 
231d77d6153SAntony Dovgal 	if (truncated) {
2323e5afbf0SJakub Zelenka 		len = zlog_truncate_buf(buf, zlog_limit < buf_size ? zlog_limit : buf_size, 1);
233d77d6153SAntony Dovgal 	}
234d77d6153SAntony Dovgal 
2355842a414SJérôme Loyet #ifdef HAVE_SYSLOG_H
2365842a414SJérôme Loyet 	if (zlog_fd == ZLOG_SYSLOG) {
2375842a414SJérôme Loyet 		buf[len] = '\0';
2385842a414SJérôme Loyet 		php_syslog(syslog_priorities[zlog_level], "%s", buf);
2395842a414SJérôme Loyet 		buf[len++] = '\n';
240b7a7b1a6SStanislav Malyshev 	} else
2415842a414SJérôme Loyet #endif
2425842a414SJérôme Loyet 	{
2435842a414SJérôme Loyet 		buf[len++] = '\n';
244bc30206bSBob Weinand 		zend_quiet_write(zlog_fd > -1 ? zlog_fd : STDERR_FILENO, buf, len);
2455842a414SJérôme Loyet 	}
2465842a414SJérôme Loyet 
2473e5afbf0SJakub Zelenka 	if (zlog_fd != STDERR_FILENO && zlog_fd != -1 &&
2483e5afbf0SJakub Zelenka 			!launched && (flags & ZLOG_LEVEL_MASK) >= ZLOG_NOTICE) {
249bc30206bSBob Weinand 		zend_quiet_write(STDERR_FILENO, buf, len);
250b72460a4SJérôme Loyet 	}
251d77d6153SAntony Dovgal }
252d77d6153SAntony Dovgal /* }}} */
253d77d6153SAntony Dovgal 
zlog_ex(const char * function,int line,int flags,const char * fmt,...)2543e5afbf0SJakub Zelenka void zlog_ex(const char *function, int line, int flags, const char *fmt, ...) /* {{{ */
2553e5afbf0SJakub Zelenka {
25618cf4e0aSXinchen Hui 	va_list args;
25718cf4e0aSXinchen Hui 	va_start(args, fmt);
25818cf4e0aSXinchen Hui 	vzlog(function, line, flags, fmt, args);
25918cf4e0aSXinchen Hui 	va_end(args);
26018cf4e0aSXinchen Hui }
26118cf4e0aSXinchen Hui /* }}} */
2623e5afbf0SJakub Zelenka 
2633e5afbf0SJakub Zelenka /* predefine stream init that is used by zlog_msg_ex */
2643e5afbf0SJakub Zelenka static inline void zlog_stream_init_internal(
2653e5afbf0SJakub Zelenka 		struct zlog_stream *stream, int flags, size_t capacity, int fd);
2663e5afbf0SJakub Zelenka 
zlog_msg_ex(const char * function,int line,int flags,const char * prefix,const char * msg)2673e5afbf0SJakub Zelenka void zlog_msg_ex(const char *function, int line, int flags,
2683e5afbf0SJakub Zelenka 		const char *prefix, const char *msg) /* {{{ */
2693e5afbf0SJakub Zelenka {
2703e5afbf0SJakub Zelenka 	struct zlog_stream stream;
2713e5afbf0SJakub Zelenka 	size_t prefix_len = strlen(prefix);
2723e5afbf0SJakub Zelenka 	size_t msg_len = strlen(msg);
2733e5afbf0SJakub Zelenka 
2743e5afbf0SJakub Zelenka 	zlog_stream_init_internal(&stream, flags, msg_len + prefix_len, 0);
2753e5afbf0SJakub Zelenka 	zlog_stream_prefix_ex(&stream, function, line);
2763e5afbf0SJakub Zelenka 	zlog_stream_str(&stream, prefix, prefix_len);
2773e5afbf0SJakub Zelenka 	zlog_stream_str(&stream, msg, msg_len);
2783e5afbf0SJakub Zelenka 	zlog_stream_finish(&stream);
2793e5afbf0SJakub Zelenka 	zlog_stream_destroy(&stream);
2803e5afbf0SJakub Zelenka }
2813e5afbf0SJakub Zelenka /* }}} */
2823e5afbf0SJakub Zelenka 
2833e5afbf0SJakub Zelenka /* STREAM OPS */
2843e5afbf0SJakub Zelenka 
zlog_stream_buf_alloc_ex(struct zlog_stream * stream,size_t needed)2853e5afbf0SJakub Zelenka static zlog_bool zlog_stream_buf_alloc_ex(struct zlog_stream *stream, size_t needed)  /* {{{ */
2863e5afbf0SJakub Zelenka {
2873e5afbf0SJakub Zelenka 	char *buf;
2883e5afbf0SJakub Zelenka 	size_t size = stream->buf.size ?: stream->buf_init_size;
2893e5afbf0SJakub Zelenka 
2903e5afbf0SJakub Zelenka 	if (stream->buf.data) {
2913e5afbf0SJakub Zelenka 		size = MIN(zlog_limit, MAX(size * 2, needed));
2923e5afbf0SJakub Zelenka 		buf = realloc(stream->buf.data, size);
2933e5afbf0SJakub Zelenka 	} else {
2943e5afbf0SJakub Zelenka 		size = MIN(zlog_limit, MAX(size, needed));
295014b1b3aSJakub Zelenka 		buf = malloc(size);
2963e5afbf0SJakub Zelenka 	}
2973e5afbf0SJakub Zelenka 
2983e5afbf0SJakub Zelenka 	if (buf == NULL) {
2993e5afbf0SJakub Zelenka 		return 0;
3003e5afbf0SJakub Zelenka 	}
3013e5afbf0SJakub Zelenka 
3023e5afbf0SJakub Zelenka 	stream->buf.data = buf;
3033e5afbf0SJakub Zelenka 	stream->buf.size = size;
3043e5afbf0SJakub Zelenka 
3053e5afbf0SJakub Zelenka 	return 1;
3063e5afbf0SJakub Zelenka }
3073e5afbf0SJakub Zelenka /* }}} */
3083e5afbf0SJakub Zelenka 
zlog_stream_buf_alloc(struct zlog_stream * stream)3093e5afbf0SJakub Zelenka inline static zlog_bool zlog_stream_buf_alloc(struct zlog_stream *stream)  /* {{{ */
3103e5afbf0SJakub Zelenka {
3113e5afbf0SJakub Zelenka 	/* if there is enough space in the buffer, we do not need to reallocate */
3123e5afbf0SJakub Zelenka 	if (stream->buf.data && stream->buf.size >= MIN(zlog_limit, stream->buf_init_size)) {
3133e5afbf0SJakub Zelenka 		return 1;
3143e5afbf0SJakub Zelenka 	}
3153e5afbf0SJakub Zelenka 	return zlog_stream_buf_alloc_ex(stream, 0);
3163e5afbf0SJakub Zelenka }
3173e5afbf0SJakub Zelenka /* }}} */
3183e5afbf0SJakub Zelenka 
zlog_stream_direct_write_ex(struct zlog_stream * stream,const char * buf,size_t len,const char * append,size_t append_len)3193e5afbf0SJakub Zelenka static inline ssize_t zlog_stream_direct_write_ex(
3203e5afbf0SJakub Zelenka 		struct zlog_stream *stream, const char *buf, size_t len,
3213e5afbf0SJakub Zelenka 		const char *append, size_t append_len) /* {{{ */
3223e5afbf0SJakub Zelenka {
3233e5afbf0SJakub Zelenka 	if (stream->use_fd) {
3243e5afbf0SJakub Zelenka 		zend_quiet_write(stream->fd, buf, len);
3253e5afbf0SJakub Zelenka 		if (append_len > 0) {
3263e5afbf0SJakub Zelenka 			zend_quiet_write(stream->fd, append, append_len);
3273e5afbf0SJakub Zelenka 		}
3283e5afbf0SJakub Zelenka 	}
3293e5afbf0SJakub Zelenka 
3303e5afbf0SJakub Zelenka 	if (stream->use_stderr) {
3313e5afbf0SJakub Zelenka 		zend_quiet_write(STDERR_FILENO, buf, len);
3323e5afbf0SJakub Zelenka 		if (append_len > 0) {
3333e5afbf0SJakub Zelenka 			zend_quiet_write(STDERR_FILENO, append, append_len);
3343e5afbf0SJakub Zelenka 		}
3353e5afbf0SJakub Zelenka 	}
3363e5afbf0SJakub Zelenka 
3373e5afbf0SJakub Zelenka 	return len;
3383e5afbf0SJakub Zelenka }
3393e5afbf0SJakub Zelenka /* }}} */
3403e5afbf0SJakub Zelenka 
zlog_stream_direct_write(struct zlog_stream * stream,const char * buf,size_t len)3413e5afbf0SJakub Zelenka static ssize_t zlog_stream_direct_write(
3423e5afbf0SJakub Zelenka 		struct zlog_stream *stream, const char *buf, size_t len) /* {{{ */
3433e5afbf0SJakub Zelenka {
3443e5afbf0SJakub Zelenka 	return zlog_stream_direct_write_ex(stream, buf, len, NULL, 0);
3453e5afbf0SJakub Zelenka }
3463e5afbf0SJakub Zelenka /* }}} */
3473e5afbf0SJakub Zelenka 
zlog_stream_unbuffered_write(struct zlog_stream * stream,const char * buf,size_t len)3483e5afbf0SJakub Zelenka static inline ssize_t zlog_stream_unbuffered_write(
3493e5afbf0SJakub Zelenka 		struct zlog_stream *stream, const char *buf, size_t len) /* {{{ */
3503e5afbf0SJakub Zelenka {
3513e5afbf0SJakub Zelenka 	const char *append;
3523e5afbf0SJakub Zelenka 	size_t append_len = 0, required_len, reserved_len;
3533e5afbf0SJakub Zelenka 	ssize_t written;
3543e5afbf0SJakub Zelenka 
3553e5afbf0SJakub Zelenka 	if (stream->len == 0) {
3563e5afbf0SJakub Zelenka 		stream->len = zlog_stream_prefix_ex(stream, stream->function, stream->line);
3573e5afbf0SJakub Zelenka 	}
3583e5afbf0SJakub Zelenka 
3593e5afbf0SJakub Zelenka 	/* msg_suffix_len and msg_quote are used only for wrapping */
3603e5afbf0SJakub Zelenka 	reserved_len = stream->len + stream->msg_suffix_len + stream->msg_quote;
3613e5afbf0SJakub Zelenka 	required_len = reserved_len + len;
3623e5afbf0SJakub Zelenka 	if (required_len >= zlog_limit) {
3633e5afbf0SJakub Zelenka 		if (stream->wrap) {
3643e5afbf0SJakub Zelenka 			size_t available_len;
3653e5afbf0SJakub Zelenka 			if (required_len == zlog_limit) {
3663e5afbf0SJakub Zelenka 				append = NULL;
3673e5afbf0SJakub Zelenka 				append_len = 0;
3683e5afbf0SJakub Zelenka 			} else {
3693e5afbf0SJakub Zelenka 				append = "\n";
3703e5afbf0SJakub Zelenka 				append_len = 1;
3713e5afbf0SJakub Zelenka 			}
3723e5afbf0SJakub Zelenka 			available_len = zlog_limit - reserved_len - 1;
3733e5afbf0SJakub Zelenka 			zlog_stream_direct_write(stream, buf, available_len);
3743e5afbf0SJakub Zelenka 			if (append != NULL) {
3753e5afbf0SJakub Zelenka 				if (stream->msg_quote) {
3763e5afbf0SJakub Zelenka 					zlog_stream_direct_write(stream, "\"", 1);
3773e5afbf0SJakub Zelenka 				}
3783e5afbf0SJakub Zelenka 				if (stream->msg_suffix) {
3793e5afbf0SJakub Zelenka 					zlog_stream_direct_write(stream, stream->msg_suffix, stream->msg_suffix_len);
3803e5afbf0SJakub Zelenka 				}
3813e5afbf0SJakub Zelenka 				zlog_stream_direct_write(stream, append, append_len);
3823e5afbf0SJakub Zelenka 			}
3833e5afbf0SJakub Zelenka 			stream->len = 0;
3843e5afbf0SJakub Zelenka 			written = zlog_stream_unbuffered_write(
3853e5afbf0SJakub Zelenka 					stream, buf + available_len, len - available_len);
3863e5afbf0SJakub Zelenka 			if (written > 0) {
3873e5afbf0SJakub Zelenka 				return available_len + written;
3883e5afbf0SJakub Zelenka 			}
3893e5afbf0SJakub Zelenka 
3903e5afbf0SJakub Zelenka 			return written;
3913e5afbf0SJakub Zelenka 		}
3923e5afbf0SJakub Zelenka 		/* this would be used in case of an option for disabling wrapping in direct write */
3933e5afbf0SJakub Zelenka 		stream->full = 1;
3943e5afbf0SJakub Zelenka 		if (required_len == zlog_limit) {
3953e5afbf0SJakub Zelenka 			append = NULL;
3963e5afbf0SJakub Zelenka 		} else {
3973e5afbf0SJakub Zelenka 			append = "...";
3986e2c54d3SNikita Popov 			append_len = sizeof("...") - 1;
3993e5afbf0SJakub Zelenka 			len = zlog_limit - stream->len - append_len;
4003e5afbf0SJakub Zelenka 		}
4013e5afbf0SJakub Zelenka 	}
4023e5afbf0SJakub Zelenka 
4033e5afbf0SJakub Zelenka 	written = zlog_stream_direct_write_ex(stream, buf, len, append, append_len);
4043e5afbf0SJakub Zelenka 	if (written > 0) {
4053e5afbf0SJakub Zelenka 		/* currently written will be always len as the write is blocking
4063e5afbf0SJakub Zelenka 		 * - this should be address if we change to non-blocking write */
4073e5afbf0SJakub Zelenka 		stream->len += written;
4083e5afbf0SJakub Zelenka 	}
4093e5afbf0SJakub Zelenka 
4103e5afbf0SJakub Zelenka 	return written;
4113e5afbf0SJakub Zelenka }
4123e5afbf0SJakub Zelenka /* }}} */
4133e5afbf0SJakub Zelenka 
zlog_stream_buf_copy_cstr(struct zlog_stream * stream,const char * str,size_t str_len)4143e5afbf0SJakub Zelenka static inline ssize_t zlog_stream_buf_copy_cstr(
4153e5afbf0SJakub Zelenka 		struct zlog_stream *stream, const char *str, size_t str_len) /* {{{ */
4163e5afbf0SJakub Zelenka {
4173e5afbf0SJakub Zelenka 	if (stream->buf.size - stream->len <= str_len && !zlog_stream_buf_alloc_ex(stream, str_len)) {
4183e5afbf0SJakub Zelenka 		return -1;
4193e5afbf0SJakub Zelenka 	}
4203e5afbf0SJakub Zelenka 
4213e5afbf0SJakub Zelenka 	memcpy(stream->buf.data + stream->len, str, str_len);
4223e5afbf0SJakub Zelenka 	stream->len += str_len;
4233e5afbf0SJakub Zelenka 
4243e5afbf0SJakub Zelenka 	return str_len;
4253e5afbf0SJakub Zelenka }
4263e5afbf0SJakub Zelenka /* }}} */
4273e5afbf0SJakub Zelenka 
zlog_stream_buf_copy_char(struct zlog_stream * stream,char c)4283e5afbf0SJakub Zelenka static inline ssize_t zlog_stream_buf_copy_char(struct zlog_stream *stream, char c) /* {{{ */
4293e5afbf0SJakub Zelenka {
4303e5afbf0SJakub Zelenka 	if (stream->buf.size - stream->len < 1 && !zlog_stream_buf_alloc_ex(stream, 1)) {
4313e5afbf0SJakub Zelenka 		return -1;
4323e5afbf0SJakub Zelenka 	}
4333e5afbf0SJakub Zelenka 
4343e5afbf0SJakub Zelenka 	stream->buf.data[stream->len++] = c;
4353e5afbf0SJakub Zelenka 
4363e5afbf0SJakub Zelenka 	return 1;
4373e5afbf0SJakub Zelenka }
4383e5afbf0SJakub Zelenka /* }}} */
4393e5afbf0SJakub Zelenka 
zlog_stream_buf_flush(struct zlog_stream * stream)4403e5afbf0SJakub Zelenka static ssize_t zlog_stream_buf_flush(struct zlog_stream *stream) /* {{{ */
4413e5afbf0SJakub Zelenka {
4423e5afbf0SJakub Zelenka 	ssize_t written;
4433e5afbf0SJakub Zelenka 
4443e5afbf0SJakub Zelenka #ifdef HAVE_SYSLOG_H
4453e5afbf0SJakub Zelenka 	if (stream->use_syslog) {
4463e5afbf0SJakub Zelenka 		zlog_stream_buf_copy_char(stream, '\0');
4473e5afbf0SJakub Zelenka 		php_syslog(syslog_priorities[zlog_level], "%s", stream->buf.data);
4483e5afbf0SJakub Zelenka 		--stream->len;
4493e5afbf0SJakub Zelenka 	}
4503e5afbf0SJakub Zelenka #endif
4513e5afbf0SJakub Zelenka 
4523e5afbf0SJakub Zelenka 	if (external_logger != NULL) {
4533e5afbf0SJakub Zelenka 		external_logger(stream->flags & ZLOG_LEVEL_MASK,
4543e5afbf0SJakub Zelenka 				stream->buf.data + stream->prefix_len, stream->len - stream->prefix_len);
4553e5afbf0SJakub Zelenka 	}
4563e5afbf0SJakub Zelenka 	zlog_stream_buf_copy_char(stream, '\n');
4573e5afbf0SJakub Zelenka 	written = zlog_stream_direct_write(stream, stream->buf.data, stream->len);
4583e5afbf0SJakub Zelenka 	stream->len = 0;
4593e5afbf0SJakub Zelenka 
4603e5afbf0SJakub Zelenka 	return written;
4613e5afbf0SJakub Zelenka }
4623e5afbf0SJakub Zelenka /* }}} */
4633e5afbf0SJakub Zelenka 
zlog_stream_buf_append(struct zlog_stream * stream,const char * str,size_t str_len)4643e5afbf0SJakub Zelenka static ssize_t zlog_stream_buf_append(
4653e5afbf0SJakub Zelenka 		struct zlog_stream *stream, const char *str, size_t str_len)  /* {{{ */
4663e5afbf0SJakub Zelenka {
4673e5afbf0SJakub Zelenka 	int over_limit = 0;
4683e5afbf0SJakub Zelenka 	size_t available_len, required_len, reserved_len;
4693e5afbf0SJakub Zelenka 
4703e5afbf0SJakub Zelenka 	if (stream->len == 0) {
4713e5afbf0SJakub Zelenka 		stream->len = zlog_stream_prefix_ex(stream, stream->function, stream->line);
4723e5afbf0SJakub Zelenka 	}
4733e5afbf0SJakub Zelenka 
4743e5afbf0SJakub Zelenka 	/* msg_suffix_len and msg_quote are used only for wrapping */
4753e5afbf0SJakub Zelenka 	reserved_len = stream->len + stream->msg_suffix_len + stream->msg_quote;
4763e5afbf0SJakub Zelenka 	required_len = reserved_len + str_len;
4773e5afbf0SJakub Zelenka 	if (required_len >= zlog_limit) {
4783e5afbf0SJakub Zelenka 		over_limit = 1;
4793e5afbf0SJakub Zelenka 		available_len = zlog_limit - reserved_len - 1;
4803e5afbf0SJakub Zelenka 	} else {
4813e5afbf0SJakub Zelenka 		available_len = str_len;
4823e5afbf0SJakub Zelenka 	}
4833e5afbf0SJakub Zelenka 
4843e5afbf0SJakub Zelenka 	if (zlog_stream_buf_copy_cstr(stream, str, available_len) < 0) {
4853e5afbf0SJakub Zelenka 		return -1;
4863e5afbf0SJakub Zelenka 	}
4873e5afbf0SJakub Zelenka 
4883e5afbf0SJakub Zelenka 	if (!over_limit) {
4893e5afbf0SJakub Zelenka 		return available_len;
4903e5afbf0SJakub Zelenka 	}
4913e5afbf0SJakub Zelenka 
4923e5afbf0SJakub Zelenka 	if (stream->wrap) {
4933e5afbf0SJakub Zelenka 		if (stream->msg_quote) {
4943e5afbf0SJakub Zelenka 			zlog_stream_buf_copy_char(stream, '"');
4953e5afbf0SJakub Zelenka 		}
4963e5afbf0SJakub Zelenka 		if (stream->msg_suffix != NULL) {
4973e5afbf0SJakub Zelenka 			zlog_stream_buf_copy_cstr(stream, stream->msg_suffix, stream->msg_suffix_len);
4983e5afbf0SJakub Zelenka 		}
4993e5afbf0SJakub Zelenka 		zlog_stream_buf_flush(stream);
5003e5afbf0SJakub Zelenka 		zlog_stream_prefix_ex(stream, stream->function, stream->line);
5013e5afbf0SJakub Zelenka 		return available_len + zlog_stream_buf_append(
5023e5afbf0SJakub Zelenka 				stream, str + available_len, str_len - available_len);
5033e5afbf0SJakub Zelenka 	}
5043e5afbf0SJakub Zelenka 
5053e5afbf0SJakub Zelenka 	stream->len = zlog_truncate_buf(stream->buf.data, stream->len, 0);
5063e5afbf0SJakub Zelenka 	stream->full = 1;
5073e5afbf0SJakub Zelenka 	return available_len;
5083e5afbf0SJakub Zelenka }
5093e5afbf0SJakub Zelenka /* }}} */
5103e5afbf0SJakub Zelenka 
zlog_stream_init_internal(struct zlog_stream * stream,int flags,size_t capacity,int fd)5113e5afbf0SJakub Zelenka static inline void zlog_stream_init_internal(
5123e5afbf0SJakub Zelenka 		struct zlog_stream *stream, int flags, size_t capacity, int fd) /* {{{ */
5133e5afbf0SJakub Zelenka {
5143e5afbf0SJakub Zelenka 	if (fd == 0) {
5153e5afbf0SJakub Zelenka 		fd = zlog_fd;
5163e5afbf0SJakub Zelenka 	}
5173e5afbf0SJakub Zelenka 
5183e5afbf0SJakub Zelenka 	memset(stream, 0, sizeof(struct zlog_stream));
5193e5afbf0SJakub Zelenka 	stream->flags = flags;
5203e5afbf0SJakub Zelenka 	stream->use_syslog = fd == ZLOG_SYSLOG;
5213e5afbf0SJakub Zelenka 	stream->use_fd = fd > 0;
5223e5afbf0SJakub Zelenka 	stream->use_buffer = zlog_buffering || external_logger != NULL || stream->use_syslog;
5233e5afbf0SJakub Zelenka 	stream->buf_init_size = capacity;
5243e5afbf0SJakub Zelenka 	stream->use_stderr = fd < 0 ||
5253e5afbf0SJakub Zelenka 			(
5263e5afbf0SJakub Zelenka 				fd != STDERR_FILENO && fd != STDOUT_FILENO && !launched &&
5273e5afbf0SJakub Zelenka 				(flags & ZLOG_LEVEL_MASK) >= ZLOG_NOTICE
5283e5afbf0SJakub Zelenka 			);
5293e5afbf0SJakub Zelenka 	stream->prefix_buffer = (flags & ZLOG_LEVEL_MASK) >= zlog_level &&
5303e5afbf0SJakub Zelenka 			(stream->use_fd || stream->use_stderr || stream->use_syslog);
5313e5afbf0SJakub Zelenka 	stream->fd = fd > -1 ? fd : STDERR_FILENO;
5323e5afbf0SJakub Zelenka }
5333e5afbf0SJakub Zelenka /* }}} */
5343e5afbf0SJakub Zelenka 
zlog_stream_init(struct zlog_stream * stream,int flags)5353e5afbf0SJakub Zelenka void zlog_stream_init(struct zlog_stream *stream, int flags) /* {{{ */
5363e5afbf0SJakub Zelenka {
5373e5afbf0SJakub Zelenka 	zlog_stream_init_internal(stream, flags, 1024, 0);
5383e5afbf0SJakub Zelenka }
5393e5afbf0SJakub Zelenka /* }}} */
5403e5afbf0SJakub Zelenka 
zlog_stream_init_ex(struct zlog_stream * stream,int flags,int fd)5413e5afbf0SJakub Zelenka void zlog_stream_init_ex(struct zlog_stream *stream, int flags, int fd) /* {{{ */
5423e5afbf0SJakub Zelenka {
5433e5afbf0SJakub Zelenka 	zlog_stream_init_internal(stream, flags, 1024, fd);
5443e5afbf0SJakub Zelenka 	stream->wrap = 1;
5453e5afbf0SJakub Zelenka }
5463e5afbf0SJakub Zelenka /* }}} */
5473e5afbf0SJakub Zelenka 
zlog_stream_set_decorating(struct zlog_stream * stream,zlog_bool decorate)5483e5afbf0SJakub Zelenka void zlog_stream_set_decorating(struct zlog_stream *stream, zlog_bool decorate) /* {{{ */
5493e5afbf0SJakub Zelenka {
5503e5afbf0SJakub Zelenka 	if (decorate) {
5513e5afbf0SJakub Zelenka 		stream->decorate = 1;
5523e5afbf0SJakub Zelenka 	} else {
5533e5afbf0SJakub Zelenka 		stream->decorate = 0;
5543e5afbf0SJakub Zelenka 		stream->msg_quote = 0;
5553e5afbf0SJakub Zelenka 		stream->prefix_buffer = 0;
5563e5afbf0SJakub Zelenka 	}
5573e5afbf0SJakub Zelenka }
5583e5afbf0SJakub Zelenka /* }}} */
5593e5afbf0SJakub Zelenka 
zlog_stream_set_wrapping(struct zlog_stream * stream,zlog_bool wrap)5603e5afbf0SJakub Zelenka void zlog_stream_set_wrapping(struct zlog_stream *stream, zlog_bool wrap) /* {{{ */
5613e5afbf0SJakub Zelenka {
5623e5afbf0SJakub Zelenka 	stream->wrap = wrap ? 1 : 0;
5633e5afbf0SJakub Zelenka }
5643e5afbf0SJakub Zelenka /* }}} */
5653e5afbf0SJakub Zelenka 
zlog_stream_set_is_stdout(struct zlog_stream * stream,zlog_bool is_stdout)566ffcf57faSTsuyoshi Sadakata void zlog_stream_set_is_stdout(struct zlog_stream *stream, zlog_bool is_stdout) /* {{{ */
567ffcf57faSTsuyoshi Sadakata {
568ffcf57faSTsuyoshi Sadakata 	stream->is_stdout = is_stdout ? 1 : 0;
569ffcf57faSTsuyoshi Sadakata }
570ffcf57faSTsuyoshi Sadakata /* }}} */
571ffcf57faSTsuyoshi Sadakata 
zlog_stream_set_child_pid(struct zlog_stream * stream,int child_pid)572ffcf57faSTsuyoshi Sadakata void zlog_stream_set_child_pid(struct zlog_stream *stream, int child_pid) /* {{{ */
573ffcf57faSTsuyoshi Sadakata {
574ffcf57faSTsuyoshi Sadakata 	stream->child_pid = child_pid;
575ffcf57faSTsuyoshi Sadakata }
576ffcf57faSTsuyoshi Sadakata /* }}} */
577ffcf57faSTsuyoshi Sadakata 
zlog_stream_set_msg_quoting(struct zlog_stream * stream,zlog_bool quote)5783e5afbf0SJakub Zelenka void zlog_stream_set_msg_quoting(struct zlog_stream *stream, zlog_bool quote) /* {{{ */
5793e5afbf0SJakub Zelenka {
5803e5afbf0SJakub Zelenka 	stream->msg_quote = quote && stream->decorate ? 1 : 0;
5813e5afbf0SJakub Zelenka }
5823e5afbf0SJakub Zelenka /* }}} */
5833e5afbf0SJakub Zelenka 
zlog_stream_set_msg_prefix(struct zlog_stream * stream,const char * fmt,...)58426cd84d1SJakub Zelenka zlog_bool zlog_stream_set_msg_prefix(struct zlog_stream *stream, const char *fmt, ...) /* {{{ */
5853e5afbf0SJakub Zelenka {
5863e5afbf0SJakub Zelenka 	char buf[MAX_WRAPPING_PREFIX_LENGTH];
5873e5afbf0SJakub Zelenka 	size_t len;
5883e5afbf0SJakub Zelenka 	va_list args;
5893e5afbf0SJakub Zelenka 
5903e5afbf0SJakub Zelenka 	if (!stream->decorate) {
59126cd84d1SJakub Zelenka 		return ZLOG_TRUE;
5923e5afbf0SJakub Zelenka 	}
5933e5afbf0SJakub Zelenka 
5943e5afbf0SJakub Zelenka 	va_start(args, fmt);
5953e5afbf0SJakub Zelenka 	len = vsnprintf(buf, MAX_WRAPPING_PREFIX_LENGTH - 1, fmt, args);
5963e5afbf0SJakub Zelenka 	va_end(args);
5973e5afbf0SJakub Zelenka 
598ffcf57faSTsuyoshi Sadakata 	if (stream->msg_prefix_len < len) {
599ffcf57faSTsuyoshi Sadakata 		stream->msg_prefix = stream->msg_prefix_len ? realloc(stream->msg_prefix, len + 1) : malloc(len + 1);
600ffcf57faSTsuyoshi Sadakata 		if (stream->msg_prefix == NULL) {
601ffcf57faSTsuyoshi Sadakata 			return ZLOG_FALSE;
602ffcf57faSTsuyoshi Sadakata 		}
6033e5afbf0SJakub Zelenka 	}
6043e5afbf0SJakub Zelenka 	memcpy(stream->msg_prefix, buf, len);
6053e5afbf0SJakub Zelenka 	stream->msg_prefix[len] = 0;
6063e5afbf0SJakub Zelenka 	stream->msg_prefix_len = len;
6073e5afbf0SJakub Zelenka 
6083e5afbf0SJakub Zelenka 	return len;
6093e5afbf0SJakub Zelenka }
6103e5afbf0SJakub Zelenka /* }}} */
6113e5afbf0SJakub Zelenka 
zlog_stream_set_msg_suffix(struct zlog_stream * stream,const char * suffix,const char * final_suffix)61226cd84d1SJakub Zelenka zlog_bool zlog_stream_set_msg_suffix(
6133e5afbf0SJakub Zelenka 		struct zlog_stream *stream, const char *suffix, const char *final_suffix)  /* {{{ */
6143e5afbf0SJakub Zelenka {
6153e5afbf0SJakub Zelenka 	size_t len;
6163e5afbf0SJakub Zelenka 	if (!stream->wrap || !stream->decorate) {
61726cd84d1SJakub Zelenka 		return ZLOG_TRUE;
6183e5afbf0SJakub Zelenka 	}
6193e5afbf0SJakub Zelenka 
6203e5afbf0SJakub Zelenka 	if (suffix != NULL && final_suffix != NULL) {
6213e5afbf0SJakub Zelenka 		stream->msg_suffix_len = strlen(suffix);
6223e5afbf0SJakub Zelenka 		stream->msg_final_suffix_len = strlen(final_suffix);
6233e5afbf0SJakub Zelenka 		len = stream->msg_suffix_len + stream->msg_final_suffix_len + 2;
624f9d9c5ebSJakub Zelenka 		if (stream->msg_suffix != NULL) {
625f9d9c5ebSJakub Zelenka 			free(stream->msg_suffix);
626f9d9c5ebSJakub Zelenka 		}
6273e5afbf0SJakub Zelenka 		stream->msg_suffix = malloc(len);
6283e5afbf0SJakub Zelenka 		if (stream->msg_suffix == NULL) {
62926cd84d1SJakub Zelenka 			return ZLOG_FALSE;
6303e5afbf0SJakub Zelenka 		}
6313e5afbf0SJakub Zelenka 		stream->msg_final_suffix = stream->msg_suffix + stream->msg_suffix_len + 1;
6323e5afbf0SJakub Zelenka 		memcpy(stream->msg_suffix, suffix, stream->msg_suffix_len + 1);
6333e5afbf0SJakub Zelenka 		memcpy(stream->msg_final_suffix, final_suffix, stream->msg_final_suffix_len + 1);
63426cd84d1SJakub Zelenka 		return ZLOG_TRUE;
6353e5afbf0SJakub Zelenka 	}
6363e5afbf0SJakub Zelenka 	if (suffix != NULL) {
63726cd84d1SJakub Zelenka 		stream->msg_suffix_len = strlen(suffix);
63826cd84d1SJakub Zelenka 		len = stream->msg_suffix_len + 1;
63926cd84d1SJakub Zelenka 		stream->msg_suffix = malloc(len);
640f9d9c5ebSJakub Zelenka 		if (stream->msg_suffix != NULL) {
641f9d9c5ebSJakub Zelenka 			free(stream->msg_suffix);
642f9d9c5ebSJakub Zelenka 		}
6433e5afbf0SJakub Zelenka 		if (stream->msg_suffix == NULL) {
64426cd84d1SJakub Zelenka 			return ZLOG_FALSE;
6453e5afbf0SJakub Zelenka 		}
64626cd84d1SJakub Zelenka 		memcpy(stream->msg_suffix, suffix, len);
64726cd84d1SJakub Zelenka 		return ZLOG_TRUE;
6483e5afbf0SJakub Zelenka 	}
6493e5afbf0SJakub Zelenka 	if (final_suffix != NULL) {
65026cd84d1SJakub Zelenka 		stream->msg_final_suffix_len = strlen(final_suffix);
65126cd84d1SJakub Zelenka 		len = stream->msg_final_suffix_len + 1;
65226cd84d1SJakub Zelenka 		stream->msg_final_suffix = malloc(len);
653f9d9c5ebSJakub Zelenka 		if (stream->msg_final_suffix != NULL) {
654f9d9c5ebSJakub Zelenka 			free(stream->msg_suffix);
655f9d9c5ebSJakub Zelenka 		}
6563e5afbf0SJakub Zelenka 		if (stream->msg_final_suffix == NULL) {
65726cd84d1SJakub Zelenka 			return ZLOG_FALSE;
6583e5afbf0SJakub Zelenka 		}
65926cd84d1SJakub Zelenka 		memcpy(stream->msg_final_suffix, final_suffix, len);
66026cd84d1SJakub Zelenka 		return ZLOG_TRUE;
6613e5afbf0SJakub Zelenka 	}
6623e5afbf0SJakub Zelenka 
66326cd84d1SJakub Zelenka 	return ZLOG_TRUE;
6643e5afbf0SJakub Zelenka }
6653e5afbf0SJakub Zelenka /* }}} */
6663e5afbf0SJakub Zelenka 
zlog_stream_prefix_ex(struct zlog_stream * stream,const char * function,int line)6673e5afbf0SJakub Zelenka ssize_t zlog_stream_prefix_ex(struct zlog_stream *stream, const char *function, int line) /* {{{ */
6683e5afbf0SJakub Zelenka {
6693e5afbf0SJakub Zelenka 	size_t len;
6703e5afbf0SJakub Zelenka 
6713e5afbf0SJakub Zelenka 	if (!stream->prefix_buffer) {
6723e5afbf0SJakub Zelenka 		return 0;
6733e5afbf0SJakub Zelenka 	}
6743e5afbf0SJakub Zelenka 	if (stream->wrap && stream->function == NULL) {
6753e5afbf0SJakub Zelenka 		stream->function = function;
6763e5afbf0SJakub Zelenka 		stream->line = line;
6773e5afbf0SJakub Zelenka 	}
6783e5afbf0SJakub Zelenka 
6793e5afbf0SJakub Zelenka 	if (stream->use_buffer) {
6803e5afbf0SJakub Zelenka 		if (!zlog_stream_buf_alloc(stream)) {
6813e5afbf0SJakub Zelenka 			return -1;
6823e5afbf0SJakub Zelenka 		}
6833e5afbf0SJakub Zelenka 		len = zlog_buf_prefix(
6843e5afbf0SJakub Zelenka 				function, line, stream->flags,
6853e5afbf0SJakub Zelenka 				stream->buf.data, stream->buf.size, stream->use_syslog);
6863e5afbf0SJakub Zelenka 		stream->len = stream->prefix_len = len;
6873e5afbf0SJakub Zelenka 		if (stream->msg_prefix != NULL) {
6883e5afbf0SJakub Zelenka 			zlog_stream_buf_copy_cstr(stream, stream->msg_prefix, stream->msg_prefix_len);
6893e5afbf0SJakub Zelenka 		}
6903e5afbf0SJakub Zelenka 		if (stream->msg_quote) {
6913e5afbf0SJakub Zelenka 			zlog_stream_buf_copy_char(stream, '"');
6923e5afbf0SJakub Zelenka 		}
6933e5afbf0SJakub Zelenka 		return stream->len;
6943e5afbf0SJakub Zelenka 	} else {
6953e5afbf0SJakub Zelenka 		char sbuf[1024];
6963e5afbf0SJakub Zelenka 		ssize_t written;
6973e5afbf0SJakub Zelenka 		len = zlog_buf_prefix(function, line, stream->flags, sbuf, 1024, stream->use_syslog);
6983e5afbf0SJakub Zelenka 		written = zlog_stream_direct_write(stream, sbuf, len);
6993e5afbf0SJakub Zelenka 		if (stream->msg_prefix != NULL) {
7003e5afbf0SJakub Zelenka 			written += zlog_stream_direct_write(
7013e5afbf0SJakub Zelenka 					stream, stream->msg_prefix, stream->msg_prefix_len);
7023e5afbf0SJakub Zelenka 		}
7033e5afbf0SJakub Zelenka 		if (stream->msg_quote) {
7043e5afbf0SJakub Zelenka 			written += zlog_stream_direct_write(stream, "\"", 1);
7053e5afbf0SJakub Zelenka 		}
7063e5afbf0SJakub Zelenka 		return written;
7073e5afbf0SJakub Zelenka 	}
7083e5afbf0SJakub Zelenka }
7093e5afbf0SJakub Zelenka /* }}} */
7103e5afbf0SJakub Zelenka 
zlog_stream_vformat(struct zlog_stream * stream,const char * fmt,va_list args)7113e5afbf0SJakub Zelenka ssize_t zlog_stream_vformat(struct zlog_stream *stream, const char *fmt, va_list args) /* {{{ */
7123e5afbf0SJakub Zelenka {
7133e5afbf0SJakub Zelenka 	char sbuf[1024];
7143e5afbf0SJakub Zelenka 	size_t len;
7153e5afbf0SJakub Zelenka 
7163e5afbf0SJakub Zelenka 	len = vsnprintf(sbuf, 1024, fmt, args);
7173e5afbf0SJakub Zelenka 
7183e5afbf0SJakub Zelenka 	return zlog_stream_str(stream, sbuf, len);
7193e5afbf0SJakub Zelenka }
7203e5afbf0SJakub Zelenka /* }}} */
7213e5afbf0SJakub Zelenka 
zlog_stream_format(struct zlog_stream * stream,const char * fmt,...)7223e5afbf0SJakub Zelenka ssize_t zlog_stream_format(struct zlog_stream *stream, const char *fmt, ...) /* {{{ */
7233e5afbf0SJakub Zelenka {
7243e5afbf0SJakub Zelenka 	ssize_t len;
7253e5afbf0SJakub Zelenka 
7263e5afbf0SJakub Zelenka 	va_list args;
7273e5afbf0SJakub Zelenka 	va_start(args, fmt);
7283e5afbf0SJakub Zelenka 	len = zlog_stream_vformat(stream, fmt, args);
7293e5afbf0SJakub Zelenka 	va_end(args);
7303e5afbf0SJakub Zelenka 
7313e5afbf0SJakub Zelenka 	return len;
7323e5afbf0SJakub Zelenka }
7333e5afbf0SJakub Zelenka /* }}} */
7343e5afbf0SJakub Zelenka 
zlog_stream_str(struct zlog_stream * stream,const char * str,size_t str_len)7353e5afbf0SJakub Zelenka ssize_t zlog_stream_str(struct zlog_stream *stream, const char *str, size_t str_len) /* {{{ */
7363e5afbf0SJakub Zelenka {
737*0bc6a66aSJakub Zelenka 	/* do not write anything if the stream is full or str is empty */
738*0bc6a66aSJakub Zelenka 	if (str_len == 0 || stream->full) {
739*0bc6a66aSJakub Zelenka 		return 0;
740*0bc6a66aSJakub Zelenka 	}
741*0bc6a66aSJakub Zelenka 
7423e5afbf0SJakub Zelenka 	/* reset stream if it is finished */
7433e5afbf0SJakub Zelenka 	if (stream->finished) {
7443e5afbf0SJakub Zelenka 		stream->finished = 0;
7453e5afbf0SJakub Zelenka 		stream->len = 0;
7463e5afbf0SJakub Zelenka 		stream->full = 0;
7473e5afbf0SJakub Zelenka 	}
7483e5afbf0SJakub Zelenka 
7493e5afbf0SJakub Zelenka 	if (stream->use_buffer) {
7503e5afbf0SJakub Zelenka 		return zlog_stream_buf_append(stream, str, str_len);
7513e5afbf0SJakub Zelenka 	}
7523e5afbf0SJakub Zelenka 
7533e5afbf0SJakub Zelenka 	return zlog_stream_unbuffered_write(stream, str, str_len);
7543e5afbf0SJakub Zelenka }
7553e5afbf0SJakub Zelenka /* }}} */
7563e5afbf0SJakub Zelenka 
zlog_stream_finish_buffer_suffix(struct zlog_stream * stream)7573e5afbf0SJakub Zelenka static inline void zlog_stream_finish_buffer_suffix(struct zlog_stream *stream) /* {{{ */
7583e5afbf0SJakub Zelenka {
7593e5afbf0SJakub Zelenka 	if (stream->msg_quote) {
7603e5afbf0SJakub Zelenka 		zlog_stream_buf_copy_char(stream, '"');
7613e5afbf0SJakub Zelenka 	}
7623e5afbf0SJakub Zelenka 	if (stream->msg_suffix != NULL) {
7633e5afbf0SJakub Zelenka 		zlog_stream_buf_copy_cstr(stream, stream->msg_suffix, stream->msg_suffix_len);
7643e5afbf0SJakub Zelenka 	}
7653e5afbf0SJakub Zelenka 	if (stream->msg_final_suffix != NULL) {
7663e5afbf0SJakub Zelenka 		if (stream->len + stream->msg_final_suffix_len >= zlog_limit) {
7673e5afbf0SJakub Zelenka 			zlog_bool quoting = stream->msg_quote;
7683e5afbf0SJakub Zelenka 			size_t final_suffix_wrap = stream->len + stream->msg_final_suffix_len + 1 - zlog_limit;
7693e5afbf0SJakub Zelenka 			zlog_stream_buf_copy_cstr(
7703e5afbf0SJakub Zelenka 					stream, stream->msg_final_suffix,
7713e5afbf0SJakub Zelenka 					stream->msg_final_suffix_len - final_suffix_wrap);
7723e5afbf0SJakub Zelenka 			zlog_stream_buf_copy_char(stream, '\n');
7733e5afbf0SJakub Zelenka 			zlog_stream_buf_flush(stream);
7743e5afbf0SJakub Zelenka 			stream->msg_quote = 0;
7753e5afbf0SJakub Zelenka 			zlog_stream_prefix_ex(stream, stream->function, stream->line);
7763e5afbf0SJakub Zelenka 			stream->msg_quote = quoting;
7773e5afbf0SJakub Zelenka 			zlog_stream_buf_copy_cstr(
7783e5afbf0SJakub Zelenka 					stream,
7793e5afbf0SJakub Zelenka 					stream->msg_final_suffix + (stream->msg_final_suffix_len - final_suffix_wrap),
7803e5afbf0SJakub Zelenka 					final_suffix_wrap);
7813e5afbf0SJakub Zelenka 			zlog_stream_buf_copy_char(stream, '\n');
7823e5afbf0SJakub Zelenka 		} else {
7833e5afbf0SJakub Zelenka 			zlog_stream_buf_copy_cstr(
7843e5afbf0SJakub Zelenka 					stream, stream->msg_final_suffix, stream->msg_final_suffix_len);
7853e5afbf0SJakub Zelenka 		}
7863e5afbf0SJakub Zelenka 	}
7873e5afbf0SJakub Zelenka }
7883e5afbf0SJakub Zelenka /* }}} */
7893e5afbf0SJakub Zelenka 
zlog_stream_finish_direct_suffix(struct zlog_stream * stream)7903e5afbf0SJakub Zelenka static inline void zlog_stream_finish_direct_suffix(struct zlog_stream *stream) /* {{{ */
7913e5afbf0SJakub Zelenka {
7923e5afbf0SJakub Zelenka 	if (stream->msg_quote) {
7933e5afbf0SJakub Zelenka 		zlog_stream_direct_write(stream, "\"", 1);
7943e5afbf0SJakub Zelenka 		++stream->len;
7953e5afbf0SJakub Zelenka 	}
7963e5afbf0SJakub Zelenka 	if (stream->msg_suffix != NULL) {
7973e5afbf0SJakub Zelenka 		/* we should always have space for wrap suffix so we don't have to check it */
7983e5afbf0SJakub Zelenka 		zlog_stream_direct_write(stream, stream->msg_suffix, stream->msg_suffix_len);
7993e5afbf0SJakub Zelenka 		stream->len += stream->msg_suffix_len;
8003e5afbf0SJakub Zelenka 	}
8013e5afbf0SJakub Zelenka 	if (stream->msg_final_suffix != NULL) {
8023e5afbf0SJakub Zelenka 		if (stream->len + stream->msg_final_suffix_len >= zlog_limit) {
8033e5afbf0SJakub Zelenka 			zlog_bool quoting = stream->msg_quote;
8043e5afbf0SJakub Zelenka 			size_t final_suffix_wrap = stream->len + stream->msg_final_suffix_len + 1 - zlog_limit;
8053e5afbf0SJakub Zelenka 			zlog_stream_direct_write_ex(
8063e5afbf0SJakub Zelenka 					stream, stream->msg_final_suffix,
8073e5afbf0SJakub Zelenka 					stream->msg_final_suffix_len - final_suffix_wrap, "\n", 1);
8083e5afbf0SJakub Zelenka 			stream->msg_quote = 0;
8093e5afbf0SJakub Zelenka 			zlog_stream_prefix_ex(stream, stream->function, stream->line);
8103e5afbf0SJakub Zelenka 			stream->msg_quote = quoting;
8113e5afbf0SJakub Zelenka 			zlog_stream_direct_write_ex(
8123e5afbf0SJakub Zelenka 					stream,
8133e5afbf0SJakub Zelenka 					stream->msg_final_suffix + (stream->msg_final_suffix_len - final_suffix_wrap),
8143e5afbf0SJakub Zelenka 					final_suffix_wrap, "\n", 1);
8153e5afbf0SJakub Zelenka 		} else {
8163e5afbf0SJakub Zelenka 			zlog_stream_direct_write_ex(
8173e5afbf0SJakub Zelenka 					stream, stream->msg_final_suffix, stream->msg_final_suffix_len, "\n", 1);
8183e5afbf0SJakub Zelenka 		}
8193e5afbf0SJakub Zelenka 	} else {
8203e5afbf0SJakub Zelenka 		zlog_stream_direct_write(stream, "\n", 1);
8213e5afbf0SJakub Zelenka 	}
8223e5afbf0SJakub Zelenka }
8233e5afbf0SJakub Zelenka /* }}} */
8243e5afbf0SJakub Zelenka 
zlog_stream_finish(struct zlog_stream * stream)8253e5afbf0SJakub Zelenka zlog_bool zlog_stream_finish(struct zlog_stream *stream) /* {{{ */
8263e5afbf0SJakub Zelenka {
8273e5afbf0SJakub Zelenka 	if (stream->finished || stream->len == 0) {
8283e5afbf0SJakub Zelenka 		return ZLOG_TRUE;
8293e5afbf0SJakub Zelenka 	}
8303e5afbf0SJakub Zelenka 
8313e5afbf0SJakub Zelenka 	if (stream->use_buffer) {
8323e5afbf0SJakub Zelenka 		if (stream->decorate) {
8333e5afbf0SJakub Zelenka 			zlog_stream_finish_buffer_suffix(stream);
8343e5afbf0SJakub Zelenka 		}
8353e5afbf0SJakub Zelenka 		zlog_stream_buf_flush(stream);
8363e5afbf0SJakub Zelenka 	} else {
8373e5afbf0SJakub Zelenka 		if (stream->decorate) {
8383e5afbf0SJakub Zelenka 			zlog_stream_finish_direct_suffix(stream);
8393e5afbf0SJakub Zelenka 		} else {
8403e5afbf0SJakub Zelenka 			zlog_stream_direct_write(stream, "\n", 1);
8413e5afbf0SJakub Zelenka 		}
8423e5afbf0SJakub Zelenka 	}
8433e5afbf0SJakub Zelenka 	stream->finished = 1;
8443e5afbf0SJakub Zelenka 
8453e5afbf0SJakub Zelenka 	return ZLOG_TRUE;
8463e5afbf0SJakub Zelenka }
8473e5afbf0SJakub Zelenka /* }}} */
8483e5afbf0SJakub Zelenka 
zlog_stream_destroy(struct zlog_stream * stream)8493e5afbf0SJakub Zelenka void zlog_stream_destroy(struct zlog_stream *stream) /* {{{ */
8503e5afbf0SJakub Zelenka {
851014b1b3aSJakub Zelenka 	if (stream->buf.data != NULL) {
8523e5afbf0SJakub Zelenka 		free(stream->buf.data);
8533e5afbf0SJakub Zelenka 	}
8543e5afbf0SJakub Zelenka 	if (stream->msg_prefix != NULL) {
8553e5afbf0SJakub Zelenka 		free(stream->msg_prefix);
8563e5afbf0SJakub Zelenka 	}
8573e5afbf0SJakub Zelenka 	if (stream->msg_suffix != NULL) {
8583e5afbf0SJakub Zelenka 		free(stream->msg_suffix);
8593e5afbf0SJakub Zelenka 	} else if (stream->msg_final_suffix != NULL) {
8603e5afbf0SJakub Zelenka 		free(stream->msg_final_suffix);
8613e5afbf0SJakub Zelenka 	}
8623e5afbf0SJakub Zelenka }
8633e5afbf0SJakub Zelenka /* }}} */
8643e5afbf0SJakub Zelenka 
zlog_stream_close(struct zlog_stream * stream)8653e5afbf0SJakub Zelenka zlog_bool zlog_stream_close(struct zlog_stream *stream) /* {{{ */
8663e5afbf0SJakub Zelenka {
8673e5afbf0SJakub Zelenka 	zlog_bool finished = zlog_stream_finish(stream);
8683e5afbf0SJakub Zelenka 	zlog_stream_destroy(stream);
8693e5afbf0SJakub Zelenka 
8703e5afbf0SJakub Zelenka 	return finished;
8713e5afbf0SJakub Zelenka }
8723e5afbf0SJakub Zelenka /* }}} */
873