/*	Copyright (c) 1990 UNIX System Laboratories, Inc.	*/
/*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF     	*/
/*	UNIX System Laboratories, Inc.                     	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/


#ident	"@(#)saf:readtab.c	1.12.4.1"

# include <stdio.h>
# include <signal.h>
# include "misc.h"
# include "msgs.h"
# include <sac.h>
# include "structs.h"
# include <sys/types.h>
# include <unistd.h>
# include "extern.h"


/*
 * read_table - read in SAC's administrative file and build internal
 *		data structures
 *
 *	args:	startflag - flag to indicate if port monitor's should be
 *			    started as a side effect of reading
 */

void
read_table(startflag)
int startflag;
{
	FILE *fp;		/* scratch file pointer */
	int ret;		/* return code from check_version */
	struct sactab *sp;	/* working pointer to move through PM info *?

# ifdef DEBUG
	debug("in read_table");
# endif

/*
 * make sure _sactab is ok
 */

	Nentries = 0;
	if ((ret = check_version(VERSION, SACTAB)) == 1)
		error(E_BADVER, EXIT);
	else if (ret == 2)
		error(E_SACOPEN, EXIT);
	else if (ret == 3)
		error(E_BADFILE, EXIT);
	fp = fopen(SACTAB, "r");
	if (fp == NULL)
		error(E_SACOPEN, EXIT);

/*
 * mark all entries as invalid
 */

	for (sp = Sactab; sp; sp = sp->sc_next)
		sp->sc_valid = 0;

/*
 * build internal structures
 */

	while (sp = read_entry(fp))
		insert(sp, startflag);
	purge();
	(void) fclose(fp);
}


/*
 * read_entry - read an entry from _sactab
 *
 *	args:	fp - file pointer referencing _sactab
 */

struct sactab *
read_entry(fp)
FILE *fp;
{
	register struct sactab *sp;	/* working pointer */
	register char *p;		/* scratch pointer */
	char buf[SIZE];			/* scratch buffer */

/*
 * retrieve a line from the file
 */

	do {
		if (fgets(buf, SIZE, fp) == NULL)
			return(NULL);
		p = trim(buf);
	} while (*p == '\0');

/*
 * allocate a list element for it and then parse the line, parsed
 * info goes into list element
 */

	sp = (struct sactab *) calloc(1, sizeof(struct sactab));
	if (sp == NULL)
		error(E_MALLOC, EXIT);
	sp->sc_sstate = sp->sc_lstate = sp->sc_pstate = NOTRUNNING;
	(void) memset(sp->sc_utid, '\0', IDLEN);
	parse(p, sp);
	return(sp);
}


/*
 * insert - insert a sactab entry into the linked list
 *
 *	args:	sp - entry to be inserted
 *		startflag - flag to indicate if port monitor's should be
 *			    started as a side effect of reading
 */

void
insert(sp, startflag)
register struct sactab *sp;
int startflag;
{
	register struct sactab *tsp, *savtsp;	/* scratch pointers */
	int ret;				/* strcmp return value */

# ifdef DEBUG
	debug("in insert");
# endif
	savtsp = tsp = Sactab;

/*
 * find the correct place to insert this element
 */

	while (tsp) {
		ret = strcmp(sp->sc_tag, tsp->sc_tag);
# ifdef DEBUG
		(void) sprintf(Scratch, "sp->sc_tag <%s> tsp->sc_tag <%s>, ret is %d", sp->sc_tag, tsp->sc_tag, ret);
		debug(Scratch);
# endif
		if (ret > 0) {
			/* keep on looking */
			savtsp = tsp;
			tsp = tsp->sc_next;
			continue;
		}
		else if (ret == 0) {

/*
 * found an entry for it in the list, either a duplicate or we're
 * rereading the file.
 */

			if (tsp->sc_valid) {
				/* this is a duplicate entry, ignore it */
				(void) sprintf(Scratch, "Ignoring duplicate entry for <%s>", tsp->sc_tag);
				log(Scratch);
			}
			else {
				/* found a valid match, replace flags & restart max only */
				tsp->sc_rsmax = sp->sc_rsmax;
				tsp->sc_flags = sp->sc_flags;
# ifdef DEBUG
				(void) sprintf(Scratch, "replacing <%s>", sp->sc_tag);
				debug(Scratch);
# endif
				/* this entry is "current" */
				tsp->sc_valid = 1;
				Nentries++;
			}
			free(sp->sc_cmd);
			free(sp);
			return;
		}
		else {
			/* insert it here */
			if (tsp == Sactab) {
				sp->sc_next = Sactab;
				Sactab = sp;
			}
			else {
				sp->sc_next = savtsp->sc_next;
				savtsp->sc_next = sp;
			}
# ifdef DEBUG
			(void) sprintf(Scratch, "adding <%s>", sp->sc_tag);
			debug(Scratch);
# endif
			Nentries++;
			/* this entry is "current" */
			sp->sc_valid = 1;
			if (startflag && !(sp->sc_flags & X_FLAG))
				(void) startpm(sp);
			return;
		}
	}

/*
 * either an empty list or should put element at end of list
 */

	sp->sc_next = NULL;
	if (Sactab == NULL)
		Sactab = sp;
	else
		savtsp->sc_next = sp;
# ifdef DEBUG
	(void) sprintf(Scratch, "adding <%s>", sp->sc_tag);
	debug(Scratch);
# endif
	++Nentries;
	/* this entry is "current" */
	sp->sc_valid = 1;
	if (startflag && !(sp->sc_flags & X_FLAG))
		(void) startpm(sp);
}



