/*
 * mkraid.c : Utility for the Linux Multiple Devices driver
 *            Copyright (C) 1997 Ingo Molnar, Miguel de Icaza, Gadi Oxman
 *            Copyright (C) 1998 Erik Troan
 *
 * This utility reads a Linux MD raid1/4/5 configuration file, writes a
 * raid superblock on the devices in the raid set, and initializes the
 * set for first time use, depending on command line switches and
 * configuration file settings.
 *
 * This source is covered by the GNU GPL, the same as all Linux kernel
 * sources.
 */

#include "common.h"
#include "parser.h"
#include "raid_io.h"
#include "raidlib.h"
#include "popt.h"
#include "version.h"

void printcfg (md_cfg_entry_t * cfg);

void usage (void) {
    printf("usage: mkraid [--configfile] [--version] [--force] [--upgrade]\n");
    printf("       [--force-resync] [-acfhuv] </dev/md?>*\n");
}

static int makeOneRaid(struct md_version * ver, md_cfg_entry_t * cfg, 
		       int force, int upgrade, int force_resync) {
int i, ret, file;

    if (check_active(cfg)) 
	return 1;

    if (analyze_sb(ver, cfg, force, upgrade, force_resync))
	return 1;

    if (upgrade)
	return 0;
#if 0
    printcfg(cfg);
#endif

    file = open(cfg->md_name,O_RDONLY);
    ret = ioctl(file, SET_ARRAY_INFO, (unsigned long)&cfg->array.param);
    if (ret)
        return 1;

    for (i = 0; i < cfg->array.param.nr_disks; i++) {
            ret = ioctl(file, ADD_NEW_DISK,(unsigned long)(cfg->array.disks+i));
            if (ret)
                 return 1;
    }
    close(file);

    handleOneConfig(raidrun, cfg);

    return 0;
}

int main (int argc, char *argv[])
{
    FILE *fp = NULL;
    md_cfg_entry_t *p;
    int exit_status=0;
    int version = 0, help = 0, debug = 0;
    char * configFile = RAID_CONFIG;
    int force_flag = 0;
    int old_force_flag = 0;
    int upgrade_flag = 0;
    int resync_flag = 0;
    char ** args;
    struct md_version ver;
    poptContext optCon;
    int i;
    struct poptOption optionsTable[] = {
	{ "configfile", 'c', POPT_ARG_STRING, &configFile, 0 },
	{ "force", 'f', 0, &old_force_flag, 0 },
	{ "really-force", 'R', 0, &force_flag, 0 },
	{ "upgrade", 'u', 0, &upgrade_flag, 0 },
	{ "force-resync", 'r', 0, &resync_flag, 0 },
	{ "help", 'h', 0, &help, 0 },
	{ "version", 'V', 0, &version, 0 },
	{ "debug", 0, 0, &debug, 0 },
	{ NULL, 0, 0, NULL, 0 }
    } ;

    optCon = poptGetContext("mkraid", argc, argv, optionsTable, 0);
    if ((i = poptGetNextOpt(optCon)) < -1) {
	fprintf(stderr, "%s: %s\n", 
		poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
		poptStrerror(i));
	usage();
	return EXIT_FAILURE;
    }

    if (prepare_raidlib())
         return EXIT_FAILURE;

    if (help) {
	usage();
	return EXIT_FAILURE;
    } else if (version) {
	printf("mkraid version %d.%d.%d\n", MKRAID_MAJOR_VERSION,
			MKRAID_MINOR_VERSION, MKRAID_PATCHLEVEL_VERSION);
	return EXIT_VERSION;
    }

    fp = fopen(configFile, "r");
    if (fp == NULL) {
	fprintf(stderr, "Couldn't open %s -- %s\n", configFile, 
	        strerror(errno));
	goto abort;
    }

    srand((unsigned int) time(NULL));
    if (parse_config(fp))
	goto abort;

    args = poptGetArgs(optCon);
    if (!args) {
	fprintf(stderr, "nothing to do!\n");
	usage();
	return EXIT_FAILURE;
    }

    if (getMdVersion(&ver)) {
	fprintf(stderr, "cannot determine md version: %s\n", strerror(errno));
	return EXIT_FAILURE;
    }

    if (old_force_flag) {
	fprintf(stderr, 

"--force and the new RAID 0.90 hot-add/hot-remove functionality should be\n"
" used with extreme care! If /etc/raidtab is not in sync with the real array\n"
" configuration, then a --force will DESTROY ALL YOUR DATA. It's especially\n"
" dangerous to use -f if the array is in degraded mode. \n\n"
" PLEASE dont mention the --really-force flag in any email, documentation or\n"
" HOWTO, just suggest the --force flag instead. Thus everybody will read\n"
" this warning at least once :) It really sucks to LOSE DATA. If you are\n"
" confident that everything will go ok then you can use the --really-force\n"
" flag. Also, if you are unsure what this is all about, dont hesitate to\n"
" ask questions on linux-raid@vger.rutgers.edu\n");

	return EXIT_FAILURE;
    }

    if (debug) {
    	int file, ret;

	file = open(cfg_head->md_name, O_RDONLY);
	if (file == -1) {
		perror("could not open file");
		return EXIT_FAILURE;
	}
		
	ret = ioctl(file, PRINT_RAID_DEBUG, 0UL);
	if (ret) {
		perror("debug ioctl failed");
		return EXIT_FAILURE;
	}

	printf("dumped RAID status into the syslog.\n");
	return 0;
    }

    while (*args) {
	if (force_flag) {
		fprintf(stderr, 
		"DESTROYING the contents of %s in 5 seconds, Ctrl-C if unsure!\n", *args);
		sleep(5);
	}
	for (p = cfg_head; p; p = p->next) {
	    if (strcmp(p->md_name, *args)) continue;
	    if (makeOneRaid(&ver, p, force_flag, upgrade_flag, resync_flag))
		goto abort;
	    break;
	}
	if (!p) {
	    fprintf(stderr, "device %s is not described in config file\n", *args);
	    exit_status++;
	}
	args++;
    }

    fclose(fp);
    return 0;

abort:
    fprintf(stderr, "mkraid: aborted\n");
    exit_status = 1;
    if (fp)
	fclose(fp);
    return exit_status;
}


#define P(x) printf("%18s: \t %d\n",#x,cfg->array.param.x)
#define DP(x) printf("%18s: \t %d\n",#x,cfg->array.disks[i].x)

void printcfg (md_cfg_entry_t * cfg)
{
	int i;

	P(major_version);
	P(minor_version);
	P(patch_version);
        P(ctime);
        P(level);
        P(size);
        P(nr_disks);
        P(raid_disks);
        P(md_minor);

        P(utime);
        P(state);
        P(active_disks);
        P(working_disks);
        P(failed_disks);
        P(spare_disks);

        P(layout);
        P(chunk_size);

	for (i = 0; i < cfg->array.param.nr_disks; i++) {
		printf("\n");

		DP(number);
		DP(major);
		DP(minor);
		DP(raid_disk);
		DP(state);
	}
}
