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
has_entry_expired(map_ctrl * map,datum * key)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
has_map_expired(map_ctrl * map)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
update_entry_ttl(map_ctrl * map,datum * key,TTL_TYPE type)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
update_map_ttl(map_ctrl * map)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
add_to_timeval(struct timeval * t1,int t2)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
is_greater_timeval(struct timeval * t1,struct timeval * t2)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 }
336