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