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 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 /*
29 * hotspare maintenance
30 */
31
32 #include <meta.h>
33 #include <sdssc.h>
34
35 /*
36 * possible actions
37 */
38 enum metahs_op {
39 NONE,
40 ADD_A_HS,
41 DELETE_A_HS,
42 ENABLE_A_HS,
43 REPLACE_A_HS,
44 STATUS_A_HSP
45 };
46
47 /*
48 * report status of a hotspare pool
49 */
50 static int
status_hsp(mdsetname_t * sp,mdhspname_t * hspnp,md_error_t * ep)51 status_hsp(
52 mdsetname_t *sp,
53 mdhspname_t *hspnp,
54 md_error_t *ep
55 )
56 {
57 mdprtopts_t options = (PRINT_HEADER | PRINT_SUBDEVS | PRINT_DEVID);
58 mdnamelist_t *nlp = NULL;
59
60 /* must have set */
61 assert(sp != NULL);
62 assert(hspnp->hsp == MD_HSP_NONE || sp->setno == HSP_SET(hspnp->hsp));
63
64 /* print status */
65 if (meta_hsp_print(sp, hspnp, &nlp, NULL, stdout, options, ep) != 0)
66 return (-1);
67
68 /* return success */
69 return (0);
70 }
71
72 /*
73 * print usage message
74 */
75 static void
usage(mdsetname_t * sp,int eval)76 usage(
77 mdsetname_t *sp,
78 int eval
79 )
80 {
81 (void) fprintf(stderr, gettext("\
82 usage: %s [-s setname] -a hot_spare_pool [component...]\n\
83 %s [-s setname] -a \"all\" component...\n\
84 %s [-s setname] -d hot_spare_pool [component...]\n\
85 %s [-s setname] -d \"all\" component...\n\
86 %s [-s setname] -e component...\n\
87 %s [-s setname] -r hot_spare_pool component_old component_new\n\
88 %s [-s setname] -r \"all\" component_old component_new\n\
89 %s [-s setname] -i [hot_spare_pool...]\n"),
90 myname, myname, myname, myname, myname, myname, myname, myname);
91 md_exit(sp, eval);
92 }
93
94 /*
95 * parse args and add hotspares
96 */
97 static int
add_hotspares(mdsetname_t ** spp,int argc,char * argv[],mdcmdopts_t options,md_error_t * ep)98 add_hotspares(
99 mdsetname_t **spp,
100 int argc,
101 char *argv[],
102 mdcmdopts_t options,
103 md_error_t *ep
104 )
105 {
106 mdhspnamelist_t *hspnlp = NULL;
107 mdnamelist_t *nlp = NULL;
108 int cnt;
109 mdhspnamelist_t *p;
110 int rval = -1;
111
112 /* get hotspare pool name(s) */
113 if (argc < 1)
114 usage(*spp, 1);
115 if ((argc > 1) && meta_is_all(argv[0])) {
116 /* check for ownership */
117 assert(*spp != NULL);
118 if (meta_check_ownership(*spp, ep) != 0)
119 return (-1);
120
121 if ((cnt = meta_get_hsp_names(*spp, &hspnlp, 0, ep)) < 0) {
122 return (-1);
123 } else if (cnt == 0) {
124 return (mderror(ep, MDE_NO_HSPS, NULL));
125 }
126 } else { /* create the hsp nmlist from the specified hsp name */
127 if (!is_hspname(argv[0]))
128 return (mderror(ep, MDE_NAME_ILLEGAL, argv[0]));
129
130 if ((cnt = metahspnamelist(spp, &hspnlp, 1, &argv[0], ep)) < 0)
131 return (-1);
132 }
133 assert(cnt > 0);
134 --argc, ++argv;
135
136 assert(*spp != NULL);
137
138 /* grab set lock */
139 if (meta_lock(*spp, TRUE, ep))
140 return (-1);
141
142 /* check for ownership */
143 if (meta_check_ownership(*spp, ep) != 0)
144 return (-1);
145
146 /* get hotspares */
147 if (metanamelist(spp, &nlp, argc, argv,
148 LOGICAL_DEVICE, ep) < 0) {
149 goto out;
150 }
151
152 /* add hotspares */
153 for (p = hspnlp; (p != NULL); p = p->next) {
154 mdhspname_t *hspnp = p->hspnamep;
155
156 if (meta_hs_add(*spp, hspnp, nlp, options, ep) != 0)
157 goto out;
158 }
159 rval = 0;
160
161 /* cleanup, return success */
162 out:
163 if (hspnlp != NULL)
164 metafreehspnamelist(hspnlp);
165 if (nlp != NULL)
166 metafreenamelist(nlp);
167 return (rval);
168 }
169
170 /*
171 * parse args and delete hotspares
172 */
173 static int
delete_hotspares(mdsetname_t ** spp,int argc,char * argv[],mdcmdopts_t options,md_error_t * ep)174 delete_hotspares(
175 mdsetname_t **spp,
176 int argc,
177 char *argv[],
178 mdcmdopts_t options,
179 md_error_t *ep
180 )
181 {
182 mdhspnamelist_t *hspnlp = NULL;
183 mdnamelist_t *nlp = NULL;
184 int cnt;
185 mdhspnamelist_t *p;
186 int rval = -1;
187
188 /* get hotspare pool name(s) */
189 if (argc < 1)
190 usage(*spp, 1);
191 if ((argc > 1) && meta_is_all(argv[0])) {
192 /* check for ownership */
193 assert(*spp != NULL);
194 if (meta_check_ownership(*spp, ep) != 0)
195 return (-1);
196
197 if ((cnt = meta_get_hsp_names(*spp, &hspnlp, 0, ep)) < 0) {
198 return (-1);
199 } else if (cnt == 0) {
200 return (mderror(ep, MDE_NO_HSPS, NULL));
201 }
202 } else if ((cnt = metahspnamelist(spp, &hspnlp, 1, &argv[0],
203 ep)) < 0) {
204 return (-1);
205 }
206 assert(cnt > 0);
207 --argc, ++argv;
208
209 assert(*spp != NULL);
210
211 /* grab set lock */
212 if (meta_lock(*spp, TRUE, ep))
213 return (-1);
214
215 /* check for ownership */
216 if (meta_check_ownership(*spp, ep) != 0)
217 return (-1);
218
219 /* get hotspares */
220 if (metanamelist(spp, &nlp, argc, argv,
221 LOGICAL_DEVICE, ep) < 0) {
222 goto out;
223 }
224
225 /* delete hotspares */
226 cnt = 0;
227 for (p = hspnlp; (p != NULL); p = p->next) {
228 mdhspname_t *hspnp = p->hspnamep;
229
230 if (meta_hs_delete(*spp, hspnp, nlp, options, ep) != 0) {
231 if (mdisdeverror(ep, MDE_INVAL_HS))
232 mdclrerror(ep);
233 else
234 goto out;
235 } else {
236 ++cnt;
237 }
238 }
239
240 /* make sure we got some */
241 if ((nlp != NULL) && (cnt == 0)) {
242 (void) mddeverror(ep, MDE_INVAL_HS, nlp->namep->dev,
243 nlp->namep->cname);
244 goto out;
245 }
246
247 /* success */
248 rval = 0;
249
250 /* cleanup, return success */
251 out:
252 if (hspnlp != NULL)
253 metafreehspnamelist(hspnlp);
254 if (nlp != NULL)
255 metafreenamelist(nlp);
256 return (rval);
257 }
258
259 /*
260 * parse args and enable hotspares
261 */
262 static int
enable_hotspares(mdsetname_t ** spp,int argc,char * argv[],mdcmdopts_t options,md_error_t * ep)263 enable_hotspares(
264 mdsetname_t **spp,
265 int argc,
266 char *argv[],
267 mdcmdopts_t options,
268 md_error_t *ep
269 )
270 {
271 mdnamelist_t *nlp = NULL;
272 int rval = -1;
273
274 /* enable hotspares */
275 if (argc < 1)
276 usage(*spp, 1);
277
278 /* get list of hotspares */
279 if (metanamelist(spp, &nlp, argc, argv,
280 LOGICAL_DEVICE, ep) < 0)
281 goto out;
282 assert(nlp != NULL);
283
284 assert(*spp != NULL);
285
286 /* grab set lock */
287 if (meta_lock(*spp, TRUE, ep))
288 return (-1);
289
290 /* check for ownership */
291 if (meta_check_ownership(*spp, ep) != 0)
292 return (-1);
293
294 /* enable hotspares */
295 rval = meta_hs_enable(*spp, nlp, options, ep);
296
297 /* cleanup, return success */
298 out:
299 metafreenamelist(nlp);
300 return (rval);
301 }
302
303 /*
304 * parse args and replace hotspares
305 */
306 static int
replace_hotspares(mdsetname_t ** spp,int argc,char * argv[],mdcmdopts_t options,md_error_t * ep)307 replace_hotspares(
308 mdsetname_t **spp,
309 int argc,
310 char *argv[],
311 mdcmdopts_t options,
312 md_error_t *ep
313 )
314 {
315 mdhspnamelist_t *hspnlp = NULL;
316 int cnt;
317 mdname_t *oldnp;
318 mdname_t *newnp;
319 mdhspnamelist_t *p;
320 int rval = -1;
321
322 /* get hotspare pool name(s) */
323 if (argc != 3)
324 usage(*spp, 1);
325 if (meta_is_all(argv[0])) {
326 /* check for ownership */
327 assert(*spp != NULL);
328 if (meta_check_ownership(*spp, ep) != 0)
329 return (-1);
330
331 if ((cnt = meta_get_hsp_names(*spp, &hspnlp, 0, ep)) < 0) {
332 return (-1);
333 } else if (cnt == 0) {
334 return (mderror(ep, MDE_NO_HSPS, NULL));
335 }
336 } else if ((cnt = metahspnamelist(spp, &hspnlp, 1, &argv[0],
337 ep)) < 0) {
338 return (-1);
339 }
340 assert(cnt > 0);
341
342 assert(*spp != NULL);
343
344 /* grab set lock */
345 if (meta_lock(*spp, TRUE, ep))
346 return (-1);
347
348 /* check for ownership */
349 if (meta_check_ownership(*spp, ep) != 0)
350 return (-1);
351
352 /* get old component */
353 if ((oldnp = metaname(spp, argv[1], LOGICAL_DEVICE, ep)) == NULL)
354 goto out;
355
356 /* get new component */
357 if ((newnp = metaname(spp, argv[2], LOGICAL_DEVICE, ep)) == NULL)
358 goto out;
359
360 /* replace hotspares */
361 cnt = 0;
362 for (p = hspnlp; (p != NULL); p = p->next) {
363 mdhspname_t *hspnp = p->hspnamep;
364
365 if (meta_hs_replace(*spp, hspnp, oldnp, newnp, options, ep)
366 != 0) {
367 if (mdisdeverror(ep, MDE_INVAL_HS))
368 mdclrerror(ep);
369 else
370 goto out;
371 } else {
372 ++cnt;
373 }
374 }
375
376 /* make sure we got some */
377 if (cnt == 0) {
378 (void) mddeverror(ep, MDE_INVAL_HS, oldnp->dev, oldnp->cname);
379 goto out;
380 }
381
382 /* success */
383 rval = 0;
384
385 /* cleanup, return success */
386 out:
387 if (hspnlp != NULL)
388 metafreehspnamelist(hspnlp);
389 return (rval);
390 }
391
392 /*
393 * print_hsp_devid will collect the information for each underlying
394 * physical device for all the hotspare pools and print out the
395 * device relocation information
396 * INPUT:
397 * mdsetname_t *sp set the hsp is in
398 * mdhspnamelist_t *hspnlp list of hsp
399 * FILE *fp where to print to
400 * md_error_t *ep errors
401 * RETURN:
402 * 0 SUCCESS
403 * -1 ERROR
404 */
405 static int
print_hsp_devid(mdsetname_t * sp,mdhspnamelist_t * hspnlp,FILE * fp,md_error_t * ep)406 print_hsp_devid(
407 mdsetname_t *sp,
408 mdhspnamelist_t *hspnlp,
409 FILE *fp,
410 md_error_t *ep
411 )
412 {
413 mddevid_t *ldevidp = NULL;
414 int retval = 0;
415 mdhspnamelist_t *p;
416 mddevid_t *nextp;
417
418 /* for all hotspare pools */
419 for (p = hspnlp; (p != NULL); p = p->next) {
420 mdhspname_t *hspnp = p->hspnamep;
421 uint_t hsi;
422
423 /* for all hotspares within a pool */
424 for (hsi = 0;
425 hsi < hspnp->unitp->hotspares.hotspares_len; hsi++) {
426 mdname_t *hsname;
427
428 hsname =
429 hspnp->unitp->hotspares.hotspares_val[hsi].hsnamep;
430
431 meta_create_non_dup_list(hsname, &ldevidp);
432 }
433 }
434
435 retval = meta_print_devid(sp, fp, ldevidp, ep);
436
437 /* cleanup */
438 for (nextp = ldevidp; nextp != NULL; ldevidp = nextp) {
439 Free(ldevidp->ctdname);
440 nextp = ldevidp->next;
441 Free(ldevidp);
442 }
443 return (retval);
444 }
445
446 /*
447 * parse args and status hotspares
448 */
449 static int
status_hotspares(mdsetname_t ** spp,int argc,char * argv[],md_error_t * ep)450 status_hotspares(
451 mdsetname_t **spp,
452 int argc,
453 char *argv[],
454 md_error_t *ep
455 )
456 {
457 mdhspnamelist_t *hspnlp = NULL;
458 int cnt;
459 mdhspnamelist_t *p;
460 int rval = -1;
461
462 /* get hotspare pool name(s) */
463 if (argc == 0) {
464 /* check for ownership */
465 assert(*spp != NULL);
466 if (meta_check_ownership(*spp, ep) != 0)
467 return (-1);
468
469 if ((cnt = meta_get_hsp_names(*spp, &hspnlp, 0, ep)) < 0) {
470 return (-1);
471 } else if (cnt == 0) {
472 return (mderror(ep, MDE_NO_HSPS, NULL));
473 }
474 } else if ((cnt = metahspnamelist(spp, &hspnlp, argc, argv, ep)) < 0) {
475 return (-1);
476 }
477 assert(cnt > 0);
478
479 /* check for ownership */
480 assert(*spp != NULL);
481 if (meta_check_ownership(*spp, ep) != 0)
482 return (-1);
483
484 /* status hotspare pools */
485 for (p = hspnlp; (p != NULL); p = p->next) {
486 mdhspname_t *hspnp = p->hspnamep;
487
488 if (status_hsp(*spp, hspnp, ep) != 0)
489 goto out;
490 }
491
492 if (print_hsp_devid(*spp, hspnlp, stdout, ep) == 0) {
493 rval = 0;
494 }
495
496 /* cleanup, return success */
497 out:
498 if (hspnlp != NULL)
499 metafreehspnamelist(hspnlp);
500 return (rval);
501 }
502
503 /*
504 * parse args and doit
505 */
506 int
main(int argc,char ** argv)507 main(
508 int argc,
509 char **argv
510 )
511 {
512 char *sname = MD_LOCAL_NAME;
513 mdsetname_t *sp = NULL;
514 enum metahs_op which_op = NONE;
515 mdcmdopts_t options = (MDCMD_PRINT | MDCMD_DOIT);
516 int c;
517 md_error_t status = mdnullerror;
518 md_error_t *ep = &status;
519 int error;
520 bool_t called_thru_rpc = FALSE;
521 char *cp;
522
523 /*
524 * Get the locale set up before calling any other routines
525 * with messages to ouput. Just in case we're not in a build
526 * environment, make sure that TEXT_DOMAIN gets set to
527 * something.
528 */
529 #if !defined(TEXT_DOMAIN)
530 #define TEXT_DOMAIN "SYS_TEST"
531 #endif
532 (void) setlocale(LC_ALL, "");
533 (void) textdomain(TEXT_DOMAIN);
534
535
536 if ((cp = strstr(argv[0], ".rpc_call")) == NULL) {
537 if (sdssc_bind_library() == SDSSC_OKAY)
538 if (sdssc_cmd_proxy(argc, argv, SDSSC_PROXY_PRIMARY,
539 &error) == SDSSC_PROXY_DONE)
540 exit(error);
541 } else {
542 *cp = '\0'; /* cut off ".rpc_call" */
543 called_thru_rpc = TRUE;
544 }
545
546 /* initialize */
547 if (md_init(argc, argv, 0, 1, ep) != 0) {
548 mde_perror(ep, "");
549 md_exit(sp, 1);
550 }
551
552 /* parse args */
553 optind = 1;
554 opterr = 1;
555 while ((c = getopt(argc, argv, "hs:aderin?")) != -1) {
556 switch (c) {
557 case 'h':
558 usage(sp, 0);
559 break;
560
561 case 's':
562 sname = optarg;
563 break;
564
565 case 'a':
566 if (which_op != NONE)
567 usage(sp, 1);
568 which_op = ADD_A_HS;
569 break;
570
571 case 'd':
572 if (which_op != NONE)
573 usage(sp, 1);
574 which_op = DELETE_A_HS;
575 break;
576
577 case 'e':
578 if (which_op != NONE)
579 usage(sp, 1);
580 which_op = ENABLE_A_HS;
581 break;
582
583 case 'r':
584 if (which_op != NONE)
585 usage(sp, 1);
586 which_op = REPLACE_A_HS;
587 break;
588
589 case 'i':
590 if (which_op != NONE)
591 usage(sp, 1);
592 which_op = STATUS_A_HSP;
593 break;
594
595 case 'n':
596 if (called_thru_rpc == TRUE) {
597 options &= ~MDCMD_DOIT;
598 } else {
599 usage(sp, 1);
600 }
601 break;
602
603
604 case '?':
605 if (optopt == '?')
606 usage(sp, 0);
607 /*FALLTHROUGH*/
608 default:
609 usage(sp, 1);
610 break;
611 }
612 }
613
614 /* get set context */
615 if ((sp = metasetname(sname, ep)) == NULL) {
616 mde_perror(ep, "");
617 md_exit(sp, 1);
618 }
619
620 /*
621 * Send the command to all nodes if the -s argument refers to a MN
622 * set or the next argument refers to MN set hotspare name ( argc
623 * greater than optind if there is a next argument)
624 */
625 if ((called_thru_rpc == FALSE) &&
626 (meta_is_mn_set(sp, ep) || ((argc > optind) &&
627 meta_is_mn_name(&sp, argv[optind], ep)))) {
628 int i;
629 int newargc;
630 int result;
631 char **newargv;
632
633 /*
634 * If we are dealing with a MN set and we were not
635 * called thru an rpc call, we are just to send this
636 * command string to the master of the set and let it
637 * deal with it.
638 * First we send out a dryrun version of this command.
639 * If that returns success, we know it succeeded on all
640 * nodes and it is safe to do the real command now.
641 */
642 newargv = calloc(argc+1, sizeof (char *));
643 newargv[0] = "metahs";
644 newargv[1] = "-n"; /* always do "-n" first */
645 newargc = 2;
646 for (i = 1; i < argc; i++, newargc++)
647 newargv[newargc] = argv[i];
648 result = meta_mn_send_command(sp, newargc, newargv,
649 MD_DISP_STDERR | MD_DRYRUN, NO_CONTEXT_STRING, ep);
650
651 /* If we found a problem don't do it for real */
652 if (result != 0) {
653 md_exit(sp, result);
654 }
655
656 /*
657 * Do it for real now. Remove "-n" from the arguments and
658 * MD_DRYRUN from the flags. If this fails the master must panic
659 * as the mddbs may be inconsistent.
660 */
661 newargv[1] = ""; /* this was "-n" before */
662 result = meta_mn_send_command(sp, newargc, newargv,
663 MD_DISP_STDERR | MD_RETRY_BUSY | MD_PANIC_WHEN_INCONSISTENT,
664 NO_CONTEXT_STRING, ep);
665 free(newargv);
666
667 /* No further action required */
668 md_exit(sp, result);
669 }
670
671 argc -= optind;
672 argv += optind;
673 if (which_op == NONE)
674 usage(sp, 1);
675
676 /*
677 * if a hot spare pool was specified by name then
678 * get the canonical form of the name and set up
679 * sp if the name was specified in the form 'set/hsp'
680 * unless 'all' is specified or the request is made to
681 * enable a hs which means that argv[0] will be a component
682 */
683 if (argc > 0 && !meta_is_all(argv[0]) && which_op != ENABLE_A_HS) {
684 char *cname = NULL;
685
686 cname = meta_name_getname(&sp, argv[0], HSP_DEVICE, ep);
687 if (cname == NULL) {
688 mde_perror(ep, "");
689 md_exit(sp, 1);
690 }
691 Free(cname);
692 }
693
694 if (which_op == STATUS_A_HSP) {
695 if (status_hotspares(&sp, argc, argv, ep) != 0) {
696 mde_perror(ep, "");
697 md_exit(sp, 1);
698 }
699 md_exit(sp, 0);
700 }
701
702 if (meta_check_root(ep) != 0) {
703 mde_perror(ep, "");
704 md_exit(sp, 1);
705 }
706
707
708 /* dispatch */
709 switch (which_op) {
710
711 case ADD_A_HS:
712 if (add_hotspares(&sp, argc, argv, options, ep) != 0) {
713 mde_perror(ep, "");
714 md_exit(sp, 1);
715 }
716 break;
717
718 case DELETE_A_HS:
719 if (delete_hotspares(&sp, argc, argv, options, ep) != 0) {
720 mde_perror(ep, "");
721 md_exit(sp, 1);
722 }
723 break;
724
725 case ENABLE_A_HS:
726 if (enable_hotspares(&sp, argc, argv, options, ep) != 0) {
727 mde_perror(ep, "");
728 md_exit(sp, 1);
729 }
730 break;
731
732 case REPLACE_A_HS:
733 if (replace_hotspares(&sp, argc, argv, options, ep) != 0) {
734 mde_perror(ep, "");
735 md_exit(sp, 1);
736 }
737 break;
738
739 default:
740 assert(0);
741 break;
742 }
743
744 /* update md.cf */
745 out:
746 if (meta_update_md_cf(sp, ep) != 0) {
747 mde_perror(ep, "");
748 md_exit(sp, 1);
749 }
750 md_exit(sp, 0);
751 /*NOTREACHED*/
752 return (0);
753 }
754