OpenSolaris_b135/lib/libnisdb/yptol/ttl_utils.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 * DESCRIPTION: Contains utilities relating to TTL calculation.
 */
#include <unistd.h>
#include <syslog.h>
#include <errno.h>
#include <strings.h>
#include <ndbm.h>
#include "ypsym.h"
#include "ypdefs.h"
#include "shim.h"
#include "yptol.h"
#include "../ldap_util.h"

/*
 * Constants used in time calculations
 */
#define	MILLION 1000000

/*
 * Decs
 */
suc_code is_greater_timeval(struct timeval *, struct timeval *);
suc_code add_to_timeval(struct timeval *, int);

/*
 * FUNCTION:	has_entry_expired()
 *
 * DESCRIPTION:	Determines if an individual entry has expired.
 *
 * INPUTS:	Map control structure for an open map
 *		Entry key
 *
 * OUTPUTS:	TRUE =  Entry has expired or cannot be found this will cause
 *			missing entries to be pulled out of the DIT.
 *		FALSE = Entry has not expired
 *
 */
bool_t
has_entry_expired(map_ctrl *map, datum *key)
{
	datum ttl;
	struct timeval	now;
	struct timeval	old_time;
	char	*key_name;
	char *myself = "has_entry_expired";

	if ((map == NULL) || (map->ttl == NULL))
		return (FALSE);

	/* Get expiry time entry for key */
	ttl = dbm_fetch(map->ttl, *key);

	if (NULL == ttl.dptr) {
		/*
		 * If we failed to get a map expiry key, which must always be
		 * present, then something is seriously wrong. Try to recreate
		 * the map.
		 */
		if ((key->dsize == strlen(MAP_EXPIRY_KEY)) &&
			(0 == strncmp(key->dptr, MAP_EXPIRY_KEY, key->dsize))) {
			logmsg(MSG_NOTIMECHECK, LOG_ERR, "Cannot find %s TTL "
				"for map %s. Will attempt to recreate map",
				MAP_EXPIRY_KEY, map->map_name);
			return (TRUE);
		}

		/*
		 * Not a problem just no TTL entry for this entry. Maybe it has
		 * not yet been downloaded. Maybe it will be handled by a
		 * service other than NIS. Check if the entire map has expired.
		 * This prevents repeated LDAP reads when requests are made for
		 * nonexistant entries.
		 */
		if (has_map_expired(map)) {
			/* Kick of a map update */
			update_map_if_required(map, FALSE);
		}

		/* Don't update the entry */
		return (FALSE);
	}

	if (ttl.dsize != sizeof (struct timeval)) {
		/*
		 * Need to malloc some memory before can syslog the key name
		 * but this may fail. Solution log a simple message first THEn
		 * a more detailed one if it works.
		 */
		logmsg(MSG_NOTIMECHECK, LOG_ERR,
			"Invalid TTL key in map %s. error %d",
					map->map_name, dbm_error(map->ttl));

		/* Log the key name */
		key_name = (char *)am(myself, key->dsize + 1);
		if (NULL == key_name) {
			logmsg(MSG_NOMEM, LOG_ERR,
					"Could not alloc memory for keyname");
		} else {
			strncpy(key_name, key->dptr, key->dsize);
			key_name[key->dsize] = '\0';
			logmsg(MSG_NOTIMECHECK, LOG_ERR,
						"Key name was %s", key_name);
			sfree(key_name);
		}
		/* Update it Anyway */
		return (TRUE);
	}

	/* Get current time */
	gettimeofday(&now, NULL);

	/*
	 * Because dptr may not be int aligned need to build an int
	 * out of what it points to or will get a bus error
	 */
	bcopy(ttl.dptr, &old_time, sizeof (struct timeval));

	return (is_greater_timeval(&now, &old_time));
}

/*
 * FUNCTION:	has_map_expired()
 *
 * DESCRIPTION:	Determines if an entire map has expire
 *
 * INPUTS:	Map control structure for an open map
 *
 * OUTPUTS:	TRUE = Map has expired
 *		FALSE  Map has not expired
 *
 */
bool_t
has_map_expired(map_ctrl *map)
{
	datum key;

	/* Set up datum with magic expiry key */
	key.dsize = strlen(MAP_EXPIRY_KEY);
	key.dptr = MAP_EXPIRY_KEY;

	/* Call has_entry_expired() with magic map expiry key */
	return (has_entry_expired(map, &key));
}

