xref: /illumos-gate/usr/src/cmd/datadm/datadm.c (revision d0b12b660e0741581d18f1f3a7d5268e0a5f1806)
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 (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <sys/sockio.h>
29 #include <sys/stat.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <net/if.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <strings.h>
36 #include <ctype.h>
37 #include <errno.h>
38 #include <libintl.h>
39 #include <locale.h>
40 #include <fcntl.h>
41 #include <libdlpi.h>
42 #include <libdladm.h>
43 #include <libdlib.h>
44 #include <libdllink.h>
45 #include <sys/ib/ibnex/ibnex_devctl.h>
46 
47 #define	DATADM_OP_VIEW		0x0000
48 #define	DATADM_OP_UPDATE	0x0001
49 #define	DATADM_OP_ADD		0x0002
50 #define	DATADM_OP_REMOVE	0x0003
51 #define	DATADM_NUM_OPS		0x0004
52 #define	DATADM_DAT_CONF		"/etc/dat/dat.conf"
53 #define	DATADM_LINESZ		1024
54 #define	DATADM_NUM_SP_TOKENS	7
55 #define	DATADM_NUM_DAT_TOKENS	8
56 #define	DATADM_DRV_NAME		"driver_name"
57 #define	DATADM_MAX_TOKENS	16
58 
59 /*
60  * generic entry
61  * placed at the top of all entry types
62  */
63 typedef struct datadm_entry {
64 	struct datadm_entry	*de_next;
65 } datadm_entry_t;
66 
67 /*
68  * list structure
69  * can be manipulated using datadm_walk_list or
70  * datadm_enqueue_entry
71  */
72 typedef struct datadm_list {
73 	datadm_entry_t		*dl_head;
74 	datadm_entry_t		*dl_tail;
75 	uint_t			dl_count;
76 } datadm_list_t;
77 
78 /*
79  * internal representation of the version string in
80  * dat.conf or service_provider.conf. the format is
81  * <dv_name><dv_major>.<dv_minor>
82  */
83 typedef struct datadm_version {
84 	char	*dv_name;
85 	uint_t	dv_major;
86 	uint_t	dv_minor;
87 } datadm_version_t;
88 
89 /*
90  * each sp_entry corresponds to an entry in dat.conf or
91  * service_provider.conf. an sp_entry is processed by the
92  * function datadm_process_sp_entry.
93  */
94 typedef struct datadm_sp_entry {
95 	datadm_entry_t		spe_header;
96 	char			*spe_devname;
97 	datadm_version_t	spe_api_version;
98 	int			spe_threadsafe;
99 	int			spe_default;
100 	char			*spe_libpath;
101 	datadm_version_t	spe_sp_version;
102 	char			*spe_sp_data;
103 	int			spe_invalid;
104 } datadm_sp_entry_t;
105 
106 /*
107  * an hca_entry is created whenever a new hca device is
108  * encountered during sp_entry processing. this structure
109  * contains two lists. the sp_list holds sp entries that
110  * are added when sp entry processing occurs. duplicate
111  * sp entries are not added to this list. the ia_list may
112  * be built statically using the information in dat.conf or
113  * dynamically. similar to the sp_list,
114  * the ia_list contains only unique entries.
115  */
116 typedef struct datadm_hca_entry {
117 	datadm_entry_t		he_header;
118 	char			*he_name;
119 	datadm_list_t		he_sp_list;
120 	datadm_list_t		he_ia_list;
121 } datadm_hca_entry_t;
122 
123 /*
124  * an ia_entry is created when a new ia name is encountered
125  * during sp_entry processing or when a new ia name is
126  * discovered by datadm_build_ia_lists. ia_entry holds the ia
127  * device's instance number.
128  */
129 typedef struct datadm_ia_entry {
130 	datadm_entry_t		iae_header;
131 	char			iae_name[MAXLINKNAMELEN];
132 } datadm_ia_entry_t;
133 
134 /*
135  * a comment entry represents one of the comment lines at the
136  * top of dat.conf. a list of these lines are saved during the
137  * parsing of dat.conf. these lines are written back to dat.conf
138  * when dat.conf gets regenerated.
139  */
140 typedef struct datadm_cmnt_entry {
141 	datadm_entry_t		cmnt_header;
142 	char			*cmnt_line;
143 } datadm_cmnt_entry_t;
144 
145 typedef struct datadm_hca_find_by_name {
146 	char			*hf_name;
147 	datadm_hca_entry_t	*hf_hca_entry;
148 } datadm_hca_find_by_name_t;
149 
150 /*
151  * 2nd argument to datadm_hca_entry_find.
152  * hf_hca_entry is filled in if an hca_entry with
153  * a matching he_name is found.
154  */
155 typedef struct datadm_hca_find {
156 	datadm_sp_entry_t	*hf_sp_entry;
157 	datadm_hca_entry_t	*hf_hca_entry;
158 } datadm_hca_find_t;
159 
160 /*
161  * 2nd argument to datadm_ia_entry_find.
162  * if_ia_entry is filled in if an ia_entry with
163  * a matching ia_name is found.
164  */
165 typedef struct datadm_ia_find {
166 	char			*if_ia_name;
167 	datadm_ia_entry_t	*if_ia_entry;
168 } datadm_ia_find_t;
169 
170 /*
171  * this gets passed to datadm_add_plink.
172  */
173 typedef struct datadm_fill_ia_list {
174 	datadm_list_t		*ia_hca_list;
175 	dladm_handle_t		ia_dlh;
176 	int			ia_ibnex_fd;
177 	int			ia_sock_fd_v4;
178 	int			ia_sock_fd_v6;
179 } datadm_fill_ia_list_t;
180 
181 /*
182  * this defines the commandline parameters specified
183  * by the user.
184  */
185 typedef struct datadm_args {
186 	char			*da_sp_conf;
187 	char			*da_dat_conf;
188 	int			da_op_type;
189 } datadm_args_t;
190 
191 static datadm_args_t		datadm_args;
192 static datadm_list_t		datadm_conf_header;
193 static char			*datadm_conf_header_default =
194 	"#\n"
195 	"# Copyright (c) 2003, 2010, Oracle and/or its affiliates. "
196 	"All rights reserved.\n"
197 	"#\n"
198 	"# DAT configuration file.\n"
199 	"#\n"
200 	"# This file is updated using the datadm(1) command.\n"
201 	"# Do not hand edit this file.\n"
202 	"# See datadm(1) man page for more details.\n"
203 	"#\n"
204 	"# The fields in this file are -\n"
205 	"#\n"
206 	"# IAname version threadsafe default library-path provider-version \\\n"
207 	"# instance-data platform-information\n"
208 	"#\n";
209 
210 /*
211  * common parsing functions.
212  */
213 typedef int (*datadm_parse_func_t)(char *, void *);
214 static int datadm_parse_line(char *, char *[], int *);
215 static int datadm_parse_generic_str(char *, char **);
216 static int datadm_parse_nonnull_str(char *, char **);
217 static int datadm_parse_version(char *, datadm_version_t *);
218 static int datadm_parse_devname(char *, datadm_sp_entry_t *);
219 static int datadm_parse_api_version(char *, datadm_sp_entry_t *);
220 static int datadm_parse_threadsafe(char *, datadm_sp_entry_t *);
221 static int datadm_parse_default(char *, datadm_sp_entry_t *);
222 static int datadm_parse_libpath(char *, datadm_sp_entry_t *);
223 static int datadm_parse_sp_version(char *, datadm_sp_entry_t *);
224 static int datadm_parse_sp_data(char *, datadm_sp_entry_t *);
225 static int datadm_parse_ia_name(char *, char *);
226 
227 /*
228  * utility functions
229  */
230 static void datadm_enqueue_entry(datadm_list_t *, datadm_entry_t *);
231 static int datadm_walk_list(datadm_list_t *,
232     int (*)(datadm_entry_t *, void *), void *);
233 static int datadm_str_match(char *, char *);
234 static int datadm_version_match(datadm_version_t *, datadm_version_t *);
235 static int datadm_sp_entry_match(datadm_sp_entry_t *, datadm_sp_entry_t *);
236 
237 /*
238  * entry allocation/deallocation
239  */
240 static datadm_sp_entry_t *datadm_alloc_sp_entry(void);
241 static datadm_ia_entry_t *datadm_alloc_ia_entry(void);
242 static datadm_hca_entry_t *datadm_alloc_hca_entry(void);
243 static datadm_cmnt_entry_t *datadm_alloc_cmnt_entry(void);
244 static void datadm_free_sp_entry(datadm_sp_entry_t *);
245 static void datadm_free_ia_entry(datadm_ia_entry_t *);
246 static void datadm_free_hca_entry(datadm_hca_entry_t *);
247 static void datadm_free_cmnt_entry(datadm_cmnt_entry_t *);
248 
249 
250 /*
251  * high level parsing functions
252  */
253 static int datadm_parse_sp_conf(datadm_list_t *);
254 static int datadm_parse_dat_conf(datadm_list_t *);
255 static int datadm_process_sp_entry(datadm_list_t *, datadm_sp_entry_t *,
256     char *);
257 
258 /*
259  * ia devices discovery
260  */
261 static int datadm_build_ia_lists(datadm_list_t *);
262 
263 /*
264  * helper function for OP_REMOVE
265  */
266 static void datadm_invalidate_common_sp_entries(datadm_list_t *,
267     datadm_list_t *);
268 
269 /*
270  * output generation
271  */
272 static int datadm_generate_dat_conf(datadm_list_t *);
273 static int datadm_generate_conf_header(FILE *);
274 static int datadm_generate_conf_entry(FILE *, datadm_ia_entry_t *,
275     datadm_sp_entry_t *);
276 
277 /*
278  * datadm operations
279  */
280 static int datadm_view(void);
281 static int datadm_update(void);
282 static int datadm_add(void);
283 static int datadm_remove(void);
284 
285 /*
286  * usage
287  */
288 static void datadm_usage(void);
289 
290 
291 /*
292  * parse function tables
293  */
294 static datadm_parse_func_t datadm_sp_parse_funcs[DATADM_NUM_SP_TOKENS] = {
295 	(datadm_parse_func_t)datadm_parse_devname,
296 	(datadm_parse_func_t)datadm_parse_api_version,
297 	(datadm_parse_func_t)datadm_parse_threadsafe,
298 	(datadm_parse_func_t)datadm_parse_default,
299 	(datadm_parse_func_t)datadm_parse_libpath,
300 	(datadm_parse_func_t)datadm_parse_sp_version,
301 	(datadm_parse_func_t)datadm_parse_sp_data
302 };
303 
304 static datadm_parse_func_t datadm_dat_parse_funcs[DATADM_NUM_DAT_TOKENS] = {
305 	(datadm_parse_func_t)datadm_parse_ia_name,
306 	(datadm_parse_func_t)datadm_parse_api_version,
307 	(datadm_parse_func_t)datadm_parse_threadsafe,
308 	(datadm_parse_func_t)datadm_parse_default,
309 	(datadm_parse_func_t)datadm_parse_libpath,
310 	(datadm_parse_func_t)datadm_parse_sp_version,
311 	(datadm_parse_func_t)datadm_parse_sp_data,
312 	(datadm_parse_func_t)datadm_parse_devname
313 };
314 
315 /*
316  * operation table
317  */
318 static int (*datadm_ops[DATADM_NUM_OPS])(void) = {
319 	datadm_view,
320 	datadm_update,
321 	datadm_add,
322 	datadm_remove
323 };
324 
325 static void
326 datadm_usage(void)
327 {
328 	(void) fprintf(stderr, gettext(
329 	    "usage: datadm -v\n"
330 	    "              -u\n"
331 	    "              -a <service_provider.conf>\n"
332 	    "              -r <service_provider.conf>\n"));
333 }
334 
335 static int
336 datadm_parse_generic_str(char *str, char **strptr)
337 {
338 	int	len;
339 
340 	len = strlen(str);
341 	*strptr = (char *)malloc(len + 1);
342 	if (*strptr == NULL) {
343 		return (-1);
344 	}
345 	(void) strcpy(*strptr, str);
346 	return (0);
347 }
348 
349 /*
350  * this function strips off leading and trailing
351  * whitespaces and returns an error for null or
352  * empty strings.
353  */
354 static int
355 datadm_parse_nonnull_str(char *str, char **strptr)
356 {
357 	int	len, i;
358 	char	*start;
359 
360 	if (str[0] == '\0') {
361 		return (-1);
362 	}
363 	start = str;
364 	for (i = 0; str[i] != '\0'; i++) {
365 		if (!isspace(str[i])) {
366 			start = &str[i];
367 			break;
368 		}
369 	}
370 	for (; str[i] != '\0'; i++) {
371 		if (isspace(str[i])) {
372 			str[i] = '\0';
373 		}
374 	}
375 	len = strlen(start);
376 	*strptr = (char *)malloc(len + 1);
377 	if (*strptr == NULL) {
378 		return (-1);
379 	}
380 	(void) strcpy(*strptr, start);
381 	return (0);
382 }
383 
384 /*
385  * parses the api_version and sp_version fields in
386  * dat.conf and service_provider.conf
387  */
388 static int
389 datadm_parse_version(char *str, datadm_version_t *version)
390 {
391 	int	i = 0, len;
392 	int	major_idx, minor_idx;
393 
394 	len = strlen(str);
395 
396 	for (i = 0; i < len; i++) {
397 		if (isdigit(str[i])) break;
398 	}
399 	if (i == len) {
400 		return (-1);
401 	}
402 	if (i > 0) {
403 		version->dv_name = (char *)malloc(i + 1);
404 		bcopy(str, version->dv_name, i);
405 		version->dv_name[i] = '\0';
406 	} else {
407 		version->dv_name = NULL;
408 	}
409 	major_idx = i;
410 
411 	for (; i < len; i++) {
412 		if (!isdigit(str[i])) break;
413 	}
414 	if (i == len) {
415 		return (-1);
416 	}
417 	if (str[i] != '.') {
418 		return (-1);
419 	}
420 	minor_idx = ++i;
421 	if (i == len) {
422 		return (-1);
423 	}
424 	for (; i < len; i++) {
425 		if (!isdigit(str[i])) break;
426 	}
427 	if (i != len) {
428 		return (-1);
429 	}
430 	version->dv_major = atoi(str + major_idx);
431 	version->dv_minor = atoi(str + minor_idx);
432 	return (0);
433 }
434 
435 /*
436  * parses the ia_name field in dat.conf
437  */
438 static int
439 datadm_parse_ia_name(char *str, char *ia_name)
440 {
441 	if (strlen(str) >= MAXLINKNAMELEN)
442 		return (-1);
443 	(void) strlcpy(ia_name, str, MAXLINKNAMELEN);
444 	return (0);
445 }
446 
447 /*
448  * parses the device name, strips leading and trailing spaces.
449  * the format should be "driver_name=<dev_name>"
450  */
451 static int
452 datadm_parse_devname(char *str, datadm_sp_entry_t *sp_entry)
453 {
454 	int	len, dlen, i, j = 0;
455 	char	*drv_name = DATADM_DRV_NAME;
456 
457 	len = strlen(str);
458 	dlen = strlen(drv_name);
459 
460 	/*
461 	 * strip out leading spaces and try to match
462 	 * the expected string
463 	 */
464 	for (i = 0; i < len; i++) {
465 		if (isspace(str[i]) && j == 0) {
466 			continue;
467 		} else {
468 			if (str[i] == drv_name[j]) {
469 				j++;
470 				if (j == dlen) {
471 					break;
472 				} else {
473 					continue;
474 				}
475 			} else {
476 				break;
477 			}
478 		}
479 	}
480 
481 	/*
482 	 * j must be dlen if the matching string is found
483 	 */
484 	if (j != dlen) {
485 		return (-1);
486 	}
487 
488 	/*
489 	 * skip past the last char of drv_name
490 	 */
491 	i++;
492 
493 	/*
494 	 * strip the spaces before the '='
495 	 */
496 	for (; i < len; i++) {
497 		if (!isspace(str[i])) {
498 			break;
499 		}
500 	}
501 
502 	/*
503 	 * return if the string is too long or if
504 	 * the '=' isn't found
505 	 */
506 	if (i >= len || str[i] != '=') {
507 		return (-1);
508 	}
509 	i++;
510 	if (i >= len) {
511 		/*
512 		 * no string after the equal
513 		 */
514 		return (-1);
515 	}
516 	return (datadm_parse_nonnull_str(str + i, &sp_entry->spe_devname));
517 }
518 
519 static int
520 datadm_parse_api_version(char *str, datadm_sp_entry_t *sp_entry)
521 {
522 	return (datadm_parse_version(str, &sp_entry->spe_api_version));
523 }
524 
525 static int
526 datadm_parse_threadsafe(char *str, datadm_sp_entry_t *sp_entry)
527 {
528 	int retval = 0;
529 
530 	if (strcmp(str, "threadsafe") == 0) {
531 		sp_entry->spe_threadsafe = 1;
532 	} else if (strcmp(str, "nonthreadsafe") == 0) {
533 		sp_entry->spe_threadsafe = 0;
534 	} else {
535 		retval = -1;
536 	}
537 	return (retval);
538 }
539 
540 static int
541 datadm_parse_default(char *str, datadm_sp_entry_t *sp_entry)
542 {
543 	int retval = 0;
544 
545 	if (strcmp(str, "default") == 0) {
546 		sp_entry->spe_default = 1;
547 	} else if (strcmp(str, "nondefault") == 0) {
548 		sp_entry->spe_default = 0;
549 	} else {
550 		retval = -1;
551 	}
552 	return (retval);
553 }
554 
555 static int
556 datadm_parse_libpath(char *str, datadm_sp_entry_t *sp_entry)
557 {
558 	return (datadm_parse_nonnull_str(str, &sp_entry->spe_libpath));
559 }
560 
561 static int
562 datadm_parse_sp_version(char *str, datadm_sp_entry_t *sp_entry)
563 {
564 	return (datadm_parse_version(str, &sp_entry->spe_sp_version));
565 }
566 
567 static int
568 datadm_parse_sp_data(char *str, datadm_sp_entry_t *sp_entry)
569 {
570 	return (datadm_parse_generic_str(str, &sp_entry->spe_sp_data));
571 }
572 
573 static void
574 datadm_enqueue_entry(datadm_list_t *list, datadm_entry_t *entry)
575 {
576 	if (list->dl_head == NULL) {
577 		list->dl_head = entry;
578 		list->dl_tail = entry;
579 		list->dl_count = 1;
580 	} else {
581 		list->dl_tail->de_next = entry;
582 		list->dl_tail = entry;
583 		list->dl_count++;
584 	}
585 }
586 
587 /*
588  * iterates through the list applying func on each element.
589  * break and return if func returns non-zero.
590  */
591 static int
592 datadm_walk_list(datadm_list_t *list, int (*func)(datadm_entry_t *, void *),
593     void *arg)
594 {
595 	datadm_entry_t	*entry;
596 	int		retval = 0;
597 
598 	entry = list->dl_head;
599 	while (entry != NULL) {
600 		retval = (*func)(entry, arg);
601 		if (retval != 0) break;
602 		entry = entry->de_next;
603 	}
604 	return (retval);
605 }
606 
607 /*
608  * iterates through the list applying free_func to each element.
609  * list becomes empty when the function returns.
610  */
611 static void
612 datadm_free_list(datadm_list_t *list, void (*free_func)(datadm_entry_t *))
613 {
614 	while (list->dl_head != NULL) {
615 		datadm_entry_t	*entry;
616 
617 		entry = list->dl_head;
618 		list->dl_head = entry->de_next;
619 		(*free_func)(entry);
620 	}
621 	list->dl_count = 0;
622 	list->dl_tail = NULL;
623 }
624 
625 static datadm_sp_entry_t *
626 datadm_alloc_sp_entry(void)
627 {
628 	datadm_sp_entry_t	*sp_entry;
629 
630 	sp_entry = (datadm_sp_entry_t *)malloc(sizeof (*sp_entry));
631 	if (sp_entry == NULL) {
632 		return (NULL);
633 	}
634 	bzero(sp_entry, sizeof (*sp_entry));
635 	return (sp_entry);
636 }
637 
638 static void
639 datadm_free_sp_entry(datadm_sp_entry_t *sp_entry)
640 {
641 	if (sp_entry->spe_devname != NULL) {
642 		free(sp_entry->spe_devname);
643 		sp_entry->spe_devname = NULL;
644 	}
645 	if (sp_entry->spe_api_version.dv_name != NULL) {
646 		free(sp_entry->spe_api_version.dv_name);
647 		sp_entry->spe_api_version.dv_name = NULL;
648 	}
649 	sp_entry->spe_api_version.dv_major = 0;
650 	sp_entry->spe_api_version.dv_minor = 0;
651 	sp_entry->spe_threadsafe = 0;
652 	sp_entry->spe_default = 0;
653 	if (sp_entry->spe_libpath != NULL) {
654 		free(sp_entry->spe_libpath);
655 		sp_entry->spe_libpath = NULL;
656 	}
657 	if (sp_entry->spe_sp_version.dv_name != NULL) {
658 		free(sp_entry->spe_sp_version.dv_name);
659 		sp_entry->spe_sp_version.dv_name = NULL;
660 	}
661 	sp_entry->spe_sp_version.dv_major = 0;
662 	sp_entry->spe_sp_version.dv_minor = 0;
663 	if (sp_entry->spe_sp_data != NULL) {
664 		free(sp_entry->spe_sp_data);
665 		sp_entry->spe_sp_data = NULL;
666 	}
667 	free(sp_entry);
668 }
669 
670 static int
671 datadm_str_match(char *s1, char *s2)
672 {
673 	if (s1 == NULL || s2 == NULL) {
674 		if (s1 != s2) {
675 			return (0);
676 		}
677 	} else {
678 		if (strcmp(s1, s2) != 0) {
679 			return (0);
680 		}
681 	}
682 	return (1);
683 }
684 
685 static int
686 datadm_version_match(datadm_version_t *v1, datadm_version_t *v2)
687 {
688 	if (!datadm_str_match(v1->dv_name, v2->dv_name)) {
689 		return (0);
690 	}
691 	if (v1->dv_major != v2->dv_major) {
692 		return (0);
693 	}
694 	if (v1->dv_minor != v2->dv_minor) {
695 		return (0);
696 	}
697 	return (1);
698 }
699 
700 static int
701 datadm_sp_entry_match(datadm_sp_entry_t *sp1, datadm_sp_entry_t *sp2)
702 {
703 	if (!datadm_str_match(sp1->spe_devname, sp2->spe_devname)) {
704 		return (0);
705 	}
706 	if (!datadm_version_match(&sp1->spe_api_version,
707 	    &sp2->spe_api_version)) {
708 		return (0);
709 	}
710 	if (sp1->spe_threadsafe != sp2->spe_threadsafe) {
711 		return (0);
712 	}
713 	if (sp1->spe_default != sp2->spe_default) {
714 		return (0);
715 	}
716 	if (!datadm_str_match(sp1->spe_libpath, sp2->spe_libpath)) {
717 		return (0);
718 	}
719 	if (!datadm_version_match(&sp1->spe_sp_version,
720 	    &sp2->spe_sp_version)) {
721 		return (0);
722 	}
723 	if (!datadm_str_match(sp1->spe_sp_data, sp2->spe_sp_data)) {
724 		return (0);
725 	}
726 	return (1);
727 }
728 
729 static datadm_ia_entry_t *
730 datadm_alloc_ia_entry(void)
731 {
732 	datadm_ia_entry_t	*ia_entry;
733 
734 	ia_entry = (datadm_ia_entry_t *)malloc(sizeof (*ia_entry));
735 	if (ia_entry == NULL) {
736 		return (NULL);
737 	}
738 	bzero(ia_entry, sizeof (*ia_entry));
739 	return (ia_entry);
740 }
741 
742 static void
743 datadm_free_ia_entry(datadm_ia_entry_t *ia_entry)
744 {
745 	free(ia_entry);
746 }
747 
748 static datadm_hca_entry_t *
749 datadm_alloc_hca_entry(void)
750 {
751 	datadm_hca_entry_t	*hca_entry;
752 
753 	hca_entry = (datadm_hca_entry_t *)malloc(sizeof (*hca_entry));
754 	if (hca_entry == NULL) {
755 		return (NULL);
756 	}
757 	bzero(hca_entry, sizeof (*hca_entry));
758 	return (hca_entry);
759 }
760 
761 static void
762 datadm_free_hca_entry(datadm_hca_entry_t *hca_entry)
763 {
764 	if (hca_entry->he_name != NULL) {
765 		free(hca_entry->he_name);
766 		hca_entry->he_name = NULL;
767 	}
768 	datadm_free_list(&hca_entry->he_sp_list,
769 	    (void (*)(datadm_entry_t *))datadm_free_sp_entry);
770 	datadm_free_list(&hca_entry->he_ia_list,
771 	    (void (*)(datadm_entry_t *))datadm_free_ia_entry);
772 	free(hca_entry);
773 }
774 
775 static int
776 datadm_hca_entry_match(datadm_hca_entry_t *h1, datadm_hca_entry_t *h2)
777 {
778 	if (!datadm_str_match(h1->he_name, h2->he_name)) {
779 		return (0);
780 	}
781 	return (1);
782 }
783 
784 static int
785 datadm_hca_entry_find(datadm_hca_entry_t *h1, datadm_hca_find_t *hf)
786 {
787 	if (datadm_str_match(h1->he_name, hf->hf_sp_entry->spe_devname)) {
788 		hf->hf_hca_entry = h1;
789 		return (1);
790 	}
791 	return (0);
792 }
793 
794 static int
795 datadm_ia_entry_find(datadm_ia_entry_t *i1, datadm_ia_find_t *iaf)
796 {
797 	if (strcmp(i1->iae_name, iaf->if_ia_name) == 0) {
798 		iaf->if_ia_entry = i1;
799 		return (1);
800 	}
801 	return (0);
802 }
803 
804 static datadm_cmnt_entry_t *
805 datadm_alloc_cmnt_entry(void)
806 {
807 	datadm_cmnt_entry_t	*cmnt_entry;
808 
809 	cmnt_entry = (datadm_cmnt_entry_t *)malloc(sizeof (*cmnt_entry));
810 	if (cmnt_entry == NULL) {
811 		return (NULL);
812 	}
813 	bzero(cmnt_entry, sizeof (*cmnt_entry));
814 	return (cmnt_entry);
815 }
816 
817 static void
818 datadm_free_cmnt_entry(datadm_cmnt_entry_t *cmnt_entry)
819 {
820 	if (cmnt_entry->cmnt_line != NULL) {
821 		free(cmnt_entry->cmnt_line);
822 		cmnt_entry->cmnt_line = NULL;
823 	}
824 	free(cmnt_entry);
825 }
826 
827 /*
828  * tokenizes a line and strips off the quotes from quoted strings
829  */
830 static int
831 datadm_parse_line(char *line_buf, char *tokens[], int *token_count)
832 {
833 	int			len, i;
834 	int			count = 0;
835 	char			*start = NULL;
836 
837 	/* the line must not be longer than DATADM_LINESZ */
838 	len = strlen(line_buf);
839 	if (line_buf[len - 1] != '\n') {
840 		return (-1);
841 	}
842 	/* discard blank lines and comments */
843 	if (len == 1) {
844 		*token_count = 0;
845 		return (0);
846 	}
847 	if (len >= 2 && line_buf[0] == '#') {
848 		*token_count = 0;
849 		return (0);
850 	}
851 	/* removes the new line */
852 	line_buf[len - 1] = '\0';
853 	len--;
854 
855 	for (i = 0; i < len; i++) {
856 		if (start != NULL) {
857 			/*
858 			 * start points to the start of
859 			 * a new token. if start is '"',
860 			 * we should expect a quoted
861 			 * string.
862 			 */
863 			if (*start == '\"') {
864 				/*
865 				 * keep scanning until we
866 				 * hit the end quote.
867 				 */
868 				if (line_buf[i] != '\"') {
869 					continue;
870 				}
871 				/*
872 				 * skip past the start quote
873 				 */
874 				start++;
875 			} else {
876 				/*
877 				 * our token is not a quoted
878 				 * string. our token ends only
879 				 * when we hit a whitespace.
880 				 */
881 				if (!isspace(line_buf[i])) {
882 					continue;
883 				}
884 			}
885 			/*
886 			 * nullify the end quote (if any)
887 			 * and update the tokens array.
888 			 */
889 			line_buf[i] = '\0';
890 			tokens[count] = start;
891 			start = NULL;
892 			count++;
893 		} else {
894 			/*
895 			 * skip whitespaces
896 			 */
897 			if (isspace(line_buf[i])) {
898 				continue;
899 			} else {
900 				start = &line_buf[i];
901 			}
902 		}
903 		if (count == DATADM_MAX_TOKENS) {
904 			start = NULL;
905 			break;
906 		}
907 	}
908 	if (start != NULL) {
909 		tokens[count] = start;
910 		start = NULL;
911 		count++;
912 	}
913 	*token_count = count;
914 	return (0);
915 }
916 
917 /*
918  * attempts to save sp_entry into hca_list.
919  * becomes no-op if sp entry already exists.
920  * new hca entries and ia entries are created as needed.
921  */
922 static int
923 datadm_process_sp_entry(datadm_list_t *hca_list, datadm_sp_entry_t *sp_entry,
924     char *ia_name)
925 {
926 	datadm_hca_find_t	hca_find;
927 	datadm_ia_find_t	ia_find;
928 	datadm_hca_entry_t	*hca_entry;
929 
930 	hca_find.hf_sp_entry = sp_entry;
931 	hca_find.hf_hca_entry = NULL;
932 	(void) datadm_walk_list(hca_list, (int (*)(datadm_entry_t *, void *))
933 	    datadm_hca_entry_find, (void *)&hca_find);
934 
935 	if (hca_find.hf_hca_entry == NULL) {
936 		int	dlen;
937 
938 		/*
939 		 * hca_entry not found, need to create
940 		 * and insert one.
941 		 */
942 		hca_entry = datadm_alloc_hca_entry();
943 		if (hca_entry == NULL) {
944 			return (-1);
945 		}
946 		dlen = strlen(sp_entry->spe_devname);
947 		hca_entry->he_name = (char *)malloc(dlen + 1);
948 		if (hca_entry->he_name == NULL) {
949 			datadm_free_hca_entry(hca_entry);
950 			return (-1);
951 		}
952 		(void) strcpy(hca_entry->he_name, sp_entry->spe_devname);
953 		datadm_enqueue_entry(hca_list, (datadm_entry_t *)hca_entry);
954 	} else {
955 		hca_entry = hca_find.hf_hca_entry;
956 	}
957 	if (ia_name == NULL) {
958 		goto put_sp_entry;
959 	}
960 	ia_find.if_ia_name = ia_name;
961 	ia_find.if_ia_entry = NULL;
962 	(void) datadm_walk_list(&hca_entry->he_ia_list,
963 	    (int (*)(datadm_entry_t *, void *))datadm_ia_entry_find, &ia_find);
964 
965 	if (ia_find.if_ia_entry == NULL) {
966 		datadm_ia_entry_t	*ia_entry;
967 
968 		/*
969 		 * ia_entry not found, need to create
970 		 * and insert one.
971 		 */
972 		ia_entry = datadm_alloc_ia_entry();
973 		if (ia_entry == NULL) {
974 			return (-1);
975 		}
976 		(void) strlcpy(ia_entry->iae_name, ia_name, MAXLINKNAMELEN);
977 		datadm_enqueue_entry(&hca_entry->he_ia_list,
978 		    (datadm_entry_t *)ia_entry);
979 	}
980 
981 put_sp_entry:;
982 
983 	if (datadm_walk_list(&hca_entry->he_sp_list,
984 	    (int (*)(datadm_entry_t *, void *))datadm_sp_entry_match,
985 	    (void *)sp_entry)) {
986 		return (1);
987 	} else {
988 		/*
989 		 * only insert sp_entry if it is not found.
990 		 */
991 		datadm_enqueue_entry(&hca_entry->he_sp_list,
992 		    (datadm_entry_t *)sp_entry);
993 	}
994 	return (0);
995 }
996 
997 /*
998  * parses service_provider.conf
999  */
1000 static int
1001 datadm_parse_sp_conf(datadm_list_t *hca_list)
1002 {
1003 	datadm_sp_entry_t	*sp_entry;
1004 	FILE			*sp_file;
1005 	char			*sp_conf = datadm_args.da_sp_conf;
1006 	char			*tokens[DATADM_MAX_TOKENS];
1007 	char			line_buf[DATADM_LINESZ];
1008 	int			retval = 0;
1009 	int			token_count = 0;
1010 	int			line_count = 0;
1011 
1012 	sp_file = fopen(sp_conf, "r");
1013 	if (sp_file == NULL) {
1014 		(void) fprintf(stderr,
1015 		    gettext("datadm: cannot open %s\n"), sp_conf);
1016 		return (-1);
1017 	}
1018 
1019 	for (;;) {
1020 		bzero(line_buf, DATADM_LINESZ);
1021 		if (fgets(line_buf, DATADM_LINESZ, sp_file) == NULL) {
1022 			break;
1023 		}
1024 		token_count = 0;
1025 		line_count++;
1026 		retval = datadm_parse_line(line_buf, tokens, &token_count);
1027 		if (retval != 0) {
1028 			(void) fprintf(stderr, gettext(
1029 			    "datadm: %s: line %d exceeded max length %d\n"),
1030 			    sp_conf, line_count, DATADM_LINESZ);
1031 			break;
1032 		}
1033 		if (token_count == 0) continue;
1034 		if (token_count == DATADM_NUM_SP_TOKENS) {
1035 			int i = 0;
1036 
1037 			sp_entry = datadm_alloc_sp_entry();
1038 			if (sp_entry == NULL) {
1039 				retval = -1;
1040 				break;
1041 			}
1042 
1043 			/*
1044 			 * sp_entry gets filled incrementally by
1045 			 * each parsing function
1046 			 */
1047 			for (i = 0; i < DATADM_NUM_SP_TOKENS &&
1048 			    retval == 0; i++) {
1049 				retval = (*datadm_sp_parse_funcs[i])
1050 				    (tokens[i], (void *)sp_entry);
1051 			}
1052 			if (retval != 0) {
1053 				(void) fprintf(stderr, gettext(
1054 				    "datadm: parse error: %s, "
1055 				    "line %d, token: %s\n"),
1056 				    sp_conf, line_count, tokens[i - 1]);
1057 				datadm_free_sp_entry(sp_entry);
1058 				sp_entry = NULL;
1059 				break;
1060 			}
1061 
1062 			retval = datadm_process_sp_entry(hca_list,
1063 			    sp_entry, NULL);
1064 			if (retval != 0) {
1065 				datadm_free_sp_entry(sp_entry);
1066 				if (retval == 1) {
1067 					retval = 0;
1068 				} else {
1069 					break;
1070 				}
1071 			}
1072 		} else {
1073 			(void) fprintf(stderr, gettext(
1074 			    "datadm: parse error: %s, line %d, "
1075 			    "# of tokens: %d, expected %d\n"), sp_conf,
1076 			    line_count, token_count, DATADM_NUM_SP_TOKENS);
1077 			retval = -1;
1078 			break;
1079 		}
1080 	}
1081 	if (retval != 0) {
1082 		datadm_free_list(hca_list,
1083 		    (void (*)(datadm_entry_t *))datadm_free_hca_entry);
1084 	}
1085 	(void) fclose(sp_file);
1086 	return (retval);
1087 }
1088 
1089 /*
1090  * parses dat.conf
1091  */
1092 static int
1093 datadm_parse_dat_conf(datadm_list_t *hca_list)
1094 {
1095 	boolean_t		save_header = B_TRUE;
1096 	datadm_sp_entry_t	*sp_entry;
1097 	FILE			*dat_file;
1098 	char			*dat_conf = datadm_args.da_dat_conf;
1099 	char			*tokens[DATADM_MAX_TOKENS];
1100 	char			line_buf[DATADM_LINESZ];
1101 	int			retval = 0;
1102 	int			token_count = 0;
1103 	int			line_count = 0;
1104 
1105 	dat_file = fopen(dat_conf, "r");
1106 	if (dat_file == NULL) {
1107 		/* dat.conf not existing is not an error for OP_ADD */
1108 		if (datadm_args.da_op_type == DATADM_OP_ADD) {
1109 			return (0);
1110 		}
1111 		(void) fprintf(stderr, gettext("datadm: cannot open %s\n"),
1112 		    dat_conf);
1113 		return (-1);
1114 	}
1115 
1116 	for (;;) {
1117 		bzero(line_buf, DATADM_LINESZ);
1118 		if (fgets(line_buf, DATADM_LINESZ, dat_file) == NULL) {
1119 			break;
1120 		}
1121 		token_count = 0;
1122 		line_count++;
1123 		retval = datadm_parse_line(line_buf, tokens, &token_count);
1124 		if (retval != 0) {
1125 			(void) fprintf(stderr, gettext(
1126 			    "datadm: %s: line %d exceeded max length %d\n"),
1127 			    dat_conf, line_count, DATADM_LINESZ);
1128 			break;
1129 		}
1130 		if (token_count == 0) {
1131 			datadm_cmnt_entry_t	*cmnt_entry;
1132 			int			cmnt_len;
1133 
1134 			/*
1135 			 * comments are saved only if they are
1136 			 * at the top of dat.conf.
1137 			 */
1138 			if (!save_header) continue;
1139 			cmnt_entry = datadm_alloc_cmnt_entry();
1140 			if (cmnt_entry == NULL) {
1141 				perror("datadm: malloc");
1142 				retval = -1;
1143 				break;
1144 			}
1145 			cmnt_len = strlen(line_buf);
1146 			cmnt_entry->cmnt_line = (char *)malloc(cmnt_len + 1);
1147 			if (cmnt_entry->cmnt_line == NULL) {
1148 				perror("datadm: malloc");
1149 				datadm_free_cmnt_entry(cmnt_entry);
1150 				retval = -1;
1151 				break;
1152 			}
1153 			(void) strncpy(cmnt_entry->cmnt_line,
1154 			    line_buf, cmnt_len);
1155 			cmnt_entry->cmnt_line[cmnt_len] = '\0';
1156 			datadm_enqueue_entry(&datadm_conf_header,
1157 			    (datadm_entry_t *)cmnt_entry);
1158 			continue;
1159 		}
1160 		if (token_count == DATADM_NUM_DAT_TOKENS) {
1161 			int i = 0;
1162 			char ia_name[MAXLINKNAMELEN];
1163 
1164 			/*
1165 			 * we stop saving comment lines once
1166 			 * we see the first valid line.
1167 			 */
1168 			save_header = B_FALSE;
1169 			sp_entry = datadm_alloc_sp_entry();
1170 			if (sp_entry == NULL) {
1171 				retval = -1;
1172 				break;
1173 			}
1174 
1175 			/*
1176 			 * sp_entry gets filled incrementally by
1177 			 * each parsing function
1178 			 */
1179 			for (i = 0; i < DATADM_NUM_DAT_TOKENS &&
1180 			    retval == 0; i++) {
1181 				void	*arg;
1182 
1183 				if (i == 0) {
1184 					/*
1185 					 * the first token (ia name)
1186 					 * does not belong to an
1187 					 * sp_entry
1188 					 */
1189 					arg = (void *)ia_name;
1190 				} else {
1191 					arg = (void *)sp_entry;
1192 				}
1193 				retval = (*datadm_dat_parse_funcs[i])
1194 				    (tokens[i], arg);
1195 			}
1196 			if (retval != 0) {
1197 				(void) fprintf(stderr, gettext(
1198 				    "datadm: parse error: %s, "
1199 				    "line %d, token: %s\n"), dat_conf,
1200 				    line_count, tokens[i - 1]);
1201 				datadm_free_sp_entry(sp_entry);
1202 				sp_entry = NULL;
1203 				break;
1204 			}
1205 
1206 			/*
1207 			 * we ignore the ibds in dat.conf if we are
1208 			 * doing update
1209 			 */
1210 			if (datadm_args.da_op_type == DATADM_OP_UPDATE) {
1211 				retval = datadm_process_sp_entry(hca_list,
1212 				    sp_entry, NULL);
1213 			} else {
1214 				retval = datadm_process_sp_entry(hca_list,
1215 				    sp_entry, ia_name);
1216 			}
1217 			if (retval != 0) {
1218 				datadm_free_sp_entry(sp_entry);
1219 				if (retval == 1) {
1220 					retval = 0;
1221 				} else {
1222 					break;
1223 				}
1224 			}
1225 		} else {
1226 			(void) fprintf(stderr, gettext(
1227 			    "datadm: parse error: %s, line %d, "
1228 			    "# of tokens: %d, expected %d\n"), dat_conf,
1229 			    line_count, token_count, DATADM_NUM_DAT_TOKENS);
1230 			retval = -1;
1231 			break;
1232 		}
1233 	}
1234 	if (retval != 0) {
1235 		datadm_free_list(&datadm_conf_header,
1236 		    (void (*)(datadm_entry_t *))datadm_free_cmnt_entry);
1237 		datadm_free_list(hca_list,
1238 		    (void (*)(datadm_entry_t *))datadm_free_hca_entry);
1239 	}
1240 	(void) fclose(dat_file);
1241 	return (retval);
1242 }
1243 
1244 /*
1245  * used by OP_REMOVE to invalidate common sp entries between hl1 and hl2.
1246  * invalid sp entries will be ignored by datadm_generate_dat_conf.
1247  */
1248 static void
1249 datadm_invalidate_common_sp_entries(datadm_list_t *hl1, datadm_list_t *hl2)
1250 {
1251 	datadm_entry_t	*he1, *he2;
1252 
1253 	he1 = hl1->dl_head;
1254 	while (he1 != NULL) {
1255 		he2 = hl2->dl_head;
1256 		while (he2 != NULL) {
1257 			datadm_entry_t	*se1, *se2;
1258 
1259 			if (!datadm_hca_entry_match(
1260 			    (datadm_hca_entry_t *)he1,
1261 			    (datadm_hca_entry_t *)he2)) {
1262 				he2 = he2->de_next;
1263 				continue;
1264 			}
1265 			se1 = ((datadm_hca_entry_t *)he1)->he_sp_list.dl_head;
1266 			while (se1 != NULL) {
1267 				se2 = ((datadm_hca_entry_t *)he2)->
1268 				    he_sp_list.dl_head;
1269 				while (se2 != NULL) {
1270 					if (!datadm_sp_entry_match(
1271 					    (datadm_sp_entry_t *)se1,
1272 					    (datadm_sp_entry_t *)se2)) {
1273 						se2 = se2->de_next;
1274 						continue;
1275 					}
1276 					((datadm_sp_entry_t *)se1)->
1277 					    spe_invalid = 1;
1278 					break;
1279 				}
1280 				se1 = se1->de_next;
1281 			}
1282 			break;
1283 		}
1284 		he1 = he1->de_next;
1285 	}
1286 }
1287 
1288 static int
1289 datadm_hca_entry_find_by_name(datadm_hca_entry_t *h1,
1290     datadm_hca_find_by_name_t *hf)
1291 {
1292 	if (datadm_str_match(h1->he_name, hf->hf_name)) {
1293 		hf->hf_hca_entry = h1;
1294 		return (1);
1295 	}
1296 	return (0);
1297 }
1298 
1299 datadm_hca_entry_t *
1300 datadm_hca_lookup_by_name(datadm_list_t *hca_list, char *hca_driver_name)
1301 {
1302 	datadm_hca_find_by_name_t	hf;
1303 
1304 	hf.hf_name = hca_driver_name;
1305 	hf.hf_hca_entry = NULL;
1306 	(void) datadm_walk_list(hca_list,
1307 	    (int (*)(datadm_entry_t *, void *))datadm_hca_entry_find_by_name,
1308 	    &hf);
1309 	return (hf.hf_hca_entry);
1310 }
1311 
1312 static boolean_t
1313 datadm_add_plink(char *linkname, datadm_fill_ia_list_t *ia_args)
1314 {
1315 	datalink_class_t	class;
1316 	datalink_id_t		linkid;
1317 	dladm_ib_attr_t		ib_attr;
1318 	ibnex_ctl_query_hca_t	query_hca;
1319 	datadm_hca_entry_t	*hca;
1320 	struct lifreq		req;
1321 	datadm_ia_find_t	ia_find;
1322 	datadm_ia_entry_t	*ia_entry;
1323 
1324 	if ((dladm_name2info(ia_args->ia_dlh, linkname, &linkid, NULL, &class,
1325 	    NULL) != DLADM_STATUS_OK) ||
1326 	    (class != DATALINK_CLASS_PART) ||
1327 	    (dladm_part_info(ia_args->ia_dlh, linkid, &ib_attr,
1328 	    DLADM_OPT_ACTIVE) != DLADM_STATUS_OK)) {
1329 		return (B_FALSE);
1330 	}
1331 
1332 	(void) strlcpy(req.lifr_name, linkname, sizeof (req.lifr_name));
1333 	/*
1334 	 * we don't really need to know the ip address.
1335 	 * we just want to check if the device is plumbed
1336 	 * or not.
1337 	 */
1338 	if (ioctl(ia_args->ia_sock_fd_v4, SIOCGLIFADDR, (caddr_t)&req) != 0) {
1339 		/*
1340 		 * we try v6 if the v4 address isn't found.
1341 		 */
1342 		if (ioctl(ia_args->ia_sock_fd_v6, SIOCGLIFADDR,
1343 		    (caddr_t)&req) != 0)
1344 			return (B_FALSE);
1345 	}
1346 
1347 	bzero(&query_hca, sizeof (query_hca));
1348 	query_hca.hca_guid = ib_attr.dia_hca_guid;
1349 	if (ioctl(ia_args->ia_ibnex_fd, IBNEX_CTL_QUERY_HCA, &query_hca) == -1)
1350 		return (B_FALSE);
1351 
1352 	if ((hca = datadm_hca_lookup_by_name(ia_args->ia_hca_list,
1353 	    query_hca.hca_info.hca_driver_name)) == NULL)
1354 		return (B_FALSE);
1355 
1356 	ia_find.if_ia_name = linkname;
1357 	ia_find.if_ia_entry = NULL;
1358 	(void) datadm_walk_list(&hca->he_ia_list,
1359 	    (int (*)(datadm_entry_t *, void *))
1360 	    datadm_ia_entry_find, &ia_find);
1361 
1362 	if (ia_find.if_ia_entry == NULL) {
1363 		/*
1364 		 * we insert an ia entry only if
1365 		 * it is unique.
1366 		 */
1367 		ia_entry = datadm_alloc_ia_entry();
1368 		if (ia_entry != NULL) {
1369 			(void) strlcpy(ia_entry->iae_name, linkname,
1370 			    MAXLINKNAMELEN);
1371 			datadm_enqueue_entry(&hca->he_ia_list,
1372 			    (datadm_entry_t *)ia_entry);
1373 		}
1374 	}
1375 
1376 	return (B_FALSE);
1377 }
1378 
1379 /*
1380  * build ia lists for each hca_list element
1381  */
1382 static int
1383 datadm_build_ia_lists(datadm_list_t *hca_list)
1384 {
1385 	dladm_handle_t		dlh;
1386 	datadm_fill_ia_list_t	ia_args;
1387 	int			rv = -1;
1388 	int			fd = -1;
1389 	int			sv4 = -1;
1390 	int			sv6 = -1;
1391 
1392 	if (dladm_open(&dlh) != DLADM_STATUS_OK)
1393 		return (-1);
1394 
1395 	if ((fd = open(IBNEX_DEVCTL_DEV, O_RDONLY)) < 0)
1396 		goto out;
1397 
1398 	if ((sv4 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
1399 		perror("datadm: socket");
1400 		goto out;
1401 	}
1402 
1403 	if ((sv6 = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
1404 		perror("datadm: socket");
1405 		goto out;
1406 	}
1407 
1408 	ia_args.ia_hca_list = hca_list;
1409 	ia_args.ia_dlh = dlh;
1410 	ia_args.ia_ibnex_fd = fd;
1411 	ia_args.ia_sock_fd_v4 = sv4;
1412 	ia_args.ia_sock_fd_v6 = sv6;
1413 
1414 	dlpi_walk((boolean_t (*) (const char *, void *))datadm_add_plink,
1415 	    &ia_args, 0);
1416 	rv = 0;
1417 
1418 out:
1419 	if (sv4 != -1)
1420 		(void) close(sv4);
1421 	if (sv6 != -1)
1422 		(void) close(sv6);
1423 	if (fd != -1)
1424 		(void) close(fd);
1425 
1426 	dladm_close(dlh);
1427 	return (rv);
1428 }
1429 
1430 static int
1431 datadm_generate_conf_entry(FILE *outfile, datadm_ia_entry_t *ia_entry,
1432     datadm_sp_entry_t *sp_entry)
1433 {
1434 	int	retval;
1435 
1436 	retval = fprintf(outfile,
1437 	    "%s  %s%d.%d  %s  %s  %s  %s%d.%d  \"%s\"  \"%s%s%s\"\n",
1438 	    ia_entry->iae_name,
1439 	    (sp_entry->spe_api_version.dv_name ?
1440 	    sp_entry->spe_api_version.dv_name : ""),
1441 	    sp_entry->spe_api_version.dv_major,
1442 	    sp_entry->spe_api_version.dv_minor,
1443 	    (sp_entry->spe_threadsafe ? "threadsafe" : "nonthreadsafe"),
1444 	    (sp_entry->spe_default ? "default" : "nondefault"),
1445 	    sp_entry->spe_libpath,
1446 	    (sp_entry->spe_sp_version.dv_name ?
1447 	    sp_entry->spe_sp_version.dv_name : ""),
1448 	    sp_entry->spe_sp_version.dv_major,
1449 	    sp_entry->spe_sp_version.dv_minor,
1450 	    sp_entry->spe_sp_data,
1451 	    DATADM_DRV_NAME, "=", sp_entry->spe_devname);
1452 
1453 	if (retval < 0) {
1454 		return (-1);
1455 	}
1456 	return (0);
1457 }
1458 
1459 /*
1460  * generate dat.conf header
1461  */
1462 static int
1463 datadm_generate_conf_header(FILE *outfile)
1464 {
1465 	datadm_entry_t		*cep;
1466 	datadm_cmnt_entry_t	*cmnt;
1467 	int			retval = 0;
1468 
1469 	cep = datadm_conf_header.dl_head;
1470 	if (cep == NULL) {
1471 		/*
1472 		 * if dat.conf doesn't have a header, we prepend a
1473 		 * default one.
1474 		 */
1475 		retval = fprintf(outfile, "%s", datadm_conf_header_default);
1476 		goto done;
1477 	}
1478 	while (cep != NULL) {
1479 		cmnt = (datadm_cmnt_entry_t *)cep;
1480 		if (cmnt->cmnt_line != NULL) {
1481 			int		len;
1482 
1483 			retval = fprintf(outfile, "%s", cmnt->cmnt_line);
1484 			if (retval < 0) {
1485 				break;
1486 			}
1487 
1488 			/*
1489 			 * append a newline if the comment line doesn't
1490 			 * have one.
1491 			 */
1492 			len = strlen(cmnt->cmnt_line);
1493 			if (cmnt->cmnt_line[len - 1] != '\n') {
1494 				retval = fprintf(outfile, "\n");
1495 				if (retval < 0) {
1496 					break;
1497 				}
1498 			}
1499 		}
1500 		cep = cep->de_next;
1501 	}
1502 done:;
1503 	if (retval < 0) {
1504 		return (-1);
1505 	}
1506 	return (0);
1507 }
1508 
1509 /*
1510  * outputs dat.conf to stdout or to basedir/etc/dat/dat.conf
1511  */
1512 static int
1513 datadm_generate_dat_conf(datadm_list_t *hca_list)
1514 {
1515 	FILE			*outfile = NULL;
1516 	char			*dat_conf = datadm_args.da_dat_conf;
1517 	datadm_entry_t		*hep;
1518 	int			retval = 0;
1519 
1520 	if (datadm_args.da_op_type == DATADM_OP_VIEW) {
1521 		outfile = stdout;
1522 	} else {
1523 		outfile = fopen(dat_conf, "w+");
1524 		if (outfile == NULL) {
1525 			(void) fprintf(stderr, gettext(
1526 			    "datadm: cannot open %s: %s\n"),
1527 			    dat_conf, strerror(errno));
1528 			return (-1);
1529 		}
1530 	}
1531 	if (outfile != stdout) {
1532 		/*
1533 		 * do not generate the header if we are
1534 		 * printing to the screen
1535 		 */
1536 		retval = datadm_generate_conf_header(outfile);
1537 		if (retval != 0) {
1538 			goto done;
1539 		}
1540 	}
1541 	hep = hca_list->dl_head;
1542 	while (hep != NULL) {
1543 		datadm_entry_t	*iep;
1544 
1545 		iep = ((datadm_hca_entry_t *)hep)->he_ia_list.dl_head;
1546 		while (iep != NULL) {
1547 			datadm_entry_t	*sep;
1548 
1549 			sep = ((datadm_hca_entry_t *)hep)->he_sp_list.dl_head;
1550 			while (sep != NULL) {
1551 				if (((datadm_sp_entry_t *)sep)->spe_invalid) {
1552 					sep = sep->de_next;
1553 					continue;
1554 				}
1555 				retval = datadm_generate_conf_entry(outfile,
1556 				    (datadm_ia_entry_t *)iep,
1557 				    (datadm_sp_entry_t *)sep);
1558 				if (retval != 0) {
1559 					goto done;
1560 				}
1561 				sep = sep->de_next;
1562 			}
1563 			iep = iep->de_next;
1564 		}
1565 		hep = hep->de_next;
1566 	}
1567 	retval = fflush(outfile);
1568 done:;
1569 	if (outfile != stdout) {
1570 		(void) fclose(outfile);
1571 	}
1572 	if (retval < 0) {
1573 		perror("datadm: fprintf");
1574 	}
1575 	return (retval);
1576 }
1577 
1578 static int
1579 datadm_view(void)
1580 {
1581 	int			retval = 0;
1582 	datadm_list_t		hca_list;
1583 
1584 	bzero(&hca_list, sizeof (hca_list));
1585 
1586 	retval = datadm_parse_dat_conf(&hca_list);
1587 	if (retval != 0) {
1588 		goto cleanup;
1589 	}
1590 	retval = datadm_generate_dat_conf(&hca_list);
1591 	if (retval != 0) {
1592 		goto cleanup;
1593 	}
1594 
1595 cleanup:;
1596 	datadm_free_list(&datadm_conf_header,
1597 	    (void (*)(datadm_entry_t *))datadm_free_cmnt_entry);
1598 	datadm_free_list(&hca_list,
1599 	    (void (*)(datadm_entry_t *))datadm_free_hca_entry);
1600 	return (retval);
1601 }
1602 
1603 static int
1604 datadm_update(void)
1605 {
1606 	int			retval = 0;
1607 	datadm_list_t		hca_list;
1608 
1609 	bzero(&hca_list, sizeof (hca_list));
1610 
1611 	retval = datadm_parse_dat_conf(&hca_list);
1612 	if (retval != 0) {
1613 		goto cleanup;
1614 	}
1615 	retval = datadm_build_ia_lists(&hca_list);
1616 	if (retval != 0) {
1617 		goto cleanup;
1618 	}
1619 	retval = datadm_generate_dat_conf(&hca_list);
1620 	if (retval != 0) {
1621 		goto cleanup;
1622 	}
1623 
1624 cleanup:;
1625 	datadm_free_list(&datadm_conf_header,
1626 	    (void (*)(datadm_entry_t *))datadm_free_cmnt_entry);
1627 	datadm_free_list(&hca_list,
1628 	    (void (*)(datadm_entry_t *))datadm_free_hca_entry);
1629 	return (retval);
1630 }
1631 
1632 static int
1633 datadm_add(void)
1634 {
1635 	int			retval = 0;
1636 	datadm_list_t		hca_list;
1637 
1638 	bzero(&hca_list, sizeof (hca_list));
1639 
1640 	retval = datadm_parse_dat_conf(&hca_list);
1641 	if (retval != 0) {
1642 		goto cleanup;
1643 	}
1644 	retval = datadm_parse_sp_conf(&hca_list);
1645 	if (retval != 0) {
1646 		goto cleanup;
1647 	}
1648 	retval = datadm_build_ia_lists(&hca_list);
1649 	if (retval != 0) {
1650 		goto cleanup;
1651 	}
1652 	retval = datadm_generate_dat_conf(&hca_list);
1653 	if (retval != 0) {
1654 		goto cleanup;
1655 	}
1656 
1657 cleanup:;
1658 	datadm_free_list(&datadm_conf_header,
1659 	    (void (*)(datadm_entry_t *))datadm_free_cmnt_entry);
1660 	datadm_free_list(&hca_list,
1661 	    (void (*)(datadm_entry_t *))datadm_free_hca_entry);
1662 	return (retval);
1663 }
1664 
1665 static int
1666 datadm_remove(void)
1667 {
1668 	int			retval = 0;
1669 	datadm_list_t		hca_list;
1670 	datadm_list_t		hca_list2;
1671 
1672 	bzero(&hca_list, sizeof (hca_list));
1673 	bzero(&hca_list2, sizeof (hca_list2));
1674 
1675 	retval = datadm_parse_dat_conf(&hca_list);
1676 	if (retval != 0) {
1677 		goto cleanup;
1678 	}
1679 	retval = datadm_parse_sp_conf(&hca_list2);
1680 	if (retval != 0) {
1681 		goto cleanup;
1682 	}
1683 	datadm_invalidate_common_sp_entries(&hca_list, &hca_list2);
1684 
1685 	retval = datadm_generate_dat_conf(&hca_list);
1686 	if (retval != 0) {
1687 		goto cleanup;
1688 	}
1689 
1690 cleanup:;
1691 	datadm_free_list(&datadm_conf_header,
1692 	    (void (*)(datadm_entry_t *))datadm_free_cmnt_entry);
1693 	datadm_free_list(&hca_list,
1694 	    (void (*)(datadm_entry_t *))datadm_free_hca_entry);
1695 	datadm_free_list(&hca_list2,
1696 	    (void (*)(datadm_entry_t *))datadm_free_hca_entry);
1697 	return (retval);
1698 }
1699 
1700 static int
1701 datadm_locate_dat_conf(char *basedir)
1702 {
1703 	char		*dat_conf;
1704 
1705 	if (basedir == NULL) {
1706 		datadm_args.da_dat_conf = DATADM_DAT_CONF;
1707 		return (0);
1708 	}
1709 	dat_conf = (char *)malloc(strlen(basedir) +
1710 	    strlen(DATADM_DAT_CONF) + 1);
1711 	if (dat_conf == NULL) {
1712 		return (-1);
1713 	}
1714 	dat_conf[0] = '\0';
1715 	(void) strcat(dat_conf, basedir);
1716 	(void) strcat(dat_conf, DATADM_DAT_CONF);
1717 	datadm_args.da_dat_conf = dat_conf;
1718 	return (0);
1719 }
1720 
1721 int
1722 main(int argc, char **argv)
1723 {
1724 	extern char	*optarg;
1725 	extern int	optind;
1726 	char		*basedir = NULL;
1727 	int		c, retval;
1728 	int		op_type = -1, errflg = 0;
1729 
1730 	bzero(&datadm_args, sizeof (datadm_args));
1731 	bzero(&datadm_conf_header, sizeof (datadm_conf_header));
1732 
1733 	(void) setlocale(LC_ALL, "");
1734 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
1735 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
1736 #endif
1737 	(void) textdomain(TEXT_DOMAIN);
1738 
1739 	while ((c = getopt(argc, argv, "vua:r:b:")) != EOF) {
1740 		switch (c) {
1741 		case 'v':
1742 			if (op_type != -1) errflg = 1;
1743 			op_type = DATADM_OP_VIEW;
1744 			break;
1745 		case 'u':
1746 			if (op_type != -1) errflg = 1;
1747 			op_type = DATADM_OP_UPDATE;
1748 			break;
1749 		case 'a':
1750 			if (op_type != -1) errflg = 1;
1751 			op_type = DATADM_OP_ADD;
1752 			datadm_args.da_sp_conf = optarg;
1753 			break;
1754 		case 'r':
1755 			if (op_type != -1) errflg = 1;
1756 			op_type = DATADM_OP_REMOVE;
1757 			datadm_args.da_sp_conf = optarg;
1758 			break;
1759 		case 'b':
1760 			basedir = optarg;
1761 			break;
1762 		default:
1763 			errflg = 1;
1764 			break;
1765 		}
1766 		if (errflg != 0) {
1767 			break;
1768 		}
1769 	}
1770 	if (errflg != 0 || op_type == -1 || optind < argc) {
1771 		datadm_usage();
1772 		return (1);
1773 	}
1774 	datadm_args.da_op_type = op_type;
1775 	if (datadm_locate_dat_conf(basedir)) {
1776 		return (1);
1777 	}
1778 
1779 	retval = (*datadm_ops[op_type])();
1780 	return (retval);
1781 }
1782