/*
 * 5799-WZQ (C) COPYRIGHT = NONE
 * LICENSED MATERIALS - PROPERTY OF IBM
 */
/* $Header:vddb.c 12.0$ */
/* $ACIS:vddb.c 12.0$ */
/* $Source: /ibm/acis/usr/src/ibm/rvd/misc/RCS/vddb.c,v $ */

#ifndef lint
static char *rcsid = "$Header:vddb.c 12.0$";
#endif


#ifndef lint
static char rcsid_main_c[] = "$Header:vddb.c 12.0$";
#endif lint

/*  Copyright 1984 by the Massachusetts Institute of Technology  */
/*  See permission and disclaimer notice in file "notice.h"  */
#include	"notice.h"


/* Database management program for RVD.  Read in the database file,
 * prompt the user for operations, do them, and notify the server.
 *
 * Syntax:
 *	vddb [-n] [database]
 * If the database file is not present, the file "/site/rvd/rvddb"
 * is used.  Otherwise, database is used and the last component after
 * a '/' is used as a hostname.
 *
 * The -n flag, if present, indicates that only the database file
 * should be modified and no server control messages should be sent.
 * The undocumented -d flag causes a packet trace to be sent to the
 * standard error.
 */

#include	<stdio.h>
#include	<signal.h>
#include	<strings.h>
#include	<sys/types.h>
#include	<sys/socket.h>
#include	<sys/file.h>
#include	<sys/time.h>
#include	<netinet/in.h>
#ifdef	KERBEROS
#include	<krb.h>
#endif	KERBEROS

#include	"rvd_types.h"
#include	"custom.h"
#include	"ctl_pkt.h"
#include	"logging.h"
#include	"extern.h"
#include	"vddb.h"

unsigned log_flag = LOG_ERR;
char	*rvddb = RVDDB;
char	*rvdmaster = RVDMASTER;
char	*getpass();
boolean	sendctl = TRUE;
char	sbuf[BUFLEN];
char	rbuf[BUFLEN];
int	year;

char	passw[33];
char	server_name[BUFLEN];		/* Name of the current machine. */
boolean	passw_flag;			/* If true, use a password else */
					/* use Kerberos authenticator.  */

boolean	remote_server = FALSE;
boolean	local_file_modified = FALSE;
char	rcp_string[BUFLEN];

char	*myname;

main (argc, argv)
	int	argc;
	char	**argv;
{
	register FILE	*db;
	register char	*pw, *server;
	register int	i;
	register int	len;
	struct tm	*tm;
	long		now;
	static	char	lbuf[BUFLEN];
#ifdef KERBEROS
	char	authent[sizeof(struct ktext)];	/* An authenticator */
#endif KERBEROS

	myname = argv[0];

	setlinebuf(stderr);

	/* Handle the command line options.
	 */
	while (*argv[1] == '-') {
		switch(argv[1][1]) {
		    case 'n':
			sendctl = FALSE;
			argc--; argv++;
			break;

		    case 'd':
			log_flag |= LOG_TRACE;
			argc--; argv++;
			break;

		    default:
			printf("usage: %s [-n] [-d] [database]\n", myname);
			exit(1);
		}
	}


	/* Use either the last component of the path name of the given
	 * database file (small kluge) or the current machine's name 
	 * as the server name.  This name is then used to get Kerberos
	 * tickets and to initialize the connection to the RVD server.
	 */
	if (argc > 1) {
		rvddb = argv[1];
		if ((server = rindex(rvddb, '/')) == NULL)
			server = rvddb;
		else
			server++;	/* Increment past the '/'. */
		(void) strcpy(server_name, server);
		sprintf(rcp_string, "rcp %s %s:%s\n", rvddb, server, rvddb);
		remote_server = TRUE;
	} else {
		if (gethostname(server_name, BUFLEN) != NULL) {
			perror(myname);
			exit(1);
		}
	}
	server = server_name;


	/* Translate any symbolic links to the respective pathname.
	 */
	while ((len = readlink(rvddb, lbuf, BUFLEN)) > 0) {
		lbuf[len] = '\0';
		rvddb = lbuf;
	}

	/* Open the data base file for both reading and writing.  If that
	 * fails then try opening the file read only.  If that fails, give up.
	 */
	if ((db = fopen(rvddb, "r+")) == NULL) {
		if ((db = fopen(rvddb, "r")) == NULL) {
			perror(rvddb);
			exit(1);
		}
		fprintf(stderr, "%s: read only access to %s\n", myname, rvddb);
		fprintf(stderr, "Only the list and help commands will work.\n");
		log_flag |= LOG_RDWR;
	}

	if (flock(fileno(db), LOCK_EX | LOCK_NB) < 0) {
		fprintf(stderr, "%s: %s: ", myname, "flock");
		perror(rvddb);
		exit(1);
	}

	/* Try to get a ticket for rvdsrv.  If it fails,
	 * fall back on the old password scheme.
	 */
#ifdef	KERBEROS
	if (get_auth(authent, server, myname, log_flag&LOG_TRACE?TRUE:FALSE))
		passw_flag = FALSE;
	else
#endif	KERBEROS
	{
		strcpy(passw, getpass("Password: ", 32));
		passw_flag = TRUE;
	}

	if (sendctl)
		ctl_init(server);

	time(&now);
	tm = localtime(&now);
	year = tm->tm_year;

	input_db(db);

	loop(db);
	quit(db);
}


