/*
 * tools/vgscan.c
 *
 * Copyright (C) 1997 - 2000  Heinz Mauelshagen, Sistina Software
 *
 * Oktober  1997
 * January  1998
 * May-July,September 1998
 * May,July,October,November 1999
 * February,July 2000
 * January 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
 *
 *    11/01/1998 - added creation of volume group directory and special files
 *    03/05/1998 - enhanced error checking with lvm_tab_vg_insert()
 *                 and lvm_tab_vg_remove()
 *    27/06/1998 - changed lvm_tab_* calling convention
 *    10/07/1998 - implemented -A option
 *    02/09/1998 - corrected some messages
 *    19/05/1999 - major rewrite to correct multiple assigned volume
 *                 group numbers and block device specials
 *               - avoided option A
 *    04/08/1999 - support for snapshot logical volumes
 *    06/10/1999 - implemented support for long options
 *    30/11/1999 - delete special files and directory before
 *                 trying to create them
 *    12/02/2000 - use lvm_remove_recursive() instead of system ( "rm -fr ...
 *    15/02/2000 - use lvm_error()
 *    05/07/2000 - avoided resetting of snapshots
 *    05/01/2001 - reworded VGDA backup message (JT)
 *
 */

#include <lvm_user.h>

#define VGSCAN_DO_READ { \
   if ( opt_v > 0) printf ( "%s -- reading data of volume " \
                            "group \"%s\" from physical " \
                            "volume(s)\n", \
                            cmd, vg_name_ptr[i]); \
   if ( ( ret = vg_read_with_pv_and_lv ( vg_name_ptr[i], \
                                         &vg)) < 0) { \
      fprintf ( stderr, "%s -- ERROR \"%s\" can't get data of " \
                        "volume group \"%s\" from physical " \
                        "volume(s)\n", \
                        cmd, lvm_error ( ret), vg_name_ptr[i]); \
      continue; \
   } \
}

#define VGSCAN_DO_INSERT { \
   if ( opt_v > 0) printf ( "%s -- inserting \"%s\" into lvmtab\n", \
                            cmd, vg_name_ptr[i]); \
   if ( ( ret = lvm_tab_vg_insert ( vg_name_ptr[i])) < 0 && \
        ret != -LVM_ELVM_TAB_VG_INSERT_VG_EXISTS) { \
      fprintf ( stderr, "%s -- ERROR \"%s\" inserting volume group " \
                        "\"%s\" into \"%s\"\n", \
                        cmd, lvm_error ( ret), vg_name_ptr[i], LVMTAB); \
      return LVM_EVGSCAN_VG_INSERT; \
   } \
}


#define VGSCAN_DO_BACKUP_AND_CREATE_GROUP { \
   if ( opt_v > 0) printf ( "%s -- backing up volume group " \
                            "\"%s\"\n", \
                            cmd, vg_name_ptr[i]); \
   if ( ( ret = vg_cfgbackup ( vg_name_ptr[i], LVMTAB_DIR, \
                               opt_v, vg)) < 0) { \
      fprintf ( stderr, "%s -- ERROR: unable to do a backup " \
                        "of volume group \"%s\"\n", \
                        cmd, vg_name_ptr[i]); \
      if ( ( ret = lvm_tab_vg_remove ( vg_name_ptr[i])) < 0) { \
         fprintf ( stderr, "%s -- ERROR \"%s\" removing volume group " \
                           "\"%s\" from \"%s\"\n", \
                           cmd, lvm_error ( ret), vg_name_ptr[i], LVMTAB); \
      } \
      continue; \
   } \
   if ( opt_v > 0) printf ( "%s -- removing special files and directory " \
                            "for volume group \"%s\"\n", \
                            cmd, vg_name_ptr[i]); \
   if ( ( ret = vg_remove_dir_and_group_and_nodes ( vg->vg_name)) < 0) { \
      fprintf ( stderr, "%s -- ERROR \"%s\" removing volume group " \
                        "directory and special files\n", \
                        cmd, lvm_error ( ret)); \
   } \
   if ( ( ret = vg_create_dir_and_group_and_nodes ( vg, \
                                                    opt_v)) < 0) { \
      fprintf ( stderr, "%s -- ERROR \"%s\" creating volume group " \
                        "directory and special files\n", \
                        cmd, lvm_error ( ret)); \
      continue; \
   } \
}

char *cmd = NULL;

#ifdef DEBUG
int opt_d = 0;
#endif


