/*
 * tools/lvmdiskscan.c
 *
 * Copyright (C) 1997 - 2002  Heinz Mauelshagen, Sistina Software
 *
 * May,June,November 1998
 * February-March,July-October 1999
 * 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
 *
 *    16/11/1998 - avoided checking partition special files, if
 *                 disk doesn't exist
 *    20/02/1999 - supported additional BSD partition type identifiers
 *    15/03/1999 - avoided LVMTAB_CHECK
 *    17/03/1999 - avoided loop device support
 *    17/07/1999 - fixed bug: MD was increased instead in case of NBD
 *               - added LVM/free info in case of whole disk
 *    15/08/1999 - fixed DAC960 bug
 *    02/09/1999 - avoided open optimization because it fails in non
 *                 standard environments
 *    22/09/1999 - added support for new partition identifier 0xDE
 *    06/10/1999 - implemented support for long options
 *    03/11/1999 - corrected wrong type of st_rdev from short to dev_t
 *                 and wromg partition count
 *                 (Thanks to Thomas Fehr <fehr@suse.de>)
 *    23/01/2001 - added call to lvm_init (JT)
 *    06/02/2002 - cope with changed pv_get_size() return cast
 *    07/02/2002 - fixes for > 1TB support
 *    17/02/2002 - use pv_get_size_ll() for library compatibility
 *
 */

#include <lvm_user.h>
#define	LVM_WHOLE_DISK( a) \
   (MAJOR(a) + ( MINOR ( a) - ( MINOR ( a) % lvm_partition_count ( a))))

char *cmd = NULL;

#ifdef DEBUG
int opt_d = 0;
#endif


