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