OpenSolaris_b135/cmd/saf/pmadm.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "extern.h"
#include "misc.h"
#include <sac.h>
#include "structs.h"

#define	ADD		0x1	/* -a or other required options seen */
#define	REMOVE		0x2	/* -r seen */
#define	ENABLE		0x4	/* -e seen */
#define	DISABLE		0x8	/* -d seen */
#define	PLIST		0x10	/* -l seen */
#define	LIST		0x20	/* -L seen */
#define	CONFIG		0x40	/* -g seen */

# define U_FLAG		0x1	/* -fu seen */
# define X_FLAG		0x2	/* -fx seen */

/*
 * functions
 */

char	*pflags();
char	*pspec();
struct	taglist	*find_type();
void	usage();
void	parseline();
void	add_svc();
void	rem_svc();
void	ed_svc();
void	list_svcs();
void	doconf();

/*
 * format of a _pmtab entry - used to hold parsed info
 */

struct	pmtab {
	char	*p_tag;		/* service tag */
	long	p_flags;	/* flags */
	char	*p_id;		/* logname to start service as */
	char	*p_res1;	/* reserved field */
	char	*p_res2;	/* reserved field */
	char	*p_res3;	/* reserved field */
	char	*p_pmspec;	/* port monitor specific info */
};

/*
 * format of a tag list, which is a list of port monitor tags of
 * a designated type
 */

struct	taglist {
	struct	taglist	*t_next;	/* next in list */
	char	t_tag[PMTAGSIZE + 1];	/* PM tag */
	char	t_type[PMTYPESIZE + 1];	/* PM type */
};

/*
 * common error messages
 */

# define NOTPRIV	"User not privileged for operation"
# define BADINP		"Embedded newlines not allowed"

int	Saferrno;	/* internal `errno' for exit */


/*
 * main - scan args for pmadm and call appropriate handling code
 */

