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
add_pointer(mftsd_t * tsdp,const char * fn,const char * pname,uchar_t * hash)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 *
get_thread_specific_data()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
process(const char * fn,const struct stat * sp,int ftw_type,struct FTW * ftws)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
find_manifests(scf_handle_t * hndl,const char * dir,manifest_info_t *** arrayp,int flags)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 (-1);
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
free_manifest_array(manifest_info_t ** array)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