/*
 * tools/vgrename.c
 *
 * Copyright (C) 1998 - 2000  Heinz Mauelshagen, Sistina Software
 *
 * May-June,September 1998
 * January,October 1999
 * February,September 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
 *
 *    27/06/1998 - changed lvm_tab_* calling convention
 *    05/09/1998 - corrected some messages
 *    26/01/1999 - made volume group directory prefix a preprocessor option
 *    28/01/1999 - fixed volume group/path too long in command line
 *    06/10/1999 - implemented support for long options
 *    15/02/2000 - use lvm_error()
 *    27/09/2000 - allow renaming of active volume groups using
 *                 new vg_rename() function
 *    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 c = 0;
   int l = 0;
   int length = 0;
   int p = 0;
   int opt_A = 1;
   int opt_A_set = 0;
   int opt_v = 0;
   int ret = 0;
   char *lv_name_ptr = NULL;
   char *vg_name_old = NULL;
   char *vg_name_new = NULL;
   char  vg_name_old_buf[NAME_LEN] = { 0, };
   char  vg_name_new_buf[NAME_LEN] = { 0, };
   vg_t *vg = NULL;
   struct stat stat_buf;

#ifdef DEBUG
   char *options = "A:dh?v";
#else
   char *options = "A:h?v";
#endif
   struct option long_options[] = {
      { "autobackup", required_argument, NULL, 'A'},
#ifdef DEBUG
      { "debug",      no_argument,       NULL, 'd'},
#endif
      { "help",       no_argument,       NULL, 'h'},
      { "verbose",    no_argument,       NULL, 'v'},
      { NULL,         0,                 NULL, 0},
   };

   /* lvm_init(argc, argv); */
   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;
            }
            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 -- Volume Group Rename\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"
                     "\tOldVolumeGroupPath NewVolumeGroupPath /\n"
                     "\tOldVolumeGroupName NewVolumeGroupName\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",
                      cmd, c);
            return LVM_EINVALID_CMD_LINE;
      }
   }

   CMD_MINUS_CHK;
   LVM_CHECK_IOP;
   LVM_LOCK ( 0);
   CMD_CHECK_OPT_A_SET;


   if ( argc - optind != 2) {
      fprintf ( stderr, "%s -- invalid command line\n\n", cmd);
      return LVM_EINVALID_CMD_LINE;
   }


   length = strlen ( LVM_DIR_PREFIX);
   if ( strlen ( vg_name_old = argv[optind]) >
        NAME_LEN - length - 2) {
      fprintf ( stderr, "%s -- the old logical volume path is longer than "
                        "the maximum of %d!\n\n",
                        cmd, NAME_LEN - length - 2);
      return LVM_EVGRENAME_VG_CHECK_NAME_OLD;
   }
   if ( strlen ( vg_name_new = argv[optind+1]) >
        NAME_LEN - length - 2) {
      fprintf ( stderr, "%s -- the new logical volume path is longer than "
                        "the maximum of %d!\n\n",
                        cmd, NAME_LEN - length - 2);
      return LVM_EVGRENAME_VG_CHECK_NAME_NEW;
   }

   if ( opt_v > 0) printf ( "%s -- checking old volume group name \"%s\"\n",
                            cmd, vg_name_old);
   if ( vg_check_name ( vg_name_old) < 0) {
      fprintf ( stderr, "%s -- ERROR: invalid old volume group name \"%s\"\n\n",
                        cmd, vg_name_old);
      return LVM_EVGRENAME_VG_CHECK_NAME_OLD;
   }

   if ( opt_v > 0) printf ( "%s -- checking new volume group name \"%s\"\n",
                            cmd, vg_name_new);
   if ( vg_check_name ( vg_name_new) < 0) {
      fprintf ( stderr, "%s -- ERROR: invalid new volume group name \"%s\"\n\n",
                        cmd, vg_name_new);
      return LVM_EVGRENAME_VG_CHECK_NAME_NEW;
   }

   if ( strncmp ( vg_name_old, LVM_DIR_PREFIX, length) != 0) {
      sprintf ( vg_name_old_buf, LVM_DIR_PREFIX "%s", vg_name_old);
      vg_name_old = vg_name_old_buf;
   }
   if ( strncmp ( vg_name_new, LVM_DIR_PREFIX, length) != 0) {
      sprintf ( vg_name_new_buf, LVM_DIR_PREFIX "%s", vg_name_new);
      vg_name_new = vg_name_new_buf;
   }

   if ( stat ( vg_name_new, &stat_buf) == 0) {
      lvm_show_filetype ( stat_buf.st_mode, vg_name_new);
      return LVM_EVGRENAME_VG_DIR_NEW;
   }

   if ( lstat ( vg_name_old, &stat_buf) < 0) {
      lvm_show_filetype ( stat_buf.st_mode, vg_name_old);
      return LVM_EVGRENAME_VG_DIR_OLD;
   }

   if ( opt_v > 0) printf ( "%s -- checking for inactivity of volume "
                            "group \"%s\"\n",
                            cmd, vg_name_old);
   if ( opt_v > 0) printf ( "%s -- checking volume group \"%s\" existence\n",
                            cmd, vg_name_old);
   if ( lvm_tab_vg_check_exist ( vg_name_old, &vg) != TRUE) {
      fprintf ( stderr, "%s -- ERROR: volume group \"%s\" doesn't exist\n\n",
                        cmd, vg_name_old);
      return LVM_EVGRENAME_VG_CHECK_EXIST_OLD;
   }

   if ( strcmp ( vg_name_old, vg_name_new) == 0) {
      fprintf ( stderr, "%s -- ERROR: volume group names "
                        "must be different\n\n", cmd);
      return LVM_EVGRENAME_VG_NAMES_IDENTICAL;
   }

   if ( opt_v > 0) printf ( "%s -- checking for nonexistence of new "
                            "volume group \"%s\"\n",
                            cmd, vg_name_new);
   if ( lvm_tab_vg_check_exist ( vg_name_new, &vg) == TRUE) {
      fprintf ( stderr, "%s -- volume group \"%s\" already exists\n\n",
                        cmd, vg_name_new);
      return LVM_EVGRENAME_VG_CHECK_EXIST_NEW;
   }

   if ( opt_v > 0) printf ( "%s -- reading data of volume group \"%s\" from "
                            "\"%s\"\n",
                            cmd, vg_name_old, LVMTAB);
   if ( ( ret = lvm_tab_vg_read_with_pv_and_lv ( vg_name_old, &vg)) != 0) {
      fprintf ( stderr, "%s -- ERROR \"%s\" getting data of volume "
                        "group \"%s\"\n\n",
                        cmd, lvm_error ( ret), vg_name_old);
      return LVM_EVGRENAME_VG_READ;
   }

   /* change the volume name in all structures */
   strcpy ( vg->vg_name, vg_name_new);

   for ( p = 0; p < vg->pv_max; p++)
      if ( vg->pv[p] != NULL) strcpy ( vg->pv[p]->vg_name, vg_name_new);

   for ( l = 0; l < vg->lv_max; l++) {
      if ( vg->lv[l] != NULL) {
         strcpy ( vg->lv[l]->vg_name, vg_name_new);
         if ( ( lv_name_ptr =
                   lv_change_vgname ( vg_name_new,
                                      vg->lv[l]->lv_name)) == NULL) {
            fprintf ( stderr, "%s -- a new logical volume path is longer "
                              "than the maximum of %d!\n",
                              cmd, NAME_LEN - 2);
            return LVM_EVGRENAME_VG_CHECK_NAME_NEW;
         }
         strcpy ( vg->lv[l]->lv_name, lv_name_ptr);
      }
   }
   /* end change the volume name in all structures */


   if ( opt_v > 0) printf ( "%s -- renaming volume group \"%s\"in kernel\n",
                            cmd, vg_name_old);
   if ( ( ret = vg_rename ( vg_name_old, vg_name_new)) < 0) {
      fprintf ( stderr, "%s -- ERROR \"%s\" renaming volume group \"%s\""
                        " in kernel\n\n", 
                        cmd, lvm_error ( ret), vg_name_old);
      return LVM_EVGRENAME_VG_RENAME;
   }

   /* store it on disks */
   if ( opt_v > 0) printf ( "%s -- storing VGDA 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 disks\n\n", 
                        cmd, lvm_error ( ret), vg_name_new);
      fprintf ( stderr, "%s -- removing new VGDA of \"%s\" from kernel\n",
                        cmd, vg_name_new);
      if ( ( ret = vg_remove ( vg_name_new)) < 0)
         fprintf ( stderr, "%s -- ERROR \"%s\" removing VGDA of \"%s\" "
                           "from kernel\n\n",
                           cmd, lvm_error ( ret), vg_name_new);
      return LVM_EVGRENAME_VG_REMOVE;
   }


   if ( opt_v > 0) printf ( "%s -- removing \"%s\" entry of \n"
                            "volume group \"%s\"\n",
                            cmd, LVMTAB, vg_name_old);
   if ( ( ret = lvm_tab_vg_remove ( vg_name_old)) < 0) {
      fprintf ( stderr, "%s -- ERROR \"%s\" removing volume group \"%s\" "
                        "from \"%s\"\n\n",
                        cmd, lvm_error ( ret), vg_name_old, LVMTAB);
      return LVM_EVGRENAME_LVM_TAB_VG_REMOVE;
   }

   if ( opt_v) printf ( "%s -- removing any invalid special files "
                        "for volume group \"%s\"\n", cmd, vg_name_old);
   if ( vg_remove_dir_and_group_and_nodes ( vg_name_old) < 0) {
      fprintf ( stderr, "%s -- ERROR removing volume group nodes and "
                        "directory of \"%s\"\n",
                        cmd, vg_name_old);
      return LVM_EVGRENAME_VG_REMOVE_DIR_AND_GROUP_NODES;
   }

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


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

   if ( ( ret = vg_cfgbackup ( vg_name_new, LVMTAB_DIR,
                               opt_v, vg)) == 0) {
      if ( opt_A > 0) {
         printf ( "%s -- doing automatic backup of volume group \"%s\"\n",
                  cmd, vg_name_new);
         if ( ( ret = vg_cfgbackup ( vg_name_new, VG_BACKUP_DIR,
                                     opt_v, vg)) < 0) {
            fprintf ( stderr, "%s -- ERROR \"%s\" writing VG backup\n\n",
                              cmd, lvm_error ( ret));
            return LVM_EVGRENAME_VG_CFGBACKUP;
         }
      } else {
         printf ( "%s -- WARNING: you don't have an automatic "
                  "backup of \"%s\"\n",
                  cmd, vg_name_new);
      }
   } else {
      fprintf ( stderr, "%s -- ERROR \"%s\" writing lvmtab\n\n",
                        cmd, lvm_error ( ret));
      return LVM_EVGRENAME_VG_CFGBACKUP_LVMTAB;
   }


   lvm_interrupt ();
   LVM_UNLOCK ( 0);

   printf ( "%s -- volume group \"%s\" successfully renamed to \"%s\"\n\n",
            cmd, vg_name_old, vg_name_new);

   return 0;
}
