/*
 * tools/lib/pv_read_all_pv_of_vg.c
 *
 * Copyright (C) 1997 - 2002  Heinz Mauelshagen, Sistina Software
 *
 * March-May,October-November 1997
 * May,August,November 1998
 * January,March,April,September,October 2000
 * February,November 2001
 * 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
 *
 *    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
 *    07/02/2001 - fixed multiple access path avoidance problem
 *                 leading to removed but valid PV structure
 *    22/11/2001 - support non sequential PV numbers
 *    01/14/2002 - fixed contiguous PV array bug (MG)
 *
 */

#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 p1 = 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;

   debug_enter ( "pv_read_all_pv_of_vg -- CALLED with vg_name: \"%s\"\n",
                 vg_name);

   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_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) {
          int last_one;

	  for ( p = 0; p < np; p++) {
	      /* avoid multiple access paths for now (2.4.0-test8)
		 and MD covered pathes as well */
              if ( pv_this[p] == NULL) continue;
	      for ( i = 0; i < np; i++) {
                  if ( pv_this[p] == NULL) continue;
                  if ( pv_this[i] == NULL || p == i) continue;
		  if ( 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 MD addresses the same data -> remove the
                             direct disk device rather than the MD device */
			  if ( MAJOR ( pv_this[p]->pv_dev) == MD_MAJOR) pp = i;
                          else                                          pp = p;
			  pv_this[pp] = NULL;
		      }
		  }
	      }
	  }
          /* make array contiguous again */
          /* last one that wasn't NULL */
          last_one = -1;
          for ( i = 0; i < np; i++) {
             if ( pv_this[i] != NULL) {
                 last_one++;
                 /* we only need this when last_one and i differ */
                 if (last_one < i) {
                    pv_this[last_one]=pv_this[i];
                    pv_this[i]=NULL;
                 }
             }
         }
         np = last_one+1;
      }

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

      /* Hopefully we do have at least one ;-) */
      if ( np == 0) {
         ret = -LVM_EPV_READ_ALL_PV_OF_VG_NP;
         goto pv_read_all_pv_of_vg_end;
      }

      /* pass to check pv numbers uniqueness */
      for ( p = 0; p < np; p++) {
         pv_number = pv_this[p]->pv_number;
         for ( p1 = p + 1; p1 < np; p1++) {
            if ( pv_this[p1]->pv_number == pv_number) {
               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:

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