8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / libnisdb / yptol / shim.c
blob89795b502ae65b5309562da6871fc690b28b4097
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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
42 * freed.
45 #include <unistd.h>
46 #include <syslog.h>
47 #include <ndbm.h>
48 #include <strings.h>
49 #include "ypsym.h"
50 #include "ypdefs.h"
51 #include "shim.h"
52 #include "yptol.h"
53 #include "stubs.h"
54 #include "../ldap_parse.h"
55 #include "../ldap_util.h"
58 * Globals
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 */
71 * Decs
73 void check_old_map_date(map_ctrl *);
76 * Constants
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.
90 void
91 shim_dbm_close(DBM *db)
93 map_ctrl *map;
95 /* Lock the map */
96 map = get_map_ctrl(db);
97 if (map == NULL)
98 return;
100 free_map_ctrl(map);
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
108 * future.
110 * INPUTS: Identical to equivalent dbm call.
112 * OUTPUTS: Identical to equivalent dbm call.
116 shim_dbm_delete(DBM *db, datum key)
118 int ret;
119 map_ctrl *map;
121 /* Lock the map */
122 map = get_map_ctrl(db);
123 if (map == NULL)
124 return (FAILURE);
125 if (1 != lock_map_ctrl(map))
126 return (FAILURE);
128 if (yptol_mode) {
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);
137 return (ret);
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.
151 datum
152 shim_dbm_fetch(DBM *db, datum key)
154 datum ret = {0, NULL};
155 map_ctrl *map;
157 /* Lock the map */
158 map = get_map_ctrl(db);
159 if (map == NULL)
160 return (ret);
161 if (1 != lock_map_ctrl(map))
162 return (ret);
164 if (yptol_mode) {
165 if (SUCCESS == update_entry_if_required(map, &key)) {
166 /* Update thinks we should return something */
167 ret = dbm_fetch(map->entries, key);
169 } else {
170 /* Non yptol mode do a normal fetch */
171 ret = dbm_fetch(map->entries, key);
174 unlock_map_ctrl(map);
176 return (ret);
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.
190 datum
191 shim_dbm_fetch_noupdate(DBM *db, datum key)
193 datum ret = {0, NULL};
194 map_ctrl *map;
196 /* Get the map control block */
197 map = get_map_ctrl(db);
198 if (map == NULL)
199 return (ret);
201 /* Not updating so no need to lock */
202 ret = dbm_fetch(map->entries, key);
204 return (ret);
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
212 * created.
214 * INPUTS: Identical to equivalent dbm call.
216 * OUTPUTS: Identical to equivalent dbm call.
219 datum
220 shim_dbm_firstkey(DBM *db)
222 int count;
223 bool_t wait_flag;
225 datum ret = {0, NULL};
226 map_ctrl *map;
228 /* Lock the map */
229 map = get_map_ctrl(db);
230 if (map == NULL)
231 return (ret);
232 if (1 != lock_map_ctrl(map))
233 return (ret);
235 if (yptol_mode) {
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.
250 wait_flag = FALSE;
251 update_map_if_required(map, wait_flag);
253 if (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
268 * doing this.
270 dbm_close(map->entries);
271 dbm_close(map->ttl);
272 if (FAILURE == open_yptol_files(map)) {
273 logmsg(MSG_NOTIMECHECK, LOG_ERR,
274 "Could not reopen DBM files");
276 } else {
277 /* For daemons that don't wait just try once */
278 break;
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 */
290 if (yptol_mode) {
291 set_key_data(map, &ret);
293 unlock_map_ctrl(map);
295 return (ret);
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.
309 datum
310 shim_dbm_nextkey(DBM *db)
312 datum ret;
313 map_ctrl *map;
315 /* Lock the map */
316 map = get_map_ctrl(db);
317 if (map == NULL)
318 return (ret);
319 if (1 != lock_map_ctrl(map))
320 return (ret);
322 ret = dbm_nextkey(map->entries);
324 /* Move key data out of static memory. See NOTE in file header above */
325 if (yptol_mode) {
326 set_key_data(map, &ret);
329 unlock_map_ctrl(map);
331 return (ret);
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
343 * the illegal call.
345 * INPUTS: Identical to equivalent dbm call.
347 * OUTPUTS: Identical to equivalent dbm call.
350 datum
351 shim_dbm_do_nextkey(DBM *db, datum inkey)
353 datum ret;
354 map_ctrl *map;
356 /* Lock the map */
357 map = get_map_ctrl(db);
358 if (map == NULL)
359 return (ret);
360 if (1 != lock_map_ctrl(map))
361 return (ret);
363 ret = dbm_do_nextkey(map->entries, inkey);
365 /* Move key data out of static memory. See NOTE in file header above */
366 if (yptol_mode) {
367 set_key_data(map, &ret);
370 unlock_map_ctrl(map);
372 return (ret);
375 * FUNCTION: shim_dbm_open()
377 * INPUTS: Identical to equivalent dbm call.
379 * OUTPUTS: Identical to equivalent dbm call.
382 DBM *
383 shim_dbm_open(const char *file, int open_flags, mode_t file_mode)
385 map_ctrl *map;
386 suc_code ret = FAILURE;
388 /* Find or create map_ctrl for this map */
389 map = create_map_ctrl((char *)file);
391 if (map == NULL)
392 return (NULL);
394 /* Lock map */
395 if (1 != lock_map_ctrl(map))
396 return (NULL);
398 /* Remember flags and mode in case we have to reopen */
399 map->open_flags = open_flags;
400 map->open_mode = file_mode;
402 if (yptol_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
408 * updated.
410 if (SUCCESS == ret)
411 check_old_map_date(map);
413 } else {
414 /* Open entries map */
415 map->entries = dbm_open(map->map_path, map->open_flags,
416 map->open_mode);
418 if (NULL != map->entries)
419 ret = SUCCESS;
422 /* If we were not successful unravel what we have done so far */
423 if (ret != SUCCESS) {
424 unlock_map_ctrl(map);
425 free_map_ctrl(map);
426 return (NULL);
429 unlock_map_ctrl(map);
431 /* Return map_ctrl pointer as a DBM *. To the outside world it is */
432 /* opaque. */
433 return ((DBM *)map);
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)
456 int ret;
457 map_ctrl *map;
459 /* Get map name */
460 map = get_map_ctrl(db);
461 if (map == NULL)
462 return (FAILURE);
464 if (yptol_mode) {
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))
468 return (FAILURE);
471 /* Lock the map */
472 if (1 != lock_map_ctrl(map))
473 return (FAILURE);
475 if (yptol_mode) {
476 if (!is_map_updating(map)) {
477 ret = dbm_store(map->entries, key, content,
478 DBM_REPLACE);
480 if (SUCCESS == ret)
481 /* Update TTL */
482 update_entry_ttl(map, &key, TTL_RAND);
484 } else {
485 ret = dbm_store(map->entries, key, content, store_mode);
488 unlock_map_ctrl(map);
490 return (ret);
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()
504 * RETURNS : Never
506 void
507 shim_exit(int code)
509 thr_join(NULL, NULL, NULL);
510 exit(code);
514 * FUNCTION : init_yptol_flag()
516 * DESCRIPTION: Initializes two flags these are similar but their function is
517 * subtly different.
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.
526 void
527 init_yptol_flag()
530 * yp2ldap is used to switch appropriate code in the
531 * common libnisdb library used by rpc.nisd and ypserv.
533 yp2ldap = 1;
534 yptol_mode = is_yptol_mode();
536 * Use the new lock mapping mechanism
537 * if in N2L mode.
539 yptol_newlock = yptol_mode;
543 * FUNCTION : set_yxfrd_flag()
545 void
546 set_ypxfrd_flag()
548 ypxfrd_flag = TRUE;
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
556 * logged.
558 * GIVEN : A map_ctrl containing details of the NEW STYLE map.
560 * RETURNS : Nothing
562 void
563 check_old_map_date(map_ctrl *map)
565 datum key;
566 datum value;
567 struct stat stats;
568 time_t old_time;
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
575 * instead of NIS.
577 return;
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 */
596 return;
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
624 * which do.
626 * RETURNS : TRUE = Everything worked
627 * FALSE = There were problems
629 bool_t
630 init_lock_system(bool_t ypxfrd)
632 /* Remember what called us */
633 if (ypxfrd)
634 set_ypxfrd_flag();
637 * Remember PID of process which called us. This enables update threads
638 * created by YP children to be handled differently to those created
639 * by YP parents.
641 parent_pid = getpid();
643 /* Init map locks */
644 if (!init_lock_map()) {
645 logmsg(MSG_NOTIMECHECK, LOG_ERR,
646 "Failed to init process synchronization");
647 return (FALSE);
650 /* If we are in yptol mode set flag indicating the fact */
651 init_yptol_flag();
654 * If boot random number system. For now go for reproducible
655 * random numbers.
657 srand48(0x12345678);
660 * If not N2L mode then no error but do not bother initializing update
661 * flags.
663 if (yptol_mode) {
664 if (!init_update_lock_map()) {
665 logmsg(MSG_NOTIMECHECK, LOG_ERR,
666 "Failed to init update synchronization");
667 return (FALSE);
671 return (TRUE);