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 (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 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 code supporting the 'update in progress' flag. This is 31 * a near copy of lock flag code (in 32 * usr/src/cmd/ypcmd/shared/lockmp.c) If we implement a clean 33 * version of the locking code this file will probably disappear. 34 * 35 * These locks are held while a map is being updated from the 36 * DIT. They prevent a second update being started while this is 37 * in progress. This is independant from the `lockmap` mechanism 38 * which protects maps, generally for a much shorter period, 39 * while their control structures are modified. 40 */ 41 42 #include <unistd.h> 43 #include <syslog.h> 44 #include <sys/mman.h> 45 #include <thread.h> 46 #include <synch.h> 47 #include <ndbm.h> 48 #include <strings.h> 49 #include "ypsym.h" 50 #include "shim.h" 51 #include "yptol.h" 52 #include "../ldap_util.h" 53 54 #define LOCKFILE "/var/run/yp_mapupdate" 55 struct updatearray { 56 mutex_t updatenode[MAXHASH]; 57 }; 58 typedef struct updatearray updatearray; 59 60 /* 61 * Cross-process robust mutex locks. 62 * Provide synchronization between YP processes 63 * by implementing an exclusive locking mechanism 64 * via a memory-mapped file. 65 */ 66 static struct updatearray *shmupdatearray; 67 static int lockfile; 68 69 bool_t 70 init_update_locks_mem() 71 { 72 int iiter, rc; 73 int ebusy_cnt = 0; 74 75 /* 76 * Initialize cross-process locks in memory-mapped file. 77 */ 78 for (iiter = 0; iiter < MAXHASH; iiter++) { 79 if (rc = mutex_init(&(shmupdatearray->updatenode[iiter]), 80 USYNC_PROCESS | LOCK_ROBUST, 0)) { 81 if (rc == EBUSY) { 82 ebusy_cnt++; 83 } else { 84 logmsg(MSG_NOTIMECHECK, LOG_ERR, 85 "init_update_locks_mem():mutex_init():" 86 "error=%d", rc); 87 return (FALSE); 88 } 89 } 90 } 91 92 /* 93 * EBUSY for all locks OK, it means another process 94 * has already initialized locks. 95 */ 96 if ((ebusy_cnt > 0) && (ebusy_cnt != MAXHASH)) { 97 logmsg(MSG_NOTIMECHECK, LOG_ERR, 98 "%s inconsistent. Remove this file and restart NIS (YP)", 99 LOCKFILE); 100 return (FALSE); 101 } 102 return (TRUE); 103 } 104 105 bool_t 106 init_update_lock_map() 107 { 108 char buff[ sizeof (updatearray) ]; 109 int write_cnt, lf_size; 110 struct stat fdata; 111 112 /* 113 * Locking file initialization algorithm, with recovery mechanism. 114 * This mechanism has been devised to ensure proper creation 115 * of a memory-mapped lock file containing mutexes for robust, 116 * inter-process communication. 117 * File name is /var/run/yp_mapupate (LOCKFILE). It might or might 118 * not exist. 119 * 120 * Algorithm: 121 * Try to open the file. If file doesn't exist, or size is too small, 122 * create/rewrite the file, m-map it into memory and initialize the 123 * mutexes in it. 124 * If file exists and size is at least large enough, assume it's a 125 * good file, and m-map the lock structure directly to it. 126 * 127 * Recovery from inconsistent state is easy - simply delete the file 128 * and restart NIS (YP). 129 */ 130 131 lockfile = open(LOCKFILE, O_RDWR|O_CREAT, 0600); 132 if (lockfile != -1) { 133 if (lockf(lockfile, F_LOCK, 0) == 0) { 134 if (fstat(lockfile, &fdata) == 0) { 135 lf_size = fdata.st_size; 136 if (lf_size < sizeof (updatearray)) { 137 bzero(buff, sizeof (buff)); 138 if ((write_cnt = write(lockfile, buff, 139 sizeof (buff)) != sizeof (buff))) { 140 if (write_cnt < 0) { 141 logmsg(MSG_NOTIMECHECK, 142 LOG_ERR, 143 "write(%s) => errno=%d", 144 LOCKFILE, errno); 145 } else { 146 logmsg(MSG_NOTIMECHECK, 147 LOG_ERR, 148 "write(%s) => %d!=%d: wrong number of bytes written", 149 LOCKFILE, 150 write_cnt, 151 sizeof (buff)); 152 } 153 lockf(lockfile, F_ULOCK, 0); 154 close(lockfile); 155 return (FALSE); 156 } 157 } 158 } else { 159 logmsg(MSG_NOTIMECHECK, LOG_ERR, 160 "fstat(%s) => errno=%d", LOCKFILE, errno); 161 lockf(lockfile, F_ULOCK, 0); 162 close(lockfile); 163 return (FALSE); 164 } 165 } else { 166 logmsg(MSG_NOTIMECHECK, LOG_ERR, 167 "lockf(%s,F_LOCK) => errno=%d", LOCKFILE, errno); 168 close(lockfile); 169 return (FALSE); 170 } 171 } else { 172 logmsg(MSG_NOTIMECHECK, LOG_ERR, 173 "open(%s) => errno=%d", LOCKFILE, errno); 174 return (FALSE); 175 } 176 177 /* 178 * File exists with correct size, is open, and we're holding 179 * the file lock. 180 */ 181 shmupdatearray = (updatearray *)mmap((caddr_t)0, sizeof (updatearray), 182 PROT_READ | PROT_WRITE, MAP_SHARED, lockfile, 0); 183 if (shmupdatearray == MAP_FAILED) { 184 logmsg(MSG_NOTIMECHECK, LOG_ERR, 185 "mmap(%s) => errno=%d", LOCKFILE, errno); 186 lockf(lockfile, F_ULOCK, 0); 187 close(lockfile); 188 return (FALSE); 189 } 190 191 /* 192 * If we wrote zeroes to the file, we also need to initialize 193 * the mutex locks. 194 */ 195 if (lf_size < sizeof (updatearray)) { 196 if (init_update_locks_mem() == FALSE) { 197 lockf(lockfile, F_ULOCK, 0); 198 close(lockfile); 199 if (remove(LOCKFILE) != 0) { 200 logmsg(MSG_NOTIMECHECK, LOG_ERR, 201 "remove(%s) => errno=%d: Please delete file", 202 LOCKFILE, errno); 203 } 204 return (FALSE); 205 } 206 } 207 208 if (lockf(lockfile, F_ULOCK, 0) != 0) { 209 logmsg(MSG_NOTIMECHECK, LOG_ERR, 210 "lockf(%s,F_ULOCK) => errno=%d", LOCKFILE, errno); 211 close(lockfile); 212 return (FALSE); 213 } 214 215 if (close(lockfile) == 0) { 216 return (TRUE); 217 } else { 218 logmsg(MSG_NOTIMECHECK, LOG_ERR, 219 "close(%s) => errno=%d", LOCKFILE, errno); 220 return (FALSE); 221 } 222 } 223 224 suc_code 225 lock_map_update(map_ctrl *map) 226 { 227 int hashval = map->hash_val; 228 int rc; 229 230 /* 231 * Robust, cross-process lock implementation 232 */ 233 rc = mutex_lock(&(shmupdatearray->updatenode[hashval])); 234 while (rc != 0) { 235 switch (rc) { 236 case EOWNERDEAD: 237 /* 238 * Previous lock owner died, resetting lock 239 * to recover from error. 240 */ 241 rc = mutex_consistent( 242 &(shmupdatearray->updatenode[hashval])); 243 if (rc != 0) { 244 logmsg(MSG_NOTIMECHECK, LOG_ERR, 245 "mutex_consistent(): error=%d", rc); 246 return (FAILURE); 247 } 248 rc = mutex_unlock( 249 &(shmupdatearray->updatenode[hashval])); 250 if (rc != 0) { 251 logmsg(MSG_NOTIMECHECK, LOG_ERR, 252 "mutex_unlock(): error=%d", rc); 253 return (FAILURE); 254 } 255 break; 256 default: 257 /* 258 * Unrecoverable problem - nothing to do 259 * but exit YP and delete lock file. 260 */ 261 logmsg(MSG_NOTIMECHECK, LOG_ERR, 262 "mutex_lock(): error=%d", rc); 263 logmsg(MSG_NOTIMECHECK, LOG_ERR, 264 "Please restart NIS (ypstop/ypstart)"); 265 if (remove(LOCKFILE) != 0) { 266 logmsg(MSG_NOTIMECHECK, LOG_ERR, 267 "remove(%s) => errno=%d: Please delete file", 268 LOCKFILE, errno); 269 } 270 return (FAILURE); 271 } 272 rc = mutex_lock(&(shmupdatearray->updatenode[hashval])); 273 } 274 275 /* Success */ 276 return (SUCCESS); 277 } 278 279 280 suc_code 281 unlock_map_update(map_ctrl *map) 282 { 283 int hashval = map->hash_val; 284 int rc; 285 286 rc = mutex_unlock(&(shmupdatearray->updatenode[hashval])); 287 if (rc != 0) { 288 logmsg(MSG_NOTIMECHECK, LOG_ERR, 289 "mutex_unlock(): error=%d", rc); 290 logmsg(MSG_NOTIMECHECK, LOG_ERR, 291 "Please restart NIS (ypstop/ypstart)"); 292 if (remove(LOCKFILE) != 0) { 293 logmsg(MSG_NOTIMECHECK, LOG_ERR, 294 "remove(%s) => errno=%d: Please delete file", 295 LOCKFILE, errno); 296 } 297 return (FAILURE); 298 } 299 300 /* Success */ 301 return (SUCCESS); 302 } 303 304 /* 305 * FUNCTION : is_map_updating() 306 * 307 * DESCRIPTION: Determines if a map is currently locked for update 308 * 309 * GIVEN : Pointer to map_ctrl structure 310 * 311 * RETURNS : TRUE = Map is locked 312 * FALSE = Map is not locked 313 */ 314 bool_t 315 is_map_updating(map_ctrl *map) 316 { 317 int ret; 318 319 /* It appears not to be possible to just read a mutex. Try to lock it */ 320 ret = mutex_trylock(&(shmupdatearray->updatenode[map->hash_val])); 321 322 if (0 != ret) { 323 /* Didn't get the lock ... was already locked */ 324 return (TRUE); 325 } 326 327 /* Didn't need the lock so free it again */ 328 mutex_unlock(&(shmupdatearray->updatenode[map->hash_val])); 329 return (FALSE); 330 } 331 332 /* 333 * FUNCTION : try_lock_map_update() 334 * 335 * DESCRIPTION: Tries to to lock a map for update. 336 * 337 * GIVEN : Pointer to the map to lock 338 * 339 * RETURNS : 0 = The map is now locked 340 * EBUSY = The map was already locked lock not obtained. 341 * Other = There was an error 342 */ 343 int 344 try_lock_map_update(map_ctrl *map) 345 { 346 int hashval = map->hash_val; 347 int rc; 348 349 /* 350 * Robust, cross-process lock implementation 351 * 352 * Keep trying until either lock is obtained or somebody else gets it. 353 */ 354 while (1) { 355 rc = mutex_trylock(&(shmupdatearray->updatenode[hashval])); 356 357 switch (rc) { 358 359 case 0: 360 case EBUSY: 361 /* Either got it or somebody else has it */ 362 return (rc); 363 364 case EOWNERDEAD: 365 /* 366 * Previous lock owner died, resetting lock 367 * to recover from error. 368 */ 369 rc = mutex_consistent( 370 &(shmupdatearray->updatenode[hashval])); 371 if (rc != 0) { 372 logmsg(MSG_NOTIMECHECK, LOG_ERR, 373 "mutex_consistent(): error=%d", rc); 374 return (rc); 375 } 376 rc = mutex_unlock( 377 &(shmupdatearray->updatenode[hashval])); 378 if (rc != 0) { 379 logmsg(MSG_NOTIMECHECK, LOG_ERR, 380 "mutex_unlock(): error=%d", rc); 381 return (rc); 382 } 383 break; 384 default: 385 /* 386 * Unrecoverable problem - nothing to do 387 * but exit YP and delete lock file. 388 */ 389 logmsg(MSG_NOTIMECHECK, LOG_ERR, 390 "mutex_lock(): error=%d", rc); 391 logmsg(MSG_NOTIMECHECK, LOG_ERR, 392 "Please restart NIS (ypstop/ypstart)"); 393 if (remove(LOCKFILE) != 0) { 394 logmsg(MSG_NOTIMECHECK, LOG_ERR, 395 "remove(%s) => errno=%d: Please delete file", 396 LOCKFILE, errno); 397 } 398 return (rc); 399 } 400 } 401 } 402