/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include "cfga_ib.h"
#include "cfga_conf.h"
#include <sys/stat.h>

/*
 * cfga_conf.c
 *
 *	This file supports adding/deleting/listing services from IBCONF_FILE.
 */

/*
 * function prototypes:
 */
static ib_service_type_t	ib_get_var_type(char *);
static ib_token_t		ib_lex(char *, char **);
static void			ib_find_eol();
static int			ib_get_string(char **, char *);
static int			ib_service_record_add(char *,
				    ib_service_type_t);
static ib_token_t		ib_get_services(char **);
static boolean_t		ib_cmp_service();
static void			ib_free_service_recs(void);
static int			ib_cleanup_file(int);
static int			ib_init_file(char **);
int				ib_add_service(char **);
int				ib_delete_service(char **);
int				ib_list_services(struct cfga_msg *, char **);
static cfga_ib_ret_t		ib_conf_control_ioctl(char *, uint_t);
static int			ib_service_record_valid(char *);

extern void			cfga_msg(struct cfga_msg *, const char *);


/* Global variables */

/*
 * supported "name=value" pairs from IBCONF_FILE
 */
static ibcfg_var_t ibcfg_varlist[] = {
	{ "name",		IB_NAME },
	{ "class",		IB_CLASS },
	{ "port-svc-list",	IB_PORT_SERVICE },
	{ "vppa-svc-list",	IB_VPPA_SERVICE },
	{ "hca-svc-list",	IB_HCASVC_SERVICE },
	{ NULL,			IB_NONE }
};

static char		ibconf_file[] = IBCONF_FILE;	/* file being read */
static int		ibcfg_linenum = 1;		/* track line#s */
static int		ibcfg_cntr = 0;			/* current char read */
static int		ibcfg_brec = 0;			/* beginning of rec */
static int		bvpparec = 0;			/* begin of vppa rec */
static int		bportrec = 0;			/* begin of port rec */
static int		bhcarec = 0;			/* begin of HCA rec */
static int		ibcfg_btoken = 0;		/* begin of new token */
static mutex_t		ibcfg_lock = DEFAULTMUTEX;	/* lock for the file */
static int		ibcfg_fd = -1;			/* file descriptor */
static int		ibcfg_tmpfd = 0;		/* tmp file "fd" */
static char		*file_buf = (char *)NULL;	/* read file into buf */
static char		*tmpnamef = (char *)NULL;	/* tmp file name */
static boolean_t	wrote_tmp = B_FALSE;		/* tmp file write in */
							/* progress indicator */
static struct stat	ibcfg_st;			/* file stat struct */

static int		ibcfg_nport_services;		/* # of PORT services */
static int		ibcfg_nvppa_services;		/* # of VPPA services */
static int		ibcfg_nhca_services;		/* # of HCA services */
static ib_svc_rec_t	*ibcfg_vppa_head;		/* VPPA service recs */
static ib_svc_rec_t	*ibcfg_port_head;		/* PORT service recs */
static ib_svc_rec_t	*ibcfg_hca_head;		/* HCA service recs */

extern char		*service_name;			/* service name */
extern ib_service_type_t service_type;			/* service type */


/*
 * Function:
 *	ib_get_var_type
 * Input:
 *	str	-	A parsed string from IBCONF_FILE
 * Output:
 *	NONE
 * Returns:
 *	Service type
 * Description:
 *	Returns the field from the token
 */
static ib_service_type_t
ib_get_var_type(char *str)
{
	register ibcfg_var_t    *cfgvar;

	cfgvar = &ibcfg_varlist[0];
	while (cfgvar->type != IB_NONE) {
		if (strcasecmp(cfgvar->name, str) == 0)
			break;
		else
			cfgvar++;
	}
	return (cfgvar->type);
}


/*
 * Function:
 *	ib_lex
 * Input:
 *	NONE
 * Output:
 *	val	-	value just read
 *	errmsg	-	pointer to error message string, if there are any errors
 * Returns:
 *	valid IB token
 * Description:
 *	Read tokens from the IBCONF_FILE and parse them
 */
