/* made to run from inetd */

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <syslog.h>
#include <sys/file.h>
#include <ndbm.h>
#include <errno.h>
#include "v.h"

#define MAIL_COMMAND "/bin/mail testtrackers"
#define ZEPHYR_COMMAND "/usr/athena/zwrite -c testtrackers -i panic"
#define SERVER_NAME "vserv"
#define DEBUG_FILE "/tmp/vserv.debug" /* "/dev/null" /* */

static FILE *debug_file;

/*
 * Sending packets...
 */

static void send_response (struct v_pkt *pkt,
			   struct sockaddr_in *cl)
	{
	int s, rc;
	struct sockaddr_in ad;
	struct v_info info;

	fprintf(debug_file,"parsing packet..."); fflush(debug_file);
	v_parse_pkt(pkt, &info);		
	fprintf(debug_file,"Sending message %s for %s %s (%s) \n",
		(pkt->op_code==V_OK) ? info.message : "ERROR",
		info.appl_name, 
		info.appl_vers,
		info.platform); fflush(debug_file);
						/*** Set up socket and transmit return info ***/
	s = socket(AF_INET, SOCK_DGRAM, 0);
	bzero(&ad, sizeof(ad));
	ad.sin_family = AF_INET;
	ad.sin_port = htons(VERSION_CLIENT_PORT);
	if (bind(s, &ad, sizeof(ad))) 
		syslog(LOG_ALERT,"can't bind to port: %m");
	else
		if ((rc = sendto(s, (char *) pkt, sizeof(struct v_pkt), 
				 0, cl, sizeof(struct sockaddr_in))) < 0)
			syslog(LOG_ALERT,"can't send reply packet: %m");
	}

/*
 * Main program...
 */

main()
	{
	DBM            *db;
	char           keystr[KEY_SIZE];
	struct v_pkt    pkt, resp;
	struct v_info	info;
	int             rc, size;
	struct sockaddr_in cl;
	datum           rec, key;
	time_t		now;

	openlog(SERVER_NAME, 0, LOG_CLASS);
	if ((debug_file = (FILE *) fopen(DEBUG_FILE, "a+")) == NULL) 
		{
		syslog(LOG_ALERT,"can't open log file: %m");
		exit(1);
		}
	db = dbm_open(VERSION_DB_FILE, O_RDONLY, 0);
	if (!db) 
		{
		syslog(LOG_ALERT,"can't open version database: %m");
		exit(1);
		}
						/*** Receive packet ***/

	size = sizeof(cl);
	if ((rc = recvfrom(0, &pkt, sizeof(pkt), 0, &cl, &size)) < 1)
		{
		syslog(LOG_ERR, "can't recvfrom packet: %m");
		exit(1);
		}
	fprintf(debug_file, "\nGot a packet %s: opcode = %d\n", pkt.data, pkt.op_code); fflush(debug_file);

						/* Form a packet to be sent if error */

	bzero(&resp, sizeof(resp));
	resp.protocol_version = htons(VERSION_PROTOCOL_VERSION);
	resp.packet_number = htons(1);
	resp.number_of_packets = htons(1);
	resp.seq = pkt.seq;

						/*** parse data from packet  ***/

	fprintf(debug_file,"parsing packet..."); fflush(debug_file);
	v_parse_pkt(&pkt, &info);		
	fprintf(debug_file,"packet parsed: %s\n", info.appl_name); fflush(debug_file);
	
						/*** build key & fetch record ***/

	sprintf(keystr, "%s%s%s", 
		info.appl_name, 
		info.appl_vers,
		info.platform);
	key.dptr = keystr;
	key.dsize = strlen(key.dptr) + 1;
	rec = dbm_fetch(db, key);
	if (rec.dptr) 
		fprintf (debug_file,"key %s found\n", keystr);
	else
		fprintf (debug_file,"key %s NOT FOUND\n", keystr);
	fflush(debug_file);
	dbm_close(db);

						/*** Actually do what the client wants ***/

	if (!rec.dptr || (pkt.op_code != V_CHECK &&
			  pkt.op_code != V_CHECK_AND_LOG &&
			  pkt.op_code != V_LOG &&
			  pkt.op_code != V_LOG_PANIC))
		{
		fprintf (debug_file,"Sending error response\n"); fflush(debug_file);
		resp.op_code = V_ERROR;
		send_response(&resp, &cl);	/* Send error message and you're done */
     		}
	else 
		{
		if (pkt.op_code == V_CHECK ||	/* Opcodes that require checking */
		    pkt.op_code == V_CHECK_AND_LOG)
			{
			fprintf(debug_file, "responding to request\n"); fflush(debug_file);

			resp.op_code = V_OK;	/*** Finish response packet ***/
			bcopy(rec.dptr, resp.data, rec.dsize);
			send_response(&resp, &cl);
			}
		if (pkt.op_code == V_CHECK_AND_LOG || /* Opcodes that require logging */
		    pkt.op_code == V_LOG ||
		    pkt.op_code == V_LOG_PANIC) 
			{
			FILE *log_file;
			char log_fname[BUFSIZ];
			char *nowstring;

			sprintf(log_fname, "%s%s", VERSION_LOG_DIR, info.appl_name);
			fprintf(debug_file,"logging to file %s\n", log_fname); fflush(debug_file);
			if ((log_file = fopen(log_fname, "a+")) == NULL) 
				{
				syslog(LOG_ALERT, "can't open log file %s: %m", log_fname);
				exit(1);
				}
			time(&now);
			nowstring = ctime(&now);
			nowstring[24] = 0;	/* Kill a pesky newline */
			fprintf(log_file, "%s: %s %s (%s) %s",
				nowstring, info.appl_name, info.appl_vers, info.platform,
				(pkt.op_code == V_CHECK_AND_LOG ? "has been launched" :
				 (pkt.op_code == V_LOG ? "is logging" :
				  "PANIC")));
			if (*info.message) 
				fprintf(log_file,": %s", info.message);
			fprintf(log_file,"\n");
			fclose(log_file);
			}
		if (pkt.op_code == V_LOG_PANIC)	/* Special stuff for panic condition */
			{
			FILE *proc_pipe;
			
			fprintf(debug_file,"panic condition ... sending mail and zephyr\n"); fflush(debug_file);
			if ((proc_pipe = popen(MAIL_COMMAND, "w")) == NULL) 
				{
				syslog(LOG_ALERT, "can't invoke mailer for panic notification: %m");
				exit(1);
				}
			fprintf(proc_pipe,"\nPanic condition reported from %s %s (%s):\n%s\n",
				info.appl_name, info.appl_vers, info.platform, info.message);
			fclose(proc_pipe);
			
			if ((proc_pipe = popen(ZEPHYR_COMMAND, "w")) == NULL) 
				syslog(LOG_ALERT, "can't invoke zephyr for panic notification: %m");
			else
				{
				fprintf(proc_pipe,"\nPanic condition reported from %s %s (%s)\n.\n",
					info.appl_name, info.appl_vers, info.platform);
				fclose(proc_pipe);
				}
			}
		}
	fprintf(debug_file,"normal exit\n"); 
	fclose(debug_file);
	exit(0);
	}
