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