/*
 * purge - purge linked list of "old" entries
 */


void
purge()
{
	register struct sactab *sp;		/* working pointer */
	register struct sactab *savesp, *tsp;	/* scratch pointers */
	sigset_t cset;				/* for signal handling */
	sigset_t tset;				/* for signal handling */

# ifdef DEBUG
	debug("in purge");
# endif
	/* get current signal mask */
	(void) sigprocmask(SIG_SETMASK, NULL, &cset);
	sp = savesp = Sactab;
	while (sp) {
		if (sp->sc_valid) {
			savesp = sp;
			sp = sp->sc_next;
			continue;
		}

		/* element should be removed */
		switch (sp->sc_sstate) {
		case UNKNOWN:
		case ENABLED:
		case DISABLED:
		case STARTING:
			/* need to kill it */
			tset = cset;
			(void) sigaddset(&tset, SIGALRM);
			(void) sigaddset(&tset, SIGCLD);
			(void) sigprocmask(SIG_SETMASK, &tset, NULL);
			if (sendsig(sp, SIGTERM))
				(void) sprintf(Scratch, "could not send SIGTERM to <%s>", sp->sc_tag);
			else
				(void) sprintf(Scratch, "terminating <%s>", sp->sc_tag);
			log(Scratch);
			(void) sigdelset(&tset, SIGALRM);
			(void) sigprocmask(SIG_SETMASK, &tset, NULL);
			/* fall thru */
		case STOPPING:
			(void) close(sp->sc_fd);
			/* fall thru */
		case NOTRUNNING:
		case FAILED:
			cleanut(sp);
			tsp = sp;
			if (tsp == Sactab) {
				Sactab = sp->sc_next;
				savesp = Sactab;
			}
			else
				savesp->sc_next = sp->sc_next;
# ifdef DEBUG
			(void) sprintf(Scratch, "purging <%s>", sp->sc_tag);
			debug(Scratch);
# endif
			sp = sp->sc_next;
			free(tsp->sc_cmd);
			free(tsp->sc_comment);
			free(tsp);

/*
 * all done cleaning up, restore signal mask
 */

			(void) sigprocmask(SIG_SETMASK, &cset, NULL);
			break;
		}
	}
}


/*
 * dump_table - dump the internal SAC table, used to satisfy sacadm -l
 */


char **
dump_table()
{
	register struct sactab *sp;	/* working pointer */
	register char *p;		/* scratch pointer */
	register int size;		/* size of "dumped" table */
	char **info, **savinfo;		/* scratch pointers */

# ifdef DEBUG
	(void) sprintf(Scratch, "about to 'info' malloc %d entries", Nentries);
	debug(Scratch);
# endif

/*
 * get space for number of entries we have
 */

	if (Nentries == 0)
		return(NULL);
	if ((info = (char **) malloc(Nentries * sizeof(char *))) == NULL) {
		error(E_MALLOC, CONT);
		return(NULL);
	}
	savinfo = info;

/*
 * traverse the list allocating space for entries and formatting them
 */

	for (sp = Sactab; sp; sp = sp->sc_next) {
		size = strlen(sp->sc_tag) + strlen(sp->sc_type) + strlen(sp->sc_cmd) + strlen(sp->sc_comment) + SLOP;
		if ((p = malloc((unsigned) size)) == NULL) {
			error(E_MALLOC, CONT);
			return(NULL);
		}
		(void) sprintf(p, "%s:%s:%d:%d:%d:%s:%s\n", sp->sc_tag, sp->sc_type,
			sp->sc_flags, sp->sc_rsmax, sp->sc_pstate, sp->sc_cmd, sp->sc_comment);
		*info++ = p;
# ifdef DEBUG
		debug(*(info - 1));
# endif
	}
	return(savinfo);
}
