/*
 * uuid_fixer.c
 *
 * Copyright (C) 2001 Sistina Software
 *
 * May 2001
 *
 * LVM is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 * 
 * LVM is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with LVM; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA. 
 *
 */

/*
 * Changelog
 *
 *	31/05/2001 - First version [AJ Lewis]
 *  05/06/2001 - Started using liblvm calls
 *             - PV consistency checks implemented
 *             - VG consistency checks implemented
 *
 */

#include<sys/types.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<ctype.h>
#include<getopt.h>

#include<lvm_user.h>

#ifndef MAJORVER
#define MAJORVER 0
#endif
#ifndef MINORVER
#define MINORVER 0
#endif



#define MAX_BUF_SIZE UUID_LEN+1

#define UUID_LIST_SEPARATION NAME_LEN - UUID_LEN

char *cmd = NULL;

#ifdef DEBUG
int opt_d = 0;
#endif /* DEBUG */

void usage(int ret)
{
   FILE *stream = stderr;

   if (ret == 0) {
      stream = stdout;
      printf("%s (IOP %d)\n\n", lvm_version, LVM_LIB_IOP_VERSION);
      printf("uuid_fixer v%d.%d -- fix incorrect UUID list\n",
			  MAJORVER, MINORVER);
   }

   fprintf(stream, "\nusage: %s [flags] <PVs>\n\n"
	"Flags:\n"
#ifdef DEBUG
	"  -d, --debug \n"
#endif
	"  -f, --force  \n"
	"  -v, --verbose \n"
	"  -h, --help  \n\n",
	cmd);
   exit(ret);
}

