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