/*
 * FUNCTION:	update_entry_ttl()
 *
 * DESCRIPTION:	Updates the TTL for one map entry
 *
 * INPUTS:	Map control structure for an open map
 *		Entry key
 *		Flag indication if TTL should be max, min or random
 *
 * OUTPUTS:	SUCCESS = TTL updated
 *		FAILURE = TTL not updated
 *
 */

suc_code
update_entry_ttl(map_ctrl *map, datum *key, TTL_TYPE type)
{
	datum expire;
	struct timeval	now;
	int	ttl;

	/* Get current time */
	gettimeofday(&now, NULL);

	/* Get TTL from mapping file */
	ttl = get_ttl_value(map, type);

	if (FAILURE == add_to_timeval(&now, ttl))
		return (FAILURE);

	/* Convert time into a datum */
	expire.dsize = sizeof (struct timeval);
	expire.dptr = (char *)&now;

	/* Set expiry time entry for key */
	errno = 0;
	if (0 > dbm_store(map->ttl, *key, expire, DBM_REPLACE)) {
		logmsg(MSG_NOTIMECHECK, LOG_ERR, "Could not write TTL entry "
						"(errno=%d)", errno);
		return (FAILURE);
	}

	return (SUCCESS);
}

/*
 * FUNCTION:	update_map_ttl()
 *
 * DESCRIPTION:	Updates the TTL for entire map. This can be called either with
 *		the map open (map_ctrl DBM pointer set up) or the map closed
 *		(map_ctrl DBM pointers not set). The latter case will occur
 *		when we have just created a new map.
 *
 *		This function must open the TTL map but, in either case, must
 *		return with the map_ctrl in it's original state.
 *
 * INPUTS:	Map control structure for an open map
 *
 * OUTPUTS:	SUCCESS = TTL updated
 *		FAILURE = TTL not updated
 *
 */
suc_code
update_map_ttl(map_ctrl *map)
{
	datum key;
	bool_t map_was_open = TRUE;
	suc_code ret;

	/* Set up datum with magic expiry key */
	key.dsize = strlen(MAP_EXPIRY_KEY);
	key.dptr = MAP_EXPIRY_KEY;

	/* If TTL not open open it */
	if (NULL == map->ttl) {
		map->ttl = dbm_open(map->ttl_path, O_RDWR, 0644);
		if (NULL == map->ttl)
			return (FAILURE);
		map_was_open = FALSE;
	}

	/* Call update_entry_ttl() with magic map expiry key */
	ret = update_entry_ttl(map, &key, TTL_MIN);

	/* If we had to open TTL file close it */
	if (!map_was_open) {
		dbm_close(map->ttl);
		map->ttl_path = NULL;
	}

	return (ret);
}

/*
 * FUNCTION:	add_to_timeval()
 *
 * DESCRIPTION:	Adds an int to a timeval
 *
 * NOTE :	Seems strange that there is not a library function to do this
 *		if one exists then this function can be removed.
 *
 * NOTE :	Does not handle UNIX clock wrap round but this is a much bigger
 *		problem.
 *
 * INPUTS:	Time value to add to
 *		Time value to add in seconds
 *
 * OUTPUTS:	SUCCESS = Addition successful
 *		FAILURE = Addition failed (probably wrapped)
 *
 */
suc_code
add_to_timeval(struct timeval *t1, int t2)
{
	long usec;
	struct timeval oldval;

	oldval.tv_sec = t1->tv_sec;

	/* Add seconds part */
	t1->tv_sec += t2;

	/* Check for clock wrap */
	if (!(t1->tv_sec >= oldval.tv_sec)) {
		logmsg(MSG_NOTIMECHECK, LOG_ERR,
			"Wrap when adding %d to %d", t2, oldval.tv_sec);
		return (FAILURE);
	}

	return (SUCCESS);
}

/*
 * FUNCTION:	is_greater_timeval()
 *
 * DESCRIPTION:	Compares two timevals
 *
 * NOTE :	Seems strange that there is not a library function to do this
 *		if one exists then this function can be removed.
 *
 * INPUTS:	First time value
 *		Time value to compare it with
 *
 * OUTPUTS:	TRUE t1 > t2
 *		FALSE t1 <= t2
 *
 */
suc_code
is_greater_timeval(struct timeval *t1, struct timeval *t2)
{
	if (t1->tv_sec > t2->tv_sec)
		return (TRUE);

	if (t1->tv_sec == t2->tv_sec) {
		if (t1->tv_usec > t2->tv_usec)
			return (TRUE);
		else
			return (FALSE);
	}

	return (FALSE);
}