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
update_entry_if_required(map_ctrl * map,datum * key)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
update_from_dit(map_ctrl * map,datum * key)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
update_map_if_required(map_ctrl * map,bool_t wait)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 *
update_thread(void * arg)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
is_special_key(datum * key)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