/*
 * tools/lib/lv_show.c
 *
 * Copyright (C) 1997 - 2000  Heinz Mauelshagen, Germany
 *
 * March 1997
 * June,July,September 1998
 * February,July,November 1999
 *
 *
 * 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
 *
 */

#include <liblvm.h>

void lv_show_colon ( lv_t *lv) {
#ifdef DEBUG
   debug ( "lv_show_colon -- CALLED\n");
#endif

   if ( lv == NULL) return;

   printf ( "%s:%s:%d:%d:%d:%d:%d:%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)
          );

#ifdef DEBUG
   debug ( "lv_show_colon -- LEAVING\n");
#endif
   return;
}


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

#ifdef DEBUG
   debug ( "lv_show -- CALLED\n");
#endif

   if ( lv == NULL) return;
   strncpy ( vg_name, vg_name_of_lv ( lv->lv_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 ( ( lv->lv_access & LV_SNAPSHOT_ORG) ||
           lv->lv_remap_ptr <= lv->lv_remap_end)
         printf ( "active ");
      else
         printf ( "INACTIVE ");
      if ( lv->lv_access & LV_SNAPSHOT_ORG) {
         vg_t *vg_core;
         if ( vg_check_active ( vg_name) == TRUE) {
            if ( ( ret = vg_status_with_pv_and_lv ( vg_name, &vg_core)) == 0) {
               printf ( "source of\n");
               for ( l = 0; l < vg_core->lv_max; l++) {
                  if ( vg_core->lv[l] != NULL) {
                     if ( vg_core->lv[l]->lv_snapshot_minor ==
                          MINOR ( lv->lv_dev)) {
                        printf ( "                      %s\n",
                                 vg_core->lv[l]->lv_name);
                     }
                  }
               }
               vg_free ( vg_core, TRUE);
            }
         }
      } else {
         printf ( "destination for ");
         for ( l = 0; l < vg->lv_max; l++) {
            if ( vg->lv[l] != NULL &&
                 MINOR ( vg->lv[l]->lv_dev) == lv->lv_snapshot_minor) {
               printf ( "%s\n", vg->lv[l]->lv_name);
               break;
            }
         }
      }

   }
   printf ( "LV Status             ");
   if ( ! ( lv->lv_status & LV_ACTIVE)) 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;
      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 %d%% [%s/%s]\n",
               lv_remap_ptr * 100 / lv->lv_remap_end,
               dummy, dummy1);
      free ( dummy);
      free ( dummy1);
   }

   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:

#ifdef DEBUG
   debug ( "lv_show -- LEAVING\n");
#endif
   return;
}


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

#ifdef DEBUG
   debug ( "lv_show_all_lv_of_vg -- CALLED\n");
#endif

   if ( vg == NULL) return;
   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");
      }
   }

#ifdef DEBUG
   debug ( "lv_show_all_lv_of_vg -- LEAVING\n");
#endif
   return;
}


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

#ifdef DEBUG
   debug ( "lv_show_current_pe -- CALLED\n");
#endif

   if ( lv == NULL ||
        lv->lv_current_pe == NULL) return;
   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);
   }

#ifdef DEBUG
   debug ( "lv_show_current_pe -- LEAVING\n");
#endif
   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;

#ifdef DEBUG
   debug ( "lv_show_current_pe_text -- CALLED\n");
#endif

   if ( lv == NULL ||
        lv->lv_current_pe == NULL) return -LVM_EPARAM;

   if ( ( ret = lvm_tab_vg_read_with_pv_and_lv ( vg_name_of_lv ( lv->lv_name),
                                                 &vg)) < 0) return ret;
   if ( ( l = lv_get_index_by_name ( vg, lv->lv_name)) < 0)
      return -LVM_ELV_SHOW_CURRENT_PE_TEXT_LV_INDEX;
   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++) {
      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;
      }
      printf ( "   %05d %-25s %05u  %-9d  %-9d\n",
               pe,
               pv_name,
               ( lv->lv_current_pe[pe].pe -
                 vg->pv[pv_get_index_by_kdev_t
                        ( vg, lv->lv_current_pe[pe].dev)]->pe_on_disk.base /
                 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);

#ifdef DEBUG
   debug ( "lv_show_current_pe_text -- LEAVING\n");
#endif
   return 0;
}
