#ifndef LINT
static char *rcsid="$Id: serv_unixd.c,v 1.5 2001/07/20 07:35:54 crosser Exp $";
#endif

/*
	$Log: serv_unixd.c,v $
	Revision 1.5  2001/07/20 07:35:54  crosser
	more tuning of AF_UNIX address size, now works on FreeBSD
	
	Revision 1.4  2001/07/20 05:48:59  crosser
	address bug with AF_UNIX address size
	fix permissions of unix datagram socket (on Linux, honors umask)
	
	Revision 1.3  1999/10/03 21:18:04  crosser
	First (probably) working version with new run time config parser.
	Also added listenq parameter (backlog size for listen()) and
	renamed wtest to whoson (and with man page).  Release 2.00beta1.
	
	Revision 1.2  1999/08/19 17:22:16  crosser
	Move to new config scheme (does not work yet)
	
	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 <sys/stat.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"

#include "rtc_begin.h"
#include "serv_unixd_cfg.h"
#include "rtc_middle.h"
#include "serv_unixd_cfg.h"
#include "rtc_end.h"

struct _unixd_data_rec {
	char buf[512];
};

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);
			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)
{
	wso_serv_unixd_cfg *rec=(wso_serv_unixd_cfg *)priv;
	struct _evdesc evdesc;
	struct _unixd_data_rec *data;
	struct sockaddr_un server;
	int msgsock=-1;
	int tries,on=1;
	mode_t 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)+1) < 0) {
			if ((errno == EADDRINUSE) && (tries < 10)) {
				sleep(tries);
				continue;
			}
			(void)umask(savemask);
			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;
}


syntax highlighted by Code2HTML, v. 0.9.1