int
main(int argc, char *argv[])
{
	int c;			/* option letter */
	int ret;		/* return code from check_version */
	uid_t uid;		/* invoker's real uid */
	int flag = 0;		/* flag to record requested operations */
	int errflg = 0;		/* error indicator */
	int badcnt = 0;		/* count of bad args to -f */
	int version = -1;	/* argument to -v */
	int sawaflag = 0;	/* true if actually saw -a */
	int conflag = 0;	/* true if output should be in condensed form */
	long flags = 0;		/* arguments to -f */
	char *pmtag = NULL;	/* argument to -p */
	char *type = NULL;	/* argument to -t */
	char *script = NULL;	/* argument to -z */
	char *comment = " ";	/* argument to -y */
	char *id = NULL;	/* argument to -i */
	char *svctag = NULL;	/* argument to -s */
	char *pmspec = NULL;	/* argument to -m */
	char badargs[SIZE];	/* place to hold bad args to -f */
	char buf[SIZE];		/* scratch buffer */
	register char *p;	/* scratch pointer */

	if (argc == 1)
		usage(argv[0]);
	while ((c = getopt(argc, argv, "adef:gi:Llm:p:rs:t:v:y:z:")) != -1) {
		switch (c) {
		case 'a':
			flag |= ADD;
			sawaflag = 1;
			break;
		case 'd':
			flag |= DISABLE;
			break;
		case 'e':
			flag |= ENABLE;
			break;
		case 'f':
			flag |= ADD;
			while (*optarg) {
				switch (*optarg++) {
				case 'u':
					flags |= U_FLAG;
					break;
				case 'x':
					flags |= X_FLAG;
					break;
				default:
					badargs[badcnt++] = *(optarg - 1);
					break;
				}
			}
			/* null terminate just in case anything is there */
			badargs[badcnt] = '\0';
			break;
		case 'g':
			flag |= CONFIG;
			break;
		case 'i':
			if (strchr(optarg, '\n')) {
				Saferrno = E_BADARGS;
				error(BADINP);
			}
			flag |= ADD;
			id = optarg;
			break;
		case 'L':
			flag |= LIST;
			break;
		case 'l':
			flag |= PLIST;
			break;
		case 'm':
			if (strchr(optarg, '\n')) {
				Saferrno = E_BADARGS;
				error(BADINP);
			}
			if (*optarg == '\0') {
				/* this will generate a usage message below */
				errflg++;
				break;
			}
			flag |= ADD;
			pmspec = optarg;
			break;
		case 'p':
			if (strchr(optarg, '\n')) {
				Saferrno = E_BADARGS;
				error(BADINP);
			}
			pmtag = optarg;
			if (strlen(pmtag) > PMTAGSIZE) {
				pmtag[PMTAGSIZE] = '\0';
				(void) fprintf(stderr, "tag too long, truncated to <%s>\n", pmtag);
			}
			for (p = pmtag; *p; p++) {
				if (!isalnum(*p)) {
					Saferrno = E_BADARGS;
					error("port monitor tag must be alphanumeric");
				}
			}
			break;
		case 'r':
			flag |= REMOVE;
			break;
		case 's':
			if (strchr(optarg, '\n')) {
				Saferrno = E_BADARGS;
				error(BADINP);
			}
			svctag = optarg;
			if (strlen(svctag) > SVCTAGSIZE) {
				svctag[SVCTAGSIZE] = '\0';
				(void) fprintf(stderr, "svctag too long, truncated to <%s>\n", svctag);
			}
			for (p = svctag; *p; p++) {
				if (!isalnum(*p)) {
					Saferrno = E_BADARGS;
					error("service tag must be alphanumeric");
				}
			}
			break;
		case 't':
			if (strchr(optarg, '\n')) {
				Saferrno = E_BADARGS;
				error(BADINP);
			}
			type = optarg;
			if (strlen(type) > PMTYPESIZE) {
				type[PMTYPESIZE] = '\0';
				(void) fprintf(stderr, "type too long, truncated to <%s>\n", type);
			}
			for (p = type; *p; p++) {
				if (!isalnum(*p)) {
					Saferrno = E_BADARGS;
					error("port monitor type must be alphanumeric");
				}
			}
			break;
		case 'v':
			flag |= ADD;
			version = atoi(optarg);
			if (version < 0) {
				Saferrno = E_BADARGS;
				error("version number can not be negative");
			}
			break;
		case 'y':
			if (strchr(optarg, '\n')) {
				Saferrno = E_BADARGS;
				error(BADINP);
			}
			flag |= ADD;
			comment = optarg;
			break;
		case 'z':
			if (strchr(optarg, '\n')) {
				Saferrno = E_BADARGS;
				error(BADINP);
			}
			script = optarg;
			break;
		case '?':
			errflg++;
		}
	}
	if (errflg || (optind < argc))
		usage(argv[0]);

	if (badcnt) {
		/* bad flags were given to -f */
		(void) sprintf(buf, "Invalid request, %s are not valid arguments for \"-f\"", badargs);
		Saferrno = E_BADARGS;
		error(buf);
	}

	uid = getuid();

/*
 * don't do anything if _sactab isn't the version we understand
 */

	if ((ret = check_version(VERSION, SACTAB)) == 1) {
		Saferrno = E_SAFERR;
		error("_sactab version number is incorrect");
	}
	else if (ret == 2) {
		(void) sprintf(buf, "could not open %s", SACTAB);
		Saferrno = E_SYSERR;
		error(buf);
	}
	else if (ret == 3) {
		(void) sprintf(buf, "%s file is corrupt", SACTAB);
		Saferrno = E_SAFERR;
		error(buf);
	}

	switch (flag) {
	case ADD:
		if (uid) {
			Saferrno = E_NOPRIV;
			error(NOTPRIV);
		}
		if (!sawaflag || (pmtag && type) || (!pmtag && !type) || !svctag || !id || !pmspec || (version < 0))
			usage(argv[0]);
		add_svc(pmtag, type, svctag, id, pmspec, flags, version, comment, script);
		break;
	case REMOVE:
		if (uid) {
			Saferrno = E_NOPRIV;
			error(NOTPRIV);
		}
		if (!pmtag || !svctag || type || script)
			usage(argv[0]);
		rem_svc(pmtag, svctag);
		break;
	case ENABLE:
		if (uid) {
			Saferrno = E_NOPRIV;
			error(NOTPRIV);
		}
		if (!pmtag || !svctag || type || script)
			usage(argv[0]);
		ed_svc(pmtag, svctag, ENABLE);
		break;
	case DISABLE:
		if (uid) {
			Saferrno = E_NOPRIV;
			error(NOTPRIV);
		}
		if (!pmtag || !svctag || type || script)
			usage(argv[0]);
		ed_svc(pmtag, svctag, DISABLE);
		break;
	case LIST:
		conflag = 1;
		/* fall through */
	case PLIST:
		if ((pmtag && type) || script)
			usage(argv[0]);
		list_svcs(pmtag, type, svctag, conflag);
		break;
	case CONFIG:
		if (script && uid) {
			Saferrno = E_NOPRIV;
			error(NOTPRIV);
		}
		if ((pmtag && type) || (!pmtag && !type) || !svctag || (type && !script))
			usage(argv[0]);
		doconf(script, pmtag, type, svctag);
		break;
	default:
		/* we only get here if more than one flag bit was set */
		usage(argv[0]);
		/* NOTREACHED */
	}
	quit();
	/* NOTREACHED */
}


