/*
 * tools/lib/lv_show.c
 *
 * Copyright (C) 1997 - 2002  Heinz Mauelshagen, Sistina Software
 *
 * March 1997
 * June,July,September 1998
 * February,July,November 1999
 * January,July 2000
 * January,March 2001
 * February 2002
 *
 *
 * This LVM library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This LVM library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this LVM library; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA
 *
 */

/*
 * Changelog
 *
 *    30/04/1998 - changed LV number display starting from 1
 *    22/05/1998 - added free for lvm_show_size malloced memory
 *    27/06/1998 - changed to new lvm_tab_vg_read_with_pv_and_lv()
 *                 calling convention in lv_show_current_pe_text()
 *    05/07/1998 - output # of PVs in lv_show_current_pe_text()
 *    07/07/1998 - output # of I/Os since LV activation in
 *                 lv_show_current_pe_text()
 *    29/07/1998 - added PV i/o statistics in lv_show_current_pe_text()
 *    07/09/1998 - added vg_free() in lv_show_current_pe_text()
 *    18/02/1999 - added output of MAJOR:MINOR to lv_show()
 *    03/08/1999 - avoided current_pe output in case of snapshot logical volume
 *               - output snapshot logical volume status
 *    03/11/1999 - implemented lv_show_colon to generate colon seperated output
 *    31/01/2000 - use debug_enter()/debug_leave()
 *    02/07/2000 - don't display original logical volume in active
 *                 list in lv_show()
 *    06/01/2001 - display snapshot information correctly (PC)
 *    08/01/2001 - avoided snapshot_minor based search in lv_show() due
 *                 too enhancements in vg_status_with_pv_and_lv()
 *    05/03/2001 - fixed display for snapshot original to display
 *                 active / inactive correctly
 *    06/03/2001 - changed active check to make use of lv->lv_status member
 *                 rather than lv->lv_block_exception (HM)
 *    04/02/2002 - fix offset bug displaying PEs in
 *                 lv_show_current_pe_text() [HM]
 *
 */

#include <liblvm.h>

void lv_show_colon ( lv_t *lv) {

   debug_enter ( "lv_show_colon -- CALLED\n");

   if ( lv != NULL) {
      printf ( "%s:%s:%d:%d:%d:%d:%u:%d:%d:%d:%d:%d:%d\n",
               lv->lv_name,
               lv->vg_name,
               lv->lv_access,
               lv->lv_status,
               lv->lv_number,
               lv->lv_open,
               lv->lv_size,
               lv->lv_current_le,
               lv->lv_allocated_le,
               lv->lv_allocation,
               lv->lv_read_ahead,
               MAJOR ( lv->lv_dev),
               MINOR ( lv->lv_dev)
             );
   }

   debug_leave ( "lv_show_colon -- LEAVING\n");
   return;
}


