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 }