/*
 * tools/vgcreate.c
 *
 * Copyright (C) 1997 - 2002  Heinz Mauelshagen, Sistina Software
 *
 * March-June 1997
 * May,June,September 1998
 * January,February,October 1999
 * February 2000
 * January,June 2001
 * January 2002
 * February 2002
 *
 * 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
 *    16/02/1998 - corrected loop ending
 *    01/05/1998 - change to vg_create_dir_and_group() new calling convention
 *    03/05/1998 - enhanced error checking
 *    04/05/1998 - added multiple device support
 *    16/05/1998 - added lvmtab checking
 *    08/06/1998 - checked new return from vg_setup_for_create() to
 *                 handle acceptable PV/PE size relation
 *    12/06/1998 - enhanced checking numbers in option arguments
 *    27/06/1998 - changed lvm_tab_* calling convention
 *               - changed to new vg_setup_for_create() calling convention
 *    05/09/1998 - corrected some messages
 *    26/01/1999 - made volume group directory prefix a preprocessor option
 *    06/02/1999 - fixed lvm_check_number() usage
 *    06/10/1999 - implemented support for long options
 *    15/02/2000 - use lvm_error()
 *    22/01/2001 - fixed PE size default unit bug
 *    23/01/2001 - added call to lvm_init (JT)
 *    29/01/2001 - changed type of pe_size var for correct use
 *                 with lvm_check_number() I forgot last time
 *    19/03/2001 - Improved the phrasing of error message
 *    09/04/2001 - Tidy usage message and command-line options.
 *    22/06/2001 - added Andreas Dilger's PE on 4k boundary alignment
 *                 enhancements
 *    27/06/2001 - fixed minimum PV size message
 *    29/01/2002 - removed wrong PV size maximum message
 *    06/02/2002 - cope with changed pv_get_size() return cast
 *    07/02/2002 - fixes for > 1TB support
 *    17/02/2002 - avoid calling pv_get_size()
 *
 */

#include <lvm_user.h>

char *cmd = NULL;

#ifdef DEBUG
int opt_d = 0;
#endif

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

   if (ret == 0) {
      printf("%s (IOP %d)\n\n", lvm_version, LVM_LIB_IOP_VERSION);
      printf("%s -- initialize a volume group for use by LVM\n", cmd);
      stream = stdout;
   }

   fprintf(stream, "\n%s "
           "[-A|--autobackup {y|n}] "
           DEBUG_HELP_2
           "[-l|--maxlogicalvolumes MaxLogicalVolumes]\n\t"
           "[-p|--maxphysicalvolumes MaxPhysicalVolumes] "
           "[-h|--help]\n\t"
           "[-s|--physicalextentsize PhysicalExtentSize[kKmMgGtT]] "
           "[-v|--verbose]\n\t"
           "[--version] "
           "VolumeGroupName "
           "PhysicalVolume [PhysicalVolume...]\n",
           cmd);
   exit ( ret);
}

