xref: /titanic_44/usr/src/cmd/avs/dsw/iiboot.c (revision d0f3f37e7f24f68fdbd85386c60e576883622762)
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/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 	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 		strncpy(ii_suspend.shadow_vol, cfg_cluster_tag, DSW_NAMELEN);
190 		rc = ioctl(ioctl_fd, flag, &ii_suspend);
191 		if ((rc) && (errno != DSW_ECNOTFOUND)) {
192 			spcs_log("ii", &ii_suspend.status,
193 			    "iiboot resume cluster %s failed", cfg_cluster_tag);
194 			iiboot_err(&ii_suspend.status, gettext("ioctl error"));
195 			spcs_s_ufree(&ii_suspend.status);
196 			return (-1);
197 		}
198 		spcs_s_ufree(&ii_suspend.status);
199 		return (0);
200 
201 	} else if ((cfg_cluster_tag != NULL) && (flag == DSWIOC_RESUME)) {
202 		/*
203 		 * If we are running in a Sun Cluster, this is a resume
204 		 * operation, get a list of all shadow volumes, where the
205 		 * shadow volumes match the shadows of the sets being resumed
206 		 */
207 		rc = ioctl(ioctl_fd, DSWIOC_LISTLEN, &args);
208 		if (rc == -1) {
209 			spcs_log("ii", NULL,
210 				"iiboot get LIST failed, errno %d", errno);
211 			iiboot_err(NULL,
212 				gettext("Failed to get LIST of Point-in-Time "
213 				    "sets"));
214 			return (-1);
215 		}
216 
217 		args.status = spcs_s_ucreate();
218 		args.list_used = 0;
219 		args.list_size = rc + 4;
220 		lp = args.list = (dsw_config_t *)
221 		    malloc(args.list_size * sizeof (dsw_config_t));
222 		if (args.list == NULL) {
223 			iiboot_err(NULL,
224 				gettext("Failed to allocate memory"));
225 		}
226 		if (ioctl(ioctl_fd, DSWIOC_LIST, &args)  == -1) {
227 			spcs_log("ii", &args.status, "Failed to get LIST");
228 			iiboot_err(&args.status, gettext("ioctl error"));
229 		}
230 		spcs_s_ufree(&args.status);
231 
232 		/* Remove all elements that are not in the resume list */
233 		for (j = args.list_used; j; j--) {
234 			for (i = 0; i < pairs; i++) {
235 				if (strcmp(lp->shadow_vol,
236 				    resume_list[i].shadow_vol) == 0) {
237 					if (strlen(lp->cluster_tag) == 0) {
238 						lp++;
239 						break;
240 					}
241 				}
242 			}
243 			if (i != pairs)
244 				continue;
245 			memmove(lp, lp + 1, j * sizeof (dsw_config_t));
246 			args.list_used--;
247 		}
248 	}
249 
250 	sigset(SIGCHLD, sigchld);
251 	fork_cnt = fork_rc = 0;
252 	for (i = 0; i < pairs; i++) {
253 		ustatus = spcs_s_ucreate();
254 		if (flag == DSWIOC_RESUME) {
255 			ioarg = (void *) (ii_cfgp = (resume_list + i));
256 			ii_cfgp->status = ustatus;
257 			pid = fork();
258 		} else {
259 			ioarg = (void *) (ii_iop = (suspend_list + i));
260 			ii_iop->status = ustatus;
261 		}
262 		while (pid == -1) {		/* error forking */
263 			perror("fork");
264 
265 			/* back off on the max processes and try again */
266 			--max_processes;
267 			if (fork_cnt > 0) {
268 				pause();
269 			}
270 			pid = fork();
271 		}
272 
273 		if (pid > 0) {		/* this is parent process */
274 			++fork_cnt;
275 			while (fork_cnt > MAX_PROCESSES) {
276 				pause();
277 			}
278 			continue;
279 		}
280 
281 		rc = ioctl(ioctl_fd, flag, ioarg);
282 		if (rc == SPCS_S_ERROR) {
283 			if (flag == DSWIOC_RESUME)
284 				spcs_log("ii", &ustatus,
285 					"iiboot resume %s failed",
286 					ii_cfgp->shadow_vol);
287 			else
288 				spcs_log("ii", &ustatus,
289 					"iiboot suspend %s failed",
290 					ii_iop->shadow_vol);
291 			iiboot_err(&ustatus, gettext("ioctl error"));
292 		}
293 		/* Resuming child */
294 		spcs_s_ufree(&ustatus);
295 		if (flag == DSWIOC_RESUME)
296 			exit(0);
297 	}
298 
299 	/*
300 	 * Allow all processes to finish up before exiting
301 	 * Set rc for success
302 	 */
303 	while (fork_cnt > 0) {
304 		alarm(60);		/* wake up in 60 secs just in case */
305 		pause();
306 	}
307 	alarm(0);
308 
309 	/* Disable duplicate shadows that were part of the implicit join */
310 	if ((j = args.list_used) != 0) {
311 		int setno;
312 		char key[CFG_MAX_KEY], buf[CFG_MAX_BUF], sn[CFG_MAX_BUF];
313 		CFGFILE *cfg;
314 		char *mst, *shd, *ctag;
315 		pid_t pid = fork();
316 
317 		if (pid == -1) {
318 			iiboot_err(NULL, gettext("Failed to fork"));
319 			return (errno);
320 		} else if (pid > 0) {
321 			return (0);	/* Parent, OK exit */
322 		}
323 
324 		for (j = args.list_used, lp = args.list; j; j--, lp++) {
325 		    setno = 0;
326 		    while (++setno) {
327 
328 			/*
329 			 * Open the configuration database
330 			 */
331 			if (!(cfg = cfg_open(""))) {
332 			    iiboot_err(NULL, gettext("Failed to open dscfg"));
333 			    return (-1);
334 			}
335 
336 			/* Sooner or later, this lock will be free */
337 			while (!cfg_lock(cfg, CFG_WRLOCK))
338 				sleep(2);
339 
340 			snprintf(key, CFG_MAX_KEY, "ii.set%d", setno);
341 			if (cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF) < 0) {
342 				cfg_close(cfg);
343 				break;
344 			}
345 
346 			/* For imported shadows, master must be special tag */
347 			mst = strtok(buf, " ");		/* master */
348 			shd = strtok(NULL, " ");	/* shadow */
349 			(void) strtok(NULL, " ");	/* bitmap */
350 			(void) strtok(NULL, " ");	/* mode */
351 			(void) strtok(NULL, " ");	/* overflow */
352 			ctag = strtok(NULL, " ");	/* cnode */
353 
354 			/*
355 			 * For this record to be processed, the shadow volume
356 			 * name must match and the cluster tag must be blank
357 			 */
358 			if (strcmp(lp->shadow_vol, shd) || strcmp(ctag, "-")) {
359 				cfg_close(cfg);
360 				continue;
361 			}
362 
363 			/* Derrive local cluster tag */
364 			if (cfg_l_dgname(lp->shadow_vol, sn, sizeof (sn)))
365 				ctag = sn;
366 			else
367 				iiboot_err(NULL, gettext(
368 					"Failed to device group for shadow %s"),
369 					lp->shadow_vol);
370 
371 			/* disable master volume if not imported */
372 			if (strcmp(mst, II_IMPORTED_SHADOW))
373 			    if (cfg_vol_disable(cfg, mst, cfg_cluster_tag,
374 				"ii") < 0)
375 				iiboot_err(NULL, gettext(
376 				    "SV disable of master failed"));
377 
378 			/*
379 			 * Delete the Imported Shadow set
380 			 */
381 			if (cfg_put_cstring(cfg, key, NULL, 0) < 0) {
382 				iiboot_err(NULL, gettext(
383 					"Failed to delete Imported shadow %s"),
384 					lp->shadow_vol);
385 			}
386 
387 			/*
388 			 * SV disable shadow volume
389 			 */
390 			if (cfg_vol_disable(cfg, shd, NULL, "ii") < 0)
391 				iiboot_err(NULL, gettext(
392 					"SV disable of shadow failed"));
393 
394 			/*
395 			 * Commit the delete
396 			 */
397 			cfg_commit(cfg);
398 			cfg_close(cfg);
399 
400 			/*
401 			 * Open the configuration database
402 			 */
403 			if (!(cfg = cfg_open(""))) {
404 			    iiboot_err(NULL, gettext("Failed to open dscfg"));
405 			    return (-1);
406 			}
407 
408 			/* Sooner or later, this lock will be free */
409 			while (!cfg_lock(cfg, CFG_WRLOCK))
410 				sleep(2);
411 
412 			/* Set cluster tag for Shadow volume */
413 			cfg_vol_enable(cfg, shd, ctag, "ii");
414 
415 
416 			/*
417 			 * Commit the delete
418 			 */
419 			cfg_commit(cfg);
420 			cfg_close(cfg);
421 		    }
422 		}
423 	}
424 	return (fork_rc);
425 }
426 
427 static int
428 set_is_offline(char *cflags)
429 {
430 	unsigned int flags;
431 	int conv;
432 
433 	if (!cflags || !*cflags)
434 		return (0);
435 
436 	/* convert flags to an int */
437 	conv = sscanf(cflags, "%x", &flags);
438 	return ((conv == 1) && ((flags & DSW_OFFLINE) != 0));
439 }
440 
441 /*
442  * read_resume_cfg()
443  *
444  * DESCRIPTION: Read the relevant config info via libcfg
445  *
446  * Outputs:
447  *	int i			Number of Point-in-Time Copy sets
448  *
449  * Side Effects: The 0 to i-1 entries in the resume_list are filled.
450  *
451  */
452 
453 int
454 read_resume_cfg()
455 {
456 	CFGFILE *cfg;
457 	int i;
458 	char *buf, **entry, *mst, *shd, *bmp, *ctag, *opt, *ptr;
459 	int valid_sets;
460 	dsw_config_t *p;
461 	static int offset = sizeof (NSKERN_II_BMP_OPTION);
462 
463 	spcs_log("ii", NULL, "iiboot resume cluster tag %s",
464 			cfg_cluster_tag ? cfg_cluster_tag : "<none>");
465 	if ((cfg = cfg_open("")) == NULL) {
466 		spcs_log("ii", NULL, "iiboot cfg_open failed, errno %d",
467 			errno);
468 		iiboot_err(NULL, gettext("Error opening config"));
469 	}
470 
471 	cfg_resource(cfg, cfg_cluster_tag);
472 	if (!cfg_lock(cfg, CFG_RDLOCK)) {
473 		spcs_log("ii", NULL, "iiboot CFG_RDLOCK failed, errno %d",
474 			errno);
475 		iiboot_err(NULL, gettext("Error locking config"));
476 	}
477 
478 	/* Determine number of set, if zero return 0 */
479 	if ((n_structs = cfg_get_section(cfg, &entry, "ii")) == 0)
480 		return (0);
481 
482 	resume_list = calloc(n_structs, sizeof (*resume_list));
483 	if (resume_list == NULL) {
484 		spcs_log("ii", NULL, "iiboot resume realloc failed, errno %d",
485 		    errno);
486 		iiboot_err(NULL, gettext("Resume realloc failed"));
487 	}
488 
489 	valid_sets = 0;
490 	p = resume_list;
491 	for (i = 0; i < n_structs; i++) {
492 		buf = entry[i];
493 		mst = strtok(buf, " ");
494 		shd = strtok(NULL, " ");
495 		bmp = strtok(NULL, " ");
496 		(void) strtok(NULL, " ");	/* mode */
497 		(void) strtok(NULL, " ");	/* overflow */
498 		ctag = strtok(NULL, " ");	/* ctag */
499 		if (ctag)
500 			ctag += strspn(ctag, "-");
501 		opt = strtok(NULL, " ");
502 
503 		if (!mst || !shd || !bmp)
504 			break;
505 
506 		/* If cluster tags don't match, skip record */
507 		if ((cfg_cluster_tag && strcmp(ctag, cfg_cluster_tag)) ||
508 		    (!cfg_cluster_tag && strlen(ctag))) {
509 			free(buf);
510 			continue;
511 		}
512 
513 		ptr = strstr(opt, NSKERN_II_BMP_OPTION "=");
514 		if (ptr && set_is_offline(ptr + offset)) {
515 			free(buf);
516 			continue;
517 		}
518 
519 		strncpy(p->master_vol, mst, DSW_NAMELEN);
520 		strncpy(p->shadow_vol, shd, DSW_NAMELEN);
521 		strncpy(p->bitmap_vol, bmp, DSW_NAMELEN);
522 		if (ctag)
523 			strncpy(p->cluster_tag, ctag, DSW_NAMELEN);
524 		free(buf);
525 		++p;
526 		++valid_sets;
527 	}
528 
529 	while (i < n_structs)
530 		free(entry[i++]);
531 	if (entry)
532 		free(entry);
533 
534 	cfg_close(cfg);
535 	return (valid_sets);
536 }
537 
538 /*
539  * read_suspend_cfg()
540  *
541  * DESCRIPTION: Read the relevant config info via libcfg
542  *
543  * Outputs:
544  *	int i			Number of Point-in-Time Copy sets
545  *
546  * Side Effects: The 0 to i-1 entries in the suspend_list are filled.
547  *
548  */
549 
550 int
551 read_suspend_cfg()
552 {
553 	int rc;
554 	CFGFILE *cfg;
555 	int i;
556 	char buf[CFG_MAX_BUF];
557 	char key[CFG_MAX_KEY];
558 	int setnumber;
559 	dsw_ioctl_t *p;
560 
561 	spcs_log("ii", NULL, "iiboot suspend cluster tag %s",
562 			cfg_cluster_tag ? cfg_cluster_tag : "<none>");
563 
564 	if (cfg_cluster_tag == NULL) {
565 		return (1);
566 	}
567 
568 	if ((cfg = cfg_open("")) == NULL) {
569 		spcs_log("ii", NULL, "iiboot cfg_open failed, errno %d",
570 			errno);
571 		iiboot_err(NULL, gettext("Error opening config"));
572 	}
573 
574 	cfg_resource(cfg, cfg_cluster_tag);
575 	if (!cfg_lock(cfg, CFG_RDLOCK)) {
576 		spcs_log("ii", NULL, "iiboot CFG_RDLOCK failed, errno %d",
577 			errno);
578 		iiboot_err(NULL, gettext("Error locking config"));
579 	}
580 
581 
582 	/*CSTYLED*/
583 	for (i = 0; ; i++) {
584 		setnumber = i + 1;
585 
586 		bzero(buf, CFG_MAX_BUF);
587 		(void) snprintf(key, sizeof (key), "ii.set%d", setnumber);
588 		rc = cfg_get_cstring(cfg, key, buf, CFG_MAX_BUF);
589 		if (rc < 0)
590 			break;
591 		if (n_structs < setnumber) {
592 			n_structs += 2;
593 			suspend_list = realloc(suspend_list,
594 					sizeof (*suspend_list) * n_structs);
595 			if (suspend_list == NULL) {
596 			    spcs_log("ii", NULL,
597 			    "iiboot suspend realloc failed, errno %d",
598 			    errno);
599 			    iiboot_err(NULL, gettext("Suspend realloc failed"));
600 			}
601 		}
602 		p = suspend_list + i;
603 
604 		(void) snprintf(key, sizeof (key), "ii.set%d.shadow",
605 		    setnumber);
606 		(void) cfg_get_cstring(cfg, key, p->shadow_vol, DSW_NAMELEN);
607 
608 	}
609 
610 	cfg_close(cfg);
611 	return (i);
612 }
613 
614 
615 int
616 parseopts(argc, argv, flag)
617 int argc;
618 char **argv;
619 int *flag;
620 {
621 	int  errflag = 0;
622 	int  Cflag = 0;
623 	char c;
624 	char inval = 0;
625 
626 	while ((c = getopt(argc, argv, "hrsC:")) != -1) {
627 		switch (c) {
628 		case 'C':
629 			if (Cflag) {
630 				iiboot_warn(NULL,
631 				    gettext("-C specified multiple times"));
632 				iiboot_usage();
633 				return (-1);
634 			}
635 
636 			Cflag++;
637 			cfg_cluster_tag = (optarg[0] == '-') ? NULL : optarg;
638 			break;
639 
640 		case 'h':
641 			iiboot_usage();
642 			exit(0);
643 			/* NOTREACHED */
644 
645 		case 'r':
646 			if (*flag)
647 				inval = 1;
648 			*flag = DSWIOC_RESUME;
649 			break;
650 		case 's':
651 			if (*flag)
652 				inval = 1;
653 			*flag = DSWIOC_SUSPEND;
654 			break;
655 		case '?':
656 			errflag++;
657 		}
658 	}
659 
660 	if (inval) {
661 		iiboot_warn(NULL, gettext("Invalid argument combination"));
662 		errflag = 1;
663 	}
664 
665 	if (!*flag || errflag) {
666 		iiboot_usage();
667 		return (-1);
668 	}
669 
670 	return (0);
671 }
672 
673 void
674 iiboot_usage()
675 {
676 	(void) fprintf(stderr, gettext("usage:\n"));
677 	(void) fprintf(stderr,
678 		gettext("\t%s -r [-C tag]\t\tresume\n"), program);
679 	(void) fprintf(stderr,
680 		gettext("\t%s -s [-C tag]\t\tsuspend\n"), program);
681 	(void) fprintf(stderr, gettext("\t%s -h\t\t\tthis help message\n"),
682 	    program);
683 }
684