/*
 * tools/pvchange.c
 *
 * Copyright (C) 1997 - 2000  Heinz Mauelshagen, Sistina Software
 *
 * May,November 1997
 * March,May,September,October 1998
 * 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/11/1997 - find VG after pv_read/pv_check_consistency
 *               - only change lvmtab/do backup in case of PV correction
 *    14/11/1997 - added clearing of PV namelist in case of PV name change
 *    15/11/1997 - implemented option a (all PV names)
 *    27/04/1998 - corrected bug with option -ac and no lvm driver/module
 *                 (LVM_LOCK called too late :-( )
 *    17/05/1998 - obsoleted physical volume name change (it's no in pv_read())
 *    26/05/1998 - fixed display bug of (not) done physical volumes
 *    04/09/1998 - corrected some messages
 *    06/10/1999 - implemented support for long options
 *    15/02/2000 - use lvm_error()
 *    23/01/2001 - added call to lvm_init (JT)
 *
 */

#include <lvm_user.h>

char *cmd = NULL;

#ifdef DEBUG
int opt_d = 0;
#endif

int main ( int argc, char **argv)
{
   int act = UNDEF;
   int back_it_up = FALSE;
   int c = 0;
   int change_msg = FALSE;
   int done = 0;
   int doit = FALSE;
   int not_done = 0;
   int opt_a = 0;
   int opt_A = 1;
   int opt_A_set = 0;
   int opt_v = 0;
   int opt_x = 0;
   int ret = 0;
#ifdef DEBUG
   char *options = "aA:dh?vx:";
#else
   char *options = "aA:h?vx:";
#endif
   struct option long_options[] = {
      { "all",        no_argument,       NULL, 'a'},
      { "autobackup", required_argument, NULL, 'A'},
#ifdef DEBUG
      { "debug",      no_argument,       NULL, 'd'},
#endif
      { "help",       no_argument,       NULL, 'h'},
      { "verbose",    no_argument,       NULL, 'v'},
      { "allocation", required_argument, NULL, 'x'},
      { NULL, 0, NULL, 0}
   };
   char *pv_name = NULL;
   char *vg_name = NULL;
   pv_t *pv = NULL;

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

   SUSER_CHECK;

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

         case 'A':
            opt_A_set++;
            if ( opt_A > 1) {
               fprintf ( stderr, "%s -- A option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            if ( strcmp ( optarg, "y") == 0);
            else if ( strcmp ( optarg, "n") == 0) opt_A = 0;
            else {
               fprintf ( stderr, "%s -- invalid option argument \"%s\"\n\n",
                                 cmd, optarg);
               return LVM_EINVALID_CMD_LINE;
            }
            break;

#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 'h':
         case '?':
            printf ( "\n%s (IOP %d)\n\n%s -- Physical Volume Change\n\n"
                     "Synopsis:\n"
                     "---------\n\n"
                     "%s\n"
                     "\t[-A/--autobackup y/n]\n"
#ifdef DEBUG
                     "\t[-d/--debug]\n"
#endif
                     "\t[-h/-?/--help]\n"
                     "\t[-v/--verbose]\n"
                     "\t[-a/--all]\n"
                     "\t[-x/--allocation y/n]\n"
                     "\t[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;

         case 'x':
            if ( opt_x > 0) {
               fprintf ( stderr, "%s -- x option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            opt_x++;
            if ( strcmp ( optarg, "y") == 0) { act = PV_ALLOCATABLE; break;}
            if ( strcmp ( optarg, "n") == 0) { act = 0; break;}
            fprintf ( stderr, "%s -- x option argument \"%s\" invalid\n\n",
                      cmd, optarg);
            return LVM_EINVALID_CMD_LINE;
            break;

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


   if ( opt_x == 0) {
      fprintf ( stderr, "%s -- please give the x option\n\n", cmd);
      return LVM_EINVALID_CMD_LINE;
   }

   LVM_CHECK_IOP;
   LVM_LOCK ( 0);

   if ( opt_a == 0) {
      if ( optind == argc) {
         fprintf ( stderr, "%s -- please give a physical volume path\n\n", cmd);
         return LVM_EPVCHANGE_PV_MISSING;
      }
   } else {
      if ( optind != argc) {
         fprintf ( stderr, "%s -- option a and PhysicalVolumePath are "
                           "exclusive\n\n", cmd);
         return LVM_EINVALID_CMD_LINE;
      }
      if ( opt_v > 0) printf ( "%s -- searching for physical volume names\n",
                               cmd);
      if ( ( argv = pv_find_all_pv_names ()) == NULL) {
         fprintf ( stderr, "%s -- can't find any physical volumes\n\n", cmd);
         return LVM_EPVCHANGE_PV_FIND_ALL_PV_NAMES;
      } else {
         optind = argc = 0;
         while ( argv[argc] != NULL) argc++;
      }
   }

   for ( ; optind < argc; optind++) {
      pv_name = argv[optind];
      change_msg = FALSE;
   
      if ( pv_check_name ( pv_name) < 0) {
         fprintf ( stderr, "%s -- physical volume name \"%s\" is invalid\n",
                           cmd, pv_name);
         continue;
      }

      if ( opt_v > 0) printf ( "%s -- reading physical volume data "
                               "%s from disk\n", cmd, pv_name);
      if ( ( ret = pv_read ( pv_name, &pv, NULL)) < 0 &&
             ret != -LVM_EPV_READ_MD_DEVICE) {
         if ( ret == -LVM_EPV_READ_LVM_STRUCT_VERSION)
            fprintf ( stderr, "%s -- physical volume \"%s\" has an invalid "
                              "version\n",
                              cmd, pv_name);
         else if ( ret == -LVM_EPV_READ_OPEN)
            fprintf ( stderr, "%s -- cannot open physical volume \"%s\"\n",
                      cmd, pv_name);
         else if ( ret == -LVM_EPV_READ_ID_INVALID)
            fprintf ( stderr, "%s -- physical volume \"%s\" has invalid "
                              "identity\n",
                              cmd, pv_name);
         else 
            fprintf ( stderr, "%s -- ERROR \"%s\" reading physical "
                              "volume \"%s\"\n",
                              cmd, lvm_error ( ret), pv_name);
         continue;
      }
   
      if ( opt_v > 0) printf ( "%s -- checking physical volume \"%s\" "
                               "consistency\n", cmd, pv_name);
      if ( ( ret = pv_check_consistency ( pv)) < 0) {
         fprintf ( stderr, "%s -- ERROR \"%s\" checking consistency of "
                           "physical volume \"%s\"\n\n",
                           cmd, lvm_error ( ret), pv_name);
         continue;
      }
   
      if ( opt_v > 0) printf ( "%s -- finding volume group of physical "
                               "volume \"%s\"\n", cmd, pv_name);
      if ( pv_find_vg ( pv_name, &vg_name) == FALSE) {
         printf ( "%s -- unable to find volume group of \"%s\" "
                  "(VG not active?)\n",
                   cmd, pv_name);
         doit = FALSE;
         continue;
      } else back_it_up = doit = TRUE;
   
      /* change allocatability for a PV */
      if ( opt_x > 0) {
         if ( act == PV_ALLOCATABLE &&
              ( pv->pv_allocatable & PV_ALLOCATABLE)) {
            fprintf ( stderr, "%s -- physical volume \"%s\" is allocatable\n",
                      cmd, pv_name);
            not_done++;
            continue;
         } else change_msg = TRUE;
   
         if ( act == 0 && ! ( pv->pv_allocatable & PV_ALLOCATABLE)) {
            fprintf ( stderr, "%s -- physical volume \"%s\" is "
                              "unallocatable\n", cmd, pv_name);
            not_done++;
            continue;
         } else change_msg = TRUE;
   
         if ( act == PV_ALLOCATABLE) {
            if ( opt_v > 0) printf ( "%s -- setting physical volume \"%s\" "
                                     "allocatable\n", cmd, pv_name);
            pv->pv_allocatable |= PV_ALLOCATABLE;
         } else {
            if ( opt_v > 0) printf ( "%s -- setting physical volume \"%s\" "
                                     "NOT allocatable\n", cmd, pv_name);
            pv->pv_allocatable &= ~PV_ALLOCATABLE;
         }
      }

      done++;
   
      if ( doit == TRUE) {
         lvm_dont_interrupt ( 0);
         if ( opt_v > 0) printf ( "%s -- checking physical volume \"%s\" "
                                  "activity\n", cmd, pv_name);
         if ( pv_check_active ( vg_name, pv_name) == TRUE) {
            if ( opt_v > 0) printf ( "%s -- changing physical volume "
                                     "%s in VGDA of kernel\n",
                                     cmd, pv_name);
            if ( ( ret = pv_change ( vg_name, pv)) != 0) {
               fprintf ( stderr, "%s -- ERROR \"%s\" unable to change physical "
                                 "volume \"%s\" in kernel\n",
                                 cmd, lvm_error ( ret), pv_name);
               return LVM_EPVCHANGE_PV_CHANGE;
            }
         } else if ( opt_v > 0) printf ( "%s -- physical volume \"%s\" "
                                         "inactive\n",
                                         cmd, pv_name);
   
         if ( opt_v > 0) printf ( "%s -- storing physical volume \"%s\"\n",
                                  cmd, pv_name);
         if ( ( ret = pv_write ( pv_name, pv)) != 0) {
            fprintf ( stderr, "%s -- ERROR \"%s\" unable to store physical "
                              "volume \"%s\"\n",
                              cmd, lvm_error ( ret), pv_name);
            return LVM_EPVCHANGE_PV_WRITE;
         }

         printf ( "%s -- physical volume \"%s\" ", cmd, pv_name);
         if ( change_msg == FALSE) printf ( "not ");
         printf ( "changed\n");
         lvm_interrupt ();
      }
   }

   if ( back_it_up == TRUE) {
      if ( opt_v > 0) printf ( "%s -- changing lvmtab\n", cmd);
      if ( vg_cfgbackup ( vg_name, LVMTAB_DIR, opt_v, NULL) == 0 &&
           opt_A > 0) {
         printf ( "%s -- doing automatic backup of volume group \"%s\"\n",
                  cmd, vg_name);
         vg_cfgbackup ( vg_name, VG_BACKUP_DIR, opt_v, NULL);
      } else {
         printf ( "%s -- WARNING: you don't have an automatic "
                  "backup of \"%s\"\n",
                  cmd, vg_name);
      }
   }

   lvm_interrupt ();
   LVM_UNLOCK ( 0);

   printf ( "%s -- %d physical volume%s"
            " changed / %d physical volume%s already o.k.\n\n",
            cmd, done, done != 1 ? "s" : "",
            not_done, not_done != 1 ? "s" : "");

   return 0;
}
