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