/*
 * usage - print out a usage message
 *
 *	args:	cmdname - the name command was invoked with
 */

void
usage(cmdname)
char *cmdname;
{
	(void) fprintf(stderr, "Usage:\t%s -a [ -p pmtag | -t type ] -s svctag -i id -m \"pmspecific\"\n", cmdname);
	(void) fprintf(stderr, "\t\t-v version [ -f xu ] [ -y comment ] [ -z script]\n");
	(void) fprintf(stderr, "\t%s -r -p pmtag -s svctag\n", cmdname);
	(void) fprintf(stderr, "\t%s -e -p pmtag -s svctag\n", cmdname);
	(void) fprintf(stderr, "\t%s -d -p pmtag -s svctag\n", cmdname);
	(void) fprintf(stderr, "\t%s -l [ -p pmtag | -t type ] [ -s svctag ]\n", cmdname);
	(void) fprintf(stderr, "\t%s -L [ -p pmtag | -t type ] [ -s svctag ]\n", cmdname);
	(void) fprintf(stderr, "\t%s -g -p pmtag -s svctag [ -z script ]\n", cmdname);
	(void) fprintf(stderr, "\t%s -g -s svctag -t type -z script\n", cmdname);
	Saferrno = E_BADARGS;
	quit();
}


/*
 * add_svc - add a service entry
 *
 *	args:	tag - port monitor's tag (may be null)
 *		type - port monitor's type (may be null)
 *		svctag - service's tag
 *		id - identity under which service should run
 *		pmspec - uninterpreted port monitor-specific info
 *		flags - service flags
 *		version - version number of port monitor's pmtab
 *		comment - comment describing service
 *		script - service's configuration script
 */

void
add_svc(tag, type, svctag, id, pmspec, flags, version, comment, script)
char *tag;
char *type;
char *svctag;
char *id;
char *pmspec;
long flags;
int version;
char *comment;
char *script;
{
	FILE *fp;			/* scratch file pointer */
	struct taglist tl;		/* 'list' for degenerate case (1 PM) */
	register struct taglist *tp = NULL;	/* working pointer */
	int ret;			/* return code from check_version */
	char buf[SIZE];			/* scratch buffer */
	char fname[SIZE];		/* scratch buffer for building names */
	int added;			/* count number added */

	fp = fopen(SACTAB, "r");
	if (fp == NULL) {
		Saferrno = E_SYSERR;
		error("Could not open _sactab");
	}
	if (tag && !find_pm(fp, tag)) {
		(void) sprintf(buf, "Invalid request, %s does not exist", tag);
		Saferrno = E_NOEXIST;
		error(buf);
	}
	if (type && !(tp = find_type(fp, type))) {
		(void) sprintf(buf, "Invalid request, %s does not exist", type);
		Saferrno = E_NOEXIST;
		error(buf);
	}
	(void) fclose(fp);

	if (tag) {

/*
 * treat the case of 1 PM as a degenerate case of a list of PMs from a
 * type specification.  Build the 'list' here.
 */

		tp = &tl;
		tp->t_next = NULL;
		(void) strcpy(tp->t_tag, tag);
	}

	added = 0;
	while (tp) {
		(void) sprintf(fname, "%s/%s/_pmtab", HOME, tp->t_tag);
		if ((ret = check_version(version, fname)) == 1) {
			(void) sprintf(buf, "%s version number is incorrect", fname);
			Saferrno = E_SAFERR;
			error(buf);
		}
		else if (ret == 2) {
			(void) sprintf(buf, "could not open %s", fname);
			Saferrno = E_SYSERR;
			error(buf);
		}
		else if (ret == 3) {
			(void) sprintf(buf, "%s file is corrupt", fname);
			Saferrno = E_SAFERR;
			error(buf);
		}
		fp = fopen(fname, "r");
		if (fp == NULL) {
			(void) sprintf(buf, "Could not open %s", fname);
			Saferrno = E_SYSERR;
			error(buf);
		}
		if (find_svc(fp, tp->t_tag, svctag)) {
			if (tag) {
				/* special case of tag only */
				(void) sprintf(buf, "Invalid request, %s already exists under %s", svctag, tag);
				Saferrno = E_DUP;
				error(buf);
			}
			else {
				(void) fprintf(stderr, "warning - %s already exists under %s - ignoring\n", svctag, tp->t_tag);
				tp = tp->t_next;
				(void) fclose(fp);
				continue;
			}
		}
		(void) fclose(fp);

/*
 * put in the config script, if specified
*/

		if (script) {
			(void) sprintf(fname, "%s/%s", tp->t_tag, svctag);
			if (do_config(script, fname)) {
				/* do_config put out any messages */
				tp = tp->t_next;
				continue;
			}
		}

/*
 * add the line
 */

		(void) sprintf(fname, "%s/%s/_pmtab", HOME, tp->t_tag);
		fp = fopen(fname, "a");
		if (fp == NULL) {
			(void) sprintf(buf, "Could not open %s", fname);
			Saferrno = E_SYSERR;
			error(buf);
		}
		(void) fprintf(fp, "%s:%s:%s:reserved:reserved:reserved:%s#%s\n",
			svctag, (flags ? pflags(flags, FALSE) : ""), id, pmspec,
			(comment ? comment : ""));
		(void) fclose(fp);
		added++;

/*
 * tell the SAC to to tell PM to read _pmtab
 */

		(void) tell_sac(tp->t_tag);
		tp = tp->t_next;
	}
	if (added == 0) {
		Saferrno = E_SAFERR;
		error("No services added");
	}
	return;
}