int main ( int argc, char **argv)
{
   dev_t st_rdev = 0;
   int c = 0;
   int dir_cache_size = 0;
   int len = 0;
   int maxlen = 0;
   int opt_l = 0;
   int opt_v = 0;
   int n_disks = 0;
   int n_whole_disks = 0;
   int n_lvm_pvs = 0;
   int n_loop_devices = 0;
   int n_md_devices = 0;
   int n_nbd_devices = 0;
   int n_partitions = 0;
   int p = 0;
   long long pv_size = 0;
   ulong size = 0;
   int tst = -1;
   char *dummy = NULL;
   char fmt[32] = { 0, };
   char *options = "h?lv" DEBUG_SHORT_OPTION;
   struct option long_options[] = {
      DEBUG_LONG_OPTION
      { "help",         no_argument, NULL, 'h'},
      { "lvmpartition", no_argument, NULL, 'l'},
      { "verbose",      no_argument, NULL, 'v'},
      { NULL, 0, NULL, 0}
   };
   struct partition part;
   dir_cache_t *dir_cache = NULL;
   pv_t *pv = NULL;;

   /* 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 'h':
         case '?':
            printf ( "\n%s (IOP %d)\n\n%s -- Physical Volume Scan\n\n"
                     "Synopsis:\n"
                     "---------\n\n"
                     "%s\n"
                     DEBUG_HELP
                     "\t[-h/--help]\n"
                     "\t[-l/--lvmpartition]\n"
                     "\t[-v/--verbose]\n\n",
                     lvm_version, LVM_LIB_IOP_VERSION,  cmd, cmd);
            return 0;
            break;

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

   printf ( "%s -- reading all disks / partitions (this may take a while...)\n",
            cmd);
   if ( opt_v > 0) printf ( "%s -- filling directory cache...\n", cmd);
   if ( ( dir_cache_size = lvm_dir_cache ( &dir_cache)) <= 0) {
      fprintf ( stderr, "%s -- no disks / partitions found\n\n", cmd);
      return LVM_ELVMDISKSCAN_NO_FILES_FOUND;
   }

   if ( opt_v > 0) printf ( "%s -- walking through all found disks "
                            "/ partitions\n", cmd);
   for ( p = 0; p < dir_cache_size; p++) {
      len = strlen ( dir_cache[p].dev_name);
      if ( maxlen < len) maxlen = len;
   }
   memset ( fmt, 0, sizeof ( fmt));
   snprintf ( fmt, sizeof ( fmt) - 1,
                   "%s%d%s ", "%s -- %-", maxlen, "s [%14s]");

   if ( opt_l > 0) printf ( "%s -- only showing LVM partitions/disks...\n",
                            cmd);
   for ( p = 0; p < dir_cache_size; p++) {
      if ( ( tst = open ( dir_cache[p].dev_name, O_RDONLY)) == -1) {
         continue;
      } else {
         close ( tst);
         if ( lvm_check_partitioned_dev ( dir_cache[p].st_rdev) == TRUE) {
            if ( lvm_check_whole_disk_dev ( dir_cache[p].st_rdev) == TRUE)
               n_disks++;
            else
               n_partitions++;
         }
      }

      /* Skip partition entries in the directory cache in
         case we found a LVM whole disk before */
      if ( st_rdev > 0 && LVM_WHOLE_DISK ( st_rdev) ==
           LVM_WHOLE_DISK ( dir_cache[p].st_rdev)) continue;
      else st_rdev = 0;

      pv_size = pv_get_size_ll ( dir_cache[p].dev_name, &part);
      if ( pv_size < 0LL) continue;
      if ( pv_size > LVM_MAX_SIZE) {
         fprintf ( stderr, "%s -- size of \"%s\" is larger than 2TB\n\n",
                           dir_cache[p].dev_name, cmd);
         continue;
      }
      size = pv_size;

      if ( pv_check_partitioned_whole ( dir_cache[p].dev_name ) )
	  continue;
	  
      if ( opt_l > 0 &&
           part.sys_ind != LVM_PARTITION &&
           part.sys_ind != LVM_NEW_PARTITION) continue;

      if ( part.sys_ind != 0 &&
           pv_check_part ( dir_cache[p].dev_name) < 1) continue;

      printf ( fmt, cmd, dir_cache[p].dev_name,
               ( dummy = lvm_show_size ( size / 2, SHORT)));
      free ( dummy); dummy = NULL;

      if ( part.sys_ind == 0) {
         if ( pv_read ( dir_cache[p].dev_name, &pv, NULL) == 0) {
            if ( pv_check_new ( pv) == TRUE) printf ( "new ");
            else printf ( "USED ");
            printf ( "LVM ");
         } else printf ( "free ");
         
#ifdef LOOP_MAJOR
         if ( MAJOR ( dir_cache[p].st_rdev) == LOOP_MAJOR) {
            n_loop_devices++;
            printf ( "loop device");
         } else
#endif
#ifdef MD_MAJOR
         if ( MAJOR ( dir_cache[p].st_rdev) == MD_MAJOR) {
            n_md_devices++;
            printf ( "meta device");
         } else
#endif
#ifdef NBD_MAJOR
         if ( MAJOR ( dir_cache[p].st_rdev) == NBD_MAJOR) {
            n_nbd_devices++;
            printf ( "network block device");
         } else
#endif
         {
            n_whole_disks++;
            printf ( "whole disk");
            st_rdev = dir_cache[p].st_rdev;
         }
      } else {
         if ( opt_l == 0) {
            if ( lvm_check_extended_partition ( dir_cache[p].st_rdev) == TRUE)
               printf ( "Extended ");
            else if ( part.sys_ind != DOS_EXTENDED_PARTITION)
               printf ( "Primary  ");
         }
         if ( part.sys_ind == 0x6)
            printf ( "DOS 16bit");
         else if ( part.sys_ind == 0x83)
            printf ( "LINUX native partition");
         else if ( part.sys_ind == 0x64 || part.sys_ind == 0x65)
            printf ( "Novell Netware");
#ifdef	FREEBSD_PARTITION
         else if ( part.sys_ind == FREEBSD_PARTITION)
            printf ( "FREEBSD");
#elif	BSD_PARTITION
         else if ( part.sys_ind == BSD_PARTITION)
            printf ( "BSD");
#endif
#ifdef	OPENBSD_PARTITION
         else if ( part.sys_ind == OPENBSD_PARTITION)
            printf ( "OPENBSD");
#endif
#ifdef	NETBSD_PARTITION
         else if ( part.sys_ind == NETBSD_PARTITION)
            printf ( "NETBSD");
#endif
#ifdef	BSDI_PARTITION
         else if ( part.sys_ind == BSDI_PARTITION)
            printf ( "BSDI");
#endif
         else if ( part.sys_ind == LVM_PARTITION ||
                   part.sys_ind == LVM_NEW_PARTITION) {
            n_lvm_pvs++;
            if ( opt_l == 0) printf ( "LVM partition");
         } else if ( part.sys_ind == DOS_EXTENDED_PARTITION)
            printf ( "DOS extended partition");
         else if ( part.sys_ind == LINUX_EXTENDED_PARTITION)
            printf ( "LINUX extended partition");
#ifdef LINUX_SWAP_PARTITION
         else if ( part.sys_ind == LINUX_SWAP_PARTITION)
            printf ( "LINUX swap partition");
#endif
#ifdef WIN98_EXTENDED_PARTITION
         else if ( part.sys_ind == WIN98_EXTENDED_PARTITION)
            printf ( "Windows98 extended partition");
#endif
      }
      if ( opt_l == 0 && part.sys_ind != 0) printf(" [0x%02X]\n", part.sys_ind);
      else             putchar ( '\n');
   }

   if ( n_partitions == 0) {
      printf ( "%s -- no valid disks / partitions found\n"
               "%s -- please check your disk device special files!\n\n",
               cmd, cmd);
   } else {
      printf ( "%s -- %d disk%s\n",
               cmd, n_disks, n_disks == 0 || n_disks > 1 ? "s" : "");
      printf ( "%s -- %d whole disk%s\n",
               cmd, n_whole_disks,
               n_whole_disks == 0 || n_whole_disks > 1 ? "s" : "");
      printf ( "%s -- %d loop device%s\n",
               cmd, n_loop_devices,
               n_loop_devices == 0 || n_loop_devices > 1 ? "s" : "");
      printf ( "%s -- %d multiple device%s\n",
               cmd, n_md_devices,
               n_md_devices == 0 || n_md_devices > 1 ? "s" : "");
      printf ( "%s -- %d network block device%s\n",
               cmd, n_nbd_devices,
               n_nbd_devices == 0 || n_nbd_devices > 1 ? "s" : "");
      printf ( "%s -- %d partition%s\n",
               cmd, n_partitions,
               n_partitions == 0 || n_partitions  > 1 ? "s" : "");
      printf ( "%s -- %d LVM physical volume partition%s\n",
               cmd, n_lvm_pvs,
               n_lvm_pvs == 0 || n_lvm_pvs  > 1 ? "s" : "");
      printf ( "\n\n");
   }

   if ( n_disks + n_whole_disks + n_lvm_pvs + n_loop_devices +
        n_md_devices + n_nbd_devices + n_partitions == 0)
      return LVM_ELVMDISKSCAN_NO_DISKS_FOUND;

   return 0;
}
