/*
 * tools/pvcreate.c
 *
 * Copyright (C) 1997 - 2002  Heinz Mauelshagen, Sistina Software
 *
 * March, October-November 1997
 * May,June,September 1998
 * January,June,September,October 1999
 * February 2000
 * 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
 *
 *    12/10/1997 - added check for active physical volume
 *    07/11/1997 - added error message for not existing partitions
 *    03/05/1998 - enhanced error checking
 *    04/05/1998 - added multiple device support
 *    16/05/1998 - added lvmtab checking
 *    27/06/1998 - changed lvm_tab_* calling convention
 *    04/09/1998 - corrected some messages
 *    13/01/1999 - avoided error message for uninitialized physical volume
 *    22/06/1999 - added recongnition of Win98 extended partitions
 *                 by Steve Brueggeman
 *    22/09/1999 - added support for new partition identifier 0xDE
 *    24/09/1999 - stop in case of a physical volume error instead of
 *                 continuing with the next one in line
 *               - added new exit codes
 *    06/10/1999 - implemented support for long options
 *    15/02/2000 - use lvm_error()
 *    23/01/2001 - added call to lvm_init (JT)
 *    14/02/2001 - tidied usage message and options
 *    09/04/2001 - Remove check_partitioned_whole into pv_get_size.c
 *    06/02/2002 - cope with changed pv_get_size() return cast
 *    07/02/2002 - added size option
 *               - fixes for > 1TB support
 *    15/02/2002 - further > 1 TB additions
 *    17/02/2002 - use pv_get_size_ll() for library compatibility
 *
 */

#include <lvm_user.h>

char *cmd = NULL;

#ifdef DEBUG
int opt_d = 0;
#endif
int opt_v = 0;

static 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 physical volume for use by LVM\n", cmd);
      stream = stdout;
   }

   fprintf(stream, "\n%s "
           DEBUG_HELP_2
           "[-f[f]|--force [--force]] "
           "[-h|--help]\n\t"
           "[-s|--size PhysicalVolumeSize[kKmMgGtT]] "
           "[-y|--yes] "
           "[-v|--verbose]\n\t"
           "[--version] "
           "PhysicalVolume [PhysicalVolume...]\n\n",
           cmd);
   exit(ret);
}

