#ifndef LINT
static char *rcsid="serv_unixd.c,v 1.1.1.1 1999/08/12 19:36:46 woods Exp";
#endif

/*
	serv_unixd.c,v
	Revision 1.1.1.1  1999/08/12 19:36:46  woods
	whoson version 1.08
	
	Revision 1.2  1999/06/03 09:23:10  crosser
	fix size of sockaddr_un for freebsd (crazy!)

	Revision 1.1  1999/01/30 15:43:40  crosser
	Initial revision

*/

/*
	WHAT IS IT:
		Implementation of experimental "whoson" protocol
	AUTHOR:
		Eugene G. Crosser <crosser@average.org>
	COPYRIGHT:
		Public domain
*/

#include "config.h"

#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <setjmp.h>
#include <stdio.h>
#include <ctype.h>

#include "whosond.h"
#include "rtconfig.h"
#include "serv_common.h"
#include "checkperm.h"
#include "report.h"

struct _unixd_serv_rec {
	char port[UNIX_PATH_MAX];
};

struct _unixd_data_rec {
	char buf[512];
};

int unixd_serv_cfg_init(void **priv)
{
	struct _unixd_serv_rec *rec;

	if ((rec=(struct _unixd_serv_rec *)malloc
					(sizeof(struct _unixd_serv_rec)))) {
		memset(rec,0,sizeof(struct _unixd_serv_rec));
		(*priv)=(void*)rec;
		return 0;
	} else {
		ERRLOG((LOG_ERR,"allocating struct _unixd_serv_rec: %m"))
		return 1;
	}
}

int unixd_serv_cfg_next(char *key,char *val,void **priv)
{
	struct _unixd_serv_rec *rec=(struct _unixd_serv_rec *)(*priv);

	if (strcasecmp(key,"port") == 0) {
		strncpy(rec->port,val,sizeof(rec->port)-1);
		(rec->port)[sizeof(rec->port)-1]='\0';
	} else {
		ERRLOG((LOG_ERR,"bad keyword \"%s\"\n",key))
		return 1;
	}

	return 0;
}

int unixd_serv_cfg_end(void **priv)
{
	struct _unixd_serv_rec *rec=(struct _unixd_serv_rec *)(*priv);

	if (rec->port == 0) {
		ERRLOG((LOG_ERR,"bad port value \"%d\"\n",rec->port))
		return 1;
	}

	return 0;
}

static struct _evdesc unixd_read_evproc(int fd,void *priv)
{
	struct _unixd_data_rec *data=(struct _unixd_data_rec *)priv;
	struct _evdesc evdesc;
	int len,slen;
	struct sockaddr_un fromunix;
	char retbuf[512];

DPRINT(("unixd_read_evproc(%d,%p)\n",fd,priv))

	memset(&evdesc,0,sizeof(struct _evdesc));
	evdesc.fd=-1;
	if (fd < 0) {
		fd=-fd;
		close(fd);
		free(data);
	} else {
		slen = sizeof(fromunix);
		if ((len=recvfrom(fd,data->buf,sizeof(data->buf)-1,
				0,(struct sockaddr *)&fromunix,&slen)) > 0) {
DPRINT(("UNIX DGRAM message (%d bytes) from %s\n",len,fromunix.sun_path))
			retbuf[0]='\0';
		do_request(data->buf,retbuf,sizeof(retbuf));
			len=strlen(retbuf);
			slen = sizeof(fromunix);
			if (sendto(fd,retbuf,len,0,
				(struct sockaddr *)&fromunix,slen) != len) {
				ERRLOG((LOG_ERR,"sendto: %m"))
			}
		} else {
			ERRLOG((LOG_ERR,"recvfrom: %m"))
		}
	}

DPRINT(("unixd_read_evproc returns fd=%d evproc=%p\n",evdesc.fd,evdesc.evproc))
	return evdesc;
}

struct _evdesc unixd_serv_init(void *priv)
{
	struct _unixd_serv_rec *rec=(struct _unixd_serv_rec *)priv;
	struct _evdesc evdesc;
	struct _unixd_data_rec *data;
	struct sockaddr_un server;
	int msgsock=-1;
	int tries,on=1;
	int savemask;

DPRINT(("unixd_serv_init(%p)\n",priv))

	memset((char *)&server,0,sizeof(server));
	server.sun_family = AF_UNIX;
	strncpy(server.sun_path,rec->port,sizeof(server.sun_path)-1);
	server.sun_path[sizeof(server.sun_path)-1]='\0';
	(void)unlink(server.sun_path);
	if ((msgsock=socket(AF_UNIX,SOCK_DGRAM,0)) < 0) {
		ERRLOG((LOG_ERR,"socket: %m"))
		goto exit;
	}
	if (setsockopt(msgsock,SOL_SOCKET,SO_REUSEADDR,
				(char*)&on,sizeof(on)) < 0) {
		ERRLOG((LOG_ERR,"setsockopt: %m"))
		msgsock=-1;
		goto exit;
	}
	savemask=umask(0);
	for (tries=0;;tries++) {
		if (bind(msgsock,(struct sockaddr*)&server,
				sizeof(server)-sizeof(server.sun_path)
					+strlen(server.sun_path)) < 0) {
			if ((errno == EADDRINUSE) && (tries < 10)) {
				sleep(tries);
				continue;
			}
			ERRLOG((LOG_ERR,"bind: %m"))
			msgsock=-1;
			goto exit;
		} else break;
	}
	(void)umask(savemask);

exit:
	memset(&evdesc,0,sizeof(struct _evdesc));
	evdesc.fd=msgsock;
	evdesc.evproc=unixd_read_evproc;
	evdesc.ttl=0;
	evdesc.priv=priv;
	if ((data=(struct _unixd_data_rec *)malloc
			(sizeof(struct _unixd_data_rec)))) {
		memset(data,0,sizeof(struct _unixd_data_rec));
		evdesc.priv=data;
	} else {
		ERRLOG((LOG_ERR,"allocating struct _unixd_data_rec: %m"))
		evdesc.fd=-1;
		evdesc.evproc=NULL;
	}
DPRINT(("unixd_serv_init returns fd=%d evproc=%p\n",evdesc.fd,evdesc.evproc))
	return evdesc;
}
