/*
 * tools/pvdata.c
 *
 * Copyright (C) 1997 - 2000  Heinz Mauelshagen, Sistina Software
 *
 * March-May,October 1997
 * May,September 1998
 * February,October 1999
 * February,March 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 - used new pv_read_namelist ()
 *    09/05/1998 - extended V option output by pv_dev
 *    04/09/1998 - corrected some messages
 *    07/09/1998 - implemented with ne data layer functions
 *                 pv_copy_from_disk(), vg_copy_from_disk and lv_copy_from_disk
 *    21/02/1999 - remove LVM_LOCK and LVM_UNLOCK
 *    06/10/1999 - implemented support for long options
 *    15/02/2000 - use lvm_error()
 *    08/03/2000 - used new pv_read_uuidlist() instead of pv_read_namelist()
 *    17/03/2000 - changed namelist to uuidlist
 *    23/01/2001 - added call to lvm_init (JT)
 *    28/02/2001 - Copy pv_name into pv_core so we can read the uuidlist (PC)
 *               - used memset/strncpy to prohibit bad copy length
 *    19/03/2001 - Fix some typos in messages
 *    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 -- display physical volume debug data\n", cmd);
   }

   fprintf(stream, "%s "
	   "[-a|--all] "
#ifdef DEBUG
	   "[-d|--debug] "
#endif
	   "[-E|--physicalextent] "
	   "[-h|--help]\n\t"
	   "[-L|--logicalvolume] "
	   "[-P[P]|--physicalvolume [--physicalvolume]]\n\t"
	   "[-U|--uuidlist] "
	   "[-v[v]|--verbose [--verbose]] "
	   "[-V|--volumegroup]\n\t"
           "[--version] "
	   "PhysicalVolume [PhysicalVolume...]\n\n",
	   cmd);
   exit(ret);
}

int main ( int argc, char **argv) {
   int c = 0;
   int i = 0;
   int l = 0;
   int opt = 0;
   int opt_a = 0;
   int opt_E = 0;
   int opt_L = 0;
   int opt_U = 0;
   int opt_P = 0;
   int opt_v = 0;
   int opt_V = 0;
   int pv_handle = -1;
   int ret = 0;
   int size = 0;
#ifdef DEBUG
   char *options = "adEh?LPUvV";
#else
   char *options = "aEh?LPUvV";
#endif
   struct option long_options[] = {
      { "all",            no_argument, NULL, 'a'},
#ifdef DEBUG
      { "debug",          no_argument, NULL, 'd'},
#endif
      { "help",           no_argument, NULL, 'h'},
      { "logicalextent",  no_argument, NULL, 'L'},
      { "physicalextent", no_argument, NULL, 'E'},
      { "physicalvolume", no_argument, NULL, 'P'},
      { "uuidlist",       no_argument, NULL, 'U'},
      { "verbose",        no_argument, NULL, 'v'},
      { "volumegroup",    no_argument, NULL, 'V'},
      { "version",        no_argument, NULL, 22 },
      { NULL, 0, NULL, 0}
   };
   char *pv_name = NULL;
   char *pv_uuidlist = NULL;
   pv_disk_t pv;
   pv_t *pv_core = NULL;
   vg_disk_t vg;
   vg_t *vg_core = NULL;
   lv_disk_t lv;
   lv_t *lv_core = NULL;
   pe_disk_t *pe = NULL;
   pe_disk_t *pe_core = NULL;

   /* lvm_init(argc, argv); */
   cmd = basename ( argv[0]);

   SUSER_CHECK;

   while ( ( c = getopt_long ( argc, argv, options,
                               long_options, NULL)) != EOF) {
      switch ( c) {
         case 'a':
            opt_a++;
            opt++;
            opt_E++; opt_L++; opt_U++; opt_P++; opt_V++;
            break;

#ifdef DEBUG
         case 'd':
            opt_d++;
            opt++;
            break;
#endif

         case 'E':
            opt_E++;
            opt++;
            break;

         case 'h':
            opt++;
            usage(0);
            break;

         case 'L':
            opt_L++;
            opt++;
            break;

         case 'U':
            opt_U++;
            opt++;
            break;

         case 'P':
            opt_P++;
            opt++;
            break;

         case 'v':
            opt_v++;
            break;

         case 'V':
            opt_V++;
            opt++;
            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 argument %s\n\n", cmd, argv[optind]);
      usage(LVM_EINVALID_CMD_LINE);
   }

   if ( optind == argc) {
      fprintf ( stderr, "%s -- please enter a physical volume path\n\n", cmd);
      usage(LVM_EPVDATA_PV_MISSING);
   }

   if (opt == 0) {
      opt_L++; opt_U++; opt_P++; opt_V++;
   }

   if ( opt_E > 0 || opt_L > 0 || opt_U > 0 || opt_V > 0) opt_V++;

   for ( ; optind < argc; optind++) {
      pv_name = argv[optind];

      if ( pe != NULL) {
         free ( pe);
         pe = NULL;
      }

      if ( pv_handle != -1) close ( pv_handle);

      if ( ( pv_handle = open ( pv_name, O_RDONLY)) == -1) {
         fprintf ( stderr, "%s -- can't open physical volume \"%s\" readonly\n",
                           cmd, pv_name);
         continue;
      }

      /* physical volume */
      if ( opt_v > 1)
         printf ( "%s -- reading physical volume data from disk\n", cmd);
      if ( read ( pv_handle, &pv, sizeof ( pv)) != sizeof ( pv)) {
         fprintf ( stderr, "%s -- can't read data of physical volume \"%s\"\n",
                   cmd, pv_name);
	 continue;
      }
      pv_core = pv_copy_from_disk ( &pv);      
      memset(pv_core->pv_name, 0, NAME_LEN);
      strncpy(pv_core->pv_name, pv_name, NAME_LEN-1);
      if (opt_P) {
	 pv_show ( pv_core);
	 if ( opt_P > 1) {
	    printf ( "pv_dev                   %d:%d\n",
		     MAJOR ( pv_core->pv_dev), MINOR ( pv_core->pv_dev));
	    printf ( "system_id                %s\n",
		     pv_core->system_id);
	    printf ( "pv_on_disk.base          %u\n",
		     pv_core->pv_on_disk.base);
	    printf ( "pv_on_disk.size          %u\n",
		     pv_core->pv_on_disk.size);
	    printf ( "vg_on_disk.base          %u\n",
		     pv_core->vg_on_disk.base);
	    printf ( "vg_on_disk.size          %u\n",
		     pv_core->vg_on_disk.size);
	    printf ( "pv_uuidlist_on_disk.base %u\n",
		     pv_core->pv_uuidlist_on_disk.base);
	    printf ( "pv_uuidlist_on_disk.size %u\n",
		     pv_core->pv_uuidlist_on_disk.size);
	    printf ( "lv_on_disk.base          %u\n",
		     pv_core->lv_on_disk.base);
	    printf ( "lv_on_disk.size          %u\n",
		     pv_core->lv_on_disk.size);
	    printf ( "pe_on_disk.base          %u\n",
		     pv_core->pe_on_disk.base);
	    printf ( "pe_on_disk.size          %u\n\n",
		     pv_core->pe_on_disk.size);
	 }
	 putchar ( '\n');
      }

      /* volume group */
      if ( opt_V > 0) {
         if ( lseek ( pv_handle, pv_core->vg_on_disk.base, SEEK_SET) !=
              pv_core->vg_on_disk.base) {
            fprintf ( stderr, "%s -- can't seek to volume group struct "
                              "physical volume \"%s\"\n",
                              cmd, pv_name);
            continue;
         }

         if ( opt_v > 1)
	    printf ( "%s -- reading volume group data from disk\n", cmd);
         if ( read ( pv_handle, &vg, sizeof ( vg)) != sizeof ( vg)) {
            fprintf ( stderr, "%s -- can't read volume group data from "
                              "physical volume \"%s\"\n",
                              cmd, pv_name);
            continue;
         }
         vg_core = vg_copy_from_disk ( &vg);
         if ( opt_V > 1) vg_show ( vg_core);
      }

      /* logical volumes */
      if ( opt_L > 0) {
         printf ( "\n--- List of logical volumes ---\n\n");
         for ( l = 0; l < vg_core->lv_max; l++) {
            if ( opt_v > 1) printf ( "%s -- seeking to logical volume "
                                     "struct #%d on disk\n", cmd, l);
            if ( lseek ( pv_handle, LVM_LV_DISK_OFFSET ( pv_core, l), SEEK_SET)
                 != LVM_LV_DISK_OFFSET ( pv_core, l)) {
               fprintf ( stderr, "%s -- can't seek to logical volume struct "
                                 "#%d on physical volume \"%s\"\n",
                                 cmd, l, pv_name);
               continue;
            }

            if ( opt_v > 1) printf ( "%s -- reading logical volume "
                                     "struct #%d from disk\n", cmd, l);
            if ( read ( pv_handle, &lv, sizeof ( lv)) != sizeof ( lv)) {
               fprintf ( stderr, "%s -- can't read logical volume struct "
                                 "#%d from physical volume \"%s\"\n",
                                 cmd, l, pv_name);
               continue;
            }
            lv_core = lv_copy_from_disk ( &lv);
            if ( opt_v > 1) printf ( "%s -- checking consistency of "
                                     "logical volume #%d\n", cmd, l);
            if ( lv_core->vg_name[0] == 0 &&
                 lv_core->lv_name[0] == 0) {
               printf ( "%s -- logical volume struct at offset %3d "
                        "is empty\n", cmd, l);
               continue;
            }
            else if ( ( ret = lv_check_consistency ( lv_core)) < 0) {
               fprintf ( stderr,
                         "%s -- logical volume struct at offset %3d "
                         "is inconsistent\n", cmd, l);
               continue;
            }

            if ( opt_v > 1) printf ( "\n%s -- logical volume struct #%d\n",
                                     cmd, l);
            if ( opt_v > 0) {
               lv_show ( lv_core);
               printf ( "read_ahead: %d\n", lv_core->lv_read_ahead);
               printf ( "\n");
            } else {
               printf ( "%s -- logical volume \"%s\" at offset %3d\n",
                        cmd, lv_core->lv_name, l);
            }
            free ( lv_core); lv_core = NULL;
         }
      }

      /* physical extents */
      if ( opt_E > 0) {
         if ( opt_v > 1)
	    printf ( "%s -- seeking to physical extent structs on disk\n", cmd);
         if ( lseek ( pv_handle, pv_core->pe_on_disk.base, SEEK_SET)
              != pv_core->pe_on_disk.base) {
            fprintf ( stderr, "%s -- can't seek to physical extent structs on "
                              "physical volume \"%s\"\n",
                              cmd, pv_name);
            continue;
         }
         size = pv_core->pe_total * sizeof ( pe_disk_t);
         if ( ( pe = malloc ( size)) == NULL) {
            fprintf ( stderr, "%s -- malloc error in file \"%s\" [line %d]\n\n",
                              cmd, __FILE__, __LINE__);
            continue;
         }

         printf ( "\n--- List of physical extents ---\n\n");
         if ( opt_v > 1)
	    printf ( "%s -- reading physical extent structs from disk", cmd);
         if ( read ( pv_handle, pe, size) != size) {
            fprintf ( stderr, "%s -- can't read physical extent structs from "
                              "physical volume \"%s\"\n",
                              cmd, pv_name);
            continue;
         }
         pe_core = pe_copy_from_disk ( pe, pv_core->pe_total);
         for ( i = 0; i < pv_core->pe_total; i++) {
            printf ( "PE: %05d  LV: ", i);
            if ( pe_core[i].lv_num == 0) printf ( "---");
            else                         printf ( "%03d", pe_core[i].lv_num);
            printf ( "  LE: ");
            if ( pe_core[i].lv_num == 0) {
               if ( pe_core[i].le_num == 0) printf ( "-----\n");
               else                         printf ( "ERROR\n");
            } else printf ( "%05d\n", pe_core[i].le_num);
         }
         printf ( "\n");
      }

      /* physical volume uuid list */
      if ( opt_U > 0) {
         char *u;
         vg_core->pv[0] = pv_core;
         if ((ret = pv_read_uuidlist(pv_core, &pv_uuidlist)) < 0) {
            fprintf ( stderr, "%s -- ERROR \"%s\" reading physical volume uuid "
                              "list from physical volume \"%s\"\n",
                              cmd, lvm_error ( ret), pv_name);
            continue;
         }

         printf ( "--- List of physical volume UUIDs ---\n\n");
         for (i = 1, u = pv_uuidlist; i <= vg_core->pv_cur; i++, u += NAME_LEN){
            int len;
            printf ( "%03d: ", i);
	    if (lvm_check_uuid(u) == 0)
               printf("%s\n", lvm_show_uuid(u));
	    else if ((len = strlen(u)) > 0 && len < NAME_LEN)
               printf("%s (invalid UUID)\n", u);
            else
               printf("--- EMPTY ---\n");
         }
      }
   }

   printf ( "\n");
   return 0;
}
