/*
 * tools/lib/pv_read_all_pv.c
 *
 * Copyright (C) 1997 - 2002  Heinz Mauelshagen, Sistina Software
 *
 * March-May,October-November 1997
 * May,August,November 1998
 * September 1999
 * January 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
 *
 *    08/10/1997 - pv_read_all_pv(): added check for correct partition type
 *    11/10/1997 - pv_read_all_pv(): stop processing > 4 partitions if first
 *                                   4 are not configured
 *    05/05/1998 - avoided members of a MD in pv_read_all_pv()
 *               - extended pv_read_all_pv() to search /dev/dsk too
 *                 for future use
 *    16/05/1998 - canceled wrong physical volume device names by
 *                 inserting the correct ones at load time
 *               - changed algorithm in pv_read_all_pv() to avoid
 *                 multiple MD entries according
 *    15/11/1998 - avoided pv_read'ing all partition special files, if
 *                 disk doesn't exist
 *    02/09/1999 - avoided open optimization because it fails in non
 *                 standard environments
 *    22/09/1999 - added support for new partition identifier 0xDE
 *    29/10/1999 - fixed possible free() bug
 *    03/02/2000 - use debug_enter()/debug_leave()
 *    17/02/2002 - use pv_get_size_ll() for library compatibility
 *
 */

#include <liblvm.h>


int pv_read_all_pv ( pv_t ***pv, int reread) {
   int cache_size = 0;
   int n = 0;
   int np = 0;
   int p = 0;
   int p_sav1 = 0;
   int p_sav2 = 0;
   int pv_read_errno = 0;
   int ret = 0;
   char *dev_name = NULL;
   static int first = 0;
   struct partition partition;
   pv_t **pv_this_sav = NULL;
   static pv_t **pv_this = NULL;
   pv_t *pv_tmp = NULL;
   dir_cache_t *dir_cache = NULL;
   struct stat statbuf;

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

   if ( pv == NULL ||
        ( reread != TRUE && reread != FALSE)) {
      ret = -LVM_EPARAM;
      goto pv_read_all_pv_end;
   }

   *pv = NULL;

   if ( reread == TRUE) {
      if ( pv_this != NULL) {
         for ( p = 0; pv_this[p] != NULL; p++) free ( pv_this[p]);
         free ( pv_this);
         pv_this = NULL;
      }
      first = 0;
   }

   if ( first == 0) {
         debug ( "pv_read_all_pv -- calling lvm_dir_cache\n");
      if ( ( cache_size = lvm_dir_cache ( &dir_cache)) < 1) {
         ret = -LVM_EPV_READ_ALL_PV_LVM_DIR_CACHE;
         goto pv_read_all_pv_end;
      }

      np = 0;
      for ( n = 0; n < cache_size; n++) {
         dev_name = dir_cache[n].dev_name;
         debug ( "pv_read_all_pv -- calling stat with \"%s\"\n",
                  dev_name);
         if ( stat ( dev_name, &statbuf) == -1) continue;

         pv_read_errno = 0;
         if ( ( ret = pv_read ( dev_name, &pv_tmp, &pv_read_errno)) == 0 ||
              ret == -LVM_EPV_READ_MD_DEVICE ||
              ret == -LVM_EPV_READ_PV_EXPORTED) {
            memset ( &partition, 0, sizeof ( partition));
            if ( pv_get_size_ll ( dev_name, &partition) < 0ll) continue;
            if ( partition.sys_ind != 0 && 
                 partition.sys_ind != LVM_PARTITION &&
                 partition.sys_ind != LVM_NEW_PARTITION) continue;
            if ( pv_check_volume ( dev_name, pv_tmp) == TRUE) {
                debug ( "pv_read_all_pv: allocating for %s %s\n",
                         pv_tmp->pv_name, pv_tmp->vg_name);
               pv_this_sav = pv_this;
               if ( ( pv_this = realloc ( pv_this,
                                          ( np + 2) * sizeof ( pv_t*)))
                    == NULL) {
                  fprintf ( stderr, "realloc error in %s [line %d]\n",
                                    __FILE__, __LINE__);
                  for ( p = 0; pv_this_sav != NULL &&
                               pv_this_sav[p] != NULL; p++)
                     free ( pv_this_sav[p]);
                  ret = -LVM_EPV_READ_ALL_PV_MALLOC;
                  goto pv_read_all_pv_end;
               } else pv_this_sav = NULL;
               if ( ( pv_this[np] = malloc ( sizeof ( pv_t))) == NULL) {
                  fprintf ( stderr, "malloc error in %s [line %d]\n",
                                    __FILE__, __LINE__);
                  for ( p = 0; pv_this[p] != NULL; p++)
                     free ( pv_this[p]);
                  free ( pv_this);
                  pv_this = NULL;
                  ret = -LVM_EPV_READ_ALL_PV_MALLOC;
                  goto pv_read_all_pv_end;
               }
               memcpy ( pv_this[np], pv_tmp, sizeof ( pv_t));
               np++;
               pv_this[np] = NULL;
            }
#ifdef DEBUG
            else {
               debug ( "pv_read_all_pv -- device %s NOT used\n", dev_name);
            }
#endif
         } 
#ifdef DEBUG
         else debug ( "pv_read_all_pv -- pv_read returned: %d\n", ret);
#endif
      }
      first = 1;
      if ( np > 0 || ret == -LVM_EPV_READ_READ) ret = 0;
   }

#ifdef DEBUG
   debug ( "pv_read_all_pv -- avoiding multiple entries "
           "in case of MD; np: %d\n", np);
#endif

   /* FIXME: this doesn't work perfect in case of MD inactivity */
   /* clear out SCSI, IDE, ... doubles */
   for ( p = 0; pv_this != NULL && pv_this[p] != NULL; p++) {
      p_sav1 = p;
      for ( p = 0; pv_this[p] != NULL; p++) {
         if ( pv_this[p_sav1] != pv_this[p] && 
              strcmp ( pv_this[p_sav1]->vg_name, pv_this[p]->vg_name) == 0) {
            if ( pv_this[p_sav1]->pv_dev == pv_this[p]->pv_dev) {
               free ( pv_this[p]);
               pv_this[p] = NULL;
               if ( p < np) np--;
               p_sav2 = p - 1;
               p++;
               for ( ;  pv_this[p] != NULL; p++) {
                  pv_this[p-1] = pv_this[p];
                  pv_this[p] = NULL;
               }
               p = p_sav2;
            }
         }
      }
      p = p_sav1;
   }

   *pv = pv_this;

pv_read_all_pv_end:

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