2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * DESCRIPTION: Contains utilities relating to TTL calculation.
2N/A */
2N/A#include <unistd.h>
2N/A#include <syslog.h>
2N/A#include <errno.h>
2N/A#include <strings.h>
2N/A#include <ndbm.h>
2N/A#include "ypsym.h"
2N/A#include "ypdefs.h"
2N/A#include "shim.h"
2N/A#include "yptol.h"
2N/A#include "../ldap_util.h"
2N/A
2N/A/*
2N/A * Constants used in time calculations
2N/A */
2N/A#define MILLION 1000000
2N/A
2N/A/*
2N/A * Decs
2N/A */
2N/Asuc_code is_greater_timeval(struct timeval *, struct timeval *);
2N/Asuc_code add_to_timeval(struct timeval *, int);
2N/A
2N/A/*
2N/A * FUNCTION: has_entry_expired()
2N/A *
2N/A * DESCRIPTION: Determines if an individual entry has expired.
2N/A *
2N/A * INPUTS: Map control structure for an open map
2N/A * Entry key
2N/A *
2N/A * OUTPUTS: TRUE = Entry has expired or cannot be found this will cause
2N/A * missing entries to be pulled out of the DIT.
2N/A * FALSE = Entry has not expired
2N/A *
2N/A */
2N/Abool_t
2N/Ahas_entry_expired(map_ctrl *map, datum *key)
2N/A{
2N/A datum ttl;
2N/A struct timeval now;
2N/A struct timeval old_time;
2N/A char *key_name;
2N/A const char *myself = "has_entry_expired";
2N/A
2N/A if ((map == NULL) || (map->ttl == NULL))
2N/A return (FALSE);
2N/A
2N/A /* Get expiry time entry for key */
2N/A ttl = dbm_fetch(map->ttl, *key);
2N/A
2N/A if (NULL == ttl.dptr) {
2N/A /*
2N/A * If we failed to get a map expiry key, which must always be
2N/A * present, then something is seriously wrong. Try to recreate
2N/A * the map.
2N/A */
2N/A if ((key->dsize == strlen(MAP_EXPIRY_KEY)) &&
2N/A (0 == strncmp(key->dptr, MAP_EXPIRY_KEY, key->dsize))) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR, "Cannot find %s TTL "
2N/A "for map %s. Will attempt to recreate map",
2N/A MAP_EXPIRY_KEY, map->map_name);
2N/A return (TRUE);
2N/A }
2N/A
2N/A /*
2N/A * Not a problem just no TTL entry for this entry. Maybe it has
2N/A * not yet been downloaded. Maybe it will be handled by a
2N/A * service other than NIS. Check if the entire map has expired.
2N/A * This prevents repeated LDAP reads when requests are made for
2N/A * nonexistant entries.
2N/A */
2N/A if (has_map_expired(map)) {
2N/A /* Kick of a map update */
2N/A update_map_if_required(map, FALSE);
2N/A }
2N/A
2N/A /* Don't update the entry */
2N/A return (FALSE);
2N/A }
2N/A
2N/A if (ttl.dsize != sizeof (struct timeval)) {
2N/A /*
2N/A * Need to malloc some memory before can syslog the key name
2N/A * but this may fail. Solution log a simple message first THEn
2N/A * a more detailed one if it works.
2N/A */
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "Invalid TTL key in map %s. error %d",
2N/A map->map_name, dbm_error(map->ttl));
2N/A
2N/A /* Log the key name */
2N/A key_name = (char *)am(myself, key->dsize + 1);
2N/A if (NULL == key_name) {
2N/A logmsg(MSG_NOMEM, LOG_ERR,
2N/A "Could not alloc memory for keyname");
2N/A } else {
2N/A strncpy(key_name, key->dptr, key->dsize);
2N/A key_name[key->dsize] = '\0';
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "Key name was %s", key_name);
2N/A sfree(key_name);
2N/A }
2N/A /* Update it Anyway */
2N/A return (TRUE);
2N/A }
2N/A
2N/A /* Get current time */
2N/A gettimeofday(&now, NULL);
2N/A
2N/A /*
2N/A * Because dptr may not be int aligned need to build an int
2N/A * out of what it points to or will get a bus error
2N/A */
2N/A bcopy(ttl.dptr, &old_time, sizeof (struct timeval));
2N/A
2N/A return (is_greater_timeval(&now, &old_time));
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: has_map_expired()
2N/A *
2N/A * DESCRIPTION: Determines if an entire map has expire
2N/A *
2N/A * INPUTS: Map control structure for an open map
2N/A *
2N/A * OUTPUTS: TRUE = Map has expired
2N/A * FALSE Map has not expired
2N/A *
2N/A */
2N/Abool_t
2N/Ahas_map_expired(map_ctrl *map)
2N/A{
2N/A datum key;
2N/A
2N/A /* Set up datum with magic expiry key */
2N/A key.dsize = strlen(MAP_EXPIRY_KEY);
2N/A key.dptr = MAP_EXPIRY_KEY;
2N/A
2N/A /* Call has_entry_expired() with magic map expiry key */
2N/A return (has_entry_expired(map, &key));
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: update_entry_ttl()
2N/A *
2N/A * DESCRIPTION: Updates the TTL for one map entry
2N/A *
2N/A * INPUTS: Map control structure for an open map
2N/A * Entry key
2N/A * Flag indication if TTL should be max, min or random
2N/A *
2N/A * OUTPUTS: SUCCESS = TTL updated
2N/A * FAILURE = TTL not updated
2N/A *
2N/A */
2N/A
2N/Asuc_code
2N/Aupdate_entry_ttl(map_ctrl *map, datum *key, TTL_TYPE type)
2N/A{
2N/A datum expire;
2N/A struct timeval now;
2N/A int ttl;
2N/A
2N/A /* Get current time */
2N/A gettimeofday(&now, NULL);
2N/A
2N/A /* Get TTL from mapping file */
2N/A ttl = get_ttl_value(map, type);
2N/A
2N/A if (FAILURE == add_to_timeval(&now, ttl))
2N/A return (FAILURE);
2N/A
2N/A /* Convert time into a datum */
2N/A expire.dsize = sizeof (struct timeval);
2N/A expire.dptr = (char *)&now;
2N/A
2N/A /* Set expiry time entry for key */
2N/A errno = 0;
2N/A if (0 > dbm_store(map->ttl, *key, expire, DBM_REPLACE)) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR, "Could not write TTL entry "
2N/A "(errno=%d)", errno);
2N/A return (FAILURE);
2N/A }
2N/A
2N/A return (SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: update_map_ttl()
2N/A *
2N/A * DESCRIPTION: Updates the TTL for entire map. This can be called either with
2N/A * the map open (map_ctrl DBM pointer set up) or the map closed
2N/A * (map_ctrl DBM pointers not set). The latter case will occur
2N/A * when we have just created a new map.
2N/A *
2N/A * This function must open the TTL map but, in either case, must
2N/A * return with the map_ctrl in it's original state.
2N/A *
2N/A * INPUTS: Map control structure for an open map
2N/A *
2N/A * OUTPUTS: SUCCESS = TTL updated
2N/A * FAILURE = TTL not updated
2N/A *
2N/A */
2N/Asuc_code
2N/Aupdate_map_ttl(map_ctrl *map)
2N/A{
2N/A datum key;
2N/A bool_t map_was_open = TRUE;
2N/A suc_code ret;
2N/A
2N/A /* Set up datum with magic expiry key */
2N/A key.dsize = strlen(MAP_EXPIRY_KEY);
2N/A key.dptr = MAP_EXPIRY_KEY;
2N/A
2N/A /* If TTL not open open it */
2N/A if (NULL == map->ttl) {
2N/A map->ttl = dbm_open(map->ttl_path, O_RDWR, 0644);
2N/A if (NULL == map->ttl)
2N/A return (FAILURE);
2N/A map_was_open = FALSE;
2N/A }
2N/A
2N/A /* Call update_entry_ttl() with magic map expiry key */
2N/A ret = update_entry_ttl(map, &key, TTL_MIN);
2N/A
2N/A /* If we had to open TTL file close it */
2N/A if (!map_was_open) {
2N/A dbm_close(map->ttl);
2N/A map->ttl_path = NULL;
2N/A }
2N/A
2N/A return (ret);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: add_to_timeval()
2N/A *
2N/A * DESCRIPTION: Adds an int to a timeval
2N/A *
2N/A * NOTE : Seems strange that there is not a library function to do this
2N/A * if one exists then this function can be removed.
2N/A *
2N/A * NOTE : Does not handle UNIX clock wrap round but this is a much bigger
2N/A * problem.
2N/A *
2N/A * INPUTS: Time value to add to
2N/A * Time value to add in seconds
2N/A *
2N/A * OUTPUTS: SUCCESS = Addition successful
2N/A * FAILURE = Addition failed (probably wrapped)
2N/A *
2N/A */
2N/Asuc_code
2N/Aadd_to_timeval(struct timeval *t1, int t2)
2N/A{
2N/A long usec;
2N/A struct timeval oldval;
2N/A
2N/A oldval.tv_sec = t1->tv_sec;
2N/A
2N/A /* Add seconds part */
2N/A t1->tv_sec += t2;
2N/A
2N/A /* Check for clock wrap */
2N/A if (!(t1->tv_sec >= oldval.tv_sec)) {
2N/A logmsg(MSG_NOTIMECHECK, LOG_ERR,
2N/A "Wrap when adding %d to %d", t2, oldval.tv_sec);
2N/A return (FAILURE);
2N/A }
2N/A
2N/A return (SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * FUNCTION: is_greater_timeval()
2N/A *
2N/A * DESCRIPTION: Compares two timevals
2N/A *
2N/A * NOTE : Seems strange that there is not a library function to do this
2N/A * if one exists then this function can be removed.
2N/A *
2N/A * INPUTS: First time value
2N/A * Time value to compare it with
2N/A *
2N/A * OUTPUTS: TRUE t1 > t2
2N/A * FALSE t1 <= t2
2N/A *
2N/A */
2N/Asuc_code
2N/Ais_greater_timeval(struct timeval *t1, struct timeval *t2)
2N/A{
2N/A if (t1->tv_sec > t2->tv_sec)
2N/A return (TRUE);
2N/A
2N/A if (t1->tv_sec == t2->tv_sec) {
2N/A if (t1->tv_usec > t2->tv_usec)
2N/A return (TRUE);
2N/A else
2N/A return (FALSE);
2N/A }
2N/A
2N/A return (FALSE);
2N/A}