/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2015 Gary Mills * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * DESCRIPTION: Contains the map update thread and related code. */ #include #include #include #include #include #include #include "ypsym.h" #include "ypdefs.h" #include "shim.h" #include "yptol.h" #include "../ldap_util.h" /* Enable standard YP code features defined in ypdefs.h */ USE_YP_PREFIX USE_YP_MASTER_NAME USE_YP_LAST_MODIFIED USE_YP_INPUT_FILE USE_YP_OUTPUT_NAME USE_YP_DOMAIN_NAME USE_YP_SECURE USE_YP_INTERDOMAIN /* * Decs */ suc_code update_from_dit(map_ctrl *, datum *); void * update_thread(void *); /* * Globals */ extern pid_t parent_pid; /* * FUNCTION: update_entry_if_required() * * DESCRIPTION: Determines if an entry is to be updated and if it is does the * update. * * GIVEN : Pointer to the open map ctrl * Pointer to the entry key * * RETURNS : SUCCESS = Entry is in a state to be returned to the client * i.e. either got updated, did not need to be updated or we are * in a mode where it is acceptable to return out of date * information. * FAILURE = Entry need an update but it could not be done. */ suc_code update_entry_if_required(map_ctrl *map, datum *key) { /* Only update individual entries if entire map is */ /* not being updated */ if (is_map_updating(map)) return (SUCCESS); /* * If we are being asked for the order then need to check if * the map is in need of an update. If it is then fake a * recent order. The client will then read the map, using * dbm_firstkey and this will do the update. */ if (0 == strncmp(key->dptr, yp_last_modified, yp_last_modified_sz)) { if (has_map_expired(map)) update_timestamp(map->entries); return (SUCCESS); } /* Never update special keys. Have no TTLs */ if (is_special_key(key)) return (SUCCESS); if (!has_entry_expired(map, key)) /* Didn't need an update */ return (SUCCESS); /* Do the update */ return (update_from_dit(map, key)); } /* * FUNCTION: update_from_dit() * * DESCRIPTION: Called to update an entry from the DIT * * INPUTS: Map control structure for an open map * Entry key * * OUTPUTS: SUCCESS = Update complete or we are in a mode where it is * acceptable to return out of date information. * FAILURE = Update failed * */ suc_code update_from_dit(map_ctrl *map, datum *key) { datum dat; int ret; suc_code res; /* * Netgroup maps are a special case we cannot update just one entry so * update the entire map instead. */ if ((0 == strcmp(map->map_name, NETGROUP_BYHOST)) || (0 == strcmp(map->map_name, NETGROUP_BYUSER))) { return (update_map_if_required(map, FALSE)); } /* Read entry from the DIT */ ret = read_from_dit(map->map_name, map->domain, key, &dat); /* Check that we got something */ if (NULL == dat.dptr) { if (0 == ret) { /* * In a mode where it is acceptable to return out of * date information. */ logmsg(MSG_NOTIMECHECK, LOG_INFO, "LDAP inaccessible returning old information"); return (SUCCESS); } else { /* * In a mode where it is not acceptable to return out * of date information. * * If the error positviely indicates that there is no * such entry delete it. For errors where object may * still exist in the DIT leave it. */ if (MAP_NO_MATCHING_KEY == ret) { /* * Don't log errors. If the entry was not * already present then no problem. The user * just asked us for a non existant entry. */ dbm_delete(map->entries, *key); dbm_delete(map->ttl, *key); } return (FAILURE); } } /* Write it to DBM */ res = dbm_store(map->entries, *key, dat, DBM_REPLACE); sfree(dat.dptr); if (SUCCESS != res) return (FAILURE); /* Update TTL */ update_entry_ttl(map, key, TTL_RUNNING); return (SUCCESS); } /* * FUNCTION: update_map_if_required() * * DESCRIPTION: Called to update an entire map if it is out of date. Map ctrl * must be locked before this is called. This handles checking if * the map is already being updated. It is important that this is * done atomically with obtaining the maps update lock. * * INPUTS: Map control structure for an open map * Flag indication if we should wait for completion * * OUTPUTS: SUCCESS = Map update initiated * FAILURE = Map update not initiated */ suc_code update_map_if_required(map_ctrl *map, bool_t wait) { thread_t tid; map_ctrl *new_map; suc_code res; long flags; if (wait) { /* * Actually get the lock * * May block so unlock map_ctrl while it is done */ unlock_map_ctrl(map); res = lock_map_update(map); lock_map_ctrl(map); if (SUCCESS != res) { logmsg(MSG_NOTIMECHECK, LOG_ERR, "Could not lock map %s for update", map->map_name); return (FAILURE); } } else { /* If not waiting try to get the lock */ switch (try_lock_map_update(map)) { case 0: /* * We got the lock. Continue to start an update. */ break; case EBUSY: /* * Some one else got the lock. OK they are * doing the update so we can just return. */ return (SUCCESS); default: /* * Some serious problem with lock. */ return (FAILURE); } } /* * If we get here are holding the update lock. Make a final check that * nobody beat us to the map update while we were getting it. */ if (!has_map_expired(map)) { /* A big waste of time. Somebody else did the update */ unlock_map_update(map); return (SUCCESS); } /* * We got the lock and nobody beat us to doing the update. Start our * own update. * * Thread will free the update lock when update is complete. */ /* * Make a copy of the map_ctrl structure so the update thread has an * independent version to work with. Note: Must not be on stack. * * On exit the update thread must free this. */ new_map = dup_map_ctrl(map); if (NULL == new_map) { unlock_map_update(map); return (FAILURE); } /* * While thread is running unlock map so other processes can * execute non update related accesses */ unlock_map_ctrl(map); flags = THR_BOUND | THR_NEW_LWP; /* * If we are not going to thr_join then need to create detached. * This prevents a zombie being left when nobody joins us. */ if (!wait && (getpid() == parent_pid)) flags |= THR_DETACHED; /* Kick off update thread */ if (0 != thr_create(NULL, NULL, update_thread, new_map, flags, &tid)) { logmsg(MSG_NOTIMECHECK, LOG_ERR, "Could not create NIS update thread"); free_map_ctrl(new_map); unlock_map_update(map); if (SUCCESS != lock_map_ctrl(map)) logmsg(MSG_NOTIMECHECK, LOG_ERR, "Could not acquire update lock for %s", map->map_name); return (FAILURE); } if (wait) { /* May block but no problem map_ctrl is already unlocked. */ thr_join(tid, NULL, NULL); } /* Re acquire lock */ if (1 != lock_map_ctrl(map)) { logmsg(MSG_NOTIMECHECK, LOG_ERR, "Could not re-acquire lock for %s", map->map_name); return (FAILURE); } return (SUCCESS); } /* * FUNCTION: update_thread() * * DESCRIPTION: The update thread this is called to update an entire NIS map. * if several NIS maps are found to be out of date several * instances of this may be running at the same time. * * Since we are using a duplicate map_ctrl we do not have to lock * it. If we did would end up using the same mutex as the parent * map ctrl an possibly deadlocking. * * INPUTS: Map handle (because we need access to name and lock) * * OUTPUTS: None exits when finished. */ void * update_thread(void *arg) { void *ret = (void *)-1; map_ctrl *map; /* Cast argument pointer to correct type */ map = (map_ctrl *)arg; /* Actually do the work */ if (SUCCESS == update_map_from_dit(map, FALSE)) ret = 0; /* Update complete or failed */ unlock_map_update(map); /* Free up duplicate copy of the map_ctrl */ free_map_ctrl(map); thr_exit(ret); return (NULL); } /* * FUNCTION : is_special_key() * * DESCRIPTION: Works out if a given key is one of the special ones. We just * check for the "YP_" prefix. This is not 100% safe but if * valid keys with a "YP_" prefix exist in the DIT then a lot of * other parts of NIS wont work. */ bool_t is_special_key(datum *key) { if (0 == strncmp(key->dptr, yp_prefix, yp_prefix_sz)) return (TRUE); return (FALSE); }