/* ARGSUSED */
static ib_token_t
ib_lex(char *val, char **errmsg)
{
	int		ch, oval, badquote;
	char		*cp = val;
	ib_token_t	token;

	while ((ch = GETC(file_buf, ibcfg_cntr)) == ' ' || ch == '\t')
		;

	/* make a note of the beginning of token */
	ibcfg_btoken = ibcfg_cntr - 1;

	*cp++ = (char)ch;
	switch (ch) {
	case '=':
		token = EQUALS;
		break;
	case '&':
		token = AMPERSAND;
		break;
	case '|':
		token = BIT_OR;
		break;
	case '*':
		token = STAR;
		break;
	case '#':
		token = POUND;
		break;
	case ':':
		token = COLON;
		break;
	case ';':
		token = SEMICOLON;
		break;
	case ',':
		token = COMMA;
		break;
	case '/':
		token = SLASH;
		break;
	case ' ':
	case '\t':
	case '\f':
		while ((ch  = GETC(file_buf, ibcfg_cntr)) == ' ' ||
		    ch == '\t' || ch == '\f')
			*cp++ = (char)ch;
		(void) UNGETC(ibcfg_cntr);
		token = WHITE_SPACE;
		break;
	case '\n':
	case '\r':
		token = NEWLINE;
		break;
	case '"':
		cp--;
		badquote = 0;
		while (!badquote && (ch  = GETC(file_buf, ibcfg_cntr)) != '"') {
			switch (ch) {
			case '\n':
			case -1:
				(void) snprintf(*errmsg, MAXPATHLEN,
				    "Missing \"");
				cp = val;
				*cp++ = '\n';
				badquote = 1;
				/* since we consumed the newline/EOF */
				(void) UNGETC(ibcfg_cntr);
				break;

			case '\\':
				ch = (char)GETC(file_buf, ibcfg_cntr);
				if (!isdigit(ch)) {
					/* escape the character */
					*cp++ = (char)ch;
					break;
				}
				oval = 0;
				while (ch >= '0' && ch <= '7') {
					ch -= '0';
					oval = (oval << 3) + ch;
					ch = (char)GETC(file_buf, ibcfg_cntr);
				}
				(void) UNGETC(ibcfg_cntr);
				/* check for character overflow? */
				if (oval > 127) {
					(void) snprintf(*errmsg, MAXPATHLEN,
					    "Character overflow detected.\n");
				}
				*cp++ = (char)oval;
				break;
			default:
				*cp++ = (char)ch;
				break;
			}
		}
		token = STRING;
		break;
	default:
		if (ch == -1) {
			token = EOF;
			break;
		}
		/*
		 * detect a lone '-' (including at the end of a line), and
		 * identify it as a 'name'
		 */
		if (ch == '-') {
			*cp++ = (char)(ch = GETC(file_buf, ibcfg_cntr));
			if (iswhite(ch) || (ch == '\n')) {
				(void) UNGETC(ibcfg_cntr);
				cp--;
				token = NAME;
				break;
			}
		} else if (isunary(ch)) {
			*cp++ = (char)(ch = GETC(file_buf, ibcfg_cntr));
		}

		if (isdigit(ch)) {
			if (ch == '0') {
				if ((ch = GETC(file_buf, ibcfg_cntr)) == 'x') {
					*cp++ = (char)ch;
					ch = GETC(file_buf, ibcfg_cntr);
					while (isxdigit(ch)) {
						*cp++ = (char)ch;
						ch = GETC(file_buf, ibcfg_cntr);
					}
					(void) UNGETC(ibcfg_cntr);
					token = HEXVAL;
				} else {
					goto digit;
				}
			} else {
				ch = GETC(file_buf, ibcfg_cntr);
digit:
				while (isdigit(ch)) {
					*cp++ = (char)ch;
					ch = GETC(file_buf, ibcfg_cntr);
				}
				(void) UNGETC(ibcfg_cntr);
				token = DECVAL;
			}
		} else if (isalpha(ch) || ch == '\\') {
			if (ch != '\\') {
				ch = GETC(file_buf, ibcfg_cntr);
			} else {
				/*
				 * if the character was a backslash,
				 * back up so we can overwrite it with
				 * the next (i.e. escaped) character.
				 */
				cp--;
			}

			while (isnamechar(ch) || ch == '\\') {
				if (ch == '\\')
					ch = GETC(file_buf, ibcfg_cntr);
				*cp++ = (char)ch;
				ch = GETC(file_buf, ibcfg_cntr);
			}
			(void) UNGETC(ibcfg_cntr);
			token = NAME;
		} else
			return (-1);
		break;
	}
	*cp = '\0';
	return (token);
}


/*
 * Function:
 *	ib_find_eol
 * Input:
 *	NONE
 * Output:
 *	NONE
 * Returns:
 *	NONE
 * Description:
 *	Leave NEWLINE as the next character.
 */
static void
ib_find_eol()
{
	int ch;

	while ((ch = GETC(file_buf, ibcfg_cntr)) != -1) {
		if (isnewline(ch))  {
			(void) UNGETC(ibcfg_cntr);
			break;
		}
	}
}


/*
 * Function:
 *	ib_get_string
 * Input:
 *	tchar		- name of the string
 * Output:
 *	llptr		- Valid string
 * Returns:
 *	1 for success, NULL for errors.
 * Description:
 *	The next item on the line is a string value. Allocate memory for
 *	it and copy the string. Return 1, and set arg ptr to newly allocated
 *	and initialized buffer, or NULL if an error occurs.
 */
