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