/*
 * rem_svc - remove a service
 *
 *	args:	pmtag - tag of port monitor responsible for the service
 *		svctag - tag of the service to be removed
 */

void
rem_svc(pmtag, svctag)
char *pmtag;
char *svctag;
{
	FILE *fp;		/* scratch file pointer */
	FILE *tfp;		/* file pointer for temp file */
	int line;		/* line number entry is on */
	char *tname;		/* temp file name */
	char buf[SIZE];		/* scratch buffer */
	char fname[SIZE];	/* path to correct _pmtab */

	fp = fopen(SACTAB, "r");
	if (fp == NULL) {
		Saferrno = E_SYSERR;
		error("Could not open _sactab");
	}
	if (!find_pm(fp, pmtag)) {
		(void) sprintf(buf, "Invalid request, %s does not exist", pmtag);
		Saferrno = E_NOEXIST;
		error(buf);
	}
	(void) fclose(fp);

	(void) sprintf(fname, "%s/_pmtab", pmtag);
	(void) sprintf(buf, "%s/%s", HOME, fname);
	fp = fopen(buf, "r");
	if (fp == NULL) {
		(void) sprintf(buf, "Could not open %s/%s", HOME, fname);
		Saferrno = E_SYSERR;
		error(buf);
	}
	if ((line = find_svc(fp, pmtag, svctag)) == 0) {
		(void) sprintf(buf, "Invalid request, %s does not exist under %s", svctag, pmtag);
		Saferrno = E_NOEXIST;
		error(buf);
	}
	tname = make_tempname(fname);
	tfp = open_temp(tname);
	if (line != 1) {
		if (copy_file(fp, tfp, 1, line - 1)) {
			(void) unlink(tname);
			Saferrno = E_SYSERR;
			error("error accessing temp file");
		}
	}
	if (copy_file(fp, tfp, line + 1, -1)) {
		(void) unlink(tname);
		Saferrno = E_SYSERR;
		error("error accessing temp file");
	}
	(void) fclose(fp);
	if (fclose(tfp) == EOF) {
		(void) unlink(tname);
		Saferrno = E_SYSERR;
		error("error closing tempfile");
	}
	/* note - replace only returns if successful */
	replace(fname, tname);

/*
 * tell the SAC to to tell PM to read _pmtab
 */

	if (tell_sac(pmtag)) {

/*
 * if we got rid of the service, try to remove the config script too.
 * Don't check return status since it may not have existed anyhow.
 */

		(void) sprintf(buf, "%s/%s/%s", HOME, pmtag, svctag);
		(void) unlink(buf);
		return;
	}
}



/*
 * ed_svc - enable or disable a particular service
 *
 *	args:	pmtag - tag of port monitor responsible for the service
 *		svctag - tag of service to be enabled or disabled
 *		flag - operation to perform (ENABLE or DISABLE)
 */

