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 #include <unistd.h> 30 #include <syslog.h> 31 #include <sys/mman.h> 32 #include <thread.h> 33 #include <synch.h> 34 #include <ndbm.h> 35 #include "../ypsym.h" 36 #include "../ypdefs.h" 37 38 /* 39 * These routines provide mutual exclusion between ypserv and ypxfr. 40 * Mutual exclusion is needed so that ypxfr doesn't try to rename 41 * dbm files while ypserv is trying to open them. After ypserv has 42 * opened a dbm file, it is safe to rename it because ypserv still 43 * has access to the file through its file descriptor. 44 */ 45 46 #define LOCKFILE "/var/run/yp_maplock" 47 struct lockarray { 48 mutex_t locknode[MAXHASH]; 49 }; 50 typedef struct lockarray lockarray; 51 52 /* 53 * Cross-process robust mutex locks. 54 * Provide synchronization between YP processes 55 * by implementing an exclusive locking mechanism 56 * via a memory-mapped file. 57 */ 58 static struct lockarray *shmlockarray; 59 static int lockfile; 60 61 int 62 hash(char *s) 63 { 64 int n = 0; 65 int i; 66 67 for (i = 1; *s; i += 10, s++) { 68 n += i * (*s); 69 } 70 n %= MAXHASH; 71 return (n); 72 } 73 74 bool 75 init_locks_mem() 76 { 77 int iiter, rc; 78 int ebusy_cnt = 0; 79 80 /* 81 * Initialize cross-process locks in memory-mapped file. 82 */ 83 for (iiter = 0; iiter < MAXHASH; iiter++) { 84 if (rc = mutex_init(&(shmlockarray->locknode[iiter]), 85 USYNC_PROCESS_ROBUST, 0)) { 86 if (rc == EBUSY) { 87 ebusy_cnt++; 88 } else { 89 syslog(LOG_ERR, 90 "init_locks_mem():mutex_init():error=%d", 91 rc); 92 return (FALSE); 93 } 94 } 95 } 96 97 /* 98 * EBUSY for all locks OK, it means another process 99 * has already initialized locks. 100 */ 101 if ((ebusy_cnt > 0) && (ebusy_cnt != MAXHASH)) { 102 syslog(LOG_ERR, 103 "%s inconsistent. Remove and restart NIS (YP).", LOCKFILE); 104 return (FALSE); 105 } 106 return (TRUE); 107 } 108 109 bool 110 init_lock_map() 111 { 112 char buff[ sizeof (lockarray) ]; 113 int write_cnt, lf_size; 114 struct stat fdata; 115 116 /* 117 * Locking file initialization algorithm, with recovery mechanism. 118 * This mechanism has been devised to ensure proper creation 119 * of a memory-mapped lock file containing mutexes for robust, 120 * inter-process communication. 121 * File name is /var/run/yp_maplock (LOCKFILE). It might or might 122 * not exist. 123 * 124 * Algorithm: 125 * Try to open the file. If file doesn't exist, or size is too small, 126 * create/rewrite the file, m-map it into memory and initialize the 127 * mutexes in it. 128 * If file exists and size is at least large enough, assume it's a 129 * good file, and m-map the lock structure directly to it. 130 * 131 * Recovery from inconsistent state is easy - simply delete the file 132 * and restart NIS (YP). 133 */ 134 135 lockfile = open(LOCKFILE, O_RDWR|O_CREAT, 0600); 136 if (lockfile != -1) { 137 if (lockf(lockfile, F_LOCK, 0) == 0) { 138 if (fstat(lockfile, &fdata) == 0) { 139 lf_size = fdata.st_size; 140 if (lf_size < sizeof (lockarray)) { 141 bzero(buff, sizeof (buff)); 142 if ((write_cnt = write(lockfile, buff, 143 sizeof (buff)) != sizeof (buff))) { 144 if (write_cnt < 0) { 145 syslog(LOG_ERR, 146 "write(%s) => errno=%d", 147 LOCKFILE, errno); 148 } else { 149 syslog(LOG_ERR, 150 "write(%s) => %d!=%d: wrong number of bytes written.", 151 LOCKFILE, 152 write_cnt, 153 sizeof (buff)); 154 } 155 lockf(lockfile, F_ULOCK, 0); 156 close(lockfile); 157 return (FALSE); 158 } 159 } 160 } else { 161 syslog(LOG_ERR, 162 "fstat(%s) => errno=%d", LOCKFILE, errno); 163 lockf(lockfile, F_ULOCK, 0); 164 close(lockfile); 165 return (FALSE); 166 } 167 } else { 168 syslog(LOG_ERR, 169 "lockf(%s,F_LOCK) => errno=%d", LOCKFILE, errno); 170 close(lockfile); 171 return (FALSE); 172 } 173 } else { 174 syslog(LOG_ERR, 175 "open(%s) => errno=%d", LOCKFILE, errno); 176 return (FALSE); 177 } 178 179 /* 180 * File exists with correct size, is open, and we're holding 181 * the file lock. 182 */ 183 shmlockarray = (lockarray *)mmap((caddr_t) 0, sizeof (lockarray), 184 PROT_READ | PROT_WRITE, MAP_SHARED, lockfile, 0); 185 if (shmlockarray == MAP_FAILED) { 186 syslog(LOG_ERR, "mmap(%s) => errno=%d", LOCKFILE, errno); 187 lockf(lockfile, F_ULOCK, 0); 188 close(lockfile); 189 return (FALSE); 190 } 191 192 /* 193 * If we wrote zeroes to the file, we also need to initialize 194 * the mutex locks. 195 */ 196 if (lf_size < sizeof (lockarray)) { 197 if (init_locks_mem() == FALSE) { 198 lockf(lockfile, F_ULOCK, 0); 199 close(lockfile); 200 if (remove(LOCKFILE) != 0) { 201 syslog(LOG_ERR, 202 "remove(%s) => errno=%d: Please delete file.", 203 LOCKFILE, errno); 204 } 205 return (FALSE); 206 } 207 } 208 209 if (lockf(lockfile, F_ULOCK, 0) != 0) { 210 syslog(LOG_ERR, 211 "lockf(%s,F_ULOCK) => errno=%d", 212 LOCKFILE, errno); 213 close(lockfile); 214 return (FALSE); 215 } 216 217 if (close(lockfile) == 0) { 218 return (TRUE); 219 } else { 220 syslog(LOG_ERR, 221 "close(%s) => errno=%d", LOCKFILE, errno); 222 return (FALSE); 223 } 224 } 225 226 /* 227 * FUNCTION : lock_map() 228 * 229 * DESCRIPTION: Front end to the lock routine taking map name as argument. 230 * 231 * GIVEN : Map name. 232 * 233 * RETURNS : Same as lock_core 234 */ 235 int 236 lock_map(char *mapname) 237 { 238 int hashval; 239 240 hashval = hash(mapname); 241 242 return( lock_core(hashval)); 243 } 244 245 /* 246 * FUNCTION : lock_core() 247 * 248 * DESCRIPTION: The core map locking function 249 * 250 * GIVEN : Map hash value 251 * 252 * RETURNS : 0 = Failure 253 * 1 = Success 254 */ 255 int 256 lock_core(int hashval) 257 { 258 int rc; 259 260 /* 261 *Robust, cross-process lock implementation 262 */ 263 rc = mutex_lock(&(shmlockarray->locknode[hashval])); 264 while (rc != 0) { 265 switch (rc) { 266 case EOWNERDEAD: 267 /* 268 * Previows lock owner died, resetting lock 269 * to recover from error. 270 */ 271 rc = mutex_init(&(shmlockarray->locknode[hashval]), 272 USYNC_PROCESS_ROBUST, 0); 273 if (rc != 0) { 274 syslog(LOG_ERR, 275 "mutex_init(): error=%d", rc); 276 return (0); 277 } 278 rc = mutex_unlock(&(shmlockarray->locknode[hashval])); 279 if (rc != 0) { 280 syslog(LOG_ERR, 281 "mutex_unlock(): error=%d", rc); 282 return (0); 283 } 284 break; 285 default: 286 /* 287 * Unrecoverable problem - nothing to do 288 * but exit YP and delete lock file. 289 */ 290 syslog(LOG_ERR, 291 "mutex_lock(): error=%d", rc); 292 syslog(LOG_ERR, 293 "Please restart NIS (ypstop/ypstart)."); 294 if (remove(LOCKFILE) != 0) { 295 syslog(LOG_ERR, 296 "remove(%s) => errno=%d: Please delete file.", 297 LOCKFILE, errno); 298 } 299 return (0); 300 } 301 rc = mutex_lock(&(shmlockarray->locknode[hashval])); 302 } 303 304 /* Success */ 305 return (1); 306 } 307 308 309 /* 310 * FUNCTION : unlock_map() 311 * 312 * DESCRIPTION: Front end to the unlock routine taking map name as argument. 313 * 314 * GIVEN : Map name. 315 * 316 * RETURNS : Same as unlock_core 317 */ 318 int 319 unlock_map(char *mapname) 320 { 321 int hashval; 322 323 hashval = hash(mapname); 324 325 return( unlock_core( hashval )); 326 } 327 328 /* 329 * FUNCTION : unlock_core() 330 * 331 * DESCRIPTION: The core map locking function 332 * 333 * GIVEN : Map hash value 334 * 335 * RETURNS : 0 = Failure 336 * 1 = Success 337 */ 338 int 339 unlock_core(int hashval) 340 { 341 int rc; 342 343 rc = mutex_unlock(&(shmlockarray->locknode[hashval])); 344 if (rc != 0) { 345 syslog(LOG_ERR, 346 "mutex_unlock(): error=%d", rc); 347 syslog(LOG_ERR, 348 "Please restart NIS (ypstop/ypstart)."); 349 if (remove(LOCKFILE) != 0) { 350 syslog(LOG_ERR, 351 "remove(%s) => errno=%d: Please delete file.", 352 LOCKFILE, errno); 353 } 354 return (0); 355 } 356 357 /* Success */ 358 return (1); 359 } 360