/* Syslog listener for systems where /dev/log is a Unix domain socket. */

/* 2000-02-09 - With some time stamp stripping and facility/priority translation
 * code from Rodrigo Severo and Rilson Raposo. */
/* 2000-02-10 - Some small improvements on the code and huge ones on the
 * comments with the help of Joao Neves. */

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <string.h>

#define SIZE 1024

int main(void) {
        char buf[SIZE], buf2[SIZE];
/* 2000-02-10 - The 2 following tables were "deduced" from syslog.h.
 * If someone can manage to get this info automatically from syslog.h even when
 * it changes, DO IT NOW! I couldn't find a way, syslog.h is kind of messy. */
		char *tab_pri[] = 
			{
				"emerg",
				"alert",
				"crit",
				"err",
				"warning",
				"notice",
				"info",
				"debug"
			};
		char *tab_fac[] = 
			{
				"kern",
				"user",
				"mail",
				"daemon",
				"auth",
				"syslog",
				"lpr",
				"news",
				"uucp",
				"cron",
				"authpriv",
				"ftp",
				"RESERVED",
				"RESERVED",
				"RESERVED",
				"RESERVED",
				"local0",
				"local1",
				"local2",
				"local3",
				"local4",
				"local5",
				"local6",
				"local7"
			};
        ssize_t r, w;
        struct sockaddr_in sa0;
        struct sockaddr_un sa1;
/* 2000-02-10 - I know people shouldn't create AUXies variables but sometimes
 * they seem to be the right thing. */
        int s0, s1, 
			aux, aux2;
        fd_set fs;

        /* Listen on UDP/514. */
        if ((s0 = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
                perror("socket");
                exit(1);
        }
        sa0.sin_family = AF_INET;
        sa0.sin_port = 514;
        sa0.sin_addr.s_addr = 0;
        if (bind(s0, &sa0, sizeof(sa0)) == -1) {
                perror("bind");
                exit(1);
        }

        /* And /dev/log. */
        if ((s1 = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
                perror("socket");
                exit(1);
        }
        /* Assertion: s1 > s0. */
        sa1.sun_family = AF_UNIX;
        strcpy(sa1.sun_path, "/dev/log");
        /* Assume that since we got udp/514, it's ok to grab /dev/log. */
        unlink("/dev/log");
        if (bind(s1, &sa1, sizeof(sa1)) == -1) {
                perror("bind");
                exit(1);
        }

        for(;;) {
                FD_ZERO(&fs);
                FD_SET(s0, &fs);
                FD_SET(s1, &fs);
                if (select(s1 + 1, &fs, 0, 0, 0) < 1) {
                        perror("select");
                        continue;
                }
				r = recvfrom((FD_ISSET(s0, &fs) ? s0 : s1), buf, SIZE, 0, 0, 0);
/* 2000-02-10 - After hours of painfull suffering, I finally found out that
 * recvfrom doesn't bothers to write a \0 at the end of buf, so I'm writing it.
 * */ 
 				if (r == 1024) {
					buf[1023] = 0;
				}
				else {
					buf[r] = 0;
				}
				if (r < 0) {
                        perror("recvfrom");
                        continue;
                }

/* 2000-02-09 - Let's cut syslog's time stamp off! While we are at it let's
 * translate the facility/priority code to their proper mnemonics. */
				if (r > 0) {
						aux = strcspn(buf, "<"); /* 2000-02-10 - Getting the												 * facility/priority code. */
						aux2 = strcspn(buf, ">");
/* 2000-02-10 - The next if includes some hocus pocus with indexes to extract
 * the facility/priority number code. */
						if ((aux < r) && (aux < aux2) && (aux2 < r)) {
							strncpy(buf2, &buf[(aux + 1)], (aux2 - aux - 1));
							buf2[(aux2 - aux - 1)] = 0;
							aux = atoi(buf2);	
						}
/* 2000-02-10 - The trick used to locate the end of syslog's time stamp
 * works with the MMM DD HH:MM:SS format. Other formats may need some
 * tinkering. */
						aux2 += strcspn(&buf[aux2], ":");
						if (strlen(&buf[aux2]) > 6) {
							aux2 += 6;
							aux2 += strspn(&buf[aux2], " ");
						}	
/* 2000-02-10 - Here we finally obtain buf as we really want it. The snprintf
 * builds the complete message as we want it. The hocus pocus with aux is a
 * direct consequence of syslog's coding method, don't blame it on me. */
						snprintf(buf2, SIZE, "{%s|%s} %s",
							tab_fac[(aux & 248)>>3],
							tab_pri[aux & 7],
							&buf[aux2]);
						strcpy(buf, buf2);
						r = strlen(buf);
				}	
/* 2000-02-10 - After lot's of messing around, here are buf and r back to
 * business. */

				while (r) {
                        w = write(1, buf, r);
                        if (w < 0) {
                                perror("write");
                                exit(1);
                        }
                        r -= w;
                }
                write(1, "\n", 1);
        }
}