void
ed_svc(pmtag, svctag, flag)
char *pmtag;
char *svctag;
int flag;
{
	FILE *fp;		/* scratch file pointer */
	FILE *tfp;		/* file pointer for temp file */
	int line;		/* line number entry is on */
	register char *from;	/* working pointer */
	register char *to;	/* working pointer */
	char *tname;		/* temp file name */
	char *p;		/* scratch pointer */
	char buf[SIZE];		/* scratch buffer */
	char tbuf[SIZE];	/* scratch buffer */
	char fname[SIZE];	/* path to correct _pmtab */

	fp = fopen(SACTAB, "r");
	if (fp == NULL) {
		Saferrno = E_SYSERR;
		error("Could not open _sactab");
	}
	if (!find_pm(fp, pmtag)) {
		(void) sprintf(buf, "Invalid request, %s does not exist", pmtag);
		Saferrno = E_NOEXIST;
		error(buf);
	}
	(void) fclose(fp);

	(void) sprintf(fname, "%s/_pmtab", pmtag);
	(void) sprintf(buf, "%s/%s", HOME, fname);
	fp = fopen(buf, "r");
	if (fp == NULL) {
		(void) sprintf(buf, "Could not open %s/%s", HOME, fname);
		Saferrno = E_SYSERR;
		error(buf);
	}
	if ((line = find_svc(fp, pmtag, svctag)) == 0) {
		(void) sprintf(buf, "Invalid request, %s does not exist under %s", svctag, pmtag);
		Saferrno = E_NOEXIST;
		error(buf);
	}
	tname = make_tempname(fname);
	tfp = open_temp(tname);
	if (line != 1) {
		if (copy_file(fp, tfp, 1, line - 1)) {
			(void) unlink(tname);
			Saferrno = E_SYSERR;
			error("error accessing temp file");
		}
	}

/*
 * Note: find_svc above has already read and parsed this entry, thus
 * we know it to be well-formed, so just change the flags as appropriate
 */

	if (fgets(buf, SIZE, fp) == NULL) {
		(void) unlink(tname);
		Saferrno = E_SYSERR;
		error("error accessing temp file");
	}
	from = buf;
	to = tbuf;

/*
 * copy initial portion of entry
 */

	p = strchr(from, DELIMC);
	for ( ; from <= p; )
		*to++ = *from++;

/*
 * isolate and fix the flags
 */

	p = strchr(from, DELIMC);
	for ( ; from < p; ) {
		if (*from == 'x') {
			from++;
			continue;
		}
		*to++ = *from++;
	}

/*
 * above we removed x flag, if this was a disable operation, stick it in
 * and also copy the field delimiter
 */

	if (flag == DISABLE)
		*to++ = 'x';
	*to++ = *from++;

/*
 * copy the rest of the line
 */

	for ( ; from < &buf[SIZE - 1] ;)
		*to++ = *from++;
/***	*to = '\0';  BUG: Don't uncomment it ****/

	(void) fprintf(tfp, "%s", tbuf);

	if (copy_file(fp, tfp, line + 1, -1)) {
		(void) unlink(tname);
		Saferrno = E_SYSERR;
		error("error accessing temp file");
	}
	(void) fclose(fp);
	if (fclose(tfp) == EOF) {
		(void) unlink(tname);
		Saferrno = E_SYSERR;
		error("error closing tempfile");
	}
	/* note - replace only returns if successful */
	replace(fname, tname);


/*
 * tell the SAC to to tell PM to read _pmtab
 */

	(void) tell_sac(pmtag);
}


/*
 * doconf - take a config script and have it put where it belongs or
 *	    output an existing one
 *
 *	args:	script - name of file containing script (if NULL, means
 *			 output existing one instead)
 *		tag - tag of port monitor that is responsible for the
 *		      designated service (may be null)
 *		type - type of port monitor that is responsible for the
 *		       designated service (may be null)
 *		svctag - tag of service whose config script we're operating on
 */

void
doconf(script, tag, type, svctag)
char *script;
char *tag;
char *type;
char *svctag;
{
	FILE *fp;			/* scratch file pointer */
	int added;			/* count of config scripts added */
	struct taglist tl;		/* 'list' for degenerate case (1 PM) */
	register struct taglist *tp = NULL;	/* working pointer */
	char buf[SIZE];			/* scratch buffer */
	char fname[SIZE];		/* scratch buffer for names */

	fp = fopen(SACTAB, "r");
	if (fp == NULL) {
		Saferrno = E_SYSERR;
		error("Could not open _sactab");
	}
	if (tag && !find_pm(fp, tag)) {
		(void) sprintf(buf, "Invalid request, %s does not exist", tag);
		Saferrno = E_NOEXIST;
		error(buf);
	}
	if (type && !(tp = find_type(fp, type))) {
		(void) sprintf(buf, "Invalid request, %s does not exist", type);
		Saferrno = E_NOEXIST;
		error(buf);
	}
	(void) fclose(fp);

	if (tag) {

/*
 * treat the case of 1 PM as a degenerate case of a list of PMs from a
 * type specification.  Build the 'list' here.
 */

		tp = &tl;
		tp->t_next = NULL;
		(void) strcpy(tp->t_tag, tag);
	}

	added = 0;
	while (tp) {
		(void) sprintf(fname, "%s/%s/_pmtab", HOME, tp->t_tag);
		fp = fopen(fname, "r");
		if (fp == NULL) {
			(void) sprintf(buf, "Could not open %s", fname);
			Saferrno = E_SYSERR;
			error(buf);
		}
		if (!find_svc(fp, tp->t_tag, svctag)) {
			if (tag) {
				/* special case of tag only */
				(void) sprintf(buf, "Invalid request, %s does not exist under %s", svctag, tag);
				Saferrno = E_NOEXIST;
				error(buf);
			}
			else {
				(void) fprintf(stderr, "warning - %s does not exist under %s - ignoring\n", svctag, tp->t_tag);
				Saferrno = E_NOEXIST;
				tp = tp->t_next;
				(void) fclose(fp);
				continue;
			}
		}
		(void) fclose(fp);

		(void) sprintf(fname, "%s/%s", tp->t_tag, svctag);

/*
 * do_config does all the real work (keep track if any errors occurred)
 */

		if (do_config(script, fname) == 0)
			added++;
		tp = tp->t_next;
	}
	if (added == 0) {
		Saferrno = E_SAFERR;
		error("No configuration scripts installed");
	}
	return;
}


