#include <machine/rtems-bsd-user-space.h>

/*
 * RTEMS version of syslog and associated routines
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <rtems.h>
#include <rtems/thread.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

#include <unistd.h>

static int LogStatus = LOG_CONS;
static const char *LogTag = "syslog";
static int LogFacility = LOG_USER;
static int LogMask = 0xff;

static int LogFd = -1;
static rtems_recursive_mutex LogSemaphore =
  RTEMS_RECURSIVE_MUTEX_INITIALIZER("syslog");
extern struct in_addr rtems_bsdnet_log_host_address;

#define SYSLOG_PORT	514

void
syslog (int pri, const char *fmt, ...)
{
	va_list ap;

	va_start (ap, fmt);
	vsyslog (pri, fmt, ap);
	va_end (ap);
}

/*
 * FIXME: Should cbuf be static?  It could be if we put the mutex
 *        around the entire body of this routine.  Then we wouldn't
 *        have to worry about blowing stacks with a local variable
 *        that large.  Could make cbuf bigger, too.
 */
void
vsyslog (int pri, const char *fmt, va_list ap)
{
	int cnt;
	char *msgp, cbuf[200];
	int sent;

	if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) {
		syslog (LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID,
								"syslog: unknown facility/priority: %#x", pri);
		pri &= LOG_PRIMASK|LOG_FACMASK;
	}

	if (!(LOG_MASK(LOG_PRI(pri)) & LogMask))
		return;

	if ((pri & LOG_FACMASK) == 0)
		pri |= LogFacility;

	cnt = snprintf (cbuf, sizeof (cbuf), "<%d>", pri);
	msgp = cbuf + (cnt < sizeof (cbuf) ? cnt : sizeof (cbuf) - 1);
	if (LogTag && cnt < sizeof (cbuf) - 1)
		cnt += snprintf (cbuf + cnt, sizeof (cbuf) - cnt, "%s", LogTag);
	if (LogStatus & LOG_PID && cnt < sizeof (cbuf) - 1) {
		rtems_id tid;
		rtems_task_ident (RTEMS_SELF, 0, &tid);
		cnt += snprintf (cbuf + cnt, sizeof (cbuf) - cnt, "[%#lx]", (unsigned long)tid);
	}
	if (LogTag && cnt < sizeof (cbuf) - 1)
		cnt += snprintf (cbuf + cnt, sizeof (cbuf) - cnt, ": ");
	cnt += vsnprintf (cbuf + cnt, sizeof (cbuf) - cnt, fmt, ap);
	if (cnt > sizeof (cbuf) - 1)
		cnt = sizeof (cbuf) - 1;
	while (cnt > 0 && cbuf[cnt-1] == '\n')
		cbuf[--cnt] = '\0';

	if (LogStatus & LOG_PERROR)
		printf ("%s\n", cbuf);

	/*
	 * Grab the mutex
	 */
	sent = 0;
	if ((rtems_bsdnet_log_host_address.s_addr != INADDR_ANY)
	 && (LogFd >= 0)) {
		/*
		 * Set the destination address/port
		 */
		struct sockaddr_in farAddress;
		farAddress.sin_family = AF_INET;
		farAddress.sin_port = htons (SYSLOG_PORT);
		farAddress.sin_addr = rtems_bsdnet_log_host_address;
		memset (farAddress.sin_zero, '\0', sizeof farAddress.sin_zero);

		rtems_recursive_mutex_lock (&LogSemaphore);
		/*
		 * Send the message
		 */
		if (sendto (LogFd, cbuf, cnt, 0, (struct sockaddr *)&farAddress, sizeof farAddress) >= 0)
			sent = 1;
		rtems_recursive_mutex_unlock (&LogSemaphore);
	}
	if (!sent && (LogStatus & LOG_CONS) && !(LogStatus & LOG_PERROR))
		printf ("%s\n", msgp);
}

void
openlog (const char *ident, int logstat, int logfac)
{
	struct sockaddr_in myAddress;

	if (ident != NULL)
		LogTag = ident;
	LogStatus = logstat;
	if (logfac != 0 && (logfac & ~LOG_FACMASK) == 0)
		LogFacility = logfac;

	/*
	 * Create the socket
	 */
	if ((LogFd = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
		printf ("Can't create syslog socket: %d\n", errno);
		return;
	}

	/*
	 * Bind socket to name
	 */
	myAddress.sin_family = AF_INET;
	myAddress.sin_addr.s_addr = INADDR_ANY;
	myAddress.sin_port = htons (SYSLOG_PORT);;
	memset (myAddress.sin_zero, '\0', sizeof myAddress.sin_zero);
	if (bind (LogFd, (struct sockaddr *)&myAddress, sizeof (myAddress)) < 0) {
		close (LogFd);
		LogFd = -1;
		printf ("Can't bind syslog socket: %d\n", errno);
		return;
	}
}

void
closelog(void)
{
	if (LogFd >= 0) {
		close (LogFd);
		LogFd = -1;
	}
}

int
setlogmask (int pmask)
{
	int omask;

	omask = LogMask;
	if (pmask != 0)
		LogMask = pmask;
	return (omask);
}