int main ( int argc, char **argv) {
   int c = 0;
   int count_sav = 0;
   int np = 0;
   int opt_A = 1;
   int opt_A_set = 0;
   int opt_l = 0;
   int opt_p = 0;
   int opt_s = 0;
   int opt_v = 0;
   int p = 0;
   int p1 = 0;
   int max_lv = MAX_LV;
   int max_pv = MAX_PV;
   int min_pv_index = 0;
   long long max_pv_size = 0;
   ulong min_pv_size = -1;
   long pe_size = LVM_DEFAULT_PE_SIZE;
   int ret = 0;
   uint size = 0;
   int v = 0;
   int vg_count = 0;
   char *dummy = NULL;
   char *vg_name = NULL;
   vg_t vg;
   pv_t **pv = NULL;
   pv_t **pvp = NULL;
   char *options = "A:h?l:p:s:v" DEBUG_SHORT_OPTION;
   struct option long_options[] = {
      { "autobackup",         required_argument, NULL, 'A'},
      DEBUG_LONG_OPTION
      { "help",               no_argument,       NULL, 'h'},
      { "maxlogicalvolumes",  required_argument, NULL, 'l'},
      { "maxphysicalvolumes", required_argument, NULL, 'p'},
      { "physicalextentsize", required_argument, NULL, 's'},
      { "verbose",            no_argument,       NULL, 'v'},
      { "version",            no_argument,       NULL, 22},
      { NULL, 0, NULL, 0}
   };
   char *pv_name = NULL;
   char **vg_name_ptr = NULL;

   /* lvm_init(argc, argv); */
   cmd = basename ( argv[0]);

   LVMTAB_CHECK;

   while ( ( c = getopt_long ( argc, argv, options,
                               long_options, NULL)) != EOF) {
      switch ( c) {
         case 'A':
            opt_A_set++;
            opt_A++;
            if ( optarg[0] == 'n') opt_A = 0;
            else if ( optarg[0] != 'y') {
               fprintf ( stderr, "%s -- ERROR option A argument \"%s\"\n",
                                 cmd, optarg);
               usage ( LVM_EINVALID_CMD_LINE);
            }
            break;

         DEBUG_HANDLE_CASE_D;

         case 'h':
            usage (0);
            break;

         case 'l':
            if ( ( max_lv = lvm_check_number ( optarg, FALSE)) > 0) {
               opt_l++;
            } else {
               fprintf ( stderr, "%s -- ERROR option l argument \"%s\"\n",
                                 cmd, optarg);
               usage ( LVM_EINVALID_CMD_LINE);
            }
            break;

         case 'p':
            if ( ( max_pv = lvm_check_number ( optarg, FALSE)) > 0) {
               opt_p++;
            } else {
               fprintf ( stderr, "%s -- ERROR option p argument \"%s\"\n",
                                 cmd, optarg);
               usage ( LVM_EINVALID_CMD_LINE);
            }
            break;

         case 's':
            if ( ( pe_size = lvm_check_number ( optarg, TRUE)) > 0) {
               pe_size = ( (unsigned long long) pe_size * 1024) / SECTOR_SIZE;
               opt_s++;
               if (vg_check_pe_size(pe_size) < 0) {
                  char *d1, *d2, *d3;
		  d1 = lvm_show_size (sectors_to_k(pe_size), SHORT);
		  d2 = lvm_show_size (sectors_to_k(LVM_MIN_PE_SIZE), SHORT);
                  d3 = lvm_show_size (sectors_to_k(LVM_MAX_PE_SIZE), SHORT);
		  fprintf(stderr,
		          "%s -- invalid physical extent size %s\n"
			  "%s -- must be power of 2 and between "
			  "%s and %s\n\n",
			  cmd, d1, cmd, d2, d3);
                  free ( d1); free ( d2); free ( d3);
		  usage(LVM_EINVALID_CMD_LINE);
               }
            } else {
               fprintf ( stderr, "%s -- ERROR option s argument \"%s\"\n",
                                 cmd, optarg);
               usage ( LVM_EINVALID_CMD_LINE);
            }
            break;

         case 'v':
            opt_v++;
            break;

         case 22:
            printf ("%s: %s (IOP %d)\n", cmd, lvm_version, LVM_LIB_IOP_VERSION);
            return 0;

         default:
            fprintf ( stderr, "%s -- invalid command line option \"%c\"\n",
                      cmd, c);
         case '?':
            usage ( LVM_EINVALID_CMD_LINE);
      }
   }

   /* Not until all commands are converted
   CMD_MINUS_CHK;
   */
   if ( optind < argc && *argv[optind] == '-') {
      fprintf ( stderr, "%s -- invalid command line\n", cmd);
      usage ( LVM_EINVALID_CMD_LINE);
   }
   CMD_CHECK_OPT_A_SET;


   if ( optind == argc) {
      fprintf ( stderr, "%s -- please enter a volume group name"
                        " and physical volumes\n", cmd);
      usage ( LVM_EVGCREATE_VG_AND_PV_MISSING);
   }
   vg_name = argv[optind];
   optind++;

   if ( optind == argc) {
      fprintf ( stderr, "%s -- please enter physical volume name(s)\n", cmd);
      usage ( LVM_EVGCREATE_PV_MISSING);
   }

   /* valid VG name? */
   if ( opt_v > 0) printf ( "%s -- checking volume group name\n", cmd);
   if ( ( ret = vg_check_name ( vg_name)) < 0) {
      fprintf ( stderr, "%s -- invalid volume group name \"%s\"\n\n",
                cmd, vg_name);
      usage ( LVM_EVGCREATE_VG_CHECK_NAME);
   }

   /* does /dev/VolumeGroupName already exist? */
   if ( opt_v > 0)
      printf ( "%s -- checking volume group directory existence\n", cmd);
   if ( ( ret = vg_check_dir ( vg_name)) == TRUE) {
      fprintf ( stderr, "%s -- volume group directory or file already exists\n"
                        "%s -- please choose a different name \n\n",
                        cmd, cmd);
      return LVM_EVGCREATE_VG_CHECK_DIR;
   }

   LVM_LOCK ( 0);
   LVM_CHECK_IOP;

   /* does VG exist? */
   if ( opt_v > 0) printf ( "%s -- checking volume group \"%s\" existence\n",
                            cmd, vg_name);
   if ( lvm_tab_vg_check_exist ( vg_name, NULL) == TRUE) {
      fprintf ( stderr, "%s -- volume group \"%s\" already exists\n\n",
                        cmd, vg_name);
      return LVM_EVGCREATE_VG_CHECK_EXIST;
   }

   if ( opt_v > 0) printf ( "%s -- counting all existing volume groups\n", cmd);
   vg_name_ptr = lvm_tab_vg_check_exist_all_vg ();
   vg_count = 0;
   if ( vg_name_ptr != NULL)
      for ( v = 0; vg_name_ptr[v] != NULL && vg_count < MAX_VG; v++) vg_count++;
   if ( vg_count >= MAX_VG) {
      fprintf ( stderr, "%s -- maximum volume group count of %d reached\n\n",
                        cmd, MAX_VG);
      return LVM_EVGCREATE_MAX_VG;
   }

   /* read all PVs */
   if ( opt_v > 0) printf ( "%s -- reading all physical volume data from "
                            "disks\n", cmd);
   if ( ( ret = pv_read_all_pv ( &pv, FALSE)) < 0) {
          fprintf ( stderr, "%s -- ERROR \"%s\" reading physical volumes\n\n",
                            cmd, lvm_error ( ret));
          return LVM_EVGCREATE_PV_READ_ALL_PV;
   }

   /* check, if PVs are all defined and new */
   if ( opt_v > 0) printf ( "%s -- checking if all given physical volumes in "
                            "command line are new\n", cmd);
   count_sav = argc - optind;
   np = 0;
   for ( ; pv != NULL && optind < argc ; optind++) {
      pv_name = argv[optind];
      if ( opt_v > 0) printf ( "%s -- checking physical volumes name \"%s\"\n",
                               cmd, pv_name);
      if ( pv_check_name ( pv_name) < 0) {
         fprintf ( stderr, "%s -- physical volume name \"%s\" is invalid\n\n",
                           cmd, pv_name);
         return LVM_EVGCREATE_PV_CHECK_NAME;
      }
      for ( p = 0; pv[p] != NULL; p++) {
         if ( strcmp ( pv_name, pv[p]->pv_name) == 0) {
            if ( opt_v > 0) printf ( "%s -- checking physical volume \"%s\"\n"
                                     "%s -- size of physical volume \"%s\" "
                                     " is %u sectors\n"
                                     "%s -- checking for new physical "
                                     "volume \"%s\"\n",
                                     cmd, pv_name,
                                     cmd, pv_name, pv[p]->pv_size,
                                     cmd, pv_name);
            if ( pv_check_new ( pv[p]) == FALSE) {
               fprintf ( stderr, "%s -- \"%s\" is not a new physical volume\n",
				 cmd, pv_name);
               if ( opt_v > 0) printf ( "%s -- checking volume group name "
                                        "of physical volume \"%s\"\n",
                                        cmd, pv_name);
               if ( vg_check_name ( pv[p]->vg_name) == 0) {
                  fprintf ( stderr, "%s -- physical volume \"%s\" already "
                                    "belongs to volume group \"%s\"\n",
                                    cmd, pv_name, pv[p]->vg_name);
               } else {
                  fprintf ( stderr, "%s -- ERROR: \"%s\" is an invalid "
                                    "physical volume\n",
                                    cmd, pv_name);
               }
               fprintf ( stderr, "\n");
               return LVM_EVGCREATE_PV_CHECK_NEW;
            }
            if ( opt_v > 0) printf ( "%s -- checking for identical physical "
                                     "volumes on command line\n", cmd);
            for ( p1 = 0; pvp != NULL && pvp[p1] != NULL; p1++) {
               if ( strcmp ( pv_name, pvp[p1]->pv_name) == 0) {
                  fprintf ( stderr, "%s -- ERROR: physical volume \"%s\" "
                                    "occurs multiple times\n\n",
                                   cmd, pv_name);
                     return LVM_EVGCREATE_PV_MULTIPLE;
               }
            }

            if ( ( pvp = realloc ( pvp, ( np + 2) * sizeof ( pv_t*))) == NULL) {
               fprintf ( stderr, "%s -- realloc error in file "
                                 "\"%s\" [line %d]\n\n",
                                 cmd, __FILE__, __LINE__);
               return LVM_EVGCREATE_REALLOC;
            }

            pvp[np] = pv[p];
            if ( max_pv_size < pvp[np]->pv_size)
               max_pv_size = pvp[np]->pv_size;
            if ( min_pv_size > pvp[np]->pv_size) {
               min_pv_size = pvp[np]->pv_size;
               min_pv_index = np;
            }
            np++;
            pvp[np] = NULL;
            /* p = MAX_PV; */
         }
      }
   }

   if ( np == 0) {
      fprintf ( stderr, "%s -- no valid physical volumes in command line\n\n",
                        cmd);
      return LVM_EVGCREATE_NO_VALID_PV;
   }

   if ( np != count_sav) {
      fprintf ( stderr,
                "%s -- some invalid physical volumes in command line\n\n", cmd);
      return LVM_EVGCREATE_SOME_INVALID_PV;
   }

   if ( opt_v > 0) {
      printf ( "%s -- %d physical volume%s will be inserted into "
               "volume group \"%s\"\n",
               cmd, np, np > 1 ? "s" : "", vg_name);
   }

   /* load volume group */
   /* checking command line arguments */
   if ( opt_v > 0) printf ( "%s -- checking command line arguments\n", cmd);

   if ( opt_v > 0) printf ( "%s -- INFO: maximum of %d physical volumes\n",
                            cmd, max_pv);
   if ( max_pv < 0 || max_pv < np || max_pv > ABS_MAX_PV) {
      fprintf ( stderr, "%s -- invalid maximum physical volumes -p %d\n\n",
                        cmd, max_pv);
      return LVM_EINVALID_CMD_LINE;
   }

   if ( opt_v > 0) printf ( "%s -- INFO: maximum of %d logical volumes\n",
                            cmd, max_lv);
   if ( max_lv < 0 || max_lv > ABS_MAX_LV ) {
      fprintf ( stderr, "%s -- invalid maximum logical volumes -l %d\n\n",
                        cmd, max_lv);
      return LVM_EINVALID_CMD_LINE;
   }

   size = ( LVM_PV_DISK_SIZE +
            LVM_VG_DISK_SIZE +
            max_pv * NAME_LEN +
            max_lv * sizeof ( lv_t));
   if ( size / SECTOR_SIZE > min_pv_size / 5) {
      fprintf ( stderr, "%s -- more than 20%% [%u KB] of physical volume "
                        "%s with %u KB would be used\n\n",
                        cmd,
                        size / 2,
                        pvp[min_pv_index]->pv_name,
                        pvp[min_pv_index]->pv_size / 2);
      return LVM_EVGCREATE_PV_TOO_SMALL;
   }

   if ( opt_v > 0) printf ( "%s -- setting up volume data for creation\n",
                            cmd);
   if ( ( ret = vg_setup_for_create ( vg_name, &vg, pvp, pe_size,
                                      max_pv, max_lv)) < 0) {
      if ( ret == -LVM_EVG_SETUP_FOR_CREATE_PV_SIZE_MIN) {
         fprintf ( stderr, "%s -- ERROR: physical volume(s) too small "
                           "for physical extent size of %s\n"
                           "%s -- minimum physical volume "
                           "at this physical extent size is %s\n\n",
                           cmd, vg_name, cmd,
                           ( dummy = lvm_show_size ( sectors_to_k(vg.pe_size) *
			     LVM_PE_SIZE_PV_SIZE_REL,
                             SHORT)));
                           free ( dummy); dummy = NULL;
      } else {
         fprintf ( stderr, "%s -- ERROR \"%s\" setting volume group \"%s\" up "
                           "for create\n\n",
                           cmd, lvm_error ( ret), vg_name);
      }
      return LVM_EVGCREATE_VG_SETUP;
   }

   if ( opt_s == 0) {
      printf ( "%s -- INFO: using default physical extent size %s\n",
               cmd, ( dummy = lvm_show_size ( pe_size / 2, SHORT)));
      free ( dummy); dummy = NULL;
   }
   printf ( "%s -- INFO: maximum logical volume size is %s\n",
            cmd, ( dummy = lvm_show_size ( LVM_LV_SIZE_2TB ( &vg) / 2, LONG)));
   free ( dummy); dummy = NULL;

   lvm_dont_interrupt ( 0);

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

   if ( opt_v) printf ( "%s -- removing any invalid special files "
                        "of volume group \"%s\"\n", cmd, vg_name);
   vg_remove_dir_and_group_and_nodes ( vg_name);

   if ( opt_v > 0) printf ( "%s -- creating volume group directory "
                            LVM_DIR_PREFIX
                            "%s\n",
                            cmd, vg_name);
   if ( ( ret = vg_create_dir_and_group ( &vg)) < 0) {
      if ( ret  == -LVM_EVG_CREATE_DIR_AND_GROUP_MKDIR) {
         fprintf ( stderr, "%s -- problem creating volume group "
                           "directory "
                           LVM_DIR_PREFIX
                           "%s\n",
                           cmd, vg_name);
      } else if ( ret == -LVM_EVG_CREATE_DIR_AND_GROUP_CHMOD_DIR) {
         fprintf ( stderr, "%s -- problem changing permission "
                           "for volume group directory "
                           LVM_DIR_PREFIX
                           "%s\n",
                           cmd, vg_name);
      } else if ( ret == -LVM_EVG_CREATE_DIR_AND_GROUP_CHMOD_GROUP) {
         fprintf ( stderr, "%s -- problem changing permission "
                           "for volume group file "
                           LVM_DIR_PREFIX
                           "%s/group\n",
                           cmd, vg_name);
      } else {
         fprintf ( stderr, "%s -- ERROR \"%s\" creating volume group "
                           "directory "
                           LVM_DIR_PREFIX
                           "%s\n",
                           cmd, lvm_error ( ret), vg_name);
      }
   }


   /* create VGDA in kernel */
   if ( opt_v > 0) printf ( "%s -- creating VGDA for volume group \"%s\" "
                            "in kernel\n",
                            cmd, vg_name);
   if ( ( ret = vg_create ( vg_name, &vg)) < 0) {
      fprintf ( stderr, "%s -- ERROR \"%s\" creating VGDA for volume "
                        "group \"%s\" in kernel\n",
                        cmd, lvm_error ( ret), vg_name);
      if ( ret == -LVM_EVG_CREATE_REMOVE_OPEN)
         fprintf ( stderr, "%s -- LVM not in kernel?\n", cmd);
      fprintf ( stderr, "\n");
      return LVM_EVGCREATE_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_EVGCREATE_VG_INSERT;
   }

   if ( ( ret = vg_cfgbackup ( vg_name, LVMTAB_DIR, opt_v, &vg)) == 0) {
      if ( opt_A > 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_EVGCREATE_VG_CFGBACKUP;
         }
      } else {
         printf ( "%s -- WARNING: you don't have an automatic "
                  "backup of \"%s\"\n",
                  cmd, vg_name);
      }
   } else {
      fprintf ( stderr, "%s -- ERROR \"%s\" writing \"%s\"\n\n",
                        cmd, lvm_error ( ret), LVMTAB);
      return LVM_EVGCREATE_VG_CFGBACKUP_LVMTAB;
   }

   lvm_interrupt ();
   LVM_UNLOCK ( 0);

   printf ( "%s -- volume group \"%s\" successfully created and activated\n\n",
            cmd, vg_name);
   return 0;
}
