/*
 * dsyslog - a dumb syslog (e.g. syslog for people who have a clue)
 * Copyright (c) 2008 William Pitcock <nenolod@sacredspiral.co.uk>
 *
 * 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/un.h>
#include <sys/poll.h>
#include <sys/stat.h>

#include "dsyslog.h"

static void
parse_and_dispatch(gchar *line)
{
	gint logcode, pid = 0;
	gchar *date, *program, *message;

	_ENTER;

	if (*line != '<') {
		_LEAVE;
	}

	if ((logcode = atoi(++line)) <= 0) {
		_LEAVE;
	}

	_DEBUG("have logcode(%d)", logcode);

	while (*line != '>') {
		if (*line == '\0') {
			_LEAVE;
		}

		line++;
	}
	date = ++line;

	while (*line != ':') {
		if (*line == '\0') {
			_LEAVE;
		}

		line++;
	}
	line++;

	while (!isspace(*line)) {
		if (*line == '\0') {
			_LEAVE;
		}

		line++;
	}
	*line++ = '\0';
	_DEBUG("have date<%s>", date);

	program = line;
	while (*line != '[' && *line != ':') {
		if (*line == '\0') {
			_LEAVE;
		}

		line++;
	}
	if (*line == '[') {
		*line++ = '\0';
		pid = atoi(line);
		while (*line != ']') {
			if (*line == '\0') {
				_LEAVE;
			}
			line++;
		}
		line++;
		while (*line != ':') {
			if (*line == '\0') {
				_LEAVE;
			}
			line++;
		}
	} else {
		*line = '\0';
	}
	line++;

	while (!isspace(*line)) {
		if (*line == '\0') {
			_LEAVE;
		}

		line++;
	}
	message = ++line;

	dsyslog_event_dispatch(logcode, date, NULL, program, pid, message);

	_LEAVE;
}

static gboolean
incoming_syslog_line(GIOChannel *source, GIOCondition cond, gpointer unused)
{
	gchar line[1024];
	gint ret = -1;
	gint fd = g_io_channel_unix_get_fd(source);

	_ENTER;

	ret = read(fd, &line, 1024);
	line[ret] = '\0';
	_DEBUG("got syslog line: %s", line);

	parse_and_dispatch(line);

	_LEAVE TRUE;
}

struct dsyslog_source_localsock {
	GIOChannel *logchan;
	gint tag;
};

static void
dsyslog_source_localsock_delete(dsyslog_source_t *src)
{
	struct dsyslog_source_localsock *lsock;
	gint fd;

	_ENTER;

	lsock = src->opaque;
	g_source_remove(lsock->tag);
	fd = g_io_channel_unix_get_fd(lsock->logchan);
	g_io_channel_unref(lsock->logchan);
	close(fd);
	g_slice_free(struct dsyslog_source_localsock, lsock);

	_LEAVE;
}

static void
dsyslog_source_localsock_new(dsyslog_source_t *src)
{
	int sock;
	struct sockaddr_un sa;
	struct dsyslog_source_localsock *lsock;

	_ENTER;

	if (!src->path) {
		_LEAVE;
	}

	sock = socket(PF_UNIX, SOCK_DGRAM, 0);
	if (sock < 0) {
		perror("logsocket");
		_LEAVE;
	}

	sa.sun_family = AF_UNIX;
	g_strlcpy(sa.sun_path, src->path, sizeof sa.sun_path);
	unlink(sa.sun_path);

	if (bind(sock, (struct sockaddr *) &sa, (socklen_t) sizeof sa) < 0) {
		perror("logsocket:bind");
		_LEAVE;
	}

	chmod(sa.sun_path, 0666);

	lsock = g_slice_new0(struct dsyslog_source_localsock);
	lsock->logchan = g_io_channel_unix_new(sock);
	lsock->tag = g_io_add_watch(lsock->logchan, G_IO_IN, incoming_syslog_line, NULL);
	src->opaque = lsock;
	src->destructor = dsyslog_source_localsock_delete;

	_LEAVE;
}

void
_modinit(void)
{
	_ENTER;

	dsyslog_source_register("localsock", dsyslog_source_localsock_new);

	_LEAVE;
}

void
_modfini(void)
{
	_ENTER;

	dsyslog_source_unregister("localsock");

	_LEAVE;
}
