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

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

	Revision 1.10  1999/06/02 22:10:36  crosser
	deal with UNIX_PATH_MAX in a better way

	Revision 1.9  1999/01/30 14:41:05  crosser
	fix use of saved errno

	Revision 1.8  1998/07/26 14:06:40  crosser
	fix for multiline responces

	Revision 1.7  1998/07/12 16:43:57  crosser
	Change protocol: responce now is terminated with empty line

	Revision 1.6  1998/07/05 00:26:18  crosser
	Change copyright

	Revision 1.5  1998/07/03 11:10:39  crosser
	setup for _REENTRANT mode

	Revision 1.4  1998/07/03 09:32:48  crosser
	make persistant connections work

	Revision 1.3  1998/07/02 18:01:15  crosser
	change error reporting to syslog

	Revision 1.2  1998/07/02 15:37:07  crosser
	make right return if req invalid or disallowed

	Revision 1.1  1998/07/01 21:55:16  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 <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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "whoson.h"
#include "rtconfig.h"
#include "clnt_common.h"
#include "report.h"

#define MAXTRIES 3
#define MAXREQL 1024

struct _unix_serv_rec {
#ifndef _REENTRANT
	int fd;
#define FD (rec->fd)
#endif
	char port[UNIX_PATH_MAX];
};

int wso_unix_serv_connect(void *priv,char *buf)
{
	struct _unix_serv_rec *rec=(struct _unix_serv_rec *)(priv);
	struct sockaddr_un server;
	int len,off,left,crcount,lfcount,saveerr;
	char *p;
	int tries=0;
	RETSIGTYPE (*savepipe)(int);
#ifdef _REENTRANT
	int fd=-1;
#define FD fd
#endif

	if (FD >= 0) goto session;
reconnect:
	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';
	if ((FD=socket(AF_UNIX,SOCK_STREAM,0)) < 0) {
		ERRLOG((LOG_ERR,"[WHOSON] server socket: %m"))
		return -1;
	}
	if (connect(FD,(struct sockaddr *)&server,
		sizeof(server)-sizeof(server.sun_path)
				+strlen(server.sun_path)) < 0) {
		ERRLOG((LOG_ERR,"[WHOSON] connect server: %m"))
		close(FD);
		FD=-1;
		return -1;
	}
session:
	savepipe=signal(SIGPIPE,SIG_IGN);
	len=write(FD,buf,strlen(buf));
	saveerr=errno;
	(void)signal(SIGPIPE,savepipe);
	errno=saveerr;
	if (len != strlen(buf)) {
		if ((errno == EPIPE) && (++tries < MAXTRIES)) {
			DPRINT(("server gone (write), reconnecting %d\n",tries))
			close(FD);
			goto reconnect;
		}
		ERRLOG((LOG_ERR,"[WHOSON] write to server: %m"))
		close(FD);
		FD=-1;
		return -1;
	}
	off=0;
	left=MAXREQL-1;
	crcount=0;
	lfcount=0;
	while (left) {
		if ((len=read(FD,buf+off,left)) < 0) {
			if ((errno == EPIPE) && (++tries < MAXTRIES)) {
				DPRINT(("server gone (read), reconnect %d\n",
					tries))
				close(FD);
				goto reconnect;
			}
			ERRLOG((LOG_ERR,"[WHOSON] read from server: %m"))
			close(FD);
			FD=-1;
			return -1;
		}
		if (len == 0) break;
		for (p=buf+off;
		     (p<buf+MAXREQL) && (crcount<2) && (lfcount<2);
		     p++)
		switch (*p) {
		case '\r':      crcount++;
				break;
		case '\n':      lfcount++;
				break;
		default:        crcount=0; lfcount=0;
				break;
		}
		off+=len;
		left-=len;
		if ((crcount > 1) || (lfcount > 1)) break;
	}
	buf[off]='\0';

#ifdef _REENTRANT
	close(FD);
	FD=-1;
#endif

	return 0;
}

int wso_unix_serv_cfg_init(void **priv)
{
	struct _unix_serv_rec *rec;

	if ((rec=(struct _unix_serv_rec *)malloc
				(sizeof(struct _unix_serv_rec)))) {
		memset(rec,0,sizeof(struct _unix_serv_rec));
#ifndef _REENTRANT
		rec->fd=-1;
#endif
		(*priv)=(void*)rec;
		return 0;
	} else {
		ERRLOG((LOG_ERR,"[WHOSON] allocating struct _unix_serv_rec: %m"))
		return 1;
	}
}

int wso_unix_serv_cfg_next(char *key,char *val,void **priv)
{
	struct _unix_serv_rec *rec=(struct _unix_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,"[WHOSON] bad keyword \"%s\"\n",key))
		return 1;
	}

	return 0;
}

int wso_unix_serv_cfg_end(void **priv)
{
	struct _unix_serv_rec *rec=(struct _unix_serv_rec *)(*priv);

	if ((rec->port)[0] == '\0') {
		ERRLOG((LOG_ERR,"[WHOSON] port value missing\n"))
		return 1;
	} else
		return 0;
}
