/*
 * tools/pvscan.c
 *
 * Copyright (C) 1997 - 2002  Heinz Mauelshagen, Sistina Software
 *
 * May, November 1997
 * May,June 1998
 * February,April,May,October 1999
 * February,March 2000
 * February 2002
 *
 * 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
 *
 *    08/10/1997 - changed some messages
 *    30/11/1997 -           "
 *    05/05/1998 - added multiple device support
 *    16/05/1998 - added lvmtab checking
 *    17/05/1998 - obsoleted physical volume name change (it's now in pv_read())
 *    18/05/1998 - fixed display bug for unused physical volumes
 *    22/05/1998 - changed display of physical volumes in case
 *                 of multiple devives
 *    06/08/1998 - LVMTAB_CHECK after command line options
 *    27/06/1998 - changed lvm_tab_* calling convention
 *    06/02/1999 - added options n and s
 *    21/02/1999 - removed LVM_LOCK and LVM_UNLOCK
 *    22/04/1999 - avoided LVM_CHECK_IOP
 *    09/05/1999 - added capacity totals
 *    06/10/1999 - implemented support for long options
 *    15/02/2000 - use lvm_error()
 *    17/03/2000 - added -u option to display UUIDs
 *    23/01/2001 - added call to lvm_init (JT)
 *    06/04/2001 - Tidy usage message and command-line options.
 *    07/02/2002 - fixes for > 1TB support
 *               - display volume group name of unknown VGs
 *
 */

#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 --  scan for physical volumes\n", cmd);
   }

   fprintf(stream, "\n%s "
           DEBUG_HELP_2
           "{-e|--exported | -n/--novolumegroup} "
           "[-h|--help]\n\t"
           "[-s|--short] "
           "[-u|--uuid] "
           "[-v[v]|--verbose [--verbose]] "
           "[--version]\n",
           cmd);
   exit(ret);
}