static int
ib_get_string(char **llptr, char *tchar)
{
	int	tlen = strlen(tchar);
	char	*cp;
	char	*start = (char *)0;

	start = tchar;
	/* copy string */
	if ((cp = (char *)calloc(tlen + 1, sizeof (char))) == (char *)NULL) {
		*llptr = NULL;
		return (0);
	}
	bzero(cp, tlen + 1);

	*llptr = cp;
	for (; tlen > 0; tlen--) {
		/* convert some common escape sequences */
		if (*start == '\\') {
			switch (*(start + 1)) {
			case 't':
				/* tab */
				*cp++ = '\t';
				tlen--;
				start += 2;
				break;
			case 'n':
				/* new line */
				*cp++ = '\n';
				tlen--;
				start += 2;
				break;
			case 'b':
				/* back space */
				*cp++ = '\b';
				tlen--;
				start += 2;
				break;
			default:
				/* simply copy it */
				*cp++ = *start++;
				break;
			}
		} else {
			*cp++ = *start++;
		}
	}
	*cp = '\0';
	return (1);
}


/*
 * Function:
 *	ib_service_record_add
 * Input:
 *	service		- name of the service
 *	type		- type of the service
 * Output:
 *	rec		- one valid service record
 * Returns:
 *	CFGA_IB_OK on success or an appropriate error
 * Description:
 *	Add one record to internal data structures
 */
static int
ib_service_record_add(char *service, ib_service_type_t type)
{
	ib_svc_rec_t	*tmp, *recp;

	DPRINTF("ib_service_record_add: (%x, %s) "
	    "(#port = %d #vppa = %d #hca = %d)\n", type, service,
	    ibcfg_nport_services, ibcfg_nvppa_services,
	    ibcfg_nhca_services);
	recp = (ib_svc_rec_t *)calloc(1, sizeof (ib_svc_rec_t));
	if (recp == NULL)
		return (CFGA_IB_ALLOC_FAIL);

	recp->type = type;
	recp->name = strdup((char *)service);
	if (type == IB_PORT_SERVICE) {
		if (ibcfg_port_head) {
			for (tmp = ibcfg_port_head; tmp->next != NULL; )
				tmp = tmp->next;
			tmp->next = recp;
		} else
			ibcfg_port_head = recp;
		ibcfg_nport_services++;
	} else if (type == IB_VPPA_SERVICE) {
		if (ibcfg_vppa_head) {
			for (tmp = ibcfg_vppa_head; tmp->next != NULL; )
				tmp = tmp->next;
			tmp->next = recp;
		} else
			ibcfg_vppa_head = recp;
		ibcfg_nvppa_services++;
	} else if (type == IB_HCASVC_SERVICE) {
		if (ibcfg_hca_head) {
			for (tmp = ibcfg_hca_head; tmp->next != NULL; )
				tmp = tmp->next;
			tmp->next = recp;
		} else
			ibcfg_hca_head = recp;
		ibcfg_nhca_services++;
	}

	return (CFGA_IB_OK);
}


/*
 * Function:
 *	ib_get_services
 * Input:
 *	errmsg		- Error message filled in case of a failure
 * Output:
 *	rec		- one valid service record
 * Returns:
 *	CFGA_IB_OK on success or an appropriate error
 * Description:
 *	Fetch one record from the IBCONF_FILE
 */
static ib_token_t
ib_get_services(char **errmsg)
{
	char			tokval[MAXLINESIZE];
	char			*llptr;
	boolean_t		sor = B_TRUE;
	ib_token_t		token;
	ib_service_type_t	cfgvar;
	ib_parse_state_t	parse_state = IB_NEWVAR;

	token = ib_lex(tokval, errmsg);
	while ((token != EOF) && (token != SEMICOLON)) {
		if (token == STAR || token == POUND) {
			/* skip comments */
			ib_find_eol();
		} else if (token == NEWLINE) {
			ibcfg_linenum++;
		} else if (token == NAME || token == STRING) {
			if (parse_state == IB_NEWVAR) {
				cfgvar = ib_get_var_type(tokval);
				if (cfgvar == IB_NONE) {
					parse_state = IB_ERROR;
					(void) snprintf(*errmsg, MAXPATHLEN,
					    "Syntax Error: Invalid type %s",
					    tokval);
				} else {
					/* Note the beginning of the entry */
					if (sor) {
						ibcfg_brec = ibcfg_btoken;
						sor = B_FALSE;
					}
					parse_state = IB_CONFIG_VAR;
					if (cfgvar == IB_PORT_SERVICE)
						bportrec = ibcfg_cntr + 1;
					else if (cfgvar == IB_VPPA_SERVICE)
						bvpparec = ibcfg_cntr + 1;
					else if (cfgvar == IB_HCASVC_SERVICE)
						bhcarec = ibcfg_cntr + 1;
				}

			} else if (parse_state == IB_VAR_VALUE) {
				llptr = NULL;
				if (ib_get_string(&llptr, tokval)) {
					if ((cfgvar == IB_PORT_SERVICE) ||
					    (cfgvar == IB_VPPA_SERVICE) ||
					    (cfgvar == IB_HCASVC_SERVICE)) {
						if (ib_service_record_valid(
						    llptr) &&
						    ib_service_record_add(
						    (char *)llptr, cfgvar) !=
						    CFGA_IB_OK) {
							return (E_O_F);
						} else {
							parse_state =
							    IB_CONFIG_VAR;
						}
					} else if ((cfgvar == IB_NAME) ||
					    (cfgvar == IB_CLASS)) {
						free((char *)llptr);
						parse_state = IB_NEWVAR;
					} else {
						free((char *)llptr);
						parse_state = IB_ERROR;
					}
				} else {
					parse_state = IB_ERROR;
					(void) snprintf(*errmsg, MAXPATHLEN,
					    "Syntax Error: Invalid value %s "
					    "for type: %s\n", tokval,
					    ibcfg_varlist[cfgvar].name);
				}
			} else if (parse_state == IB_ERROR) {
				/* just skip */
				DPRINTF("ib_get_services: ERROR\n");
			} else {
				parse_state = IB_ERROR;
				(void) snprintf(*errmsg, MAXPATHLEN,
				    "Syntax Error: at %s", tokval);
			}
		} else if (token == COMMA || token == EQUALS) {
			if (parse_state == IB_CONFIG_VAR) {
				if (cfgvar == IB_NONE) {
					parse_state = IB_ERROR;
					(void) snprintf(*errmsg, MAXPATHLEN,
					    "Syntax Error: unexpected '='");
				} else {
					parse_state = IB_VAR_VALUE;
				}
			} else if (parse_state != IB_ERROR) {
				(void) snprintf(*errmsg, MAXPATHLEN,
				    "Syntax Error: unexpected '='");
				parse_state = IB_ERROR;
			}
		} else {
			(void) snprintf(*errmsg, MAXPATHLEN,
			    "Syntax Error: at: %s", tokval);
			parse_state = IB_ERROR;
		}
		token = ib_lex(tokval, errmsg);
		if (ib_get_var_type(tokval) != IB_NONE)
			parse_state = IB_NEWVAR;
	}
	return (token);
}

