1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * The primary role of this file is to obtain a list of manifests that are 28 * located in a specified directory or one of its subdirectories. The 29 * find_manifests() function provides this service, and 30 * free_manifest_array() is used to free the memory associated with the 31 * returned list. 32 * 33 * The find_manifests() function can return an array consisting of all the 34 * .xml files in the directory and its subdirectories. Alternatively, 35 * find_manifests() can be asked to only return new manifests based on the 36 * return of mhash_test_file(). The list that is returned is an array of 37 * pointers to manifest_info structures. 38 * 39 * Implementation Notes: 40 * ==================== 41 * This module makes use of the nftw(3C) function to scan the directory. 42 * nftw() calls a processing function for every file that it finds. 43 * Unfortunately, nftw does not allow us to pass in any structure pointers 44 * to the processing function, and that makes it hard to accumulate a list. 45 * Thus, we will use the thread specific data area to hold data that must 46 * be retained between calls to the processing function. This will allow 47 * this module to be used in multi-threaded applications if the need 48 * arises. 49 */ 50 51 #include <assert.h> 52 #include <errno.h> 53 #include <ftw.h> 54 #include <libscf.h> 55 #include <libuutil.h> 56 #include <pthread.h> 57 #include <stdlib.h> 58 #include <string.h> 59 #include "manifest_find.h" 60 #include "manifest_hash.h" 61 62 #define MAX_DEPTH 24 63 64 /* Thread specific data */ 65 typedef struct mftsd { 66 manifest_info_t ** tsd_array; /* Array of manifest_info structs */ 67 int tsd_count; /* Number items in list */ 68 int tsd_max; /* Number of pointers allocated */ 69 /* at tsd_array. */ 70 int tsd_flags; /* Check flags for hash and extension */ 71 scf_handle_t *tsd_hndl; /* Handle for libscf. */ 72 } mftsd_t; 73 74 static pthread_key_t tsd_key = PTHREAD_ONCE_KEY_NP; 75 76 /* 77 * Add the manifest info consisting of filename (fn), hash property name 78 * (pname) and hash to the array at tsd_array. If necessary, realloc() 79 * will be called to increase the size of the buffer at tsd_array. 80 * 81 * Returns 0 on success and -1 on failure. If a failure occurs, errno will 82 * be set. 83 */ 84 static int 85 add_pointer(mftsd_t *tsdp, const char *fn, const char *pname, uchar_t *hash) 86 { 87 manifest_info_t *info; 88 manifest_info_t **newblock; 89 int new_max; 90 91 if (tsdp->tsd_count >= (tsdp->tsd_max - 1)) { 92 /* Need more memory. */ 93 new_max = (tsdp->tsd_max == 0) ? 16 : 2 * tsdp->tsd_max; 94 newblock = realloc(tsdp->tsd_array, 95 new_max * sizeof (*tsdp->tsd_array)); 96 if (newblock == NULL) 97 return (-1); 98 tsdp->tsd_array = newblock; 99 /* NULL terminate list in case allocations fail below. */ 100 *(tsdp->tsd_array + tsdp->tsd_count) = NULL; 101 tsdp->tsd_max = new_max; 102 } 103 info = uu_zalloc(sizeof (*info)); 104 if (info == NULL) { 105 errno = ENOMEM; 106 return (-1); 107 } 108 info->mi_path = uu_strdup(fn); 109 if (info->mi_path == NULL) { 110 uu_free(info); 111 errno = ENOMEM; 112 return (-1); 113 } 114 info->mi_prop = pname; 115 if (hash != NULL) 116 (void) memcpy(info->mi_hash, hash, MHASH_SIZE); 117 *(tsdp->tsd_array + tsdp->tsd_count) = info; 118 tsdp->tsd_count++; 119 120 /* NULL terminate the list. */ 121 *(tsdp->tsd_array + tsdp->tsd_count) = NULL; 122 123 return (0); 124 } 125 126 /* 127 * If necessary initialize the thread specific data key at tsd_key, and 128 * allocate a mftsd_t structure to hold our thread specific data. Upon 129 * success, the address the thread specific data is returned. On failure, 130 * NULL is returned and errno is set. 131 */ 132 static mftsd_t * 133 get_thread_specific_data() 134 { 135 mftsd_t *tsdp; 136 137 if (pthread_key_create_once_np(&tsd_key, NULL) != 0) 138 return (NULL); 139 tsdp = (mftsd_t *)pthread_getspecific(tsd_key); 140 if (tsdp == NULL) { 141 /* 142 * First time for this thread. We need to allocate memory 143 * for our thread specific data. 144 */ 145 tsdp = uu_zalloc(sizeof (*tsdp)); 146 if (tsdp == NULL) { 147 errno = ENOMEM; 148 return (NULL); 149 } 150 errno = pthread_setspecific(tsd_key, tsdp); 151 if (errno != 0) { 152 /* 153 * EINVAL means that our key is invalid, which 154 * would be a coding error. 155 */ 156 assert(errno != EINVAL); 157 return (NULL); 158 } 159 } 160 return (tsdp); 161 } 162 163 /* 164 * This function is called by nftw(3C) every time that it finds an object 165 * in a directory of interest. If the object is a file, process() checks 166 * to see if it is a service bundle file by insuring that it has a .xml 167 * extension. 168 * 169 * If the file is a service bundle file, and the CHECKHASH flag is set process() 170 * calls mhash_test_file() to see if it is a new bundle. Bundle file data 171 * for selected bundles is added to tsd_array in our thread specific data. 172 * 173 * Assume given file is a manifest unless BUNDLE_PROF flag is set to indicate 174 * it's a profile. For profile bundles, call mhash_test_file() with the 175 * appropriate argument. 176 * 177 * The CHECKEXT flag may be set if this was not a directory search request 178 * but a single service bundle file check that was determined by the caller to 179 * be found based not on the extension of the file. 180 */ 181 /*ARGSUSED*/ 182 static int 183 process(const char *fn, const struct stat *sp, int ftw_type, 184 struct FTW *ftws) 185 { 186 int is_profile; 187 char *suffix_match; 188 uchar_t hash[MHASH_SIZE]; 189 char *pname; 190 mftsd_t *tsdp; 191 192 if (ftw_type != FTW_F) 193 return (0); 194 195 tsdp = get_thread_specific_data(); 196 if (tsdp == NULL) 197 return (-1); 198 199 /* 200 * Only check the extension on the file when 201 * requested. 202 */ 203 if (tsdp->tsd_flags & CHECKEXT) { 204 suffix_match = strstr(fn, ".xml"); 205 if (suffix_match == NULL || strcmp(suffix_match, ".xml") != 0) 206 return (0); 207 } 208 209 if (tsdp->tsd_flags & CHECKHASH) { 210 is_profile = (tsdp->tsd_flags & BUNDLE_PROF) ? 1 : 0; 211 if (mhash_test_file(tsdp->tsd_hndl, fn, is_profile, &pname, 212 hash) == MHASH_NEWFILE) { 213 return (add_pointer(tsdp, fn, pname, hash)); 214 } 215 } else { 216 return (add_pointer(tsdp, fn, NULL, NULL)); 217 } 218 219 return (0); 220 } 221 222 /* 223 * This function returns a pointer to an array of manifest_info_t pointers. 224 * There is one manifest_info_t pointer for each service bundle file in the 225 * directory, dir, that satifies the selection criteria. The array is 226 * returned to arrayp. The array will be terminated with a NULL pointer. 227 * It is the responsibility of the caller to free the memory associated 228 * with the array by calling free_manifest_array(). 229 * 230 * flags : 231 * 0x1 - CHECKHASH - do the hash check and only return bundle 232 * files that do not have a hash entry in the smf/manifest table 233 * or the hash value has changed due to the bundle file having 234 * been modified. If not set then all service bundle files found 235 * are returned, regardless of the hash status. 236 * 237 * 0x2 - CHECKEXT - Check the extension of the file is .xml 238 * 239 * On success a count of the number of selected bundles is returned. 240 * Note, however, that *arrayp will be set to NULL if the selection is 241 * empty, and a count of 0 will be returned. In the case of failure, -1 242 * will be returned and errno will be set. 243 * 244 * This function takes a repository handle argument from the caller and saves 245 * that handle in a thread specific data structure. The thread specific 246 * repository handle is used in process() to communicate with the appropriate 247 * repository. Thus callers should take care of thread safety with respect to 248 * the repository handle. Currently, the two callers of find_manifests are both 249 * single threaded, i.e. svccfg and mfstscan, so thread safety not an issue. 250 */ 251 int 252 find_manifests(scf_handle_t *hndl, const char *dir, 253 manifest_info_t ***arrayp, int flags) 254 { 255 mftsd_t *tsdp; 256 int status = -1; 257 int count; 258 259 tsdp = get_thread_specific_data(); 260 if (tsdp == NULL) 261 return (NULL); 262 263 tsdp->tsd_flags = flags; 264 265 if (tsdp->tsd_flags & CHECKHASH) { 266 tsdp->tsd_hndl = hndl; 267 } 268 269 if (nftw(dir, process, MAX_DEPTH, FTW_MOUNT) == 0) { 270 status = 0; 271 } 272 273 out: 274 if (status == 0) { 275 *arrayp = tsdp->tsd_array; 276 count = tsdp->tsd_count; 277 } else { 278 *arrayp = NULL; 279 free_manifest_array(tsdp->tsd_array); 280 count = -1; 281 } 282 283 /* Reset thread specific data. */ 284 (void) memset(tsdp, 0, sizeof (*tsdp)); 285 286 return (count); 287 } 288 289 /* 290 * Free the memory associated with the array of manifest_info structures. 291 */ 292 void 293 free_manifest_array(manifest_info_t **array) 294 { 295 manifest_info_t **entry; 296 manifest_info_t *info; 297 298 if (array == NULL) 299 return; 300 301 for (entry = array; *entry != NULL; entry++) { 302 info = *entry; 303 uu_free((void *) info->mi_path); 304 uu_free((void *) info->mi_prop); 305 uu_free(info); 306 } 307 308 /* 309 * Array is allocated with realloc(3C), so it must be freed with 310 * free(3c) rather than uu_free(). 311 */ 312 free(array); 313 } 314