input_db(db)

/* Input the database file open on the specified file descriptor, building
 * an internal representation of the database.
 */

register FILE	*db;
{
	register int	len;
	struct	sockaddr_in	temp;		/* just for temp */

	pd_init();
/*	vd_init();	*/
	while ((len = get_message(db, sbuf, BUFLEN)) > 0)
		ctl_domsg(&temp, sbuf, len);
}


get_message(infile, buf, len)

/* Read the next message (until an empty line) from the specified input file
 * into the specified buffer, up to len characters.  Returns the size of the
 * message in bytes, or 0 if end-of-file or error.
 */

register FILE	*infile;
char	*buf;
int	len;
{
	register char	*p;
	register int	left, l;

	for (p = buf, left = len; fgets(p, left, infile) != NULL && *p != '\n';
	    left -= (l = strlen(p)), p += l)
		;
	return(len - left);
}


FILE *
db_output(fname, db)

/* Output the current value of the database to the specified file.  We assume
 * that the file is open on descriptor db and is locked for exclusive access.
 * We create a new temporary file, write the database to it, rename it to
 * fname, open a new descriptor on in, close db, and return the new descriptor.
 * Returns NULL on error.
 */

char	*fname;				/* database file name */
FILE	*db;				/* database file descriptor */
{
	char	*tempn;			/* temporary file name */
	register FILE	*tempf;		/* temporary file handle */
	int	tempd;			/* descriptor for temp file */

	tempn = temp_in_dir(fname);
	if ((tempd = open(tempn, O_WRONLY | O_CREAT | O_EXCL, 0600)) < 0) {
		perror("Unable to write database file");
		return(NULL);
	}
	if (flock(tempd, LOCK_EX | LOCK_NB) < 0) {
		perror("Unable to lock temporary file");
		close(tempd);
		unlink(tempn);
		return(NULL);
	}
	if ((tempf = fdopen(tempd, "w")) == NULL) {
		fprintf(stderr, "Unable to fdopen temp file\n");
		close(tempd);
		unlink(tempn);
		return(NULL);
	}

	if (!pd_write(tempf)) {
		fclose(tempf);
		unlink(tempn);
		return(NULL);
	}

	fflush(tempf);

	if (rename(tempn, fname) < 0) {
		perror("Unable to rename temporary");
		fclose(tempf);
		unlink(tempn);
		return(NULL);
	}

	local_file_modified = TRUE;
	return(tempf);
}


/* Time to leave.  Close the current data base file and, if it was modified
 * and we're dealing with a remote server, rcp it to the remote server.
 */
quit(db)
	FILE	*db;
{
	fclose(db);
	if (local_file_modified && remote_server)
		if (system(rcp_string) != NULL) {
			fprintf(stderr, "%s: could not copy %s to %s:%s\n",
				myname, rvddb, server_name, rvddb);
			exit(1);
		}
	exit(0);
}