/*
 * Function:
 *	ib_cmp_service
 * Input:
 *	NONE
 * Output:
 *	NONE
 * Returns:
 *	B_TRUE if this service is already seen. B_FALSE if not.
 * Description:
 *	Compare the service just read from the services already seen.
 *	Check if this service was already seen or not.
 */
static boolean_t
ib_cmp_service()
{
	ib_svc_rec_t	*recp;

	DPRINTF("ib_cmp_service: (%x, %s) "
	    "(#port = %d #vppa = %d #hca = %d)\n", service_type,
	    service_name, ibcfg_nport_services, ibcfg_nvppa_services,
	    ibcfg_nhca_services);

	for (recp = ibcfg_port_head; recp != NULL; recp = recp->next) {
		DPRINTF("ib_cmp_service:P usvc = %s, usvc_name = %s\n",
		    service_name, recp->name ? recp->name : "NONE");
		if (recp->name && strcmp(recp->name, service_name) == 0)
			return (B_TRUE);
	}
	for (recp = ibcfg_vppa_head; recp != NULL; recp = recp->next) {
		DPRINTF("ib_cmp_service:V utype = %x, usvc_name = %s\n",
		    recp->type, recp->name ? recp->name : "NONE");
		if (recp->name && strcmp(recp->name, service_name) == 0)
			return (B_TRUE);
	}
	for (recp = ibcfg_hca_head; recp != NULL; recp = recp->next) {
		DPRINTF("ib_cmp_service:V utype = %x, usvc_name = %s\n",
		    recp->type, recp->name ? recp->name : "NONE");
		if (recp->name && strcmp(recp->name, service_name) == 0)
			return (B_TRUE);
	}

	return (B_FALSE);
}


/*
 * Function:
 *	ib_free_service_recs
 * Input:
 *	NONE
 * Output:
 *	NONE
 * Returns:
 *	CFGA_IB_OK on success or an appropriate error
 * Description:
 *	Free the service records allocated in ib_get_services
 */
static void
ib_free_service_recs(void)
{
	ib_svc_rec_t	*tmp, *recp;

	DPRINTF("ib_free_service_recs: "
	    "#port_services = %d, #vppa_services = %d, #hca_services = %d\n",
	    ibcfg_nport_services, ibcfg_nvppa_services, ibcfg_nhca_services);

	for (recp = ibcfg_port_head; recp != NULL; ) {
		if (recp && strlen(recp->name))
			S_FREE(recp->name);
		tmp = recp;
		recp = recp->next;
		S_FREE(tmp);
	}

	for (recp = ibcfg_vppa_head; recp != NULL; ) {
		if (recp && strlen(recp->name))
			S_FREE(recp->name);
		tmp = recp;
		recp = recp->next;
		S_FREE(tmp);
	}

	for (recp = ibcfg_hca_head; recp != NULL; ) {
		if (recp && strlen(recp->name))
			S_FREE(recp->name);
		tmp = recp;
		recp = recp->next;
		S_FREE(tmp);
	}
}


/*
 * Function:
 *	ib_cleanup_file
 * Input:
 *	rval		- error return value
 * Output:
 *	NONE
 * Returns:
 *	CFGA_IB_OK on success or an appropriate error
 * Description:
 *	Cleanup  IBCONF_FILE etc.
 */
