1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 /*
  30  * DESCRIPTION: Contains utilities relating to TTL calculation.
  31  */
  32 #include <unistd.h>
  33 #include <syslog.h>
  34 #include <errno.h>
  35 #include <strings.h>
  36 #include <ndbm.h>
  37 #include "ypsym.h"
  38 #include "ypdefs.h"
  39 #include "shim.h"
  40 #include "yptol.h"
  41 #include "../ldap_util.h"
  42 
  43 /*
  44  * Constants used in time calculations
  45  */
  46 #define MILLION 1000000
  47 
  48 /*
  49  * Decs
  50  */
  51 suc_code is_greater_timeval(struct timeval *, struct timeval *);
  52 suc_code add_to_timeval(struct timeval *, int);
  53 
  54 /*
  55  * FUNCTION:    has_entry_expired()
  56  *
  57  * DESCRIPTION: Determines if an individual entry has expired.
  58  *
  59  * INPUTS:      Map control structure for an open map
  60  *              Entry key
  61  *
  62  * OUTPUTS:     TRUE =  Entry has expired or cannot be found this will cause
  63  *                      missing entries to be pulled out of the DIT.
  64  *              FALSE = Entry has not expired
  65  *
  66  */
  67 bool_t
  68 has_entry_expired(map_ctrl *map, datum *key)
  69 {
  70         datum ttl;
  71         struct timeval  now;
  72         struct timeval  old_time;
  73         char    *key_name;
  74         char *myself = "has_entry_expired";
  75 
  76         if ((map == NULL) || (map->ttl == NULL))
  77                 return (FALSE);
  78 
  79         /* Get expiry time entry for key */
  80         ttl = dbm_fetch(map->ttl, *key);
  81 
  82         if (NULL == ttl.dptr) {
  83                 /*
  84                  * If we failed to get a map expiry key, which must always be
  85                  * present, then something is seriously wrong. Try to recreate
  86                  * the map.
  87                  */
  88                 if ((key->dsize == strlen(MAP_EXPIRY_KEY)) &&
  89                         (0 == strncmp(key->dptr, MAP_EXPIRY_KEY, key->dsize))) {
  90                         logmsg(MSG_NOTIMECHECK, LOG_ERR, "Cannot find %s TTL "
  91                                 "for map %s. Will attempt to recreate map",
  92                                 MAP_EXPIRY_KEY, map->map_name);
  93                         return (TRUE);
  94                 }
  95 
  96                 /*
  97                  * Not a problem just no TTL entry for this entry. Maybe it has
  98                  * not yet been downloaded. Maybe it will be handled by a
  99                  * service other than NIS. Check if the entire map has expired.
 100                  * This prevents repeated LDAP reads when requests are made for
 101                  * nonexistant entries.
 102                  */
 103                 if (has_map_expired(map)) {
 104                         /* Kick of a map update */
 105                         update_map_if_required(map, FALSE);
 106                 }
 107 
 108                 /* Don't update the entry */
 109                 return (FALSE);
 110         }
 111 
 112         if (ttl.dsize != sizeof (struct timeval)) {
 113                 /*
 114                  * Need to malloc some memory before can syslog the key name
 115                  * but this may fail. Solution log a simple message first THEn
 116                  * a more detailed one if it works.
 117                  */
 118                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
 119                         "Invalid TTL key in map %s. error %d",
 120                                         map->map_name, dbm_error(map->ttl));
 121 
 122                 /* Log the key name */
 123                 key_name = (char *)am(myself, key->dsize + 1);
 124                 if (NULL == key_name) {
 125                         logmsg(MSG_NOMEM, LOG_ERR,
 126                                         "Could not alloc memory for keyname");
 127                 } else {
 128                         strncpy(key_name, key->dptr, key->dsize);
 129                         key_name[key->dsize] = '\0';
 130                         logmsg(MSG_NOTIMECHECK, LOG_ERR,
 131                                                 "Key name was %s", key_name);
 132                         sfree(key_name);
 133                 }
 134                 /* Update it Anyway */
 135                 return (TRUE);
 136         }
 137 
 138         /* Get current time */
 139         gettimeofday(&now, NULL);
 140 
 141         /*
 142          * Because dptr may not be int aligned need to build an int
 143          * out of what it points to or will get a bus error
 144          */
 145         bcopy(ttl.dptr, &old_time, sizeof (struct timeval));
 146 
 147         return (is_greater_timeval(&now, &old_time));
 148 }
 149 
 150 /*
 151  * FUNCTION:    has_map_expired()
 152  *
 153  * DESCRIPTION: Determines if an entire map has expire
 154  *
 155  * INPUTS:      Map control structure for an open map
 156  *
 157  * OUTPUTS:     TRUE = Map has expired
 158  *              FALSE  Map has not expired
 159  *
 160  */
 161 bool_t
 162 has_map_expired(map_ctrl *map)
 163 {
 164         datum key;
 165 
 166         /* Set up datum with magic expiry key */
 167         key.dsize = strlen(MAP_EXPIRY_KEY);
 168         key.dptr = MAP_EXPIRY_KEY;
 169 
 170         /* Call has_entry_expired() with magic map expiry key */
 171         return (has_entry_expired(map, &key));
 172 }
 173 
 174 /*
 175  * FUNCTION:    update_entry_ttl()
 176  *
 177  * DESCRIPTION: Updates the TTL for one map entry
 178  *
 179  * INPUTS:      Map control structure for an open map
 180  *              Entry key
 181  *              Flag indication if TTL should be max, min or random
 182  *
 183  * OUTPUTS:     SUCCESS = TTL updated
 184  *              FAILURE = TTL not updated
 185  *
 186  */
 187 
 188 suc_code
 189 update_entry_ttl(map_ctrl *map, datum *key, TTL_TYPE type)
 190 {
 191         datum expire;
 192         struct timeval  now;
 193         int     ttl;
 194 
 195         /* Get current time */
 196         gettimeofday(&now, NULL);
 197 
 198         /* Get TTL from mapping file */
 199         ttl = get_ttl_value(map, type);
 200 
 201         if (FAILURE == add_to_timeval(&now, ttl))
 202                 return (FAILURE);
 203 
 204         /* Convert time into a datum */
 205         expire.dsize = sizeof (struct timeval);
 206         expire.dptr = (char *)&now;
 207 
 208         /* Set expiry time entry for key */
 209         errno = 0;
 210         if (0 > dbm_store(map->ttl, *key, expire, DBM_REPLACE)) {
 211                 logmsg(MSG_NOTIMECHECK, LOG_ERR, "Could not write TTL entry "
 212                                                 "(errno=%d)", errno);
 213                 return (FAILURE);
 214         }
 215 
 216         return (SUCCESS);
 217 }
 218 
 219 /*
 220  * FUNCTION:    update_map_ttl()
 221  *
 222  * DESCRIPTION: Updates the TTL for entire map. This can be called either with
 223  *              the map open (map_ctrl DBM pointer set up) or the map closed
 224  *              (map_ctrl DBM pointers not set). The latter case will occur
 225  *              when we have just created a new map.
 226  *
 227  *              This function must open the TTL map but, in either case, must
 228  *              return with the map_ctrl in it's original state.
 229  *
 230  * INPUTS:      Map control structure for an open map
 231  *
 232  * OUTPUTS:     SUCCESS = TTL updated
 233  *              FAILURE = TTL not updated
 234  *
 235  */
 236 suc_code
 237 update_map_ttl(map_ctrl *map)
 238 {
 239         datum key;
 240         bool_t map_was_open = TRUE;
 241         suc_code ret;
 242 
 243         /* Set up datum with magic expiry key */
 244         key.dsize = strlen(MAP_EXPIRY_KEY);
 245         key.dptr = MAP_EXPIRY_KEY;
 246 
 247         /* If TTL not open open it */
 248         if (NULL == map->ttl) {
 249                 map->ttl = dbm_open(map->ttl_path, O_RDWR, 0644);
 250                 if (NULL == map->ttl)
 251                         return (FAILURE);
 252                 map_was_open = FALSE;
 253         }
 254 
 255         /* Call update_entry_ttl() with magic map expiry key */
 256         ret = update_entry_ttl(map, &key, TTL_MIN);
 257 
 258         /* If we had to open TTL file close it */
 259         if (!map_was_open) {
 260                 dbm_close(map->ttl);
 261                 map->ttl_path = NULL;
 262         }
 263 
 264         return (ret);
 265 }
 266 
 267 /*
 268  * FUNCTION:    add_to_timeval()
 269  *
 270  * DESCRIPTION: Adds an int to a timeval
 271  *
 272  * NOTE :       Seems strange that there is not a library function to do this
 273  *              if one exists then this function can be removed.
 274  *
 275  * NOTE :       Does not handle UNIX clock wrap round but this is a much bigger
 276  *              problem.
 277  *
 278  * INPUTS:      Time value to add to
 279  *              Time value to add in seconds
 280  *
 281  * OUTPUTS:     SUCCESS = Addition successful
 282  *              FAILURE = Addition failed (probably wrapped)
 283  *
 284  */
 285 suc_code
 286 add_to_timeval(struct timeval *t1, int t2)
 287 {
 288         long usec;
 289         struct timeval oldval;
 290 
 291         oldval.tv_sec = t1->tv_sec;
 292 
 293         /* Add seconds part */
 294         t1->tv_sec += t2;
 295 
 296         /* Check for clock wrap */
 297         if (!(t1->tv_sec >= oldval.tv_sec)) {
 298                 logmsg(MSG_NOTIMECHECK, LOG_ERR,
 299                         "Wrap when adding %d to %d", t2, oldval.tv_sec);
 300                 return (FAILURE);
 301         }
 302 
 303         return (SUCCESS);
 304 }
 305 
 306 /*
 307  * FUNCTION:    is_greater_timeval()
 308  *
 309  * DESCRIPTION: Compares two timevals
 310  *
 311  * NOTE :       Seems strange that there is not a library function to do this
 312  *              if one exists then this function can be removed.
 313  *
 314  * INPUTS:      First time value
 315  *              Time value to compare it with
 316  *
 317  * OUTPUTS:     TRUE t1 > t2
 318  *              FALSE t1 <= t2
 319  *
 320  */
 321 suc_code
 322 is_greater_timeval(struct timeval *t1, struct timeval *t2)
 323 {
 324         if (t1->tv_sec > t2->tv_sec)
 325                 return (TRUE);
 326 
 327         if (t1->tv_sec == t2->tv_sec) {
 328                 if (t1->tv_usec > t2->tv_usec)
 329                         return (TRUE);
 330                 else
 331                         return (FALSE);
 332         }
 333 
 334         return (FALSE);
 335 }