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