static int
ib_cleanup_file(int rval)
{
	int	rv = rval;

	ib_free_service_recs();
	if (lockf(ibcfg_fd, F_ULOCK, 0) == -1) {
		DPRINTF("ib_cleanup_file: unlock file %s failed\n",
		    ibconf_file);
		rv = CFGA_IB_UNLOCK_FILE_ERR;
	}
	S_FREE(file_buf);
	close(ibcfg_fd);
	ibcfg_fd = -1;
	if (ibcfg_tmpfd && wrote_tmp == B_TRUE) {
		DPRINTF("ib_cleanup_file: tmpfile %s being renamed to %s\n",
		    tmpnamef, IBCONF_FILE);
		close(ibcfg_tmpfd);
		rename((const char *)tmpnamef, (const char *)IBCONF_FILE);
		unlink(tmpnamef);
	}
	(void) mutex_unlock(&ibcfg_lock);
	return (rv);
}


/*
 * Function:
 *	ib_init_file
 * Input:
 *	NONE
 * Output:
 *	errmsg		- Error message filled in case of a failure
 * Returns:
 *	CFGA_IB_OK on success or an appropriate error
 * Description:
 *	Initialize IBCONF_FILE for reading
 */
static int
ib_init_file(char **errmsg)
{
	(void) mutex_lock(&ibcfg_lock);

	if (*errmsg == (char *)NULL) {
		if ((*errmsg = calloc(MAXPATHLEN, 1)) == (char *)NULL) {
			(void) mutex_unlock(&ibcfg_lock);
			DPRINTF("ib_init_file: calloc errmsg failed\n");
			return (CFGA_IB_CONFIG_FILE_ERR);
		}
	}

	/* Open the .conf file */
	if ((ibcfg_fd = open(ibconf_file, O_RDWR, 0666)) == -1) {
		(void) snprintf(*errmsg, MAXPATHLEN,
		    "failed to open %s file\n", ibconf_file);
		(void) mutex_unlock(&ibcfg_lock);
		return (CFGA_IB_CONFIG_FILE_ERR);
	}

	/* Lock the file so that another cfgadm instance doesn't modify it */
	if (lockf(ibcfg_fd, F_TLOCK, 0) == -1) {
		(void) snprintf(*errmsg, MAXPATHLEN,
		    "failed to lock %s file\n", ibconf_file);
		close(ibcfg_fd);
		ibcfg_fd = -1;
		(void) mutex_unlock(&ibcfg_lock);
		return (CFGA_IB_LOCK_FILE_ERR);
	}

	if (fstat(ibcfg_fd, &ibcfg_st) != 0) {
		DPRINTF("ib_init_file: failed to fstat %s file\n", ibconf_file);
		return (ib_cleanup_file(CFGA_IB_CONFIG_FILE_ERR));
	}

	/* Allocate a buffer for the file */
	if ((file_buf = (char *)malloc(ibcfg_st.st_size)) == NULL) {
		DPRINTF("ib_init_file: failed to fstat %s file\n",
		    ibconf_file);
		return (ib_cleanup_file(CFGA_IB_ALLOC_FAIL));
	}

	/* Check if size matches */
	if (ibcfg_st.st_size != read(ibcfg_fd, file_buf, ibcfg_st.st_size)) {
		DPRINTF("ib_init_file: failed to read %s file\n", ibconf_file);
		return (ib_cleanup_file(CFGA_IB_CONFIG_FILE_ERR));
	}

	/*
	 * These variables need to be reinitialized here as they may
	 * have been modified by a previous thread that called this
	 * function
	 */
	ibcfg_linenum = 1;
	ibcfg_cntr = 0;
	ibcfg_brec = 0;
	ibcfg_btoken = 0;

	ibcfg_nport_services = 0;
	ibcfg_nvppa_services = 0;
	ibcfg_nhca_services = 0;
	ibcfg_port_head = (ib_svc_rec_t *)NULL;
	ibcfg_vppa_head = (ib_svc_rec_t *)NULL;
	ibcfg_hca_head = (ib_svc_rec_t *)NULL;
	return (CFGA_IB_OK);
}


/*
 * Function:
 *	ib_add_service
 * Input:
 *	NONE
 * Output:
 *	errmsg		- Error message filled in case of a failure
 * Returns:
 *	CFGA_IB_OK on success or an appropriate error
 * Description:
 *	open IBCONF_FILE and add "service_name".
 */