/*
 * tell_sac - use sacadm to tell the sac to tell a port monitor to read
 *	its _pmtab.  Return TRUE on success, FALSE on failure.
 *
 *	args:	tag - tag of port monitor to be notified
 */


int
tell_sac(char *tag)
{
	pid_t pid;	/* returned pid from fork */
	int status;	/* return status from sacadm child */

	if ((pid = fork()) < 0) {
		(void) fprintf(stderr, "warning - fork failed - could not notify <%s> about modified table\n", tag);
		(void) fprintf(stderr, "try executing the command \"sacadm -x -p %s\"\n", tag);
		Saferrno = E_SYSERR;
		return(FALSE);
	}
	else if (pid) {
		/* parent */
		(void) wait(&status);
		if (status) {
			if (((status >> 8) & 0xff) == E_PMNOTRUN) {
				(void) fprintf(stderr, "warning - port monitor, %s is not running\n", tag);
				return (FALSE);
			}
			if (((status >> 8) & 0xff) == E_SACNOTRUN) {
				Saferrno = E_SACNOTRUN;
			} else {
				Saferrno = E_SYSERR;
			}
			(void) fprintf(stderr,
			    "warning - could not notify <%s> about modified"
			    " table\n", tag);
			(void) fprintf(stderr, "try executing the command"
			    " \"sacadm -x -p %s\"\n", tag);
			return(FALSE);
		}
		else {
			return(TRUE);
		}
	}
	else {
		/* set IFS for security */
		(void) putenv("IFS=\" \"");
		/* muffle sacadm warning messages */
		(void) fclose(stderr);
		(void) fopen("/dev/null", "w");
		(void) execl("/usr/sbin/sacadm", "sacadm", "-x", "-p", tag, 0);

/*
 * if we got here, it didn't work, exit status will clue in parent to
 * put out the warning
 */

		exit(1);
	}
	/* NOTREACHED */
}


/*
 * list_svcs - list information about services
 *
 *	args:	pmtag - tag of port monitor responsible for the service
 *			(may be null)
 *		type - type of port monitor responsible for the service
 *		       (may be null)
 *		svctag - tag of service to be listed (may be null)
 *		oflag - true if output should be easily parseable
 */

