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