int
ib_add_service(char **errmsg)
{
	int		rval;
	char		*sbuf;
	boolean_t	found = B_FALSE;
	ib_token_t	token = NEWLINE;

	DPRINTF("ib_add_service: type = %x, service_name=%s\n", service_type,
	    service_name);
	if ((rval = ib_init_file(errmsg)) != CFGA_IB_OK) {
		DPRINTF("ib_add_service: initializing file failed\n");
		return (rval);
	}

	/* Start reading the file */
	while (token != EOF) {
		token = ib_get_services(errmsg);
		found = ib_cmp_service();
		if (found == B_TRUE) {
			DPRINTF("ib_add_service: token=%x, found=%x\n",
			    token, found);
			break;
		}
	}

	/* Service shouldn't already exist while adding */
	if (found) {
		(void) snprintf(*errmsg, MAXPATHLEN, "service entry %s exists ",
		    service_name);
		DPRINTF("ib_add_service: invalid add operation\n");
		return (ib_cleanup_file(CFGA_IB_SVC_EXISTS_ERR));
	}

	DPRINTF("!FOUND and adding\n");
	switch (service_type) {
		case IB_PORT_SERVICE :
			ibcfg_brec = bportrec;
			break;
		case IB_VPPA_SERVICE :
			ibcfg_brec = bvpparec;
			break;
		case IB_HCASVC_SERVICE :
			ibcfg_brec = bhcarec;
			break;
		default :
			DPRINTF("ib_add_service: invalid add operation\n");
			return (ib_cleanup_file(CFGA_IB_SVC_INVAL_ERR));
	}


	if ((sbuf = (char *)calloc(12, sizeof (char))) == NULL) {
		DPRINTF("ib_add_service: failed to calloc sbuf %s file\n",
		    ibconf_file);
		return (ib_cleanup_file(CFGA_IB_ALLOC_FAIL));
	}
	if (file_buf[ibcfg_brec] == '"' && file_buf[ibcfg_brec + 1] == '"') {
		(void) snprintf(sbuf, 9, "%s", service_name);
		ibcfg_brec += 1;
	} else
		(void) snprintf(sbuf, 9, "\"%s\", ", service_name);


	/* Seek to the beginning of the file */
	if (lseek(ibcfg_fd, ibcfg_brec, SEEK_SET) == -1) {
		DPRINTF("ib_add_service: lseek %s file failed\n", ibconf_file);
		return (ib_cleanup_file(CFGA_IB_CONFIG_FILE_ERR));
	}

	/* Add service to w/ IBNEX  */
	if (ib_conf_control_ioctl(service_name, IBNEX_CONF_ENTRY_ADD)) {
		DPRINTF("ib_add_service: ioctl add failed %d\n", errno);
		(void) snprintf(*errmsg, MAXPATHLEN, "failed to add "
		    "%s service incore ", service_name);
		return (ib_cleanup_file(CFGA_IB_SVC_EXISTS_ERR));
	}

	/* Write the modified file */
	if (write(ibcfg_fd, sbuf, strlen(sbuf)) == -1) {
		DPRINTF("ib_add_service: write %s file failed\n", ibconf_file);
		return (ib_cleanup_file(CFGA_IB_CONFIG_FILE_ERR));
	}

	/* Write the rest of the file as it was */
	if (write(ibcfg_fd, file_buf + ibcfg_brec,
	    ibcfg_st.st_size - ibcfg_brec) == -1) {
		DPRINTF("ib_add_service: write %s file failed 2\n",
		    ibconf_file);
		return (ib_cleanup_file(CFGA_IB_CONFIG_FILE_ERR));
	}

	return (ib_cleanup_file(rval));
}


/*
 * Function:
 *	ib_delete_service
 * Input:
 *	NONE
 * Output:
 *	errmsg		- Error message filled in case of a failure
 * Returns:
 *	CFGA_IB_OK on success or an appropriate error
 * Description:
 *	open ib.conf file and delete "service_name"
 */
