/*
 * tools/vgimport.c
 *
 * Copyright (C) 1997 - 2000  Heinz Mauelshagen, Sistina Software
 *
 * May 1997
 * April-June,September 1998
 * February,May,October 1999
 * February 2000
 *
 * 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
 *
 *    09/11/1997 - added lvmtab handling
 *    12/02/1998 - added missing lvm_tab_vg_insert call
 *               - corrected VG number
 *    30/04/1998 - changed to new lv_create_kdev_t() calling convention
 *    03/05/1998 - enhanced error checking with lvm_tab_vg_insert()
 *               - avoided lv_create_name()
 *    16/05/1998 - added lvmtab checking
 *    17/05/1998 - obsoleted physical volume name correction (now in pv_read())
 *    27/06/1998 - changed lvm_tab_* calling convention
 *    05/09/1998 - corrected some messages
 *    16/02/1999 - changed to new lvm_tab_get_free_blk_dev()
 *    20/05/1999 - fixed MAX_LV check
 *    06/10/1999 - implemented support for long options
 *    15/02/2000 - use lvm_error()
 *
 */

#include <lvm_user.h>

char *cmd = NULL;

#ifdef DEBUG
int opt_d = 0;
#endif

int main ( int argc, char **argv) {
   int blk_dev = 0;
   int blk_dev_free_count = 0;
   int c = 0;
   int l = 0;
   int p = 0;
   int p1 = 0;
   int pv_number = 0;
   int pv_number_this = 0;
   int *pv_number_count = NULL;
   int ret = 0;
   char *pv_name = NULL;
   char *vg_name = NULL;
   char vg_name_this[NAME_LEN] = { 0, };
#ifdef DEBUG
   char *options = "dfh?v";
#else
   char *options = "fh?v";
#endif
   struct option long_options[] = {
#ifdef DEBUG
      { "debug",   no_argument, NULL, 'd'},
#endif
      { "force",   no_argument, NULL, 'f'},
      { "help",    no_argument, NULL, 'h'},
      { "verbose", no_argument, NULL, 'v'},
      { NULL,      0,           NULL, 0},
   };
   int opt_f = 0;
   int opt_v = 0;
   kdev_t *blk_dev_free = NULL;
   pv_t *pv = NULL;
   pv_t **pv_this = NULL;
   vg_t *vg = NULL;

   cmd = basename ( argv[0]);

   SUSER_CHECK;
   LVMTAB_CHECK;

   while ( ( c = getopt_long ( argc, argv, options,
                               long_options, NULL)) != EOF) {
      switch ( c) {
#ifdef DEBUG
         case 'd':
            if ( opt_d > 0) {
               fprintf ( stderr, "%s -- d option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            opt_d++;
            break;
#endif

         case 'f':
            if ( opt_f > 0) {
               fprintf ( stderr, "%s -- f option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            opt_f++;
            break;

         case 'h':
         case '?':
            printf ( "\n%s  (IOP %d)\n\n%s -- Volume Group Import\n\n"
                     "Synopsis:\n"
                     "---------\n\n"
                     "%s\n"
#ifdef DEBUG
                     "\t[-d/--debug]\n"
#endif
                     "\t[-f/--force]\n"
                     "\t[-h/-?/--help]\n"
                     "\t[-v/--verbose]\n"
                     "\tVolumeGroupName PhysicalVolumePath "
                     "[PhysicalVolumePath...]\n\n",
                     lvm_version, LVM_LIB_IOP_VERSION,  cmd, cmd);
            return 0;
            break;

         case 'v':
            if ( opt_v > 0) {
               fprintf ( stderr, "%s -- v option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            opt_v++;
            break;

         default:
            fprintf ( stderr, "%s -- invalid command line option \"%c\"\n\n",
                      cmd, c);
            return LVM_EINVALID_CMD_LINE;
      }
   }
  
   CMD_MINUS_CHK;
   LVM_CHECK_IOP;
   LVM_LOCK ( 0);

   if ( optind == argc) {
      fprintf ( stderr, "%s -- please give a volume group name\n\n", cmd);
      return LVM_EVGIMPORT_VG_MISSING;
   }

   vg_name = argv[optind++];
   if ( opt_v > 0) printf ( "%s -- checking volume group name\n", cmd);
   if ( vg_check_name ( vg_name) < 0) {
      fprintf ( stderr, "%s -- invalid volume group name \"%s\"\n\n",
                        cmd, vg_name);
      return LVM_EVGIMPORT_VG_CHECK_NAME;
   }

   if ( optind == argc) {
      fprintf ( stderr, "%s -- please enter a physical volume path\n\n", cmd);
      return LVM_EVGIMPORT_PV_MISSING;
   }

   if ( opt_v > 0) printf ( "%s -- checking volume group \"%s\" existence\n",
                            cmd, vg_name);
   if ( ( ret = lvm_tab_vg_check_exist ( vg_name, NULL)) == TRUE) {
      fprintf ( stderr, "%s -- a volume group named \"%s\" already exists\n\n",
                cmd, vg_name);
      return LVM_EVGIMPORT_VG_CHECK_EXIST;
   }

   /* get the physical volume data */
   if ( opt_v > 0) printf ( "%s -- trying to read physical volume%s\n",
                            cmd, argc - optind > 1 ? "s" : "");
   p = 0;
   for ( ; optind < argc; optind++) {
      pv_name = argv[optind];

      if ( opt_v > 0) printf ( "%s -- checking for multiple "
                               "physical volumes\n", cmd);
      for ( p1 = 0; pv_this != NULL && pv_this[p1] != NULL; p1++) {
         if ( strcmp ( pv_name, pv_this[p1]->pv_name) == 0) {
            fprintf ( stderr, "%s -- ERROR: physical volume \"%s\" occurs "
                              "multiple times\n\n",
                               cmd, pv_name);
            return LVM_EVGIMPORT_PV_MULTIPLE;
         }
      }

      if ( opt_v > 0) printf ( "%s -- checking physical volume name \"%s\"\n",
                               cmd, pv_name);
      if ( pv_check_name ( pv_name) < 0) {
         fprintf ( stderr, "%s -- invalid physical volume name \"%s\"\n\n",
                   cmd, pv_name);
         return LVM_EVGIMPORT_PV_CHECK_NAME;
      }

      if ( opt_v > 0) printf ( "%s -- reading data of physical volume \"%s\" "
                               "from disk\n", cmd, pv_name);
      if ( ( ret = pv_read ( pv_name, &pv, NULL)) < 0 &&
           ret != -LVM_EPV_READ_PV_EXPORTED) {
         fprintf ( stderr, "%s -- ERROR \"%s\" reading data of physical "
                           "volume \"%s\"\n\n",
                           cmd, lvm_error ( ret), pv_name);
         return LVM_EVGIMPORT_PV_READ;
      }

      if ( opt_v > 0) printf ( "%s -- checking for exported "
                               "physical volume \"%s\"\n",
                               cmd, pv_name);
      if ( opt_f == 0 &&
           system_id_check_exported ( pv->system_id) == FALSE) {
         fprintf ( stderr, "%s -- ERROR: physical volume \"%s\" doesn't belong "
                           "to an exported volume group\n\n",
                           cmd, pv_name);
         return LVM_EVGIMPORT_CHECK_EXPORTED;
      }

      if ( opt_v > 0) printf ( "%s -- reallocating memory\n", cmd);
      if ( ( pv_this = realloc ( pv_this,
                                 ( p + 2) * sizeof ( pv_t*))) == NULL) {
         fprintf ( stderr, "%s -- realloc error in file \"%s\" [line %d]\n\n",
                           cmd, __FILE__, __LINE__);
         return LVM_EVGIMPORT_REALLOC;
      }

      if ( ( pv_this[p] = malloc ( sizeof ( pv_t))) == NULL) {
         fprintf ( stderr, "%s -- malloc error in file \"%s\" [line %d]\n\n",
                           cmd, __FILE__, __LINE__);
         return LVM_EVGIMPORT_MALLOC;
      }
      memcpy ( pv_this[p], pv, sizeof ( pv_t));

      if ( opt_v > 0) printf ( "%s -- checking consistency of physical "
                               "volume \"%s\"\n", cmd, pv_name);
      if ( ( ret = pv_check_consistency ( pv_this[p])) < 0) {
         fprintf ( stderr, "%s -- ERROR \"%s\" checking physical "
                           "volume consistency of \"%s\"\n",
                           cmd, lvm_error ( ret), pv_name);
         continue;
      }

      p++;
      pv_this[p] = NULL;
   }
   pv_number_this = p;

   if ( p == 0) {
      fprintf ( stderr, "%s -- ERROR: no valid physical volumes found to "
                        "import volume group \"%s\"\n\n",
                        cmd, vg_name);
      return LVM_EVGIMPORT_NO_PV_FOUND;
   }

   /* check volume group consistency of found physical volumes */
   pv_number = pv_this[0]->pv_number;
   for ( p = 1; pv_this[p] != NULL; p++) {
      if ( strcmp ( pv_this[0]->vg_name, pv_this[p]->vg_name) != 0 ||
           strcmp ( pv_this[0]->system_id, pv_this[p]->system_id) != 0) {
         fprintf ( stderr, "%s -- physical volume \"%s\" doesn't belong to "
                           "the same exported volume group as \"%s\"\n\n",
                   cmd, pv_this[p]->pv_name, pv_this[0]->pv_name);
         return LVM_EVGIMPORT_VG_DIFF;
      }
      /* get the highest pv_number */
      if ( pv_number < pv_this[p]->pv_number) pv_number = pv_this[p]->pv_number;
   }

   if ( pv_number != pv_number_this) {
      fprintf ( stderr, "%s -- ERROR: wrong number of physical volumes "
                        "to import volume group \"%s\"\n\n",
                        cmd, vg_name);
      return LVM_EVGIMPORT_PV_COUNT;
   }

   /* get the physical volume numbers counter array */
   if ( ( pv_number_count = malloc ( pv_number * sizeof ( int))) == NULL) {
      fprintf ( stderr, "%s -- malloc error in file \"%s\" [line %d]\n\n",
                        cmd, __FILE__, __LINE__);
      return LVM_EVGIMPORT_MALLOC;
   }
   memset ( pv_number_count, 0, pv_number * sizeof ( int));

   /* fill the counter array to check physical volume numbers */
   for ( p = 0; pv_this[p] != NULL; p++)
      pv_number_count[pv_this[p]->pv_number-1]++;

   for ( p = 0; p < pv_number; p++) {
      if ( pv_number_count[p] != 1) {
         fprintf ( stderr, "%s -- ERROR: physical volume number %u "
                           "of \"%s\" occurs %d times\n\n",
                            cmd, pv_this[p]->pv_number,
                            pv_this[p]->pv_name, pv_number_count[p]);
         return LVM_EVGIMPORT_PV_MULTIPLE;
      }
   }
   free ( pv_number_count);
   pv_number_count = NULL;

   /* read the exported volume group */
   memset ( vg_name_this, 0, NAME_LEN);
   strncpy ( vg_name_this,
             pv_this[0]->vg_name,
             strlen ( pv_this[0]->vg_name) - strlen ( EXPORTED));
   if ( opt_v > 0) printf ( "%s -- reading exported volume group data "
                            " of \"%s\" from disk(s)\n", cmd, vg_name_this);
   if ( ( ret = vg_read_with_pv_and_lv ( pv_this[0]->vg_name, &vg)) < 0) {
      fprintf ( stderr, "%s -- ERROR \"%s\" reading data of "
                        "volume group \"%s\"\n\n",
                        cmd, lvm_error ( ret), vg_name_this);
      return LVM_EVGIMPORT_VG_READ;
   }

   if ( pv_number_this != vg->pv_cur) {
      fprintf ( stderr, "%s -- ERROR: only %d physical volumes of %u "
                        "given for volume group \"%s\"\n\n",
                        cmd, pv_number_this, vg->pv_cur, vg_name);
      return LVM_EVGIMPORT_PV_COUNT;
   }

   /* continue checking consistency in red exported volume group */
   if ( opt_v > 0) printf ( "%s -- checking for correct system ids on "
                            "physical volumes of volume group \"%s\"\n",
                            cmd, vg->vg_name);
   for ( p = 1; vg->pv[p] != NULL; p++) {
      if ( strcmp ( vg->pv[0]->vg_name, vg->pv[p]->vg_name) != 0 ||
           strcmp ( vg->pv[0]->system_id, vg->pv[p]->system_id) != 0) {
         fprintf ( stderr, "%s -- physical volume \"%s\" has a different "
                           "system_id than \"%s\"\n\n",
                           cmd, vg->pv[p]->pv_name, vg->pv[0]->pv_name);
         return LVM_EVGIMPORT_VG_DIFF;
      }
   }

   if ( opt_v > 0) printf ( "%s -- checking exported volume group "
                            "\"%s\" consistency\n", cmd, vg->vg_name);
   if ( ( ret = vg_check_consistency_with_pv_and_lv ( vg)) < 0 &&
        ret != -LVM_EVG_CHECK_CONSISTENCY_VG_STATUS) {
      fprintf ( stderr, "%s -- ERROR \"%s\" exported volume group \"%s\" "
                        "is inconsistent\n"
                        "%s -- it's system id is \"%s\"\n\n",
                        cmd, lvm_error ( ret), vg_name_this,
                        cmd, vg->pv[0]->system_id);
      return LVM_EVGIMPORT_VG_CHECK_CONSISTENCY;
   }

   if ( vg->lv_cur > MAX_LV || vg->lv_max > MAX_LV) {
      fprintf ( stderr, "%s -- too many logical volumes to import in "
                        "exported volume group \"%s\"\n\n",
                        cmd, vg_name_this);
      return LVM_EVGIMPORT_MAX_LV;
   }

   /* correct the volume group name */
   strcpy ( vg->vg_name, vg_name);

   if ( opt_v > 0) printf ( "%s -- importing physical volumes of volume "
                            "group \"%s\"%s\n",
                            cmd, vg->vg_name, opt_f > 1 ? "(forced)" : "");
   for ( p = 0; vg->pv[p] != NULL; p++) {
      system_id_set_imported ( vg->pv[p]->system_id);
      strcpy ( vg->pv[p]->vg_name, vg_name);
   }

   lvm_dont_interrupt ( 0);

   if ( opt_v > 0) printf ( "%s -- getting free number for volume "
                            "group \"%s\"\n\n",
                            cmd, vg_name);
   if ( ( vg->vg_number = lvm_tab_get_free_vg_number ()) < 0) {
      fprintf ( stderr, "%s -- ERROR getting free number for volume "
                        "group \"%s\"\n\n",
                        cmd, vg_name);
      return LVM_EVGIMPORT_GET_FREE_VG_NUMBER;
   }

   if ( opt_v > 0) printf ( "%s -- getting free block device numbers for "
                            "volume group \"%s\"\n\n",
                            cmd, vg_name);
   if ( ( blk_dev_free_count = lvm_tab_get_free_blk_dev ( &blk_dev_free)) < 0) {
      fprintf ( stderr, "%s -- ERROR \"%s\" getting free block device "
                        "numbers\n\n",
                        cmd, lvm_error ( blk_dev_free_count));
      return LVM_EVGIMPORT_NO_DEV;
   }

   if ( blk_dev_free_count < vg->lv_cur) {
      fprintf ( stderr, "%s -- only %d free LVM block devices available vs. "
                        "%d needed\n\n",
                        cmd, blk_dev_free_count, vg->lv_cur);
      return LVM_EVGIMPORT_NO_DEV;
   }

   if ( opt_v > 0) printf ( "%s -- importing logical volumes of volume "
                            "group \"%s\"\n",
                            cmd, vg->vg_name);
   for ( blk_dev = l = 0; l < vg->lv_max; l++) {
      if ( vg->lv[l] != NULL) {
         strcpy ( vg->lv[l]->vg_name, vg_name);
         strcpy ( vg->lv[l]->lv_name,
                  lv_change_vgname ( vg_name, vg->lv[l]->lv_name));
         vg->lv[l]->lv_dev = blk_dev_free[blk_dev++];
      }
   }

   /* correct status to import */
   vg->vg_status &= ~VG_EXPORTED;

   if ( opt_v > 0) printf ( "%s -- storing data of imported volume group "
                            "\"%s\" " "on disk(s)\n",
                            cmd, vg_name);
   if ( ( ret = vg_write_with_pv_and_lv ( vg)) < 0) {
      fprintf ( stderr, "%s -- ERROR \"%s\" storing data of imported volume "
                        "\"%s\" on disks\n\n",
                        cmd, lvm_error ( ret), vg_name);
      return LVM_EVGIMPORT_VG_WRITE;
   }

   if ( ( ret = vg_create_dir_and_group_and_nodes ( vg, opt_v)) < 0)
      fprintf ( stderr, "%s -- %d errors creating volume group "
                        "directory and special files\n", cmd, -ret);

   /* create VGDA in kernel */
   if ( opt_v > 0) printf ( "%s -- creating VGDA in kernel\n", cmd);
   if ( ( ret = vg_create ( vg_name, vg)) < 0) {
      fprintf ( stderr, "%s -- ERROR: couldn't create VGDA for imported volume "
                        "group \"%s\" in kernel\n", cmd, vg_name);
      if ( ret == -LVM_EVG_CREATE_REMOVE_OPEN)
         fprintf ( stderr, "%s -- LVM not in kernel?\n", cmd);
      fprintf ( stderr, "\n");
      return LVM_EVGIMPORT_VG_CREATE;
   }

   if ( opt_v > 0) printf ( "%s -- inserting volume group "
                            "\"%s\" into \"%s\"\n",
                            cmd, vg_name, LVMTAB);
   if ( ( ret = lvm_tab_vg_insert ( vg_name)) < 0) {
      fprintf ( stderr, "%s -- ERROR \"%s\" inserting volume group \"%s\" "
                       "into \"%s\"\n\n",
                       cmd, lvm_error ( ret), vg_name, LVMTAB);
      return LVM_EVGIMPORT_VG_INSERT;
   }

   if ( ( ret = vg_cfgbackup ( vg_name, LVMTAB_DIR, opt_v, vg)) == 0) {
      printf ( "%s -- doing automatic backup of volume group \"%s\"\n",
               cmd, vg_name);
      if ( ( ret = vg_cfgbackup ( vg_name, VG_BACKUP_DIR,
                                  opt_v, vg)) < 0) {
         fprintf ( stderr, "%s -- ERROR \"%s\" writing VG backup\n\n",
                           cmd, lvm_error ( ret));
         return LVM_EVGIMPORT_VG_CFGBACKUP;
      }
   } else {
      fprintf ( stderr, "%s -- ERROR \"%s\" writing lvmtab\n\n",
                        cmd, lvm_error ( ret));
      return LVM_EVGIMPORT_VG_CFGBACKUP_LVMTAB;
   }


   lvm_interrupt ();
   LVM_UNLOCK ( 0);

   printf ( "%s -- volume group \"%s\" successfully imported and activated\n\n",
            cmd, vg_name);

   return 0;
}
