Unleashed v1.4
[unleashed.git] / usr / src / lib / libnisdb / yptol / update.c
blob02e6d26ac53a3c987b2ba5dfee0e936263f2f044
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
23 * Copyright 2015 Gary Mills
24 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
25 * Use is subject to license terms.
29 * DESCRIPTION: Contains the map update thread and related code.
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"
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
55 * Decs
57 suc_code update_from_dit(map_ctrl *, datum *);
58 void * update_thread(void *);
61 * Globals
63 extern pid_t parent_pid;
66 * FUNCTION: update_entry_if_required()
68 * DESCRIPTION: Determines if an entry is to be updated and if it is does the
69 * update.
71 * GIVEN : Pointer to the open map ctrl
72 * Pointer to the entry key
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.
80 suc_code
81 update_entry_if_required(map_ctrl *map, datum *key)
84 /* Only update individual entries if entire map is */
85 /* not being updated */
86 if (is_map_updating(map))
87 return (SUCCESS);
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.
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);
101 /* Never update special keys. Have no TTLs */
102 if (is_special_key(key))
103 return (SUCCESS);
105 if (!has_entry_expired(map, key))
106 /* Didn't need an update */
107 return (SUCCESS);
109 /* Do the update */
110 return (update_from_dit(map, key));
114 * FUNCTION: update_from_dit()
116 * DESCRIPTION: Called to update an entry from the DIT
118 * INPUTS: Map control structure for an open map
119 * Entry key
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
126 suc_code
127 update_from_dit(map_ctrl *map, datum *key)
129 datum dat;
130 int ret;
131 suc_code res;
134 * Netgroup maps are a special case we cannot update just one entry so
135 * update the entire map instead.
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));
142 /* Read entry from the DIT */
143 ret = read_from_dit(map->map_name, map->domain, key, &dat);
145 /* Check that we got something */
146 if (NULL == dat.dptr) {
147 if (0 == ret) {
149 * In a mode where it is acceptable to return out of
150 * date information.
152 logmsg(MSG_NOTIMECHECK, LOG_INFO,
153 "LDAP inaccessible returning old information");
154 return (SUCCESS);
155 } else {
157 * In a mode where it is not acceptable to return out
158 * of date information.
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.
164 if (MAP_NO_MATCHING_KEY == ret) {
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.
170 dbm_delete(map->entries, *key);
171 dbm_delete(map->ttl, *key);
173 return (FAILURE);
177 /* Write it to DBM */
178 res = dbm_store(map->entries, *key, dat, DBM_REPLACE);
179 sfree(dat.dptr);
181 if (SUCCESS != res)
182 return (FAILURE);
184 /* Update TTL */
185 update_entry_ttl(map, key, TTL_RUNNING);
187 return (SUCCESS);
191 * FUNCTION: update_map_if_required()
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.
198 * INPUTS: Map control structure for an open map
199 * Flag indication if we should wait for completion
201 * OUTPUTS: SUCCESS = Map update initiated
202 * FAILURE = Map update not initiated
204 suc_code
205 update_map_if_required(map_ctrl *map, bool_t wait)
207 thread_t tid;
208 map_ctrl *new_map;
209 suc_code res;
210 long flags;
212 if (wait) {
214 * Actually get the lock
216 * May block so unlock map_ctrl while it is done
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);
227 } else {
228 /* If not waiting try to get the lock */
229 switch (try_lock_map_update(map)) {
230 case 0:
232 * We got the lock. Continue to start an update.
234 break;
236 case EBUSY:
238 * Some one else got the lock. OK they are
239 * doing the update so we can just return.
241 return (SUCCESS);
243 default:
245 * Some serious problem with lock.
247 return (FAILURE);
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.
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);
262 * We got the lock and nobody beat us to doing the update. Start our
263 * own update.
265 * Thread will free the update lock when update is complete.
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.
273 * On exit the update thread must free this.
275 new_map = dup_map_ctrl(map);
276 if (NULL == new_map) {
277 unlock_map_update(map);
278 return (FAILURE);
282 * While thread is running unlock map so other processes can
283 * execute non update related accesses
285 unlock_map_ctrl(map);
287 flags = THR_BOUND | THR_NEW_LWP;
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.
293 if (!wait && (getpid() == parent_pid))
294 flags |= THR_DETACHED;
296 /* Kick off update thread */
297 if (0 != thr_create(NULL, 0, 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);
309 if (wait) {
310 /* May block but no problem map_ctrl is already unlocked. */
311 thr_join(tid, NULL, NULL);
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);
321 return (SUCCESS);
325 * FUNCTION: update_thread()
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.
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.
335 * INPUTS: Map handle (because we need access to name and lock)
337 * OUTPUTS: None exits when finished.
340 void *
341 update_thread(void *arg)
343 void *ret = (void *)-1;
344 map_ctrl *map;
346 /* Cast argument pointer to correct type */
347 map = (map_ctrl *)arg;
349 /* Actually do the work */
350 if (SUCCESS == update_map_from_dit(map, FALSE))
351 ret = 0;
353 /* Update complete or failed */
354 unlock_map_update(map);
356 /* Free up duplicate copy of the map_ctrl */
357 free_map_ctrl(map);
359 thr_exit(ret);
361 return (NULL);
365 * FUNCTION : is_special_key()
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.
372 bool_t
373 is_special_key(datum *key)
375 if (0 == strncmp(key->dptr, yp_prefix, yp_prefix_sz))
376 return (TRUE);
378 return (FALSE);