xref: /titanic_52/usr/src/cmd/avs/dsw/iiboot.c (revision 75d94465dbafa487b716482dc36d5150a4ec9853)
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
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
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
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
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
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
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
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
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
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
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