xref: /illumos-gate/usr/src/lib/libnisdb/yptol/update.c (revision d583b39bfb4e2571d3e41097c5c357ffe353ad45)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * DESCRIPTION: Contains the map update thread and related code.
31  */
32 
33 #include <unistd.h>
34 #include <syslog.h>
35 #include <ndbm.h>
36 #include <thread.h>
37 #include <unistd.h>
38 #include <strings.h>
39 #include "ypsym.h"
40 #include "ypdefs.h"
41 #include "shim.h"
42 #include "yptol.h"
43 #include "../ldap_util.h"
44 
45 /* Enable standard YP code features defined in ypdefs.h */
46 USE_YP_PREFIX
47 USE_YP_MASTER_NAME
48 USE_YP_LAST_MODIFIED
49 USE_YP_INPUT_FILE
50 USE_YP_OUTPUT_NAME
51 USE_YP_DOMAIN_NAME
52 USE_YP_SECURE
53 USE_YP_INTERDOMAIN
54 
55 /*
56  * Decs
57  */
58 suc_code update_from_dit(map_ctrl *, datum *);
59 void * update_thread(void *);
60 
61 /*
62  * Globals
63  */
64 extern pid_t parent_pid;
65 
66 /*
67  * FUNCTION:	update_entry_if_required()
68  *
69  * DESCRIPTION:	Determines if an entry is to be updated and if it is does the
70  *		update.
71  *
72  * GIVEN :	Pointer to the open map ctrl
73  *		Pointer to the entry key
74  *
75  * RETURNS :	SUCCESS = Entry is in a state to be returned to the client
76  *		i.e. either got updated, did not need to be updated or we are
77  *		in a mode where it is acceptable to return out of date
78  *		information.
79  *		FAILURE = Entry need an update but it could not be done.
80  */
81 suc_code
82 update_entry_if_required(map_ctrl *map, datum *key)
83 {
84 
85 	/* Only update individual entries if entire map is */
86 	/* not being updated */
87 	if (is_map_updating(map))
88 		return (SUCCESS);
89 
90 	/*
91 	 * If we are being asked for the order then need to check if
92 	 * the map is in need of an update. If it is then fake a
93 	 * recent order. The client will then read the map, using
94 	 * dbm_firstkey and this will do the update.
95 	 */
96 	if (0 == strncmp(key->dptr, yp_last_modified, yp_last_modified_sz)) {
97 		if (has_map_expired(map))
98 			update_timestamp(map->entries);
99 		return (SUCCESS);
100 	}
101 
102 	/* Never update special keys. Have no TTLs */
103 	if (is_special_key(key))
104 		return (SUCCESS);
105 
106 	if (!has_entry_expired(map, key))
107 		/* Didn't need an update */
108 		return (SUCCESS);
109 
110 	/* Do the update */
111 	return (update_from_dit(map, key));
112 }
113 
114 /*
115  * FUNCTION:	update_from_dit()
116  *
117  * DESCRIPTION:	Called to update an entry from the DIT
118  *
119  * INPUTS:	Map control structure for an open map
120  *		Entry key
121  *
122  * OUTPUTS:	SUCCESS = Update complete or we are in a mode where it is
123  *		acceptable to return out of date information.
124  *		FAILURE =  Update failed
125  *
126  */
127 suc_code
128 update_from_dit(map_ctrl *map, datum *key)
129 {
130 	datum dat;
131 	int ret;
132 	suc_code res;
133 
134 	/*
135 	 * Netgroup maps are a special case we cannot update just one entry so
136 	 * update the entire map instead.
137 	 */
138 	if ((0 == strcmp(map->map_name, NETGROUP_BYHOST)) ||
139 		(0 == strcmp(map->map_name, NETGROUP_BYUSER))) {
140 			return (update_map_if_required(map, FALSE));
141 	}
142 
143 	/* Read entry from the DIT */
144 	ret = read_from_dit(map->map_name, map->domain, key, &dat);
145 
146 	/* Check that we got something */
147 	if (NULL == dat.dptr) {
148 		if (0 == ret) {
149 			/*
150 			 * In a mode where it is acceptable to return out of
151 			 * date information.
152 			 */
153 			logmsg(MSG_NOTIMECHECK, LOG_INFO,
154 				"LDAP inaccessible returning old information");
155 			return (SUCCESS);
156 		} else {
157 			/*
158 			 * In a mode where it is not acceptable to return out
159 			 * of date information.
160 			 *
161 			 * If the error positviely indicates that there is no
162 			 * such entry delete it. For errors where object may
163 			 * still exist in the DIT leave it.
164 			 */
165 			if (MAP_NO_MATCHING_KEY == ret) {
166 				/*
167 				 * Don't log errors. If the entry was not
168 				 * already present then no problem. The user
169 				 * just asked us for a non existant entry.
170 				 */
171 				dbm_delete(map->entries, *key);
172 				dbm_delete(map->ttl, *key);
173 			}
174 			return (FAILURE);
175 		}
176 	}
177 
178 	/* Write it to DBM */
179 	res = dbm_store(map->entries, *key, dat, DBM_REPLACE);
180 	sfree(dat.dptr);
181 
182 	if (SUCCESS != res)
183 		return (FAILURE);
184 
185 	/* Update TTL */
186 	update_entry_ttl(map, key, TTL_RUNNING);
187 
188 	return (SUCCESS);
189 }
190 
191 /*
192  * FUNCTION:	update_map_if_required()
193  *
194  * DESCRIPTION:	Called to update an entire map if it is out of date. Map ctrl
195  *		must be locked before this is called. This handles checking if
196  *		the map is already being updated. It is important that this is
197  *		done atomically	with obtaining the maps update lock.
198  *
199  * INPUTS:	Map control structure for an open map
200  *		Flag indication if we should wait for completion
201  *
202  * OUTPUTS:	SUCCESS = Map update initiated
203  *		FAILURE =  Map update not initiated
204  */
205 suc_code
206 update_map_if_required(map_ctrl *map, bool_t wait)
207 {
208 	thread_t tid;
209 	map_ctrl *new_map;
210 	suc_code res;
211 	long	 flags;
212 
213 	if (wait) {
214 		/*
215 		 * Actually get the lock
216 		 *
217 		 * May block so unlock map_ctrl while it is done
218 		 */
219 		unlock_map_ctrl(map);
220 		res = lock_map_update(map);
221 		lock_map_ctrl(map);
222 		if (SUCCESS != res) {
223 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
224 				"Could not lock map %s for update",
225 								map->map_name);
226 			return (FAILURE);
227 		}
228 	} else {
229 		/* If not waiting try to get the lock */
230 		switch (try_lock_map_update(map)) {
231 			case 0:
232 				/*
233 				 * We got the lock. Continue to start an update.
234 				 */
235 				break;
236 
237 			case EBUSY:
238 				/*
239 				 * Some one else got the lock. OK they are
240 				 * doing the update so we can just return.
241 				 */
242 				return (SUCCESS);
243 
244 			default:
245 				/*
246 				 * Some serious problem with lock.
247 				 */
248 				return (FAILURE);
249 		}
250 	}
251 
252 	/*
253 	 * If we get here are holding the update lock. Make a final check that
254 	 * nobody beat us to the map update while we were getting it.
255 	 */
256 	if (!has_map_expired(map)) {
257 		/* A big waste of time. Somebody else did the update */
258 		unlock_map_update(map);
259 		return (SUCCESS);
260 	}
261 
262 	/*
263 	 * We got the lock and nobody beat us to doing the update. Start our
264 	 * own update.
265 	 *
266 	 * Thread will free the update lock when update is complete.
267 	 */
268 
269 
270 	/*
271 	 * Make a copy of the map_ctrl structure so the update thread has an
272 	 * independent version to work with. Note: Must not be on stack.
273 	 *
274 	 * On exit the update thread must free this.
275 	 */
276 	new_map = dup_map_ctrl(map);
277 	if (NULL == new_map) {
278 		unlock_map_update(map);
279 		return (FAILURE);
280 	}
281 
282 	/*
283 	 * While thread is running unlock map so other processes can
284 	 * execute non update related accesses
285 	 */
286 	unlock_map_ctrl(map);
287 
288 	flags = THR_BOUND | THR_NEW_LWP;
289 
290 	/*
291 	 * If we are not going to thr_join then need to create detached.
292 	 * This prevents a zombie being left when nobody joins us.
293 	 */
294 	if (!wait && (getpid() == parent_pid))
295 		flags |= THR_DETACHED;
296 
297 	/* Kick off update thread */
298 	if (0 != thr_create(NULL, NULL, update_thread, new_map,
299 							flags, &tid)) {
300 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
301 				"Could not create NIS update thread");
302 		free_map_ctrl(new_map);
303 		unlock_map_update(map);
304 		if (SUCCESS != lock_map_ctrl(map))
305 			logmsg(MSG_NOTIMECHECK, LOG_ERR,
306 			"Could not acquire update lock for %s", map->map_name);
307 		return (FAILURE);
308 	}
309 
310 	if (wait) {
311 		/* May block but no problem map_ctrl is already unlocked. */
312 		thr_join(tid, NULL, NULL);
313 	}
314 
315 	/* Re acquire lock */
316 	if (1 != lock_map_ctrl(map)) {
317 		logmsg(MSG_NOTIMECHECK, LOG_ERR,
318 			"Could not re-acquire lock for %s", map->map_name);
319 		return (FAILURE);
320 	}
321 
322 	return (SUCCESS);
323 }
324 
325 /*
326  * FUNCTION:	update_thread()
327  *
328  * DESCRIPTION:	The update thread this is called to update an entire NIS map.
329  *		if several NIS maps are found to be out of date several
330  *		instances of this may be running at the same time.
331  *
332  *		Since we are using a duplicate map_ctrl we do not have to lock
333  *		it. If we did would end up using the same mutex as the parent
334  *		map ctrl an possibly deadlocking.
335  *
336  * INPUTS:	Map handle (because we need access to name and lock)
337  *
338  * OUTPUTS:	None exits when finished.
339  */
340 
341 void *
342 update_thread(void *arg)
343 {
344 	void *ret = (void *)-1;
345 	map_ctrl *map;
346 
347 	/* Cast argument pointer to correct type */
348 	map = (map_ctrl *)arg;
349 
350 	/* Actually do the work */
351 	if (SUCCESS == update_map_from_dit(map, FALSE))
352 		ret = 0;
353 
354 	/* Update complete or failed */
355 	unlock_map_update(map);
356 
357 	/* Free up duplicate copy of the map_ctrl */
358 	free_map_ctrl(map);
359 
360 	thr_exit(ret);
361 }
362 
363 /*
364  * FUNCTION :	is_special_key()
365  *
366  * DESCRIPTION:	Works out if a given key is one of the special ones. We just
367  *		check for the "YP_" prefix. This is not 100% safe but if
368  *		valid keys with a "YP_" prefix exist in the DIT then a lot of
369  *		other parts of NIS wont work.
370  */
371 bool_t
372 is_special_key(datum *key)
373 {
374 	if (0 == strncmp(key->dptr, yp_prefix, yp_prefix_sz))
375 		return (TRUE);
376 
377 	return (FALSE);
378 }
379