/*
 * tools/lvscan.c
 *
 * Copyright (C) 1997 - 2000  Heinz Mauelshagen, Sistina Software
 *
 * May,June,September 1998
 * January,February,May,October 1999
 * 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
 *
 *    27/06/1998 - changed lvm_tab_* calling convention
 *    04/09/1998 - corrected some messages
 *    13/01/1999 - aborted in case of error
 *    20/02/1999 - added LV total message
 *    21/02/1999 - remove LVM_LOCK and LVM_UNLOCK
 *    10/05/1999 - added total capacity display
 *    06/10/1999 - implemented support for long options
 *    15/02/2000 - use lvm_error()
 *
 */


#include <lvm_user.h>

char *cmd = NULL;

#ifdef DEBUG
int opt_d = 0;
#endif

int main ( int argc, char **argv)
{
   int c = 0;
   int exit_code = 0;
   int l = 0;
   int lv_active = 0;
   int lv_total = 0;
   ulong lv_capacity_total = 0;
   int opt_b = 0;
   int opt_D = 0;
   int opt_v = 0;
   int ret = 0;
   int vg_total = 0;
   char *dummy = NULL;
#ifdef DEBUG
   char *options = "bdDh?v";
#else
   char *options = "bDh?v";
#endif
   struct option long_options[] = {
      { "blockdevice", no_argument, NULL, 'b'},
#ifdef DEBUG
      { "debug",       no_argument, NULL, 'd'},
#endif
      { "disk",        no_argument, NULL, 'D'},
      { "help",        no_argument, NULL, 'h'},
      { "verbose",     no_argument, NULL, 'v'},
      { NULL, 0, NULL, 0}
   };
   char *vg_name = NULL;
   vg_t *vg = NULL;

   cmd = basename ( argv[0]);

   SUSER_CHECK;
   LVMTAB_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 'b':
            if ( opt_b > 0) {
               fprintf ( stderr, "%s -- b option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            opt_b++;
            break;

         case 'D':
            if ( opt_D > 0) {
               fprintf ( stderr, "%s -- D option already given\n\n", cmd);
               return LVM_EINVALID_CMD_LINE;
            }
            opt_D++;
            break;

         case 'h':
         case '?':
            printf ( "\n%s (IOP %d)\n\n%s -- Logical Volume Scan\n\n"
                     "Synopsis:\n"
                     "---------\n\n"
                     "%s\n"
                     "\t[-b/--blockdevice]\n"
#ifdef DEBUG
                     "\t[-d/--debug]\n"
#endif
                     "\t[-D/--disk]\n"
                     "\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;

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

   argv = lvm_tab_vg_check_exist_all_vg ();
   argc = optind = 0;
   if ( argv != NULL) while ( argv[argc] != NULL) argc++;

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

   if ( opt_D > 0) printf ( "%s -- reading all physical volumes "
                            "(this may take a while...)\n", cmd);

   LVM_CHECK_IOP;

   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 -- \"%s\" is an invalid volume group name\n",
                   cmd, vg_name);
         return LVM_ELVSCAN_VG_CHECK_NAME;
      }

      if ( opt_v > 0) printf ( "%s -- checking volume group \"%s\" existence\n",
                               cmd, vg_name);
      if ( ( ret = lvm_tab_vg_check_exist ( vg_name, NULL)) == TRUE ||
           ret == -LVM_EVG_READ_VG_EXPORTED ||
           ret == -LVM_EVG_READ_LVM_STRUCT_VERSION) {
         if ( ret == -LVM_EVG_READ_VG_EXPORTED) {
            printf ( "%s -- WARNING: volume group \"%s\" is exported\n",
                     cmd, vg_name);
         }
         if ( opt_D == 0) {
            if ( opt_v > 0) printf ( "%s -- checking volume group "
                                     "\"%s\" activity\n", cmd, vg_name);
            if ( vg_check_active ( vg_name) == TRUE) {
               if ( opt_v > 0) printf ( "%s -- getting VGDA of volume "
                                        "group \"%s\" from kernel\n",
                                        cmd, vg_name);
               if ( ( ret = vg_status_with_pv_and_lv ( vg_name, &vg)) < 0) {
                  fprintf ( stderr,
                            "%s -- ERROR \"%s\" getting status of volume "
                            " group \"%s\"\n",
                             cmd, lvm_error ( ret), vg_name);
                  return LVM_ELVSCAN_VG_STATUS;
               }
            } else {
               printf ( "%s -- volume group \"%s\" is NOT active; try -D\n",
                        cmd, vg_name);
               continue;
            }
         } else { /* display from disks */
            if ( opt_v > 0) printf ( "%s -- reading volume group data of "
                                     "%s from disk(s)\n", cmd, vg_name);
            if ( ( ret = vg_read_with_pv_and_lv ( vg_name, &vg)) < 0 &&
                 ret != -LVM_EVG_READ_VG_EXPORTED) {
               exit_code = 1;
               if ( ret == -LVM_EVG_READ_LVM_STRUCT_VERSION) {
                  fprintf ( stderr,
                            "%s -- can't show volume group \"%s\" with "
                            "invalid physical volume version\n",
                             cmd, vg_name);
               } else {
                  fprintf ( stderr,
                            "%s -- ERROR \"%s\" getting data of volume "
                            "group \"%s\" form kernel\n",
                            cmd, lvm_error ( ret), vg_name);
               }
               continue;
            }
         }
         vg_total++;
         for ( l = 0; l < vg->lv_max; l++) {
            if ( vg->lv[l] != NULL &&
                 lv_check_name ( vg->lv[l]->lv_name) == 0) {
               printf ( "%s -- ", cmd);
               if ( lv_check_active ( vg_name, vg->lv[l]->lv_name) != TRUE || opt_D != 0)
                  printf ( "inactive");
               else {
                  printf ( "ACTIVE  ");
                  lv_active++;
               }
               if ( vg->lv[l]->lv_access & LV_SNAPSHOT_ORG)
                  printf ( "Original");
               else if ( vg->lv[l]->lv_access & LV_SNAPSHOT)
                  printf ( "Snapshot");
               else
                  printf ( "        ");
	       if ( vg->lv[l]->lv_access & LV_SNAPSHOT)
		 printf ( " \"%s\" [%s]",
			  vg->lv[l]->lv_name,
			  ( dummy = lvm_show_size ( vg->lv[l]->lv_remap_end * vg->lv[l]->lv_chunk_size/2,
						    SHORT)));
	       else		 
		 printf ( " \"%s\" [%s]",
			  vg->lv[l]->lv_name,
			  ( dummy = lvm_show_size ( vg->lv[l]->lv_size / 2,
						    SHORT)));


               free ( dummy); dummy = NULL;
               if ( vg->lv[l]->lv_allocation & LV_STRICT)
                  printf ( " strict");
               if ( vg->lv[l]->lv_allocation & LV_CONTIGUOUS)
                  printf ( " contiguous");
               if ( vg->lv[l]->lv_stripes > 1 && !(vg->lv[l]->lv_access & LV_SNAPSHOT))
                  printf ( " striped[%u]", vg->lv[l]->lv_stripes);
               if ( opt_b) printf ( " %d:%d",
                                    MAJOR ( vg->lv[l]->lv_dev),
                                    MINOR ( vg->lv[l]->lv_dev));
               printf ( "\n");
               lv_total++;
	       if ( vg->lv[l]->lv_access & LV_SNAPSHOT)
		 lv_capacity_total += vg->lv[l]->lv_remap_end * vg->lv[l]->lv_chunk_size;
	       else
		 lv_capacity_total += vg->lv[l]->lv_size;
            }
         }
      } 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);
         exit_code = 1;
      } else {
         fprintf ( stderr, "%s -- volume group \"%s\" not found\n",
                           cmd, vg_name);
         exit_code = 1;
      }
   }

   if ( lv_total == 0) printf ( "%s -- no logical volumes found\n\n", cmd);
   else {
      printf ( "%s -- %d logical volumes with %s total in %d volume group%s\n",
               cmd, lv_total,
               ( dummy = lvm_show_size ( lv_capacity_total / 2, SHORT)),
               vg_total,
               vg_total ==1 ? "" : "s");
      free ( dummy); dummy = NULL;
      printf ( "%s -- ", cmd);
      if ( lv_active > 0) printf ( "%d active", lv_active);
      if ( lv_active > 0 && lv_total - lv_active > 0) printf ( " / ");
      if ( lv_total - lv_active > 0) printf ( "%d inactive",
                                              lv_total - lv_active);
      printf ( " logical volumes\n\n");
   }
   return exit_code;
}
