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 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <sys/types.h>
27 #include <sys/wait.h>
28 #include <stdio.h>
29 #include <sys/param.h>
30 #include <errno.h>
31 #include <limits.h>
32 #include <fcntl.h>
33 #include <strings.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <signal.h>
37
38 #include <locale.h>
39 #include <langinfo.h>
40 #include <libintl.h>
41 #include <stdarg.h>
42 #include <ctype.h>
43
44 #include <sys/nsctl/cfg.h>
45
46 #include <sys/unistat/spcs_s.h>
47 #include <sys/unistat/spcs_s_u.h>
48 #include <sys/unistat/spcs_errors.h>
49
50 #include <sys/nsctl/dsw.h>
51 #include <sys/nskernd.h>
52
53 #define MAX_PROCESSES 64
54
55 int parseopts(int, char **, int *);
56 int read_resume_cfg();
57 int read_suspend_cfg();
58 void iiboot_usage(void);
59 extern char *basename(char *);
60
61 dsw_config_t *resume_list = 0;
62 dsw_ioctl_t *suspend_list = 0;
63 int n_structs;
64 char *program;
65 char *cfg_cluster_tag = NULL;
66
67 volatile int fork_cnt;
68 volatile int fork_rc;
69
70 static void
iiboot_msg(char * prefix,spcs_s_info_t * status,char * string,va_list ap)71 iiboot_msg(char *prefix, spcs_s_info_t *status, char *string, va_list ap)
72 {
73 if (status) {
74 (void) fprintf(stderr, "II: %s\n", prefix);
75 spcs_s_report(*status, stderr);
76 spcs_s_ufree(status);
77 } else {
78 (void) fprintf(stderr, "%s: %s: ", program, prefix);
79 }
80
81 if (string && *string != '\0') {
82 (void) vfprintf(stderr, string, ap);
83 }
84
85 (void) fprintf(stderr, "\n");
86 }
87
88 static void
iiboot_err(spcs_s_info_t * status,char * string,...)89 iiboot_err(spcs_s_info_t *status, char *string, ...)
90 {
91 va_list ap;
92 va_start(ap, string);
93
94 iiboot_msg(gettext("Error"), status, string, ap);
95
96 va_end(ap);
97 exit(1);
98 }
99
100 static void
iiboot_warn(spcs_s_info_t * status,char * string,...)101 iiboot_warn(spcs_s_info_t *status, char *string, ...)
102 {
103 va_list ap;
104 va_start(ap, string);
105
106 iiboot_msg(gettext("warning"), status, string, ap);
107
108 va_end(ap);
109 }
110
111 /* ARGSUSED */
112 static void
sigchld(int sig)113 sigchld(int sig)
114 {
115 int wait_loc = 0;
116
117 (void) wait(&wait_loc);
118 if (WIFEXITED(wait_loc) && (WEXITSTATUS(wait_loc) == 0)) {
119 ;
120 /*EMPTY*/
121 } else {
122 fork_rc = WEXITSTATUS(wait_loc);
123 }
124
125 if (fork_cnt > 0)
126 --fork_cnt;
127 }
128
129
130 int
131 #ifdef lint
iiboot_lintmain(int argc,char * argv[])132 iiboot_lintmain(int argc, char *argv[])
133 #else
134 main(int argc, char *argv[])
135 #endif
136 {
137 int pairs;
138 pid_t pid = 0;
139 int flag = 0;
140 int i, j;
141 int rc;
142 int ioctl_fd;
143 void *ioarg;
144 dsw_ioctl_t *ii_iop, ii_suspend;
145 dsw_list_t args = {0};
146 dsw_config_t *ii_cfgp, *lp = NULL;
147 spcs_s_info_t ustatus;
148 int max_processes = MAX_PROCESSES;
149
150 (void) setlocale(LC_ALL, "");
151 (void) textdomain("ii");
152
153 program = strdup(basename(argv[0]));
154
155 if ((ioctl_fd = open(DSWDEV, O_RDWR, 0)) == -1) {
156 spcs_log("ii", NULL, "iiboot open %s failed, errno %d",
157 DSWDEV, errno);
158 iiboot_err(NULL,
159 gettext("Failed to open Point-in-Time Copy control "
160 "device"));
161 }
162
163 if (parseopts(argc, argv, &flag))
164 return (1);
165
166 if (flag == DSWIOC_RESUME)
167 pairs = read_resume_cfg();
168 else
169 pairs = -1;
170
171 if (pairs == 0) {
172 #ifdef DEBUG
173 iiboot_err(NULL,
174 gettext("Config contains no Point-in-Time Copy sets"));
175 #endif
176 return (0);
177 }
178
179 if (cfg_cluster_tag == NULL && flag != DSWIOC_RESUME) {
180 if (ioctl(ioctl_fd, DSWIOC_SHUTDOWN, 0) < 0) {
181 spcs_log("ii", &ustatus, "iiboot shutdown failed");
182 iiboot_err(NULL, gettext("SHUTDOWN ioctl error"));
183 }
184 return (0);
185 } else if (cfg_cluster_tag != NULL && flag == DSWIOC_SUSPEND) {
186 bzero(&ii_suspend, sizeof (dsw_ioctl_t));
187 ii_suspend.status = spcs_s_ucreate();
188 ii_suspend.flags = CV_IS_CLUSTER;
189 (void) strncpy(ii_suspend.shadow_vol, cfg_cluster_tag,
190 DSW_NAMELEN);
191 rc = ioctl(ioctl_fd, flag, &ii_suspend);
192 if ((rc) && (errno != DSW_ECNOTFOUND)) {
193 spcs_log("ii", &ii_suspend.status,
194 "iiboot resume cluster %s failed", cfg_cluster_tag);
195 iiboot_err(&ii_suspend.status, gettext("ioctl error"));
196 spcs_s_ufree(&ii_suspend.status);
197 return (-1);
198 }
199 spcs_s_ufree(&ii_suspend.status);
200 return (0);
201
202 } else if ((cfg_cluster_tag != NULL) && (flag == DSWIOC_RESUME)) {
203 /*
204 * If we are running in a Sun Cluster, this is a resume
205 * operation, get a list of all shadow volumes, where the
206 * shadow volumes match the shadows of the sets being resumed
207 */
208 rc = ioctl(ioctl_fd, DSWIOC_LISTLEN, &args);
209 if (rc == -1) {
210 spcs_log("ii", NULL,
211 "iiboot get LIST failed, errno %d", errno);
212 iiboot_err(NULL,
213 gettext("Failed to get LIST of Point-in-Time "
214 "sets"));
215 return (-1);
216 }
217
218 args.status = spcs_s_ucreate();
219 args.list_used = 0;
220 args.list_size = rc + 4;
221 lp = args.list = (dsw_config_t *)
222 malloc(args.list_size * sizeof (dsw_config_t));
223 if (args.list == NULL) {
224 iiboot_err(NULL,
225 gettext("Failed to allocate memory"));
226 }
227 if (ioctl(ioctl_fd, DSWIOC_LIST, &args) == -1) {
228 spcs_log("ii", &args.status, "Failed to get LIST");
229 iiboot_err(&args.status, gettext("ioctl error"));
230 }
231 spcs_s_ufree(&args.status);
232
233 /* Remove all elements that are not in the resume list */
234 for (j = args.list_used; j; j--) {
235 for (i = 0; i < pairs; i++) {
236 if (strcmp(lp->shadow_vol,
237 resume_list[i].shadow_vol) == 0) {
238 if (strlen(lp->cluster_tag) == 0) {
239 lp++;
240 break;
241 }
242 }
243 }
244 if (i != pairs)
245 continue;
246 (void) memmove(lp, lp + 1, j * sizeof (dsw_config_t));
247 args.list_used--;
248 }
249 }
250
251 (void) sigset(SIGCHLD, sigchld);
252 fork_cnt = fork_rc = 0;
253 for (i = 0; i < pairs; i++) {
254 ustatus = spcs_s_ucreate();
255 if (flag == DSWIOC_RESUME) {
256 ioarg = (void *) (ii_cfgp = (resume_list + i));
257 ii_cfgp->status = ustatus;
258 pid = fork();
259 } else {
260 ioarg = (void *) (ii_iop = (suspend_list + i));
261 ii_iop->status = ustatus;
262 }
263 while (pid == -1) { /* error forking */
264 perror("fork");
265
266 /* back off on the max processes and try again */
267 --max_processes;
268 if (fork_cnt > 0) {
269 (void) pause();
270 }
271 pid = fork();
272 }
273
274 if (pid > 0) { /* this is parent process */
275 ++fork_cnt;
276 while (fork_cnt > MAX_PROCESSES) {
277 (void) pause();
278 }
279 continue;
280 }
281
282 rc = ioctl(ioctl_fd, flag, ioarg);
283 if (rc == SPCS_S_ERROR) {
284 if (flag == DSWIOC_RESUME)
285 spcs_log("ii", &ustatus,
286 "iiboot resume %s failed",
287 ii_cfgp->shadow_vol);
288 else
289 spcs_log("ii", &ustatus,
290 "iiboot suspend %s failed",
291 ii_iop->shadow_vol);
292 iiboot_err(&ustatus, gettext("ioctl error"));
293 }
294 /* Resuming child */
295 spcs_s_ufree(&ustatus);
296 if (flag == DSWIOC_RESUME)
297 exit(0);
298 }
299
300 /*
301 * Allow all processes to finish up before exiting
302 * Set rc for success
303 */
304 while (fork_cnt > 0) {
305 (void) alarm(60); /* wake up in 60 secs just in case */
306 (void) pause();
307 }
308 (void) alarm(0);
309
310 /* Disable duplicate shadows that were part of the implicit join */
311 if ((j = args.list_used) != 0) {
312 int setno;
313 char key[CFG_MAX_KEY], buf[CFG_MAX_BUF], sn[CFG_MAX_BUF];
314 CFGFILE *cfg;
315 char *mst, *shd, *ctag;
316 pid_t pid = fork();
317
318 if (pid == -1) {
319 iiboot_err(NULL, gettext("Failed to fork"));
320 return (errno);
321 } else if (pid > 0) {
322 return (0); /* Parent, OK exit */
323 }
324
325 for (j = args.list_used, lp = args.list; j; j--, lp++) {
326 setno = 0;
327 while (++setno) {
328
329 /*
330 * Open the configuration database
331 */
332 if (!(cfg = cfg_open(""))) {
333 iiboot_err(NULL, gettext("Failed to open dscfg"));
334 return (-1);
335 }
336
337 /* Sooner or later, this lock will be free */
338 while (!cfg_lock(cfg, CFG_WRLOCK))
339 (void) sleep(2);
340
341 (void) snprintf(key, CFG_MAX_KEY, "ii.set%d", setno);
342 if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
343 cfg_close(cfg);
344 break;
345 }
346
347 /* For imported shadows, master must be special tag */
348 mst = strtok(buf, " "); /* master */
349 shd = strtok(NULL, " "); /* shadow */
350 (void) strtok(NULL, " "); /* bitmap */
351 (void) strtok(NULL, " "); /* mode */
352 (void) strtok(NULL, " "); /* overflow */
353 ctag = strtok(NULL, " "); /* cnode */
354
355 /*
356 * For this record to be processed, the shadow volume
357 * name must match and the cluster tag must be blank
358 */
359 if (strcmp(lp->shadow_vol, shd) || strcmp(ctag, "-")) {
360 cfg_close(cfg);
361 continue;
362 }
363
364 /* Derrive local cluster tag */
365 if (cfg_l_dgname(lp->shadow_vol, sn, sizeof (sn)))
366 ctag = sn;
367 else
368 iiboot_err(NULL, gettext(
369 "Failed to device group for shadow %s"),
370 lp->shadow_vol);
371
372 /* disable master volume if not imported */
373 if (strcmp(mst, II_IMPORTED_SHADOW))
374 if (cfg_vol_disable(cfg, mst, cfg_cluster_tag,
375 "ii") < 0)
376 iiboot_err(NULL, gettext(
377 "SV disable of master failed"));
378
379 /*
380 * Delete the Imported Shadow set
381 */
382 if (cfg_put_cstring(cfg, key, NULL, 0) < 0) {
383 iiboot_err(NULL, gettext(
384 "Failed to delete Imported shadow %s"),
385 lp->shadow_vol);
386 }
387
388 /*
389 * SV disable shadow volume
390 */
391 if (cfg_vol_disable(cfg, shd, NULL, "ii") < 0)
392 iiboot_err(NULL, gettext(
393 "SV disable of shadow failed"));
394
395 /*
396 * Commit the delete
397 */
398 (void) cfg_commit(cfg);
399 cfg_close(cfg);
400
401 /*
402 * Open the configuration database
403 */
404 if (!(cfg = cfg_open(""))) {
405 iiboot_err(NULL, gettext("Failed to open dscfg"));
406 return (-1);
407 }
408
409 /* Sooner or later, this lock will be free */
410 while (!cfg_lock(cfg, CFG_WRLOCK))
411 (void) sleep(2);
412
413 /* Set cluster tag for Shadow volume */
414 (void) cfg_vol_enable(cfg, shd, ctag, "ii");
415
416
417 /*
418 * Commit the delete
419 */
420 (void) cfg_commit(cfg);
421 cfg_close(cfg);
422 }
423 }
424 }
425 return (fork_rc);
426 }
427
428 static int
set_is_offline(char * cflags)429 set_is_offline(char *cflags)
430 {
431 unsigned int flags;
432 int conv;
433
434 if (!cflags || !*cflags)
435 return (0);
436
437 /* convert flags to an int */
438 conv = sscanf(cflags, "%x", &flags);
439 return ((conv == 1) && ((flags & DSW_OFFLINE) != 0));
440 }
441
442 /*
443 * read_resume_cfg()
444 *
445 * DESCRIPTION: Read the relevant config info via libcfg
446 *
447 * Outputs:
448 * int i Number of Point-in-Time Copy sets
449 *
450 * Side Effects: The 0 to i-1 entries in the resume_list are filled.
451 *
452 */
453
454 int
read_resume_cfg()455 read_resume_cfg()
456 {
457 CFGFILE *cfg;
458 int i;
459 char *buf, **entry, *mst, *shd, *bmp, *ctag, *opt, *ptr;
460 int valid_sets;
461 dsw_config_t *p;
462 static int offset = sizeof (NSKERN_II_BMP_OPTION);
463
464 spcs_log("ii", NULL, "iiboot resume cluster tag %s",
465 cfg_cluster_tag ? cfg_cluster_tag : "<none>");
466 if ((cfg = cfg_open("")) == NULL) {
467 spcs_log("ii", NULL, "iiboot cfg_open failed, errno %d",
468 errno);
469 iiboot_err(NULL, gettext("Error opening config"));
470 }
471
472 cfg_resource(cfg, cfg_cluster_tag);
473 if (!cfg_lock(cfg, CFG_RDLOCK)) {
474 spcs_log("ii", NULL, "iiboot CFG_RDLOCK failed, errno %d",
475 errno);
476 iiboot_err(NULL, gettext("Error locking config"));
477 }
478
479 /* Determine number of set, if zero return 0 */
480 if ((n_structs = cfg_get_section(cfg, &entry, "ii")) == 0)
481 return (0);
482
483 resume_list = calloc(n_structs, sizeof (*resume_list));
484 if (resume_list == NULL) {
485 spcs_log("ii", NULL, "iiboot resume realloc failed, errno %d",
486 errno);
487 iiboot_err(NULL, gettext("Resume realloc failed"));
488 }
489
490 valid_sets = 0;
491 p = resume_list;
492 for (i = 0; i < n_structs; i++) {
493 buf = entry[i];
494 mst = strtok(buf, " ");
495 shd = strtok(NULL, " ");
496 bmp = strtok(NULL, " ");
497 (void) strtok(NULL, " "); /* mode */
498 (void) strtok(NULL, " "); /* overflow */
499 ctag = strtok(NULL, " "); /* ctag */
500 if (ctag)
501 ctag += strspn(ctag, "-");
502 opt = strtok(NULL, " ");
503
504 if (!mst || !shd || !bmp)
505 break;
506
507 /* If cluster tags don't match, skip record */
508 if ((cfg_cluster_tag && strcmp(ctag, cfg_cluster_tag)) ||
509 (!cfg_cluster_tag && strlen(ctag))) {
510 free(buf);
511 continue;
512 }
513
514 ptr = strstr(opt, NSKERN_II_BMP_OPTION "=");
515 if (ptr && set_is_offline(ptr + offset)) {
516 free(buf);
517 continue;
518 }
519
520 (void) strncpy(p->master_vol, mst, DSW_NAMELEN);
521 (void) strncpy(p->shadow_vol, shd, DSW_NAMELEN);
522 (void) strncpy(p->bitmap_vol, bmp, DSW_NAMELEN);
523 if (ctag)
524 (void) strncpy(p->cluster_tag, ctag, DSW_NAMELEN);
525 free(buf);
526 ++p;
527 ++valid_sets;
528 }
529
530 while (i < n_structs)
531 free(entry[i++]);
532 if (entry)
533 free(entry);
534
535 cfg_close(cfg);
536 return (valid_sets);
537 }
538
539 /*
540 * read_suspend_cfg()
541 *
542 * DESCRIPTION: Read the relevant config info via libcfg
543 *
544 * Outputs:
545 * int i Number of Point-in-Time Copy sets
546 *
547 * Side Effects: The 0 to i-1 entries in the suspend_list are filled.
548 *
549 */
550
551 int
read_suspend_cfg()552 read_suspend_cfg()
553 {
554 int rc;
555 CFGFILE *cfg;
556 int i;
557 char buf[CFG_MAX_BUF];
558 char key[CFG_MAX_KEY];
559 int setnumber;
560 dsw_ioctl_t *p;
561
562 spcs_log("ii", NULL, "iiboot suspend cluster tag %s",
563 cfg_cluster_tag ? cfg_cluster_tag : "<none>");
564
565 if (cfg_cluster_tag == NULL) {
566 return (1);
567 }
568
569 if ((cfg = cfg_open("")) == NULL) {
570 spcs_log("ii", NULL, "iiboot cfg_open failed, errno %d",
571 errno);
572 iiboot_err(NULL, gettext("Error opening config"));
573 }
574
575 cfg_resource(cfg, cfg_cluster_tag);
576 if (!cfg_lock(cfg, CFG_RDLOCK)) {
577 spcs_log("ii", NULL, "iiboot CFG_RDLOCK failed, errno %d",
578 errno);
579 iiboot_err(NULL, gettext("Error locking config"));
580 }
581
582
583 /*CSTYLED*/
584 for (i = 0; ; i++) {
585 setnumber = i + 1;
586
587 bzero(buf, CFG_MAX_BUF);
588 (void) snprintf(key, sizeof (key), "ii.set%d", setnumber);
589 rc = cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF);
590 if (rc < 0)
591 break;
592 if (n_structs < setnumber) {
593 n_structs += 2;
594 suspend_list = realloc(suspend_list,
595 sizeof (*suspend_list) * n_structs);
596 if (suspend_list == NULL) {
597 spcs_log("ii", NULL,
598 "iiboot suspend realloc failed, errno %d",
599 errno);
600 iiboot_err(NULL, gettext("Suspend realloc failed"));
601 }
602 }
603 p = suspend_list + i;
604
605 (void) snprintf(key, sizeof (key), "ii.set%d.shadow",
606 setnumber);
607 (void) cfg_get_cstring(cfg, key, p->shadow_vol, DSW_NAMELEN);
608
609 }
610
611 cfg_close(cfg);
612 return (i);
613 }
614
615
616 int
parseopts(argc,argv,flag)617 parseopts(argc, argv, flag)
618 int argc;
619 char **argv;
620 int *flag;
621 {
622 int errflag = 0;
623 int Cflag = 0;
624 char c;
625 char inval = 0;
626
627 while ((c = getopt(argc, argv, "hrsC:")) != -1) {
628 switch (c) {
629 case 'C':
630 if (Cflag) {
631 iiboot_warn(NULL,
632 gettext("-C specified multiple times"));
633 iiboot_usage();
634 return (-1);
635 }
636
637 Cflag++;
638 cfg_cluster_tag = (optarg[0] == '-') ? NULL : optarg;
639 break;
640
641 case 'h':
642 iiboot_usage();
643 exit(0);
644 /* NOTREACHED */
645
646 case 'r':
647 if (*flag)
648 inval = 1;
649 *flag = DSWIOC_RESUME;
650 break;
651 case 's':
652 if (*flag)
653 inval = 1;
654 *flag = DSWIOC_SUSPEND;
655 break;
656 case '?':
657 errflag++;
658 }
659 }
660
661 if (inval) {
662 iiboot_warn(NULL, gettext("Invalid argument combination"));
663 errflag = 1;
664 }
665
666 if (!*flag || errflag) {
667 iiboot_usage();
668 return (-1);
669 }
670
671 return (0);
672 }
673
674 void
iiboot_usage()675 iiboot_usage()
676 {
677 (void) fprintf(stderr, gettext("usage:\n"));
678 (void) fprintf(stderr,
679 gettext("\t%s -r [-C tag]\t\tresume\n"), program);
680 (void) fprintf(stderr,
681 gettext("\t%s -s [-C tag]\t\tsuspend\n"), program);
682 (void) fprintf(stderr, gettext("\t%s -h\t\t\tthis help message\n"),
683 program);
684 }
685