pjdlog.c revision 219370
1204076Spjd/*-
2217964Spjd * Copyright (c) 2009-2010 The FreeBSD Foundation
3217964Spjd * Copyright (c) 2011 Pawel Jakub Dawidek <pjd@FreeBSD.org>
4204076Spjd * All rights reserved.
5204076Spjd *
6204076Spjd * This software was developed by Pawel Jakub Dawidek under sponsorship from
7204076Spjd * the FreeBSD Foundation.
8204076Spjd *
9204076Spjd * Redistribution and use in source and binary forms, with or without
10204076Spjd * modification, are permitted provided that the following conditions
11204076Spjd * are met:
12204076Spjd * 1. Redistributions of source code must retain the above copyright
13204076Spjd *    notice, this list of conditions and the following disclaimer.
14204076Spjd * 2. Redistributions in binary form must reproduce the above copyright
15204076Spjd *    notice, this list of conditions and the following disclaimer in the
16204076Spjd *    documentation and/or other materials provided with the distribution.
17204076Spjd *
18204076Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19204076Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20204076Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21204076Spjd * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22204076Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23204076Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24204076Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25204076Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26204076Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27204076Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28204076Spjd * SUCH DAMAGE.
29204076Spjd */
30204076Spjd
31204076Spjd#include <sys/cdefs.h>
32204076Spjd__FBSDID("$FreeBSD: head/sbin/hastd/pjdlog.c 219370 2011-03-07 10:38:18Z pjd $");
33204076Spjd
34219370Spjd#include <sys/socket.h>
35219370Spjd#include <netinet/in.h>
36219370Spjd
37204076Spjd#include <assert.h>
38204076Spjd#include <errno.h>
39219370Spjd#include <libutil.h>
40219370Spjd#include <printf.h>
41204076Spjd#include <stdarg.h>
42219370Spjd#include <stdint.h>
43204076Spjd#include <stdio.h>
44204076Spjd#include <stdlib.h>
45204076Spjd#include <string.h>
46204076Spjd#include <syslog.h>
47204076Spjd
48204076Spjd#include "pjdlog.h"
49204076Spjd
50219369Spjd#define	PJDLOG_NEVER_INITIALIZED	0
51219369Spjd#define	PJDLOG_NOT_INITIALIZED		1
52219369Spjd#define	PJDLOG_INITIALIZED		2
53219369Spjd
54219369Spjdstatic int pjdlog_initialized = PJDLOG_NEVER_INITIALIZED;
55218040Spjdstatic int pjdlog_mode, pjdlog_debug_level;
56204076Spjdstatic char pjdlog_prefix[128];
57204076Spjd
58219370Spjdstatic int
59219370Spjdpjdlog_printf_arginfo_humanized_number(const struct printf_info *pi __unused,
60219370Spjd    size_t n, int *argt)
61219370Spjd{
62219370Spjd
63219370Spjd	assert(n >= 1);
64219370Spjd	argt[0] = PA_INT | PA_FLAG_INTMAX;
65219370Spjd	return (1);
66219370Spjd}
67219370Spjd
68219370Spjdstatic int
69219370Spjdpjdlog_printf_render_humanized_number(struct __printf_io *io,
70219370Spjd    const struct printf_info *pi, const void * const *arg)
71219370Spjd{
72219370Spjd	char buf[5];
73219370Spjd	intmax_t num;
74219370Spjd	int ret;
75219370Spjd
76219370Spjd	num = *(const intmax_t *)arg[0];
77219370Spjd	humanize_number(buf, sizeof(buf), (int64_t)num, "", HN_AUTOSCALE,
78219370Spjd	    HN_NOSPACE | HN_DECIMAL);
79219370Spjd	ret = __printf_out(io, pi, buf, strlen(buf));
80219370Spjd	__printf_flush(io);
81219370Spjd	return (ret);
82219370Spjd}
83219370Spjd
84219370Spjdstatic int
85219370Spjdpjdlog_printf_arginfo_sockaddr(const struct printf_info *pi __unused,
86219370Spjd    size_t n, int *argt)
87219370Spjd{
88219370Spjd
89219370Spjd	assert(n >= 1);
90219370Spjd	argt[0] = PA_POINTER;
91219370Spjd	return (1);
92219370Spjd}
93219370Spjd
94219370Spjdstatic int
95219370Spjdpjdlog_printf_render_sockaddr(struct __printf_io *io,
96219370Spjd    const struct printf_info *pi, const void * const *arg)
97219370Spjd{
98219370Spjd	const struct sockaddr *sa;
99219370Spjd	char buf[64];
100219370Spjd	int ret;
101219370Spjd
102219370Spjd	sa = *(const struct sockaddr * const *)arg[0];
103219370Spjd	switch (sa->sa_family) {
104219370Spjd	case AF_INET:
105219370Spjd	    {
106219370Spjd		const struct sockaddr_in *sin;
107219370Spjd		in_addr_t ip;
108219370Spjd		unsigned int port;
109219370Spjd
110219370Spjd		sin = (const struct sockaddr_in *)sa;
111219370Spjd		ip = ntohl(sin->sin_addr.s_addr);
112219370Spjd		port = ntohs(sin->sin_port);
113219370Spjd
114219370Spjd		snprintf(buf, sizeof(buf), "%u.%u.%u.%u:%u",
115219370Spjd		    ((ip >> 24) & 0xff), ((ip >> 16) & 0xff),
116219370Spjd		    ((ip >> 8) & 0xff), (ip & 0xff), port);
117219370Spjd		break;
118219370Spjd	    }
119219370Spjd	default:
120219370Spjd		snprintf(buf, sizeof(buf), "[unsupported family %u]",
121219370Spjd		    (unsigned int)sa->sa_family);
122219370Spjd		break;
123219370Spjd	}
124219370Spjd	ret = __printf_out(io, pi, buf, strlen(buf));
125219370Spjd	__printf_flush(io);
126219370Spjd	return (ret);
127219370Spjd}
128219370Spjd
129217965Spjdvoid
130217965Spjdpjdlog_init(int mode)
131217965Spjd{
132217965Spjd
133219369Spjd	assert(pjdlog_initialized == PJDLOG_NEVER_INITIALIZED ||
134219369Spjd	    pjdlog_initialized == PJDLOG_NOT_INITIALIZED);
135217965Spjd	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
136217965Spjd
137219370Spjd	if (pjdlog_initialized == PJDLOG_NEVER_INITIALIZED) {
138219370Spjd		__use_xprintf = 1;
139219370Spjd		register_printf_render_std("T");
140219370Spjd		register_printf_render('N',
141219370Spjd		    pjdlog_printf_render_humanized_number,
142219370Spjd		    pjdlog_printf_arginfo_humanized_number);
143219370Spjd		register_printf_render('S',
144219370Spjd		    pjdlog_printf_render_sockaddr,
145219370Spjd		    pjdlog_printf_arginfo_sockaddr);
146219370Spjd	}
147219370Spjd
148217965Spjd	if (mode == PJDLOG_MODE_SYSLOG)
149217965Spjd		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
150217965Spjd	pjdlog_mode = mode;
151218040Spjd	pjdlog_debug_level = 0;
152218040Spjd	bzero(pjdlog_prefix, sizeof(pjdlog_prefix));
153217965Spjd
154219369Spjd	pjdlog_initialized = PJDLOG_INITIALIZED;
155217965Spjd}
156217965Spjd
157217965Spjdvoid
158217965Spjdpjdlog_fini(void)
159217965Spjd{
160217965Spjd
161219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
162217965Spjd
163217965Spjd	if (pjdlog_mode == PJDLOG_MODE_SYSLOG)
164217965Spjd		closelog();
165217965Spjd
166219369Spjd	pjdlog_initialized = PJDLOG_NOT_INITIALIZED;
167217965Spjd}
168217965Spjd
169204076Spjd/*
170204076Spjd * Configure where the logs should go.
171204076Spjd * By default they are send to stdout/stderr, but after going into background
172204076Spjd * (eg. by calling daemon(3)) application is responsible for changing mode to
173204076Spjd * PJDLOG_MODE_SYSLOG, so logs will be send to syslog.
174204076Spjd */
175204076Spjdvoid
176204076Spjdpjdlog_mode_set(int mode)
177204076Spjd{
178204076Spjd
179219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
180204076Spjd	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
181204076Spjd
182217965Spjd	if (pjdlog_mode == mode)
183217965Spjd		return;
184212052Spjd
185212052Spjd	if (mode == PJDLOG_MODE_SYSLOG)
186217962Spjd		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
187217965Spjd	else /* if (mode == PJDLOG_MODE_STD) */
188217965Spjd		closelog();
189217965Spjd
190217965Spjd	pjdlog_mode = mode;
191204076Spjd}
192204076Spjd
193204076Spjd/*
194204076Spjd * Return current mode.
195204076Spjd */
196204076Spjdint
197204076Spjdpjdlog_mode_get(void)
198204076Spjd{
199204076Spjd
200219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
201217965Spjd
202204076Spjd	return (pjdlog_mode);
203204076Spjd}
204204076Spjd
205204076Spjd/*
206204076Spjd * Set debug level. All the logs above the level specified here will be
207204076Spjd * ignored.
208204076Spjd */
209204076Spjdvoid
210204076Spjdpjdlog_debug_set(int level)
211204076Spjd{
212204076Spjd
213219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
214204076Spjd	assert(level >= 0);
215204076Spjd
216204076Spjd	pjdlog_debug_level = level;
217204076Spjd}
218204076Spjd
219204076Spjd/*
220204076Spjd * Return current debug level.
221204076Spjd */
222204076Spjdint
223204076Spjdpjdlog_debug_get(void)
224204076Spjd{
225204076Spjd
226219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
227217965Spjd
228204076Spjd	return (pjdlog_debug_level);
229204076Spjd}
230204076Spjd
231204076Spjd/*
232204076Spjd * Set prefix that will be used before each log.
233204076Spjd * Setting prefix to NULL will remove it.
234204076Spjd */
235204076Spjdvoid
236204076Spjdpjdlog_prefix_set(const char *fmt, ...)
237204076Spjd{
238204076Spjd	va_list ap;
239204076Spjd
240219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
241217965Spjd
242204076Spjd	va_start(ap, fmt);
243217731Spjd	pjdlogv_prefix_set(fmt, ap);
244204076Spjd	va_end(ap);
245204076Spjd}
246204076Spjd
247204076Spjd/*
248204076Spjd * Set prefix that will be used before each log.
249204076Spjd * Setting prefix to NULL will remove it.
250204076Spjd */
251204076Spjdvoid
252217731Spjdpjdlogv_prefix_set(const char *fmt, va_list ap)
253204076Spjd{
254204076Spjd
255219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
256204076Spjd	assert(fmt != NULL);
257204076Spjd
258204076Spjd	vsnprintf(pjdlog_prefix, sizeof(pjdlog_prefix), fmt, ap);
259204076Spjd}
260204076Spjd
261204076Spjd/*
262204076Spjd * Convert log level into string.
263204076Spjd */
264204076Spjdstatic const char *
265204076Spjdpjdlog_level_string(int loglevel)
266204076Spjd{
267204076Spjd
268204076Spjd	switch (loglevel) {
269204076Spjd	case LOG_EMERG:
270204076Spjd		return ("EMERG");
271204076Spjd	case LOG_ALERT:
272204076Spjd		return ("ALERT");
273204076Spjd	case LOG_CRIT:
274204076Spjd		return ("CRIT");
275204076Spjd	case LOG_ERR:
276204076Spjd		return ("ERROR");
277204076Spjd	case LOG_WARNING:
278204076Spjd		return ("WARNING");
279204076Spjd	case LOG_NOTICE:
280204076Spjd		return ("NOTICE");
281204076Spjd	case LOG_INFO:
282204076Spjd		return ("INFO");
283204076Spjd	case LOG_DEBUG:
284204076Spjd		return ("DEBUG");
285204076Spjd	}
286204076Spjd	assert(!"Invalid log level.");
287204076Spjd	abort();	/* XXX: gcc */
288204076Spjd}
289204076Spjd
290204076Spjd/*
291204076Spjd * Common log routine.
292204076Spjd */
293204076Spjdvoid
294204076Spjdpjdlog_common(int loglevel, int debuglevel, int error, const char *fmt, ...)
295204076Spjd{
296204076Spjd	va_list ap;
297204076Spjd
298219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
299217965Spjd
300204076Spjd	va_start(ap, fmt);
301204076Spjd	pjdlogv_common(loglevel, debuglevel, error, fmt, ap);
302204076Spjd	va_end(ap);
303204076Spjd}
304204076Spjd
305204076Spjd/*
306204076Spjd * Common log routine, which can handle regular log level as well as debug
307204076Spjd * level. We decide here where to send the logs (stdout/stderr or syslog).
308204076Spjd */
309204076Spjdvoid
310204076Spjdpjdlogv_common(int loglevel, int debuglevel, int error, const char *fmt,
311204076Spjd    va_list ap)
312204076Spjd{
313204076Spjd
314219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
315204076Spjd	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
316204076Spjd	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
317204076Spjd	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
318204076Spjd	    loglevel == LOG_INFO || loglevel == LOG_DEBUG);
319204076Spjd	assert(loglevel != LOG_DEBUG || debuglevel > 0);
320204076Spjd	assert(error >= -1);
321204076Spjd
322204076Spjd	/* Ignore debug above configured level. */
323204076Spjd	if (loglevel == LOG_DEBUG && debuglevel > pjdlog_debug_level)
324204076Spjd		return;
325204076Spjd
326204076Spjd	switch (pjdlog_mode) {
327204076Spjd	case PJDLOG_MODE_STD:
328204076Spjd	    {
329204076Spjd		FILE *out;
330204076Spjd
331204076Spjd		/*
332204076Spjd		 * We send errors and warning to stderr and the rest to stdout.
333204076Spjd		 */
334204076Spjd		switch (loglevel) {
335204076Spjd		case LOG_EMERG:
336204076Spjd		case LOG_ALERT:
337204076Spjd		case LOG_CRIT:
338204076Spjd		case LOG_ERR:
339204076Spjd		case LOG_WARNING:
340204076Spjd			out = stderr;
341204076Spjd			break;
342204076Spjd		case LOG_NOTICE:
343204076Spjd		case LOG_INFO:
344204076Spjd		case LOG_DEBUG:
345204076Spjd			out = stdout;
346204076Spjd			break;
347204076Spjd		default:
348204076Spjd			assert(!"Invalid loglevel.");
349204076Spjd			abort();	/* XXX: gcc */
350204076Spjd		}
351204076Spjd
352204076Spjd		fprintf(out, "[%s]", pjdlog_level_string(loglevel));
353204076Spjd		/* Attach debuglevel if this is debug log. */
354204076Spjd		if (loglevel == LOG_DEBUG)
355204076Spjd			fprintf(out, "[%d]", debuglevel);
356213939Spjd		fprintf(out, " %s", pjdlog_prefix);
357204076Spjd		vfprintf(out, fmt, ap);
358204076Spjd		if (error != -1)
359204076Spjd			fprintf(out, ": %s.", strerror(error));
360204076Spjd		fprintf(out, "\n");
361211898Spjd		fflush(out);
362204076Spjd		break;
363204076Spjd	    }
364204076Spjd	case PJDLOG_MODE_SYSLOG:
365204076Spjd	    {
366204076Spjd		char log[1024];
367204076Spjd		int len;
368204076Spjd
369204076Spjd		len = snprintf(log, sizeof(log), "%s", pjdlog_prefix);
370204076Spjd		if ((size_t)len < sizeof(log))
371206697Spjd			len += vsnprintf(log + len, sizeof(log) - len, fmt, ap);
372204076Spjd		if (error != -1 && (size_t)len < sizeof(log)) {
373204076Spjd			(void)snprintf(log + len, sizeof(log) - len, ": %s.",
374204076Spjd			    strerror(error));
375204076Spjd		}
376204076Spjd		syslog(loglevel, "%s", log);
377204076Spjd		break;
378204076Spjd	    }
379204076Spjd	default:
380204076Spjd		assert(!"Invalid mode.");
381204076Spjd	}
382204076Spjd}
383204076Spjd
384204076Spjd/*
385204076Spjd * Regular logs.
386204076Spjd */
387204076Spjdvoid
388204076Spjdpjdlogv(int loglevel, const char *fmt, va_list ap)
389204076Spjd{
390204076Spjd
391219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
392217965Spjd
393204076Spjd	/* LOG_DEBUG is invalid here, pjdlogv?_debug() should be used. */
394204076Spjd	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
395204076Spjd	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
396204076Spjd	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
397204076Spjd	    loglevel == LOG_INFO);
398204076Spjd
399204076Spjd	pjdlogv_common(loglevel, 0, -1, fmt, ap);
400204076Spjd}
401204076Spjd
402204076Spjd/*
403204076Spjd * Regular logs.
404204076Spjd */
405204076Spjdvoid
406204076Spjdpjdlog(int loglevel, const char *fmt, ...)
407204076Spjd{
408204076Spjd	va_list ap;
409204076Spjd
410219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
411217965Spjd
412204076Spjd	va_start(ap, fmt);
413204076Spjd	pjdlogv(loglevel, fmt, ap);
414204076Spjd	va_end(ap);
415204076Spjd}
416204076Spjd
417204076Spjd/*
418204076Spjd * Debug logs.
419204076Spjd */
420204076Spjdvoid
421204076Spjdpjdlogv_debug(int debuglevel, const char *fmt, va_list ap)
422204076Spjd{
423204076Spjd
424219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
425217965Spjd
426204076Spjd	pjdlogv_common(LOG_DEBUG, debuglevel, -1, fmt, ap);
427204076Spjd}
428204076Spjd
429204076Spjd/*
430204076Spjd * Debug logs.
431204076Spjd */
432204076Spjdvoid
433204076Spjdpjdlog_debug(int debuglevel, const char *fmt, ...)
434204076Spjd{
435204076Spjd	va_list ap;
436204076Spjd
437219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
438217965Spjd
439204076Spjd	va_start(ap, fmt);
440204076Spjd	pjdlogv_debug(debuglevel, fmt, ap);
441204076Spjd	va_end(ap);
442204076Spjd}
443204076Spjd
444204076Spjd/*
445204076Spjd * Error logs with errno logging.
446204076Spjd */
447204076Spjdvoid
448204076Spjdpjdlogv_errno(int loglevel, const char *fmt, va_list ap)
449204076Spjd{
450204076Spjd
451219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
452217965Spjd
453204076Spjd	pjdlogv_common(loglevel, 0, errno, fmt, ap);
454204076Spjd}
455204076Spjd
456204076Spjd/*
457204076Spjd * Error logs with errno logging.
458204076Spjd */
459204076Spjdvoid
460204076Spjdpjdlog_errno(int loglevel, const char *fmt, ...)
461204076Spjd{
462204076Spjd	va_list ap;
463204076Spjd
464219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
465217965Spjd
466204076Spjd	va_start(ap, fmt);
467204076Spjd	pjdlogv_errno(loglevel, fmt, ap);
468204076Spjd	va_end(ap);
469204076Spjd}
470204076Spjd
471204076Spjd/*
472204076Spjd * Log error, errno and exit.
473204076Spjd */
474204076Spjdvoid
475204076Spjdpjdlogv_exit(int exitcode, const char *fmt, va_list ap)
476204076Spjd{
477204076Spjd
478219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
479217965Spjd
480204076Spjd	pjdlogv_errno(LOG_ERR, fmt, ap);
481204076Spjd	exit(exitcode);
482210872Spjd	/* NOTREACHED */
483204076Spjd}
484204076Spjd
485204076Spjd/*
486204076Spjd * Log error, errno and exit.
487204076Spjd */
488204076Spjdvoid
489204076Spjdpjdlog_exit(int exitcode, const char *fmt, ...)
490204076Spjd{
491204076Spjd	va_list ap;
492204076Spjd
493219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
494217965Spjd
495204076Spjd	va_start(ap, fmt);
496204076Spjd	pjdlogv_exit(exitcode, fmt, ap);
497204076Spjd	/* NOTREACHED */
498204076Spjd	va_end(ap);
499204076Spjd}
500204076Spjd
501204076Spjd/*
502204076Spjd * Log error and exit.
503204076Spjd */
504204076Spjdvoid
505204076Spjdpjdlogv_exitx(int exitcode, const char *fmt, va_list ap)
506204076Spjd{
507204076Spjd
508219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
509217965Spjd
510204076Spjd	pjdlogv(LOG_ERR, fmt, ap);
511204076Spjd	exit(exitcode);
512210872Spjd	/* NOTREACHED */
513204076Spjd}
514204076Spjd
515204076Spjd/*
516204076Spjd * Log error and exit.
517204076Spjd */
518204076Spjdvoid
519204076Spjdpjdlog_exitx(int exitcode, const char *fmt, ...)
520204076Spjd{
521204076Spjd	va_list ap;
522204076Spjd
523219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
524217965Spjd
525204076Spjd	va_start(ap, fmt);
526204076Spjd	pjdlogv_exitx(exitcode, fmt, ap);
527204076Spjd	/* NOTREACHED */
528204076Spjd	va_end(ap);
529204076Spjd}
530210875Spjd
531210875Spjd/*
532218132Spjd * Log failure message and exit.
533210875Spjd */
534210875Spjdvoid
535218132Spjdpjdlog_abort(const char *func, const char *file, int line,
536217966Spjd    const char *failedexpr, const char *fmt, ...)
537210875Spjd{
538217966Spjd	va_list ap;
539210875Spjd
540219369Spjd	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
541217966Spjd
542217966Spjd	/*
543217966Spjd	 * When there is no message we pass __func__ as 'fmt'.
544217966Spjd	 * It would be cleaner to pass NULL or "", but gcc generates a warning
545217966Spjd	 * for both of those.
546217966Spjd	 */
547217966Spjd	if (fmt != func) {
548217966Spjd		va_start(ap, fmt);
549217966Spjd		pjdlogv_critical(fmt, ap);
550217966Spjd		va_end(ap);
551217966Spjd	}
552217966Spjd	if (failedexpr == NULL) {
553217966Spjd		if (func == NULL) {
554217966Spjd			pjdlog_critical("Aborted at file %s, line %d.", file,
555217966Spjd			    line);
556217966Spjd		} else {
557217966Spjd			pjdlog_critical("Aborted at function %s, file %s, line %d.",
558217966Spjd			    func, file, line);
559217966Spjd		}
560210875Spjd	} else {
561217966Spjd		if (func == NULL) {
562217966Spjd			pjdlog_critical("Assertion failed: (%s), file %s, line %d.",
563217966Spjd			    failedexpr, file, line);
564217966Spjd		} else {
565217966Spjd			pjdlog_critical("Assertion failed: (%s), function %s, file %s, line %d.",
566217966Spjd			    failedexpr, func, file, line);
567217966Spjd		}
568210875Spjd	}
569210875Spjd	abort();
570210875Spjd}
571