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