xref: /illumos-gate/usr/src/cmd/svc/common/manifest_find.c (revision 2a93c3751513f2cec775a429a244bec6f0f0a635)
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 int
245 find_manifests(const char *dir, manifest_info_t ***arrayp, int flags)
246 {
247 	mftsd_t *tsdp;
248 	int status = -1;
249 	int count;
250 
251 	tsdp = get_thread_specific_data();
252 	if (tsdp == NULL)
253 		return (NULL);
254 
255 	tsdp->tsd_flags = flags;
256 
257 	/*
258 	 * Create a handle for use by mhast_test_file() if
259 	 * the flag is set to request hash checking be enabled.
260 	 */
261 	if (tsdp->tsd_flags & CHECKHASH) {
262 		tsdp->tsd_hndl = scf_handle_create(SCF_VERSION);
263 		if (tsdp->tsd_hndl == NULL) {
264 			if (scf_error() == SCF_ERROR_NO_MEMORY) {
265 				errno = ENOMEM;
266 			} else {
267 				errno = EINVAL;
268 			}
269 			goto out;
270 		}
271 		if (scf_handle_bind(tsdp->tsd_hndl) != SCF_SUCCESS) {
272 			if (scf_error() == SCF_ERROR_NO_RESOURCES) {
273 				errno = ENOMEM;
274 			} else {
275 				errno = EINVAL;
276 			}
277 			goto out;
278 		}
279 	}
280 
281 	if (nftw(dir, process, MAX_DEPTH, FTW_MOUNT) == 0) {
282 		status = 0;
283 	}
284 
285 out:
286 	if (tsdp->tsd_hndl != NULL) {
287 		(void) scf_handle_unbind(tsdp->tsd_hndl);
288 		(void) scf_handle_destroy(tsdp->tsd_hndl);
289 	}
290 	if (status == 0) {
291 		*arrayp = tsdp->tsd_array;
292 		count = tsdp->tsd_count;
293 	} else {
294 		*arrayp = NULL;
295 		free_manifest_array(tsdp->tsd_array);
296 		count = -1;
297 	}
298 
299 	/* Reset thread specific data. */
300 	(void) memset(tsdp, 0, sizeof (*tsdp));
301 
302 	return (count);
303 }
304 
305 /*
306  * Free the memory associated with the array of manifest_info structures.
307  */
308 void
309 free_manifest_array(manifest_info_t **array)
310 {
311 	manifest_info_t **entry;
312 	manifest_info_t *info;
313 
314 	if (array == NULL)
315 		return;
316 
317 	for (entry = array; *entry != NULL; entry++) {
318 		info = *entry;
319 		uu_free((void *) info->mi_path);
320 		uu_free((void *) info->mi_prop);
321 		uu_free(info);
322 	}
323 
324 	/*
325 	 * Array is allocated with realloc(3C), so it must be freed with
326 	 * free(3c) rather than uu_free().
327 	 */
328 	free(array);
329 }
330