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