/* Distributed Checksum Clearinghouse
 *
 * Copyright (c) 2005 by Rhyolite Software
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE
 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 *
 * Rhyolite Software DCC 1.2.74-1.28 $Revision$
 */

#include "dcc_defs.h"
#include "dcc_paths.h"
#ifndef DCC_WIN32
#include <syslog.h>
#endif

extern void dcc_syslog_lock(void);
extern void dcc_syslog_unlock(void);

u_char dcc_no_syslog;

int dcc_error_priority = LOG_ERR | LOG_MAIL;
int dcc_trace_priority = LOG_NOTICE | LOG_MAIL;


/* commonly used, but not thread safe */
int dcc_ex_code = EX_UNAVAILABLE;


DCC_PATH dcc_progname;

#ifdef HAVE___PROGNAME
extern const char *__progname;
#endif

void
dcc_syslog_init(u_char use_syslog,
		const char *argv0 UATTRIB, const char *suffix)
{
	const char *p;
#ifndef DCC_WIN32
	struct stat sb;
	int fd;
#endif

	/* Solaris defaults to "syslog" with a null identification string,
	 * but does not seem to have __progname set by crt0. */
#undef GOT_PROGNAME
#ifdef HAVE_GETPROGNAME
	p = getprogname();
#	define GOT_PROGNAME
#endif
#if defined(HAVE___PROGNAME) && !defined(GOT_PROGNAME)
	p = __progname;
#	define GOT_PROGNAME
#endif
#ifndef GOT_PROGNAME
	p = strrchr(argv0, '/');
	if (!p)
		p = argv0;
	else
		++p;
#endif
	snprintf(dcc_progname, sizeof(dcc_progname), "%s%s",
		 p, suffix ? suffix : "");

#ifdef DCC_WIN32
	dcc_no_syslog = 1;
#else
	/* Don't wait for the console if somehow we must use it,
	 * because that messes up dccm. */
#ifndef LOG_NOWAIT
#define LOG_NOWAIT 0
#endif
	openlog(dcc_progname, LOG_PID | LOG_NOWAIT, LOG_MAIL);
	dcc_no_syslog = !use_syslog;

	/* ensure that stdout and stderr exist so that when we open
	 * database or other files, we don't get file descriptor 1 or 2
	 * and then later write error messages to them. */
	if (0 > fstat(STDOUT_FILENO, &sb) && errno == EBADF) {
		fd = open(_PATH_DEVNULL, 0, O_RDWR);
		if (fd < 0)
			return;
		if (fd != STDOUT_FILENO) {
			dup2(fd, STDOUT_FILENO);
			close(fd);
		}
	}
	if (0 > fstat(STDERR_FILENO, &sb) && errno == EBADF) {
		fd = open(_PATH_DEVNULL, 0, O_RDWR);
		if (fd < 0)
			return;
		if (fd != STDERR_FILENO) {
			dup2(fd, STDERR_FILENO);
			close(fd);
		}
	}
#endif /* DCC_WIN32 */
}



void
dcc_vfatal_msg(const char *p, va_list args)
{
	char sbuf[200];
	int i;
	COPY_DCC_ARGS2();

	fflush(stdout);			/* keep stderr and stdout straight */
	vfprintf(stderr, p, DCC_ARGS2);
	fputs("; fatal error\n", stderr);
	fflush(stderr);
	END_DCC_ARGS2();

	if (dcc_no_syslog)
		return;

	dcc_syslog_lock();
	/* try to write the message with the "fatal error" addition as
	 * a single message */
	i = vsnprintf(sbuf, sizeof(sbuf), p, args);
	if (i < ISZ(sbuf)-ISZ("fatal error")) {
		syslog(dcc_error_priority, "%s; fatal error", sbuf);
	} else {
		vsyslog(dcc_error_priority, p, args);
		syslog(dcc_error_priority, "fatal error");
	}
	dcc_syslog_unlock();
}



void
dcc_verror_msg(const char *p, va_list args)
{
	COPY_DCC_ARGS2();

	fflush(stdout);			/* keep stderr and stdout straight */
	vfprintf(stderr, p, DCC_ARGS2);
	fputc('\n', stderr);
	END_DCC_ARGS2();

	if (dcc_no_syslog)
		return;
	dcc_syslog_lock();
	vsyslog(dcc_error_priority, p, args);
	dcc_syslog_unlock();
}



void PATTRIB(1,2)
dcc_error_msg(const char *p, ...)
{
	va_list args;

	va_start(args, p);
	dcc_verror_msg(p, args);
	va_end(args);
}



void
dcc_vtrace_msg(const char *p, va_list args)
{
	COPY_DCC_ARGS2();

	fflush(stdout);			/* keep stderr and stdout straight */
	vfprintf(stderr, p, DCC_ARGS2);
	fputc('\n', stderr);
	END_DCC_ARGS2();

	if (dcc_no_syslog)
		return;
	dcc_syslog_lock();
	vsyslog(dcc_trace_priority, p, args);
	dcc_syslog_unlock();
}



void PATTRIB(1,2)
dcc_trace_msg(const char *p, ...)
{
	va_list args;

	va_start(args, p);
	dcc_vtrace_msg(p, args);
	va_end(args);
}



void
dcc_vpemsg(int ex_code, DCC_EMSG emsg, const char *msg, va_list args)
{
	if (!emsg) {
		dcc_verror_msg(msg, args);
	} else {
		dcc_ex_code = ex_code;
		vsnprintf(emsg, sizeof(DCC_EMSG), msg, args);
	}
}



void PATTRIB(3,4)
dcc_pemsg(int ex_code, DCC_EMSG emsg, const char *msg, ...)
{
	va_list args;

	va_start(args, msg);
	dcc_vpemsg(ex_code, emsg, msg, args);
	va_end(args);
}
