/*
* 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 (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
*/
/*
* DESCRIPTION: Contains code supporting the 'update in progress' flag. This is
* a near copy of lock flag code (in
* usr/src/cmd/ypcmd/shared/lockmp.c) If we implement a clean
* version of the locking code this file will probably disappear.
*
* These locks are held while a map is being updated from the
* DIT. They prevent a second update being started while this is
* in progress. This is independant from the `lockmap` mechanism
* which protects maps, generally for a much shorter period,
* while their control structures are modified.
*/
#include <unistd.h>
#include <syslog.h>
#include <sys/mman.h>
#include <thread.h>
#include <synch.h>
#include <ndbm.h>
#include <strings.h>
#include "ypsym.h"
#include "shim.h"
#include "yptol.h"
#include "../ldap_util.h"
#define LOCKFILE _PATH_SYSVOL "/yp_mapupdate"
struct updatearray {
mutex_t updatenode[MAXHASH];
};
typedef struct updatearray updatearray;
/*
* Cross-process robust mutex locks.
* Provide synchronization between YP processes
* by implementing an exclusive locking mechanism
* via a memory-mapped file.
*/
static struct updatearray *shmupdatearray;
static int lockfile;
bool_t
init_update_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(&(shmupdatearray->updatenode[iiter]),
USYNC_PROCESS | LOCK_ROBUST, 0)) {
if (rc == EBUSY) {
ebusy_cnt++;
} else {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"init_update_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)) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"%s inconsistent. Remove this file and restart NIS (YP)",
LOCKFILE);
return (FALSE);
}
return (TRUE);
}
bool_t
init_update_lock_map()
{
char buff[ sizeof (updatearray) ];
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 $_PATH_SYSVOL/yp_mapupate (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 (updatearray)) {
bzero(buff, sizeof (buff));
if ((write_cnt = write(lockfile, buff,
sizeof (buff)) != sizeof (buff))) {
if (write_cnt < 0) {
logmsg(MSG_NOTIMECHECK,
LOG_ERR,
"write(%s) => errno=%d",
LOCKFILE, errno);
} else {
logmsg(MSG_NOTIMECHECK,
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 {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"fstat(%s) => errno=%d", LOCKFILE, errno);
lockf(lockfile, F_ULOCK, 0);
close(lockfile);
return (FALSE);
}
} else {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"lockf(%s,F_LOCK) => errno=%d", LOCKFILE, errno);
close(lockfile);
return (FALSE);
}
} else {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"open(%s) => errno=%d", LOCKFILE, errno);
return (FALSE);
}
/*
* File exists with correct size, is open, and we're holding
* the file lock.
*/
shmupdatearray = (updatearray *)mmap((caddr_t)0, sizeof (updatearray),
PROT_READ | PROT_WRITE, MAP_SHARED, lockfile, 0);
if (shmupdatearray == MAP_FAILED) {
logmsg(MSG_NOTIMECHECK, 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 (updatearray)) {
if (init_update_locks_mem() == FALSE) {
lockf(lockfile, F_ULOCK, 0);
close(lockfile);
if (remove(LOCKFILE) != 0) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"remove(%s) => errno=%d: Please delete file",
LOCKFILE, errno);
}
return (FALSE);
}
}
if (lockf(lockfile, F_ULOCK, 0) != 0) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"lockf(%s,F_ULOCK) => errno=%d", LOCKFILE, errno);
close(lockfile);
return (FALSE);
}
if (close(lockfile) == 0) {
return (TRUE);
} else {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"close(%s) => errno=%d", LOCKFILE, errno);
return (FALSE);
}
}
suc_code
lock_map_update(map_ctrl *map)
{
int hashval = map->hash_val;
int rc;
/*
* Robust, cross-process lock implementation
*/
rc = mutex_lock(&(shmupdatearray->updatenode[hashval]));
while (rc != 0) {
switch (rc) {
case EOWNERDEAD:
/*
* Previous lock owner died, resetting lock
* to recover from error.
*/
rc = mutex_consistent(
&(shmupdatearray->updatenode[hashval]));
if (rc != 0) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"mutex_consistent(): error=%d", rc);
return (FAILURE);
}
rc = mutex_unlock(
&(shmupdatearray->updatenode[hashval]));
if (rc != 0) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"mutex_unlock(): error=%d", rc);
return (FAILURE);
}
break;
default:
/*
* Unrecoverable problem - nothing to do
* but exit YP and delete lock file.
*/
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"mutex_lock(): error=%d", rc);
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"Please restart NIS (ypstop/ypstart)");
if (remove(LOCKFILE) != 0) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"remove(%s) => errno=%d: Please delete file",
LOCKFILE, errno);
}
return (FAILURE);
}
rc = mutex_lock(&(shmupdatearray->updatenode[hashval]));
}
/* Success */
return (SUCCESS);
}
suc_code
unlock_map_update(map_ctrl *map)
{
int hashval = map->hash_val;
int rc;
rc = mutex_unlock(&(shmupdatearray->updatenode[hashval]));
if (rc != 0) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"mutex_unlock(): error=%d", rc);
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"Please restart NIS (ypstop/ypstart)");
if (remove(LOCKFILE) != 0) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"remove(%s) => errno=%d: Please delete file",
LOCKFILE, errno);
}
return (FAILURE);
}
/* Success */
return (SUCCESS);
}
/*
* FUNCTION : is_map_updating()
*
* DESCRIPTION: Determines if a map is currently locked for update
*
* GIVEN : Pointer to map_ctrl structure
*
* RETURNS : TRUE = Map is locked
* FALSE = Map is not locked
*/
bool_t
is_map_updating(map_ctrl *map)
{
int ret;
/* It appears not to be possible to just read a mutex. Try to lock it */
ret = mutex_trylock(&(shmupdatearray->updatenode[map->hash_val]));
if (0 != ret) {
/* Didn't get the lock ... was already locked */
return (TRUE);
}
/* Didn't need the lock so free it again */
mutex_unlock(&(shmupdatearray->updatenode[map->hash_val]));
return (FALSE);
}
/*
* FUNCTION : try_lock_map_update()
*
* DESCRIPTION: Tries to to lock a map for update.
*
* GIVEN : Pointer to the map to lock
*
* RETURNS : 0 = The map is now locked
* EBUSY = The map was already locked lock not obtained.
* Other = There was an error
*/
int
try_lock_map_update(map_ctrl *map)
{
int hashval = map->hash_val;
int rc;
/*
* Robust, cross-process lock implementation
*
* Keep trying until either lock is obtained or somebody else gets it.
*/
while (1) {
rc = mutex_trylock(&(shmupdatearray->updatenode[hashval]));
switch (rc) {
case 0:
case EBUSY:
/* Either got it or somebody else has it */
return (rc);
case EOWNERDEAD:
/*
* Previous lock owner died, resetting lock
* to recover from error.
*/
rc = mutex_consistent(
&(shmupdatearray->updatenode[hashval]));
if (rc != 0) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"mutex_consistent(): error=%d", rc);
return (rc);
}
rc = mutex_unlock(
&(shmupdatearray->updatenode[hashval]));
if (rc != 0) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"mutex_unlock(): error=%d", rc);
return (rc);
}
break;
default:
/*
* Unrecoverable problem - nothing to do
* but exit YP and delete lock file.
*/
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"mutex_lock(): error=%d", rc);
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"Please restart NIS (ypstop/ypstart)");
if (remove(LOCKFILE) != 0) {
logmsg(MSG_NOTIMECHECK, LOG_ERR,
"remove(%s) => errno=%d: Please delete file",
LOCKFILE, errno);
}
return (rc);
}
}
}