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 2003 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 the map update thread and related code. 31 */ 32 33 #include <unistd.h> 34 #include <syslog.h> 35 #include <ndbm.h> 36 #include <thread.h> 37 #include <unistd.h> 38 #include <strings.h> 39 #include "ypsym.h" 40 #include "ypdefs.h" 41 #include "shim.h" 42 #include "yptol.h" 43 #include "../ldap_util.h" 44 45 /* Enable standard YP code features defined in ypdefs.h */ 46 USE_YP_PREFIX 47 USE_YP_MASTER_NAME 48 USE_YP_LAST_MODIFIED 49 USE_YP_INPUT_FILE 50 USE_YP_OUTPUT_NAME 51 USE_YP_DOMAIN_NAME 52 USE_YP_SECURE 53 USE_YP_INTERDOMAIN 54 55 /* 56 * Decs 57 */ 58 suc_code update_from_dit(map_ctrl *, datum *); 59 void * update_thread(void *); 60 61 /* 62 * Globals 63 */ 64 extern pid_t parent_pid; 65 66 /* 67 * FUNCTION: update_entry_if_required() 68 * 69 * DESCRIPTION: Determines if an entry is to be updated and if it is does the 70 * update. 71 * 72 * GIVEN : Pointer to the open map ctrl 73 * Pointer to the entry key 74 * 75 * RETURNS : SUCCESS = Entry is in a state to be returned to the client 76 * i.e. either got updated, did not need to be updated or we are 77 * in a mode where it is acceptable to return out of date 78 * information. 79 * FAILURE = Entry need an update but it could not be done. 80 */ 81 suc_code 82 update_entry_if_required(map_ctrl *map, datum *key) 83 { 84 85 /* Only update individual entries if entire map is */ 86 /* not being updated */ 87 if (is_map_updating(map)) 88 return (SUCCESS); 89 90 /* 91 * If we are being asked for the order then need to check if 92 * the map is in need of an update. If it is then fake a 93 * recent order. The client will then read the map, using 94 * dbm_firstkey and this will do the update. 95 */ 96 if (0 == strncmp(key->dptr, yp_last_modified, yp_last_modified_sz)) { 97 if (has_map_expired(map)) 98 update_timestamp(map->entries); 99 return (SUCCESS); 100 } 101 102 /* Never update special keys. Have no TTLs */ 103 if (is_special_key(key)) 104 return (SUCCESS); 105 106 if (!has_entry_expired(map, key)) 107 /* Didn't need an update */ 108 return (SUCCESS); 109 110 /* Do the update */ 111 return (update_from_dit(map, key)); 112 } 113 114 /* 115 * FUNCTION: update_from_dit() 116 * 117 * DESCRIPTION: Called to update an entry from the DIT 118 * 119 * INPUTS: Map control structure for an open map 120 * Entry key 121 * 122 * OUTPUTS: SUCCESS = Update complete or we are in a mode where it is 123 * acceptable to return out of date information. 124 * FAILURE = Update failed 125 * 126 */ 127 suc_code 128 update_from_dit(map_ctrl *map, datum *key) 129 { 130 datum dat; 131 int ret; 132 suc_code res; 133 134 /* 135 * Netgroup maps are a special case we cannot update just one entry so 136 * update the entire map instead. 137 */ 138 if ((0 == strcmp(map->map_name, NETGROUP_BYHOST)) || 139 (0 == strcmp(map->map_name, NETGROUP_BYUSER))) { 140 return (update_map_if_required(map, FALSE)); 141 } 142 143 /* Read entry from the DIT */ 144 ret = read_from_dit(map->map_name, map->domain, key, &dat); 145 146 /* Check that we got something */ 147 if (NULL == dat.dptr) { 148 if (0 == ret) { 149 /* 150 * In a mode where it is acceptable to return out of 151 * date information. 152 */ 153 logmsg(MSG_NOTIMECHECK, LOG_INFO, 154 "LDAP inaccessible returning old information"); 155 return (SUCCESS); 156 } else { 157 /* 158 * In a mode where it is not acceptable to return out 159 * of date information. 160 * 161 * If the error positviely indicates that there is no 162 * such entry delete it. For errors where object may 163 * still exist in the DIT leave it. 164 */ 165 if (MAP_NO_MATCHING_KEY == ret) { 166 /* 167 * Don't log errors. If the entry was not 168 * already present then no problem. The user 169 * just asked us for a non existant entry. 170 */ 171 dbm_delete(map->entries, *key); 172 dbm_delete(map->ttl, *key); 173 } 174 return (FAILURE); 175 } 176 } 177 178 /* Write it to DBM */ 179 res = dbm_store(map->entries, *key, dat, DBM_REPLACE); 180 sfree(dat.dptr); 181 182 if (SUCCESS != res) 183 return (FAILURE); 184 185 /* Update TTL */ 186 update_entry_ttl(map, key, TTL_RUNNING); 187 188 return (SUCCESS); 189 } 190 191 /* 192 * FUNCTION: update_map_if_required() 193 * 194 * DESCRIPTION: Called to update an entire map if it is out of date. Map ctrl 195 * must be locked before this is called. This handles checking if 196 * the map is already being updated. It is important that this is 197 * done atomically with obtaining the maps update lock. 198 * 199 * INPUTS: Map control structure for an open map 200 * Flag indication if we should wait for completion 201 * 202 * OUTPUTS: SUCCESS = Map update initiated 203 * FAILURE = Map update not initiated 204 */ 205 suc_code 206 update_map_if_required(map_ctrl *map, bool_t wait) 207 { 208 thread_t tid; 209 map_ctrl *new_map; 210 suc_code res; 211 long flags; 212 213 if (wait) { 214 /* 215 * Actually get the lock 216 * 217 * May block so unlock map_ctrl while it is done 218 */ 219 unlock_map_ctrl(map); 220 res = lock_map_update(map); 221 lock_map_ctrl(map); 222 if (SUCCESS != res) { 223 logmsg(MSG_NOTIMECHECK, LOG_ERR, 224 "Could not lock map %s for update", 225 map->map_name); 226 return (FAILURE); 227 } 228 } else { 229 /* If not waiting try to get the lock */ 230 switch (try_lock_map_update(map)) { 231 case 0: 232 /* 233 * We got the lock. Continue to start an update. 234 */ 235 break; 236 237 case EBUSY: 238 /* 239 * Some one else got the lock. OK they are 240 * doing the update so we can just return. 241 */ 242 return (SUCCESS); 243 244 default: 245 /* 246 * Some serious problem with lock. 247 */ 248 return (FAILURE); 249 } 250 } 251 252 /* 253 * If we get here are holding the update lock. Make a final check that 254 * nobody beat us to the map update while we were getting it. 255 */ 256 if (!has_map_expired(map)) { 257 /* A big waste of time. Somebody else did the update */ 258 unlock_map_update(map); 259 return (SUCCESS); 260 } 261 262 /* 263 * We got the lock and nobody beat us to doing the update. Start our 264 * own update. 265 * 266 * Thread will free the update lock when update is complete. 267 */ 268 269 270 /* 271 * Make a copy of the map_ctrl structure so the update thread has an 272 * independent version to work with. Note: Must not be on stack. 273 * 274 * On exit the update thread must free this. 275 */ 276 new_map = dup_map_ctrl(map); 277 if (NULL == new_map) { 278 unlock_map_update(map); 279 return (FAILURE); 280 } 281 282 /* 283 * While thread is running unlock map so other processes can 284 * execute non update related accesses 285 */ 286 unlock_map_ctrl(map); 287 288 flags = THR_BOUND | THR_NEW_LWP; 289 290 /* 291 * If we are not going to thr_join then need to create detached. 292 * This prevents a zombie being left when nobody joins us. 293 */ 294 if (!wait && (getpid() == parent_pid)) 295 flags |= THR_DETACHED; 296 297 /* Kick off update thread */ 298 if (0 != thr_create(NULL, NULL, update_thread, new_map, 299 flags, &tid)) { 300 logmsg(MSG_NOTIMECHECK, LOG_ERR, 301 "Could not create NIS update thread"); 302 free_map_ctrl(new_map); 303 unlock_map_update(map); 304 if (SUCCESS != lock_map_ctrl(map)) 305 logmsg(MSG_NOTIMECHECK, LOG_ERR, 306 "Could not acquire update lock for %s", map->map_name); 307 return (FAILURE); 308 } 309 310 if (wait) { 311 /* May block but no problem map_ctrl is already unlocked. */ 312 thr_join(tid, NULL, NULL); 313 } 314 315 /* Re acquire lock */ 316 if (1 != lock_map_ctrl(map)) { 317 logmsg(MSG_NOTIMECHECK, LOG_ERR, 318 "Could not re-acquire lock for %s", map->map_name); 319 return (FAILURE); 320 } 321 322 return (SUCCESS); 323 } 324 325 /* 326 * FUNCTION: update_thread() 327 * 328 * DESCRIPTION: The update thread this is called to update an entire NIS map. 329 * if several NIS maps are found to be out of date several 330 * instances of this may be running at the same time. 331 * 332 * Since we are using a duplicate map_ctrl we do not have to lock 333 * it. If we did would end up using the same mutex as the parent 334 * map ctrl an possibly deadlocking. 335 * 336 * INPUTS: Map handle (because we need access to name and lock) 337 * 338 * OUTPUTS: None exits when finished. 339 */ 340 341 void * 342 update_thread(void *arg) 343 { 344 void *ret = (void *)-1; 345 map_ctrl *map; 346 347 /* Cast argument pointer to correct type */ 348 map = (map_ctrl *)arg; 349 350 /* Actually do the work */ 351 if (SUCCESS == update_map_from_dit(map, FALSE)) 352 ret = 0; 353 354 /* Update complete or failed */ 355 unlock_map_update(map); 356 357 /* Free up duplicate copy of the map_ctrl */ 358 free_map_ctrl(map); 359 360 thr_exit(ret); 361 } 362 363 /* 364 * FUNCTION : is_special_key() 365 * 366 * DESCRIPTION: Works out if a given key is one of the special ones. We just 367 * check for the "YP_" prefix. This is not 100% safe but if 368 * valid keys with a "YP_" prefix exist in the DIT then a lot of 369 * other parts of NIS wont work. 370 */ 371 bool_t 372 is_special_key(datum *key) 373 { 374 if (0 == strncmp(key->dptr, yp_prefix, yp_prefix_sz)) 375 return (TRUE); 376 377 return (FALSE); 378 }