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