/*
 * tools/lvrename.c
 *
 * Copyright (C) 1997 - 2001  Heinz Mauelshagen, Sistina Software
 *
 * April-June,September 1998
 * January,February,October 1999
 * February,November 2000
 * March 2001
 *
 * 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
 *    04/09/1998 - corrected some messages
 *    26/01/1999 - made volume group directory prefix a preprocessor option
 *    28/01/1999 - fixed command line bug
 *               - fixed logical volume path too long in command line
 *    16/02/1999 - changed to bew lv_create_node()
 *    06/10/1999 - implemented support for long options
 *    15/02/2000 - use lvm_error()
 *    07/11/2000 - used LVM_VG_NAME environment variable
 *    23/01/2001 - added call to lvm_init (JT)
 *    05/04/2001 - fixed seg fault in case an invalid lv name is
 *                 entered as reported in bug #229 (HM)
 *    06/04/2001 - Tidy usage message and command-line options.
 *
 */

#include <lvm_user.h>

char *cmd = NULL;

#ifdef DEBUG
   int opt_d = 0;
#endif

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

   if (ret == 0) {
      stream = stdout;
      printf("%s (IOP %d)\n\n", lvm_version, LVM_LIB_IOP_VERSION);
      printf("%s --  rename a logical volume\n", cmd);
   }

   fprintf(stream, "\n%s "
           "[-A|--autobackup {y|n}] "
           DEBUG_HELP_2
           "[-h|--help] "
           "[-v|--verbose]\n\t"
           "[--version] "
           "{ OldLogicalVolumePath NewLogicalVolumePath |\n\t"
           "  VolumeGroupName OldLogicalVolumeName NewLogicalVolumeName }\n",
           cmd);
   exit(ret);
}


