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