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