xref: /titanic_52/usr/src/cmd/avs/sv/svadm.c (revision e5803b76927480e8f9b67b22201c484ccf4c2bcf)
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 <sys/stat.h>
28 #include <sys/mkdev.h>
29 #include <sys/param.h>
30 #include <sys/wait.h>
31 #include <fcntl.h>
32 #include <stdarg.h>
33 #include <stdlib.h>
34 #include <strings.h>
35 #include <errno.h>
36 #include <stdio.h>
37 #include <locale.h>
38 #include <unistd.h>
39 #include <search.h>
40 #include <libgen.h>
41 #include <nsctl.h>
42 
43 #include <sys/unistat/spcs_s.h>
44 #include <sys/unistat/spcs_s_u.h>
45 #include <sys/unistat/spcs_errors.h>
46 
47 #include <sys/nsctl/sv.h>
48 #include <sys/nsctl/sv_impl.h>
49 
50 #include <sys/nsctl/cfg.h>
51 #include <sys/nsctl/nsc_hash.h>
52 
53 #include "../sv/svadm.h"
54 
55 
56 static int sv_max_devices;
57 
58 
59 /*
60  * support for the special cluster tag "local" to be used with -C in a
61  * cluster for local volumes.
62  */
63 
64 #define	SV_LOCAL_TAG	"local"
65 
66 static int sv_islocal;
67 
68 /*
69  * libcfg access.
70  */
71 
72 static CFGFILE *cfg;		/* libcfg file pointer */
73 static int cfg_changed;		/* set to 1 if we need to commit changes */
74 
75 static char *cfg_cluster_tag;	/* local cluster tag */
76 
77 static char *implicit_tag;	/* implicit cluster tag */
78 
79 
80 /*
81  * Print width for print_sv() output.
82  */
83 
84 #define	STATWIDTH	(SV_MAXPATH / 2)
85 
86 /*
87  * Pathnames.
88  */
89 
90 static const caddr_t sv_rpath = SV_DEVICE;
91 
92 /*
93  * Functions.
94  */
95 
96 static int read_config_file(const caddr_t, sv_name_t []);
97 static int enable_dev(sv_name_t *);
98 static int disable_dev(const caddr_t);
99 static void error(spcs_s_info_t *, caddr_t, ...);
100 static void create_cfg_hash();
101 static int find_in_hash(char *path);
102 static void destroy_hashtable();
103 static void remove_from_cfgfile(char *path, int setnumber);
104 
105 static caddr_t program;
106 
107 static void
108 sv_cfg_open(CFGLOCK mode)
109 {
110 	if (cfg != NULL)
111 		return;
112 
113 	cfg = cfg_open(NULL);
114 	if (cfg == NULL) {
115 		error(NULL, gettext("unable to access the configuration"));
116 		/* NOTREACHED */
117 	}
118 
119 	if (cfg_cluster_tag && *cfg_cluster_tag) {
120 		cfg_resource(cfg, cfg_cluster_tag);
121 	} else {
122 		cfg_resource(cfg, NULL);
123 	}
124 	if (!cfg_lock(cfg, mode)) {
125 		error(NULL, gettext("unable to lock the configuration"));
126 		/* NOTREACHED */
127 	}
128 }
129 
130 
131 static void
132 sv_cfg_close(void)
133 {
134 	if (cfg == NULL)
135 		return;
136 
137 	if (cfg_changed) {
138 		(void) cfg_commit(cfg);
139 		cfg_changed = 0;
140 	}
141 
142 	cfg_close(cfg);
143 	cfg = NULL;
144 }
145 
146 
147 
148 static void
149 usage(void)
150 {
151 	(void) fprintf(stderr, gettext("usage:\n"));
152 
153 	(void) fprintf(stderr, gettext(
154 	    "\t%s -h                                 help\n"), program);
155 
156 	(void) fprintf(stderr, gettext(
157 	    "\t%s [-C tag]                           display status\n"),
158 	    program);
159 
160 	(void) fprintf(stderr, gettext(
161 	    "\t%s [-C tag] -i                        display "
162 	    "extended status\n"), program);
163 
164 	(void) fprintf(stderr, gettext(
165 	    "\t%s [-C tag] -v                        display "
166 	    "version number\n"), program);
167 
168 	(void) fprintf(stderr, gettext(
169 	    "\t%s [-C tag] -e { -f file | volume }   enable\n"), program);
170 
171 	(void) fprintf(stderr, gettext(
172 	    "\t%s [-C tag] -d { -f file | volume }   disable\n"), program);
173 
174 	(void) fprintf(stderr, gettext(
175 	    "\t%s [-C tag] -r { -f file | volume }   reconfigure\n"), program);
176 
177 	sv_cfg_close();
178 }
179 
180 static void
181 message(caddr_t prefix, spcs_s_info_t *status, caddr_t string, va_list ap)
182 {
183 	(void) fprintf(stderr, "%s: %s: ", program, prefix);
184 	(void) vfprintf(stderr, string, ap);
185 	(void) fprintf(stderr, "\n");
186 
187 	if (status) {
188 		spcs_s_report(*status, stderr);
189 		spcs_s_ufree(status);
190 	}
191 }
192 
193 
194 static void
195 error(spcs_s_info_t *status, caddr_t string, ...)
196 {
197 	va_list ap;
198 	va_start(ap, string);
199 
200 	message(gettext("error"), status, string, ap);
201 
202 	va_end(ap);
203 
204 	sv_cfg_close();
205 	exit(1);
206 }
207 
208 
209 static void
210 warn(spcs_s_info_t *status, caddr_t string, ...)
211 {
212 	va_list ap;
213 	va_start(ap, string);
214 
215 	message(gettext("warning"), status, string, ap);
216 
217 	va_end(ap);
218 }
219 
220 
221 static void
222 sv_get_maxdevs(void)
223 {
224 	sv_name_t svn[1];
225 	sv_list_t svl;
226 	int fd;
227 
228 	if (sv_max_devices > 0)
229 		return;
230 
231 	fd = open(sv_rpath, O_RDONLY);
232 	if (fd < 0)
233 		error(NULL, gettext("unable to open %s: %s"),
234 			sv_rpath, strerror(errno));
235 
236 	bzero(&svl, sizeof (svl));
237 	bzero(&svn[0], sizeof (svn));
238 
239 	svl.svl_names = &svn[0];
240 	svl.svl_error = spcs_s_ucreate();
241 
242 	if (ioctl(fd, SVIOC_LIST, &svl) < 0) {
243 		(void) close(fd);
244 		error(&svl.svl_error, gettext("unable to get max devs"));
245 	}
246 
247 	spcs_s_ufree(&svl.svl_error);
248 	sv_max_devices = svl.svl_maxdevs;
249 
250 	(void) close(fd);
251 }
252 
253 
254 static sv_name_t *
255 sv_alloc_svnames(void)
256 {
257 	sv_name_t *svn = NULL;
258 
259 	sv_get_maxdevs();
260 
261 	svn = calloc(sv_max_devices, sizeof (*svn));
262 	if (svn == NULL) {
263 		error(NULL, "unable to allocate %ld bytes of memory",
264 		    sv_max_devices * sizeof (*svn));
265 	}
266 
267 	return (svn);
268 }
269 
270 
271 static void
272 sv_check_dgislocal(char *dgname)
273 {
274 	char *othernode;
275 	int rc;
276 
277 	/*
278 	 * check where this disk service is mastered
279 	 */
280 
281 	rc = cfg_dgname_islocal(dgname, &othernode);
282 	if (rc < 0) {
283 		error(NULL, gettext("unable to find "
284 		    "disk service, %s: %s"), dgname, strerror(errno));
285 	}
286 
287 	if (rc == 0) {
288 		error(NULL, gettext("disk service, %s, is "
289 		    "active on node \"%s\"\nPlease re-issue "
290 		    "the command on that node"), dgname, othernode);
291 	}
292 }
293 
294 
295 /*
296  * Carry out cluster based checks for a specified volume, or just
297  * global options.
298  */
299 static void
300 sv_check_cluster(char *path)
301 {
302 	char dgname[CFG_MAX_BUF];
303 	static int sv_iscluster = -1;	/* set to 1 if running in a cluster */
304 
305 	/*
306 	 * Find out if we are running in a cluster
307 	 */
308 	if (sv_iscluster == -1) {
309 		if ((sv_iscluster = cfg_iscluster()) < 0) {
310 			error(NULL, gettext("unable to ascertain environment"));
311 		}
312 	}
313 
314 	if (!sv_iscluster && cfg_cluster_tag != NULL) {
315 		error(NULL, gettext("-C is not valid when not in a cluster"));
316 	}
317 
318 	if (!sv_iscluster || sv_islocal || path == NULL) {
319 		return;
320 	}
321 
322 
323 	/*
324 	 * Cluster-only checks on pathname
325 	 */
326 	if (cfg_dgname(path, dgname, sizeof (dgname)) == NULL) {
327 		error(NULL, gettext("unable to determine "
328 		    "disk group name for %s"), path);
329 		return;
330 	}
331 
332 	if (cfg_cluster_tag != NULL) {
333 		/*
334 		 * Do dgislocal check now in case path did not contain
335 		 * a dgname.
336 		 *
337 		 * E.g. adding a /dev/did/ device to a disk service.
338 		 */
339 
340 		sv_check_dgislocal(cfg_cluster_tag);
341 	}
342 
343 	if (strcmp(dgname, "") == 0)
344 		return;		/* NULL dgname is valid */
345 
346 	if (cfg_cluster_tag == NULL) {
347 		/*
348 		 * Implicitly set the cluster tag to dgname
349 		 */
350 
351 		sv_check_dgislocal(dgname);
352 
353 		if (implicit_tag) {
354 			free(implicit_tag);
355 			implicit_tag = NULL;
356 		}
357 
358 		implicit_tag = strdup(dgname);
359 		if (implicit_tag == NULL) {
360 			error(NULL,
361 			    gettext("unable to allocate memory "
362 			    "for cluster tag"));
363 		}
364 	} else {
365 		/*
366 		 * Check dgname and cluster tag from -C are the same.
367 		 */
368 
369 		if (strcmp(dgname, cfg_cluster_tag) != 0) {
370 			error(NULL,
371 			    gettext("-C (%s) does not match disk group "
372 			    "name (%s) for %s"), cfg_cluster_tag,
373 			    dgname, path);
374 		}
375 
376 		/*
377 		 * sv_check_dgislocal(cfg_cluster_tag) was called above.
378 		 */
379 	}
380 }
381 
382 
383 static void
384 print_version(void)
385 {
386 	sv_version_t svv;
387 	int fd;
388 
389 	bzero(&svv, sizeof (svv));
390 	svv.svv_error = spcs_s_ucreate();
391 
392 	fd = open(sv_rpath, O_RDONLY);
393 	if (fd < 0) {
394 		warn(NULL, gettext("unable to open %s: %s"),
395 			sv_rpath, strerror(errno));
396 		return;
397 	}
398 
399 	if (ioctl(fd, SVIOC_VERSION, &svv) != 0) {
400 		error(&svv.svv_error,
401 		    gettext("unable to read the version number"));
402 		/* NOTREACHED */
403 	}
404 
405 	spcs_s_ufree(&svv.svv_error);
406 #ifdef DEBUG
407 	(void) printf(gettext("Storage Volume version %d.%d.%d.%d\n"),
408 	    svv.svv_major_rev, svv.svv_minor_rev,
409 	    svv.svv_micro_rev, svv.svv_baseline_rev);
410 #else
411 	if (svv.svv_micro_rev) {
412 		(void) printf(gettext("Storage Volume version %d.%d.%d\n"),
413 		    svv.svv_major_rev, svv.svv_minor_rev, svv.svv_micro_rev);
414 	} else {
415 		(void) printf(gettext("Storage Volume version %d.%d\n"),
416 		    svv.svv_major_rev, svv.svv_minor_rev);
417 	}
418 #endif
419 
420 	(void) close(fd);
421 }
422 
423 int
424 main(int argc, char *argv[])
425 {
426 	extern int optind;
427 	extern char *optarg;
428 	char *conf_file = NULL;
429 	int enable, disable, compare, print, version;
430 	int opt, Cflag, fflag, iflag;
431 	int rc;
432 
433 	(void) setlocale(LC_ALL, "");
434 	(void) textdomain("svadm");
435 
436 	program = strdup(basename(argv[0]));
437 
438 	Cflag = fflag = iflag = 0;
439 	compare = enable = disable = version = 0;
440 
441 	print = 1;
442 
443 	while ((opt = getopt(argc, argv, "C:def:hirv")) != EOF) {
444 		switch (opt) {
445 
446 		case 'C':
447 			if (Cflag) {
448 				warn(NULL,
449 				    gettext("-C specified multiple times"));
450 				usage();
451 				exit(2);
452 				/* NOTREACHED */
453 			}
454 
455 			Cflag++;
456 			cfg_cluster_tag = optarg;
457 			break;
458 
459 		case 'e':
460 			print = 0;
461 			enable++;
462 			break;
463 
464 		case 'd':
465 			print = 0;
466 			disable++;
467 			break;
468 
469 		case 'f':
470 			fflag++;
471 			conf_file = optarg;
472 			break;
473 
474 		case 'i':
475 			iflag++;
476 			break;
477 
478 		case 'r':
479 			/* Compare running system with sv.cf */
480 			print = 0;
481 			compare++;
482 			break;
483 
484 		case 'v':
485 			print = 0;
486 			version++;
487 			break;
488 
489 		case 'h':
490 			usage();
491 			exit(0);
492 
493 		default:
494 			usage();
495 			exit(2);
496 			/* NOTREACHED */
497 		}
498 	}
499 
500 
501 	/*
502 	 * Usage checks
503 	 */
504 
505 	if ((enable + disable + compare) > 1) {
506 		warn(NULL, gettext("-d, -e and -r are mutually exclusive"));
507 		usage();
508 		exit(2);
509 	}
510 
511 	if (fflag && (print || version)) {
512 		warn(NULL, gettext("-f is only valid with -d, -e or -r"));
513 		usage();
514 		exit(2);
515 	}
516 
517 	if (fflag && optind != argc) {
518 		usage();
519 		exit(2);
520 	}
521 
522 	if (print || version) {
523 		/* check for no more args */
524 
525 		if (optind != argc) {
526 			usage();
527 			exit(2);
528 		}
529 	} else {
530 		/* check for inline args */
531 
532 		if (!fflag && (argc - optind) != 1) {
533 			usage();
534 			exit(2);
535 		}
536 	}
537 
538 	if (!print && iflag) {
539 		usage();
540 		exit(2);
541 	}
542 
543 
544 	/*
545 	 * Check for the special cluster tag and convert into the
546 	 * internal representation.
547 	 */
548 
549 	if (cfg_cluster_tag != NULL &&
550 	    strcmp(cfg_cluster_tag, SV_LOCAL_TAG) == 0) {
551 		cfg_cluster_tag = "-";
552 		sv_islocal = 1;
553 	}
554 
555 
556 	/*
557 	 * Process commands
558 	 */
559 
560 	if (optind != argc) {
561 		/* deal with inline volume argument */
562 
563 		rc = 0;
564 		if (enable)
565 			rc = enable_one_sv(argv[optind]);
566 		else if (disable)
567 			rc = disable_one_sv(argv[optind]);
568 		else /* if (compare) */
569 			compare_one_sv(argv[optind]);
570 
571 		if (rc != 0)
572 			return (1);
573 
574 		return (0);
575 	}
576 
577 	rc = 0;
578 	if (enable)
579 		rc = enable_sv(conf_file);
580 	else if (disable)
581 		rc = disable_sv(conf_file);
582 	else if (compare)
583 		compare_sv(conf_file);
584 	else if (print)
585 		print_sv(iflag);
586 	else /* if (version) */
587 		print_version();
588 
589 	if (rc != 0)
590 		return (1);
591 
592 	return (0);
593 }
594 
595 
596 
597 /* LINT - not static as fwcadm uses it */
598 static int
599 enable_sv(char *conf_file)
600 {
601 	int index;
602 	sv_name_t *svn;
603 	int cnt;
604 	int rc, ret;
605 
606 	svn = sv_alloc_svnames();
607 
608 	index = read_config_file(conf_file, svn);
609 
610 	rc = ret = 0;
611 
612 	for (cnt = 0; cnt < index; cnt++) {
613 
614 		/*
615 		 * Check for more data.
616 		 */
617 		if (svn[cnt].svn_path[0] == '\0') {
618 			/*
619 			 * This was set when reading sv.conf.  After the last
620 			 * line svn_path was set to \0, so we are finished.
621 			 * We shouldn't get here, but put this in just in
622 			 * case.
623 			 */
624 			break;
625 		}
626 		rc = enable_dev(&svn[cnt]);
627 		if (rc && !ret)
628 			ret = rc;
629 	}
630 
631 	sv_cfg_close();
632 
633 	return (ret);
634 }
635 
636 
637 /* LINT - not static as fwcadm uses it */
638 static int
639 enable_one_sv(caddr_t path)
640 {
641 	sv_name_t svn;
642 	int rc;
643 
644 	sv_get_maxdevs();
645 
646 	bzero(&svn, sizeof (svn));
647 	(void) strncpy(svn.svn_path, path, sizeof (svn.svn_path));
648 	svn.svn_mode = (NSC_DEVICE | NSC_CACHE);
649 
650 	/* force NULL termination */
651 	svn.svn_path[sizeof (svn.svn_path) - 1] = '\0';
652 
653 	rc = enable_dev(&svn);
654 	sv_cfg_close();
655 
656 	return (rc);
657 }
658 
659 
660 static int
661 enable_dev(sv_name_t *svn)
662 {
663 	char buf[CFG_MAX_BUF];
664 	struct stat stb;
665 	sv_conf_t svc;
666 	int fd;
667 	int sev;
668 	int rc;
669 	char *lcltag;
670 	char *altname;
671 
672 	sv_check_cluster(svn->svn_path);
673 	sv_cfg_open(CFG_WRLOCK);
674 
675 	bzero(&svc, sizeof (svc));
676 
677 	if (stat(svn->svn_path, &stb) != 0) {
678 		warn(NULL, gettext("unable to access %s: %s"),
679 			svn->svn_path, strerror(errno));
680 		return (1);
681 	}
682 
683 	if (!S_ISCHR(stb.st_mode)) {
684 		warn(NULL, gettext("%s is not a character device - ignored"),
685 		    svn->svn_path);
686 		return (1);
687 	}
688 
689 	svc.svc_major = major(stb.st_rdev);
690 	svc.svc_minor = minor(stb.st_rdev);
691 	(void) strncpy(svc.svc_path, svn->svn_path, sizeof (svc.svc_path));
692 
693 	fd = open(sv_rpath, O_RDONLY);
694 	if (fd < 0) {
695 		warn(NULL, gettext("unable to open %s: %s"),
696 			svn->svn_path, strerror(errno));
697 		return (1);
698 	}
699 
700 	svc.svc_flag = svn->svn_mode;
701 	svc.svc_error = spcs_s_ucreate();
702 
703 	/* first, check for duplicates */
704 	rc = cfg_get_canonical_name(cfg, svn->svn_path, &altname);
705 	if (rc < 0) {
706 		spcs_log("sv", NULL, gettext("Unable to parse config file"));
707 		warn(NULL, gettext("Unable to parse config file"));
708 		(void) close(fd);
709 		return (1);
710 	}
711 	if (rc) {
712 		error(NULL, gettext("'%s' has already been configured as "
713 		    "'%s'.  Re-enter command with the latter name."),
714 		    svn->svn_path, altname);
715 	}
716 
717 	/* secondly, try to insert it into the dsvol config */
718 	if (implicit_tag && *implicit_tag) {
719 		lcltag = implicit_tag;
720 	} else if (cfg_cluster_tag && *cfg_cluster_tag) {
721 		lcltag = cfg_cluster_tag;
722 	} else {
723 		lcltag = "-";
724 	}
725 	rc = cfg_add_user(cfg, svn->svn_path, lcltag, "sv");
726 	if (CFG_USER_ERR == rc) {
727 		spcs_log("sv", NULL,
728 		    gettext("%s: unable to put %s into dsvol cfg"),
729 		    program, svn->svn_path);
730 		warn(NULL, gettext("unable to put %s into dsvol cfg"),
731 		    svn->svn_path);
732 		(void) close(fd);
733 		return (1);
734 	}
735 	cfg_changed = 1;
736 
737 	if (CFG_USER_OK == rc) {
738 		/* success */
739 		(void) close(fd);
740 		return (0);
741 	}
742 
743 	if (ioctl(fd, SVIOC_ENABLE, &svc) < 0) {
744 		if ((CFG_USER_REPEAT == rc) && (SV_EENABLED == errno)) {
745 			/* it's ok -- we were just double-checking */
746 			(void) close(fd);
747 			return (0);
748 		}
749 
750 		spcs_log("sv", &svc.svc_error,
751 		    gettext("%s: unable to enable %s"),
752 		    program, svn->svn_path);
753 
754 		warn(&svc.svc_error, gettext("unable to enable %s"),
755 			svn->svn_path);
756 
757 		/* remove it from dsvol, if we're the ones who put it in */
758 		if (CFG_USER_FIRST == rc) {
759 			(void) cfg_rem_user(cfg, svn->svn_path, lcltag, "sv");
760 		}
761 		(void) close(fd);
762 		return (1);
763 	}
764 
765 	spcs_log("sv", NULL, gettext("%s: enabled %s"),
766 	    program, svn->svn_path);
767 
768 	if (implicit_tag != NULL) {
769 #ifdef DEBUG
770 		if (cfg_cluster_tag != NULL) {
771 			error(NULL,
772 			    gettext("enable_dev: -C %s AND implicit_tag %s!"),
773 			    cfg_cluster_tag, implicit_tag);
774 		}
775 #endif
776 
777 		(void) snprintf(buf, sizeof (buf), "%s - %s",
778 		    svc.svc_path, implicit_tag);
779 	} else {
780 		(void) strcpy(buf, svc.svc_path);
781 	}
782 
783 	rc = 0;
784 	if (cfg_put_cstring(cfg, "sv", buf, sizeof (buf)) < 0) {
785 		warn(NULL,
786 		    gettext("unable to add %s to configuration storage: %s"),
787 		    svc.svc_path, cfg_error(&sev));
788 		rc = 1;
789 	}
790 
791 	cfg_changed = 1;
792 	spcs_s_ufree(&svc.svc_error);
793 	(void) close(fd);
794 
795 	return (rc);
796 }
797 
798 
799 /*
800  * This routine parses the config file passed in via conf_file and
801  * stores the data in the svn array.  The return value is the number
802  * of entries read from conf_file.  If an error occurs the error()
803  * routine is called (which exits the program).
804  */
805 static int
806 read_config_file(const caddr_t conf_file, sv_name_t svn[])
807 {
808 	char line[1024], rdev[1024], junk[1024];
809 	struct stat stb;
810 	int lineno;
811 	int cnt, i;
812 	int index = 0;		/* Current location in svn array	*/
813 	sv_name_t *cur_svn;	/* Pointer to svn[index]		*/
814 	FILE *fp;
815 
816 	if (access(conf_file, R_OK) != 0 ||
817 	    stat(conf_file, &stb) != 0 ||
818 	    !S_ISREG(stb.st_mode)) {
819 		error(NULL, gettext("cannot read config file %s"), conf_file);
820 	}
821 
822 	if ((fp = fopen(conf_file, "r")) == NULL) {
823 		error(NULL, gettext("unable to open config file %s: %s"),
824 			conf_file, strerror(errno));
825 	}
826 
827 	lineno = 0;
828 
829 	while (fgets(line, sizeof (line), fp) != NULL) {
830 		lineno++;
831 
832 		i = strlen(line);
833 
834 		if (i < 1)
835 			continue;
836 
837 		if (line[i-1] == '\n')
838 			line[i-1] = '\0';
839 		else if (i == (sizeof (line) - 1)) {
840 			warn(NULL, gettext(
841 		"line %d: line too long -- should be less than %d characters"),
842 				lineno, (sizeof (line) - 1));
843 			warn(NULL, gettext("line %d: ignored"), lineno);
844 		}
845 
846 		/*
847 		 * check for comment line.
848 		 */
849 		if (line[0] == '#')
850 			continue;
851 
852 		cnt = sscanf(line, "%s %s", rdev, junk);
853 
854 		if (cnt != 1 && cnt != 2) {
855 			if (cnt > 0) {
856 				warn(NULL, gettext("line %d: invalid format"),
857 					lineno);
858 				warn(NULL, gettext("line %d: ignored"), lineno);
859 			}
860 			continue;
861 		}
862 
863 		rdev[sizeof (rdev) - 1] = '\0';
864 
865 		cur_svn = &svn[index];  /* For easier reading below */
866 
867 		if (strlen(rdev) >= sizeof (cur_svn->svn_path)) {
868 			warn(NULL, gettext(
869 		"line %d: raw device name (%s) longer than %d characters"),
870 				lineno, rdev,
871 				(sizeof (cur_svn->svn_path) - 1));
872 			warn(NULL, gettext("line %d: ignored"), lineno);
873 			continue;
874 		}
875 
876 		(void) strcpy(cur_svn->svn_path, rdev);
877 		cur_svn->svn_mode = (NSC_DEVICE | NSC_CACHE);
878 
879 		index++;
880 	}
881 
882 	/* Set the last path to NULL */
883 	svn[index].svn_path[0] = '\0';
884 
885 	(void) fclose(fp);
886 
887 	return (index);
888 }
889 
890 
891 /*
892  * Disable the device from the kernel configuration.
893  *
894  * RETURN:
895  *   0 on success
896  *   non-zero on failure.
897  *
898  * Failures are reported to the user.
899  */
900 static int
901 disable_dev(const caddr_t path)
902 {
903 	struct stat stb;
904 	sv_conf_t svc;
905 	int fd;
906 
907 	sv_check_cluster(path);
908 
909 	if (stat(path, &stb) < 0) {
910 		svc.svc_major = (major_t)-1;
911 		svc.svc_minor = (minor_t)-1;
912 	} else {
913 		svc.svc_major = major(stb.st_rdev);
914 		svc.svc_minor = minor(stb.st_rdev);
915 	}
916 
917 	if ((fd = open(sv_rpath, O_RDONLY)) < 0) {
918 		warn(NULL, gettext("unable to open %s: %s"),
919 			sv_rpath, strerror(errno));
920 		return (-1);
921 	}
922 
923 	(void) strcpy(svc.svc_path, path);
924 	svc.svc_error = spcs_s_ucreate();
925 
926 	/*
927 	 * Issue the ioctl to attempt to disable this device.  Note that all
928 	 * the libdscfg details are handled elsewhere.
929 	 */
930 	if (ioctl(fd, SVIOC_DISABLE, &svc) < 0) {
931 		if (errno != SV_EDISABLED) {
932 			spcs_log("sv", &svc.svc_error,
933 					gettext("%s: unable to disable %s"),
934 					program, path);
935 
936 			warn(&svc.svc_error,
937 					gettext("unable to disable %s"), path);
938 			(void) close(fd);
939 			return (-1);
940 		}
941 	}
942 
943 	spcs_log("sv", NULL, gettext("%s: disabled %s"), program, path);
944 
945 	spcs_s_ufree(&svc.svc_error);
946 	(void) close(fd);
947 
948 	return (0);
949 }
950 
951 
952 static void
953 print_cluster_tag(const int setnumber)
954 {
955 	char buf[CFG_MAX_BUF];
956 	char key[CFG_MAX_KEY];
957 
958 	bzero(buf, sizeof (buf));
959 	(void) snprintf(key, sizeof (key), "sv.set%d.cnode", setnumber);
960 
961 	(void) cfg_get_cstring(cfg, key, buf, sizeof (buf));
962 
963 	if (*buf != '\0') {
964 		if (strcmp(buf, "-") == 0) {
965 			(void) printf(" [%s]", gettext("local to node"));
966 		} else {
967 			(void) printf(" [%s: %s]", gettext("cluster"), buf);
968 		}
969 	}
970 }
971 
972 
973 /* LINT - not static as fwcadm uses it */
974 static void
975 print_sv(int verbose)
976 {
977 	sv_name_t *svn, *svn_system;	/* Devices in system */
978 	sv_list_t svl_system;
979 	int fd, i;
980 	int setnumber;
981 
982 	sv_check_cluster(NULL);
983 	sv_cfg_open(CFG_RDLOCK);
984 
985 	svn_system = sv_alloc_svnames();
986 
987 	if ((fd = open(sv_rpath, O_RDONLY)) < 0) {
988 		(void) printf(gettext("unable to open %s: %s"),
989 			sv_rpath, strerror(errno));
990 		return;
991 	}
992 
993 	/* Grab the system list from the driver */
994 	svl_system.svl_count = sv_max_devices;
995 	svl_system.svl_names = &svn_system[0];
996 	svl_system.svl_error = spcs_s_ucreate();
997 
998 	if (ioctl(fd, SVIOC_LIST, &svl_system) < 0) {
999 		error(&svl_system.svl_error, gettext("unable to get list"));
1000 	}
1001 
1002 	spcs_s_ufree(&svl_system.svl_error);
1003 	(void) close(fd);
1004 
1005 	/*
1006 	 * We build a hashmap out of the entries from the config file to make
1007 	 * searching faster. We end up taking a performance hit when the # of
1008 	 * volumes is small, but for larger configurations it's a
1009 	 * HUGE improvement.
1010 	 */
1011 
1012 	/* build the hashtable */
1013 	cfg_rewind(cfg, CFG_SEC_CONF);
1014 	create_cfg_hash();
1015 
1016 	/*
1017 	 * For each volume found from the kernel, print out
1018 	 * info about it from the kernel.
1019 	 */
1020 	for (i = 0; i < svl_system.svl_count; i++) {
1021 		if (*svn_system[i].svn_path == '\0') {
1022 			break;
1023 		}
1024 
1025 		svn = &svn_system[i];
1026 		if (svn->svn_mode == 0) {
1027 #ifdef DEBUG
1028 			(void) printf(gettext("%s [kernel guard]\n"),
1029 			    svn->svn_path);
1030 #endif
1031 			continue;
1032 		}
1033 		/* get sv entry from the hashtable */
1034 		if ((setnumber = find_in_hash(svn->svn_path)) != -1) {
1035 			(void) printf("%-*s", STATWIDTH, svn->svn_path);
1036 
1037 			if (verbose) {
1038 				print_cluster_tag(setnumber);
1039 			}
1040 
1041 			(void) printf("\n");
1042 
1043 		} else {
1044 			/*
1045 			 * We didn't find the entry in the hashtable.  Let
1046 			 * the user know that the persistent storage is
1047 			 * inconsistent with the kernel configuration.
1048 			 */
1049 			if (cfg_cluster_tag == NULL)
1050 				warn(NULL, gettext(
1051 					"%s is configured, but not in the "
1052 					"config storage"), svn->svn_path);
1053 		}
1054 	}
1055 
1056 	/* free up the hashtable */
1057 	destroy_hashtable();
1058 
1059 	sv_cfg_close();
1060 }
1061 
1062 
1063 /* LINT - not static as fwcadm uses it */
1064 static int
1065 disable_sv(char *conf_file)
1066 {
1067 	sv_name_t *svn, *svn_system;	/* Devices in system */
1068 	sv_list_t svl_system;
1069 	int fd, i, setnumber;
1070 	int rc, ret;
1071 
1072 	svn_system = sv_alloc_svnames();
1073 
1074 	rc = ret = 0;
1075 
1076 	if (conf_file == NULL) {
1077 		if ((fd = open(sv_rpath, O_RDONLY)) < 0) {
1078 			(void) printf(gettext("unable to open %s: %s"),
1079 				sv_rpath, strerror(errno));
1080 			return (1);
1081 		}
1082 
1083 		/* Grab the system list from the driver */
1084 		svl_system.svl_count = sv_max_devices;
1085 		svl_system.svl_names = &svn_system[0];
1086 		svl_system.svl_error = spcs_s_ucreate();
1087 
1088 		if (ioctl(fd, SVIOC_LIST, &svl_system) < 0) {
1089 			error(&(svl_system.svl_error),
1090 					gettext("unable to get list"));
1091 		}
1092 
1093 		spcs_s_ufree(&(svl_system.svl_error));
1094 		(void) close(fd);
1095 	} else {
1096 		svl_system.svl_count = read_config_file(conf_file, svn_system);
1097 	}
1098 
1099 
1100 	for (i = 0; i < svl_system.svl_count; i++) {
1101 		if (*svn_system[i].svn_path == '\0')
1102 			break;
1103 
1104 		svn = &svn_system[i];
1105 
1106 		sv_check_cluster(svn->svn_path);
1107 		sv_cfg_open(CFG_WRLOCK);
1108 		create_cfg_hash();
1109 		rc = 0;
1110 		if ((setnumber = find_in_hash(svn->svn_path)) != -1) {
1111 			if ((rc = disable_dev(svn->svn_path)) != -1) {
1112 				remove_from_cfgfile(svn->svn_path, setnumber);
1113 			} else if (errno == SV_ENODEV) {
1114 				remove_from_cfgfile(svn->svn_path, setnumber);
1115 			}
1116 		} else {
1117 			/* warn the user that we didn't find it in cfg file */
1118 			warn(NULL, gettext(
1119 				"%s was not found in the config storage"),
1120 				svn->svn_path);
1121 			/* try to disable anyway */
1122 			(void) disable_dev(svn->svn_path);
1123 			rc = 1;
1124 		}
1125 
1126 		sv_cfg_close();
1127 		destroy_hashtable();
1128 
1129 		if (rc && !ret)
1130 			ret = rc;
1131 	}
1132 
1133 	return (ret);
1134 }
1135 
1136 
1137 /* LINT - not static as fwcadm uses it */
1138 static int
1139 disable_one_sv(char *path)
1140 {
1141 	int setnumber;
1142 	int rc;
1143 
1144 	sv_get_maxdevs();
1145 	sv_check_cluster(path);
1146 	sv_cfg_open(CFG_WRLOCK);
1147 
1148 	create_cfg_hash();
1149 	if ((setnumber = find_in_hash(path)) != -1) {
1150 		/* remove from kernel */
1151 		if ((rc = disable_dev(path)) == 0) {
1152 			/* remove the cfgline */
1153 			remove_from_cfgfile(path, setnumber);
1154 		} else if (errno == SV_ENODEV) {
1155 			remove_from_cfgfile(path, setnumber);
1156 		}
1157 	} else {
1158 		/* warn the user that we didn't find it in cfg file */
1159 		warn(NULL,
1160 		    gettext("%s was not found in the config storage"), path);
1161 		/* still attempt to remove */
1162 		(void) disable_dev(path);
1163 		rc = 1;
1164 	}
1165 	destroy_hashtable();
1166 
1167 	sv_cfg_close();
1168 	return (rc);
1169 }
1170 
1171 
1172 static void
1173 compare_tag(char *path)
1174 {
1175 	char buf[CFG_MAX_BUF], vol[CFG_MAX_BUF], cnode[CFG_MAX_BUF];
1176 	char key[CFG_MAX_KEY];
1177 	int found, setnumber, i;
1178 	char *tag;
1179 
1180 	sv_check_cluster(path);
1181 	cfg_resource(cfg, (char *)NULL);	/* reset */
1182 	cfg_rewind(cfg, CFG_SEC_CONF);
1183 
1184 #ifdef DEBUG
1185 	if (cfg_cluster_tag != NULL && implicit_tag != NULL) {
1186 		error(NULL, gettext("compare_tag: -C %s AND implicit_tag %s!"),
1187 		    cfg_cluster_tag, implicit_tag);
1188 	}
1189 #endif
1190 
1191 	if (cfg_cluster_tag != NULL)
1192 		tag = cfg_cluster_tag;
1193 	else if (implicit_tag != NULL)
1194 		tag = implicit_tag;
1195 	else
1196 		tag = "-";
1197 
1198 	found = 0;
1199 	for (i = 0; i < sv_max_devices; i++) {
1200 		setnumber = i + 1;
1201 		(void) snprintf(key, sizeof (key), "sv.set%d", setnumber);
1202 		if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0) {
1203 			break;
1204 		}
1205 
1206 		if (sscanf(buf, "%s - %s", vol, cnode) != 2) {
1207 			continue;
1208 		}
1209 
1210 		if (strcmp(path, vol) == 0) {
1211 			found = 1;
1212 			break;
1213 		}
1214 	}
1215 
1216 	if (!found) {
1217 		warn(NULL, gettext("unable to find %s in the configuration"),
1218 		    path);
1219 		return;
1220 	}
1221 
1222 	/* have name match, compare cnode to new tag */
1223 
1224 	if (strcmp(tag, cnode) == 0) {
1225 		/* cluster tags match */
1226 		return;
1227 	}
1228 
1229 	/* need to change the cluster tag */
1230 
1231 	(void) snprintf(key, sizeof (key), "sv.set%d.cnode", setnumber);
1232 	if (cfg_put_cstring(cfg, key, tag, strlen(tag)) < 0) {
1233 		warn(NULL,
1234 		    gettext("unable to change cluster tag for %s"), path);
1235 		return;
1236 	}
1237 
1238 	cfg_changed = 1;
1239 
1240 	/* change "-" tags to "" for display purposes */
1241 
1242 	if (strcmp(tag, "-") == 0)
1243 		tag = "";
1244 
1245 	if (strcmp(cnode, "-") == 0)
1246 		(void) strcpy(cnode, "");
1247 
1248 	(void) printf(
1249 	    gettext("%s: changed cluster tag for %s from \"%s\" to \"%s\"\n"),
1250 	    program, path, cnode, tag);
1251 
1252 	spcs_log("sv", NULL,
1253 	    gettext("%s: changed cluster tag for %s from \"%s\" to \"%s\""),
1254 	    program, path, cnode, tag);
1255 }
1256 
1257 
1258 /* LINT - not static as fwcadm uses it */
1259 static void
1260 compare_sv(char *conf_file)
1261 {
1262 	sv_name_t *svn_config;		/* Devices in config file */
1263 	sv_name_t *svn_system;		/* Devices in system */
1264 	sv_name_t *enable;		/* Devices that need enabled */
1265 	sv_list_t svl_system;
1266 	int config_cnt;
1267 	int sys_cnt = 0;
1268 	int setnumber, i, j;
1269 	int index = 0;	/* Index in enable[] */
1270 	int found;
1271 	int fd0;
1272 
1273 	svn_config = sv_alloc_svnames();
1274 	svn_system = sv_alloc_svnames();
1275 	enable = sv_alloc_svnames();
1276 
1277 	bzero(svn_system, sizeof (svn_system));
1278 	bzero(&svl_system, sizeof (svl_system));
1279 	bzero(enable, sizeof (enable));
1280 
1281 	/*
1282 	 * Read the configuration file
1283 	 * The return value is the number of entries
1284 	 */
1285 	config_cnt = read_config_file(conf_file, svn_config);
1286 
1287 	if ((fd0 = open(sv_rpath, O_RDONLY)) < 0)
1288 		error(NULL, gettext("unable to open %s: %s"),
1289 			sv_rpath, strerror(errno));
1290 
1291 	/* Grab the system list from the driver */
1292 	svl_system.svl_count = sv_max_devices;
1293 	svl_system.svl_names = &svn_system[0];
1294 	svl_system.svl_error = spcs_s_ucreate();
1295 
1296 	if (ioctl(fd0, SVIOC_LIST, &svl_system) < 0) {
1297 		error(&svl_system.svl_error, gettext("unable to get list"));
1298 	}
1299 
1300 	spcs_s_ufree(&svl_system.svl_error);
1301 	(void) close(fd0);
1302 
1303 	/*
1304 	 * Count the number of devices in the system.
1305 	 * The last entry in the array has '\0' for a path name.
1306 	 */
1307 	for (j = 0; j < sv_max_devices; j++) {
1308 		if (svn_system[j].svn_path[0] != '\0') {
1309 			sys_cnt++;
1310 		} else {
1311 			break;
1312 		}
1313 	}
1314 	/*
1315 	 * Compare the configuration array with the system array.
1316 	 * Mark any differences and disable conflicting devices.
1317 	 */
1318 	for (i = 0; i < config_cnt; i++) {
1319 		found = 0;
1320 		for (j = 0; j < sys_cnt; j++) {
1321 			if (svn_system[j].svn_path[0] == '\0' ||
1322 			    svn_system[j].svn_mode == 0)
1323 				continue;
1324 
1325 			/*  Check to see if path matches */
1326 			if (strcmp(svn_system[j].svn_path,
1327 			    svn_config[i].svn_path) == 0) {
1328 				/*  Found a match  */
1329 				svn_system[j].svn_path[0] = '\0';
1330 				found++;
1331 				break;
1332 			}
1333 		}
1334 
1335 		if (!found) {
1336 			/* Minor number not in system  = > enable device */
1337 			enable[index].svn_mode = svn_config[i].svn_mode;
1338 			(void) strcpy(enable[index].svn_path,
1339 			    svn_config[i].svn_path);
1340 			index++;
1341 		}
1342 	}
1343 
1344 	/* Disable any devices that weren't in the config file */
1345 	for (j = 0; j < sys_cnt; j++) {
1346 		sv_check_cluster(NULL);
1347 		sv_cfg_open(CFG_WRLOCK);
1348 		create_cfg_hash();
1349 		if (svn_system[j].svn_path[0] != '\0' &&
1350 		    svn_system[j].svn_mode != 0) {
1351 			(void) printf(gettext("%s: disabling sv: %s\n"),
1352 			    program, svn_system[j].svn_path);
1353 			if (disable_dev(svn_system[j].svn_path) == 0) {
1354 				setnumber =
1355 					find_in_hash(svn_system[j].svn_path);
1356 				if (setnumber != -1) {
1357 					/* the volume was found in cfg store */
1358 					remove_from_cfgfile(
1359 					svn_system[j].svn_path, setnumber);
1360 				}
1361 			}
1362 		}
1363 		sv_cfg_close();
1364 		destroy_hashtable();
1365 	}
1366 
1367 	while (index) {
1368 		/*
1369 		 * Config file doesn't match system => enable the devices
1370 		 * in enable[]
1371 		 */
1372 		index--;
1373 		(void) printf(gettext("%s: enabling new sv: %s\n"),
1374 		    program, enable[index].svn_path);
1375 		(void) enable_dev(&enable[index]);
1376 	}
1377 
1378 	/*
1379 	 * Search for entries where the cluster tag has changed.
1380 	 */
1381 	sv_check_cluster(NULL);
1382 	sv_cfg_open(CFG_WRLOCK);
1383 
1384 	for (i = 0; i < sv_max_devices; i++) {
1385 		if (svn_config[i].svn_path[0] == '\0')
1386 			break;
1387 
1388 		compare_tag(svn_config[i].svn_path);
1389 	}
1390 
1391 	sv_cfg_close();
1392 }
1393 
1394 
1395 /*
1396  * We assume that the volume is already enabled and we can only
1397  * be changing the cluster tag.  Anything else is an error.
1398  */
1399 /* LINT - not static as fwcadm uses it */
1400 static void
1401 compare_one_sv(char *path)
1402 {
1403 	sv_get_maxdevs();
1404 	sv_check_cluster(NULL);
1405 	sv_cfg_open(CFG_WRLOCK);
1406 
1407 	compare_tag(path);
1408 
1409 	sv_cfg_close();
1410 }
1411 
1412 /*
1413  * Read all sets from the libdscfg configuration file, and store everything in
1414  * the hashfile.
1415  *
1416  * We assume that the config file has been opened & rewound for us.  We store
1417  * the volume name as the key, and the setnumber where we found it as the data.
1418  *
1419  * The caller can pass in a pointer to the maximum number of volumes, or
1420  * a pointer to NULL, specifying we want 'all' the volumes.  The table is
1421  * searched using find_in_hash.
1422  */
1423 static void
1424 create_cfg_hash()
1425 {
1426 	char key[CFG_MAX_KEY], buf[CFG_MAX_BUF];
1427 	char vol[CFG_MAX_BUF], cnode[CFG_MAX_BUF];
1428 	int setnumber;
1429 	ENTRY item;
1430 
1431 	if (hcreate((size_t)sv_max_devices) == 0)
1432 		error(NULL, gettext("unable to create hash table"));
1433 
1434 	for (setnumber = 1; /* CSTYLED */; setnumber++) {
1435 		(void) snprintf(key, sizeof (key), "sv.set%d", setnumber);
1436 		if (cfg_get_cstring(cfg, key, buf, sizeof (buf)) < 0)
1437 			break;
1438 
1439 		if (sscanf(buf, "%s - %s", vol, cnode) != 2) {
1440 			continue;
1441 		}
1442 
1443 		item.key = strdup(vol);
1444 		item.data = (void *)setnumber;
1445 		if (hsearch(item, ENTER) == NULL) {
1446 			error(NULL,
1447 			    gettext("unable to add entry to hash table"));
1448 		}
1449 	}
1450 }
1451 
1452 /*
1453  * Function to search the hash for a specific volume.  If it is found,
1454  * we return the set number.  If it isn't found, we return -1
1455  */
1456 static int
1457 find_in_hash(char *path)
1458 {
1459 	ENTRY *found_entry, item;
1460 	int retval = -1;
1461 
1462 	item.key = path;
1463 
1464 	if ((found_entry = hsearch(item, FIND)) != NULL) {
1465 		retval = (int)found_entry->data;
1466 	}
1467 
1468 	return (retval);
1469 }
1470 
1471 /*
1472  * Just a wrapper to destory the hashtable.  At some point in the future we
1473  * might want to do something more....  For instance, verify that the cfg
1474  * database and the kernel configuration match (?)  Just an idea.
1475  */
1476 static void
1477 destroy_hashtable()
1478 {
1479 	hdestroy();
1480 }
1481 
1482 /*
1483  * This function will remove a particular set from the config file.
1484  *
1485  * We make a whole host of assumptions:
1486  *   o the hashfile is up to date;
1487  *   o The config file has been opened with a WRLOCK for us.
1488  */
1489 static void
1490 remove_from_cfgfile(char *path, int setnumber)
1491 {
1492 	char key[CFG_MAX_KEY];
1493 	int sev;
1494 	char *lcltag;
1495 
1496 	/* attempt to remove the volume from config storage */
1497 	(void) snprintf(key, sizeof (key), "sv.set%d", setnumber);
1498 	if (cfg_put_cstring(cfg, key, NULL, 0) < 0) {
1499 		warn(NULL, gettext("unable to remove %s from "
1500 		    "config storage: %s"), path, cfg_error(&sev));
1501 	} else {
1502 		if (implicit_tag && *implicit_tag) {
1503 			lcltag = implicit_tag;
1504 		} else if (cfg_cluster_tag && *cfg_cluster_tag) {
1505 			lcltag = cfg_cluster_tag;
1506 		} else {
1507 			lcltag = "-";
1508 		}
1509 		if (cfg_rem_user(cfg, path, lcltag, "sv") != CFG_USER_LAST) {
1510 			warn(NULL, gettext("unable to remove %s from dsvol"),
1511 			    path);
1512 		}
1513 		cfg_changed = 1;
1514 	}
1515 }
1516