/*
 * tools/vgchange.c
 *
 * Copyright (C) 1997 - 2000  Heinz Mauelshagen, Sistina Software
 *
 * March-May,October 1997
 * May,June,September 1998
 * February,July,October,December 1999
 * February,July 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
 *
 *    11/10/1997 - added display of missing physical volumes
 *    12/10/1997 - added trying deactivation of inconsistent volume groups
 *    10/11/1997 - used lvm_tab
 *    09/05/1998 - implemented -x option
 *    16/05/1998 - added lvmtab checking
 *    27/06/1998 - changed lvm_tab_* calling convention
 *    20/09/1998 - changed message in deativation volume group
 *                 with open logical volumes
 *    03/08/1999 - avoided snapshot logical volumes during activation
 *    06/10/1999 - implemented support for long options
 *    25/12/1999 - check for vg_check_online_all_pv() error in case
 *                 pv_create_name_from_kdev_t() wasn't able to get a name,
 *                 because /proc isn't mounted
 *    15/02/2000 - use lvm_error()
 *    05/07/2000 - added reading copy on write exception table of snapshot
 *                 logical volumes using new function lv_read_COW_table()
 *
 */

#include <lvm_user.h>

char *cmd = NULL;

#ifdef DEBUG
int opt_d = 0;
#endif