int
ib_delete_service(char **errmsg)
{
	int		rval;
	int		num_svcs;
	int		skip_len;
	int		sbuf_len;
	int		tot_len;
	char		tmp[12];
	char		*sbuf = (char *)NULL;
	boolean_t	found = B_FALSE;
	ib_token_t	token = NEWLINE;
	ib_svc_rec_t	*recp;

	DPRINTF("ib_delete_service: type = %x, service_name=%s\n",
	    service_type, service_name);
	if ((rval = ib_init_file(errmsg)) != CFGA_IB_OK) {
		DPRINTF("ib_delete_service: initializing file failed\n");
		return (rval);
	}

	/* Start reading the file */
	while (token != EOF) {
		token = ib_get_services(errmsg);
		found = ib_cmp_service();		/* search for a match */
		if (found == B_TRUE) {
			DPRINTF("ib_delete_service: token=%x, found=%x\n",
			    token, found);
			break;
		}
	}

	/* No service found, return */
	if (!found) {
		DPRINTF("ib_delete_service: invalid delete operation\n");
		(void) snprintf(*errmsg, MAXPATHLEN, "service entry %s "
		    "does not exist ", service_name);
		return (ib_cleanup_file(CFGA_IB_SVC_NO_EXIST_ERR));
	}

	DPRINTF("FOUND and deleting \n");

	switch (service_type) {
		case IB_PORT_SERVICE :
			ibcfg_brec = bportrec;
			num_svcs = ibcfg_nport_services;
			break;
		case IB_VPPA_SERVICE :
			ibcfg_brec = bvpparec;
			num_svcs = ibcfg_nvppa_services;
			break;
		case IB_HCASVC_SERVICE :
			ibcfg_brec = bhcarec;
			num_svcs = ibcfg_nhca_services;
			break;
		default :
			DPRINTF("ib_delete_service: invalid delete "
			    "operation\n");
			return (ib_cleanup_file(CFGA_IB_SVC_INVAL_ERR));
	}

	if ((sbuf = (char *)calloc(num_svcs * 8, sizeof (char))) == NULL) {
		DPRINTF("ib_delete_service: sbuf alloc failed %s\n",
		    ibconf_file);
		return (ib_cleanup_file(CFGA_IB_ALLOC_FAIL));
	}

	if (num_svcs == 1) {
		(void) snprintf(sbuf, 9, "\"\"");
		sbuf_len = 2;
		skip_len = 0;
	} else {
		if (service_type == IB_PORT_SERVICE) {
			for (recp = ibcfg_port_head; recp; recp = recp->next) {
				if (strcmp(recp->name, service_name) == 0)
					continue;
				(void) snprintf(tmp, 9, "\"%s\", ", recp->name);
				(void) strcat(sbuf, tmp);
			}

		} else if (service_type == IB_VPPA_SERVICE) {
			for (recp = ibcfg_vppa_head; recp; recp = recp->next) {
				if (strcmp(recp->name, service_name) == 0)
					continue;
				(void) snprintf(tmp, 9, "\"%s\", ", recp->name);
				(void) strcat(sbuf, tmp);
			}
		} else {
			for (recp = ibcfg_hca_head; recp; recp = recp->next) {
				if (strcmp(recp->name, service_name) == 0)
					continue;
				(void) snprintf(tmp, 9, "\"%s\", ", recp->name);
				(void) strcat(sbuf, tmp);
			}
		}
		skip_len = 4;
		sbuf_len = strlen(sbuf);
		sbuf[sbuf_len - 2] = '\0';
		sbuf_len -= 2;
	}

	tot_len = strlen(service_name) + skip_len;

	tmpnamef = tmpnam(ibconf_file);
	DPRINTF("ib_delete_service: tmpnamef = %s\n", tmpnamef);
	if ((ibcfg_tmpfd = creat(tmpnamef, 0666)) == -1) {
		(void) snprintf(*errmsg, MAXPATHLEN,
		    "failed to creat %s file\n", ibconf_file);
		DPRINTF("ib_delete_service: failed to creat tmpnamef\n");
		return (ib_cleanup_file(CFGA_IB_ALLOC_FAIL));
	}

	/* Delete service from IBNEX  */
	if (ib_conf_control_ioctl(service_name, IBNEX_CONF_ENTRY_DEL)) {
		DPRINTF("ib_delete_service: ioctl delete failed %d\n", errno);
		(void) snprintf(*errmsg, MAXPATHLEN, "failed to delete "
		    "in core %s entry ", service_name);
		close(ibcfg_tmpfd);
		unlink(tmpnamef);
		return (ib_cleanup_file(CFGA_IB_SVC_EXISTS_ERR));
	}

	/* write till ibcfg_brec */
	if (write(ibcfg_tmpfd, file_buf, ibcfg_brec) == -1) {
		DPRINTF("ib_delete_service: write %s file failed 1\n",
		    ibconf_file);
		close(ibcfg_tmpfd);
		unlink(tmpnamef);
		return (ib_cleanup_file(CFGA_IB_CONFIG_FILE_ERR));
	}

	/* write modified buffer */
	if (write(ibcfg_tmpfd, sbuf, sbuf_len) == -1) {
		DPRINTF("ib_delete_service: write %s file failed 2\n",
		    ibconf_file);
		close(ibcfg_tmpfd);
		unlink(tmpnamef);
		return (ib_cleanup_file(CFGA_IB_CONFIG_FILE_ERR));
	}

	/* Write the rest of the file as it was */
	if (write(ibcfg_tmpfd, file_buf + ibcfg_brec + sbuf_len + tot_len,
	    ibcfg_st.st_size - ibcfg_brec - sbuf_len - tot_len) == -1) {
		DPRINTF("ib_delete_service: write %s file failed 3\n",
		    ibconf_file);
		close(ibcfg_tmpfd);
		unlink(tmpnamef);
		return (ib_cleanup_file(CFGA_IB_CONFIG_FILE_ERR));
	}
	wrote_tmp = B_TRUE;

	/* No error encountered */
	return (ib_cleanup_file(rval));
}


/*
 * Function:
 *	ib_list_services
 * Input:
 *	msgp		- CFGADM message pointer
 * Output:
 *	errmsg		- Error message filled in case of a failure
 * Returns:
 *	CFGA_IB_OK on success or an appropriate error
 * Description:
 *	open IBCONF_FILE and list services.
 */