void
list_svcs(pmtag, type, svctag, oflag)
char *pmtag;
char *type;
char *svctag;
{
	FILE *fp;				/* scratch file pointer */
	register struct taglist *tp;		/* pointer to PM list */
	int nprint = 0;				/* count # of svcs printed */
	struct pmtab pmtab;			/* place to hold parsed info */
	register struct pmtab *pp = &pmtab;	/* and a pointer to it */
	register char *p;			/* working pointer */
	char buf[SIZE];				/* scratch buffer */
	char fname[SIZE];			/* scratch buffer for building names */

	fp = fopen(SACTAB, "r");
	if (fp == NULL) {
		Saferrno = E_SYSERR;
		error("Could not open _sactab");
	}
	if (pmtag && !find_pm(fp, pmtag)) {
		(void) sprintf(buf, "Invalid request, %s does not exist", pmtag);
		Saferrno = E_NOEXIST;
		error(buf);
	}
	rewind(fp);
	if (type) {
		tp = find_type(fp, type);
		if (tp == NULL) {
			(void) sprintf(buf, "Invalid request, %s does not exist", type);
			Saferrno = E_NOEXIST;
			error(buf);
		}
	}
	else
		tp = find_type(fp, NULL);
	(void) fclose(fp);

	while (tp) {
		if (pmtag && strcmp(tp->t_tag, pmtag)) {
			/* not interested in this port monitor */
			tp = tp->t_next;
			continue;
		}
		(void) sprintf(fname, "%s/%s/_pmtab", HOME, tp->t_tag);
		fp = fopen(fname, "r");
		if (fp == NULL) {
			(void) sprintf(buf, "Could not open %s", fname);
			Saferrno = E_SYSERR;
			error(buf);
		}
		while (fgets(buf, SIZE, fp)) {
			p = trim(buf);
			if (*p == '\0')
				continue;
			parseline(p, pp, tp->t_tag);
			if (!svctag || !strcmp(pp->p_tag, svctag)) {
				if (oflag) {
					(void) printf("%s:%s:%s:%s:%s:%s:%s:%s:%s#%s\n",
						tp->t_tag, tp->t_type, pp->p_tag,
						pflags(pp->p_flags, FALSE),
						pp->p_id, pp->p_res1, pp->p_res2,
						pp->p_res3,pp->p_pmspec, Comment);
				}
				else {
					if (nprint == 0) {
						(void) printf("PMTAG          PMTYPE         SVCTAG         FLGS ID       <PMSPECIFIC>\n");
					}
					(void) printf("%-14s %-14s %-14s %-4s %-8s %s #%s\n", tp->t_tag, tp->t_type, pp->p_tag,
						pflags(pp->p_flags, TRUE), pp->p_id, pspec(pp->p_pmspec), Comment);
				}
				nprint++;
			}
		}
		if (!feof(fp)) {
			(void) sprintf(buf, "error reading %s", fname);
			Saferrno = E_SYSERR;
			error(buf);
		}
		else {
			(void) fclose(fp);
			tp = tp->t_next;
		}
	}
	/* if we didn't find any valid ones, indicate an error */
	if (nprint == 0) {
		if (svctag)
			(void) fprintf(stderr, "Service <%s> does not exist\n", svctag);
		else
			(void) fprintf(stderr, "No services defined\n");
		Saferrno = E_NOEXIST;
	}
	return;
}


/*
 * find_svc - find an entry in _pmtab for a particular service tag
 *
 *	args:	fp - file pointer for _pmtab
 *		tag - port monitor tag (for error reporting)
 *		svctag - tag of service we're looking for
 */

int
find_svc(FILE *fp, char *tag, char *svctag)
{
	register char *p;	/* working pointer */
	int line = 0;		/* line number we found entry on */
	struct pmtab pmtab;	/* place to hold parsed info */
	static char buf[SIZE];	/* scratch buffer */

	while (fgets(buf, SIZE, fp)) {
		line++;
		p = trim(buf);
		if (*p == '\0')
			continue;
		parseline(p, &pmtab, tag);
		if (!(strcmp(pmtab.p_tag, svctag)))
			return(line);
	}
	if (!feof(fp)) {
		(void) sprintf(buf, "error reading %s/%s/_pmtab", HOME, tag);
		Saferrno = E_SYSERR;
		error(buf);
		/* NOTREACHED */
		return (0);
	} else
		return (0);
}


/*
 * parseline - parse a line from _pmtab.  This routine will return if the
 *		parse wa successful, otherwise it will output an error and
 *		exit.
 *
 *	args:	p - pointer to the data read from the file (note - this is
 *		    a static data region, so we can point into it)
 *		pp - pointer to a structure in which the separated fields
 *		     are placed
 *		tag - port monitor tag (for error reporting)
 *
 *	A line in the file has the following format:
 *
 *	tag:flags:identity:reserved:reserved:reserved:PM_spec_info # comment
 */


