4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2015 Gary Mills
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * DESCRIPTION: Contains the top level shim hook functions. These must have
29 * identical interfaces to the equivalent standard dbm calls.
31 * Unfortunately many of these will do a copy of a datum structure
32 * on return. This is a side effect of the original DBM function
33 * being written to pass structures rather than pointers.
35 * NOTE : There is a major bug/feature in dbm. A key obtained by
36 * dbm_nextkey() of dbm_firstkey() cannot be passed to dbm_store().
37 * When the store occurs dbm's internal memory get's reorganized
38 * and the static strings pointed to by the key are destroyed. The
39 * data is then stored in the wrong place. We attempt to get round
40 * this by dbm_firstkey() and dbm_nextkey() making a copy of the
41 * key data in malloced memory. This is freed when map_ctrl is
54 #include "../ldap_parse.h"
55 #include "../ldap_util.h"
60 bool_t yptol_mode
= FALSE
; /* Set if in N2L mode */
61 bool_t yptol_newlock
= FALSE
;
63 * Set if in N2L mode and we want to use the new
64 * lock mapping mechanism
66 bool_t ypxfrd_flag
= FALSE
; /* Set if called from ypxfrd */
67 pid_t parent_pid
; /* ID of calling parent process */
73 void check_old_map_date(map_ctrl
*);
78 /* Number of times to try to update a map before giving up */
79 /* #define MAX_UPDATE_ATTEMPTS 3 */
80 #define MAX_UPDATE_ATTEMPTS 1
83 * FUNCTION: shim_dbm_close();
85 * INPUTS: Identical to equivalent dbm call.
87 * OUTPUTS: Identical to equivalent dbm call.
91 shim_dbm_close(DBM
*db
)
96 map
= get_map_ctrl(db
);
104 * FUNCTION: shim_dbm_delete();
106 * DESCRIPTION: This function is currently unused but is present so that the
107 * set of shim_dbm_xxx() interfaces is complete if required in
110 * INPUTS: Identical to equivalent dbm call.
112 * OUTPUTS: Identical to equivalent dbm call.
116 shim_dbm_delete(DBM
*db
, datum key
)
122 map
= get_map_ctrl(db
);
125 if (1 != lock_map_ctrl(map
))
129 /* Delete from and ttl map. Not a huge disaster if it fails. */
130 dbm_delete(map
->ttl
, key
);
133 ret
= dbm_delete(map
->entries
, key
);
135 unlock_map_ctrl(map
);
142 * FUNCTION: shim_dbm_fetch()
144 * DESCRIPTION: N2L function used to handle 'normal' dbm_fetch() operations.
146 * INPUTS: First two identical to equivalent dbm call.
148 * OUTPUTS: Identical to equivalent dbm call.
152 shim_dbm_fetch(DBM
*db
, datum key
)
154 datum ret
= {0, NULL
};
158 map
= get_map_ctrl(db
);
161 if (1 != lock_map_ctrl(map
))
165 if (SUCCESS
== update_entry_if_required(map
, &key
)) {
166 /* Update thinks we should return something */
167 ret
= dbm_fetch(map
->entries
, key
);
170 /* Non yptol mode do a normal fetch */
171 ret
= dbm_fetch(map
->entries
, key
);
174 unlock_map_ctrl(map
);
180 * FUNCTION: shim_dbm_fetch_noupdate()
182 * DESCRIPTION: A special version of shim_dbm_fetch() that never checks TTLs
183 * or updates entries.
185 * INPUTS: Identical to equivalent dbm call.
187 * OUTPUTS: Identical to equivalent dbm call.
191 shim_dbm_fetch_noupdate(DBM
*db
, datum key
)
193 datum ret
= {0, NULL
};
196 /* Get the map control block */
197 map
= get_map_ctrl(db
);
201 /* Not updating so no need to lock */
202 ret
= dbm_fetch(map
->entries
, key
);
208 * FUNCTION: shim_dbm_firstkey()
210 * DESCRIPTION: Get firstkey in an enumeration. If the map is out of date then
211 * this is the time to scan it and see if any new entries have been
214 * INPUTS: Identical to equivalent dbm call.
216 * OUTPUTS: Identical to equivalent dbm call.
220 shim_dbm_firstkey(DBM
*db
)
225 datum ret
= {0, NULL
};
229 map
= get_map_ctrl(db
);
232 if (1 != lock_map_ctrl(map
))
237 * Due to the limitations in the hashing algorithm ypxfrd
238 * may end up waiting on the wrong update. It must thus loop
239 * until the right map has been updated.
241 for (count
= 0; has_map_expired(map
) &&
242 (MAX_UPDATE_ATTEMPTS
> count
); count
++) {
244 * Ideally ypxfr should wait for the map update
245 * to complete i.e. pass ypxfrd_flag into
246 * update_map_if_required(). This cannot be done
247 * because if there is a large map update the client
248 * side, ypxfr, can time out while waiting.
251 update_map_if_required(map
, wait_flag
);
255 * Because ypxfrd does weird things with DBMs
256 * internal structures it's a good idea to
257 * reopen here. (Code that uses the real DBM
258 * API appears not to need this.)
260 * This should not be necessary all we have
261 * done is 'mv' the new file over the old one.
262 * Open handles should get the old data but if
263 * these lines are removed the first ypxfrd
264 * read access fail with bad file handle.
266 * NOTE : If we don't wait, because of the
267 * ypxfr timeout problem, there is no point
270 dbm_close(map
->entries
);
272 if (FAILURE
== open_yptol_files(map
)) {
273 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
274 "Could not reopen DBM files");
277 /* For daemons that don't wait just try once */
282 if (MAX_UPDATE_ATTEMPTS
< count
)
283 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
284 "Cannot update map %s", map
->map_name
);
287 ret
= dbm_firstkey(map
->entries
);
289 /* Move key data out of static memory. See NOTE in file header above */
291 set_key_data(map
, &ret
);
293 unlock_map_ctrl(map
);
299 * FUNCTION: shim_dbm_nextkey()
301 * DESCRIPTION: Get next key in an enumeration. Since updating an entry would
302 * invalidate the enumeration we never do it.
304 * INPUTS: Identical to equivalent dbm call.
306 * OUTPUTS: Identical to equivalent dbm call.
310 shim_dbm_nextkey(DBM
*db
)
316 map
= get_map_ctrl(db
);
319 if (1 != lock_map_ctrl(map
))
322 ret
= dbm_nextkey(map
->entries
);
324 /* Move key data out of static memory. See NOTE in file header above */
326 set_key_data(map
, &ret
);
329 unlock_map_ctrl(map
);
335 * FUNCTION: shim_dbm_do_nextkey()
337 * DESCRIPTION: Get next key in an enumeration. Since updating an entry would
338 * invalidate the enumeration we never do it.
340 * NOTE : dbm_do_nextkey is not a documented or legal DBM API.
341 * Despite this the existing NIS code calls it. One gross hack
342 * deserves another so we have this extra shim function to handle
345 * INPUTS: Identical to equivalent dbm call.
347 * OUTPUTS: Identical to equivalent dbm call.
351 shim_dbm_do_nextkey(DBM
*db
, datum inkey
)
357 map
= get_map_ctrl(db
);
360 if (1 != lock_map_ctrl(map
))
363 ret
= dbm_do_nextkey(map
->entries
, inkey
);
365 /* Move key data out of static memory. See NOTE in file header above */
367 set_key_data(map
, &ret
);
370 unlock_map_ctrl(map
);
375 * FUNCTION: shim_dbm_open()
377 * INPUTS: Identical to equivalent dbm call.
379 * OUTPUTS: Identical to equivalent dbm call.
383 shim_dbm_open(const char *file
, int open_flags
, mode_t file_mode
)
386 suc_code ret
= FAILURE
;
388 /* Find or create map_ctrl for this map */
389 map
= create_map_ctrl((char *)file
);
395 if (1 != lock_map_ctrl(map
))
398 /* Remember flags and mode in case we have to reopen */
399 map
->open_flags
= open_flags
;
400 map
->open_mode
= file_mode
;
403 ret
= open_yptol_files(map
);
406 * This is a good place to check that the
407 * equivalent old style map file has not been
411 check_old_map_date(map
);
414 /* Open entries map */
415 map
->entries
= dbm_open(map
->map_path
, map
->open_flags
,
418 if (NULL
!= map
->entries
)
422 /* If we were not successful unravel what we have done so far */
423 if (ret
!= SUCCESS
) {
424 unlock_map_ctrl(map
);
429 unlock_map_ctrl(map
);
431 /* Return map_ctrl pointer as a DBM *. To the outside world it is */
437 * FUNCTION: shim_dbm_store()
439 * DESCRIPTION: Shim for dbm_store.
441 * In N2L mode if we are asked to store in DBM_INSERT mode
442 * then first an attempt is made to write to the DIT (in the same
443 * mode). If this is successful then the value is forced into DBM
444 * using DBM_REPLACE. This is because the DIT is authoritative.
445 * The success of failure of an 'insert' is determined by the
446 * presence or otherwise of an entry in the DIT not DBM.
448 * INPUTS: Identical to equivalent dbm call.
450 * OUTPUTS: Identical to equivalent dbm call.
454 shim_dbm_store(DBM
*db
, datum key
, datum content
, int store_mode
)
460 map
= get_map_ctrl(db
);
465 /* Write to the DIT before doing anything else */
466 if (!write_to_dit(map
->map_name
, map
->domain
, key
, content
,
467 DBM_REPLACE
== store_mode
, FALSE
))
472 if (1 != lock_map_ctrl(map
))
476 if (!is_map_updating(map
)) {
477 ret
= dbm_store(map
->entries
, key
, content
,
482 update_entry_ttl(map
, &key
, TTL_RAND
);
485 ret
= dbm_store(map
->entries
, key
, content
, store_mode
);
488 unlock_map_ctrl(map
);
494 * FUNCTION : shim_exit()
496 * DESCRIPTION: Intercepts exit() calls made by N2L compatible NIS components.
497 * This is required because any call to the shim_dbm... series
498 * of functions may have started an update thread. If the process
499 * exits normally then this thread may be killed before it can
500 * complete its work. We thus wait here for the thread to complete.
502 * GIVEN : Same arg as exit()
509 thr_join(NULL
, NULL
, NULL
);
514 * FUNCTION : init_yptol_flag()
516 * DESCRIPTION: Initializes two flags these are similar but their function is
519 * yp2ldap tells the mapping system if it is to work in NIS or
520 * NIS+ mode. For N2L this is always set to NIS mode.
522 * yptol tells the shim if it is to work in N2L or traditional
523 * NIS mode. For N2L this is turned on if the N2L mapping file
524 * is found to be present. In NIS+ mode it is meaningless.
530 * yp2ldap is used to switch appropriate code in the
531 * common libnisdb library used by rpc.nisd and ypserv.
534 yptol_mode
= is_yptol_mode();
536 * Use the new lock mapping mechanism
539 yptol_newlock
= yptol_mode
;
543 * FUNCTION : set_yxfrd_flag()
552 * FUNCTION : check_old_map_date()
554 * DESCRIPTION: Checks that an old style map has not been updated. If it has
555 * then ypmake has probably erroneously been run and an error is
558 * GIVEN : A map_ctrl containing details of the NEW STYLE map.
563 check_old_map_date(map_ctrl
*map
)
570 /* Get date of last update */
571 if (0 != stat(map
->trad_map_path
, &stats
)) {
573 * No problem. We have a new style map but no old style map
574 * this will occur if the original data came from native LDAP
580 /* Set up datum with key for recorded old map update time */
581 key
.dsize
= strlen(MAP_OLD_MAP_DATE_KEY
);
582 key
.dptr
= MAP_OLD_MAP_DATE_KEY
;
583 value
= dbm_fetch(map
->ttl
, key
);
585 if (NULL
!= value
.dptr
) {
587 * Because dptr may not be int aligned need to build an int
588 * out of what it points to or will get a bus error.
590 bcopy(value
.dptr
, &old_time
, sizeof (time_t));
593 /* Do the comparison */
594 if (stats
.st_mtime
<= old_time
) {
595 /* All is well, has not been updated */
599 /* If we get here the file has been updated */
600 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
601 "Caution. ypmake may have been run in N2L "
602 "mode. This will NOT initiate a NIS map push. In "
603 "this mode pushes should be initiated with yppush");
607 * If we get here then either the file was updated or there was not
608 * a valid old map date (no problem, maybe this is the first time we
609 * checked). In either case the old map date entry must be update.
611 value
.dptr
= (char *)&(stats
.st_mtime
);
612 value
.dsize
= sizeof (time_t);
613 dbm_store(map
->ttl
, key
, value
, DBM_REPLACE
);
617 * FUNCTION : init_lock_system()
619 * DESCRIPTION: Initializes all the systems related to map locking. This must
620 * be called before any access to the shim functions.
622 * GIVEN : A flag indicating if we are being called from ypserv, which does
623 * not wait for map updates to complete, or other NIS components
626 * RETURNS : TRUE = Everything worked
627 * FALSE = There were problems
630 init_lock_system(bool_t ypxfrd
)
632 /* Remember what called us */
637 * Remember PID of process which called us. This enables update threads
638 * created by YP children to be handled differently to those created
641 parent_pid
= getpid();
644 if (!init_lock_map()) {
645 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
646 "Failed to init process synchronization");
650 /* If we are in yptol mode set flag indicating the fact */
654 * If boot random number system. For now go for reproducible
660 * If not N2L mode then no error but do not bother initializing update
664 if (!init_update_lock_map()) {
665 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
666 "Failed to init update synchronization");