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
71static int pjdlog_initialized = PJDLOG_NEVER_INITIALIZED;
72static int pjdlog_mode, pjdlog_debug_level, pjdlog_sock;
73static int pjdlog_prefix_current;
74static char pjdlog_prefix[PJDLOG_PREFIX_STACK][PJDLOG_PREFIX_MAXSIZE];
75
76static int
77pjdlog_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
86static int
87pjdlog_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
102static int
103pjdlog_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
112static int
113pjdlog_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
156static int
157pjdlog_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
219void
220pjdlog_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
262void
263pjdlog_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 */
286void
287pjdlog_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 */
321int
322pjdlog_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 */
334void
335pjdlog_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 */
350int
351pjdlog_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 */
366void
367pjdlog_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 */
380int
381pjdlog_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 */
392void
393pjdlog_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 */
407void
408pjdlogv_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 */
426const char *
427pjdlog_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 */
438void
439pjdlog_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 */
453void
454pjdlogv_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 */
468void
469pjdlog_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 */
481static const char *
482pjdlog_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
507static int
508vsnprlcat(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
517static int
518snprlcat(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
529static void
530pjdlogv_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 */
641void
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 */
693void
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 */
709void
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 */
725void
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 */
742void
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 */
782int
783pjdlog_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