int main ( int argc, char **argv)
{
   int c = 0;
   int len = 0;
   int opt_e = 0;
   int opt_n = 0;
   int opt_s = 0;
   int opt_u = 0;
   int opt_v = 0;
   int p = 0;
   int new_pv = 0;
   int pv_max_name_len = 0;
   int ret = 0;
   int vg_name_len = 0;
   int vg_max_name_len = 0;
   unsigned long long total_cap = 0;
   unsigned long long new_cap = 0;
   char *dummy1 = NULL;
   char *dummy2 = NULL;
   char *dummy3 = NULL;
   char *options = "eh?nsuv" DEBUG_SHORT_OPTION;
   struct option long_options[] = {
      DEBUG_LONG_OPTION
      { "exported",      no_argument, NULL, 'e'},
      { "help",          no_argument, NULL, 'h'},
      { "novolumegroup", no_argument, NULL, 'n'},
      { "short",         no_argument, NULL, 's'},
      { "uuid",          no_argument, NULL, 'u'},
      { "verbose",       no_argument, NULL, 'v'},
      { "version",       no_argument, NULL, 22 },
      { NULL, 0, NULL, 0}
   };
   char pv_tmp_name[NAME_LEN] = { 0, };
   char vg_tmp_name[NAME_LEN] = { 0, };
   char vg_name_this[NAME_LEN] = { 0, };
   pv_t **pv = NULL;
   pv_t *pv_tmp;

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

   while ( ( c = getopt_long ( argc, argv, options,
                               long_options, NULL)) != EOF) {
      switch ( c) {
         DEBUG_HANDLE_CASE_D;

         case 'e':
            opt_e++;
            break;

         case 'h':
            usage ( 0);

         case 'n':
            opt_n++;
            break;

         case 's':
            opt_s++;
            break;

         case 'u':
            opt_u++;
            break;

         case 'v':
            opt_v++;
            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 '?':
            return LVM_EINVALID_CMD_LINE;
      }
   }

   if ( opt_n && opt_e) {
      fprintf ( stderr, "%s -- option e and n incompatible\n", cmd);
      return LVM_EINVALID_CMD_LINE;
   }
   /* Not until all of the commands are converted
   CMD_MINUS_CHK;
   */
   if ( optind < argc && *argv[optind] == '-') {
      fprintf ( stderr, "%s -- invalid command line\n", cmd);
      usage ( LVM_EINVALID_CMD_LINE);
   }
   LVM_CHECK_IOP;

   printf ( "%s -- reading all physical volumes (this may take a while...)\n",
            cmd);
   if ( ( ret = pv_read_all_pv ( &pv, TRUE)) < 0) {
      fprintf ( stderr, "%s -- ERROR \"%s\" reading physical volumes\n\n",
                        cmd, lvm_error ( ret));
      return LVM_EPVSCAN_PV_READ_ALL_PV;
   }


   if ( opt_e || opt_n)
      printf ( "%s -- WARNING: only displaying physical volumes %s\n", cmd,
               opt_e ? "of exported volume group(s)" : "in no volume group");

   if ( opt_v)
      printf ( "%s -- walking through all physical volumes found\n", cmd);

   /* check maximum vg and pv name length */
   if ( pv != NULL) for ( p = 0; pv[p] != NULL; p++) {
      len = strlen ( pv[p]->pv_name);
      if ( pv_max_name_len < len) pv_max_name_len = len;
      len = strlen ( pv[p]->vg_name);
      if ( vg_max_name_len < len) vg_max_name_len = len;
   }
   pv_max_name_len += 2;
   vg_max_name_len += 2;

   if ( pv != NULL) for ( p = 0; pv[p] != NULL; p++) {
      if ( ( ret = pv_read ( pv[p]->pv_name, &pv_tmp, NULL)) < 0 &&
           ret == -LVM_EPV_READ_MD_DEVICE) {
         if ( MAJOR ( pv_create_kdev_t ( pv[p]->pv_name)) != MD_MAJOR) {
            printf ( "%s -- WARNING: physical volume \"%s\" belongs "
                     "to a meta device\n",
                     cmd, pv[p]->pv_name);
         }
         if ( MAJOR ( pv[p]->pv_dev) != MD_MAJOR) continue;
      }

      total_cap += pv[p]->pv_size;

      /* short listing? */
      if ( opt_s > 0) {
         int out = 0;
         if ( opt_e > 0 && ret == -LVM_EPV_READ_PV_EXPORTED) out = 1;
         if ( opt_n > 0 && pv_check_new ( pv_tmp) == TRUE)   out = 1;
         if ( out == 1) printf ( "%s\n", pv[p]->pv_name);
         continue;
      } else {
         /* only exported PVs? */
         if ( opt_e > 0 && ret != -LVM_EPV_READ_PV_EXPORTED) continue;
         /* only free/new PVs? */
         if ( opt_n > 0 && pv_check_new ( pv_tmp) == FALSE) continue;
      }

      if ( pv_check_new ( pv[p]) == TRUE) {
         new_pv++;
         new_cap += pv[p]->pv_size;
      }

      if ( opt_v > 1) {
         printf ( "%s -- getting data for physical volume \"%s\" from kernel\n",
                  cmd, pv[p]->pv_name);
         if ( pv_status ( pv[p]->vg_name, pv[p]->pv_name, &pv_tmp) < 0)
            printf ( "%s -- physical volume \"%s\" is not active\n",
                     cmd, pv[p]->pv_name);
         pv_show ( pv[p]);
         printf ( "System Id             %s\n\n", pv[p]->system_id);
         printf ( "\n");
      } else {
         printf ( "%s -- ", cmd);
         if ( pv_check_active ( pv[p]->vg_name, pv[p]->pv_name) != TRUE)
            printf ( "inactive ");
         else
            printf ( "ACTIVE   ");
         vg_name_len = strlen ( pv[p]->vg_name) - sizeof ( EXPORTED) + 1;

         memset ( pv_tmp_name, 0, sizeof ( pv_tmp_name));
         if ( opt_u > 0) {
            snprintf ( pv_tmp_name, sizeof ( pv_tmp_name) - 1,
                                    "\"%-*s\" with UUID \"%s\"",
                                    pv_max_name_len-2,
                                    pv[p]->pv_name,
                                    lvm_show_uuid ( pv[p]->pv_uuid));
         } else {
            snprintf ( pv_tmp_name, sizeof ( pv_tmp_name) - 1,
                                    "\"%s\"", pv[p]->pv_name);
         }

         if ( pv_check_new ( pv[p]) == TRUE) {
            printf ( "PV %-*s is in no VG  [%s]\n",
                     pv_max_name_len, pv_tmp_name,
                     ( dummy1 = lvm_show_size ( pv[p]->pv_size / 2, SHORT)));
            free ( dummy1); dummy1 = NULL;
         } else if ( strcmp ( &pv[p]->vg_name[vg_name_len],
                              EXPORTED) == 0) {
            strncpy ( vg_name_this, pv[p]->vg_name, vg_name_len);
            printf ( "PV %-*s  is in EXPORTED VG \"%s\" [%s / %s free]\n",
                     pv_max_name_len, pv_tmp_name, vg_name_this,
                     ( dummy1 = lvm_show_size ( pv[p]->pe_total *
                                            pv[p]->pe_size / 2, SHORT)),
                     ( dummy2 = lvm_show_size ( ( pv[p]->pe_total - pv[p]->pe_allocated) * pv[p]->pe_size / 2, SHORT)));
            free ( dummy1); dummy1 = NULL;
            free ( dummy2); dummy2 = NULL;
         } else if ( vg_check_name ( pv[p]->vg_name) == 0 &&
                    lvm_tab_vg_check_exist ( pv[p]->vg_name, NULL) != TRUE) {
            printf ( "PV %-*s  is associated to unknown VG \"%s\" "
                     "(run vgscan)\n",
                     pv_max_name_len, pv_tmp_name, pv[p]->vg_name);
         } else {
            memset ( vg_tmp_name, 0, sizeof ( vg_tmp_name));
            snprintf ( vg_tmp_name, sizeof ( vg_tmp_name) - 1,
                                    "\"%s\"", pv[p]->vg_name);
            printf ( "PV %-*s of VG %-*s [%s / %s free]\n",
                     pv_max_name_len, pv_tmp_name, vg_max_name_len,
                     vg_tmp_name,
                     ( dummy1 = lvm_show_size ( pv[p]->pe_total *
                                            pv[p]->pe_size / 2, SHORT)),
                     ( dummy2 = lvm_show_size ( ( pv[p]->pe_total -
                                                  pv[p]->pe_allocated) *
                                                pv[p]->pe_size / 2, SHORT)));
            free ( dummy1); dummy1 = NULL;
            free ( dummy2); dummy2 = NULL;
         }
      }
   }

   if ( p == 0) printf ( "%s -- no valid physical volumes found\n\n", cmd);
   else {
      printf ( "%s -- total: %d [%s] / in use: %d [%s] / in no "
               "VG: %d [%s]\n\n",
               cmd, p,
               ( dummy1 = lvm_show_size ( total_cap / 2, SHORT)),
               p - new_pv,
               ( dummy2 = lvm_show_size ( ( total_cap - new_cap) / 2, SHORT)),
               new_pv,
               ( dummy3 = lvm_show_size ( new_cap / 2, SHORT)));
      free ( dummy1); free ( dummy2); free ( dummy3);
   }

   if ( p == 0) return LVM_EPVSCAN_NO_PV_FOUND;
   else         return 0;
}
