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
sv_cfg_open(CFGLOCK mode)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
sv_cfg_close(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
usage(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
message(caddr_t prefix,spcs_s_info_t * status,caddr_t string,va_list ap)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
error(spcs_s_info_t * status,caddr_t string,...)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
warn(spcs_s_info_t * status,caddr_t string,...)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
sv_get_maxdevs(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 *
sv_alloc_svnames(void)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
sv_check_dgislocal(char * dgname)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
sv_check_cluster(char * path)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
print_version(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
main(int argc,char * argv[])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
enable_sv(char * conf_file)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
enable_one_sv(caddr_t path)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
enable_dev(sv_name_t * svn)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
read_config_file(const caddr_t conf_file,sv_name_t svn[])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
disable_dev(const caddr_t path)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
print_cluster_tag(const int setnumber)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
print_sv(int verbose)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
disable_sv(char * conf_file)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
disable_one_sv(char * path)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
compare_tag(char * path)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
compare_sv(char * conf_file)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
compare_one_sv(char * path)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
create_cfg_hash()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
find_in_hash(char * path)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
destroy_hashtable()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
remove_from_cfgfile(char * path,int setnumber)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