pjdlog.c revision 222087
1224110Sjchandra/*-
2224110Sjchandra * Copyright (c) 2009-2010 The FreeBSD Foundation
3224110Sjchandra * Copyright (c) 2011 Pawel Jakub Dawidek <pjd@FreeBSD.org>
4224110Sjchandra * All rights reserved.
5224110Sjchandra *
6224110Sjchandra * This software was developed by Pawel Jakub Dawidek under sponsorship from
7224110Sjchandra * the FreeBSD Foundation.
8224110Sjchandra *
9224110Sjchandra * Redistribution and use in source and binary forms, with or without
10224110Sjchandra * modification, are permitted provided that the following conditions
11224110Sjchandra * are met:
12224110Sjchandra * 1. Redistributions of source code must retain the above copyright
13224110Sjchandra *    notice, this list of conditions and the following disclaimer.
14224110Sjchandra * 2. Redistributions in binary form must reproduce the above copyright
15224110Sjchandra *    notice, this list of conditions and the following disclaimer in the
16224110Sjchandra *    documentation and/or other materials provided with the distribution.
17224110Sjchandra *
18224110Sjchandra * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19224110Sjchandra * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20224110Sjchandra * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21224110Sjchandra * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22224110Sjchandra * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23224110Sjchandra * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24224110Sjchandra * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25224110Sjchandra * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26224110Sjchandra * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27224110Sjchandra * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28225394Sjchandra * SUCH DAMAGE.
29224110Sjchandra */
30225394Sjchandra
31224110Sjchandra#include <sys/cdefs.h>
32225394Sjchandra__FBSDID("$FreeBSD: head/sbin/hastd/pjdlog.c 222087 2011-05-18 22:43:56Z pjd $");
33227783Sjchandra
34224110Sjchandra#include <sys/types.h>
35224110Sjchandra#include <sys/socket.h>
36227783Sjchandra#include <netinet/in.h>
37224110Sjchandra#include <arpa/inet.h>
38225394Sjchandra
39227783Sjchandra#include <assert.h>
40227783Sjchandra#include <errno.h>
41227783Sjchandra#include <libutil.h>
42227783Sjchandra#include <printf.h>
43227783Sjchandra#include <stdarg.h>
44227783Sjchandra#include <stdint.h>
45227783Sjchandra#include <stdio.h>
46227783Sjchandra#include <stdlib.h>
47227783Sjchandra#include <string.h>
48224110Sjchandra#include <syslog.h>
49225394Sjchandra
50227783Sjchandra#include "pjdlog.h"
51227783Sjchandra
52227783Sjchandra#define	PJDLOG_NEVER_INITIALIZED	0
53227783Sjchandra#define	PJDLOG_NOT_INITIALIZED		1
54227783Sjchandra#define	PJDLOG_INITIALIZED		2
55224110Sjchandra
56225394Sjchandrastatic int pjdlog_initialized = PJDLOG_NEVER_INITIALIZED;
57227783Sjchandrastatic int pjdlog_mode, pjdlog_debug_level;
58227783Sjchandrastatic char pjdlog_prefix[128];
59227783Sjchandra
60227783Sjchandrastatic int
61224110Sjchandrapjdlog_printf_arginfo_humanized_number(const struct printf_info *pi __unused,
62225394Sjchandra    size_t n, int *argt)
63227783Sjchandra{
64227783Sjchandra
65227783Sjchandra	assert(n >= 1);
66227783Sjchandra	argt[0] = PA_INT | PA_FLAG_INTMAX;
67227783Sjchandra	return (1);
68227783Sjchandra}
69227783Sjchandra
70224110Sjchandrastatic int
71227783Sjchandrapjdlog_printf_render_humanized_number(struct __printf_io *io,
72227783Sjchandra    const struct printf_info *pi, const void * const *arg)
73227783Sjchandra{
74227783Sjchandra	char buf[5];
75227783Sjchandra	intmax_t num;
76227783Sjchandra	int ret;
77227783Sjchandra
78227783Sjchandra	num = *(const intmax_t *)arg[0];
79227783Sjchandra	humanize_number(buf, sizeof(buf), (int64_t)num, "", HN_AUTOSCALE,
80227783Sjchandra	    HN_NOSPACE | HN_DECIMAL);
81227783Sjchandra	ret = __printf_out(io, pi, buf, strlen(buf));
82224110Sjchandra	__printf_flush(io);
83227783Sjchandra	return (ret);
84227783Sjchandra}
85227783Sjchandra
86227783Sjchandrastatic int
87227783Sjchandrapjdlog_printf_arginfo_sockaddr(const struct printf_info *pi __unused,
88227783Sjchandra    size_t n, int *argt)
89227783Sjchandra{
90224110Sjchandra
91227783Sjchandra	assert(n >= 1);
92227783Sjchandra	argt[0] = PA_POINTER;
93227783Sjchandra	return (1);
94227783Sjchandra}
95227783Sjchandra
96227783Sjchandrastatic int
97227783Sjchandrapjdlog_printf_render_sockaddr(struct __printf_io *io,
98224110Sjchandra    const struct printf_info *pi, const void * const *arg)
99227783Sjchandra{
100227783Sjchandra	const struct sockaddr_storage *ss;
101227783Sjchandra	char buf[64];
102227783Sjchandra	int ret;
103227783Sjchandra
104227783Sjchandra	ss = *(const struct sockaddr_storage * const *)arg[0];
105227783Sjchandra	switch (ss->ss_family) {
106224110Sjchandra	case AF_INET:
107227783Sjchandra	    {
108227783Sjchandra		char addr[INET_ADDRSTRLEN];
109227783Sjchandra		const struct sockaddr_in *sin;
110227783Sjchandra		unsigned int port;
111227783Sjchandra
112227783Sjchandra		sin = (const struct sockaddr_in *)ss;
113227783Sjchandra		port = ntohs(sin->sin_port);
114227783Sjchandra		if (inet_ntop(ss->ss_family, &sin->sin_addr, addr,
115227783Sjchandra		    sizeof(addr)) == NULL) {
116224110Sjchandra			PJDLOG_ABORT("inet_ntop(AF_INET) failed: %s.",
117227783Sjchandra			    strerror(errno));
118227783Sjchandra		}
119227783Sjchandra		snprintf(buf, sizeof(buf), "%s:%u", addr, port);
120227783Sjchandra		break;
121227783Sjchandra	    }
122227783Sjchandra	case AF_INET6:
123227783Sjchandra	    {
124227783Sjchandra		char addr[INET6_ADDRSTRLEN];
125227783Sjchandra		const struct sockaddr_in6 *sin;
126224110Sjchandra		unsigned int port;
127227783Sjchandra
128227783Sjchandra		sin = (const struct sockaddr_in6 *)ss;
129227783Sjchandra		port = ntohs(sin->sin6_port);
130227783Sjchandra		if (inet_ntop(ss->ss_family, &sin->sin6_addr, addr,
131227783Sjchandra		    sizeof(addr)) == NULL) {
132227783Sjchandra			PJDLOG_ABORT("inet_ntop(AF_INET6) failed: %s.",
133227783Sjchandra			    strerror(errno));
134227783Sjchandra		}
135227783Sjchandra		snprintf(buf, sizeof(buf), "[%s]:%u", addr, port);
136225394Sjchandra		break;
137227783Sjchandra	    }
138227783Sjchandra	default:
139227783Sjchandra		snprintf(buf, sizeof(buf), "[unsupported family %hhu]",
140227783Sjchandra		    ss->ss_family);
141227783Sjchandra		break;
142227783Sjchandra	}
143227783Sjchandra	ret = __printf_out(io, pi, buf, strlen(buf));
144227783Sjchandra	__printf_flush(io);
145227783Sjchandra	return (ret);
146225394Sjchandra}
147227783Sjchandra
148227783Sjchandravoid
149225394Sjchandrapjdlog_init(int mode)
150227783Sjchandra{
151225394Sjchandra
152225394Sjchandra	assert(pjdlog_initialized == PJDLOG_NEVER_INITIALIZED ||
153225394Sjchandra	    pjdlog_initialized == PJDLOG_NOT_INITIALIZED);
154225394Sjchandra	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
155227783Sjchandra
156227783Sjchandra	if (pjdlog_initialized == PJDLOG_NEVER_INITIALIZED) {
157227783Sjchandra		__use_xprintf = 1;
158227783Sjchandra		register_printf_render_std("T");
159227783Sjchandra		register_printf_render('N',
160227783Sjchandra		    pjdlog_printf_render_humanized_number,
161227783Sjchandra		    pjdlog_printf_arginfo_humanized_number);
162227783Sjchandra		register_printf_render('S',
163227783Sjchandra		    pjdlog_printf_render_sockaddr,
164227783Sjchandra		    pjdlog_printf_arginfo_sockaddr);
165227783Sjchandra	}
166227783Sjchandra
167227783Sjchandra	if (mode == PJDLOG_MODE_SYSLOG)
168227783Sjchandra		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
169225394Sjchandra	pjdlog_mode = mode;
170227783Sjchandra	pjdlog_debug_level = 0;
171225394Sjchandra	bzero(pjdlog_prefix, sizeof(pjdlog_prefix));
172225394Sjchandra
173224110Sjchandra	pjdlog_initialized = PJDLOG_INITIALIZED;
174225394Sjchandra}
175225394Sjchandra
176225394Sjchandravoid
177227783Sjchandrapjdlog_fini(void)
178227783Sjchandra{
179227783Sjchandra
180224110Sjchandra	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
181227783Sjchandra
182227783Sjchandra	if (pjdlog_mode == PJDLOG_MODE_SYSLOG)
183227783Sjchandra		closelog();
184227783Sjchandra
185224110Sjchandra	pjdlog_initialized = PJDLOG_NOT_INITIALIZED;
186224110Sjchandra}
187225394Sjchandra
188225394Sjchandra/*
189224110Sjchandra * Configure where the logs should go.
190225394Sjchandra * By default they are send to stdout/stderr, but after going into background
191224110Sjchandra * (eg. by calling daemon(3)) application is responsible for changing mode to
192224110Sjchandra * PJDLOG_MODE_SYSLOG, so logs will be send to syslog.
193225394Sjchandra */
194225394Sjchandravoid
195224110Sjchandrapjdlog_mode_set(int mode)
196225394Sjchandra{
197225394Sjchandra
198224110Sjchandra	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
199225394Sjchandra	assert(mode == PJDLOG_MODE_STD || mode == PJDLOG_MODE_SYSLOG);
200225394Sjchandra
201225394Sjchandra	if (pjdlog_mode == mode)
202225394Sjchandra		return;
203225394Sjchandra
204225394Sjchandra	if (mode == PJDLOG_MODE_SYSLOG)
205224110Sjchandra		openlog(NULL, LOG_PID | LOG_NDELAY, LOG_DAEMON);
206225394Sjchandra	else /* if (mode == PJDLOG_MODE_STD) */
207224110Sjchandra		closelog();
208224110Sjchandra
209225394Sjchandra	pjdlog_mode = mode;
210225394Sjchandra}
211224110Sjchandra
212225394Sjchandra/*
213224110Sjchandra * Return current mode.
214224110Sjchandra */
215225394Sjchandraint
216225394Sjchandrapjdlog_mode_get(void)
217224110Sjchandra{
218225394Sjchandra
219224110Sjchandra	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
220224110Sjchandra
221225394Sjchandra	return (pjdlog_mode);
222225394Sjchandra}
223224110Sjchandra
224225394Sjchandra/*
225224110Sjchandra * Set debug level. All the logs above the level specified here will be
226225394Sjchandra * ignored.
227225394Sjchandra */
228224110Sjchandravoid
229224110Sjchandrapjdlog_debug_set(int level)
230225394Sjchandra{
231225394Sjchandra
232224110Sjchandra	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
233225394Sjchandra	assert(level >= 0);
234224110Sjchandra
235225394Sjchandra	pjdlog_debug_level = level;
236225394Sjchandra}
237225394Sjchandra
238224110Sjchandra/*
239224110Sjchandra * Return current debug level.
240225394Sjchandra */
241225394Sjchandraint
242224110Sjchandrapjdlog_debug_get(void)
243225394Sjchandra{
244224110Sjchandra
245225394Sjchandra	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
246225394Sjchandra
247225394Sjchandra	return (pjdlog_debug_level);
248225394Sjchandra}
249225394Sjchandra
250224110Sjchandra/*
251224110Sjchandra * Set prefix that will be used before each log.
252225394Sjchandra * Setting prefix to NULL will remove it.
253225394Sjchandra */
254225394Sjchandravoid
255224110Sjchandrapjdlog_prefix_set(const char *fmt, ...)
256225394Sjchandra{
257224110Sjchandra	va_list ap;
258225394Sjchandra
259225394Sjchandra	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
260225394Sjchandra
261225394Sjchandra	va_start(ap, fmt);
262224110Sjchandra	pjdlogv_prefix_set(fmt, ap);
263225394Sjchandra	va_end(ap);
264224110Sjchandra}
265224110Sjchandra
266225394Sjchandra/*
267225394Sjchandra * Set prefix that will be used before each log.
268225394Sjchandra * Setting prefix to NULL will remove it.
269224110Sjchandra */
270225394Sjchandravoid
271225394Sjchandrapjdlogv_prefix_set(const char *fmt, va_list ap)
272225394Sjchandra{
273224110Sjchandra
274224110Sjchandra	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
275225394Sjchandra	assert(fmt != NULL);
276225394Sjchandra
277225394Sjchandra	vsnprintf(pjdlog_prefix, sizeof(pjdlog_prefix), fmt, ap);
278225394Sjchandra}
279225394Sjchandra
280224110Sjchandra/*
281225394Sjchandra * Convert log level into string.
282225394Sjchandra */
283224110Sjchandrastatic const char *
284225394Sjchandrapjdlog_level_string(int loglevel)
285224110Sjchandra{
286224110Sjchandra
287225394Sjchandra	switch (loglevel) {
288225394Sjchandra	case LOG_EMERG:
289224110Sjchandra		return ("EMERG");
290227783Sjchandra	case LOG_ALERT:
291227783Sjchandra		return ("ALERT");
292224110Sjchandra	case LOG_CRIT:
293227783Sjchandra		return ("CRIT");
294227783Sjchandra	case LOG_ERR:
295227783Sjchandra		return ("ERROR");
296227783Sjchandra	case LOG_WARNING:
297227783Sjchandra		return ("WARNING");
298227783Sjchandra	case LOG_NOTICE:
299227783Sjchandra		return ("NOTICE");
300225394Sjchandra	case LOG_INFO:
301225394Sjchandra		return ("INFO");
302227783Sjchandra	case LOG_DEBUG:
303225394Sjchandra		return ("DEBUG");
304225394Sjchandra	}
305227783Sjchandra	assert(!"Invalid log level.");
306225394Sjchandra	abort();	/* XXX: gcc */
307225394Sjchandra}
308224110Sjchandra
309224110Sjchandra/*
310225394Sjchandra * Common log routine.
311225394Sjchandra */
312void
313pjdlog_common(int loglevel, int debuglevel, int error, const char *fmt, ...)
314{
315	va_list ap;
316
317	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
318
319	va_start(ap, fmt);
320	pjdlogv_common(loglevel, debuglevel, error, fmt, ap);
321	va_end(ap);
322}
323
324/*
325 * Common log routine, which can handle regular log level as well as debug
326 * level. We decide here where to send the logs (stdout/stderr or syslog).
327 */
328void
329pjdlogv_common(int loglevel, int debuglevel, int error, const char *fmt,
330    va_list ap)
331{
332
333	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
334	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
335	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
336	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
337	    loglevel == LOG_INFO || loglevel == LOG_DEBUG);
338	assert(loglevel != LOG_DEBUG || debuglevel > 0);
339	assert(error >= -1);
340
341	/* Ignore debug above configured level. */
342	if (loglevel == LOG_DEBUG && debuglevel > pjdlog_debug_level)
343		return;
344
345	switch (pjdlog_mode) {
346	case PJDLOG_MODE_STD:
347	    {
348		FILE *out;
349
350		/*
351		 * We send errors and warning to stderr and the rest to stdout.
352		 */
353		switch (loglevel) {
354		case LOG_EMERG:
355		case LOG_ALERT:
356		case LOG_CRIT:
357		case LOG_ERR:
358		case LOG_WARNING:
359			out = stderr;
360			break;
361		case LOG_NOTICE:
362		case LOG_INFO:
363		case LOG_DEBUG:
364			out = stdout;
365			break;
366		default:
367			assert(!"Invalid loglevel.");
368			abort();	/* XXX: gcc */
369		}
370
371		fprintf(out, "[%s]", pjdlog_level_string(loglevel));
372		/* Attach debuglevel if this is debug log. */
373		if (loglevel == LOG_DEBUG)
374			fprintf(out, "[%d]", debuglevel);
375		fprintf(out, " %s", pjdlog_prefix);
376		vfprintf(out, fmt, ap);
377		if (error != -1)
378			fprintf(out, ": %s.", strerror(error));
379		fprintf(out, "\n");
380		fflush(out);
381		break;
382	    }
383	case PJDLOG_MODE_SYSLOG:
384	    {
385		char log[1024];
386		int len;
387
388		len = snprintf(log, sizeof(log), "%s", pjdlog_prefix);
389		if ((size_t)len < sizeof(log))
390			len += vsnprintf(log + len, sizeof(log) - len, fmt, ap);
391		if (error != -1 && (size_t)len < sizeof(log)) {
392			(void)snprintf(log + len, sizeof(log) - len, ": %s.",
393			    strerror(error));
394		}
395		syslog(loglevel, "%s", log);
396		break;
397	    }
398	default:
399		assert(!"Invalid mode.");
400	}
401}
402
403/*
404 * Regular logs.
405 */
406void
407pjdlogv(int loglevel, const char *fmt, va_list ap)
408{
409
410	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
411
412	/* LOG_DEBUG is invalid here, pjdlogv?_debug() should be used. */
413	assert(loglevel == LOG_EMERG || loglevel == LOG_ALERT ||
414	    loglevel == LOG_CRIT || loglevel == LOG_ERR ||
415	    loglevel == LOG_WARNING || loglevel == LOG_NOTICE ||
416	    loglevel == LOG_INFO);
417
418	pjdlogv_common(loglevel, 0, -1, fmt, ap);
419}
420
421/*
422 * Regular logs.
423 */
424void
425pjdlog(int loglevel, const char *fmt, ...)
426{
427	va_list ap;
428
429	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
430
431	va_start(ap, fmt);
432	pjdlogv(loglevel, fmt, ap);
433	va_end(ap);
434}
435
436/*
437 * Debug logs.
438 */
439void
440pjdlogv_debug(int debuglevel, const char *fmt, va_list ap)
441{
442
443	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
444
445	pjdlogv_common(LOG_DEBUG, debuglevel, -1, fmt, ap);
446}
447
448/*
449 * Debug logs.
450 */
451void
452pjdlog_debug(int debuglevel, const char *fmt, ...)
453{
454	va_list ap;
455
456	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
457
458	va_start(ap, fmt);
459	pjdlogv_debug(debuglevel, fmt, ap);
460	va_end(ap);
461}
462
463/*
464 * Error logs with errno logging.
465 */
466void
467pjdlogv_errno(int loglevel, const char *fmt, va_list ap)
468{
469
470	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
471
472	pjdlogv_common(loglevel, 0, errno, fmt, ap);
473}
474
475/*
476 * Error logs with errno logging.
477 */
478void
479pjdlog_errno(int loglevel, const char *fmt, ...)
480{
481	va_list ap;
482
483	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
484
485	va_start(ap, fmt);
486	pjdlogv_errno(loglevel, fmt, ap);
487	va_end(ap);
488}
489
490/*
491 * Log error, errno and exit.
492 */
493void
494pjdlogv_exit(int exitcode, const char *fmt, va_list ap)
495{
496
497	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
498
499	pjdlogv_errno(LOG_ERR, fmt, ap);
500	exit(exitcode);
501	/* NOTREACHED */
502}
503
504/*
505 * Log error, errno and exit.
506 */
507void
508pjdlog_exit(int exitcode, const char *fmt, ...)
509{
510	va_list ap;
511
512	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
513
514	va_start(ap, fmt);
515	pjdlogv_exit(exitcode, fmt, ap);
516	/* NOTREACHED */
517	va_end(ap);
518}
519
520/*
521 * Log error and exit.
522 */
523void
524pjdlogv_exitx(int exitcode, const char *fmt, va_list ap)
525{
526
527	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
528
529	pjdlogv(LOG_ERR, fmt, ap);
530	exit(exitcode);
531	/* NOTREACHED */
532}
533
534/*
535 * Log error and exit.
536 */
537void
538pjdlog_exitx(int exitcode, const char *fmt, ...)
539{
540	va_list ap;
541
542	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
543
544	va_start(ap, fmt);
545	pjdlogv_exitx(exitcode, fmt, ap);
546	/* NOTREACHED */
547	va_end(ap);
548}
549
550/*
551 * Log failure message and exit.
552 */
553void
554pjdlog_abort(const char *func, const char *file, int line,
555    const char *failedexpr, const char *fmt, ...)
556{
557	va_list ap;
558
559	assert(pjdlog_initialized == PJDLOG_INITIALIZED);
560
561	/*
562	 * When there is no message we pass __func__ as 'fmt'.
563	 * It would be cleaner to pass NULL or "", but gcc generates a warning
564	 * for both of those.
565	 */
566	if (fmt != func) {
567		va_start(ap, fmt);
568		pjdlogv_critical(fmt, ap);
569		va_end(ap);
570	}
571	if (failedexpr == NULL) {
572		if (func == NULL) {
573			pjdlog_critical("Aborted at file %s, line %d.", file,
574			    line);
575		} else {
576			pjdlog_critical("Aborted at function %s, file %s, line %d.",
577			    func, file, line);
578		}
579	} else {
580		if (func == NULL) {
581			pjdlog_critical("Assertion failed: (%s), file %s, line %d.",
582			    failedexpr, file, line);
583		} else {
584			pjdlog_critical("Assertion failed: (%s), function %s, file %s, line %d.",
585			    failedexpr, func, file, line);
586		}
587	}
588	abort();
589}
590