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
55int parseopts(int, char **, int *);
56int read_resume_cfg();
57int read_suspend_cfg();
58void iiboot_usage(void);
59extern char *basename(char *);
60
61dsw_config_t *resume_list = 0;
62dsw_ioctl_t *suspend_list = 0;
63int	n_structs;
64char *program;
65char *cfg_cluster_tag = NULL;
66
67volatile int fork_cnt;
68volatile int fork_rc;
69
70static void
71iiboot_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
88static void
89iiboot_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
100static void
101iiboot_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 */
112static void
113sigchld(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
130int
131#ifdef lint
132iiboot_lintmain(int argc, char *argv[])
133#else
134main(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
428static int
429set_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
454int
455read_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
551int
552read_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
616int
617parseopts(argc, argv, flag)
618int argc;
619char **argv;
620int *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
674void
675iiboot_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