int
ib_list_services(struct cfga_msg *msgp, char **errmsg)
{
	int		rval = CFGA_IB_OK;
	char		pbuf[IBCONF_SERVICE_HDR_LEN];
	ib_token_t	token = NEWLINE;
	ib_svc_rec_t	*recp;

	DPRINTF("ib_list_services:\n");
	if ((rval = ib_init_file(errmsg)) != CFGA_IB_OK) {
		DPRINTF("ib_list_services: initializing file failed\n");
		return (rval);
	}

	/* start reading the file */
	while (token != EOF)
		token = ib_get_services(errmsg);

	DPRINTF("ib_list_services: #port_services = %d, #vppa_services = %d,"
	    " #hca_services = %d\n", ibcfg_nport_services,
	    ibcfg_nvppa_services, ibcfg_nhca_services);

	bzero(pbuf, IBCONF_SERVICE_HDR_LEN);
	if (ibcfg_nport_services) {
		(void) snprintf(pbuf, IBCONF_SERVICE_HDR_LEN,
		    IBCONF_PORT_SERVICE_HDR);
		cfga_msg(msgp, pbuf);
		for (recp = ibcfg_port_head; recp; recp = recp->next) {
			DPRINTF("ib_list_services: svc_name = %s\n",
			    recp->name ? recp->name : "NONE");
			(void) snprintf(pbuf, 14, "\t\t%s\n", recp->name);
			cfga_msg(msgp, pbuf);
		}
		(void) snprintf(pbuf, 2, "\n");
		cfga_msg(msgp, pbuf);
	}

	if (ibcfg_nvppa_services) {
		(void) snprintf(pbuf, IBCONF_SERVICE_HDR_LEN,
		    IBCONF_VPPA_SERVICE_HDR);
		cfga_msg(msgp, pbuf);
		for (recp = ibcfg_vppa_head; recp; recp = recp->next) {
			DPRINTF("ib_list_services: svc_name = %s\n",
			    strlen(recp->name) > 0 ? recp->name : "NONE");
			(void) snprintf(pbuf, 14, "\t\t%s\n", recp->name);
			cfga_msg(msgp, pbuf);
		}
	}

	if (ibcfg_nhca_services) {
		(void) snprintf(pbuf, IBCONF_SERVICE_HDR_LEN,
		    IBCONF_HCA_SERVICE_HDR);
		cfga_msg(msgp, pbuf);
		for (recp = ibcfg_hca_head; recp; recp = recp->next) {
			DPRINTF("ib_list_services: svc_name = %s\n",
			    strlen(recp->name) > 0 ? recp->name : "NONE");
			(void) snprintf(pbuf, 14, "\t\t%s\n", recp->name);
			cfga_msg(msgp, pbuf);
		}
	}
	return (ib_cleanup_file(CFGA_IB_OK));
}


/*
 * Function:
 *	ib_conf_control_ioctl
 * Input:
 *	svc		- Service being added/deleted
 *	cmd		- Command to DEVCTL_AP_CONTROL devctl
 * Output:
 *	NONE
 * Returns:
 *	CFGA_IB_OK if it succeeds or an appropriate error.
 * Description:
 *	Issues DEVCTL_AP_CONTROL devctl with cmd
 */
static cfga_ib_ret_t
ib_conf_control_ioctl(char *svc, uint_t cmd)
{
	int			apid_fd = -1;
	cfga_ib_ret_t		rv = CFGA_IB_OK;
	struct ibnex_ioctl_data	ioctl_data;

	DPRINTF("Service = %s len = %x, type = %x\n", svc,
	    strlen(svc), service_type);

	/* try to open the static IB ap_id */
	if ((apid_fd = open(IB_STATIC_APID, O_RDONLY)) == -1) {
		DPRINTF("ib_conf_control_ioctl: open failed: errno = %d\n",
		    errno);
		/* Provides a more useful error msg */
		rv = (errno == EBUSY) ? CFGA_IB_BUSY_ERR : CFGA_IB_OPEN_ERR;
		return (rv);
	}

	ioctl_data.cmd = cmd;
	ioctl_data.misc_arg = (uint_t)service_type;
	ioctl_data.buf = (caddr_t)svc;
	ioctl_data.bufsiz = strlen(svc);
	ioctl_data.ap_id = (caddr_t)IB_STATIC_APID;
	ioctl_data.ap_id_len = strlen(IB_STATIC_APID);

	if (ioctl(apid_fd, DEVCTL_AP_CONTROL, &ioctl_data) != 0) {
		DPRINTF("ib_conf_control_ioctl: size ioctl ERR, errno: %d\n",
		    errno);
		rv = (errno == EBUSY) ? CFGA_IB_BUSY_ERR : CFGA_IB_IOCTL_ERR;
	}
	(void) close(apid_fd);
	return (rv);
}

/*
 * This functions checks if the service name is valid. Valid
 * service names have  :
 *		0 < strlen(name) <= 4
 *		Service name is unique
 * Returns: 	0 - Name is not valid, 1 - Name is valid
 */
static int
ib_service_record_valid(char *sname)
{
	int rc = 1, len;
	char *tmp_service_name;

	tmp_service_name = service_name;
	service_name = strdup(sname);
	len = strlen(sname);
	if (len == 0 || len > 4) {
		S_FREE(service_name);
		service_name = tmp_service_name;
		return (0);
	}
	if (ib_cmp_service() == B_TRUE)
		rc = 0;
	S_FREE(service_name);
	service_name = tmp_service_name;
	return (rc);
}