1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include "cfga_ib.h"
27#include "cfga_conf.h"
28#include <sys/stat.h>
29
30/*
31 * cfga_conf.c
32 *
33 *	This file supports adding/deleting/listing services from IBCONF_FILE.
34 */
35
36/*
37 * function prototypes:
38 */
39static ib_service_type_t	ib_get_var_type(char *);
40static ib_token_t		ib_lex(char *, char **);
41static void			ib_find_eol();
42static int			ib_get_string(char **, char *);
43static int			ib_service_record_add(char *,
44				    ib_service_type_t);
45static ib_token_t		ib_get_services(char **);
46static boolean_t		ib_cmp_service();
47static void			ib_free_service_recs(void);
48static int			ib_cleanup_file(int);
49static int			ib_init_file(char **);
50int				ib_add_service(char **);
51int				ib_delete_service(char **);
52int				ib_list_services(struct cfga_msg *, char **);
53static cfga_ib_ret_t		ib_conf_control_ioctl(char *, uint_t);
54static int			ib_service_record_valid(char *);
55
56extern void			cfga_msg(struct cfga_msg *, const char *);
57
58
59/* Global variables */
60
61/*
62 * supported "name=value" pairs from IBCONF_FILE
63 */
64static ibcfg_var_t ibcfg_varlist[] = {
65	{ "name",		IB_NAME },
66	{ "class",		IB_CLASS },
67	{ "port-svc-list",	IB_PORT_SERVICE },
68	{ "vppa-svc-list",	IB_VPPA_SERVICE },
69	{ "hca-svc-list",	IB_HCASVC_SERVICE },
70	{ NULL,			IB_NONE }
71};
72
73static char		ibconf_file[] = IBCONF_FILE;	/* file being read */
74static int		ibcfg_linenum = 1;		/* track line#s */
75static int		ibcfg_cntr = 0;			/* current char read */
76static int		ibcfg_brec = 0;			/* beginning of rec */
77static int		bvpparec = 0;			/* begin of vppa rec */
78static int		bportrec = 0;			/* begin of port rec */
79static int		bhcarec = 0;			/* begin of HCA rec */
80static int		ibcfg_btoken = 0;		/* begin of new token */
81static mutex_t		ibcfg_lock = DEFAULTMUTEX;	/* lock for the file */
82static int		ibcfg_fd = -1;			/* file descriptor */
83static int		ibcfg_tmpfd = 0;		/* tmp file "fd" */
84static char		*file_buf = (char *)NULL;	/* read file into buf */
85static char		*tmpnamef = (char *)NULL;	/* tmp file name */
86static boolean_t	wrote_tmp = B_FALSE;		/* tmp file write in */
87							/* progress indicator */
88static struct stat	ibcfg_st;			/* file stat struct */
89
90static int		ibcfg_nport_services;		/* # of PORT services */
91static int		ibcfg_nvppa_services;		/* # of VPPA services */
92static int		ibcfg_nhca_services;		/* # of HCA services */
93static ib_svc_rec_t	*ibcfg_vppa_head;		/* VPPA service recs */
94static ib_svc_rec_t	*ibcfg_port_head;		/* PORT service recs */
95static ib_svc_rec_t	*ibcfg_hca_head;		/* HCA service recs */
96
97extern char		*service_name;			/* service name */
98extern ib_service_type_t service_type;			/* service type */
99
100
101/*
102 * Function:
103 *	ib_get_var_type
104 * Input:
105 *	str	-	A parsed string from IBCONF_FILE
106 * Output:
107 *	NONE
108 * Returns:
109 *	Service type
110 * Description:
111 *	Returns the field from the token
112 */
113static ib_service_type_t
114ib_get_var_type(char *str)
115{
116	register ibcfg_var_t    *cfgvar;
117
118	cfgvar = &ibcfg_varlist[0];
119	while (cfgvar->type != IB_NONE) {
120		if (strcasecmp(cfgvar->name, str) == NULL)
121			break;
122		else
123			cfgvar++;
124	}
125	return (cfgvar->type);
126}
127
128
129/*
130 * Function:
131 *	ib_lex
132 * Input:
133 *	NONE
134 * Output:
135 *	val	-	value just read
136 *	errmsg	-	pointer to error message string, if there are any errors
137 * Returns:
138 *	valid IB token
139 * Description:
140 *	Read tokens from the IBCONF_FILE and parse them
141 */
142/* ARGSUSED */
143static ib_token_t
144ib_lex(char *val, char **errmsg)
145{
146	int		ch, oval, badquote;
147	char		*cp = val;
148	ib_token_t	token;
149
150	while ((ch = GETC(file_buf, ibcfg_cntr)) == ' ' || ch == '\t')
151		;
152
153	/* make a note of the beginning of token */
154	ibcfg_btoken = ibcfg_cntr - 1;
155
156	*cp++ = (char)ch;
157	switch (ch) {
158	case '=':
159		token = EQUALS;
160		break;
161	case '&':
162		token = AMPERSAND;
163		break;
164	case '|':
165		token = BIT_OR;
166		break;
167	case '*':
168		token = STAR;
169		break;
170	case '#':
171		token = POUND;
172		break;
173	case ':':
174		token = COLON;
175		break;
176	case ';':
177		token = SEMICOLON;
178		break;
179	case ',':
180		token = COMMA;
181		break;
182	case '/':
183		token = SLASH;
184		break;
185	case ' ':
186	case '\t':
187	case '\f':
188		while ((ch  = GETC(file_buf, ibcfg_cntr)) == ' ' ||
189		    ch == '\t' || ch == '\f')
190			*cp++ = (char)ch;
191		(void) UNGETC(ibcfg_cntr);
192		token = WHITE_SPACE;
193		break;
194	case '\n':
195	case '\r':
196		token = NEWLINE;
197		break;
198	case '"':
199		cp--;
200		badquote = 0;
201		while (!badquote && (ch  = GETC(file_buf, ibcfg_cntr)) != '"') {
202			switch (ch) {
203			case '\n':
204			case -1:
205				(void) snprintf(*errmsg, MAXPATHLEN,
206				    "Missing \"");
207				cp = val;
208				*cp++ = '\n';
209				badquote = 1;
210				/* since we consumed the newline/EOF */
211				(void) UNGETC(ibcfg_cntr);
212				break;
213
214			case '\\':
215				ch = (char)GETC(file_buf, ibcfg_cntr);
216				if (!isdigit(ch)) {
217					/* escape the character */
218					*cp++ = (char)ch;
219					break;
220				}
221				oval = 0;
222				while (ch >= '0' && ch <= '7') {
223					ch -= '0';
224					oval = (oval << 3) + ch;
225					ch = (char)GETC(file_buf, ibcfg_cntr);
226				}
227				(void) UNGETC(ibcfg_cntr);
228				/* check for character overflow? */
229				if (oval > 127) {
230					(void) snprintf(*errmsg, MAXPATHLEN,
231					    "Character overflow detected.\n");
232				}
233				*cp++ = (char)oval;
234				break;
235			default:
236				*cp++ = (char)ch;
237				break;
238			}
239		}
240		token = STRING;
241		break;
242	default:
243		if (ch == -1) {
244			token = EOF;
245			break;
246		}
247		/*
248		 * detect a lone '-' (including at the end of a line), and
249		 * identify it as a 'name'
250		 */
251		if (ch == '-') {
252			*cp++ = (char)(ch = GETC(file_buf, ibcfg_cntr));
253			if (iswhite(ch) || (ch == '\n')) {
254				(void) UNGETC(ibcfg_cntr);
255				cp--;
256				token = NAME;
257				break;
258			}
259		} else if (isunary(ch)) {
260			*cp++ = (char)(ch = GETC(file_buf, ibcfg_cntr));
261		}
262
263		if (isdigit(ch)) {
264			if (ch == '0') {
265				if ((ch = GETC(file_buf, ibcfg_cntr)) == 'x') {
266					*cp++ = (char)ch;
267					ch = GETC(file_buf, ibcfg_cntr);
268					while (isxdigit(ch)) {
269						*cp++ = (char)ch;
270						ch = GETC(file_buf, ibcfg_cntr);
271					}
272					(void) UNGETC(ibcfg_cntr);
273					token = HEXVAL;
274				} else {
275					goto digit;
276				}
277			} else {
278				ch = GETC(file_buf, ibcfg_cntr);
279digit:
280				while (isdigit(ch)) {
281					*cp++ = (char)ch;
282					ch = GETC(file_buf, ibcfg_cntr);
283				}
284				(void) UNGETC(ibcfg_cntr);
285				token = DECVAL;
286			}
287		} else if (isalpha(ch) || ch == '\\') {
288			if (ch != '\\') {
289				ch = GETC(file_buf, ibcfg_cntr);
290			} else {
291				/*
292				 * if the character was a backslash,
293				 * back up so we can overwrite it with
294				 * the next (i.e. escaped) character.
295				 */
296				cp--;
297			}
298
299			while (isnamechar(ch) || ch == '\\') {
300				if (ch == '\\')
301					ch = GETC(file_buf, ibcfg_cntr);
302				*cp++ = (char)ch;
303				ch = GETC(file_buf, ibcfg_cntr);
304			}
305			(void) UNGETC(ibcfg_cntr);
306			token = NAME;
307		} else
308			return (-1);
309		break;
310	}
311	*cp = '\0';
312	return (token);
313}
314
315
316/*
317 * Function:
318 *	ib_find_eol
319 * Input:
320 *	NONE
321 * Output:
322 *	NONE
323 * Returns:
324 *	NONE
325 * Description:
326 *	Leave NEWLINE as the next character.
327 */
328static void
329ib_find_eol()
330{
331	int ch;
332
333	while ((ch = GETC(file_buf, ibcfg_cntr)) != -1) {
334		if (isnewline(ch))  {
335			(void) UNGETC(ibcfg_cntr);
336			break;
337		}
338	}
339}
340
341
342/*
343 * Function:
344 *	ib_get_string
345 * Input:
346 *	tchar		- name of the string
347 * Output:
348 *	llptr		- Valid string
349 * Returns:
350 *	1 for success, NULL for errors.
351 * Description:
352 *	The next item on the line is a string value. Allocate memory for
353 *	it and copy the string. Return 1, and set arg ptr to newly allocated
354 *	and initialized buffer, or NULL if an error occurs.
355 */
356static int
357ib_get_string(char **llptr, char *tchar)
358{
359	int	tlen = strlen(tchar);
360	char	*cp;
361	char	*start = (char *)0;
362
363	start = tchar;
364	/* copy string */
365	if ((cp = (char *)calloc(tlen + 1, sizeof (char))) == (char *)NULL) {
366		*llptr = NULL;
367		return (0);
368	}
369	bzero(cp, tlen + 1);
370
371	*llptr = cp;
372	for (; tlen > 0; tlen--) {
373		/* convert some common escape sequences */
374		if (*start == '\\') {
375			switch (*(start + 1)) {
376			case 't':
377				/* tab */
378				*cp++ = '\t';
379				tlen--;
380				start += 2;
381				break;
382			case 'n':
383				/* new line */
384				*cp++ = '\n';
385				tlen--;
386				start += 2;
387				break;
388			case 'b':
389				/* back space */
390				*cp++ = '\b';
391				tlen--;
392				start += 2;
393				break;
394			default:
395				/* simply copy it */
396				*cp++ = *start++;
397				break;
398			}
399		} else {
400			*cp++ = *start++;
401		}
402	}
403	*cp = '\0';
404	return (1);
405}
406
407
408/*
409 * Function:
410 *	ib_service_record_add
411 * Input:
412 *	service		- name of the service
413 *	type		- type of the service
414 * Output:
415 *	rec		- one valid service record
416 * Returns:
417 *	CFGA_IB_OK on success or an appropriate error
418 * Description:
419 *	Add one record to internal data structures
420 */
421static int
422ib_service_record_add(char *service, ib_service_type_t type)
423{
424	ib_svc_rec_t	*tmp, *recp;
425
426	DPRINTF("ib_service_record_add: (%x, %s) "
427	    "(#port = %d #vppa = %d #hca = %d)\n", type, service,
428	    ibcfg_nport_services, ibcfg_nvppa_services,
429	    ibcfg_nhca_services);
430	recp = (ib_svc_rec_t *)calloc(1, sizeof (ib_svc_rec_t));
431	if (recp == NULL)
432		return (CFGA_IB_ALLOC_FAIL);
433
434	recp->type = type;
435	recp->name = strdup((char *)service);
436	if (type == IB_PORT_SERVICE) {
437		if (ibcfg_port_head) {
438			for (tmp = ibcfg_port_head; tmp->next != NULL; )
439				tmp = tmp->next;
440			tmp->next = recp;
441		} else
442			ibcfg_port_head = recp;
443		ibcfg_nport_services++;
444	} else if (type == IB_VPPA_SERVICE) {
445		if (ibcfg_vppa_head) {
446			for (tmp = ibcfg_vppa_head; tmp->next != NULL; )
447				tmp = tmp->next;
448			tmp->next = recp;
449		} else
450			ibcfg_vppa_head = recp;
451		ibcfg_nvppa_services++;
452	} else if (type == IB_HCASVC_SERVICE) {
453		if (ibcfg_hca_head) {
454			for (tmp = ibcfg_hca_head; tmp->next != NULL; )
455				tmp = tmp->next;
456			tmp->next = recp;
457		} else
458			ibcfg_hca_head = recp;
459		ibcfg_nhca_services++;
460	}
461
462	return (CFGA_IB_OK);
463}
464
465
466/*
467 * Function:
468 *	ib_get_services
469 * Input:
470 *	errmsg		- Error message filled in case of a failure
471 * Output:
472 *	rec		- one valid service record
473 * Returns:
474 *	CFGA_IB_OK on success or an appropriate error
475 * Description:
476 *	Fetch one record from the IBCONF_FILE
477 */
478static ib_token_t
479ib_get_services(char **errmsg)
480{
481	char			tokval[MAXLINESIZE];
482	char			*llptr;
483	boolean_t		sor = B_TRUE;
484	ib_token_t		token;
485	ib_service_type_t	cfgvar;
486	ib_parse_state_t	parse_state = IB_NEWVAR;
487
488	token = ib_lex(tokval, errmsg);
489	while ((token != EOF) && (token != SEMICOLON)) {
490		if (token == STAR || token == POUND) {
491			/* skip comments */
492			ib_find_eol();
493		} else if (token == NEWLINE) {
494			ibcfg_linenum++;
495		} else if (token == NAME || token == STRING) {
496			if (parse_state == IB_NEWVAR) {
497				cfgvar = ib_get_var_type(tokval);
498				if (cfgvar == IB_NONE) {
499					parse_state = IB_ERROR;
500					(void) snprintf(*errmsg, MAXPATHLEN,
501					    "Syntax Error: Invalid type %s",
502					    tokval);
503				} else {
504					/* Note the beginning of the entry */
505					if (sor) {
506						ibcfg_brec = ibcfg_btoken;
507						sor = B_FALSE;
508					}
509					parse_state = IB_CONFIG_VAR;
510					if (cfgvar == IB_PORT_SERVICE)
511						bportrec = ibcfg_cntr + 1;
512					else if (cfgvar == IB_VPPA_SERVICE)
513						bvpparec = ibcfg_cntr + 1;
514					else if (cfgvar == IB_HCASVC_SERVICE)
515						bhcarec = ibcfg_cntr + 1;
516				}
517
518			} else if (parse_state == IB_VAR_VALUE) {
519				llptr = NULL;
520				if (ib_get_string(&llptr, tokval)) {
521					if ((cfgvar == IB_PORT_SERVICE) ||
522					    (cfgvar == IB_VPPA_SERVICE) ||
523					    (cfgvar == IB_HCASVC_SERVICE)) {
524						if (ib_service_record_valid(
525						    llptr) &&
526						    ib_service_record_add(
527						    (char *)llptr, cfgvar) !=
528						    CFGA_IB_OK) {
529							return (E_O_F);
530						} else {
531							parse_state =
532							    IB_CONFIG_VAR;
533						}
534					} else if ((cfgvar == IB_NAME) ||
535					    (cfgvar == IB_CLASS)) {
536						free((char *)llptr);
537						parse_state = IB_NEWVAR;
538					} else {
539						free((char *)llptr);
540						parse_state = IB_ERROR;
541					}
542				} else {
543					parse_state = IB_ERROR;
544					(void) snprintf(*errmsg, MAXPATHLEN,
545					    "Syntax Error: Invalid value %s "
546					    "for type: %s\n", tokval,
547					    ibcfg_varlist[cfgvar].name);
548				}
549			} else if (parse_state == IB_ERROR) {
550				/* just skip */
551				DPRINTF("ib_get_services: ERROR\n");
552			} else {
553				parse_state = IB_ERROR;
554				(void) snprintf(*errmsg, MAXPATHLEN,
555				    "Syntax Error: at %s", tokval);
556			}
557		} else if (token == COMMA || token == EQUALS) {
558			if (parse_state == IB_CONFIG_VAR) {
559				if (cfgvar == IB_NONE) {
560					parse_state = IB_ERROR;
561					(void) snprintf(*errmsg, MAXPATHLEN,
562					    "Syntax Error: unexpected '='");
563				} else {
564					parse_state = IB_VAR_VALUE;
565				}
566			} else if (parse_state != IB_ERROR) {
567				(void) snprintf(*errmsg, MAXPATHLEN,
568				    "Syntax Error: unexpected '='");
569				parse_state = IB_ERROR;
570			}
571		} else {
572			(void) snprintf(*errmsg, MAXPATHLEN,
573			    "Syntax Error: at: %s", tokval);
574			parse_state = IB_ERROR;
575		}
576		token = ib_lex(tokval, errmsg);
577		if (ib_get_var_type(tokval) != IB_NONE)
578			parse_state = IB_NEWVAR;
579	}
580	return (token);
581}
582
583/*
584 * Function:
585 *	ib_cmp_service
586 * Input:
587 *	NONE
588 * Output:
589 *	NONE
590 * Returns:
591 *	B_TRUE if this service is already seen. B_FALSE if not.
592 * Description:
593 *	Compare the service just read from the services already seen.
594 *	Check if this service was already seen or not.
595 */
596static boolean_t
597ib_cmp_service()
598{
599	ib_svc_rec_t	*recp;
600
601	DPRINTF("ib_cmp_service: (%x, %s) "
602	    "(#port = %d #vppa = %d #hca = %d)\n", service_type,
603	    service_name, ibcfg_nport_services, ibcfg_nvppa_services,
604	    ibcfg_nhca_services);
605
606	for (recp = ibcfg_port_head; recp != NULL; recp = recp->next) {
607		DPRINTF("ib_cmp_service:P usvc = %s, usvc_name = %s\n",
608		    service_name, recp->name ? recp->name : "NONE");
609		if (recp->name && strcmp(recp->name, service_name) == 0)
610			return (B_TRUE);
611	}
612	for (recp = ibcfg_vppa_head; recp != NULL; recp = recp->next) {
613		DPRINTF("ib_cmp_service:V utype = %x, usvc_name = %s\n",
614		    recp->type, recp->name ? recp->name : "NONE");
615		if (recp->name && strcmp(recp->name, service_name) == 0)
616			return (B_TRUE);
617	}
618	for (recp = ibcfg_hca_head; recp != NULL; recp = recp->next) {
619		DPRINTF("ib_cmp_service:V utype = %x, usvc_name = %s\n",
620		    recp->type, recp->name ? recp->name : "NONE");
621		if (recp->name && strcmp(recp->name, service_name) == 0)
622			return (B_TRUE);
623	}
624
625	return (B_FALSE);
626}
627
628
629/*
630 * Function:
631 *	ib_free_service_recs
632 * Input:
633 *	NONE
634 * Output:
635 *	NONE
636 * Returns:
637 *	CFGA_IB_OK on success or an appropriate error
638 * Description:
639 *	Free the service records allocated in ib_get_services
640 */
641static void
642ib_free_service_recs(void)
643{
644	ib_svc_rec_t	*tmp, *recp;
645
646	DPRINTF("ib_free_service_recs: "
647	    "#port_services = %d, #vppa_services = %d, #hca_services = %d\n",
648	    ibcfg_nport_services, ibcfg_nvppa_services, ibcfg_nhca_services);
649
650	for (recp = ibcfg_port_head; recp != NULL; ) {
651		if (recp && strlen(recp->name))
652			S_FREE(recp->name);
653		tmp = recp;
654		recp = recp->next;
655		S_FREE(tmp);
656	}
657
658	for (recp = ibcfg_vppa_head; recp != NULL; ) {
659		if (recp && strlen(recp->name))
660			S_FREE(recp->name);
661		tmp = recp;
662		recp = recp->next;
663		S_FREE(tmp);
664	}
665
666	for (recp = ibcfg_hca_head; recp != NULL; ) {
667		if (recp && strlen(recp->name))
668			S_FREE(recp->name);
669		tmp = recp;
670		recp = recp->next;
671		S_FREE(tmp);
672	}
673}
674
675
676/*
677 * Function:
678 *	ib_cleanup_file
679 * Input:
680 *	rval		- error return value
681 * Output:
682 *	NONE
683 * Returns:
684 *	CFGA_IB_OK on success or an appropriate error
685 * Description:
686 *	Cleanup  IBCONF_FILE etc.
687 */
688static int
689ib_cleanup_file(int rval)
690{
691	int	rv = rval;
692
693	ib_free_service_recs();
694	if (lockf(ibcfg_fd, F_ULOCK, 0) == -1) {
695		DPRINTF("ib_cleanup_file: unlock file %s failed\n",
696		    ibconf_file);
697		rv = CFGA_IB_UNLOCK_FILE_ERR;
698	}
699	S_FREE(file_buf);
700	close(ibcfg_fd);
701	ibcfg_fd = -1;
702	if (ibcfg_tmpfd && wrote_tmp == B_TRUE) {
703		DPRINTF("ib_cleanup_file: tmpfile %s being renamed to %s\n",
704		    tmpnamef, IBCONF_FILE);
705		close(ibcfg_tmpfd);
706		rename((const char *)tmpnamef, (const char *)IBCONF_FILE);
707		unlink(tmpnamef);
708	}
709	(void) mutex_unlock(&ibcfg_lock);
710	return (rv);
711}
712
713
714/*
715 * Function:
716 *	ib_init_file
717 * Input:
718 *	NONE
719 * Output:
720 *	errmsg		- Error message filled in case of a failure
721 * Returns:
722 *	CFGA_IB_OK on success or an appropriate error
723 * Description:
724 *	Initialize IBCONF_FILE for reading
725 */
726static int
727ib_init_file(char **errmsg)
728{
729	(void) mutex_lock(&ibcfg_lock);
730
731	if (*errmsg == (char *)NULL) {
732		if ((*errmsg = calloc(MAXPATHLEN, 1)) == (char *)NULL) {
733			(void) mutex_unlock(&ibcfg_lock);
734			DPRINTF("ib_init_file: calloc errmsg failed\n");
735			return (CFGA_IB_CONFIG_FILE_ERR);
736		}
737	}
738
739	/* Open the .conf file */
740	if ((ibcfg_fd = open(ibconf_file, O_RDWR, 0666)) == -1) {
741		(void) snprintf(*errmsg, MAXPATHLEN,
742		    "failed to open %s file\n", ibconf_file);
743		(void) mutex_unlock(&ibcfg_lock);
744		return (CFGA_IB_CONFIG_FILE_ERR);
745	}
746
747	/* Lock the file so that another cfgadm instance doesn't modify it */
748	if (lockf(ibcfg_fd, F_TLOCK, 0) == -1) {
749		(void) snprintf(*errmsg, MAXPATHLEN,
750		    "failed to lock %s file\n", ibconf_file);
751		close(ibcfg_fd);
752		ibcfg_fd = -1;
753		(void) mutex_unlock(&ibcfg_lock);
754		return (CFGA_IB_LOCK_FILE_ERR);
755	}
756
757	if (fstat(ibcfg_fd, &ibcfg_st) != 0) {
758		DPRINTF("ib_init_file: failed to fstat %s file\n", ibconf_file);
759		return (ib_cleanup_file(CFGA_IB_CONFIG_FILE_ERR));
760	}
761
762	/* Allocate a buffer for the file */
763	if ((file_buf = (char *)malloc(ibcfg_st.st_size)) == NULL) {
764		DPRINTF("ib_init_file: failed to fstat %s file\n",
765		    ibconf_file);
766		return (ib_cleanup_file(CFGA_IB_ALLOC_FAIL));
767	}
768
769	/* Check if size matches */
770	if (ibcfg_st.st_size != read(ibcfg_fd, file_buf, ibcfg_st.st_size)) {
771		DPRINTF("ib_init_file: failed to read %s file\n", ibconf_file);
772		return (ib_cleanup_file(CFGA_IB_CONFIG_FILE_ERR));
773	}
774
775	/*
776	 * These variables need to be reinitialized here as they may
777	 * have been modified by a previous thread that called this
778	 * function
779	 */
780	ibcfg_linenum = 1;
781	ibcfg_cntr = 0;
782	ibcfg_brec = 0;
783	ibcfg_btoken = 0;
784
785	ibcfg_nport_services = 0;
786	ibcfg_nvppa_services = 0;
787	ibcfg_nhca_services = 0;
788	ibcfg_port_head = (ib_svc_rec_t *)NULL;
789	ibcfg_vppa_head = (ib_svc_rec_t *)NULL;
790	ibcfg_hca_head = (ib_svc_rec_t *)NULL;
791	return (CFGA_IB_OK);
792}
793
794
795/*
796 * Function:
797 *	ib_add_service
798 * Input:
799 *	NONE
800 * Output:
801 *	errmsg		- Error message filled in case of a failure
802 * Returns:
803 *	CFGA_IB_OK on success or an appropriate error
804 * Description:
805 *	open IBCONF_FILE and add "service_name".
806 */
807int
808ib_add_service(char **errmsg)
809{
810	int		rval;
811	char		*sbuf;
812	boolean_t	found = B_FALSE;
813	ib_token_t	token = NEWLINE;
814
815	DPRINTF("ib_add_service: type = %x, service_name=%s\n", service_type,
816	    service_name);
817	if ((rval = ib_init_file(errmsg)) != CFGA_IB_OK) {
818		DPRINTF("ib_add_service: initializing file failed\n");
819		return (rval);
820	}
821
822	/* Start reading the file */
823	while (token != EOF) {
824		token = ib_get_services(errmsg);
825		found = ib_cmp_service();
826		if (found == B_TRUE) {
827			DPRINTF("ib_add_service: token=%x, found=%x\n",
828			    token, found);
829			break;
830		}
831	}
832
833	/* Service shouldn't already exist while adding */
834	if (found) {
835		(void) snprintf(*errmsg, MAXPATHLEN, "service entry %s exists ",
836		    service_name);
837		DPRINTF("ib_add_service: invalid add operation\n");
838		return (ib_cleanup_file(CFGA_IB_SVC_EXISTS_ERR));
839	}
840
841	DPRINTF("!FOUND and adding\n");
842	switch (service_type) {
843		case IB_PORT_SERVICE :
844			ibcfg_brec = bportrec;
845			break;
846		case IB_VPPA_SERVICE :
847			ibcfg_brec = bvpparec;
848			break;
849		case IB_HCASVC_SERVICE :
850			ibcfg_brec = bhcarec;
851			break;
852		default :
853			DPRINTF("ib_add_service: invalid add operation\n");
854			return (ib_cleanup_file(CFGA_IB_SVC_INVAL_ERR));
855	}
856
857
858	if ((sbuf = (char *)calloc(12, sizeof (char))) == NULL) {
859		DPRINTF("ib_add_service: failed to calloc sbuf %s file\n",
860		    ibconf_file);
861		return (ib_cleanup_file(CFGA_IB_ALLOC_FAIL));
862	}
863	if (file_buf[ibcfg_brec] == '"' && file_buf[ibcfg_brec + 1] == '"') {
864		(void) snprintf(sbuf, 9, "%s", service_name);
865		ibcfg_brec += 1;
866	} else
867		(void) snprintf(sbuf, 9, "\"%s\", ", service_name);
868
869
870	/* Seek to the beginning of the file */
871	if (lseek(ibcfg_fd, ibcfg_brec, SEEK_SET) == -1) {
872		DPRINTF("ib_add_service: lseek %s file failed\n", ibconf_file);
873		return (ib_cleanup_file(CFGA_IB_CONFIG_FILE_ERR));
874	}
875
876	/* Add service to w/ IBNEX  */
877	if (ib_conf_control_ioctl(service_name, IBNEX_CONF_ENTRY_ADD)) {
878		DPRINTF("ib_add_service: ioctl add failed %d\n", errno);
879		(void) snprintf(*errmsg, MAXPATHLEN, "failed to add "
880		    "%s service incore ", service_name);
881		return (ib_cleanup_file(CFGA_IB_SVC_EXISTS_ERR));
882	}
883
884	/* Write the modified file */
885	if (write(ibcfg_fd, sbuf, strlen(sbuf)) == -1) {
886		DPRINTF("ib_add_service: write %s file failed\n", ibconf_file);
887		return (ib_cleanup_file(CFGA_IB_CONFIG_FILE_ERR));
888	}
889
890	/* Write the rest of the file as it was */
891	if (write(ibcfg_fd, file_buf + ibcfg_brec,
892	    ibcfg_st.st_size - ibcfg_brec) == -1) {
893		DPRINTF("ib_add_service: write %s file failed 2\n",
894		    ibconf_file);
895		return (ib_cleanup_file(CFGA_IB_CONFIG_FILE_ERR));
896	}
897
898	return (ib_cleanup_file(rval));
899}
900
901
902/*
903 * Function:
904 *	ib_delete_service
905 * Input:
906 *	NONE
907 * Output:
908 *	errmsg		- Error message filled in case of a failure
909 * Returns:
910 *	CFGA_IB_OK on success or an appropriate error
911 * Description:
912 *	open ib.conf file and delete "service_name"
913 */
914int
915ib_delete_service(char **errmsg)
916{
917	int		rval;
918	int		num_svcs;
919	int		skip_len;
920	int		sbuf_len;
921	int		tot_len;
922	char		tmp[12];
923	char		*sbuf = (char *)NULL;
924	boolean_t	found = B_FALSE;
925	ib_token_t	token = NEWLINE;
926	ib_svc_rec_t	*recp;
927
928	DPRINTF("ib_delete_service: type = %x, service_name=%s\n",
929	    service_type, service_name);
930	if ((rval = ib_init_file(errmsg)) != CFGA_IB_OK) {
931		DPRINTF("ib_delete_service: initializing file failed\n");
932		return (rval);
933	}
934
935	/* Start reading the file */
936	while (token != EOF) {
937		token = ib_get_services(errmsg);
938		found = ib_cmp_service();		/* search for a match */
939		if (found == B_TRUE) {
940			DPRINTF("ib_delete_service: token=%x, found=%x\n",
941			    token, found);
942			break;
943		}
944	}
945
946	/* No service found, return */
947	if (!found) {
948		DPRINTF("ib_delete_service: invalid delete operation\n");
949		(void) snprintf(*errmsg, MAXPATHLEN, "service entry %s "
950		    "does not exist ", service_name);
951		return (ib_cleanup_file(CFGA_IB_SVC_NO_EXIST_ERR));
952	}
953
954	DPRINTF("FOUND and deleting \n");
955
956	switch (service_type) {
957		case IB_PORT_SERVICE :
958			ibcfg_brec = bportrec;
959			num_svcs = ibcfg_nport_services;
960			break;
961		case IB_VPPA_SERVICE :
962			ibcfg_brec = bvpparec;
963			num_svcs = ibcfg_nvppa_services;
964			break;
965		case IB_HCASVC_SERVICE :
966			ibcfg_brec = bhcarec;
967			num_svcs = ibcfg_nhca_services;
968			break;
969		default :
970			DPRINTF("ib_delete_service: invalid delete "
971			    "operation\n");
972			return (ib_cleanup_file(CFGA_IB_SVC_INVAL_ERR));
973	}
974
975	if ((sbuf = (char *)calloc(num_svcs * 8, sizeof (char))) == NULL) {
976		DPRINTF("ib_delete_service: sbuf alloc failed %s\n",
977		    ibconf_file);
978		return (ib_cleanup_file(CFGA_IB_ALLOC_FAIL));
979	}
980
981	if (num_svcs == 1) {
982		(void) snprintf(sbuf, 9, "\"\"");
983		sbuf_len = 2;
984		skip_len = 0;
985	} else {
986		if (service_type == IB_PORT_SERVICE) {
987			for (recp = ibcfg_port_head; recp; recp = recp->next) {
988				if (strcmp(recp->name, service_name) == 0)
989					continue;
990				(void) snprintf(tmp, 9, "\"%s\", ", recp->name);
991				(void) strcat(sbuf, tmp);
992			}
993
994		} else if (service_type == IB_VPPA_SERVICE) {
995			for (recp = ibcfg_vppa_head; recp; recp = recp->next) {
996				if (strcmp(recp->name, service_name) == 0)
997					continue;
998				(void) snprintf(tmp, 9, "\"%s\", ", recp->name);
999				(void) strcat(sbuf, tmp);
1000			}
1001		} else {
1002			for (recp = ibcfg_hca_head; recp; recp = recp->next) {
1003				if (strcmp(recp->name, service_name) == 0)
1004					continue;
1005				(void) snprintf(tmp, 9, "\"%s\", ", recp->name);
1006				(void) strcat(sbuf, tmp);
1007			}
1008		}
1009		skip_len = 4;
1010		sbuf_len = strlen(sbuf);
1011		sbuf[sbuf_len - 2] = '\0';
1012		sbuf_len -= 2;
1013	}
1014
1015	tot_len = strlen(service_name) + skip_len;
1016
1017	tmpnamef = tmpnam(ibconf_file);
1018	DPRINTF("ib_delete_service: tmpnamef = %s\n", tmpnamef);
1019	if ((ibcfg_tmpfd = creat(tmpnamef, 0666)) == -1) {
1020		(void) snprintf(*errmsg, MAXPATHLEN,
1021		    "failed to creat %s file\n", ibconf_file);
1022		DPRINTF("ib_delete_service: failed to creat tmpnamef\n");
1023		return (ib_cleanup_file(CFGA_IB_ALLOC_FAIL));
1024	}
1025
1026	/* Delete service from IBNEX  */
1027	if (ib_conf_control_ioctl(service_name, IBNEX_CONF_ENTRY_DEL)) {
1028		DPRINTF("ib_delete_service: ioctl delete failed %d\n", errno);
1029		(void) snprintf(*errmsg, MAXPATHLEN, "failed to delete "
1030		    "in core %s entry ", service_name);
1031		close(ibcfg_tmpfd);
1032		unlink(tmpnamef);
1033		return (ib_cleanup_file(CFGA_IB_SVC_EXISTS_ERR));
1034	}
1035
1036	/* write till ibcfg_brec */
1037	if (write(ibcfg_tmpfd, file_buf, ibcfg_brec) == -1) {
1038		DPRINTF("ib_delete_service: write %s file failed 1\n",
1039		    ibconf_file);
1040		close(ibcfg_tmpfd);
1041		unlink(tmpnamef);
1042		return (ib_cleanup_file(CFGA_IB_CONFIG_FILE_ERR));
1043	}
1044
1045	/* write modified buffer */
1046	if (write(ibcfg_tmpfd, sbuf, sbuf_len) == -1) {
1047		DPRINTF("ib_delete_service: write %s file failed 2\n",
1048		    ibconf_file);
1049		close(ibcfg_tmpfd);
1050		unlink(tmpnamef);
1051		return (ib_cleanup_file(CFGA_IB_CONFIG_FILE_ERR));
1052	}
1053
1054	/* Write the rest of the file as it was */
1055	if (write(ibcfg_tmpfd, file_buf + ibcfg_brec + sbuf_len + tot_len,
1056	    ibcfg_st.st_size - ibcfg_brec - sbuf_len - tot_len) == -1) {
1057		DPRINTF("ib_delete_service: write %s file failed 3\n",
1058		    ibconf_file);
1059		close(ibcfg_tmpfd);
1060		unlink(tmpnamef);
1061		return (ib_cleanup_file(CFGA_IB_CONFIG_FILE_ERR));
1062	}
1063	wrote_tmp = B_TRUE;
1064
1065	/* No error encountered */
1066	return (ib_cleanup_file(rval));
1067}
1068
1069
1070/*
1071 * Function:
1072 *	ib_list_services
1073 * Input:
1074 *	msgp		- CFGADM message pointer
1075 * Output:
1076 *	errmsg		- Error message filled in case of a failure
1077 * Returns:
1078 *	CFGA_IB_OK on success or an appropriate error
1079 * Description:
1080 *	open IBCONF_FILE and list services.
1081 */
1082int
1083ib_list_services(struct cfga_msg *msgp, char **errmsg)
1084{
1085	int		rval = CFGA_IB_OK;
1086	char		pbuf[IBCONF_SERVICE_HDR_LEN];
1087	ib_token_t	token = NEWLINE;
1088	ib_svc_rec_t	*recp;
1089
1090	DPRINTF("ib_list_services:\n");
1091	if ((rval = ib_init_file(errmsg)) != CFGA_IB_OK) {
1092		DPRINTF("ib_list_services: initializing file failed\n");
1093		return (rval);
1094	}
1095
1096	/* start reading the file */
1097	while (token != EOF)
1098		token = ib_get_services(errmsg);
1099
1100	DPRINTF("ib_list_services: #port_services = %d, #vppa_services = %d,"
1101	    " #hca_services = %d\n", ibcfg_nport_services,
1102	    ibcfg_nvppa_services, ibcfg_nhca_services);
1103
1104	bzero(pbuf, IBCONF_SERVICE_HDR_LEN);
1105	if (ibcfg_nport_services) {
1106		(void) snprintf(pbuf, IBCONF_SERVICE_HDR_LEN,
1107		    IBCONF_PORT_SERVICE_HDR);
1108		cfga_msg(msgp, pbuf);
1109		for (recp = ibcfg_port_head; recp; recp = recp->next) {
1110			DPRINTF("ib_list_services: svc_name = %s\n",
1111			    recp->name ? recp->name : "NONE");
1112			(void) snprintf(pbuf, 14, "\t\t%s\n", recp->name);
1113			cfga_msg(msgp, pbuf);
1114		}
1115		(void) snprintf(pbuf, 2, "\n");
1116		cfga_msg(msgp, pbuf);
1117	}
1118
1119	if (ibcfg_nvppa_services) {
1120		(void) snprintf(pbuf, IBCONF_SERVICE_HDR_LEN,
1121		    IBCONF_VPPA_SERVICE_HDR);
1122		cfga_msg(msgp, pbuf);
1123		for (recp = ibcfg_vppa_head; recp; recp = recp->next) {
1124			DPRINTF("ib_list_services: svc_name = %s\n",
1125			    strlen(recp->name) > 0 ? recp->name : "NONE");
1126			(void) snprintf(pbuf, 14, "\t\t%s\n", recp->name);
1127			cfga_msg(msgp, pbuf);
1128		}
1129	}
1130
1131	if (ibcfg_nhca_services) {
1132		(void) snprintf(pbuf, IBCONF_SERVICE_HDR_LEN,
1133		    IBCONF_HCA_SERVICE_HDR);
1134		cfga_msg(msgp, pbuf);
1135		for (recp = ibcfg_hca_head; recp; recp = recp->next) {
1136			DPRINTF("ib_list_services: svc_name = %s\n",
1137			    strlen(recp->name) > 0 ? recp->name : "NONE");
1138			(void) snprintf(pbuf, 14, "\t\t%s\n", recp->name);
1139			cfga_msg(msgp, pbuf);
1140		}
1141	}
1142	return (ib_cleanup_file(CFGA_IB_OK));
1143}
1144
1145
1146/*
1147 * Function:
1148 *	ib_conf_control_ioctl
1149 * Input:
1150 *	svc		- Service being added/deleted
1151 *	cmd		- Command to DEVCTL_AP_CONTROL devctl
1152 * Output:
1153 *	NONE
1154 * Returns:
1155 *	CFGA_IB_OK if it succeeds or an appropriate error.
1156 * Description:
1157 *	Issues DEVCTL_AP_CONTROL devctl with cmd
1158 */
1159static cfga_ib_ret_t
1160ib_conf_control_ioctl(char *svc, uint_t cmd)
1161{
1162	int			apid_fd = -1;
1163	cfga_ib_ret_t		rv = CFGA_IB_OK;
1164	struct ibnex_ioctl_data	ioctl_data;
1165
1166	DPRINTF("Service = %s len = %x, type = %x\n", svc,
1167	    strlen(svc), service_type);
1168
1169	/* try to open the static IB ap_id */
1170	if ((apid_fd = open(IB_STATIC_APID, O_RDONLY)) == -1) {
1171		DPRINTF("ib_conf_control_ioctl: open failed: errno = %d\n",
1172		    errno);
1173		/* Provides a more useful error msg */
1174		rv = (errno == EBUSY) ? CFGA_IB_BUSY_ERR : CFGA_IB_OPEN_ERR;
1175		return (rv);
1176	}
1177
1178	ioctl_data.cmd = cmd;
1179	ioctl_data.misc_arg = (uint_t)service_type;
1180	ioctl_data.buf = (caddr_t)svc;
1181	ioctl_data.bufsiz = strlen(svc);
1182	ioctl_data.ap_id = (caddr_t)IB_STATIC_APID;
1183	ioctl_data.ap_id_len = strlen(IB_STATIC_APID);
1184
1185	if (ioctl(apid_fd, DEVCTL_AP_CONTROL, &ioctl_data) != 0) {
1186		DPRINTF("ib_conf_control_ioctl: size ioctl ERR, errno: %d\n",
1187		    errno);
1188		rv = (errno == EBUSY) ? CFGA_IB_BUSY_ERR : CFGA_IB_IOCTL_ERR;
1189	}
1190	(void) close(apid_fd);
1191	return (rv);
1192}
1193
1194/*
1195 * This functions checks if the service name is valid. Valid
1196 * service names have  :
1197 *		0 < strlen(name) <= 4
1198 *		Service name is unique
1199 * Returns: 	0 - Name is not valid, 1 - Name is valid
1200 */
1201static int
1202ib_service_record_valid(char *sname)
1203{
1204	int rc = 1, len;
1205	char *tmp_service_name;
1206
1207	tmp_service_name = service_name;
1208	service_name = strdup(sname);
1209	len = strlen(sname);
1210	if (len == 0 || len > 4) {
1211		S_FREE(service_name);
1212		service_name = tmp_service_name;
1213		return (0);
1214	}
1215	if (ib_cmp_service() == B_TRUE)
1216		rc = 0;
1217	S_FREE(service_name);
1218	service_name = tmp_service_name;
1219	return (rc);
1220}
1221