int main(int argc, char **argv)
{

	int i, j;
	int no_of_pvs = 0;
	int ret;
	int opt = 0;
	int opt_d = 0;
	int opt_f = 0;
	int opt_v = 0;
	int fd;
	char **uuid_list;
	char **disk;
	char *vgname = NULL;
	char answer;
	char c;
	pv_t *pv;
	vg_t *vg;

	struct option long_options[] =
	{
#ifdef DEBUG
		{ "debug", 		no_argument, 	NULL, 'd'},
#endif
		{ "force",		no_argument, 	NULL, 'f'},
		{ "verbose",	no_argument,	NULL, 'v'},
		{ "help",		no_argument,	NULL, 'h'},		
		{ NULL, 0, NULL, 0}
	};
#ifdef DEBUG
	char *options = "dfvh";
#else
	char *options = "fvh";
#endif

	cmd = argv[0];

	if(argc < 2) {
		usage(0);
		exit(-1);
	}

	while ((c = getopt_long(argc, argv, options,
				long_options, NULL)) != -1) {
		switch (c) {
		case 'd':
			opt_d++;
			opt++;
			break;
		case 'h':
			usage(0);
			break;
		case 'f':
			opt_f++;
			opt++;
			break;
		case 'v':
			opt_v++;
			opt++;
			break;
		default:
			fprintf(stderr,
				"%s -- invalid command line option \"%c\"\n",
				cmd, c);
		case '?':
			usage(1);
		}
	}
	

	/* The number of disks is argc-opt-1 */
	no_of_pvs = argc-opt-1;

	/* We want a char * for each PV that was passed in */
	if((uuid_list = (char**) malloc(sizeof(*uuid_list) * (no_of_pvs))) == NULL)
			return -ENOMEM;

	/* The list of disks is at the 'opt+1' spot in the argv array */
	disk = &(argv[opt+1]);

	/* Go through each of the files/PVs passed in and read in the UUID
	 * for each */
	for(i=0; i < no_of_pvs; i++) {

		/* Get the PV metadata for this disk */
		if ((ret = pv_read(disk[i], &pv, NULL)) != 0 &&
			ret != -LVM_EPV_READ_MD_DEVICE &&
			ret != -LVM_EPV_READ_PV_EXPORTED ) {

			fprintf(stderr, "%s - %s\n", disk[i], lvm_error(ret));
			return ret;
		}
		
		/* Check it for consistancy */
		if (pv_check_consistency(pv) != 0) {
			fprintf(stderr, "%s - %s\n", disk[i], lvm_error(ret));
			return ret;
		}

		/* Check UUID to make sure it contains valid characters */
		if (lvm_check_uuid(pv->pv_uuid) != 0) {
			fprintf(stderr, "%s - UUID corrupt\n", disk[i]);
			return -1;
		}

		/* Copy the UUID for the current PV into the list */
		if((uuid_list[i] = (char*) malloc(MAX_BUF_SIZE)) == NULL) {
			for(j=0; j < i; j++) {
				free(uuid_list[j]);
			}
			free(uuid_list);
		}

		strncpy(uuid_list[i], pv->pv_uuid, MAX_BUF_SIZE);

#ifdef DEBUG
		if(opt_d > 0) {
			printf("%s\n", uuid_list[i]);
		}
#endif /* DEBUG */

		/* Read the VG metadata from the current PV */
		if((ret = vg_read_from_pv(disk[i], &vg)) != 0 &&
			ret != -LVM_EVG_READ_VG_EXPORTED) {
			fprintf(stderr, "%s - %s\n", disk[i], lvm_error(ret));
			return ret;
		}

		/* Check to make sure it's not corrupted */
		if(vg_check_consistency(vg) != 0) {
			fprintf(stderr, "%s - %s\n", disk[i], lvm_error(ret));
			return ret;
		}

		/* If the number of PVs in the VG do not match up with the
		   number of PVs passed in, error out */
		if(vg->pv_cur != no_of_pvs) {
			fprintf(stderr, "Error: number of PVs passed in "
				"does not match number of PVs in %s's VG\n",
				disk[i]);
			fprintf(stderr, "       %d PVs were passed in and "
				"%d were expected.\n", no_of_pvs, vg->pv_cur);
			return -1;
		}

		/* If by random chance, a PV that does not belong to the initial
		   PV gets past the last test, this should stop it cold */
		if(vgname != NULL) {
			if(strncmp(vgname, vg->vg_name, NAME_LEN)) {
				fprintf(stderr, "Error: PV %s does not belong "
					"to %s's VG\n", disk[i], disk[0]);
				return -1;
			}
		}
		else {
			if((vgname = (char *) malloc(NAME_LEN * sizeof(char))) == NULL) {
				return -ENOMEM;
			}
			strncpy(vgname, vg->vg_name, NAME_LEN);
		}

	}

	/* Check with user before writing new UUIDs to the PVs
	 * but only if they haven't passed in the 'force' flag*/
	if(!opt_f) {
		printf("\nUUIDs discovered:\n");
		for(i=0; i < no_of_pvs; i++) {
			printf("%s\n", uuid_list[i]);
		}
		printf("\nShould this be written to each of the files?(y/n): ");
		answer = toupper(getc(stdin));
		if(answer != 'Y') {
			printf("Ok...goodbye\n\n");
			return 0;
		}
	}

	/* Make sure we can open all PVs before writing to any of them */
	for(i=0; i < no_of_pvs; i++) {
		if(opt_v) {
			printf("Opening %s in Write only mode\n", disk[i]);
		}
		if((fd = open(disk[i], O_WRONLY | O_SYNC)) < 0) {
			fprintf(stderr, "Unable to open input file %s\n",
				argv[i]);
			return -1;
		}
		close(fd);
	}

	/* Write the new UUID list to each of the files */
	for(i=0; i < no_of_pvs; i++) {
		if(opt_v) {
			printf("Writing out new UUID list to PV %s\n", disk[i]);
		}
		if((fd = open(disk[i], O_WRONLY | O_SYNC)) < 0) {
			fprintf(stderr, "Fatal - Unable to open input file %s\n",
					argv[i]);
			fprintf(stderr, "Metadata may be inconsistant - You should never see this\n");
			return -1;
		}
		lseek(fd, pv->pv_uuidlist_on_disk.base, SEEK_SET);
		for(j = 0; j < no_of_pvs; j++) {
			write(fd, uuid_list[j], UUID_LEN);
			lseek(fd, UUID_LIST_SEPARATION, SEEK_CUR);
		}
#ifdef DEBUG
		if(opt_d > 0) {
			printf("%s\n", uuid_list[i]);
		}
#endif /* DEBUG */
		close(fd);
	}

	/* clean up */
	for(i=0; i < no_of_pvs; i++) {
		free(uuid_list[i]);
	}
	free(uuid_list);
	free(vgname);
	
	return 0;
}

/*
 * Local variables:
 * c-file-style: "linux"
 * End:
 * vim:ai cin ts=4
 */