void
parseline(p, pp, tag)
register char *p;
register struct pmtab *pp;
char *tag;
{
	char buf[SIZE];	/* scratch buffer */

/*
 * get the service tag
 */

	p = nexttok(p, DELIM, FALSE);
	if (p == NULL) {
		(void) sprintf(buf, "%s/%s/_pmtab is corrupt", HOME, tag);
		Saferrno = E_SAFERR;
		error(buf);
	}
	if (strlen(p) > PMTAGSIZE) {
		p[PMTAGSIZE] = '\0';
		(void) fprintf(stderr, "tag too long, truncated to <%s>", p);
	}
	pp->p_tag = p;

/*
 * get the flags
 */

	p = nexttok(NULL, DELIM, FALSE);
	if (p == NULL) {
		(void) sprintf(buf, "%s/%s/_pmtab is corrupt", HOME, tag);
		Saferrno = E_SAFERR;
		error(buf);
	}
	pp->p_flags = 0;
	while (*p) {
		switch (*p++) {
		case 'u':
			pp->p_flags |= U_FLAG;
			break;
		case 'x':
			pp->p_flags |= X_FLAG;
			break;
		default:
			(void) sprintf(buf, "Unrecognized flag <%c>", *(p - 1));
			Saferrno = E_SAFERR;
			error(buf);
			break;
		}
	}

/*
 * get the identity
 */

	p = nexttok(NULL, DELIM, FALSE);
	if (p == NULL) {
		(void) sprintf(buf, "%s/%s/_pmtab is corrupt", HOME, tag);
		Saferrno = E_SAFERR;
		error(buf);
	}
	pp->p_id = p;

/*
 * get the first reserved field
 */

	p = nexttok(NULL, DELIM, FALSE);
	if (p == NULL) {
		(void) sprintf(buf, "%s/%s/_pmtab is corrupt", HOME, tag);
		Saferrno = E_SAFERR;
		error(buf);
	}
	pp->p_res1 = p;

/*
 * get the second reserved field
 */

	p = nexttok(NULL, DELIM, FALSE);
	if (p == NULL) {
		(void) sprintf(buf, "%s/%s/_pmtab is corrupt", HOME, tag);
		Saferrno = E_SAFERR;
		error(buf);
	}
	pp->p_res2 = p;

/*
 * get the third reserved field
 */

	p = nexttok(NULL, DELIM, FALSE);
	if (p == NULL) {
		(void) sprintf(buf, "%s/%s/_pmtab is corrupt", HOME, tag);
		Saferrno = E_SAFERR;
		error(buf);
	}
	pp->p_res3 = p;

/*
 * the rest is the port monitor specific info
 */

	p = nexttok(NULL, DELIM, TRUE);
	if (p == NULL) {
		(void) sprintf(buf, "%s/%s/_pmtab is corrupt", HOME, tag);
		Saferrno = E_SAFERR;
		error(buf);
	}
	pp->p_pmspec = p;
	return;
}


/*
 * pspec - format port monitor specific information
 *
 *	args:	spec - port monitor specific info, separated by
 *		       field separater character (may be escaped by \)
 */

char *
pspec(spec)
char *spec;
{
	static char buf[SIZE];		/* returned string */
	register char *from;		/* working pointer */
	register char *to;		/* working pointer */
	int newflag;			/* flag indicating new field */

	to = buf;
	from = spec;
	newflag = 1;
	while (*from) {
		switch (*from) {
		case ':':
			if (newflag) {
				*to++ = '-';
			}
			*to++ = ' ';
			from++;
			newflag = 1;
			break;
		case '\\':
			if (*(from + 1) == ':') {
				*to++ = ':';
				/* skip over \: */
				from += 2;
			}
			else
				*to++ = *from++;
			newflag = 0;
			break;
		default:
			newflag = 0;
			*to++ = *from++;
		}
	}
	*to = '\0';
	return(buf);
}


/*
 * pflags - put service flags into intelligible form for output
 *
 *	args:	flags - binary representation of flags
 *		dflag - true if a "-" should be returned if no flags
 */

char *
pflags(flags, dflag)
long flags;
int dflag;
{
	register int i;			/* scratch counter */
	static char buf[SIZE];		/* formatted flags */

	if (flags == 0) {
		if (dflag)
			return("-");
		else
			return("");
	}
	i = 0;
	if (flags & U_FLAG) {
		buf[i++] = 'u';
		flags &= ~U_FLAG;
	}
	if (flags & X_FLAG) {
		buf[i++] = 'x';
		flags &= ~X_FLAG;
	}
	if (flags) {
		Saferrno = E_SAFERR;
		error("Internal error in pflags");
	}
	buf[i] = '\0';
	return(buf);
}


/*
 * find_type - find entries in _sactab for a particular port monitor type
 *
 *	args:	fp - file pointer for _sactab
 *		type - type of port monitor we're looking for (if type is
 *		       null, it means find all PMs)
 */

struct taglist *
find_type(fp, type)
FILE *fp;
char *type;
{
	register char *p;			/* working pointer */
	struct sactab stab;			/* place to hold parsed info */
	register struct sactab *sp = &stab;	/* and a pointer to it */
	char buf[SIZE];				/* scratch buffer */
	struct taglist *thead;			/* linked list of tags */
	register struct taglist *temp;		/* scratch pointer */

	thead = NULL;
	while (fgets(buf, SIZE, fp)) {
		p = trim(buf);
		if (*p == '\0')
			continue;
		parse(p, sp);
		if ((type == NULL) || !(strcmp(sp->sc_type, type))) {
			temp = (struct taglist *) malloc(sizeof(struct taglist));
			if (temp == NULL) {
				Saferrno = E_SYSERR;
				error("malloc failed");
			}
			temp->t_next = thead;
			(void) strcpy(temp->t_tag, sp->sc_tag);
			(void) strcpy(temp->t_type, sp->sc_type);
			thead = temp;
		}
	}
	if (!feof(fp)) {
		Saferrno = E_SYSERR;
		error("error reading _sactab");
		/* NOTREACHED */
		return (0);
	} else
		return (thead ? thead : NULL);
}