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