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