xref: /titanic_50/usr/src/lib/libdscfg/common/cfg_vols.c (revision bde3d612a7c090234c60e6e4578821237a5db135)
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 <sys/types.h>
27 #include <stdio.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <sys/stat.h>
31 #include <sys/mkdev.h>
32 #include <strings.h>
33 #include <stdarg.h>
34 #include <stdlib.h>
35 #include <locale.h>
36 #include <errno.h>
37 
38 #include <sys/nsctl/cfg.h>
39 
40 #include <sys/unistat/spcs_s.h>
41 #include <sys/unistat/spcs_s_u.h>
42 #include <sys/unistat/spcs_errors.h>
43 #include <sys/unistat/spcs_s_impl.h>
44 
45 #include <sys/nsctl/sv.h>
46 #include <sys/nsctl/nsc_hash.h>
47 
48 #define	DEV_EXPAND 32
49 
50 #define	DO_DISABLE 0
51 #define	DO_ENABLE 1
52 
53 /*
54  * Utility functions for iiadm and rdcadm/sndradm.
55  */
56 
57 typedef struct hash_data_s {
58 	union {
59 		char *users;
60 		char *mode;
61 	} u;
62 	char *path;
63 	char *node;
64 	int setno;
65 } hash_data_t;
66 
67 typedef struct {
68 	dev_t	rdev;
69 	mode_t  mode;
70 	char	*path;
71 } device_t;
72 
73 static hash_data_t *make_svol_data(char *, char *, char *, int);
74 static hash_data_t *make_dsvol_data(char *, char *, char *, int);
75 static void delete_svol_data(void *);
76 static void delete_dsvol_data(void *);
77 static int sv_action(char *, CFGFILE *, char *, int);
78 
79 static int add_dev_entry(const char *);
80 static int compare(const void *, const void *);
81 static char *find_devid(const char *);
82 static void free_dev_entries();
83 static void rebuild_devhash();
84 
85 static hash_node_t **dsvol;
86 static int dsvol_loaded = 0;
87 
88 static hash_node_t **svol;
89 static int svol_loaded = 0;
90 
91 static hash_node_t **shadowvol;
92 
93 static hash_node_t **devhash;
94 static device_t *devlist;
95 static int devcount = 0;
96 static int devalloc = 0;
97 
98 /*
99  * cfg_add_user
100  *
101  * Description:
102  *	Adds the calling tool as a user of the volume.
103  *
104  * Inputs:
105  *	char *path: The pathname of the volume to be enabled.
106  *	char *cnode: The device group name, or NULL if -C local or not cluster
107  *	CFGFILE *cfg: A pointer to the current config file, or NULL if this
108  *		function is to open/write/commit/close the change itself.
109  *
110  * Return values:
111  *	CFG_USER_FIRST: Indicates that this is the first user of this
112  *		particular volume.
113  *	CFG_USER_OK: Indicates that the volume has already been entered into
114  *		the config file.
115  *	CFG_USER_ERR: Indicates that some failure has occurred and no changes
116  *		to the config file have been made.
117  *	CFG_USER_REPEAT: Indicates that this user has already registered for
118  *		the volume.
119  */
120 int
121 cfg_add_user(CFGFILE* cfg, char *path, char *cnode, char *user)
122 {
123 	int self_open, self_loaded, change_made;
124 	char *ctag, search_key[ CFG_MAX_KEY ], buf[ CFG_MAX_BUF ];
125 	int retval, rc;
126 	hash_data_t *data;
127 
128 	self_open = (cfg == NULL);
129 	self_loaded = 0;
130 	change_made = 0;
131 
132 	if (self_open) {
133 		cfg = cfg_open(NULL);
134 		if (cfg == NULL) {
135 			return (CFG_USER_ERR);
136 		}
137 
138 		if (!cfg_lock(cfg, CFG_WRLOCK)) {
139 			/* oops */
140 			cfg_close(cfg);
141 			return (CFG_USER_ERR);
142 		}
143 	}
144 
145 	/* Check cnode */
146 	ctag = cfg_get_resource(cfg);
147 	if (cnode) {
148 		if (ctag) {
149 			if (strcmp(cnode, ctag))
150 				return (CFG_USER_ERR);
151 		} else
152 			cfg_resource(cfg, cnode);
153 	} else
154 		cnode = ctag;
155 
156 	if (!dsvol_loaded) {
157 		if (cfg_load_dsvols(cfg) < 0) {
158 			if (self_open) {
159 				cfg_close(cfg);
160 			}
161 			return (CFG_USER_ERR);
162 		}
163 		self_loaded = 1;
164 	}
165 
166 	/* find the volume */
167 	(void) snprintf(search_key, CFG_MAX_KEY, "%s:%s", path, cnode);
168 	data = nsc_lookup(dsvol, search_key);
169 
170 	if (!data) {
171 		/* whoops, not found.  Add as new user */
172 		cfg_rewind(cfg, CFG_SEC_CONF);
173 		(void) snprintf(buf, CFG_MAX_BUF, "%s %s %s", path, cnode,
174 		    user);
175 		rc = cfg_put_cstring(cfg, "dsvol", buf, strlen(buf));
176 		if (rc < 0) {
177 			if (self_loaded) {
178 				cfg_unload_dsvols();
179 			}
180 			if (self_open) {
181 				cfg_close(cfg);
182 			}
183 			return (CFG_USER_ERR);
184 		}
185 		/* reload hash, if we need to */
186 		if (!self_loaded) {
187 			cfg_unload_dsvols();
188 			if (cfg_load_dsvols(cfg) < 0) {
189 				if (self_open) {
190 					cfg_close(cfg);
191 				}
192 				return (CFG_USER_ERR);
193 			}
194 		}
195 		retval = CFG_USER_FIRST;
196 		change_made = 1;
197 	} else {
198 		/* Check to ensure we're not already listed */
199 		char *p = strdup(data->u.users);
200 		char *q = strtok(p, ",");
201 		while (q && (strcmp(q, user) != 0)) {
202 			q = strtok(0, ",");
203 		}
204 		free(p);	/* not using data; only testing 'q' ptr */
205 
206 		if (!q) {
207 			/* not listed as a user */
208 			cfg_rewind(cfg, CFG_SEC_CONF);
209 			(void) snprintf(buf, CFG_MAX_BUF, "%s %s %s,%s",
210 			    data->path, data->node, data->u.users, user);
211 			(void) snprintf(search_key, CFG_MAX_KEY, "dsvol.set%d",
212 			    data->setno);
213 			if (cfg_put_cstring(cfg, search_key, buf,
214 			    strlen(buf)) < 0) {
215 				if (self_loaded) {
216 					cfg_unload_dsvols();
217 				}
218 				if (self_open) {
219 					cfg_close(cfg);
220 				}
221 				return (CFG_USER_ERR);
222 			}
223 
224 			/*
225 			 * Since we deleted an entry from the config
226 			 * file, we don't know what all the new
227 			 * set numbers are.  We need to reload
228 			 * everything
229 			 */
230 			if (!self_loaded) {
231 				cfg_unload_dsvols();
232 				if (cfg_load_dsvols(cfg) < 0) {
233 					if (self_open) {
234 						cfg_close(cfg);
235 					}
236 					return (CFG_USER_ERR);
237 				}
238 			}
239 			change_made = 1;
240 			retval = CFG_USER_OK;
241 		} else {
242 			retval = CFG_USER_REPEAT;
243 		}
244 	}
245 
246 	if (self_loaded) {
247 		cfg_unload_dsvols();
248 	}
249 
250 	if (self_open) {
251 		if (change_made)
252 			(void) cfg_commit(cfg);
253 		cfg_close(cfg);
254 	}
255 
256 	return (retval);
257 }
258 
259 /*
260  * cfg_rem_user
261  *
262  * Description:
263  *	Removes a user from the config file.
264  *
265  * Inputs:
266  *	char *path: The pathname of the volume to be enabled.
267  *	char *cnode: The device group name, or NULL if -C local or not cluster
268  *	char *user: The subsystem that is adding this tag (sv, ii, sndr)
269  *	CFGFILE *cfg: A pointer to the current config file, or NULL if this
270  *		function is to open/write/commit/close the change itself.
271  * Return values:
272  *	CFG_USER_ERR: An error occurred during the processing of this
273  *		directive.
274  *	CFG_USER_OK: User successfully removed; volume in use by other(s).
275  *	CFG_USER_LAST: User successfuly removed; no other users registered
276  *	CFG_USER_GONE: The volume is no longer listed in the dsvol section,
277  *		indicating some sort of application-level error.
278  *
279  */
280 int
281 cfg_rem_user(CFGFILE *cfg, char *path, char *cnode, char *user)
282 {
283 	int self_open, self_loaded, change_made;
284 	char *ctag, search_key[ CFG_MAX_KEY ], buf[ CFG_MAX_BUF ];
285 	char cfg_key[ CFG_MAX_KEY ];
286 	hash_data_t *data;
287 	int retval;
288 	int force_remove;
289 
290 	self_open = (cfg == NULL);
291 	self_loaded = 0;
292 	change_made = 0;
293 	force_remove = (strcmp(user, "sv") == 0);
294 
295 	if ('-' == *user) {
296 		++user;
297 	}
298 
299 	/* Check cnode */
300 	ctag = cfg_get_resource(cfg);
301 	if (cnode) {
302 		if (ctag) {
303 			if (strcmp(cnode, ctag))
304 				return (CFG_USER_ERR);
305 		} else
306 			cfg_resource(cfg, cnode);
307 	} else
308 		cnode = ctag;
309 
310 	if (self_open) {
311 		cfg = cfg_open(NULL);
312 		if (cfg == NULL) {
313 			return (CFG_USER_ERR);
314 		}
315 
316 		if (!cfg_lock(cfg, CFG_WRLOCK)) {
317 			/* oops */
318 			cfg_close(cfg);
319 			return (CFG_USER_ERR);
320 		}
321 	}
322 
323 
324 	change_made = 0;
325 	if (!dsvol_loaded) {
326 		if (cfg_load_dsvols(cfg) < 0) {
327 			if (self_open) {
328 				cfg_close(cfg);
329 			}
330 			return (CFG_USER_ERR);
331 		}
332 		self_loaded = 1;
333 	}
334 
335 	/* find the volume */
336 	(void) snprintf(search_key, CFG_MAX_KEY, "%s:%s", path, cnode);
337 	data = nsc_lookup(dsvol, search_key);
338 
339 	if (!data) {
340 		/* yipes */
341 		retval = CFG_USER_GONE;
342 	} else if (force_remove) {
343 		retval = CFG_USER_LAST;
344 		cfg_rewind(cfg, CFG_SEC_CONF);
345 		(void) snprintf(cfg_key, CFG_MAX_KEY, "dsvol.set%d",
346 		    data->setno);
347 		if (cfg_put_cstring(cfg, cfg_key, NULL, 0) < 0) {
348 			if (self_loaded) {
349 				cfg_unload_dsvols();
350 			}
351 			if (self_open) {
352 				cfg_close(cfg);
353 			}
354 			return (CFG_USER_ERR);
355 		}
356 		if (!self_loaded) {
357 			cfg_unload_dsvols();
358 			if (cfg_load_dsvols(cfg) < 0) {
359 				if (self_open) {
360 					cfg_close(cfg);
361 				}
362 				return (CFG_USER_ERR);
363 			}
364 		}
365 	} else {
366 		char *p = strdup(data->u.users);
367 		char *q = strtok(p, ",");
368 		int appended = 0;
369 
370 		(void) snprintf(buf, CFG_MAX_BUF, "%s %s ", data->path,
371 		    data->node);
372 		while (q && (strcmp(q, user) != 0)) {
373 			if (appended) {
374 				strcat(buf, ",");
375 				strcat(buf, q);
376 			} else {
377 				strcat(buf, q);
378 				appended = 1;
379 			}
380 			q = strtok(0, ",");
381 		}
382 
383 		if (!q) {
384 			/* uh-oh */
385 			retval = CFG_USER_GONE;
386 		} else {
387 			/* old user skipped; add in remaining users */
388 			while (q = strtok(0, ", ")) {
389 				if (appended) {
390 					strcat(buf, ",");
391 					strcat(buf, q);
392 				} else {
393 					strcat(buf, q);
394 					appended = 1;
395 				}
396 			}
397 
398 			if (appended) {
399 				retval = CFG_USER_OK;
400 				cfg_rewind(cfg, CFG_SEC_CONF);
401 				(void) snprintf(cfg_key, CFG_MAX_KEY,
402 				    "dsvol.set%d", data->setno);
403 				if (cfg_put_cstring(cfg, cfg_key, buf,
404 				    strlen(buf)) < 0) {
405 					if (self_loaded) {
406 						cfg_unload_dsvols();
407 					}
408 					if (self_open) {
409 						cfg_close(cfg);
410 					}
411 					return (CFG_USER_ERR);
412 				}
413 				if (!self_loaded) {
414 					cfg_unload_dsvols();
415 					if (cfg_load_dsvols(cfg) < 0) {
416 						if (self_open) {
417 							cfg_close(cfg);
418 						}
419 						return (CFG_USER_ERR);
420 					}
421 				}
422 			} else {
423 				retval = CFG_USER_LAST;
424 				cfg_rewind(cfg, CFG_SEC_CONF);
425 				(void) snprintf(cfg_key, CFG_MAX_KEY,
426 				    "dsvol.set%d", data->setno);
427 				if (cfg_put_cstring(cfg, cfg_key, NULL,
428 				    0) < 0) {
429 					if (self_loaded) {
430 						cfg_unload_dsvols();
431 					}
432 					if (self_open) {
433 						cfg_close(cfg);
434 					}
435 					return (CFG_USER_ERR);
436 				}
437 				/*
438 				 * Since we deleted an entry from the config
439 				 * file, we don't know what all the new
440 				 * set numbers are.  We need to reload
441 				 * everything
442 				 */
443 				if (!self_loaded) {
444 					cfg_unload_dsvols();
445 					if (cfg_load_dsvols(cfg) < 0) {
446 						if (self_open) {
447 							cfg_close(cfg);
448 						}
449 						return (CFG_USER_ERR);
450 					}
451 				}
452 			}
453 			change_made = 1;
454 		}
455 	}
456 
457 	if (self_loaded) {
458 		cfg_unload_dsvols();
459 	}
460 
461 	if (self_open) {
462 		if (change_made)
463 			(void) cfg_commit(cfg);
464 		cfg_close(cfg);
465 	}
466 
467 	return (retval);
468 }
469 
470 /*
471  * Enable a volume under SV control (or add this char *user to the list
472  * of users of that volume).
473  *
474  * Parameters:
475  *	cfg	- The config file to use.
476  *	path	- The pathname of the volume
477  *	ctag	- The cluster tag for this volume (if any)
478  *	user	- The user (sv, ii, sndr) of the volume.
479  */
480 int
481 cfg_vol_enable(CFGFILE *cfg, char *path, char *ctag, char *user)
482 {
483 	int rc;
484 	int retval;
485 
486 	if (!ctag || *ctag == '\0') {
487 		ctag = "-";
488 	}
489 
490 	retval = -1;
491 	rc = cfg_add_user(cfg, path, ctag, user);
492 	switch (rc) {
493 	case CFG_USER_ERR:
494 		spcs_log("dsvol", NULL,
495 		    gettext("unable to set up dsvol section of config for %s"),
496 		    path);
497 		break;
498 	case CFG_USER_OK:
499 		retval = 0;
500 		break;
501 	case CFG_USER_FIRST:
502 		/* enable sv! */
503 		retval = sv_action(path, cfg, ctag, DO_ENABLE);
504 		if (retval < 0) {
505 			(void) cfg_rem_user(cfg, path, ctag, user);
506 		}
507 		break;
508 	default:
509 		spcs_log("dsvol", NULL,
510 		    gettext("unexpected return from cfg_add_user(%d)"), rc);
511 		break;
512 	}
513 
514 	return (retval);
515 }
516 
517 /*
518  * Disable a volume from SV control (or remove this char *user from the list
519  * of users of that volume).
520  *
521  * Parameters:
522  *	cfg	- The config file to use.
523  *	path	- The pathname of the volume
524  *	ctag	- The cluster tag for this volume (if any)
525  *	user	- The user (sv, ii, sndr) of the volume.
526  */
527 int
528 cfg_vol_disable(CFGFILE *cfg, char *path, char *ctag, char *user)
529 {
530 	int rc;
531 	int retval;
532 
533 	if (!ctag || *ctag == '\0') {
534 		ctag = "-";
535 	}
536 
537 	retval = -1;
538 	rc = cfg_rem_user(cfg, path, ctag, user);
539 	switch (rc) {
540 	case CFG_USER_ERR:
541 		spcs_log("dsvol", NULL,
542 		    gettext("unable to set up dsvol section of config for %s"),
543 		    path);
544 		break;
545 	case CFG_USER_OK:
546 		retval = 0;
547 		break;
548 	case CFG_USER_GONE:
549 		spcs_log("dsvol", NULL,
550 		    gettext("%s tried to remove non-existent tag for %s"),
551 		    user, path);
552 		break;
553 	case CFG_USER_LAST:
554 		/* diable sv! */
555 		retval = sv_action(path, cfg, ctag, DO_DISABLE);
556 		break;
557 	default:
558 		spcs_log("dsvol", NULL,
559 		    gettext("unexpected return from cfg_rem_user(%d)"), rc);
560 		break;
561 	}
562 
563 	return (retval);
564 }
565 
566 /*
567  * cfg_load_dsvols
568  *
569  * Description:
570  *	Loads the dsvol section of the config file into a giant hash, to
571  *	make searching faster.  The important bit to remember is to not
572  *	release the write lock between calling cfg_load_dsvols() and the
573  *	cfg_*_user() functions.
574  *
575  * Assumptions:
576  *	1/ cfg file is open
577  *	2/ cfg file has been write-locked
578  *	3/ user of this routine may already be using hcreate/hsearch
579  *
580  * Return value:
581  *	-1 if error, or total number of sets found
582  */
583 int
584 cfg_load_dsvols(CFGFILE *cfg)
585 {
586 	int set, rc, entries;
587 	char search_key[ CFG_MAX_KEY ];
588 	char *buf;
589 	char **entry, *path, *cnode, *users;
590 	hash_data_t *data;
591 	int devs_added = 0;
592 	int offset = 0;
593 	char *ctag = cfg_get_resource(cfg);
594 	if (!ctag || *ctag == '\0') {
595 		ctag = "-";
596 	}
597 
598 	dsvol = nsc_create_hash();
599 	if (!dsvol) {
600 		return (-1);
601 	}
602 
603 	rc = 0;
604 	cfg_rewind(cfg, CFG_SEC_CONF);
605 	entries = cfg_get_section(cfg, &entry, "dsvol");
606 	for (set = 1; set <= entries; set++) {
607 		buf = entry[set - 1];
608 
609 		/* split up the line */
610 		if (!(path = strtok(buf, " "))) {
611 			/* oops, now what? */
612 			free(buf);
613 			break;
614 		}
615 		if (!(cnode = strtok(0, " "))) {
616 			free(buf);
617 			break;
618 		}
619 		if (ctag && (strcmp(cnode, ctag) != 0)) {
620 			++offset;
621 			free(buf);
622 			continue;
623 		}
624 
625 		if (!(users = strtok(0, " "))) {
626 			free(buf);
627 			break;
628 		}
629 
630 		data = make_dsvol_data(path, cnode, users, set - offset);
631 		if (!data) {
632 			free(buf);
633 			break;
634 		}
635 		(void) snprintf(search_key, CFG_MAX_KEY, "%s:%s", path, cnode);
636 		rc = nsc_insert_node(dsvol, data, search_key);
637 		if (rc < 0) {
638 			free(buf);
639 			break;
640 		}
641 
642 		/* we also need to keep track of node information */
643 		rc = add_dev_entry(path);
644 		if (rc < 0) {
645 			free(buf);
646 			break;
647 		} else if (rc)
648 			++devs_added;
649 
650 		free(buf);
651 		rc = 0;
652 	}
653 
654 	while (set < entries)
655 		free(entry[set++]);
656 	if (entries)
657 		free(entry);
658 
659 	if (devs_added) {
660 		qsort(devlist, devcount, sizeof (device_t), compare);
661 		rebuild_devhash();
662 	}
663 
664 	dsvol_loaded = 1;
665 	return (rc < 0? rc : entries);
666 }
667 
668 /*
669  * cfg_unload_dsvols
670  *
671  * Description:
672  *	Free all memory allocated with cfg_load_dsvols.
673  */
674 void
675 cfg_unload_dsvols()
676 {
677 	if (dsvol) {
678 		nsc_remove_all(dsvol, delete_dsvol_data);
679 		dsvol = 0;
680 		dsvol_loaded = 0;
681 	}
682 }
683 
684 /*
685  * cfg_load_svols
686  *
687  * Description:
688  *	Loads the sv section of the config file into a giant hash, to make
689  *	searching faster.  The important bit to remember is to not release
690  *	the write lock between calling cfg_load_svols() and the cfg_*_user()
691  *	functions.
692  *
693  * Assumptions:
694  *	1/ cfg file is open
695  *	2/ cfg file has been write-locked
696  *	3/ user of this routine may already be using builtin hcreate/hsearch
697  */
698 int
699 cfg_load_svols(CFGFILE *cfg)
700 {
701 	int set, entries, offset = 0;
702 	char *buf, **entry;
703 	char *path, *mode, *cnode;
704 	hash_data_t *data;
705 	char *ctag = cfg_get_resource(cfg);
706 	if (!ctag || *ctag == '\0') {
707 		ctag = "-";
708 	}
709 
710 	svol = nsc_create_hash();
711 	if (!svol) {
712 		return (-1);
713 	}
714 
715 	cfg_rewind(cfg, CFG_SEC_CONF);
716 	entries = cfg_get_section(cfg, &entry, "sv");
717 	for (set = 1; set <= entries; set++) {
718 		buf = entry[set - 1];
719 
720 		/* split up the line */
721 		if (!(path = strtok(buf, " "))) {
722 			free(buf);
723 			break;
724 		}
725 		if (!(mode = strtok(0, " "))) {
726 			free(buf);
727 			break;
728 		}
729 		if (!(cnode = strtok(0, " "))) {
730 			cnode = "";
731 		}
732 
733 		if (ctag && (strcmp(cnode, ctag) != 0)) {
734 			++offset;
735 			free(buf);
736 			continue;
737 		}
738 
739 		data = make_svol_data(path, mode, cnode, set - offset);
740 		if (!data) {
741 			free(buf);
742 			break;
743 		}
744 		if (nsc_insert_node(svol, data, path) < 0) {
745 			free(buf);
746 			break;
747 		}
748 		free(buf);
749 	}
750 	while (set < entries)
751 		free(entry[set++]);
752 	if (entries)
753 		free(entry);
754 
755 	svol_loaded = 1;
756 	return (0);
757 }
758 
759 /*
760  * cfg_unload_svols
761  *
762  * Description:
763  *	Frees all memory allocated with cfg_load_dsvols
764  */
765 void
766 cfg_unload_svols()
767 {
768 	if (svol) {
769 		nsc_remove_all(svol, delete_svol_data);
770 		svol = 0;
771 		svol_loaded = 0;
772 	}
773 }
774 
775 /*
776  * cfg_get_canonical_name
777  *
778  * Description:
779  *	Find out whether a device is already known by another name in
780  *	the config file.
781  *
782  * Parameters:
783  *	cfg - The config file to use
784  *	path - The pathname of the device
785  *	result - (output) The name it is otherwise known as.  This parameter
786  *			must be freed by the caller.
787  *
788  * Return values:
789  *	-1: error
790  *	0: name is as expected, or is not known
791  *	1: Name is known by different name (stored in 'result')
792  */
793 int
794 cfg_get_canonical_name(CFGFILE *cfg, const char *path, char **result)
795 {
796 	int self_loaded;
797 	char *alt_path;
798 	int retval;
799 
800 	if (devlist) {
801 		self_loaded = 0;
802 	} else {
803 		if (cfg_load_shadows(cfg) < 0) {
804 			return (-1);
805 		}
806 		self_loaded = 1;
807 	}
808 
809 	/* see if it exists under a different name */
810 	alt_path = find_devid(path);
811 	if (!alt_path || strcmp(path, alt_path) == 0) {
812 		*result = NULL;
813 		retval = 0;
814 	} else {
815 		/* a-ha */
816 		*result = strdup(alt_path);
817 		retval = 1;
818 	}
819 
820 	if (self_loaded) {
821 		free_dev_entries();
822 	}
823 
824 	return (retval);
825 }
826 
827 /*
828  * cfg_load_shadows
829  *
830  * Description:
831  *	Load in shadow and bitmap volumes from the II section of the
832  *	config file.  SNDR's volumes are handled already by cfg_load_dsvols.
833  *	Not all shadow volumes are listed under dsvol: they can be exported.
834  *
835  * Parameters:
836  *	cfg - The config file to use
837  *
838  * Return values:
839  *	-1: error
840  *	0: success
841  */
842 int
843 cfg_load_shadows(CFGFILE *cfg)
844 {
845 	int set, self_loaded, rc, entries;
846 	char *buf, **entry, *ptr;
847 	int devs_added = 0;
848 
849 	if (dsvol_loaded) {
850 		self_loaded = 0;
851 	} else {
852 		if (cfg_load_dsvols(cfg) < 0) {
853 			return (-1);
854 		}
855 		self_loaded = 1;
856 	}
857 
858 	shadowvol = nsc_create_hash();
859 	if (!shadowvol) {
860 		return (-1);
861 	}
862 
863 	rc = 0;
864 	cfg_rewind(cfg, CFG_SEC_CONF);
865 	entries = cfg_get_section(cfg, &entry, "ii");
866 	for (set = 1; set <= entries; set++) {
867 		buf = entry[set - 1];
868 
869 		/* skip the master vol */
870 		ptr = strtok(buf, " ");
871 
872 		/* shadow is next */
873 		ptr = strtok(NULL, " ");
874 
875 		rc = add_dev_entry(ptr);
876 		if (rc < 0) {
877 			free(buf);
878 			break;
879 		} else if (rc)
880 			++devs_added;
881 
882 		/* and next is bitmap */
883 		ptr = strtok(NULL, " ");
884 
885 		rc = add_dev_entry(ptr);
886 		if (rc < 0) {
887 			free(buf);
888 			break;
889 		} else if (rc)
890 			++devs_added;
891 		rc = 0;
892 		free(buf);
893 	}
894 	while (set < entries)
895 		free(entry[set++]);
896 	if (entries)
897 		free(entry);
898 
899 	if (self_loaded) {
900 		cfg_unload_dsvols();
901 	}
902 
903 	if (devs_added) {
904 		/* sort it, in preparation for lookups */
905 		qsort(devlist, devcount, sizeof (device_t), compare);
906 		rebuild_devhash();
907 	}
908 
909 	return (rc);
910 }
911 
912 void
913 cfg_unload_shadows()
914 {
915 	/* do nothing */
916 }
917 
918 /* ---------------------------------------------------------------------- */
919 
920 static hash_data_t *
921 make_dsvol_data(char *path, char *cnode, char *users, int set)
922 {
923 	hash_data_t *data;
924 
925 	data = (hash_data_t *)malloc(sizeof (hash_data_t));
926 	if (!data) {
927 		return (0);
928 	}
929 
930 	data->u.users = strdup(users);
931 	data->path = strdup(path);
932 	data->node = strdup(cnode);
933 	data->setno = set;
934 
935 	return (data);
936 }
937 
938 static void
939 delete_dsvol_data(void *data)
940 {
941 	hash_data_t *p = (hash_data_t *)data;
942 
943 	free(p->u.users);
944 	free(p->path);
945 	free(p->node);
946 	free(p);
947 }
948 
949 static hash_data_t *
950 make_svol_data(char *path, char *mode, char *cnode, int set)
951 {
952 	hash_data_t *data;
953 
954 	data = (hash_data_t *)malloc(sizeof (hash_data_t));
955 	if (!data) {
956 		return (0);
957 	}
958 
959 	data->u.mode = strdup(mode);
960 	data->path = strdup(path);
961 	data->node = strdup(cnode);
962 	data->setno = set;
963 
964 	return (data);
965 }
966 
967 
968 static void
969 delete_svol_data(void *data)
970 {
971 	hash_data_t *p = (hash_data_t *)data;
972 
973 	free(p->u.mode);
974 	free(p->path);
975 	free(p->node);
976 	free(p);
977 }
978 
979 static int
980 sv_action(char *path, CFGFILE *caller_cfg, char *ctag, int enable)
981 {
982 	struct stat stb;
983 	sv_conf_t svc;
984 	int fd = -1;
985 	int cfg_changed = 0;
986 	CFGFILE *cfg;
987 	int print_log = 0;
988 	int err = 0, rc;
989 	int sv_ioctl, spcs_err, self_loaded;
990 	char *log_str1, *log_str2;
991 	char key[ CFG_MAX_KEY ];
992 	char buf[ CFG_MAX_BUF ];
993 	hash_data_t *node;
994 	device_t *statinfo = 0;
995 
996 	if (caller_cfg == NULL) {
997 		cfg = cfg_open(NULL);
998 		if (cfg == NULL)
999 			return (-1);
1000 
1001 		if (ctag)
1002 			cfg_resource(cfg, ctag);
1003 	} else
1004 		cfg = caller_cfg;
1005 
1006 
1007 	self_loaded = 0;
1008 	sv_ioctl = (enable? SVIOC_ENABLE : SVIOC_DISABLE);
1009 	log_str1 = (enable? gettext("enabled %s") : gettext("disabled %s"));
1010 	log_str2 = (enable? gettext("unable to enable %s") :
1011 	    gettext("unable to disable %s"));
1012 	spcs_err = (enable? SV_EENABLED : SV_EDISABLED);
1013 	bzero(&svc, sizeof (svc));
1014 
1015 	if (devhash)
1016 		statinfo = nsc_lookup(devhash, path);
1017 
1018 	if (statinfo) {
1019 		if (!S_ISCHR(statinfo->mode))
1020 			goto error;
1021 		svc.svc_major = major(statinfo->rdev);
1022 		svc.svc_minor = minor(statinfo->rdev);
1023 	} else {
1024 		if (stat(path, &stb) != 0)
1025 			goto error;
1026 
1027 		if (!S_ISCHR(stb.st_mode))
1028 			goto error;
1029 		svc.svc_major = major(stb.st_rdev);
1030 		svc.svc_minor = minor(stb.st_rdev);
1031 	}
1032 
1033 	strncpy(svc.svc_path, path, sizeof (svc.svc_path));
1034 
1035 	fd = open(SV_DEVICE, O_RDONLY);
1036 	if (fd < 0)
1037 		goto error;
1038 
1039 	svc.svc_flag = (NSC_DEVICE | NSC_CACHE);
1040 	svc.svc_error = spcs_s_ucreate();
1041 
1042 	do {
1043 		rc = ioctl(fd, sv_ioctl, &svc);
1044 	} while (rc < 0 && errno == EINTR);
1045 
1046 	if (rc < 0) {
1047 		if (errno != spcs_err) {
1048 			spcs_log("sv", &svc.svc_error, log_str2, svc.svc_path);
1049 			if (enable)
1050 				goto error;
1051 			else
1052 				err = errno;
1053 		} else
1054 			err = spcs_err;
1055 	}
1056 
1057 	spcs_log("sv", NULL, log_str1, svc.svc_path);
1058 
1059 	/* SV enable succeeded */
1060 	if (caller_cfg == NULL)	 /* was not previously locked */
1061 		if (!cfg_lock(cfg, CFG_WRLOCK))
1062 			goto error;
1063 
1064 	if (err != spcs_err) { /* already enabled, already in config */
1065 		if (enable) {
1066 			cfg_rewind(cfg, CFG_SEC_CONF);
1067 			(void) snprintf(buf, CFG_MAX_BUF, "%s - %s", path,
1068 			    ctag? ctag : "-");
1069 			if (cfg_put_cstring(cfg, "sv", buf, CFG_MAX_BUF) < 0) {
1070 				/* SV config not updated, so SV disable again */
1071 				(void) ioctl(fd, SVIOC_DISABLE, &svc);
1072 				print_log++;
1073 			} else
1074 				cfg_changed = 1;
1075 		} else {
1076 			/* pull it out of the config */
1077 			if (!svol_loaded) {
1078 				if (cfg_load_svols(cfg) < 0) {
1079 					if (NULL == caller_cfg) {
1080 						cfg_close(cfg);
1081 					}
1082 					return (-1);
1083 				}
1084 				self_loaded = 1;
1085 			}
1086 			node = nsc_lookup(svol, svc.svc_path);
1087 			if (node) {
1088 				cfg_rewind(cfg, CFG_SEC_CONF);
1089 				(void) snprintf(key, CFG_MAX_KEY, "sv.set%d",
1090 				    node->setno);
1091 				if (cfg_put_cstring(cfg, key, NULL, NULL) < 0) {
1092 					spcs_log("sv", NULL,
1093 					    gettext("failed to remove %s from "
1094 					    "sv config"), svc.svc_path);
1095 				}
1096 				/*
1097 				 * Since we deleted an entry from the config
1098 				 * file, we don't know what all the new
1099 				 * set numbers are.  We need to reload
1100 				 * everything
1101 				 */
1102 				if (!self_loaded) {
1103 					cfg_unload_svols();
1104 					if (cfg_load_svols(cfg) < 0) {
1105 						if (NULL == caller_cfg) {
1106 							cfg_close(cfg);
1107 						}
1108 						return (-1);
1109 					}
1110 				}
1111 				cfg_changed = 1;
1112 			}
1113 			if (self_loaded) {
1114 				cfg_unload_svols();
1115 				self_loaded = 0;
1116 			}
1117 		}
1118 	}
1119 
1120 #ifdef lint
1121 	(void) printf("extra line to shut lint up %s\n", module_names[0]);
1122 #endif
1123 
1124 error:
1125 	if (fd >= 0)
1126 		(void) close(fd);
1127 
1128 	if (cfg == NULL)
1129 		return (-1);
1130 
1131 	if (cfg_changed)
1132 		if (caller_cfg == NULL) /* we opened config */
1133 			(void) cfg_commit(cfg);
1134 
1135 	if (caller_cfg == NULL)
1136 		cfg_close(cfg);
1137 	if ((cfg_changed) || (err == spcs_err))
1138 		return (1);
1139 	if (print_log)
1140 		spcs_log("sv", NULL,
1141 			gettext("unable to add to configuration, disabled %s"),
1142 			svc.svc_path);
1143 	spcs_s_ufree(&svc.svc_error);
1144 
1145 	return (-1);
1146 }
1147 
1148 /*
1149  * add_dev_entry
1150  *
1151  * Add an entry into the devlist and the devhash for future lookups.
1152  *
1153  * Return values:
1154  *  -1  An error occurred.
1155  *   0  Entry added
1156  *   1  Entry already exists.
1157  */
1158 static int
1159 add_dev_entry(const char *path)
1160 {
1161 	struct stat buf;
1162 	device_t *newmem;
1163 	hash_data_t *data;
1164 
1165 	if (!devhash) {
1166 		devhash = nsc_create_hash();
1167 		if (!devhash) {
1168 			return (-1);
1169 		}
1170 	} else {
1171 		data = nsc_lookup(devhash, path);
1172 		if (data) {
1173 			return (1);
1174 		}
1175 	}
1176 
1177 	if (stat(path, &buf) < 0) {
1178 		/* ignore error, we are most likely deleting entry anyway */
1179 		buf.st_rdev = 0;
1180 	}
1181 
1182 	if (devcount >= devalloc) {
1183 		/* make some room */
1184 		devalloc += DEV_EXPAND;
1185 		newmem = (device_t *)realloc(devlist, devalloc *
1186 		    sizeof (device_t));
1187 		if (!newmem) {
1188 			free_dev_entries();
1189 			return (-1);
1190 		} else {
1191 			devlist = newmem;
1192 		}
1193 	}
1194 
1195 	devlist[ devcount ].path = strdup(path);
1196 	devlist[ devcount ].rdev = buf.st_rdev;
1197 	devlist[ devcount ].mode = buf.st_mode;
1198 
1199 	if (nsc_insert_node(devhash, &devlist[devcount], path) < 0) {
1200 		return (-1);
1201 	}
1202 
1203 	++devcount;
1204 	return (0);
1205 }
1206 
1207 static void
1208 rebuild_devhash()
1209 {
1210 	int i;
1211 
1212 	if (!devhash)
1213 		nsc_remove_all(devhash, 0);
1214 
1215 	devhash = nsc_create_hash();
1216 	if (!devhash)
1217 		return;
1218 
1219 	for (i = 0; i < devcount; i++) {
1220 		nsc_insert_node(devhash, &devlist[i], devlist[i].path);
1221 	}
1222 }
1223 
1224 static int
1225 compare(const void *va, const void *vb)
1226 {
1227 	device_t *a = (device_t *)va;
1228 	device_t *b = (device_t *)vb;
1229 
1230 	return (b->rdev - a->rdev);
1231 }
1232 
1233 static char *
1234 find_devid(const char *path)
1235 {
1236 	device_t key;
1237 	device_t *result;
1238 	struct stat buf;
1239 
1240 	if (!devlist || !devhash)
1241 		return (NULL);
1242 
1243 	/* See if we already know the device id by this name */
1244 	result = (device_t *)nsc_lookup(devhash, path);
1245 	if (result) {
1246 		return (NULL);
1247 	}
1248 
1249 	/* try to find it by another name */
1250 	if (stat(path, &buf) < 0)
1251 		return (NULL);
1252 
1253 	key.rdev = buf.st_rdev;
1254 
1255 	/* it's storted, so we use the binary-chop method to find it */
1256 	result = bsearch(&key, devlist, devcount, sizeof (device_t), compare);
1257 
1258 	if (result) {
1259 		return (result->path);
1260 	}
1261 
1262 	return (NULL);
1263 }
1264 
1265 static void
1266 free_dev_entries()
1267 {
1268 	int i;
1269 	device_t *p;
1270 
1271 	if (!devlist) {
1272 		return;
1273 	}
1274 	for (i = 0, p = devlist; i < devcount; i++, p++) {
1275 		free(p->path);
1276 	}
1277 	free(devlist);
1278 	devlist = NULL;
1279 	devcount = 0;
1280 	devalloc = 0;
1281 
1282 	if (devhash) {
1283 		nsc_remove_all(devhash, 0);
1284 		devhash = NULL;
1285 	}
1286 }
1287