/*
 * dsyslog - a dumb syslog (e.g. syslog for people who have a clue)
 * Copyright (c) 2008 William Pitcock <nenolod@dereferenced.org>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice is present in all copies.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <glib.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>

#include "dsyslog.h"

struct dsyslog_tcp_socket {
	int sock;
	struct sockaddr_in sa;
};

static void
dsyslog_tcp_output_destructor(dsyslog_output_t *output)
{
	struct dsyslog_tcp_socket *us;

	_ENTER;

	us = (struct dsyslog_tcp_socket *) output->opaque;
	close(us->sock);
	g_slice_free(struct dsyslog_tcp_socket, us);

	_LEAVE;
}

static void
dsyslog_tcp_output_handler(dsyslog_event_t *event, dsyslog_output_t *output)
{
	struct dsyslog_tcp_socket *us;
	gchar msg[1024];

	_ENTER;

	if (!output->opaque) {
		struct hostent *hp;
		struct in_addr *in;

		us = g_slice_new0(struct dsyslog_tcp_socket);

		if (!output->host) {
			_LEAVE;
		}

		if (!output->port) {
			output->port = 514;
		}

		us->sock = socket(PF_INET, SOCK_STREAM, 0);
		if (us->sock < 0) {
			perror("logsocket");
			g_slice_free(struct dsyslog_tcp_socket, us);
			_LEAVE;
		}

		us->sa.sin_family = AF_INET;
		if ((hp = gethostbyname(output->host)) == NULL) {
			close(us->sock);
			g_slice_free(struct dsyslog_tcp_socket, us);
			_LEAVE;
		}

		in = (struct in_addr *)(hp->h_addr_list[0]);
		us->sa.sin_addr.s_addr = in->s_addr;
		us->sa.sin_port = htons(output->port);

		if (connect(us->sock, (struct sockaddr *) &us->sa, sizeof(us->sa)) < 0) {
			perror("logsocket:connect");
			close(us->sock);
			g_slice_free(struct dsyslog_tcp_socket, us);

			_LEAVE;
		}

		output->opaque = us;
		output->destructor = dsyslog_tcp_output_destructor;
	}

	us = (struct dsyslog_tcp_socket *) output->opaque;

	if (event->pid != 0)
		snprintf(msg, sizeof(msg), "<%d>%s[%d]: %s", event->logcode, event->program, event->pid, event->message);
	else
		snprintf(msg, sizeof(msg), "<%d>%s: %s", event->logcode, event->program, event->message);
	
	send(us->sock, msg, strlen(msg), MSG_NOSIGNAL);

	_LEAVE;
}

void
_modinit(void)
{
	_ENTER;

	dsyslog_output_type_register("tcp", dsyslog_tcp_output_handler);

	_LEAVE;
}

void
_modfini(void)
{
	_ENTER;

	dsyslog_output_type_unregister("tcp");

	_LEAVE;
}
