19444c26fSTom Whitten /*
29444c26fSTom Whitten  * CDDL HEADER START
39444c26fSTom Whitten  *
49444c26fSTom Whitten  * The contents of this file are subject to the terms of the
59444c26fSTom Whitten  * Common Development and Distribution License (the "License").
69444c26fSTom Whitten  * You may not use this file except in compliance with the License.
79444c26fSTom Whitten  *
89444c26fSTom Whitten  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99444c26fSTom Whitten  * or http://www.opensolaris.org/os/licensing.
109444c26fSTom Whitten  * See the License for the specific language governing permissions
119444c26fSTom Whitten  * and limitations under the License.
129444c26fSTom Whitten  *
139444c26fSTom Whitten  * When distributing Covered Code, include this CDDL HEADER in each
149444c26fSTom Whitten  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159444c26fSTom Whitten  * If applicable, add the following below this CDDL HEADER, with the
169444c26fSTom Whitten  * fields enclosed by brackets "[]" replaced with your own identifying
179444c26fSTom Whitten  * information: Portions Copyright [yyyy] [name of copyright owner]
189444c26fSTom Whitten  *
199444c26fSTom Whitten  * CDDL HEADER END
209444c26fSTom Whitten  */
219444c26fSTom Whitten 
229444c26fSTom Whitten /*
23adfc3118STruong Nguyen  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
249444c26fSTom Whitten  */
259444c26fSTom Whitten 
269444c26fSTom Whitten /*
279444c26fSTom Whitten  * The primary role of this file is to obtain a list of manifests that are
289444c26fSTom Whitten  * located in a specified directory or one of its subdirectories.  The
299444c26fSTom Whitten  * find_manifests() function provides this service, and
309444c26fSTom Whitten  * free_manifest_array() is used to free the memory associated with the
319444c26fSTom Whitten  * returned list.
329444c26fSTom Whitten  *
339444c26fSTom Whitten  * The find_manifests() function can return an array consisting of all the
349444c26fSTom Whitten  * .xml files in the directory and its subdirectories.  Alternatively,
359444c26fSTom Whitten  * find_manifests() can be asked to only return new manifests based on the
369444c26fSTom Whitten  * return of mhash_test_file().  The list that is returned is an array of
379444c26fSTom Whitten  * pointers to manifest_info structures.
389444c26fSTom Whitten  *
399444c26fSTom Whitten  * Implementation Notes:
409444c26fSTom Whitten  * ====================
419444c26fSTom Whitten  * This module makes use of the nftw(3C) function to scan the directory.
429444c26fSTom Whitten  * nftw() calls a processing function for every file that it finds.
439444c26fSTom Whitten  * Unfortunately, nftw does not allow us to pass in any structure pointers
449444c26fSTom Whitten  * to the processing function, and that makes it hard to accumulate a list.
459444c26fSTom Whitten  * Thus, we will use the thread specific data area to hold data that must
469444c26fSTom Whitten  * be retained between calls to the processing function.  This will allow
479444c26fSTom Whitten  * this module to be used in multi-threaded applications if the need
489444c26fSTom Whitten  * arises.
499444c26fSTom Whitten  */
509444c26fSTom Whitten 
519444c26fSTom Whitten #include <assert.h>
529444c26fSTom Whitten #include <errno.h>
539444c26fSTom Whitten #include <ftw.h>
549444c26fSTom Whitten #include <libscf.h>
559444c26fSTom Whitten #include <libuutil.h>
569444c26fSTom Whitten #include <pthread.h>
579444c26fSTom Whitten #include <stdlib.h>
589444c26fSTom Whitten #include <string.h>
599444c26fSTom Whitten #include "manifest_find.h"
609444c26fSTom Whitten #include "manifest_hash.h"
619444c26fSTom Whitten 
629444c26fSTom Whitten #define	MAX_DEPTH	24
639444c26fSTom Whitten 
649444c26fSTom Whitten /* Thread specific data */
659444c26fSTom Whitten typedef struct mftsd {
669444c26fSTom Whitten 	manifest_info_t ** tsd_array;	/* Array of manifest_info structs */
679444c26fSTom Whitten 	int		tsd_count;	/* Number items in list */
689444c26fSTom Whitten 	int		tsd_max;	/* Number of pointers allocated */
699444c26fSTom Whitten 					/* at tsd_array. */
709444c26fSTom Whitten 	int		tsd_flags;	/* Check flags for hash and extension */
719444c26fSTom Whitten 	scf_handle_t	*tsd_hndl;	/* Handle for libscf. */
729444c26fSTom Whitten } mftsd_t;
739444c26fSTom Whitten 
749444c26fSTom Whitten static pthread_key_t tsd_key = PTHREAD_ONCE_KEY_NP;
759444c26fSTom Whitten 
769444c26fSTom Whitten /*
779444c26fSTom Whitten  * Add the manifest info consisting of filename (fn), hash property name
789444c26fSTom Whitten  * (pname) and hash to the array at tsd_array.  If necessary, realloc()
799444c26fSTom Whitten  * will be called to increase the size of the buffer at tsd_array.
809444c26fSTom Whitten  *
819444c26fSTom Whitten  * Returns 0 on success and -1 on failure.  If a failure occurs, errno will
829444c26fSTom Whitten  * be set.
839444c26fSTom Whitten  */
849444c26fSTom Whitten static int
add_pointer(mftsd_t * tsdp,const char * fn,const char * pname,uchar_t * hash)859444c26fSTom Whitten add_pointer(mftsd_t *tsdp, const char *fn, const char *pname, uchar_t *hash)
869444c26fSTom Whitten {
879444c26fSTom Whitten 	manifest_info_t *info;
889444c26fSTom Whitten 	manifest_info_t **newblock;
899444c26fSTom Whitten 	int new_max;
909444c26fSTom Whitten 
919444c26fSTom Whitten 	if (tsdp->tsd_count >= (tsdp->tsd_max - 1)) {
929444c26fSTom Whitten 		/* Need more memory. */
939444c26fSTom Whitten 		new_max = (tsdp->tsd_max == 0) ? 16 : 2 * tsdp->tsd_max;
949444c26fSTom Whitten 		newblock = realloc(tsdp->tsd_array,
959444c26fSTom Whitten 		    new_max * sizeof (*tsdp->tsd_array));
969444c26fSTom Whitten 		if (newblock == NULL)
979444c26fSTom Whitten 			return (-1);
989444c26fSTom Whitten 		tsdp->tsd_array = newblock;
999444c26fSTom Whitten 		/* NULL terminate list in case allocations fail below. */
1009444c26fSTom Whitten 		*(tsdp->tsd_array + tsdp->tsd_count) = NULL;
1019444c26fSTom Whitten 		tsdp->tsd_max = new_max;
1029444c26fSTom Whitten 	}
1039444c26fSTom Whitten 	info = uu_zalloc(sizeof (*info));
1049444c26fSTom Whitten 	if (info == NULL) {
1059444c26fSTom Whitten 		errno = ENOMEM;
1069444c26fSTom Whitten 		return (-1);
1079444c26fSTom Whitten 	}
1089444c26fSTom Whitten 	info->mi_path = uu_strdup(fn);
1099444c26fSTom Whitten 	if (info->mi_path == NULL) {
1109444c26fSTom Whitten 		uu_free(info);
1119444c26fSTom Whitten 		errno = ENOMEM;
1129444c26fSTom Whitten 		return (-1);
1139444c26fSTom Whitten 	}
1149444c26fSTom Whitten 	info->mi_prop = pname;
1159444c26fSTom Whitten 	if (hash != NULL)
1169444c26fSTom Whitten 		(void) memcpy(info->mi_hash, hash, MHASH_SIZE);
1179444c26fSTom Whitten 	*(tsdp->tsd_array + tsdp->tsd_count) = info;
1189444c26fSTom Whitten 	tsdp->tsd_count++;
1199444c26fSTom Whitten 
1209444c26fSTom Whitten 	/* NULL terminate the list. */
1219444c26fSTom Whitten 	*(tsdp->tsd_array + tsdp->tsd_count) = NULL;
1229444c26fSTom Whitten 
1239444c26fSTom Whitten 	return (0);
1249444c26fSTom Whitten }
1259444c26fSTom Whitten 
1269444c26fSTom Whitten /*
1279444c26fSTom Whitten  * If necessary initialize the thread specific data key at tsd_key, and
1289444c26fSTom Whitten  * allocate a mftsd_t structure to hold our thread specific data.  Upon
1299444c26fSTom Whitten  * success, the address the thread specific data is returned.  On failure,
1309444c26fSTom Whitten  * NULL is returned and errno is set.
1319444c26fSTom Whitten  */
1329444c26fSTom Whitten static mftsd_t *
get_thread_specific_data()1339444c26fSTom Whitten get_thread_specific_data()
1349444c26fSTom Whitten {
1359444c26fSTom Whitten 	mftsd_t *tsdp;
1369444c26fSTom Whitten 
1379444c26fSTom Whitten 	if (pthread_key_create_once_np(&tsd_key, NULL) != 0)
1389444c26fSTom Whitten 		return (NULL);
1399444c26fSTom Whitten 	tsdp = (mftsd_t *)pthread_getspecific(tsd_key);
1409444c26fSTom Whitten 	if (tsdp == NULL) {
1419444c26fSTom Whitten 		/*
1429444c26fSTom Whitten 		 * First time for this thread.  We need to allocate memory
1439444c26fSTom Whitten 		 * for our thread specific data.
1449444c26fSTom Whitten 		 */
1459444c26fSTom Whitten 		tsdp = uu_zalloc(sizeof (*tsdp));
1469444c26fSTom Whitten 		if (tsdp == NULL) {
1479444c26fSTom Whitten 			errno = ENOMEM;
1489444c26fSTom Whitten 			return (NULL);
1499444c26fSTom Whitten 		}
1509444c26fSTom Whitten 		errno = pthread_setspecific(tsd_key, tsdp);
1519444c26fSTom Whitten 		if (errno != 0) {
1529444c26fSTom Whitten 			/*
1539444c26fSTom Whitten 			 * EINVAL means that our key is invalid, which
1549444c26fSTom Whitten 			 * would be a coding error.
1559444c26fSTom Whitten 			 */
1569444c26fSTom Whitten 			assert(errno != EINVAL);
1579444c26fSTom Whitten 			return (NULL);
1589444c26fSTom Whitten 		}
1599444c26fSTom Whitten 	}
1609444c26fSTom Whitten 	return (tsdp);
1619444c26fSTom Whitten }
1629444c26fSTom Whitten 
1639444c26fSTom Whitten /*
1649444c26fSTom Whitten  * This function is called by nftw(3C) every time that it finds an object
1659444c26fSTom Whitten  * in a directory of interest.  If the object is a file, process() checks
166adfc3118STruong Nguyen  * to see if it is a service bundle file by insuring that it has a .xml
1679444c26fSTom Whitten  * extension.
1689444c26fSTom Whitten  *
169adfc3118STruong Nguyen  * If the file is a service bundle file, and the CHECKHASH flag is set process()
170adfc3118STruong Nguyen  * calls mhash_test_file() to see if it is a new bundle.  Bundle file data
171adfc3118STruong Nguyen  * for selected bundles is added to tsd_array in our thread specific data.
172adfc3118STruong Nguyen  *
173adfc3118STruong Nguyen  * Assume given file is a manifest unless BUNDLE_PROF flag is set to indicate
174adfc3118STruong Nguyen  * it's a profile. For profile bundles, call mhash_test_file() with the
175adfc3118STruong Nguyen  * appropriate argument.
1769444c26fSTom Whitten  *
1779444c26fSTom Whitten  * The CHECKEXT flag may be set if this was not a directory search request
178adfc3118STruong Nguyen  * but a single service bundle file check that was determined by the caller to
1799444c26fSTom Whitten  * be found based not on the extension of the file.
1809444c26fSTom Whitten  */
1819444c26fSTom Whitten /*ARGSUSED*/
1829444c26fSTom Whitten static int
process(const char * fn,const struct stat * sp,int ftw_type,struct FTW * ftws)1839444c26fSTom Whitten process(const char *fn, const struct stat *sp, int ftw_type,
1849444c26fSTom Whitten     struct FTW *ftws)
1859444c26fSTom Whitten {
186adfc3118STruong Nguyen 	int is_profile;
1879444c26fSTom Whitten 	char *suffix_match;
1889444c26fSTom Whitten 	uchar_t hash[MHASH_SIZE];
1899444c26fSTom Whitten 	char *pname;
1909444c26fSTom Whitten 	mftsd_t *tsdp;
1919444c26fSTom Whitten 
1929444c26fSTom Whitten 	if (ftw_type != FTW_F)
1939444c26fSTom Whitten 		return (0);
1949444c26fSTom Whitten 
1959444c26fSTom Whitten 	tsdp = get_thread_specific_data();
1969444c26fSTom Whitten 	if (tsdp == NULL)
1979444c26fSTom Whitten 		return (-1);
1989444c26fSTom Whitten 
1999444c26fSTom Whitten 	/*
2009444c26fSTom Whitten 	 * Only check the extension on the file when
2019444c26fSTom Whitten 	 * requested.
2029444c26fSTom Whitten 	 */
2039444c26fSTom Whitten 	if (tsdp->tsd_flags & CHECKEXT) {
2049444c26fSTom Whitten 		suffix_match = strstr(fn, ".xml");
2059444c26fSTom Whitten 		if (suffix_match == NULL || strcmp(suffix_match, ".xml") != 0)
2069444c26fSTom Whitten 			return (0);
2079444c26fSTom Whitten 	}
2089444c26fSTom Whitten 
2099444c26fSTom Whitten 	if (tsdp->tsd_flags & CHECKHASH) {
210adfc3118STruong Nguyen 		is_profile = (tsdp->tsd_flags & BUNDLE_PROF) ? 1 : 0;
211adfc3118STruong Nguyen 		if (mhash_test_file(tsdp->tsd_hndl, fn, is_profile, &pname,
212adfc3118STruong Nguyen 		    hash) == MHASH_NEWFILE) {
2139444c26fSTom Whitten 			return (add_pointer(tsdp, fn, pname, hash));
2149444c26fSTom Whitten 		}
2159444c26fSTom Whitten 	} else {
2169444c26fSTom Whitten 		return (add_pointer(tsdp, fn, NULL, NULL));
2179444c26fSTom Whitten 	}
2189444c26fSTom Whitten 
2199444c26fSTom Whitten 	return (0);
2209444c26fSTom Whitten }
2219444c26fSTom Whitten 
2229444c26fSTom Whitten /*
2239444c26fSTom Whitten  * This function returns a pointer to an array of manifest_info_t pointers.
224adfc3118STruong Nguyen  * There is one manifest_info_t pointer for each service bundle file in the
2259444c26fSTom Whitten  * directory, dir, that satifies the selection criteria.  The array is
2269444c26fSTom Whitten  * returned to arrayp.  The array will be terminated with a NULL pointer.
2279444c26fSTom Whitten  * It is the responsibility of the caller to free the memory associated
2289444c26fSTom Whitten  * with the array by calling free_manifest_array().
2299444c26fSTom Whitten  *
2309444c26fSTom Whitten  * flags :
231adfc3118STruong Nguyen  * 	0x1 - CHECKHASH - do the hash check and only return bundle
2329444c26fSTom Whitten  * 	files that do not have a hash entry in the smf/manifest table
233adfc3118STruong Nguyen  * 	or the hash value has changed due to the bundle file having
234adfc3118STruong Nguyen  * 	been modified.  If not set then all service bundle files found
235adfc3118STruong Nguyen  * 	are returned, regardless of the hash status.
2369444c26fSTom Whitten  *
2379444c26fSTom Whitten  * 	0x2 - CHECKEXT - Check the extension of the file is .xml
2389444c26fSTom Whitten  *
239adfc3118STruong Nguyen  * On success a count of the number of selected bundles is returned.
2409444c26fSTom Whitten  * Note, however, that *arrayp will be set to NULL if the selection is
2419444c26fSTom Whitten  * empty, and a count of 0 will be returned.  In the case of failure, -1
2429444c26fSTom Whitten  * will be returned and errno will be set.
243293e3ab3STruong Q. Nguyen  *
244293e3ab3STruong Q. Nguyen  * This function takes a repository handle argument from the caller and saves
245293e3ab3STruong Q. Nguyen  * that handle in a thread specific data structure. The thread specific
246293e3ab3STruong Q. Nguyen  * repository handle is used in process() to communicate with the appropriate
247293e3ab3STruong Q. Nguyen  * repository. Thus callers should take care of thread safety with respect to
248293e3ab3STruong Q. Nguyen  * the repository handle. Currently, the two callers of find_manifests are both
249293e3ab3STruong Q. Nguyen  * single threaded, i.e. svccfg and mfstscan, so thread safety not an issue.
2509444c26fSTom Whitten  */
2519444c26fSTom Whitten int
find_manifests(scf_handle_t * hndl,const char * dir,manifest_info_t *** arrayp,int flags)252293e3ab3STruong Q. Nguyen find_manifests(scf_handle_t *hndl, const char *dir,
253293e3ab3STruong Q. Nguyen     manifest_info_t ***arrayp, int flags)
2549444c26fSTom Whitten {
2559444c26fSTom Whitten 	mftsd_t *tsdp;
2569444c26fSTom Whitten 	int status = -1;
2579444c26fSTom Whitten 	int count;
2589444c26fSTom Whitten 
2599444c26fSTom Whitten 	tsdp = get_thread_specific_data();
2609444c26fSTom Whitten 	if (tsdp == NULL)
261*23f76dc2SRichard Lowe 		return (-1);
2629444c26fSTom Whitten 
2639444c26fSTom Whitten 	tsdp->tsd_flags = flags;
2649444c26fSTom Whitten 
2659444c26fSTom Whitten 	if (tsdp->tsd_flags & CHECKHASH) {
266293e3ab3STruong Q. Nguyen 		tsdp->tsd_hndl = hndl;
2679444c26fSTom Whitten 	}
2689444c26fSTom Whitten 
2699444c26fSTom Whitten 	if (nftw(dir, process, MAX_DEPTH, FTW_MOUNT) == 0) {
2709444c26fSTom Whitten 		status = 0;
2719444c26fSTom Whitten 	}
2729444c26fSTom Whitten 
2739444c26fSTom Whitten out:
2749444c26fSTom Whitten 	if (status == 0) {
2759444c26fSTom Whitten 		*arrayp = tsdp->tsd_array;
2769444c26fSTom Whitten 		count = tsdp->tsd_count;
2779444c26fSTom Whitten 	} else {
2789444c26fSTom Whitten 		*arrayp = NULL;
2799444c26fSTom Whitten 		free_manifest_array(tsdp->tsd_array);
2809444c26fSTom Whitten 		count = -1;
2819444c26fSTom Whitten 	}
2829444c26fSTom Whitten 
2839444c26fSTom Whitten 	/* Reset thread specific data. */
2849444c26fSTom Whitten 	(void) memset(tsdp, 0, sizeof (*tsdp));
2859444c26fSTom Whitten 
2869444c26fSTom Whitten 	return (count);
2879444c26fSTom Whitten }
2889444c26fSTom Whitten 
2899444c26fSTom Whitten /*
2909444c26fSTom Whitten  * Free the memory associated with the array of manifest_info structures.
2919444c26fSTom Whitten  */
2929444c26fSTom Whitten void
free_manifest_array(manifest_info_t ** array)2939444c26fSTom Whitten free_manifest_array(manifest_info_t **array)
2949444c26fSTom Whitten {
2959444c26fSTom Whitten 	manifest_info_t **entry;
2969444c26fSTom Whitten 	manifest_info_t *info;
2979444c26fSTom Whitten 
2989444c26fSTom Whitten 	if (array == NULL)
2999444c26fSTom Whitten 		return;
3009444c26fSTom Whitten 
3019444c26fSTom Whitten 	for (entry = array; *entry != NULL; entry++) {
3029444c26fSTom Whitten 		info = *entry;
3039444c26fSTom Whitten 		uu_free((void *) info->mi_path);
3049444c26fSTom Whitten 		uu_free((void *) info->mi_prop);
3059444c26fSTom Whitten 		uu_free(info);
3069444c26fSTom Whitten 	}
3079444c26fSTom Whitten 
3089444c26fSTom Whitten 	/*
3099444c26fSTom Whitten 	 * Array is allocated with realloc(3C), so it must be freed with
3109444c26fSTom Whitten 	 * free(3c) rather than uu_free().
3119444c26fSTom Whitten 	 */
3129444c26fSTom Whitten 	free(array);
3139444c26fSTom Whitten }
314