xref: /freebsd/lib/libpjdlog/pjdlog.c (revision 95eb4b873b6a8b527c5bd78d7191975dfca38998)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2009-2010 The FreeBSD Foundation
5  * Copyright (c) 2011 Pawel Jakub Dawidek <pjd@FreeBSD.org>
6  * All rights reserved.
7  *
8  * This software was developed by Pawel Jakub Dawidek under sponsorship from
9  * the FreeBSD Foundation.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/un.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38 
39 #include <assert.h>
40 #include <errno.h>
41 #include <libutil.h>
42 #include <limits.h>
43 #include <printf.h>
44 #include <stdarg.h>
45 #include <stdint.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <syslog.h>
50 #include <unistd.h>
51 
52 #ifdef notyet
53 #include <robustio.h>
54 #endif
55 
56 #include "pjdlog.h"
57 
58 #ifndef	MAX
59 #define	MAX(a, b)	((a) > (b) ? (a) : (b))
60 #endif
61 
62 #define	PJDLOG_MAX_MSGSIZE	4096
63 
64 #define	PJDLOG_PREFIX_STACK	4
65 #define	PJDLOG_PREFIX_MAXSIZE	128
66 
67 #define	PJDLOG_NEVER_INITIALIZED	0
68 #define	PJDLOG_NOT_INITIALIZED		1
69 #define	PJDLOG_INITIALIZED		2
70 
71 static int pjdlog_initialized = PJDLOG_NEVER_INITIALIZED;
72 static int pjdlog_mode, pjdlog_debug_level, pjdlog_sock;
73 static int pjdlog_prefix_current;
74 static char pjdlog_prefix[PJDLOG_PREFIX_STACK][PJDLOG_PREFIX_MAXSIZE];
75 
76 static int
77 pjdlog_printf_arginfo_humanized_number(const struct printf_info *pi __unused,
78     size_t n, int *argt)
79 {
80 
81 	assert(n >= 1);
82 	argt[0] = PA_INT | PA_FLAG_INTMAX;
83 	return (1);
84 }
85 
86 static int
87 pjdlog_printf_render_humanized_number(struct __printf_io *io,
88     const struct printf_info *pi, const void * const *arg)
89 {
90 	char buf[5];
91 	intmax_t num;
92 	int ret;
93 
94 	num = *(const intmax_t *)arg[0];
95 	humanize_number(buf, sizeof(buf), (int64_t)num, "", HN_AUTOSCALE,
96 	    HN_NOSPACE | HN_DECIMAL);
97 	ret = __printf_out(io, pi, buf, strlen(buf));
98 	__printf_flush(io);
99 	return (ret);
100 }
101 
102 static int
103 pjdlog_printf_arginfo_sockaddr(const struct printf_info *pi __unused,
104     size_t n, int *argt)
105 {
106 
107 	assert(n >= 1);
108 	argt[0] = PA_POINTER;
109 	return (1);
110 }
111 
112 static int
113 pjdlog_printf_render_sockaddr_ip(struct __printf_io *io,
114     const struct printf_info *pi, const void * const *arg)
115 {
116 	const struct sockaddr_storage *ss;
117 	char addr[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN)];
118 	int ret;
119 
120 	ss = *(const struct sockaddr_storage * const *)arg[0];
121 	switch (ss->ss_family) {
122 	case AF_INET:
123 	    {
124 		const struct sockaddr_in *sin;
125 
126 		sin = (const struct sockaddr_in *)ss;
127 		if (inet_ntop(ss->ss_family, &sin->sin_addr, addr,
128 		    sizeof(addr)) == NULL) {
129 			PJDLOG_ABORT("inet_ntop(AF_INET) failed: %s.",
130 			    strerror(errno));
131 		}
132 		break;
133 	    }
134 	case AF_INET6:
135 	    {
136 		const struct sockaddr_in6 *sin;
137 
138 		sin = (const struct sockaddr_in6 *)ss;
139 		if (inet_ntop(ss->ss_family, &sin->sin6_addr, addr,
140 		    sizeof(addr)) == NULL) {
141 			PJDLOG_ABORT("inet_ntop(AF_INET6) failed: %s.",
142 			    strerror(errno));
143 		}
144 		break;
145 	    }
146 	default:
147 		snprintf(addr, sizeof(addr), "[unsupported family %hhu]",
148 		    ss->ss_family);
149 		break;
150 	}
151 	ret = __printf_out(io, pi, addr, strlen(addr));
152 	__printf_flush(io);
153 	return (ret);
154 }
155 
156 static int
157 pjdlog_printf_render_sockaddr(struct __printf_io *io,
158     const struct printf_info *pi, const void * const *arg)
159 {
160 	const struct sockaddr_storage *ss;
161 	char buf[PATH_MAX];
162 	int ret;
163 
164 	ss = *(const struct sockaddr_storage * const *)arg[0];
165 	switch (ss->ss_family) {
166 	case AF_UNIX:
167 	    {
168 		const struct sockaddr_un *sun;
169 
170 		sun = (const struct sockaddr_un *)ss;
171 		if (sun->sun_path[0] == '\0')
172 			snprintf(buf, sizeof(buf), "N/A");
173 		else
174 			snprintf(buf, sizeof(buf), "%s", sun->sun_path);
175 		break;
176 	    }
177 	case AF_INET:
178 	    {
179 		char addr[INET_ADDRSTRLEN];
180 		const struct sockaddr_in *sin;
181 		unsigned int port;
182 
183 		sin = (const struct sockaddr_in *)ss;
184 		port = ntohs(sin->sin_port);
185 		if (inet_ntop(ss->ss_family, &sin->sin_addr, addr,
186 		    sizeof(addr)) == NULL) {
187 			PJDLOG_ABORT("inet_ntop(AF_INET) failed: %s.",
188 			    strerror(errno));
189 		}
190 		snprintf(buf, sizeof(buf), "%s:%u", addr, port);
191 		break;
192 	    }
193 	case AF_INET6:
194 	    {
195 		char addr[INET6_ADDRSTRLEN];
196 		const struct sockaddr_in6 *sin;
197 		unsigned int port;
198 
199 		sin = (const struct sockaddr_in6 *)ss;
200 		port = ntohs(sin->sin6_port);
201 		if (inet_ntop(ss->ss_family, &sin->sin6_addr, addr,
202 		    sizeof(addr)) == NULL) {
203 			PJDLOG_ABORT("inet_ntop(AF_INET6) failed: %s.",
204 			    strerror(errno));
205 		}
206 		snprintf(buf, sizeof(buf), "[%s]:%u", addr, port);
207 		break;
208 	    }
209 	default:
210 		snprintf(buf, sizeof(buf), "[unsupported family %hhu]",
211 		    ss->ss_family);
212 		break;
213 	}
214 	ret = __printf_out(io, pi, buf, strlen(buf));
215 	__printf_flush(io);
216 	return (ret);
217 }
218 
219 void
220 pjdlog_init(int mode)
221 {
222 	int saved_errno;
223 
224 	assert(pjdlog_initialized == PJDLOG_NEVER_INITIALIZED ||
225 	    pjdlog_initialized == PJDLOG_NOT_INITIALIZED);
226 #ifdef notyet
227 	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG ||
228 	    mode == PJDLOG_MODE_SOCK);
229 #else
230 	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
231 #endif
232 
233 	saved_errno = errno;
234 
235 	if (pjdlog_initialized == PJDLOG_NEVER_INITIALIZED) {
236 		__use_xprintf = 1;
237 		register_printf_render_std("T");
238 		register_printf_render('N',
239 		    pjdlog_printf_render_humanized_number,
240 		    pjdlog_printf_arginfo_humanized_number);
241 		register_printf_render('I',
242 		    pjdlog_printf_render_sockaddr_ip,
243 		    pjdlog_printf_arginfo_sockaddr);
244 		register_printf_render('S',
245 		    pjdlog_printf_render_sockaddr,
246 		    pjdlog_printf_arginfo_sockaddr);
247 	}
248 
249 	if (mode == PJDLOG_MODE_SYSLOG)
250 		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_LOCAL0);
251 	pjdlog_mode = mode;
252 	pjdlog_debug_level = 0;
253 	pjdlog_prefix_current = 0;
254 	pjdlog_prefix[0][0] = '\0';
255 
256 	pjdlog_initialized = PJDLOG_INITIALIZED;
257 	pjdlog_sock = -1;
258 
259 	errno = saved_errno;
260 }
261 
262 void
263 pjdlog_fini(void)
264 {
265 	int saved_errno;
266 
267 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
268 
269 	saved_errno = errno;
270 
271 	if (pjdlog_mode == PJDLOG_MODE_SYSLOG)
272 		closelog();
273 
274 	pjdlog_initialized = PJDLOG_NOT_INITIALIZED;
275 	pjdlog_sock = -1;
276 
277 	errno = saved_errno;
278 }
279 
280 /*
281  * Configure where the logs should go.
282  * By default they are send to stdout/stderr, but after going into background
283  * (eg. by calling daemon(3)) application is responsible for changing mode to
284  * PJDLOG_MODE_SYSLOG, so logs will be send to syslog.
285  */
286 void
287 pjdlog_mode_set(int mode)
288 {
289 	int saved_errno;
290 
291 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
292 #ifdef notyet
293 	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG ||
294 	    mode == PJDLOG_MODE_SOCK);
295 #else
296 	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
297 #endif
298 
299 	if (pjdlog_mode == mode)
300 		return;
301 
302 	saved_errno = errno;
303 
304 	if (mode == PJDLOG_MODE_SYSLOG)
305 		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
306 	else if (mode == PJDLOG_MODE_STD)
307 		closelog();
308 
309 	if (mode != PJDLOG_MODE_SOCK)
310 		pjdlog_sock = -1;
311 
312 	pjdlog_mode = mode;
313 
314 	errno = saved_errno;
315 }
316 
317 
318 /*
319  * Return current mode.
320  */
321 int
322 pjdlog_mode_get(void)
323 {
324 
325 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
326 
327 	return (pjdlog_mode);
328 }
329 
330 #ifdef notyet
331 /*
332  * Sets socket number to use for PJDLOG_MODE_SOCK mode.
333  */
334 void
335 pjdlog_sock_set(int sock)
336 {
337 
338 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
339 	assert(pjdlog_mode == PJDLOG_MODE_SOCK);
340 	assert(sock >= 0);
341 
342 	pjdlog_sock = sock;
343 }
344 #endif
345 
346 #ifdef notyet
347 /*
348  * Returns socket number used for PJDLOG_MODE_SOCK mode.
349  */
350 int
351 pjdlog_sock_get(void)
352 {
353 
354 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
355 	assert(pjdlog_mode == PJDLOG_MODE_SOCK);
356 	assert(pjdlog_sock >= 0);
357 
358 	return (pjdlog_sock);
359 }
360 #endif
361 
362 /*
363  * Set debug level. All the logs above the level specified here will be
364  * ignored.
365  */
366 void
367 pjdlog_debug_set(int level)
368 {
369 
370 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
371 	assert(level >= 0);
372 	assert(level <= 127);
373 
374 	pjdlog_debug_level = level;
375 }
376 
377 /*
378  * Return current debug level.
379  */
380 int
381 pjdlog_debug_get(void)
382 {
383 
384 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
385 
386 	return (pjdlog_debug_level);
387 }
388 
389 /*
390  * Set prefix that will be used before each log.
391  */
392 void
393 pjdlog_prefix_set(const char *fmt, ...)
394 {
395 	va_list ap;
396 
397 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
398 
399 	va_start(ap, fmt);
400 	pjdlogv_prefix_set(fmt, ap);
401 	va_end(ap);
402 }
403 
404 /*
405  * Set prefix that will be used before each log.
406  */
407 void
408 pjdlogv_prefix_set(const char *fmt, va_list ap)
409 {
410 	int saved_errno;
411 
412 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
413 	assert(fmt != NULL);
414 
415 	saved_errno = errno;
416 
417 	vsnprintf(pjdlog_prefix[pjdlog_prefix_current],
418 	    sizeof(pjdlog_prefix[pjdlog_prefix_current]), fmt, ap);
419 
420 	errno = saved_errno;
421 }
422 
423 /*
424  * Get current prefix.
425  */
426 const char *
427 pjdlog_prefix_get(void)
428 {
429 
430 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
431 
432 	return (pjdlog_prefix[pjdlog_prefix_current]);
433 }
434 
435 /*
436  * Set new prefix and put the current one on the stack.
437  */
438 void
439 pjdlog_prefix_push(const char *fmt, ...)
440 {
441 	va_list ap;
442 
443 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
444 
445 	va_start(ap, fmt);
446 	pjdlogv_prefix_push(fmt, ap);
447 	va_end(ap);
448 }
449 
450 /*
451  * Set new prefix and put the current one on the stack.
452  */
453 void
454 pjdlogv_prefix_push(const char *fmt, va_list ap)
455 {
456 
457 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
458 	assert(pjdlog_prefix_current < PJDLOG_PREFIX_STACK - 1);
459 
460 	pjdlog_prefix_current++;
461 
462 	pjdlogv_prefix_set(fmt, ap);
463 }
464 
465 /*
466  * Removes current prefix and recovers previous one from the stack.
467  */
468 void
469 pjdlog_prefix_pop(void)
470 {
471 
472 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
473 	assert(pjdlog_prefix_current > 0);
474 
475 	pjdlog_prefix_current--;
476 }
477 
478 /*
479  * Convert log level into string.
480  */
481 static const char *
482 pjdlog_level_to_string(int loglevel)
483 {
484 
485 	switch (loglevel) {
486 	case LOG_EMERG:
487 		return ("EMERG");
488 	case LOG_ALERT:
489 		return ("ALERT");
490 	case LOG_CRIT:
491 		return ("CRIT");
492 	case LOG_ERR:
493 		return ("ERROR");
494 	case LOG_WARNING:
495 		return ("WARNING");
496 	case LOG_NOTICE:
497 		return ("NOTICE");
498 	case LOG_INFO:
499 		return ("INFO");
500 	case LOG_DEBUG:
501 		return ("DEBUG");
502 	}
503 	assert(!"Invalid log level.");
504 	abort();	/* XXX: gcc */
505 }
506 
507 static int
508 vsnprlcat(char *str, size_t size, const char *fmt, va_list ap)
509 {
510 	size_t len;
511 
512 	len = strlen(str);
513 	assert(len < size);
514 	return (vsnprintf(str + len, size - len, fmt, ap));
515 }
516 
517 static int
518 snprlcat(char *str, size_t size, const char *fmt, ...)
519 {
520 	va_list ap;
521 	int result;
522 
523 	va_start(ap, fmt);
524 	result = vsnprlcat(str, size, fmt, ap);
525 	va_end(ap);
526 	return (result);
527 }
528 
529 static void
530 pjdlogv_common_single_line(const char *func, const char *file, int line,
531     int loglevel, int debuglevel, int error, const char *msg)
532 {
533 	static char log[2 * PJDLOG_MAX_MSGSIZE];
534 	char *logp;
535 	size_t logs;
536 
537 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
538 #ifdef notyet
539 	assert(pjdlog_mode == PJDLOG_MODE_STD ||
540 	    pjdlog_mode == PJDLOG_MODE_SYSLOG ||
541 	    pjdlog_mode == PJDLOG_MODE_SOCK);
542 #else
543 	assert(pjdlog_mode == PJDLOG_MODE_STD ||
544 	    pjdlog_mode == PJDLOG_MODE_SYSLOG);
545 #endif
546 	assert(pjdlog_mode != PJDLOG_MODE_SOCK || pjdlog_sock >= 0);
547 	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
548 	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
549 	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
550 	    loglevel == LOG_INFO || loglevel == LOG_DEBUG);
551 	assert(loglevel != LOG_DEBUG || debuglevel > 0);
552 	assert(loglevel != LOG_DEBUG || debuglevel <= pjdlog_debug_level);
553 	assert(debuglevel <= 127);
554 	assert(error >= -1);
555 	assert((file != NULL && line > 0) ||
556 	    (func == NULL && file == NULL && line == 0));
557 
558 	switch (pjdlog_mode) {
559 	case PJDLOG_MODE_STD:
560 	case PJDLOG_MODE_SYSLOG:
561 		logp = log;
562 		logs = sizeof(log);
563 		break;
564 	case PJDLOG_MODE_SOCK:
565 		logp = log + 4;
566 		logs = sizeof(log) - 4;
567 		break;
568 	default:
569 		assert(!"Invalid mode.");
570 	}
571 
572 	*logp = '\0';
573 
574 	if (pjdlog_mode != PJDLOG_MODE_SOCK) {
575 		if (loglevel == LOG_DEBUG) {
576 			/* Attach debuglevel if this is debug log. */
577 			snprlcat(logp, logs, "[%s%d] ",
578 			    pjdlog_level_to_string(loglevel), debuglevel);
579 		} else {
580 			snprlcat(logp, logs, "[%s] ",
581 			    pjdlog_level_to_string(loglevel));
582 		}
583 		if (pjdlog_mode != PJDLOG_MODE_SYSLOG &&
584 		    pjdlog_debug_level >= 1) {
585 			snprlcat(logp, logs, "(pid=%d) ", getpid());
586 		}
587 	}
588 	/* Attach file, func, line if debuglevel is 2 or more. */
589 	if (pjdlog_debug_level >= 2 && file != NULL) {
590 		if (func == NULL)
591 			snprlcat(logp, logs, "(%s:%d) ", file, line);
592 		else
593 			snprlcat(logp, logs, "(%s:%d:%s) ", file, line, func);
594 	}
595 
596 	if (pjdlog_mode != PJDLOG_MODE_SOCK) {
597 		snprlcat(logp, logs, "%s",
598 		    pjdlog_prefix[pjdlog_prefix_current]);
599 	}
600 
601 	strlcat(logp, msg, logs);
602 
603 	/* Attach error description. */
604 	if (error != -1)
605 		snprlcat(logp, logs, ": %s.", strerror(error));
606 
607 	switch (pjdlog_mode) {
608 	case PJDLOG_MODE_STD:
609 		fprintf(stderr, "%s\n", logp);
610 		fflush(stderr);
611 		break;
612 	case PJDLOG_MODE_SYSLOG:
613 		syslog(loglevel, "%s", logp);
614 		break;
615 #ifdef notyet
616 	case PJDLOG_MODE_SOCK:
617 	    {
618 		char ack[2];
619 		uint16_t dlen;
620 
621 		log[2] = loglevel;
622 		log[3] = debuglevel;
623 		dlen = strlen(logp) + 3;	/* +3 = loglevel, debuglevel and terminating \0 */
624 		bcopy(&dlen, log, sizeof(dlen));
625 		if (robust_send(pjdlog_sock, log, (size_t)dlen + 2) == -1)	/* +2 for size */
626 			assert(!"Unable to send log.");
627 		if (robust_recv(pjdlog_sock, ack, sizeof(ack)) == -1)
628 			assert(!"Unable to send log.");
629 		break;
630 	    }
631 #endif
632 	default:
633 		assert(!"Invalid mode.");
634 	}
635 }
636 
637 /*
638  * Common log routine, which can handle regular log level as well as debug
639  * level. We decide here where to send the logs (stdout/stderr or syslog).
640  */
641 void
642 _pjdlogv_common(const char *func, const char *file, int line, int loglevel,
643     int debuglevel, int error, const char *fmt, va_list ap)
644 {
645 	char log[PJDLOG_MAX_MSGSIZE];
646 	char *logp, *curline;
647 	const char *prvline;
648 	int saved_errno;
649 
650 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
651 	assert(pjdlog_mode == PJDLOG_MODE_STD ||
652 	    pjdlog_mode == PJDLOG_MODE_SYSLOG ||
653 	    pjdlog_mode == PJDLOG_MODE_SOCK);
654 	assert(pjdlog_mode != PJDLOG_MODE_SOCK || pjdlog_sock >= 0);
655 	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
656 	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
657 	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
658 	    loglevel == LOG_INFO || loglevel == LOG_DEBUG);
659 	assert(loglevel != LOG_DEBUG || debuglevel > 0);
660 	assert(debuglevel <= 127);
661 	assert(error >= -1);
662 
663 	/* Ignore debug above configured level. */
664 	if (loglevel == LOG_DEBUG && debuglevel > pjdlog_debug_level)
665 		return;
666 
667 	saved_errno = errno;
668 
669 	vsnprintf(log, sizeof(log), fmt, ap);
670 	logp = log;
671 	prvline = NULL;
672 
673 	while ((curline = strsep(&logp, "\n")) != NULL) {
674 		if (*curline == '\0')
675 			continue;
676 		if (prvline != NULL) {
677 			pjdlogv_common_single_line(func, file, line, loglevel,
678 			    debuglevel, -1, prvline);
679 		}
680 		prvline = curline;
681 	}
682 	if (prvline == NULL)
683 		prvline = "";
684 	pjdlogv_common_single_line(func, file, line, loglevel, debuglevel,
685 	    error, prvline);
686 
687 	errno = saved_errno;
688 }
689 
690 /*
691  * Common log routine.
692  */
693 void
694 _pjdlog_common(const char *func, const char *file, int line, int loglevel,
695     int debuglevel, int error, const char *fmt, ...)
696 {
697 	va_list ap;
698 
699 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
700 
701 	va_start(ap, fmt);
702 	_pjdlogv_common(func, file, line, loglevel, debuglevel, error, fmt, ap);
703 	va_end(ap);
704 }
705 
706 /*
707  * Log error, errno and exit.
708  */
709 void
710 _pjdlogv_exit(const char *func, const char *file, int line, int exitcode,
711     int error, const char *fmt, va_list ap)
712 {
713 
714 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
715 
716 	_pjdlogv_common(func, file, line, exitcode == 0 ? LOG_INFO : LOG_ERR, 0,
717 	    error, fmt, ap);
718 	exit(exitcode);
719 	/* NOTREACHED */
720 }
721 
722 /*
723  * Log error, errno and exit.
724  */
725 void
726 _pjdlog_exit(const char *func, const char *file, int line, int exitcode,
727     int error, const char *fmt, ...)
728 {
729 	va_list ap;
730 
731 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
732 
733 	va_start(ap, fmt);
734 	_pjdlogv_exit(func, file, line, exitcode, error, fmt, ap);
735 	/* NOTREACHED */
736 	va_end(ap);
737 }
738 
739 /*
740  * Log failure message and exit.
741  */
742 void
743 _pjdlog_abort(const char *func, const char *file, int line,
744     int error, const char *failedexpr, const char *fmt, ...)
745 {
746 	va_list ap;
747 
748 	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
749 
750 	/*
751 	 * Set pjdlog_debug_level to 2, so that file, line and func are
752 	 * included in log. This is fine as we will exit anyway.
753 	 */
754 	if (pjdlog_debug_level < 2)
755 		pjdlog_debug_level = 2;
756 
757 	/*
758 	 * When there is no message we pass __func__ as 'fmt'.
759 	 * It would be cleaner to pass NULL or "", but gcc generates a warning
760 	 * for both of those.
761 	 */
762 	if (fmt != func) {
763 		va_start(ap, fmt);
764 		_pjdlogv_common(func, file, line, LOG_CRIT, 0, -1, fmt, ap);
765 		va_end(ap);
766 	}
767 	if (failedexpr == NULL) {
768 		_pjdlog_common(func, file, line, LOG_CRIT, 0, -1, "Aborted.");
769 	} else {
770 		_pjdlog_common(func, file, line, LOG_CRIT, 0, -1,
771 		    "Assertion failed: (%s).", failedexpr);
772 	}
773 	if (error != -1)
774 		_pjdlog_common(func, file, line, LOG_CRIT, 0, error, "Errno");
775 	abort();
776 }
777 
778 #ifdef notyet
779 /*
780  * Receive log from the given socket.
781  */
782 int
783 pjdlog_receive(int sock)
784 {
785 	char log[PJDLOG_MAX_MSGSIZE];
786 	int loglevel, debuglevel;
787 	uint16_t dlen;
788 
789 	if (robust_recv(sock, &dlen, sizeof(dlen)) == -1)
790 		return (-1);
791 
792 	PJDLOG_ASSERT(dlen > 0);
793 	PJDLOG_ASSERT(dlen <= PJDLOG_MAX_MSGSIZE - 3);
794 
795 	if (robust_recv(sock, log, (size_t)dlen) == -1)
796 		return (-1);
797 
798 	log[dlen - 1] = '\0';
799 	loglevel = log[0];
800 	debuglevel = log[1];
801 	_pjdlog_common(NULL, NULL, 0, loglevel, debuglevel, -1, "%s", log + 2);
802 
803 	if (robust_send(sock, "ok", 2) == -1)
804 		return (-1);
805 
806 	return (0);
807 }
808 #endif
809