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