int main ( int argc, char **argv)
{
   int c = 0;
   int c1 = 0;
   int force_needed = 0;
   int opt_f = 0;
   int opt_s = 0;
   int opt_y = 0;
   int ret = 0;
   uint size = 0;
   long long opt_s_size = 0;
   long long pv_size;
   char *options = "fh?s:yv" DEBUG_SHORT_OPTION;
   struct option long_options[] = {
      DEBUG_LONG_OPTION
      { "force",   no_argument,       NULL, 'f'},
      { "help",    no_argument,       NULL, 'h'},
      { "size",    required_argument, NULL, 's'},
      { "yes",     no_argument,       NULL, 'y'},
      { "verbose", no_argument,       NULL, 'v'},
      { "version", no_argument,       NULL, 22 },
      { NULL, 0, NULL, 0}
   };
   char *pv_name = NULL;
   pv_t *pv = NULL;
   pv_t pv_new;
   struct partition part;

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

   LVMTAB_CHECK;

   while ( ( c = getopt_long ( argc, argv, options,
                               long_options, NULL)) != EOF) {
      switch ( c) {
         DEBUG_HANDLE_CASE_D;

         case 'f':
            opt_f++;
            break;

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

         case 's':
            opt_s_size = lvm_check_number_ll ( optarg, TRUE);
            opt_s++;
            break;

         case 'v':
            opt_v++;
            break;

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

         case 'y':
            opt_y++;
            break;

         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\n", cmd);
      usage(LVM_EINVALID_CMD_LINE);
   }

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

   if ( opt_y > 0 && opt_f == 0) {
      fprintf ( stderr, "%s -- option y can only be given with option f\n",
                         cmd);
      usage(LVM_EINVALID_CMD_LINE);
   }

   LVM_CHECK_IOP;
   LVM_LOCK ( 0);

   for ( ; optind < argc; optind++) {
      force_needed = 0;
      pv_name = argv[optind];
   
      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",
                            cmd, pv_name);
         usage(LVM_EPVCREATE_PV_CHECK_NAME);
      }

      if ( opt_v > 0) printf ( "%s -- getting physical volume size\n", cmd);
      pv_size = pv_get_size_ll ( pv_name, &part);
      if ( pv_size < 0LL) {
         if ( pv_size == -LVM_EPV_GET_SIZE_PART) {
            fprintf ( stderr, "%s -- device \"%s\" has a partition table\n",
                              cmd, pv_name);
         } else if ( pv_size == -LVM_EPV_GET_SIZE_OPEN) {
            fprintf ( stderr, "%s -- can't open physical volume \"%s\" to "
                              "get its size\n",
                              cmd, pv_name);
         } else if ( pv_size == -LVM_EPV_GET_SIZE_NO_EXTENDED) {
            fprintf ( stderr, "%s -- extended partition \"%s\" doesn't exist\n",
                              cmd, pv_name);
         } else if ( pv_size == -LVM_EPV_GET_SIZE_LVM_DIR_CACHE) {
            fprintf ( stderr, "%s -- device \"%s\" doesn't exist\n",
                              cmd, pv_name);
         } else {
            fprintf ( stderr, "%s -- ERROR \"%s\" getting size of physical "
                              "volume \"%s\"\n",
                              cmd, lvm_error ( ( int) pv_size), pv_name);
         }
         fputc ( '\n', stderr);
         usage(LVM_EPVCREATE_PV_GET_SIZE);
      }

      if ( opt_s > 0) {
         if ( opt_s_size <= 0) {
	    fprintf ( stderr, "%s -- size parameter is invalid\n\n", cmd);
            usage(LVM_EINVALID_CMD_LINE);
         }
         printf ( "%s -- WARNING: overwriting the device size!\n", cmd);
         pv_size = opt_s_size * 2;
      }

      if ( opt_v > 0)
         printf ( "%s -- checking maximum physical volume size\n", cmd);
      if ( pv_size > LVM_PV_SIZE_MAX) pv_size--;
      if ( pv_size > LVM_PV_SIZE_MAX) {
         fprintf ( stderr, "%s -- size %Ld KB is larger than maximum "
                           "supported size %Ld KB\n",
                           cmd, pv_size / 2, LVM_PV_SIZE_MAX / 2);
         usage ( LVM_ELVCREATE_LV_SIZE);
      }
      size = pv_size;


      if ( (ret = pv_check_partitioned_whole(pv_name)) ) {
	  if (ret ==  -LVM_EPV_GET_SIZE_LVM_DIR_CACHE) {
	      fprintf ( stderr, "%s -- device \"%s\" doesn't exist\n",
			cmd, pv_name);
	  }
	  else if ( ret == -LVM_EPV_GET_SIZE_PART) {
	      fprintf ( stderr, "%s -- device \"%s\" has a partition table\n",
			cmd, pv_name);
	  }
         fputc ( '\n', stderr);
         usage(LVM_EPVCREATE_PV_GET_SIZE);
      }

      
      if ( opt_v > 0) printf ( "%s -- checking partition type\n", cmd);
      if ( part.sys_ind != 0) {
         if ( part.sys_ind == DOS_EXTENDED_PARTITION) {
            fprintf ( stderr, "%s -- can't use DOS extended partition "
                              "\"%s\"\n\n",
                              cmd, pv_name);
            return LVM_EPVCREATE_INVALID_ID;
#ifdef LINUX_SWAP_PARTITION
         } else if ( part.sys_ind == LINUX_SWAP_PARTITION) {
            fprintf ( stderr, "%s -- can't use Linux SWAP partition \"%s\"\n\n",
                              cmd, pv_name);
            return LVM_EPVCREATE_INVALID_ID;
#endif
#ifdef WIN98_EXTENDED_PARTITION
         } else if ( part.sys_ind == WIN98_EXTENDED_PARTITION) {
            fprintf ( stderr, "%s -- can't use Windows 98 extended partition "
                              "\"%s\"\n\n",
                              cmd, pv_name);
            return LVM_EPVCREATE_INVALID_ID;
#endif
         } else if ( part.sys_ind == LINUX_EXTENDED_PARTITION) {
            fprintf ( stderr, "%s -- can't use Linux extended partition "
                              "\"%s\"\n\n",
                              cmd, pv_name);
            return LVM_EPVCREATE_INVALID_ID;
         } else if ( part.sys_ind != LVM_PARTITION &&
                     part.sys_ind != LVM_NEW_PARTITION) {
            fprintf ( stderr, "%s -- invalid partition type 0x%x for \"%s\""
                              " (must be 0x%x)\n\n",
                              cmd, part.sys_ind, pv_name, LVM_NEW_PARTITION);
            return LVM_EPVCREATE_INVALID_ID;
         } else if ( part.sys_ind == LVM_PARTITION) {
            fprintf ( stderr, "%s -- please change old LVM partition type on "
                              "\"%s\" to 0x%x and retry\n\n",
                              cmd, pv_name, LVM_NEW_PARTITION);
            if ( opt_f < 2) return LVM_EPVCREATE_INVALID_ID;
         }
      }
   
      if ( ( ret = pv_read ( pv_name, &pv, NULL)) < 0 &&
           ret != -LVM_EPV_READ_MD_DEVICE) {
         if ( ret == -LVM_EPV_READ_PV_EXPORTED) {
            pv->vg_name[strlen(pv->vg_name)-strlen(EXPORTED)] = 0;
            fprintf ( stderr, "%s -- physical volume \"%s\" belongs to "
                              "exported volume group \"%s\"\n",
                               cmd, pv_name, pv->vg_name);
            force_needed++;
         } else force_needed = 0;
      } else {
         if ( opt_v > 0) printf ( "%s -- checking volume group name\n", cmd);
         if ( vg_check_name ( pv->vg_name) == 0 &&
              pv->vg_name[0] != 0) {
            if ( opt_f < 2) {
               fprintf ( stderr, "%s -- ERROR: can't initialize physical "
                                 "volume \"%s\" of volume group \"%s\" "
                                 "without -ff\n",
                                 cmd, pv_name, pv->vg_name);
               continue;
            } else {
               if ( opt_y == 0) {
                  c = 0;
                  while ( c != 'y' && c != 'n') {
                     if ( c == '\n' || c == 0)
                        printf ( "%s -- really INITIALIZE physical "
                                 "volume \"%s\" of volume group \"%s\" [y/n]? ",
                                 cmd, pv_name, pv->vg_name);
                     c = tolower ( getchar ());
                  }
                  c1 = c;
                  while ( c != '\n') c = tolower ( getchar ());
                  if ( c1 == 'y') {
                     force_needed = 0;
                  } else {
                     printf ( "%s -- physical volume \"%s\" not initialized\n",
                              cmd, pv_name);
                     continue;
                  }
               } else force_needed = 0;
            }
         }
      }

      if (pv == NULL) {
	  fprintf ( stderr, "%s -- invalid physical volume \"%s\"\n\n",
		    cmd, pv_name);
          return LVM_EPVCREATE_PV_GET_SIZE;
      }

      if ( opt_f == 0 && force_needed > 0) {
         printf ( "%s -- need -f to initialize physical volume \"%s\"\n",
                   cmd, pv_name);
         continue;
      }

      if ( pv_check_active ( pv->vg_name, pv_name) == TRUE) {
         fprintf ( stderr, "%s -- ERROR: can't force create on active physical "
                           "volume \"%s\"\n",
                           cmd, pv_name);
         continue;
      }

      if ( opt_f > 0) {
         printf ( "%s -- WARNING: forcing physical volume creation on \"%s\"",
                  cmd, pv_name);
         if ( pv_check_new ( pv) == FALSE) printf ( " of volume group \"%s\"",
                                                     pv->vg_name);
         printf ( "\n");
      }
   
      if ( lvm_tab_vg_check_exist ( pv->vg_name, NULL) == TRUE) {
         printf ( "%s -- removing lvmtab entry\n", cmd);
         if ( ( ret = lvm_tab_vg_remove ( pv->vg_name)) < 0) {
            fprintf ( stderr, "%s -- ERROR \"%s\" removing volume group \"%s\" "
                              "from \"%s\"\n\n",
                              cmd, lvm_error ( ret), pv->vg_name, LVMTAB);
            return LVM_EPVCREATE_VG_CHECK_EXIST;
         }
      }

      if ( opt_v > 0) printf ( "%s -- creating new physical volume\n", cmd);

      if ( opt_v > 0) printf ( "%s -- setting up physical volume for "
                               "%s with %u sectors\n",
                               cmd, pv_name, size);
      if ( ( ret = pv_setup_for_create ( pv_name, &pv_new, size)) < 0) {
         fprintf ( stderr, "%s -- ERROR \"%s\" setting up  physical "
                           "volume \"%s\"\n\n",
                           cmd, lvm_error ( ret), pv_name);
         return LVM_EPVCREATE_PV_SETUP;
      }
   
      lvm_dont_interrupt ( 0);
   
      if ( opt_v > 0) printf ( "%s -- writing physical volume data to "
                               "disk \"%s\"\n",
                               cmd, pv_name);
      if ( ( ret = pv_write ( pv_name, &pv_new)) == 0) {
         printf ( "%s -- physical volume \"%s\" successfully created\n",
                   cmd, pv_name);
      } else {
         fprintf ( stderr, "%s -- ERROR \"%s\" creating physical "
                           "volume \"%s\"\n\n",
                           cmd, lvm_error ( ret), pv_name);
         return LVM_EPVCREATE_PV_WRITE;
      }
      lvm_interrupt ();
   }

   lvm_interrupt ();
   LVM_UNLOCK ( 0);

   printf ( "\n");
   return 0;
}