int main ( int argc, char **argv) {
   int backup = FALSE;
   int c = 0;
   int l = 0;
   int l_dest = 0;
   int max_lv = 0;
   int p = 0;
   int ret = 0;
   int set_extendable = FALSE;
   int v = 0;
   char *dir = NULL;
   char *vg_name = NULL;
#ifdef DEBUG
   char *options = "A:a:dh?l:vx:";
#else
   char *options = "A:a:h?l:vx:";
#endif
   struct option long_options[] = {
      { "autobackup",    required_argument, NULL, 'A'},
      { "available",     required_argument, NULL, 'a'},
#ifdef DEBUG
      { "debug",         no_argument,       NULL, 'd'},
#endif
      { "help",          no_argument,       NULL, 'h'},
      { "logicalvolume", required_argument, NULL, 'l'},
      { "verbose",       no_argument,       NULL, 'v'},
      { "allocation",    required_argument, NULL, 'x'},
      { NULL, 0, NULL, 0}
   };
   char **vg_name_ptr = NULL;
   int opt_A = 1;
   int opt_A_set = 0;
   int opt_a = 0;
   int opt_a_action = 0;
   int opt_l = 0;
   int opt_v = 0;
   int opt_x = 0;
   int opt_x_action = 0;
   uint32_t pe_on_disk_base_sav;
   pv_t **pv_offl = NULL;
   pv_t **pv_incons = NULL;
   vg_t *vg = NULL;
   vg_t *vg_s = NULL;

   cmd = basename ( argv[0]);

   SUSER_CHECK;
   LVMTAB_CHECK;

   while ( ( c = getopt_long ( argc, argv, options,
                               long_options, NULL)) != EOF) {
      switch ( c) {
         case 'A':
            opt_A_set++;
            if ( opt_A > 1) {
               fprintf ( stderr, "%s -- A option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            opt_A++;
            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;

         case 'a':
            if ( opt_a > 0) {
               fprintf ( stderr, "%s -- a option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            opt_a++;
            if ( strcmp ( optarg, "y") == 0) {
               opt_a_action = VG_CREATE;
               break;
            } else if ( strcmp ( optarg, "n") == 0) {
               opt_a_action = ~VG_CREATE;
               break;
            }
            fprintf ( stderr, "%s -- a option argument \"%s\" invalid\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 -- Volume Group Change\n\n"
                     "Synopsis:\n"
                     "---------\n\n"
                     "%s\n"
                     "\t[-A/--autobackup y/n]\n"
                     "\t[-a/--available y/n [VolumeGroupName...]]\n"
#ifdef DEBUG
                     "\t[-d/--debug]\n"
#endif
                     "\t[-h/-?/--help]\n"
                     "\t[-l/--logicalvolume MaxLogicalVolumes]\n"
                     "\t[-v/--verbose]\n"
                     "\t[-x/--allocation y/n [VolumeGroupName...]]\n\n",
                     lvm_version, LVM_LIB_IOP_VERSION,  cmd, cmd);
            return 0;
            break;

         case 'l':
            if ( opt_l > 0) {
               fprintf ( stderr, "%s -- l option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            if ( ( max_lv = lvm_check_number ( optarg, FALSE)) > 0) {
               if ( max_lv < 1 || max_lv > ABS_MAX_LV) {
                  fprintf ( stderr, "%s -- MaxLogicalVolume has to be "
                                    "between 1 and %d\n\n",
                                    cmd, ABS_MAX_LV);
                  return LVM_EINVALID_CMD_LINE;
               }
               opt_l++;
            } else {
               fprintf ( stderr, "%s -- ERROR option l argument \"%s\"\n\n",
                                 cmd, optarg);
               return LVM_EINVALID_CMD_LINE;
            }
            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) {
               opt_x_action = VG_EXTENDABLE;
               break;
            } else if ( strcmp ( optarg, "n") == 0) {
               opt_x_action = ~VG_EXTENDABLE;
               break;
            }
            fprintf ( stderr, "%s -- a option argument \"%s\" invalid\n\n",
                      cmd, optarg);
            return LVM_EINVALID_CMD_LINE;
            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;
   CMD_CHECK_OPT_A_SET;


   if ( opt_a + opt_l + opt_x == 0) {
      fprintf ( stderr, "%s -- you have to give the -a, -l or the -x "
                        "option\n\n",
                        cmd);
      return LVM_EINVALID_CMD_LINE;
   }
   
   if ( opt_a + opt_l + opt_x > 1 ) {
      fprintf ( stderr, "%s -- you have to give only one option out of -a,"
                        " -l or -x\n\n",
                        cmd);
      return LVM_EINVALID_CMD_LINE;
   }
   
   if ( opt_a == 1 && opt_A > 1) {
      fprintf ( stderr, "%s -- -A option not neccessary with "
                        "-a option\n\n", cmd);
      return LVM_EINVALID_CMD_LINE;
   }
   
   LVM_CHECK_IOP;
   LVM_LOCK ( 0);

   if ( optind < argc) {
      if ( opt_v > 0) printf ( "%s -- using volume group(s) on command line\n",
                               cmd);
      vg_name_ptr = argv + optind;
      argc -= optind;
   } else {
      if ( opt_v > 0) printf ( "%s -- finding all volume group(s)\n", cmd);
      vg_name_ptr = lvm_tab_vg_check_exist_all_vg ();
      argc = 0;
      if ( vg_name_ptr != NULL)
         for ( v = 0; vg_name_ptr[v] != NULL; v++) argc++;
   }
   argv = vg_name_ptr;
   optind = 0;

   if ( optind == argc) {
      printf ( "%s -- no volume groups found\n\n", cmd);
      return 0;
   }

   /* work on all given/found volume groups */
   for ( ; optind < argc; optind++) {
      vg_name = argv[optind];

      if ( opt_v > 0) printf ( "%s -- checking volume group name \"%s\"\n",
                               cmd, vg_name);
      if ( vg_check_name ( vg_name) < 0) {
         fprintf ( stderr, "%s -- invalid volume group name \"%s\"\n",
                   cmd, vg_name);
         continue;
      }
   
      if ( opt_v > 0) printf ( "%s -- checking existence of volume "
                               "group \"%s\"\n",
                               cmd, vg_name);
      if ( ( ret = lvm_tab_vg_check_exist ( vg_name, NULL)) != TRUE) {
         if ( ret == -LVM_EPV_READ_PV_EXPORTED)
            fprintf ( stderr, "%s -- can't access exported volume "
                              "group \"%s\"\n",
                              cmd, vg_name);
         else if ( ret == -LVM_EVG_CHECK_EXIST_PV_COUNT)
            fprintf ( stderr, "%s -- ERROR: not all physical volumes of "
                              "volume group \"%s\" online\n", cmd, vg_name);
         else if ( ret == -LVM_EPV_READ_ALL_PV_OF_VG_NP)
            fprintf ( stderr, "%s -- volume group \"%s\" doesn't exist\n",
                      cmd, vg_name);
         else if ( ret == -LVM_EVG_READ_LVM_STRUCT_VERSION)
            fprintf ( stderr, "%s -- volume group \"%s\" has physical volumes "
                              "with invalid version\n",
                      cmd, vg_name);
         else
            fprintf ( stderr, "%s -- volume group \"%s\" does not exist\n",
                              cmd, vg_name);
         continue;
      }

      if ( opt_v > 0) printf ( "%s -- reading volume group data for \"%s\" "
                               "from lvmtab\n", cmd, vg_name);
      if ( vg != NULL) {
         vg_free ( vg, FALSE);
         vg = NULL;
      }

      if ( ( ret = lvm_tab_vg_read_with_pv_and_lv ( vg_name, &vg)) < 0) {
         if ( ret == -LVM_EVG_CFGRESTORE_OPEN) {
            fprintf ( stderr, "%s -- ERROR: volume group \"%s\" "
                              "doesn't exist\n",
                              cmd, vg_name);
         } else {
            fprintf ( stderr, "%s -- ERROR \"%s\" reading lvmtab data of "
                              "volume group \"%s\"\n",
                              cmd, lvm_error ( ret), vg_name);
         }
         continue;
      }
   
      if ( opt_v > 0) printf ( "%s -- checking volume group consistency "
                               " of \"%s\"\n", cmd, vg_name);
      if ( ( ret = vg_check_consistency_with_pv_and_lv ( vg)) < 0) {
         fprintf ( stderr, "%s -- ERROR \"%s\" volume group \"%s\" "
                           " is inconsistent\n",
                           cmd, lvm_error ( ret), vg_name);
         continue;
      }

      lvm_dont_interrupt ( 0);
   
      if ( opt_a > 0) {
         if ( opt_a_action & VG_CREATE) {
            if ( opt_v > 0) printf ( "%s -- checking if all physical volumes "
                                     "of volume group \"%s\" are available\n",
                                     cmd, vg_name);
            if ( ( ret = vg_check_online_all_pv ( vg, &pv_offl, &pv_incons)) < 0
                 &&  ret != -LVM_EPV_READ_MD_DEVICE) {
               if ( ret == -LVM_EPV_READ_PV_CREATE_NAME_FROM_KDEV_T) {
                     fprintf ( stderr, "%s -- ERROR: can't get name(s) of "
                                       "physical volumes\n"
                                       "%s -- Please check, if /proc "
                                       "is mounted\n",
                                       cmd, cmd);
               } else {
                  if ( pv_offl != NULL) for ( p = 0; pv_offl[p] != NULL; p++) {
                     fprintf ( stderr, "%s -- ERROR: physical volume \"%s\" of "
                                       "volume group \"%s\" is offline\n",
                                       cmd, pv_offl[p]->pv_name, vg_name);
                  }
                  if ( pv_incons != NULL) for ( p = 0; pv_incons[p] != NULL; p++) {
                     fprintf ( stderr, "%s -- ERROR: physical volume \"%s\" of "
                                       "volume group \"%s\" is inconsistent\n",
                                       cmd, pv_incons[p]->pv_name, vg_name);
                  }
                  printf ( "%s -- run vgscan\n", cmd);
               }
               if ( pv_offl   != NULL) free ( pv_offl);
               if ( pv_incons != NULL) free ( pv_incons);
               continue;
            }
      
            /* check for snapshot logical volumes and get the COW table data */
            for ( l = 0; l < vg->lv_max; l++) {
               if ( vg->lv[l] != NULL) {
                  vg->lv[l]->lv_block_exception = NULL;
                  vg->lv[l]->lv_remap_ptr = 0;
                  vg->lv[l]->lv_remap_end = 0;
                  vg->lv[l]->lv_snapshot_prev = NULL;
                  vg->lv[l]->lv_snapshot_next = NULL;

                  if ( vg->lv[l]->lv_access & LV_SNAPSHOT_ORG) {
                     vg->lv[l]->lv_snapshot_org = vg->lv[l];
                  }

                  if ( vg->lv[l]->lv_access & LV_SNAPSHOT) {
                     if ( ( ret = lv_setup_COW_table_for_create ( vg, vg->lv[lv_get_index_by_minor(vg, vg->lv[l]->lv_snapshot_minor)]->lv_name, l, LVM_SNAPSHOT_DEF_CHUNK)) < 0) {
                        fprintf ( stderr, "%s -- ERROR \"%s\" setting up snapshot "
                                          "copy on write exception table for \"%s\"\n\n",
                                          cmd, lvm_error ( ret), vg->lv[l]->lv_name);
                        return LVM_EVGCHANGE_LV_SETUP_COW_TABLE_FOR_CREATE;
                     }
                     if ( ( ret = lv_read_COW_table ( vg, vg->lv[l])) < 0) {
                        fprintf ( stderr, "%s -- ERROR %d reading copy on "
                                          "write exception table of \"%s\"\n",
                                          cmd, ret, vg->lv[l]->lv_name);
                        return LVM_EVGCHANGE_LV_READ_COW_TABLE;
                     }
                  } else {
                     vg->lv[l]->lv_snapshot_minor = 0;
                     vg->lv[l]->lv_chunk_size = 0;
                  }
               }
            }

            if ( opt_v > 0) printf ( "%s -- creating VGDA for \"%s\" "
                                     "in kernel\n",
                                     cmd, vg_name);
            if ( ( ret = vg_create ( vg_name, vg)) == -LVM_EVG_CREATE_REMOVE_OPEN) {
                  vg_create_dir_and_group ( vg);
                  ret = vg_create ( vg_name, vg);
            }

            if ( ret == 0)
               printf ( "%s -- volume group \"%s\" successfully activated\n",
                        cmd, vg_name);
            else {
#ifdef DEBUG
               debug ( "%s -- vg_create returned: %d\n", cmd, ret);
#endif
               if ( ret == -LVM_EVG_CREATE_REMOVE_OPEN) {
                  fprintf ( stderr, "%s -- can't open logical volume manager "
                                    "to activate volume group \"%s\"\n",
                                    cmd, vg_name);
               } else if ( ret == -EPERM) {
                  fprintf ( stderr, "%s -- volume group \"%s\" "
                                    "already active\n",
                                    cmd, vg_name);
               } else if ( ret == -ENOMEM) {
                  fprintf ( stderr, "%s -- memory error activating "
                                    "volume group \"%s\"\n",
                                    cmd, vg_name);
               } else {
                  fprintf ( stderr, "%s -- ERROR \"%s\" activating "
                                    "volume group \"%s\"\n",
                                    cmd, lvm_error ( ret), vg_name);
               }
               continue;
            }
         } else { /* VG_REMOVE */
            if ( opt_v > 0) printf ( "%s -- removing VGDA for volume "
                                     "group \"%s\" from kernel\n",
                                     cmd, vg_name);
            if ( ( ret = vg_remove ( vg_name)) == 0)
               printf ( "%s -- volume group \"%s\" successfully deactivated\n",
                        cmd, vg_name);
            else {
#ifdef DEBUG
               debug ( "%s -- vg_remove returned: %d\n", cmd, ret);
#endif
               if ( ret == -LVM_EVG_CREATE_REMOVE_OPEN) {
                  fprintf ( stderr, "%s -- can't open logical volume manager "
                                    "to deactivate \"%s\"?\n", cmd, vg_name);
               } else if ( ret == -EPERM) {
                  if ( opt_v > 0)
                     printf ( "%s -- getting volume group status from "
                              "VGDA in kernel\n", cmd);
                  if ( vg_status ( vg_name, &vg_s) < 0) {
                     fprintf ( stderr, "%s -- can't get status of volume group "
                                       "\"%s\" from kernel\n",
                                       cmd, vg_name);
                  } else {
                     fprintf ( stderr, "%s -- can't deactivate volume group "
                                       "\"%s\" with %d open logical volume%s\n",
                                       cmd, vg_name, vg_s->lv_open,
                                       vg_s->lv_open > 1 ? "s" : "");
                  }
               } else if ( ret == -ENXIO) {
                   fprintf ( stderr, "%s -- volume group \"%s\" isn't active\n",
                                     cmd, vg_name);
               } else {
                   fprintf ( stderr, "%s -- ERROR \"%s\" DEactivating volume "
                                     "group \"%s\"\n",
                                     cmd, lvm_error ( ret), vg_name);
               }
               continue;
            }
         }
      }

      backup = FALSE;
      if ( opt_x > 0) {
         if ( opt_x_action == VG_EXTENDABLE) {
            if ( vg->vg_status & VG_EXTENDABLE) {
               printf ( "%s -- volume group \"%s\" is extendable\n",
                        cmd, vg->vg_name);
            } else {
               vg->vg_status |= VG_EXTENDABLE;
               set_extendable = TRUE;
               backup = TRUE;
            }
         } else {
            if ( ! ( vg->vg_status & VG_EXTENDABLE)) {
               printf ( "%s -- volume group \"%s\" is not extendable\n",
                        cmd, vg_name);
            } else {
               vg->vg_status &= ~VG_EXTENDABLE;
               set_extendable = FALSE;
               backup = TRUE;
            }
         }
         if ( backup == TRUE) {
            /* store vg on disk(s) */
            if ( opt_v > 0) printf ( "%s -- storing volume group data of "
                                     "%s on disk(s)\n", cmd, vg_name);
            if ( ( ret = vg_write_with_pv_and_lv ( vg)) < 0) {
               fprintf ( stderr, "%s -- ERROR \"%s\"storing volume group data "
                                 "of \"%s\" on disk(s)\n\n",
                                 cmd, lvm_error ( ret), vg_name);
               return LVM_EVGCHANGE_VG_WRITE;
            }
      
            if ( vg_check_active ( vg_name) == TRUE) {
               if ( opt_v > 0) printf ( "%s -- changing VGDA in kernel\n", cmd);
               if ( set_extendable == TRUE) {
                  ret = vg_set_extendable ( vg_name);
               } else {
                  ret = vg_clear_extendable ( vg_name);
               }
               if ( ret < 0) {
                  fprintf ( stderr, "%s -- ERROR \"%s\" ",
                                    cmd, lvm_error ( ret));
                  if ( set_extendable == TRUE) {
                      fprintf ( stderr, "en");
                  } else {
                      fprintf ( stderr, "dis");
                 }
                 fprintf ( stderr, "abling extension of volume group \"%s\"\n\n",
                                   vg_name);
                 return LVM_EVGCHANGE_VG_EXTEND;
               }
            }

            if ( opt_v > 0) printf ( "%s -- changing lvmtab\n", cmd);
            dir = LVMTAB_DIR;
            if ( ( ret = vg_cfgbackup ( vg_name, dir, opt_v, vg)) == 0 &&
                 opt_A > 0) {
               printf ( "%s -- doing automatic backup of volume group \"%s\"\n",
                        cmd, vg_name);
               dir = VG_BACKUP_DIR;
               ret = vg_cfgbackup ( vg_name, dir, opt_v, vg);
            }
            if ( ret == 0) {
               printf ( "%s -- volume group \"%s\" successfully changed\n\n",
                        cmd, vg_name);
            } else {
               fprintf ( stderr, "%s -- ERROR \"%s\" storing volume group "
                                 "backup of \"%s\" in %s\n\n",
                                 cmd, lvm_error ( ret), vg_name, dir);
               return LVM_EVGCHANGE_VG_CFG_BACKUP;
            }
         }
      }

      /* change MaxLogicalVolume */
      if ( opt_l > 0) {
         if ( opt_v > 0) printf ( "%s -- checking for volume group \"%s\" "
                                  "activity\n", cmd, vg_name);
         if ( vg_check_active ( vg_name) == TRUE) {
            fprintf ( stderr, "%s -- MaxLogicalVolume can't be changed "
                              "in active volume group \"%s\"\n",
                              cmd, vg_name);
            continue;
         }

         if ( ! ( vg->vg_status & VG_EXTENDABLE)) {
            fprintf ( stderr, "%s -- volume group \"%s\"must be extendable "
                              "to change MaxLogicalVolume\n",
                              cmd, vg_name);
            continue;
         }

         if ( opt_v > 0) printf ( "%s -- checking if MaxLogicalVolume fits\n",
                                  cmd);
         if ( max_lv <= vg->lv_cur) {
            fprintf ( stderr, "%s -- MaxLogicalVolume is %s "
                              "the current logical volume number of \"%s\"\n",
                              cmd,
                              max_lv < vg->lv_cur ? "smaller than" : "equal to",
                              vg_name);
            continue;
         }

         if ( opt_v > 0) printf ( "%s -- rearanging VGDA structures of "
                                  "\"%s\"\n", cmd, vg_name);
         l_dest = 0;
         for ( l = 0; l < vg->lv_max; l++) {
            if ( vg->lv[l] == NULL) {
                l_dest = l;
            } else if ( vg->lv[l_dest] == NULL) {
               vg->lv[l_dest] = vg->lv[l];
               vg->lv[l] = NULL;
               vg->lv[l_dest]->lv_number = l_dest + 1;
               l_dest = l;
            }
         }
         for ( ; l < max_lv; l++) vg->lv[l] = NULL;

         vg->lv_max = max_lv;
         for ( p = 0; p < vg->pv_max; p++) {
            if ( vg->pv[p] != NULL) {
               vg->pv[p]->lv_on_disk.size = ( max_lv + 1) * sizeof ( lv_disk_t);
               pe_on_disk_base_sav = vg->pv[p]->pe_on_disk.base;
               LVM_PE_ON_DISK_BASE ( vg->pv[p]);
               vg->pv[p]->pe_on_disk.size -= vg->pv[p]->pe_on_disk.base -
                                             pe_on_disk_base_sav;
               if ( LVM_DISK_SIZE ( vg->pv[p]) / SECTOR_SIZE >
                    vg->pv[p]->pv_size - vg->pv[p]->pe_total * vg->pe_size) {
                  fprintf ( stderr, "%s -- extended VGDA would overlap "
                                    "first physical extent\n\n",
                                    cmd);
                  return LVM_EVGCHANGE_PE_OVERLAP;
               }
            }
         }

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

         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 of "
                                    "\"%s\"\n\n",
                                    cmd, lvm_error ( ret), vg_name);
                  return LVM_EVGCHANGE_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_EVGCHANGE_VG_CFGBACKUP_LVMTAB;
         }

         if ( opt_v > 0) printf ( "%s -- removing directory and nodes of "
                                  "volume group \"%s\"\n",
                                  cmd, vg_name);
         if ( ( ret = vg_remove_dir_and_group_and_nodes ( vg_name)) < 0) {
            fprintf ( stderr, "%s -- ERROR \"%s\" removing volume group "
                              "nodes of \"%s\"\n\n",
                              cmd, lvm_error ( ret), vg_name);
            return LVM_EVGCHANGE_VG_REMOVE_DIR_AND_GROUP_NODES;
         }
      
         if ( ( ret = vg_create_dir_and_group_and_nodes ( vg, opt_v)) < 0) {
            fprintf ( stderr, "%s -- ERROR \"%s\" creating volume group "
                              "nodes of \"%s\"\n\n",
                              cmd, lvm_error ( ret), vg_name);
            return LVM_EVGCHANGE_VG_CREATE_DIR_AND_GROUP_NODES;
         }
      } /* if ( opt_l > 0) */
      lvm_interrupt ();
   }

   putchar ( '\n');

   if ( vg != NULL) vg_free ( vg, FALSE);
   LVM_UNLOCK ( 0);

   return 0;
}
