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