int main ( int argc, char **argv)
{
   int blk_dev = 0;
   int blk_dev_free_count = 0;
   int c = 0;
   int i = 0;
   int l = 0;
   int must_store = FALSE;
   int opt_v = 0;
   int ret = 0;
#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},
   };
   char **vg_name_ptr = NULL;
   kdev_t *blk_dev_free = NULL;
   vg_t *vg = NULL;


   cmd = basename ( argv[0]);

   SUSER_CHECK;

   while ( ( c = getopt_long ( argc, argv, options,
                               long_options, NULL)) != EOF) {
      switch ( c) {
#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 Scan\n\n"
                     "Synopsis:\n"
                     "---------\n\n"
                     "%s\n"
#ifdef DEBUG
                     "\t[-d/--debug]\n"
#endif
                     "\t[-h/-?/--help]\n"
                     "\t[-v/--verbose]\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 ( 1);

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

   lvm_dont_interrupt ( 0);

   if ( opt_v > 0) printf ( "%s -- removing \"%s\" and \"%s\"\n",
                            cmd, LVMTAB, LVMTAB_DIR);
   unlink ( LVMTAB);
   lvm_remove_recursive ( LVMTAB_DIR);

   printf ( "%s -- reading all physical volumes (this may take a while...)\n",
            cmd);
   if ( ( vg_name_ptr = vg_check_exist_all_vg ()) == NULL) {
      if ( ( ret = lvm_tab_create ()) < 0)
         fprintf ( stderr, "%s -- ERROR \"%s\" creating lvmtab\n",
                           cmd, lvm_error ( ret));
      ret = LVM_EVGSCAN_NO_VG;
   } else {
      /* all active volume groups first so that i don't
         have to change the minor numbers in the active kernel VGDAs */
      if ( opt_v > 0) printf ( "%s -- scanning for all active volume "
                               "group(s) first\n", cmd);
      for ( i = 0; vg_name_ptr[i] != NULL; i++) {
         if ( vg_check_active ( vg_name_ptr[i]) == TRUE) {
            printf ( "%s -- found active volume group \"%s\"\n",
                     cmd, vg_name_ptr[i]);
            VGSCAN_DO_READ;
            VGSCAN_DO_INSERT;
            VGSCAN_DO_BACKUP_AND_CREATE_GROUP;
         }
      }

      /* deal with all volume groups except the active ones now */
      for ( i = 0; vg_name_ptr[i] != NULL; i++) {
         if ( vg_check_active ( vg_name_ptr[i]) != TRUE) {
            printf ( "%s -- found inactive volume group \"%s\"\n",
                     cmd, vg_name_ptr[i]);
            VGSCAN_DO_READ;
            if ( ( ret = lvm_tab_get_free_vg_number ()) < 0) {
               fprintf ( stderr, "%s -- ERROR: no free volume group "
                                 "numbers available\n\n",
                                 cmd);
               return LVM_EVGSCAN_LVMTAB;
            }
            vg->vg_number = ret;
      
            if ( ( blk_dev_free_count =
                   lvm_tab_get_free_blk_dev ( &blk_dev_free)) < 0) {
               fprintf ( stderr, "%s -- ERROR: no free block device "
                                 "specials available\n\n",
                                 cmd);
               return LVM_EVGSCAN_NO_DEV;
            }
            if ( blk_dev_free_count < vg->lv_cur) {
               fprintf ( stderr, "%s -- only %d free LVM block devices "
                                 "available vs. %d needed\n\n",
                                 cmd, blk_dev_free_count, vg->lv_cur);
               return LVM_EVGSCAN_NO_DEV;
            }

            must_store = FALSE;
            blk_dev = 0;
            for ( l = 0; l < vg->lv_max; l++) {
               if ( vg->lv[l] != NULL) {
                  if ( vg->lv[l]->lv_dev != blk_dev_free[blk_dev])
                     must_store = TRUE;
                  vg->lv[l]->lv_dev = blk_dev_free[blk_dev++];
               }
            }
            VGSCAN_DO_INSERT;
            VGSCAN_DO_BACKUP_AND_CREATE_GROUP;

            if ( must_store == 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_ptr[i]);
               if ( ( ret = vg_write_with_pv_and_lv ( vg)) < 0) {
                  fprintf ( stderr, "%s -- ERROR \"%s\" storing data of volume "
                                    "group \"%s\" on disk(s)\n\n",
                                    cmd, lvm_error ( ret), vg_name_ptr[i]);
                  return LVM_EVGSCAN_VG_WRITE;
               }
            }
            vg_free ( vg, FALSE);
         }
      }
   }

   lvm_interrupt ();
   LVM_UNLOCK ( 1);

   if ( ret == 0) {
      printf ( "%s -- \"%s\" and \"%s\" successfully created\n",
               cmd, LVMTAB, LVMTAB_DIR);
      printf ( "%s -- WARNING: This program does not do a VGDA backup "
	       "of your volume group%s\n\n", cmd, i > 1 ? "s" : "");
   } else if ( ret == LVM_EVGSCAN_NO_VG) {
      fprintf ( stderr, "%s -- no volume groups found\n\n", cmd);
   } else {
      fprintf ( stderr, "%s -- ERROR \"%s\" creating \"%s\" and \"%s\"\n\n",
                        cmd, lvm_error ( ret), LVMTAB, LVMTAB_DIR);
      ret = LVM_EVGSCAN_LVMTAB;
   }

   return ret;
}
