/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include "../ypsym.h" #include "../ypdefs.h" #include "shim.h" /* * These routines provide mutual exclusion between ypserv and ypxfr. * Mutual exclusion is needed so that ypxfr doesn't try to rename * dbm files while ypserv is trying to open them. After ypserv has * opened a dbm file, it is safe to rename it because ypserv still * has access to the file through its file descriptor. */ #define LOCKFILE "/var/run/yp_maplock" struct lockarray { mutex_t locknode[MAXHASH]; }; typedef struct lockarray lockarray; /* * Cross-process robust mutex locks. * Provide synchronization between YP processes * by implementing an exclusive locking mechanism * via a memory-mapped file. */ static struct lockarray *shmlockarray; static int lockfile; /* * Hash functions, used for by the locking mechanism. * * - hash() is the front-end function that gets called. * - get_map_id() returns a unique int value per map. * It is used in N2L mode only. * It is called by hash() in N2L mode. */ int get_map_id(char *map_name, int index) { map_id_elt_t *cur_elt; /* * Local references to hash table for map lists * and to max number of maps */ map_id_elt_t **map_list_p; int max_map; /* initializes map_list_p & max_map */ get_list_max(&map_list_p, &max_map); cur_elt = map_list_p[index]; while (cur_elt != NULL) { if (strcmp(map_name, cur_elt->map_name) == 0) { /* found */ return (cur_elt->map_id); } cur_elt = cur_elt->next; } syslog(LOG_WARNING, "get_map_id: no hash id found for %s" ", giving max_map value (%d)", map_name, max_map); /* * max_map does not match any map id, hence * will not trigger any lock collision * with existing maps. * Needed for yp regular locking mechanism. */ return (max_map); } int hash(char *s) { unsigned int n = 0; int i; char *map_name = s; for (i = 1; *s; i += 10, s++) { n += i * (*s); } n %= MAXHASH; if (yptol_mode & yptol_newlock) { return (get_map_id(map_name, n)); } else { return (n); } } bool init_locks_mem() { int iiter, rc; int ebusy_cnt = 0; /* * Initialize cross-process locks in memory-mapped file. */ for (iiter = 0; iiter < MAXHASH; iiter++) { if (rc = mutex_init(&(shmlockarray->locknode[iiter]), USYNC_PROCESS | LOCK_ROBUST, 0)) { if (rc == EBUSY) { ebusy_cnt++; } else { syslog(LOG_ERR, "init_locks_mem():mutex_init():error=%d", rc); return (FALSE); } } } /* * EBUSY for all locks OK, it means another process * has already initialized locks. */ if ((ebusy_cnt > 0) && (ebusy_cnt != MAXHASH)) { syslog(LOG_ERR, "%s inconsistent. Remove and restart NIS (YP).", LOCKFILE); return (FALSE); } return (TRUE); } bool init_lock_map() { char buff[ sizeof (lockarray) ]; int write_cnt, lf_size; struct stat fdata; /* * Locking file initialization algorithm, with recovery mechanism. * This mechanism has been devised to ensure proper creation * of a memory-mapped lock file containing mutexes for robust, * inter-process communication. * File name is /var/run/yp_maplock (LOCKFILE). It might or might * not exist. * * Algorithm: * Try to open the file. If file doesn't exist, or size is too small, * create/rewrite the file, m-map it into memory and initialize the * mutexes in it. * If file exists and size is at least large enough, assume it's a * good file, and m-map the lock structure directly to it. * * Recovery from inconsistent state is easy - simply delete the file * and restart NIS (YP). */ lockfile = open(LOCKFILE, O_RDWR|O_CREAT, 0600); if (lockfile != -1) { if (lockf(lockfile, F_LOCK, 0) == 0) { if (fstat(lockfile, &fdata) == 0) { lf_size = fdata.st_size; if (lf_size < sizeof (lockarray)) { bzero(buff, sizeof (buff)); if ((write_cnt = write(lockfile, buff, sizeof (buff)) != sizeof (buff))) { if (write_cnt < 0) { syslog(LOG_ERR, "write(%s) => errno=%d", LOCKFILE, errno); } else { syslog(LOG_ERR, "write(%s) => %d!=%d: wrong number of bytes written.", LOCKFILE, write_cnt, sizeof (buff)); } lockf(lockfile, F_ULOCK, 0); close(lockfile); return (FALSE); } } } else { syslog(LOG_ERR, "fstat(%s) => errno=%d", LOCKFILE, errno); lockf(lockfile, F_ULOCK, 0); close(lockfile); return (FALSE); } } else { syslog(LOG_ERR, "lockf(%s,F_LOCK) => errno=%d", LOCKFILE, errno); close(lockfile); return (FALSE); } } else { syslog(LOG_ERR, "open(%s) => errno=%d", LOCKFILE, errno); return (FALSE); } /* * File exists with correct size, is open, and we're holding * the file lock. */ shmlockarray = (lockarray *)mmap((caddr_t)0, sizeof (lockarray), PROT_READ | PROT_WRITE, MAP_SHARED, lockfile, 0); if (shmlockarray == MAP_FAILED) { syslog(LOG_ERR, "mmap(%s) => errno=%d", LOCKFILE, errno); lockf(lockfile, F_ULOCK, 0); close(lockfile); return (FALSE); } /* * If we wrote zeroes to the file, we also need to initialize * the mutex locks. */ if (lf_size < sizeof (lockarray)) { if (init_locks_mem() == FALSE) { lockf(lockfile, F_ULOCK, 0); close(lockfile); if (remove(LOCKFILE) != 0) { syslog(LOG_ERR, "remove(%s) => errno=%d: Please delete file.", LOCKFILE, errno); } return (FALSE); } } if (lockf(lockfile, F_ULOCK, 0) != 0) { syslog(LOG_ERR, "lockf(%s,F_ULOCK) => errno=%d", LOCKFILE, errno); close(lockfile); return (FALSE); } if (close(lockfile) == 0) { return (TRUE); } else { syslog(LOG_ERR, "close(%s) => errno=%d", LOCKFILE, errno); return (FALSE); } } /* * FUNCTION : lock_map() * * DESCRIPTION: Front end to the lock routine taking map name as argument. * * GIVEN : Map name. * * RETURNS : Same as lock_core */ int lock_map(char *mapname) { int hashval; hashval = hash(mapname); return (lock_core(hashval)); } /* * FUNCTION : lock_core() * * DESCRIPTION: The core map locking function * * GIVEN : Map hash value * * RETURNS : 0 = Failure * 1 = Success */ int lock_core(int hashval) { int rc; /* * Robust, cross-process lock implementation */ rc = mutex_lock(&(shmlockarray->locknode[hashval])); while (rc != 0) { switch (rc) { case EOWNERDEAD: /* * Previous lock owner died, resetting lock * to recover from error. */ rc = mutex_consistent( &(shmlockarray->locknode[hashval])); if (rc != 0) { syslog(LOG_ERR, "mutex_consistent(): error=%d", rc); return (0); } rc = mutex_unlock(&(shmlockarray->locknode[hashval])); if (rc != 0) { syslog(LOG_ERR, "mutex_unlock(): error=%d", rc); return (0); } break; default: /* * Unrecoverable problem - nothing to do * but exit YP and delete lock file. */ syslog(LOG_ERR, "mutex_lock(): error=%d", rc); syslog(LOG_ERR, "Please restart NIS (ypstop/ypstart)."); if (remove(LOCKFILE) != 0) { syslog(LOG_ERR, "remove(%s) => errno=%d: Please delete file.", LOCKFILE, errno); } return (0); } rc = mutex_lock(&(shmlockarray->locknode[hashval])); } /* Success */ return (1); } /* * FUNCTION : unlock_map() * * DESCRIPTION: Front end to the unlock routine taking map name as argument. * * GIVEN : Map name. * * RETURNS : Same as unlock_core */ int unlock_map(char *mapname) { int hashval; hashval = hash(mapname); return (unlock_core(hashval)); } /* * FUNCTION : unlock_core() * * DESCRIPTION: The core map locking function * * GIVEN : Map hash value * * RETURNS : 0 = Failure * 1 = Success */ int unlock_core(int hashval) { int rc; rc = mutex_unlock(&(shmlockarray->locknode[hashval])); if (rc != 0) { syslog(LOG_ERR, "mutex_unlock(): error=%d", rc); syslog(LOG_ERR, "Please restart NIS (ypstop/ypstart)."); if (remove(LOCKFILE) != 0) { syslog(LOG_ERR, "remove(%s) => errno=%d: Please delete file.", LOCKFILE, errno); } return (0); } /* Success */ return (1); }