void lv_show ( lv_t *lv) {
   int ret = 0;
   int lv_remap_ptr = 0;
   char *dummy = NULL;
   char *dummy1 = NULL;
   vg_t *vg = NULL;
   char vg_name[NAME_LEN] = { 0, };

   debug_enter ( "lv_show -- CALLED\n");

   if ( lv == NULL) goto lv_show_end;

   strncpy ( vg_name, lv->vg_name, NAME_LEN - 1);
   printf ( "--- Logical volume ---\n");
   printf ( "LV Name                %s\n", lv->lv_name);
   printf ( "VG Name                %s\n", lv->vg_name);
   printf ( "LV Write Access        ");
   if ( lv->lv_access & LV_WRITE) printf ( "read/write\n");
   else                           printf ( "read only\n");
   if ( lv->lv_access & ( LV_SNAPSHOT_ORG|LV_SNAPSHOT)) {
      if ( lvm_tab_vg_read_with_pv_and_lv ( vg_name, &vg) < 0){
         ret = -LVM_ELV_SHOW_VG_READ_WITH_PV_AND_LV;
         goto lv_show_end;
      }
      printf ( "LV snapshot status     ");
      if ( vg_check_active ( vg_name) == TRUE) {
         vg_t *vg_core;
         if ( ( ret = vg_status_with_pv_and_lv ( vg_name, &vg_core)) == 0) {
            lv_t *lv_ptr = vg_core->lv[lv_get_index_by_name ( vg_core, lv->lv_name)];
            if ( lv_ptr->lv_access & LV_SNAPSHOT) {
               if ( lv_ptr->lv_status & LV_ACTIVE)
                  printf ( "active ");
               else
                  printf ( "INACTIVE ");
            }
            if ( lv_ptr->lv_access & LV_SNAPSHOT_ORG) {
               printf ( "source of\n");
               while ( lv_ptr->lv_snapshot_next != NULL) {
                  lv_ptr = lv_ptr->lv_snapshot_next;
                  printf ( "                       %s [%s]\n",
                           lv_ptr->lv_name,
                           ( lv_ptr->lv_status & LV_ACTIVE) ? "active" : "INACTIVE");
               }
               vg_free ( vg_core, TRUE);
            } else {
               printf ( "destination for %s\n", lv_ptr->lv_snapshot_org->lv_name);
            }
         }
      } else {
         printf ( "INACTIVE ");
         if ( lv->lv_access & LV_SNAPSHOT_ORG)
            printf ( "original\n");
         else
            printf ( "snapshot\n");
      }
   }
   printf ( "LV Status              ");
   if ( ! ( lv->lv_status & LV_ACTIVE) || vg_check_active ( vg_name) != TRUE)
      printf ( "NOT ");
   printf ( "available\n");
   printf ( "LV #                   %u\n", lv->lv_number + 1);
   printf ( "# open                 %u\n", lv->lv_open);
#ifdef LVM_FUTURE
   printf ( "Mirror copies          %u\n", lv->lv_mirror_copies);
   printf ( "Consistency recovery   ");
   if ( lv->lv_recovery | LV_BADBLOCK_ON) printf ( "bad blocks\n");
   else                                   printf ( "none\n");
   printf ( "Schedule               %u\n", lv->lv_schedule);
#endif
   printf ( "LV Size                %s\n",
            ( dummy = lvm_show_size ( lv->lv_size / 2, SHORT)));
   free ( dummy);
   printf ( "Current LE             %u\n", lv->lv_current_le);
   printf ( "Allocated LE           %u\n", lv->lv_allocated_le);

   if ( lv->lv_access & LV_SNAPSHOT) {
      printf ( "snapshot chunk size    %s\n",
               ( dummy = lvm_show_size ( lv->lv_chunk_size / 2,
                                         SHORT)));
      free ( dummy); dummy = NULL;
      if ( lv->lv_remap_end > 0) {
         lv_remap_ptr = lv->lv_remap_ptr;
         if ( lv_remap_ptr > lv->lv_remap_end)
            lv_remap_ptr = lv->lv_remap_end;
         dummy  = lvm_show_size ( lv_remap_ptr *
                                  lv->lv_chunk_size / 2,
                                  SHORT);
         dummy1 = lvm_show_size ( lv->lv_remap_end *
                                  lv->lv_chunk_size / 2,
                                 SHORT);
         printf ( "Allocated to snapshot  %.2f%% [%s/%s]\n",
                  ( float) lv_remap_ptr * 100 / lv->lv_remap_end,
                  dummy, dummy1);
         free ( dummy);
         free ( dummy1);
         dummy = lvm_show_size ( ( vg->lv[lv_get_index_by_number ( vg, lv->lv_number)]->lv_size - lv->lv_remap_end * lv->lv_chunk_size) / 2, SHORT);
         printf ( "Allocated to COW-table %s\n", dummy);
         free ( dummy);
      }
   }

   if ( lv->lv_stripes > 1) {
      printf ( "Stripes                %u\n", lv->lv_stripes);
      printf ( "Stripe size (KByte)    %u\n", lv->lv_stripesize / 2);
   }
#ifdef LVM_FUTURE
   printf ( "Bad block             ");
   if ( lv->lv_badblock == LV_BADBLOCK_ON) printf ( "on\n");
   else                                    printf ( "off\n");
#endif
   printf ( "Allocation             ");
   if ( ! ( lv->lv_allocation & ( LV_STRICT|LV_CONTIGUOUS)))
      printf ( "next free");
   if ( lv->lv_allocation == LV_STRICT) printf ( "strict");
   if ( lv->lv_allocation == LV_CONTIGUOUS) printf ( "contiguous");
   if ( lv->lv_allocation == ( LV_STRICT|LV_CONTIGUOUS))
      printf ( "strict/contiguous");
   printf ( "\n");
   printf ( "Read ahead sectors     %u\n", lv->lv_read_ahead);
#ifdef LVM_FUTURE
   printf ( "IO Timeout (seconds)   ");
   if ( lv->lv_io_timeout == 0) printf ( "default\n\n");
   else                         printf ( "%lu\n\n", lv->lv_io_timeout);
#endif
   printf ( "Block device           %d:%d\n",
            MAJOR ( lv->lv_dev),
            MINOR ( lv->lv_dev));

lv_show_end:

   debug_leave ( "lv_show -- LEAVING\n");
   return;
}