int main ( int argc, char *argv[]) {
   int c = 0;
   int l = 0;
   int opt_A = 1;
   int opt_A_set = 0;
   int opt_v = 0;
   int par_count = 0;
   int ret = 0;
   char buffer1[NAME_LEN];
   char buffer2[NAME_LEN];
   char *lv_name_old = NULL;
   char *lv_name_new = NULL;
   char *prefix = NULL;
   char vg_name[NAME_LEN] = { 0, };
   char lv_name_old_buf[NAME_LEN] = { 0, };
   char lv_name_new_buf[NAME_LEN] = { 0, };
   vg_t *vg = NULL;
   struct stat stat_buf;

   char *options = "A:h?v" DEBUG_SHORT_OPTION;
   struct option long_options[] = {
      { "autobackup", required_argument, NULL, 'A'},
      DEBUG_LONG_OPTION
      { "help",       no_argument,       NULL, 'h'},
      { "verbose",    no_argument,       NULL, 'v'},
      { "version",    no_argument,       NULL, 22},
      { NULL, 0, NULL, 0}
   };

   /* 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++;
            if ( optarg[0] == 'n') opt_A = 0;
            else if ( optarg[0] != 'y') {
               fprintf ( stderr, "%s -- invalid option A argument \"%s\"\n",
                                 cmd, optarg);
               usage ( LVM_EINVALID_CMD_LINE);
            }
            break;

         DEBUG_HANDLE_CASE_D;

         case 'h':
            usage ( 0);

         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 of the commands are converted
   CMD_MINUS_CHK;
   */
   if ( optind < argc && *argv[optind] == '-') {
      fprintf ( stderr, "%s -- invalid command line option %s\n",
		cmd, argv[optind]);
      usage ( LVM_EINVALID_CMD_LINE);
   }
   CMD_CHECK_OPT_A_SET;

   /* check alternate command line syntax */
   par_count = argc - optind;
   if ( par_count != 2 && par_count != 3) {
      fprintf ( stderr, "%s -- invalid command line\n", cmd);
      usage ( LVM_EINVALID_CMD_LINE);
   }

   if ( par_count == 2) {
      if ( strlen ( lv_name_old = argv[optind]) > NAME_LEN - 1) {
         fprintf ( stderr, "%s -- the old logical volume path is longer than "
                           "the maximum of %d!\n\n",
                           cmd, NAME_LEN - 1);
         usage ( LVM_ELVRENAME_LV_NAME);
      }
      if ( strlen ( lv_name_new = argv[optind+1]) > NAME_LEN - 1) {
         fprintf ( stderr, "%s -- the old logical volume path is longer than "
                           "the maximum of %d!\n\n",
                           cmd, NAME_LEN - 1);
         usage ( LVM_ELVRENAME_LV_NAME);
      }
      LVM_CHECK_DEFAULT_VG_NAME ( lv_name_old, buffer1, sizeof ( buffer1));
      LVM_CHECK_DEFAULT_VG_NAME ( lv_name_new, buffer2, sizeof ( buffer2));
      if ( lv_name_old == NULL) {
         fprintf ( stderr, "%s -- the old logical volume \"%s\" path "
                           "is invalid\n\n",
                           cmd, argv[optind]);
         usage ( LVM_ELVRENAME_LV_NAME);
      }
      if ( lv_name_new == NULL) {
         fprintf ( stderr, "%s -- the new logical volume \"%s\" path "
                           "is invalid\n\n",
                           cmd, argv[optind+1]);
         usage ( LVM_ELVRENAME_VG_NAME_DIFFER);
      }
   } else {
      if ( vg_check_name ( argv[optind]) < 0) {
         fprintf ( stderr, "%s -- the volume group name \"%s\" "
                           "is invalid\n\n",
                           cmd, argv[optind]);
         usage ( LVM_ELVRENAME_VG_NAME);
      }
      if ( strchr ( argv[optind+1], '/') != NULL) {
         fprintf ( stderr, "%s -- the old logical name \"%s\" "
                           "may not contain a \"/\"\n\n",
                           cmd, argv[optind+1]);
         usage ( LVM_ELVRENAME_LV_NAME);
      }
      if ( strchr ( argv[optind+2], '/') != NULL) {
         fprintf ( stderr, "%s -- the new logical name \"%s\" "
                           "may not contain a \"/\"\n\n",
                           cmd, argv[optind+2]);
         usage ( LVM_ELVRENAME_LV_NAME);
      }
      if ( strncmp ( argv[optind], LVM_DIR_PREFIX,
                     strlen ( LVM_DIR_PREFIX)) == 0) prefix = "";
      else                                           prefix = LVM_DIR_PREFIX;
      if ( strlen ( prefix) + strlen ( argv[optind]) +
           strlen ( argv[optind+1] + 1) > NAME_LEN - 1) {
         fprintf ( stderr, "%s -- the old logical volume path is longer than "
                           "the maximum of %d!\n",
                           cmd, sizeof ( lv_name_old_buf));
         usage ( LVM_ELVRENAME_LV_NAME);
      }
      memset ( lv_name_old_buf, 0, sizeof ( lv_name_old_buf));
      snprintf ( lv_name_old_buf, sizeof ( lv_name_old_buf) - 1,
                                  "%s%s/%s",
                                  prefix, argv[optind], argv[optind+1]);
      if ( strlen ( prefix) + strlen ( argv[optind]) +
           strlen ( argv[optind+2] + 1) > NAME_LEN - 1) {
         fprintf ( stderr, "%s -- the new logical volume path is longer than "
                           "the maximum of %d!\n",
                           cmd, sizeof ( lv_name_new_buf));
         usage ( LVM_ELVRENAME_LV_NAME);
      }
      memset ( lv_name_new_buf, 0, sizeof ( lv_name_new_buf));
      snprintf ( lv_name_new_buf, sizeof ( lv_name_new_buf) - 1,
                                  "%s%s/%s",
                                  prefix, argv[optind], argv[optind+2]);
      lv_name_old = lv_name_old_buf;
      lv_name_new = lv_name_new_buf;
   }

   if ( opt_v > 0) printf ( "%s -- checking old logical volume name\n", cmd);
   if ( lv_check_name ( lv_name_old) < 0) {
      fprintf ( stderr, "%s -- invalid logical volume name \"%s\"\n\n",
                        cmd, lv_name_old);
      usage ( LVM_ELVRENAME_LV_NAME);
   }

   if ( vg_name_of_lv ( lv_name_old) < 0 ) {
      fprintf ( stderr, "%s -- invalid logical volume name \"%s\"\n\n",
                        cmd, lv_name_old);
      usage ( LVM_ELVRENAME_LV_NAME);
   }

   strcpy ( vg_name, vg_name_of_lv ( lv_name_old));
   if ( opt_v > 0) printf ( "%s -- checking for existence of volume "
                            "group \"%s\"\n\n",
                            cmd, vg_name);
   if ( lvm_tab_vg_check_exist ( vg_name, NULL) != TRUE) {
      fprintf ( stderr, "%s -- volume group \"%s\" doesn't exist\n\n",
                        cmd, vg_name);
      usage ( LVM_ELVRENAME_LV_CHECK_EXIST_OLD);
   }

   if ( par_count == 2) {
      if ( opt_v > 0) printf ( "%s -- checking that volume group names "
                               "are equal\n\n", cmd);
      if ( strcmp ( vg_name, vg_name_of_lv ( lv_name_new)) != 0) {
         fprintf ( stderr, "%s -- volume group names entered may not "
                           "be different\n\n",
                           cmd);
         usage ( LVM_ELVRENAME_VG_NAME);
      }
   }

   if ( opt_v > 0) printf ( "%s -- checking that logical volume names "
                            "are different\n\n", cmd);
   if ( strcmp ( lv_name_old, lv_name_new) == 0) {
      fprintf ( stderr, "%s -- logical volume names may not be equal\n\n",
                        cmd);
      usage ( LVM_ELVRENAME_LV_NAME);
   }

   if ( opt_v > 0)
      printf ( "%s -- checking for existence of old logical volume\n", cmd);
   if ( lvm_tab_lv_check_exist ( lv_name_old) != TRUE) {
      fprintf ( stderr, "%s -- logical volume \"%s\" doesn't exist\n\n",
                        cmd, lv_name_old);
      return LVM_ELVRENAME_LV_CHECK_EXIST_OLD;
   }

   if ( opt_v > 0) printf ( "%s -- checking new logical volume name\n", cmd);
   if ( lv_check_name ( lv_name_new) < 0) {
      fprintf ( stderr, "%s -- invalid logical volume name \"%s\"\n\n",
                        cmd, lv_name_new);
      usage ( LVM_ELVRENAME_LV_NAME);
   }

   if ( opt_v > 0) printf ( "%s -- checking for nonexistence of new "
                            "logical volume\n", cmd);
   if ( lvm_tab_lv_check_exist ( lv_name_new) == TRUE) {
      fprintf ( stderr, "%s -- logical volume \"%s\" already exists\n\n",
                        cmd, lv_name_new);
      usage ( LVM_ELVRENAME_LV_CHECK_EXIST_NEW);
   }

   if ( lstat ( lv_name_new, &stat_buf) != -1) {
      lvm_show_filetype ( stat_buf.st_mode, lv_name_new);
      usage ( LVM_ELVRENAME_LSTAT);
   }

   if ( vg_check_active ( vg_name) != TRUE) {
      fprintf ( stderr, "%s -- volume group \"%s\" is inactive\n\n",
                        cmd, vg_name);
      return LVM_ELVRENAME_VG_CHECK_ACTIVE;
   }

   if ( vg_name_of_lv ( lv_name_new) < 0) {
      fprintf ( stderr, "%s -- invalid logical volume name \"%s\"\n\n",
                        cmd, lv_name_new);
      usage ( LVM_ELVRENAME_LV_NAME);
   }

   if ( strcmp ( vg_name, vg_name_of_lv ( lv_name_new)) != 0) {
      fprintf ( stderr, "%s -- volume group names are different\n\n", cmd);
      usage ( LVM_ELVRENAME_VG_NAME_DIFFER);
   }


   LVM_CHECK_IOP;
   LVM_LOCK ( 0);

   if ( opt_v > 0)
      printf ( "%s -- reading data of volume group \"%s\"\n", cmd, vg_name);
   if ( ( ret = lvm_tab_vg_read_with_pv_and_lv ( vg_name, &vg)) != 0) {
      fprintf ( stderr, "%s -- ERROR \"%s\" couldn't get data of volume "
                        "group \"%s\"\n\n",
                        cmd, lvm_error ( ret), vg_name);
      return LVM_ELVRENAME_VG_READ;
   }

   if ( ( l = lv_get_index_by_name ( vg, lv_name_old)) < 0) {
      fprintf ( stderr, "%s -- ERROR: couldn't get index of logical "
                        "volume \"%s\"\n\n", cmd, lv_name_old);
      return LVM_ELVRENAME_LV_GET_INDEX;
   }


   if ( opt_v > 0)
      printf ( "%s -- renaming logical volume in kernel\n", cmd);
   if ( ( ret = lv_rename ( lv_name_new, vg->lv[l])) < 0) {
      fprintf ( stderr, "%s -- ERROR \"%s\" renaming logical volume "
                        "\"%s\" to \"%s\" in kernel\n\n",
                        cmd, lvm_error ( ret), lv_name_old, lv_name_new);
      return LVM_ELVRENAME_LV_RENAME;
   }

   /* change the logical volume name */
   strcpy ( vg->lv[l]->lv_name, lv_name_new);

   /* store it on disks */
   if ( opt_v > 0)
      printf ( "%s -- storing logical volume 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);
      fprintf ( stderr, "%s -- removing new logical volume VGDA in kernel\n",
                cmd);
      if ( ( ret = lv_remove ( vg, vg->lv[l], lv_name_new)) < 0)
         fprintf ( stderr, "%s -- ERROR \"%s\" removing VGDA for logical "
                           "volume \"%s\" from kernel\n\n",
                           cmd, lvm_error ( ret), lv_name_new);
      return LVM_ELVRENAME_VG_WRITE;
   }

   unlink ( lv_name_old);

   if ( opt_v > 0) printf ( "%s -- creating device special file\n", cmd);
   if ( ( ret = lv_create_node ( vg->lv[l])) < 0) {
      if ( ret == -LVM_ELV_CREATE_NODE_MKNOD) {
         fprintf ( stderr, "%s -- logical volume node \"%s\" already exists\n",
                           cmd, lv_name_new);
      } else {
         fprintf ( stderr, "%s -- ERROR \"%s\" creating logical volume "
                           "node \"%s\"\n",
                           cmd, lvm_error ( ret), lv_name_new);
      }
      usage ( LVM_ELVRENAME_LV_CREATE_NODE);
   }

   if ( opt_v > 0) printf ( "%s -- changing lvmtab\n", cmd);
   if ( vg_cfgbackup ( vg_name, LVMTAB_DIR, opt_v, vg) == 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, vg);
   } else {
      printf ( "%s -- WARNING: you don't have an automatic backup of \"%s\"\n",
               cmd, vg_name);
   }

   lvm_interrupt ();
   LVM_UNLOCK ( 0);


   printf ( "%s -- logical volume \"%s\" successfully renamed to \"%s\"\n\n",
            cmd, lv_name_old, lv_name_new);
   return 0;
}
