/*
 * tools/lib/pv_read_all_pv_of_vg.c
 *
 * Copyright (C) 1997 - 2000  Heinz Mauelshagen, Sistina Software
 *
 * March-May,October-November 1997
 * May,August,November 1998
 * January,March,April,September,October 2000
 *
 *
 * 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
 *
 *    03/02/2000 - use debug_enter()/debug_leave()
 *    04/04/2000 - enhanced to find physical volumes on UUID base
 *                 rather than on device special name
 *    20/09/2000 - WORKAROUND: avoid dual access pathes for now (2.4.0-test8)
 *    30/10/2000 - reworked to fix UUID related bug
 *
 */

#include <liblvm.h>


int pv_read_all_pv_of_vg ( char *vg_name, pv_t ***pv, int reread) {
   int i = 0;
   int id = 0;
   int p = 0;
   int pp = 0;
   int np = 0;
   int pv_number = 0;
   int ret = 0;
   int uuids = 0;
   static int first = 0;
   char *pv_uuid_list = NULL;
   static char vg_name_sav[NAME_LEN] = { 0, };
   pv_t **pv_tmp = NULL;
   static pv_t **pv_this = NULL;
   pv_t **pv_this_sav = NULL;
   
#ifdef DEBUG
   debug_enter ( "pv_read_all_pv_of_vg -- CALLED with vg_name: \"%s\"\n",
                 vg_name);
#endif

   if ( pv == NULL || vg_name == NULL ||
        ( reread != TRUE && reread != FALSE) ||
        vg_check_name ( vg_name) < 0) {
      ret = -LVM_EPARAM;
      goto pv_read_all_pv_of_vg_end;
   }

   *pv = NULL;

   if ( strcmp ( vg_name_sav, vg_name) != 0) {
      strcpy ( vg_name_sav, vg_name);
      reread = TRUE;
   }

   if ( reread == TRUE) {
      if ( pv_this != NULL) {
         free ( pv_this);
         pv_this = NULL;
      }
      first = 0;
   }

   if ( first == 0) {
      if ( ( ret = pv_read_all_pv ( &pv_tmp, FALSE)) < 0)
         goto pv_read_all_pv_of_vg_end;
   
      /* first physical volume who's volume group name fits
         starts work on PV UUID list */
      for ( p = 0; pv_tmp[p] != NULL; p++) {
         if ( strcmp ( pv_tmp[p]->vg_name, vg_name) == 0 &&
              pv_check_consistency ( pv_tmp[p]) == 0) {
            uuids = pv_read_uuidlist ( pv_tmp[p]->pv_name, &pv_uuid_list);
            break;
         }
      }

      /* pass to find the number of PVs in this group  anid
         to prefil the pointer array */
      for ( p = 0; pv_tmp[p] != NULL; p++) {
         if ( strncmp ( pv_tmp[p]->vg_name, vg_name, NAME_LEN) == 0) {
            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__);
               ret = -LVM_EPV_READ_ALL_PV_OF_VG_MALLOC;
               if ( pv_this_sav != NULL) free ( pv_this_sav);
               goto pv_read_all_pv_of_vg_end;
            }
            pv_this[np] = pv_tmp[p];
            pv_this[np+1] = NULL;
            np++;
         }
      }

      /* in case this PV already holds a uuid list: check against this list */
      if ( uuids > 0) {
         for ( p = 0; pv_this[p] != NULL; p++) {
            for ( id = 0; id < uuids; id++) {
               if ( memcmp ( pv_this[p]->pv_uuid,
                             &pv_uuid_list[id*NAME_LEN],
                             UUID_LEN) == 0) goto uuid_check_end;
            }
            pv_this[p] = NULL;
uuid_check_end:
         }
         for ( pp = 0; pp < p - 2; pp++) {
            if ( pv_this[pp] == NULL) {
               pv_this[pp] = pv_this[pp+1];
               pv_this[pp+1] = NULL;
            }
         }
         np = 0;
         while ( pv_this[np] != NULL) np++;
      }

      /* avoid multiple access pathes */
      if (pv_this) {
	  for ( p = 0; pv_this[p] != NULL; p++) {
	      /* avoid multiple access pathes for now (2.4.0-test8)
		 and MD covered pathes as well */
	      for ( i = 0; i < np; i++) {
		  if ( p != i &&
		       strncmp ( pv_this[p]->vg_name, vg_name, NAME_LEN) == 0) {
		      if ( pv_this[i]->pv_number == pv_this[p]->pv_number &&
			   memcmp ( pv_this[i]->pv_uuid, pv_this[p]->pv_uuid,
				    UUID_LEN) == 0) {
			  if ( MAJOR ( pv_this[p]->pv_dev) == MD_MAJOR) pp = i;
			  pv_this[pp] = NULL;
		      }
		  }
	      }
	      for ( pp = 0; pp < p - 2; pp++) {
		  if ( pv_this[pp] == NULL) {
		      pv_this[pp] = pv_this[pp+1];
		      pv_this[pp+1] = NULL;
		  }
	      }
	      np = 0;
	      while ( pv_this[np] != NULL) np++;
	  }
      }

      /* no we only have pointers to single access path PVs
         in pv_this belonging to this VG */

      if ( np == 0) {
         ret = -LVM_EPV_READ_ALL_PV_OF_VG_NP;
         goto pv_read_all_pv_of_vg_end;
      }

      /* pass to find highest pv_number */
      for ( p = 0; pv_this[p] != NULL; p++) {
         if ( pv_number < pv_this[p]->pv_number)
            pv_number = pv_this[p]->pv_number;
      }

      if ( pv_number != np) {
         ret = -LVM_EPV_READ_ALL_PV_OF_VG_PV_NUMBER;
         goto pv_read_all_pv_of_vg_end;
      }

      /* Check for contiguous PV array */
      for ( p = 0; pv_this[p] != NULL; p++)
         if ( pv_this[p] == NULL && p < np)
            ret = -LVM_EPV_READ_ALL_PV_OF_VG_NP_SORT;

      /* Correct PV sort order */
      for ( p = 0; pv_this[p] != NULL; p++) {
         pv_t *pv_ptr;
         int pp, min;
         min = p;
         for ( pp = p + 1; pv_this[pp] != NULL; pp++) {
            if ( pv_this[pp]->pv_number < pv_this[min]->pv_number) min=pp;
         }
         pv_ptr = pv_this[min];
         pv_this[min] = pv_this[p];
         pv_this[p] = pv_ptr;
      }


      first = 1;
   }

   if ( ret == 0) *pv = pv_this;

pv_read_all_pv_of_vg_end:

#ifdef DEBUG
   debug_leave ( "pv_read_all_pv_of_vg -- LEAVING with ret: %d\n", ret);
#endif
   return ret;
} /* pv_read_all_pv_of_vg() */