void lv_show_all_lv_of_vg ( vg_t *vg) {
   int l = 0;

   debug_enter ( "lv_show_all_lv_of_vg -- CALLED\n");

   if ( vg != NULL) {
      if ( vg->lv_cur == 0) {
         printf ( "--- No logical volumes defined in \"%s\" ---\n\n", vg->vg_name);
         return;
      }
      for ( l = 0; l < vg->lv_max; l++) {
         if ( vg->lv[l] != NULL) {
            lv_show ( vg->lv[l]);
            printf ( "\n");
         }
      }
   }

   debug_leave ( "lv_show_all_lv_of_vg -- LEAVING\n");
   return;
}


void lv_show_current_pe ( lv_t *lv) {
   uint p = 0;

   debug_enter ( "lv_show_current_pe -- CALLED\n");

   if ( lv != NULL &&
        lv->lv_current_pe != NULL) {
      for ( ; p < lv->lv_allocated_le; p++) {
         printf ( "dev: %02d:%02d   le: %d   pe: %u\n",
                  MAJOR ( lv->lv_current_pe[p].dev),
                  MINOR ( lv->lv_current_pe[p].dev),
                  p,
                  lv->lv_current_pe[p].pe);
      }
   }

   debug_leave ( "lv_show_current_pe -- LEAVING\n");
   return;
}


int lv_show_current_pe_text ( lv_t *lv) {
   int l = 0;
   int p = 0;
   int pe = 0;
   int pv_count = 0;
   int ret = 0;
   uint32_t sum_reads = 0;
   uint32_t reads = 0;
   uint32_t sum_writes = 0;
   uint32_t writes = 0;
   char *pv_name = NULL;
   kdev_t dev = 0;
   vg_t *vg = NULL;

   debug_enter ( "lv_show_current_pe_text -- CALLED\n");

   if ( lv == NULL ||
        lv->lv_current_pe == NULL) {
      ret = -LVM_EPARAM;
      goto lv_show_current_pe_text_end;
   }

   if ( ( ret = lvm_tab_vg_read_with_pv_and_lv ( lv->vg_name, &vg)) < 0)
      goto lv_show_current_pe_text_end;

   if ( ( l = lv_get_index_by_name ( vg, lv->lv_name)) < 0) {
      ret = -LVM_ELV_SHOW_CURRENT_PE_TEXT_LV_INDEX;
      goto lv_show_current_pe_text_end;
   }

   for ( p = 0; p < vg->pv_cur; p++) {
      if ( ( ret = lv_check_on_pv ( vg->pv[p], l + 1)) == TRUE) pv_count++;
   }
   printf ( "   --- Distribution of logical volume on %d physical "
            "volume%s  ---\n"
            "   PV Name                  PE on PV     reads      writes\n",
            pv_count, pv_count > 1 ? "s": "");
   sum_reads = sum_writes = 0;
   for ( p = 0; p < vg->pv_cur; p++) {
      if ( lv_check_on_pv ( vg->pv[p], l + 1) == TRUE) {
         reads = writes = 0;
         for ( pe = 0; pe < lv->lv_allocated_le; pe++) {
            if ( vg->pv[p]->pv_dev == lv->lv_current_pe[pe].dev) {
               reads  += lv->lv_current_pe[pe].reads;
               writes += lv->lv_current_pe[pe].writes;
            }
         }
         sum_reads  += reads;
         sum_writes += writes;
         printf ( "   %-24s %-10d   %-9d  %-9d\n",
                  vg->pv[p]->pv_name,
                  lv_count_pe ( vg->pv[p], l + 1),
                  reads, writes);
      }
   }

   printf ( "\n   --- logical volume i/o statistic ---\n"
            "   %d reads  %d writes\n", sum_reads, sum_writes);

   printf ( "\n   --- Logical extents ---\n"
            "   LE    PV                        PE     reads      writes\n");
   dev = 0;
   for ( pe = 0; pe < lv->lv_allocated_le; pe++) {
      int pv_index;

      if ( lv->lv_current_pe[pe].dev != dev) {
         pv_name = pv_create_name_from_kdev_t ( lv->lv_current_pe[pe].dev);
         dev = lv->lv_current_pe[pe].dev;
      }
      pv_index = pv_get_index_by_kdev_t ( vg, lv->lv_current_pe[pe].dev);
      printf ( "   %05d %-25s %05u  %-9u  %-9u\n",
               pe,
               pv_name,
               ( lv->lv_current_pe[pe].pe -
                 LVM_VGDA_SIZE(vg->pv[pv_index]) /
                 SECTOR_SIZE) /
                 ( lv->lv_size / lv->lv_allocated_le),
                 lv->lv_current_pe[pe].reads,
                 lv->lv_current_pe[pe].writes);
   }
   vg_free ( vg, FALSE);

lv_show_current_pe_text_end:

   debug_leave ( "lv_show_current_pe_text -- LEAVING with ret: %d\n", ret);
   return ret;
}
