OpenSolaris_b135/cmd/svr4pkg/pkgadm/lock.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


/*
 * Module: lock.c
 * Program: pkgadm (/usr/bin/pkgadm)
 * Synopsis: implements the zone/package administrative lock interface
 * Public methods:
 *	admin_lock
 * Usage:
 *  Acquire: -a [ -e | -s ] [ -o obj ] [ -k key ] [ -R root ] [ -q ] \
 *		[ -w ] [ -W timeout ]
 *  Release: -r -o object -k key [ -R altRoot ] [ -q ]
 *  Status: [ -o object ] [ -k key ] [ -R altRoot ] [ -q ]
 */

/* enable extentions to standard Unix libraries */

#define	__EXTENSIONS__

/* unix system includes */

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <locale.h>
#include <libgen.h>
#include <sys/param.h>
#include <openssl/bio.h>
#include <errno.h>
#include <assert.h>
#include <time.h>
#include <fnmatch.h>
#include <zone.h>

/* local includes */

#include <libinst.h>
#include <pkglib.h>
#include <pkgerr.h>
#include <keystore.h>
#include "pkgadm.h"
#include "pkgadm_msgs.h"

/* definition and conversion of sleep units */

#define	SECONDS(x)		((unsigned int)(x))
#define	MINUTES(x)		((unsigned int)(seconds(x)*60))

/* define how waits are timed */

#define	WAITER_INITIAL		SECONDS(1)
#define	WAITER_MAX		SECONDS(60)
#define	WAITER_NEXT(x)		((x)*2)

typedef unsigned int		WAITER_T;

/*
 * The administrative lock file resides in /tmp
 * It does not survive a reboot
 * It consists of fixed length records
 * Each record has the following information:
 * 	record number - record position within the lock file
 * 	lock count - number of lock holders maintaining this lock
 * 	lock object - object being locked
 * 	lock key - key needed to manipulate existing lock
 *	lock exclusive - is the lock exclusive (single locker only)
 */

#define	LOCK_OBJECT_MAXLEN	512-1
#define	LOCK_KEY_MAXLEN		37

#define	LOCK_DIRECTORY		"/tmp"

/*
 * this is the "well known name" of the lock file that is used by the
 * package, patch, and zone administration commands to synchronize their
 * various efforts - it must live in a temporary directory that is cleared
 * on system reboot but it is NOT a temporary file in that it survives
 * the process that creates and updates it - if the format of the lock
 * file ever changes, this path should be updated with a later "uuid"
 * so that previous (incompatible) pkgadm's will not use the later data.
 */

#define	LOCK_FILENAME	\
	"/tmp/.ai.pkg.zone.lock-afdb66cf-1dd1-11b2-a049-000d560ddc3e"

/* mode to use for LOCK_FILENAME */

#define	LOCK_FILEMODE	\
	(S_ISGID|S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)

#define	LOCK_SLEEP_INTERVAL	SECONDS(2)

/* lock contents types */

typedef unsigned long RECORDNUM_T;

#define	RECORDNUM_NONE	0xFFFFFFFF

/* actual lock data */

struct _adminLock
{
	RECORDNUM_T	lockRecordNum;
	unsigned long	lockCount;
	unsigned long	lockExclusive;
	pid_t		lockPid;
	zoneid_t	lockZoneId;
	char		lockKey[LOCK_KEY_MAXLEN+1];
	char		lockObject[LOCK_OBJECT_MAXLEN+1];
};

typedef struct _adminLock ADMINLOCK_T;

/* size of an individual "lock" */

#define	LOCK_SIZE		sizeof (ADMINLOCK_T)

/* union to allow lock to be accessed as raw or structured data */

union _lockRecord
{
	char		_lrLockData[LOCK_SIZE];
	ADMINLOCK_T	_lrLock;
};

typedef union _lockRecord LOCK_T;

/* return codes from "_findLock" */

typedef unsigned long FINDLOCK_T;

#define	FINDLOCK_FOUND		((FINDLOCK_T)0)
#define	FINDLOCK_ERROR		((FINDLOCK_T)-1)
#define	FINDLOCK_NOTFOUND	((FINDLOCK_T)-2)
#define	FINDLOCK_KEYMISMATCH	((FINDLOCK_T)-3)
#define	FINDLOCK_LOCKED		((FINDLOCK_T)-4)
#define	FINDLOCK_NOTLOCKED	((FINDLOCK_T)-5)
#define	FINDLOCK_LOCKACQUIRED	((FINDLOCK_T)-6)

/*
 * Forward declarations
 */

/* local main function implementation methods */

static FINDLOCK_T	lock_acquire(LOCK_T *a_lock, int *a_fd, char *a_root,
				char *a_key, char *a_object, int a_quiet,
				int a_wait, long a_timeout, int a_exclusive,
				char *a_altRoot, pid_t a_pid, zoneid_t a_zid);
static int		lock_release(int a_fd, char *a_key, char *a_object,
				int a_quiet);
static int		lock_status(int a_fd, char *a_key, char *a_object,
				int a_quiet);

/* local utility functions */

static int		_lockMatch(char *a_s1Lock, char *a_s2Lock);
static FINDLOCK_T	_findLock(LOCK_T *a_theLock, RECORDNUM_T *r_recordNum,
				int a_fd, char *a_object, char *a_key);
static int		_decrementLockCount(int a_fd, LOCK_T *a_theLock);
static int		_addLock(char *r_key, int a_fd, char *a_object,
				int a_exclusive, pid_t a_pid, zoneid_t a_zid);
static int		_incrementLockCount(int a_fd, LOCK_T *a_theLock);
static FINDLOCK_T	_lock_acquire(LOCK_T *a_lock, int a_fd, char *a_key,
				char *a_object, int a_quiet, int a_exclusive,
				pid_t a_pid, zoneid_t a_zid);
static char		*_getUniqueId(void);
static int		_openLockFile(char *a_root);
static void		sighup_handler(int a_signo);
static void		sigint_handler(int a_signo);
static boolean_t	_validateLock(int a_fd, LOCK_T *a_theLock, int a_quiet);

static int		signal_received = 0;

/*
 * main methods with external entry points
 */

/*
 * Name:	admin_lock
 * Synopsis:	main entry point for pkgadm "lock" subcommand
 * Description:	Control zone/package administrative locking
 * Returns: 0 on success, non-zero otherwise.
 */

int
admin_lock(int argc, char **argv)
{
	FINDLOCK_T		tResult;
	LOCK_T			theLock;
	char			*RFlag = "/";	/* altRoot */
	char			*endptr;
	char			*kFlag = "";	/* key */
	char			*oFlag = "";	/* object */
	char			*p;
	char			c;
	int			aFlag = 0;	/* acquire lock */
	int			eFlag = 0;	/* exclusive lock */
	int			exclusive = 1;	/* exclusive vs shared lock */
	int			fd;
	int			qFlag = 0;	/* quiet */
	int			rFlag = 0;	/* release lock */
	int			result;
	int			sFlag = 0;	/* shared lock */
	int			tFlag = 0;	/* test comparison */
	int			wFlag = 0;	/* wait */
	long			WFlag = 0;	/* wait timeout */
	pid_t			pFlag = 0;	/* process # */
	struct sigaction	nact;
	struct sigaction	oact;
	void			(*funcSighup)();
	void			(*funcSigint)();
	zoneid_t		zFlag = -1;	/* zone i.d. */

	while ((c = getopt(argc, argv, ":aek:o:p:qrR:stwW:z:")) != EOF) {
		switch (c) {
		case 'a':	/* acquire lock */
			aFlag++;
			break;

		case 'e':	/* exclusive lock */
			eFlag++;
			break;

		case 'k':	/* lock-key */
			kFlag = optarg;
			if (strlen(optarg) > LOCK_KEY_MAXLEN) {
				log_msg(LOG_MSG_ERR,
					MSG_LOCK_kARG_TOOLONG,
					strlen(optarg), LOCK_KEY_MAXLEN);
				return (1);
			}
			break;

		case 'o':	/* object */
			oFlag = optarg;
			if (strlen(optarg) > LOCK_OBJECT_MAXLEN) {
				log_msg(LOG_MSG_ERR,
					MSG_LOCK_oARG_TOOLONG,
					strlen(optarg), LOCK_OBJECT_MAXLEN);
				return (1);
			}
			break;

		case 'p':	/* process i.d. */
			errno = 0;
			endptr = 0;
			pFlag = strtol(optarg, &endptr, 10);
			if ((endptr != (char *)NULL) && (*endptr != '\0')) {
				log_msg(LOG_MSG_ERR, MSG_LOCK_pFLAG_BADINT,
							optarg, *endptr);
				return (1);
			}
			if ((pFlag == 0) && (errno != 0)) {
				log_msg(LOG_MSG_ERR,
					MSG_LOCK_pFLAG_ERROR,
					optarg,  strerror(errno));
				return (1);
			}
			break;

		case 'q':	/* quiet */
			qFlag++;
			break;

		case 'r':	/* release lock */
			rFlag++;
			break;

		case 'R':	/* alternative root */
			/* if root directory is not absolute path, error */
			if (*optarg != '/') {
				log_msg(LOG_MSG_ERR,
					MSG_LOCK_RARG_NOT_ABSOLUTE, optarg);
				return (1);
			}

			/* if root directory does not exist, create it */
			if (access(optarg, F_OK) != 0) {

				/* create top level root directory */
				if (mkdirp(optarg, 0755) != 0) {
					log_msg(LOG_MSG_ERR,
						MSG_LOCK_ALTROOT_CANTCREATE,
						optarg, strerror(errno));
					return (1);
				}
			}

			/* if $ALTROOT/tmp directory does not exist create it */
			p = pkgstrPrintf("%s/tmp", optarg);
			if (access(p, F_OK) != 0) {

				/* create $ALTROOT/tmp directory */
				if (mkdirp(p, 0777) != 0) {
					log_msg(LOG_MSG_ERR,
						MSG_LOCK_ALTROOT_CANTCREATE,
						p, strerror(errno));
					return (1);
				}
			}

			/* if $ALTROOT/tmp directory cannot be created, exit */
			if (access(p, F_OK) != 0) {
				log_msg(LOG_MSG_ERR, MSG_LOCK_ALTROOT_NONEXIST,
					optarg, strerror(errno));
				return (1);
			}

			(void) free(p);

			RFlag = optarg;
			break;

		case 's':	/* shared */
			sFlag++;
			break;

		case 't':	/* test comparison */
			tFlag++;
			break;

		case 'w':	/* wait */
			wFlag++;
			break;

		case 'W':	/* wait with timeout */
			errno = 0;
			endptr = 0;
			WFlag = strtol(optarg, &endptr, 10);
			if ((endptr != (char *)NULL) && (*endptr != '\0')) {
				log_msg(LOG_MSG_ERR, MSG_LOCK_WFLAG_BADINT,
							optarg, *endptr);
				return (1);
			}
			if ((WFlag == 0) && (errno != 0)) {
				log_msg(LOG_MSG_ERR,
					MSG_LOCK_WFLAG_ERROR,
					optarg,  strerror(errno));
				return (1);
			}
			wFlag++;
			break;

		case 'z':	/* zone i.d. */
			errno = 0;
			endptr = 0;
			zFlag = strtol(optarg, &endptr, 10);
			if ((endptr != (char *)NULL) && (*endptr != '\0')) {
				log_msg(LOG_MSG_ERR, MSG_LOCK_zFLAG_BADINT,
							optarg, *endptr);
				return (1);
			}
			if ((zFlag == 0) && (errno != 0)) {
				log_msg(LOG_MSG_ERR,
					MSG_LOCK_zFLAG_ERROR,
					optarg,  strerror(errno));
				return (1);
			}
			break;

		case ':':
			log_msg(LOG_MSG_ERR, MSG_MISSING_OPERAND, optopt);
			/* LINTED fallthrough on case statement */
		case '?':

		default:
			log_msg(LOG_MSG_ERR, MSG_USAGE);
			return (1);
		}
	}

	/*
	 * validate arguments
	 */

	/* if -t option is specified, override all other options */

	if (tFlag) {
		int	rs;
		int	rx;
		int	a;

		/* only 2 or 3 args are valid */

		a = argc-optind;
		if ((a < 2) || (a > 3)) {
			(void) fprintf(stderr, MSG_T_OPTION_ARGS, argc-optind);
			return (1);
		}

		/* if 3rd argument given, it is return value to check */

		if (a == 3) {
			rs = atoi(argv[optind+2]);
		}
		rx = _lockMatch(argv[optind+0], argv[optind+1]);

		/* if 3rd argument not given, code to check is code returned */

		if (a == 2) {
			rs = rx;
		}

		/* report results */

		if (a == 2) {
			(void) fprintf(stderr, MSG_T_RESULT_TWO,
				rx, argv[optind+0], argv[optind+1]);
			return (rx);
		}

		if (rx != rs) {
			(void) fprintf(stderr, MSG_T_RESULT_THREE,
				rs, rx, argv[optind+0], argv[optind+1]);
		}

		/* always successful */

		return (rx == rs ? 0 : 1);
	}

	/* must be no non-option arguments left */

	if ((argc-optind) > 0) {
		log_msg(LOG_MSG_ERR, MSG_USAGE);
		return (1);
	}

	/* -a and -r cannot be used together */

	if (aFlag && rFlag) {
		log_msg(LOG_MSG_ERR, MSG_LOCK_ar_TOGETHER);
		return (1);
	}

	/* -e and -s cannot be used together */

	if (eFlag && sFlag) {
		log_msg(LOG_MSG_ERR, MSG_LOCK_es_TOGETHER);
		return (1);
	}

	/* -e can only be used if -a is used */

	if (!aFlag && eFlag) {
		log_msg(LOG_MSG_ERR, MSG_LOCK_e_without_a);
		return (1);
	}

	/* -s can only be used if -a is used */

	if (!aFlag && sFlag) {
		log_msg(LOG_MSG_ERR, MSG_LOCK_s_without_a);
		return (1);
	}

	/*
	 * perform the requested operation
	 */

	/*
	 * hook SIGINT and SIGHUP interrupts into quit.c's trap handler
	 */

	/* hold SIGINT/SIGHUP interrupts */

	(void) sighold(SIGHUP);
	(void) sighold(SIGINT);

	/* connect sigint_handler() to SIGINT */

	nact.sa_handler = sigint_handler;
	nact.sa_flags = SA_RESTART;
	(void) sigemptyset(&nact.sa_mask);

	if (sigaction(SIGINT, &nact, &oact) < 0) {
		funcSigint = SIG_DFL;
	} else {
		funcSigint = oact.sa_handler;
	}

	/* connect sighupt_handler() to SIGHUP */

	nact.sa_handler = sighup_handler;
	nact.sa_flags = SA_RESTART;
	(void) sigemptyset(&nact.sa_mask);

	if (sigaction(SIGHUP, &nact, &oact) < 0) {
		funcSighup = SIG_DFL;
	} else {
		funcSighup = oact.sa_handler;
	}

	/* release hold on signals */

	(void) sigrelse(SIGHUP);
	(void) sigrelse(SIGINT);

	/* open the lock file */

	fd = _openLockFile(RFlag);
	if (fd < 0) {
		return (1);
	}

	if (aFlag) {
		/* set "exclusive" mode based on -e/-s flag used */

		if (sFlag) {
			exclusive = 0;
		} else if (eFlag) {
			exclusive = 1;
		}

		/* acquire lock */

		tResult = lock_acquire(&theLock, &fd, RFlag, kFlag, oFlag,
			qFlag, wFlag, WFlag, exclusive, RFlag, pFlag, zFlag);

		switch (tResult) {
		case FINDLOCK_LOCKACQUIRED:
			(void) fprintf(stdout, "%s\n",
				theLock._lrLock.lockKey);
			result = 0;
			break;
		case FINDLOCK_LOCKED:
			(void) fprintf(stdout, "%s\n",
				theLock._lrLock.lockObject);
			result = 1;
			break;
		default:
			result = 1;
			break;
		}

	} else if (rFlag) {
		/* release lock */
		result = lock_release(fd, kFlag, oFlag, qFlag);
	} else {
		/* lock status */
		result = lock_status(fd, kFlag, oFlag, qFlag);
	}

	/* close the lock file */

	(void) close(fd);

	/* return results of operation */

	return (result);
}

/*
 * local main function implementation methods
 */

/*
 * Name:	lock_acquire
 * Description:	implement lock acquisition implementing the wait/timeouts
 *		Calls _lock_acquire to attempt lock acquisition.
 * Arguments:
 *	a_theLock - lock object filled with contents of existing lock
 *	a_fd - file descriptor opened on the lock file
 *	a_root - root of file system to manipulate locks on
 *	a_key - key associated with lock to acquire
 *	a_object - object associated with lock to acquire
 *	a_wait - wait if lock cannot be acquired flag:
 *			== 0 - do not wait
 *			!= 0 - wait
 *	a_timeout - timeout if waiting to acquire busy lock:
 *			== 0 - no timeout (wait forever)
 *			!= 0 - max # seconds to wait to acquire busy lock
 *	a_quiet - quiet mode enabled flag
 *	a_exclusive - exclusive/shared lock flag
 *	a_pid - if != 0 process i.d. to associate with this lock
 *	a_zid - if >= 0 - zone i.d. to associate with this lock
 * Returns: int
 *		== 0 - successful
 *		!= 0 - not successful
 */

static FINDLOCK_T
lock_acquire(LOCK_T *a_theLock, int *a_fd, char *a_root, char *a_key,
	char *a_object, int a_quiet, int a_wait, long a_timeout,
	int a_exclusive, char *a_altRoot, pid_t a_pid, zoneid_t a_zid)
{
	int		notified = 0;
	FINDLOCK_T	result;
	time_t		timeout;
	int		closeOnExit = 0;

	/* reset the lock */

	bzero(a_theLock, sizeof (LOCK_T));

	/* open file if not open */

	if ((*a_fd) < 0) {
		(*a_fd) = _openLockFile(a_altRoot);
		if ((*a_fd) < 0) {
			return (FINDLOCK_ERROR);
		}
		closeOnExit++;
	}

	/* compute time after which acquire times out */

	timeout = time((time_t *)NULL) + a_timeout;

	for (;;) {
		time_t	curtime;

		/* attempt to aquire the lock */

		result = _lock_acquire(a_theLock, *a_fd, a_key, a_object,
				a_quiet, a_exclusive, a_pid, a_zid);

		/* return result if any result other than object is locked */

		switch (result) {
		case FINDLOCK_LOCKACQUIRED:

			/* close lock file if opened in this function */

			if (closeOnExit) {
				(void) close(*a_fd);
				*a_fd = -1;
			}

			return (FINDLOCK_LOCKACQUIRED);

		case FINDLOCK_FOUND:
		case FINDLOCK_NOTFOUND:
		case FINDLOCK_KEYMISMATCH:
		case FINDLOCK_NOTLOCKED:
		case FINDLOCK_ERROR:
		default:
			/* close lock file if opened in this function */

			if (closeOnExit) {
				(void) close(*a_fd);
				*a_fd = -1;
			}

			return (result);

		case FINDLOCK_LOCKED:
			;
			/* FALLTHROUGH */
		}

		/*
		 * object locked OR SIGINT/SIGHUP interrupt received;
		 * return error if not waiting for lock OR signal received
		 */

		if ((a_wait == 0) || (signal_received != 0)) {
			log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
				MSG_LOCK_ACQUIRE_BUSY_FIRST,
				a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
				a_object, a_key,
				a_theLock->_lrLock.lockObject,
				a_theLock->_lrLock.lockExclusive ?
						MSG_LOCK_EXC : MSG_LOCK_SHR,
				a_theLock->_lrLock.lockExclusive !=
					a_exclusive ? "" :
					MSG_LOCK_ACQUIRE_BUSY_ADDITIONAL);

			/* close lock file if opened in this function */

			if (closeOnExit) {
				(void) close(*a_fd);
				*a_fd = -1;
			}

			return (FINDLOCK_LOCKED);
		}

		/* waiting for lock - if timeout specified see if time left */

		if (a_timeout > 0) {
			curtime = time((time_t *)NULL);
			if (curtime > timeout) {
				log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
					MSG_LOCK_ACQUIRE_TIMEDOUT,
					a_exclusive ?
						MSG_LOCK_EXC : MSG_LOCK_SHR,
					a_object, a_key);

				/* close lock file if opened in this function */

				if (closeOnExit) {
					(void) close(*a_fd);
					*a_fd = -1;
				}

				return (FINDLOCK_ERROR);
			}
		}

		/*
		 * waiting to aquire lock:
		 * - notify waiting (one time only)
		 * - close lock file
		 * - sleep
		 * - open lock file
		 * - try again
		 */

		/* notify once */

		if (notified++ == 0) {
			log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_WRN,
				MSG_LOCK_ACQUIRE_WAITING,
				a_object);
		}

		/* close lock file */

		(void) close(*a_fd);

		/* wait (sleep) */

		(void) sleep(LOCK_SLEEP_INTERVAL);

		/* open the lock file and try again */

		*a_fd = _openLockFile(a_root);
		if (*a_fd < 0) {
			log_msg(LOG_MSG_ERR, MSG_LOCK_ACQUIRE_REOPEN_FAILED,
				a_object);

			/* close lock file if opened in this function */

			if (closeOnExit) {
				(void) close(*a_fd);
				*a_fd = -1;
			}

			return (FINDLOCK_ERROR);
		}
	}
}

/*
 * Name:	lock_release
 * Description:	implement lock release
 * Arguments:
 *	a_fd - file descriptor opened on the lock file
 *	a_key - key associated with lock to release
 *	a_object - object associated with lock to release
 *	a_quiet - quiet mode enabled flag
 * Returns: int
 *		== 0 - successful
 *		!= 0 - not successful
 */

static int
lock_release(int a_fd, char *a_key, char *a_object, int a_quiet)
{
	RECORDNUM_T	recordNum;
	LOCK_T		theLock;
	FINDLOCK_T	result;

	/* entry debugging info */

	log_msg(LOG_MSG_DEBUG, MSG_LOCK_RELEASE_ENTRY,
		a_key, a_object, a_quiet);

	/* find the lock to be released */

	result = _findLock(&theLock, &recordNum, a_fd, a_object, a_key);

	log_msg(LOG_MSG_DEBUG, MSG_LOCK_RELEASE_FINDRESULT,
		result, recordNum);

	/* determine how to release the lock if found */

	switch (result) {
		/*
		 * object is not locked but a key was specified
		 */
		case FINDLOCK_NOTLOCKED:
			log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
				MSG_LOCK_RELEASE_NOTLOCKED,
				a_object, a_key);
			return (result);

		/*
		 * object is locked and no matching key was specified
		 */
		case FINDLOCK_LOCKED:
			log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
				MSG_LOCK_RELEASE_LOCKED,
				a_object, a_key);
			return (result);

		/*
		 * object is not locked
		 */
		case FINDLOCK_NOTFOUND:
			log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
				MSG_LOCK_RELEASE_NOTFOUND,
				a_object, a_key);
			return (result);

		/*
		 * object is locked and specified key does not match
		 */
		case FINDLOCK_KEYMISMATCH:
			log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
				MSG_LOCK_RELEASE_KEYMISMATCH,
				a_object);
			return (result);

		/*
		 * error determining if object is locked
		 */
		case FINDLOCK_ERROR:
			log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
				MSG_LOCK_RELEASE_ERROR,
				a_object, a_key);
			perror(LOCK_FILENAME);
			return (result);

		/*
		 * object is locked and specified key matches
		 */
		case FINDLOCK_FOUND:
			log_msg(LOG_MSG_DEBUG, MSG_LOCK_RELEASE_FOUND,
				a_object, a_key);
			(void) _decrementLockCount(a_fd, &theLock);
			break;

		/*
		 * unknown return
		 */
		default:
			result = FINDLOCK_ERROR;
			break;

	}
	return (result);
}

/*
 * Name:	lock_status
 * Description:	implement lock status display/inquiry
 * Arguments:
 *	a_fd - file descriptor opened on the lock file
 *	a_key - key associated with lock to look up
 *	a_object - object associated with lock to look up
 *	a_quiet - quiet mode enabled flag
 * Returns: int
 *		== 0 - successful
 *		!= 0 - not successful
 */

static int
lock_status(int a_fd, char *a_key, char *a_object, int a_quiet)
{
	ADMINLOCK_T	*pll;
	LOCK_T		theLock;
	RECORDNUM_T	recordNum = 0;
	char		*pld;
	int		found = 0;
	long		pls;

	/* entry debugging info */

	log_msg(LOG_MSG_DEBUG, MSG_LOCK_STATUS_ENTRY,
		a_key, a_object);

	/* localize references to lock object */

	pld = &theLock._lrLockData[0];
	pll = &theLock._lrLock;
	pls = sizeof (theLock._lrLockData);

	bzero(pld, pls);

	/* read and process each lock */

	for (; pread(a_fd, pld, pls, pls*recordNum) == pls; recordNum++) {
		/* debug info on this lock */

		log_msg(LOG_MSG_DEBUG, MSG_LOCK_STATUS_READRECORD,
			recordNum, pll->lockCount,
			pll->lockObject, pll->lockKey, pll->lockPid,
			pll->lockZoneId);

		/* ignore if key specified and key does not match */

		if ((*a_key != '\0') &&
			(strcmp(pll->lockKey, a_key) != 0)) {
			continue;
		}

		/* ignore if object specified and object does not match */

		if ((*a_object != '\0') &&
			(strcmp(pll->lockObject, a_object) != 0)) {
			continue;
		}

		found++;

		/* process next lock if quiet operation */

		if (a_quiet != 0) {
			continue;
		}

		/* output header if first lock object */

		if (found == 1) {
			(void) fprintf(stdout,
				"%2s %2s %3s %8s %3s %9s %37s %s\n",
				"i#", "l#", "cnt", "pid", "zid", "lock-type",
				"---------------lock-key-------------",
				"lock-object");
		}

		/* output status line for this lock object */

		(void) fprintf(stdout,
			"%2ld %2ld %3ld %8ld %3d %9s %37s %s\n",
			recordNum, pll->lockRecordNum, pll->lockCount,
			pll->lockPid, pll->lockZoneId,
			pll->lockExclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
			pll->lockKey,
			*pll->lockObject == '\0' ? "*" : pll->lockObject);
	}

	/* return == 0 if found, != 0 if not found */

	return (found == 0 ? 1 : 0);
}

/*
 * local utility functions
 */

/*
 * Name:	_lock_acquire
 * Description:	implement lock acquisition without wait/timeouts
 * Arguments:
 *	a_theLock - lock object filled with contents of existing lock
 *	a_fd - file descriptor opened on the lock file
 *	a_key - key associated with lock to acquire
 *	a_object - object associated with lock to acquire
 *	a_quiet - quiet mode enabled flag
 *	a_exclusive - exclusive/shared lock flag
 *	a_pid - if != 0 process i.d. to associate with this lock
 *	a_zid - if >= 0 zone i.d. to associate with this lock
 * Returns: FINDLOCK_T
 */

static FINDLOCK_T
_lock_acquire(LOCK_T *a_theLock, int a_fd, char *a_key,
	char *a_object, int a_quiet, int a_exclusive, pid_t a_pid,
	zoneid_t a_zid)
{
	RECORDNUM_T	recordNum;
	FINDLOCK_T	result;
	char		key[LOCK_KEY_MAXLEN+1] = {'\0'};

	/* entry debugging info */

	log_msg(LOG_MSG_DEBUG, MSG_LOCK_ACQUIRE_ENTRY,
		a_key, a_object, a_quiet, a_exclusive);

	/* is the specified object already locked? */

	for (;;) {
		result = _findLock(a_theLock, &recordNum, a_fd, a_object,
			a_key);

		if (result != FINDLOCK_LOCKED) {
			break;
		}

		if (_validateLock(a_fd, a_theLock, a_quiet) == B_TRUE) {
			break;
		}
	}


	/* debug info on result of find of lock */

	log_msg(LOG_MSG_DEBUG, MSG_LOCK_ACQUIRE_FINDRESULT,
		a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
		result, recordNum);

	/* determine how to acquire the lock */

	switch (result) {
		/*
		 * object is not locked but a key was specified
		 */
		case FINDLOCK_NOTLOCKED:
			log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
				MSG_LOCK_ACQUIRE_NOTLOCKED,
				a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
				a_object, a_key);
			break;

		/*
		 * object is locked and no key was specified:
		 * - if lock is exclusively held, return "locked"
		 * - if exclusive lock requested, return "locked"
		 * - otherwise lock is shared and shared lock requested,
		 *   - increment lock count and return the key
		 */
		case FINDLOCK_LOCKED:
			/* return error if current lock exclusive */

			if (a_theLock->_lrLock.lockExclusive) {
				break;
			}

			/* return error if requesting exclusive lock */

			if (a_exclusive) {
				break;
			}

			/* shared requesting shared - add to shared lock */

			log_msg(LOG_MSG_DEBUG,
				MSG_LOCK_ACQUIRE_LOCKED_SHARED,
				a_object, a_key);

			/* increment shared lock count */

			if (_incrementLockCount(a_fd, a_theLock) == 0) {
				result = FINDLOCK_LOCKACQUIRED;
			} else {
				result = FINDLOCK_ERROR;
			}

			break;

		/*
		 * object is not locked
		 */
		case FINDLOCK_NOTFOUND:
			log_msg(LOG_MSG_DEBUG,
				MSG_LOCK_ACQUIRE_NOTFOUND,
				a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
				a_object);

			if (_addLock(key, a_fd, a_object, a_exclusive,
							a_pid, a_zid) == 0) {
				(void) strncpy(a_theLock->_lrLock.lockKey, key,
					sizeof (a_theLock->_lrLock.lockKey));
				result = FINDLOCK_LOCKACQUIRED;
			} else {
				result = FINDLOCK_ERROR;
			}
			break;

		/*
		 * object is locked, key specified, specified key does not match
		 */
		case FINDLOCK_KEYMISMATCH:
			log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_ERR,
				MSG_LOCK_ACQUIRE_KEYMISMATCH,
				a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
				a_object);
			break;

		/*
		 * error determining if object is locked
		 */
		case FINDLOCK_ERROR:
			log_msg(LOG_MSG_ERR, MSG_LOCK_ACQUIRE_ERROR,
				a_object, a_key, strerror(errno));
			break;

		/*
		 * object is locked and specified key matches
		 */
		case FINDLOCK_FOUND:
			/* return locked if object currently locked */
			if (a_exclusive != a_theLock->_lrLock.lockExclusive) {
				result = FINDLOCK_LOCKED;
				break;
			}

			log_msg(LOG_MSG_DEBUG, MSG_LOCK_ACQUIRE_FOUND_INC,
				a_object, a_key,
				a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR);

			/* increment shared lock */

			if (_incrementLockCount(a_fd, a_theLock) == 0) {
				result = FINDLOCK_LOCKACQUIRED;
			} else {
				result = FINDLOCK_ERROR;
			}
			break;

		/*
		 * unknown return
		 */
		default:
			result = FINDLOCK_ERROR;
			break;
	}

	return (result);
}

/*
 * Name:	_openLockFile
 * Description:	open the lock file, acquiring exclusive record locks
 * Arguments:
 *	a_root - root of file system to manipulate locks on
 * Returns: int
 *		>= 0 - successful - file descriptor lock file opened on
 *		< 0 - not successful
 */

static int
_openLockFile(char *a_root)
{
	WAITER_T	waiter;
	char		lockpath[MAXPATHLEN];
	int		fd;
	int		result;

	/* entry debugging info */

	log_msg(LOG_MSG_DEBUG, MSG_LOCK_OPENFILE_ENTRY,
		a_root, LOCK_FILENAME);

	/* generate path to lock directory */

	(void) snprintf(lockpath, sizeof (lockpath), "%s/%s",
		a_root, LOCK_DIRECTORY);

	if (access(lockpath, F_OK) != 0) {
		log_msg(LOG_MSG_ERR, MSG_LOCK_ROOTDIR_INVALID,
			lockpath, strerror(errno));
		return (-1);
	}

	/* generate path to lock file */

	(void) snprintf(lockpath, sizeof (lockpath),
		"%s/%s", a_root, LOCK_FILENAME);

	/* wait for open to succeed up to limits */

	for (waiter = WAITER_INITIAL;
		waiter < WAITER_MAX;
		waiter = WAITER_NEXT(waiter)) {

		/* LINTED O_CREAT without O_EXCL specified in call to open() */
		fd = open(lockpath, O_CREAT|O_RDWR, LOCK_FILEMODE);

		/* break out of loop if file opened */

		if (fd >= 0) {
			break;
		}

		/* failed - exit loop if due to access (permissions) failure */

		if (errno == EACCES) {
			break;
		}

		/* file is busy - wait and try again */

		if (waiter == WAITER_INITIAL) {
			log_msg(LOG_MSG_DEBUG,
				MSG_LOCK_OPENFILE_SLEEPING,
				strerror(errno), waiter);
		}

		(void) sleep(waiter);
	}

	/* if open filed generate error message and return error */

	if (fd < 0) {
		log_msg(LOG_MSG_DEBUG, MSG_LOCK_OPENFILE_FAILURE,
			strerror(errno));
		perror(lockpath);
		return (-1);
	}

	/*
	 * lock file opened - acquire exclusive section lock on entire file;
	 * wait for lockf to succeed up to limits
	 */

	for (waiter = WAITER_INITIAL;
		waiter < WAITER_MAX;
		waiter = WAITER_NEXT(waiter)) {

		/* acquire exclusive section lock on entire file */

		result = lockf(fd, F_LOCK, 0xFFFFF);

		/* break out of loop if entire file locked */

		if (result == 0) {
			break;
		}

		/* file is busy - wait and try again */

		if (waiter == WAITER_INITIAL) {
			log_msg(LOG_MSG_DEBUG, MSG_LOCK_OPENFILE_SLEEP2,
				strerror(errno), waiter);
		}

		(void) sleep(waiter);
	}

	/* if section lock failed generate error message and return error */

	if (result < 0) {
		log_msg(LOG_MSG_DEBUG, MSG_LOCK_OPENFILE_FAIL2,
			strerror(errno));
		perror(lockpath);
		(void) close(fd);
		return (-1);
	}

	/* file opened and locked - return success */

	log_msg(LOG_MSG_DEBUG, MSG_LOCK_OPENFILE_SUCCESS, fd);

	return (fd);
}

/*
 * Name:	_lockMatch
 * Description:	Compare two lock objects using file name match criteria
 * Arguments:
 *	a_s1Lock - first lock object to compare against the second
 *	a_s2Lock - second lock object to compare against the first
 * Returns:
 * 	== 0 - the locks match at some level
 *	!= 0 - the locks do not match at any level
 */

static int
_lockMatch(char *a_s1Lock, char *a_s2Lock)
{
	boolean_t	s1Sfx = B_FALSE;
	boolean_t	s2Sfx = B_FALSE;
	char		*final1Lock = (char *)NULL;
	char		*final2Lock = (char *)NULL;
	char		s1Buf[MAXPATHLEN] = {'\0'};
	char		s1Prefix[MAXPATHLEN] = {'\0'};
	char		s2Buf[MAXPATHLEN] = {'\0'};
	char		s2Prefix[MAXPATHLEN] = {'\0'};
	int		result = 0;
	int		s1Cnt;
	int		s2Cnt;

	/* entry assertions */

	assert(a_s1Lock != (char *)NULL);
	assert(a_s2Lock != (char *)NULL);

	/* entry debugging info */

	log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_ENTRY, a_s1Lock, a_s2Lock);

	/*
	 * attempt to find a common anchor between the two locks; that is,
	 * find the first node in the first lock that matches any node
	 * in the second lock; for example:
	 * --> a/b/c vs b/c/d
	 * -> common anchor is "b"; comparison would expand to:
	 * --> a/b/c/? vs ?/b/c/d
	 */

	/* process each node in the first lock */

	for (s1Cnt = 0; ; s1Cnt++) {
		/* get next first lock node */

		pkgstrGetToken_r((char *)NULL, a_s1Lock, s1Cnt, "/",
			s1Buf, sizeof (s1Buf));

		log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_FSTNODE, s1Cnt, s1Buf);

		/* exit if no more nodes left */

		if (s1Buf[0] == '\0') {
			break;
		}

		/* discover "." prefix for this node */

		pkgstrGetToken_r((char *)NULL, s1Buf, 0, ".", s1Prefix,
			sizeof (s1Prefix));

		s1Sfx = (strlen(s1Prefix) == strlen(s1Buf) ? B_FALSE : B_TRUE);

		/* search each second lock node; look for the first node lock */

		for (s2Cnt = 0; ; s2Cnt++) {
			/* get next second lock node */

			pkgstrGetToken_r((char *)NULL, a_s2Lock, s2Cnt, "/",
				s2Buf, sizeof (s2Buf));

			log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_SCNDNODE, s2Cnt,
				s2Buf);

			/* exit if no nodes left */

			if (s2Buf[0] == '\0') {
				break;
			}

			/* discover "." prefix for this node */

			pkgstrGetToken_r((char *)NULL, s2Buf, 0, ".", s2Prefix,
				sizeof (s2Prefix));

			s2Sfx = (strlen(s2Prefix) ==
					strlen(s2Buf) ? B_FALSE : B_TRUE);

			/*
			 * process this pair of nodes:
			 * if both nodes do not have a prefix, then directly
			 * compare the nodes (e.g. a/b vs c/d: a vs c, b vs d)
			 * and break out of the loop if there is a match;
			 * otherwise, compare prefixes and break out of the
			 * loop if there is a match (e.g. a.* / b.* vs
			 * vs c.* / d.*: a.* vs c.*, a.* vs d.*, b.* vs c.*,
			 * b.* vs d.*).
			 */

			log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_NODES, s1Buf,
				s1Prefix, s1Sfx, s2Buf, s2Prefix, s2Sfx);

			if ((s1Sfx == B_FALSE) || (s2Sfx == B_FALSE)) {
				/* one doesnt have a prefix direct comparison */

				if (strcmp(s1Buf, s2Buf) == 0) {
					log_msg(LOG_MSG_DEBUG,
						MSG_LCKMCH_DIRMCH,
						s1Buf, s2Buf);
					break;
				}

				/* nodes do not directly match, continue */

				log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_DIRNOMCH,
						s1Buf, s2Buf);
				continue;
			}

			/* both have prefix, compare prefixes */

			if (strcmp(s1Prefix, s2Prefix) == 0) {
				log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_PFXMCH,
					s1Prefix, s2Prefix);
				break;
			}

			/* prefixes do not match, continue */

			log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_PFXNOMCH, s1Prefix,
				s2Prefix);
		}

		/*
		 * match found if not at the end of the second lock node list,
		 * break out of loop because some match between the two lock
		 * objects has been found
		 */

		if (s2Buf[0] != '\0') {
			break;
		}
	}

	/*
	 * at this point, either a match has been found between the nodes in
	 * the two lock objects, or there is no commonality at all between
	 * the two lock objects.
	 *
	 * s1Buf[0] == '\0' && s2Buf[0] == '\0':
	 * --> nothing in first lock matches anything in second lock:
	 * ----> (s1Cnt == 1) || (s2Cnt == 1) && (s1Sfx == B_FALSE)
	 * ----> || (s2Sfx == B_FALSE)
	 * --------> an absolute lock do not match
	 * ----> else both object locks have nothing in common - match
	 *
	 * s2Buf[0] != '\0' && s1Buf[0] != '\0' && s1Cnt > 0 && s2Cnt > 0
	 * --> locks have incompatible overlaps - no match, such as:
	 * ---->  a.* / b.* / c.* / d.*   and   y.* / b.* / c.*
	 *
	 * s1Cnt == 0 && s2Cnt == 0:
	 * --> locks begin with same node - do comparison
	 *
	 * s1Cnt != 0 && s2Cnt == 0 && s2Buf[0] != '\0'
	 * --> second lock is subset of first lock
	 *
	 * s2Cnt == 0 && s2Buf[0] != '\0':
	 * --> s1Buf[s1Cnt] matches s2Buf[0] - second is subset of first
	 *
	 * s2Cnt != 0 && s1Cnt == 0 && s1Buf[0] != '\0':
	 * --> first lock is subset of second lock
	 *
	 */

	log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_FSTLCK, s1Cnt, s1Buf,
		s1Prefix, s1Sfx);
	log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_SCNDLCK, s2Cnt, s2Buf,
		s2Prefix, s2Sfx);

	/* process any direct comparisons that might be possible */

	if ((s1Buf[0] == '\0') && (s2Buf[0] == '\0')) {
		/* nothing in first matches anything in second lock */

		if (((s1Cnt == 1) || (s2Cnt == 1)) &&
			((s1Sfx == B_FALSE) || (s2Sfx == B_FALSE))) {
			/* two absolute locks match (e.g. 'file' and 'dir') */
			log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_ABSNOMCH, a_s1Lock,
				a_s2Lock);
			return (1);
		}

		/* two object locks have nothing in common: match */

		log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_OBJMCH, a_s1Lock, a_s2Lock);

		return (0);
	}

	if ((s2Buf[0] != '\0') && (s1Buf[0] != '\0') &&
						(s1Cnt > 0) && (s2Cnt > 0)) {
		/* incompatible overlapping objects */

		log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_OVLPNOMCH, a_s1Lock, a_s2Lock,
			s1Cnt+1, s1Buf);

		return (1);
	}

	/*
	 * must compare each node of each lock to determine match;
	 * start off at the first byte of both locks
	 */

	final1Lock = a_s1Lock;
	final2Lock = a_s2Lock;

	if ((s1Cnt == 0) && (s2Cnt == 0)) {
		/* both have first match - start comparison from the begining */

		log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_SAME, a_s1Lock, a_s2Lock,
			s1Buf);

	} else if ((s1Cnt != 0) && (s2Cnt == 0) && (s2Buf[0] != '\0')) {
		/* second lock begins somewhere inside of the first lock */

		log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_SCNDSUB, a_s2Lock, a_s1Lock,
			s1Cnt+1, s1Buf);

		/* advance first lock to matching node in second lock */

		if (strchr(a_s1Lock, '/') != (char *)NULL) {
			for (; s1Cnt > 0 && (*final1Lock != '\0');
				final1Lock++) {
				if (*final1Lock == '/') {
					s1Cnt--;
				}
			}
		}
	} else if ((s2Cnt != 0) && (s1Cnt == 0) && (s1Buf[0] != '\0')) {
		/* first lock begins somewhere inside of the second lock */

		log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_FRSTSUB, a_s1Lock, a_s2Lock,
			s2Cnt+1, s2Buf);

		/* advance second lock to matching node in first lock */

		if (strchr(a_s2Lock, '/') != (char *)NULL) {
			for (; s2Cnt > 0 && (*final2Lock != '\0');
				final2Lock++) {
				if (*final2Lock == '/') {
					s2Cnt--;
				}
			}
		}
	} else {
		/* unknown condition (probably impossible): directly compare */

		log_msg(LOG_MSG_ERR, MSG_LCKMCH_DONTKNOW, a_s1Lock, a_s2Lock);
	}

	/*
	 * locks have common node - compare from that node forward
	 */

	log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_READY, final1Lock, final2Lock);

	/* compare each node (prefix) - success when no more nodes to compare */

	for (s1Cnt = 0; ; s1Cnt++) {
		/* get next node from first lock */

		pkgstrGetToken_r((char *)NULL, final1Lock, s1Cnt, "/", s1Buf,
			sizeof (s1Buf));

		/* success if at end of lock */

		if (s1Buf[0] == '\0') {
			break;
		}

		/* get next node from second lock */

		pkgstrGetToken_r((char *)NULL, final2Lock, s1Cnt, "/", s2Buf,
			sizeof (s2Buf));

		/* success if at end of lock */

		if (s2Buf[0] == '\0') {
			break;
		}

		/* compare both nodes */

		result = fnmatch(s1Buf, s2Buf, 0);
		if (result != 0) {
			result = fnmatch(s2Buf, s1Buf, 0);
		}

		/* failure if nodes do not match */

		if (result != 0) {
			log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_NODEFAIL,
				s1Cnt, s1Buf, s2Buf);
			return (1);
		}

		/* nodes match, continue and compare next set of nodes */

		log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_NODEOK, s1Cnt, s1Buf, s2Buf);
	}

	/* no more nodes to compare - locks match */

	log_msg(LOG_MSG_DEBUG, MSG_LCKMCH_MATCHOK, final1Lock, final2Lock);

	return (0);
}

/*
 * Name:	_findLock
 * Description:	Locate specified lock in lock file
 * Arguments:
 *	a_theLock - lock object filled with contents of lock (if found)
 *	r_recordNum - will contain record number if lock found
 *		- will be RECORDNUM_NONE if lock not found
 *	a_fd - file descriptor opened on the lock file
 *	a_key - key associated with lock to look up
 *	a_object - object associated with lock to look up
 * Returns:
 *	FINDLOCK_FOUND - specified lock found; a_theLock contains contents
 *		of found lock, r_recordNum contain record number of lock
 *	FINDLOCK_ERROR - failed - error occurred looking up the lock
 *	FINDLOCK_NOTFOUND - specified object is not locked
 *	FINDLOCK_KEYMISMATCH - object lock found but specified key doesnt match
 *	FINDLOCK_LOCKED - object lock found but no key specified
 *	FINDLOCK_NOTLOCKED - object not locked
 */

static FINDLOCK_T
_findLock(LOCK_T *a_theLock, RECORDNUM_T *r_recordNum,
	int a_fd, char *a_object, char *a_key)
{
	ADMINLOCK_T	*pll;
	char		*pld;
	int		recordNum = 0;
	long		pls;
	off_t		pos;

	/* reset returned record number to "none" */

	*r_recordNum = RECORDNUM_NONE;

	/* localize references to lock object */

	pld = &a_theLock->_lrLockData[0];
	pll = &a_theLock->_lrLock;
	pls = sizeof (a_theLock->_lrLockData);

	/* zero out returned lock data */

	bzero(pld, pls);

	/* debug info before processing lock file */

	log_msg(LOG_MSG_DEBUG, MSG_LOCK_FINDLOCK_ENTRY,
		a_object, a_key);

	/* rewind to beginning of lock file */

	pos = lseek(a_fd, 0L, SEEK_SET);
	if (pos == (off_t)-1) {
		log_msg(LOG_MSG_ERR, MSG_LOCK_FINDLOCK_LSEEK_FAILURE,
			a_object, a_key, strerror(errno));
		return (FINDLOCK_ERROR);
	}

	/* read and process each lock */

	for (; pread(a_fd, pld, pls, pls*recordNum) == pls; recordNum++) {
		/* debug info on this lock */

		log_msg(LOG_MSG_DEBUG, MSG_LOCK_FINDLOCK_READRECORD,
			recordNum, pll->lockCount,
			pll->lockObject, pll->lockKey, pll->lockPid,
			pll->lockZoneId);

		/* continue if object is not the one we are looking for */

		if (_lockMatch(a_object, pll->lockObject) != 0) {
			continue;
		}

		/*
		 * object found; return locked if searching for no key
		 */

		if (*a_key == '\0') {
			/* no key specified - object is locked */
			*r_recordNum = recordNum;
			return (FINDLOCK_LOCKED);
		}

		/*
		 * object found and keys present; see if keys match
		 */

		if (strcmp(pll->lockKey, a_key) != 0) {
			/* keys do not match */
			*r_recordNum = recordNum;
			return (FINDLOCK_KEYMISMATCH);
		}

		/* object found and keys match - return match */

		log_msg(LOG_MSG_DEBUG, MSG_LOCK_FINDLOCK_FOUND);

		*r_recordNum = recordNum;
		return (FINDLOCK_FOUND);
	}

	/* object not locked - return error if key supplied */

	if (*a_key != '\0') {
		return (FINDLOCK_NOTLOCKED);
	}

	/* object not locked and key not supplied - no lock found */

	log_msg(LOG_MSG_DEBUG, MSG_LOCK_FINDLOCK_NOTFOUND);

	return (FINDLOCK_NOTFOUND);
}

/*
 * Name:	_addLock
 * Description:	Add a new lock to the lock file
 * Arguments:
 *	r_key - if lock acquired key is placed here
 *	a_fd - file descriptor opened on the lock file
 *	a_object - object to lock
 *	a_exclusive - type of lock to add:
 *		== 0 - shared lock
 *		!= 0 - exclusive lock
 *	a_pid - if != 0 process i.d. to associate with this lock
 *	a_zid - if >= 0 zone i.d. to associate with this lock
 * Returns:	int
 *			== 0 - success
 *			!= 0 - failure
 */

static int
_addLock(char *r_key, int a_fd, char *a_object, int a_exclusive, pid_t a_pid,
	zoneid_t a_zid)
{
	LOCK_T	theLock;
	char	*key;
	off_t	pos;
	ssize_t	result;

	/* get unique i.d. for this lock */

	key = _getUniqueId();

	/* determine record number for next record in lock file */

	pos = lseek(a_fd, 0L, SEEK_END);
	if (pos == (off_t)-1) {
		log_msg(LOG_MSG_ERR, MSG_LOCK_ADDLOCK_LSEEK_FAILURE,
			a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
			a_object, strerror(errno));
		return (1);
	}

	/* allocate storace for this lock */

	bzero(&theLock, sizeof (theLock));

	/* fill in components of the lock */

	(void) strlcpy(theLock._lrLock.lockObject, a_object,
							LOCK_OBJECT_MAXLEN);
	(void) strlcpy(theLock._lrLock.lockKey, key, LOCK_KEY_MAXLEN);
	theLock._lrLock.lockCount = 1;
	theLock._lrLock.lockPid = (a_pid > 0 ? a_pid : 0);
	theLock._lrLock.lockRecordNum = (pos == 0 ? 0 : (pos/sizeof (LOCK_T)));
	theLock._lrLock.lockExclusive = a_exclusive;
	theLock._lrLock.lockZoneId = (a_zid >= 0 ? a_zid : -1);

	/* debug info on new lock */

	log_msg(LOG_MSG_DEBUG, MSG_LOCK_ADDLOCK_ADDING,
		a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
		pos, theLock._lrLock.lockObject, theLock._lrLock.lockKey,
		theLock._lrLock.lockPid, theLock._lrLock.lockZoneId);

	/* write the new lock record to the end of the lock file */

	result = pwrite(a_fd, &theLock, LOCK_SIZE, pos);
	if (result != LOCK_SIZE) {
		log_msg(LOG_MSG_ERR, MSG_LOCK_ADDLOCK_PWRITE_FAILURE,
			a_exclusive ? MSG_LOCK_EXC : MSG_LOCK_SHR,
			a_object, strerror(errno));
		return (1);
	}

	/* output the key assigned to standard out */

	(void) strncpy(r_key, key, LOCK_KEY_MAXLEN);

	return (0);
}

static int
_incrementLockCount(int a_fd, LOCK_T *a_theLock)
{
	ADMINLOCK_T	*pll;
	char		*pld;
	long		pls;
	ssize_t		result;

	/* localize references to lock object */

	pld = &a_theLock->_lrLockData[0];
	pll = &a_theLock->_lrLock;
	pls = sizeof (a_theLock->_lrLockData);

	/* debug info on incrementing lock */

	log_msg(LOG_MSG_DEBUG, MSG_LOCK_INCLOCK_ENTRY,
		a_theLock->_lrLock.lockExclusive ?
				MSG_LOCK_EXC : MSG_LOCK_SHR,
		pll->lockRecordNum, pll->lockCount);

	/* increment lock count */

	pll->lockCount++;

	/* write out updated lock */

	result = pwrite(a_fd, pld, pls, pll->lockRecordNum*pls);
	if (result != pls) {
		log_msg(LOG_MSG_ERR, MSG_LOCK_INCLOCK_PWRITE_FAILURE,
			a_theLock->_lrLock.lockExclusive ?
					MSG_LOCK_EXC : MSG_LOCK_SHR,
			a_theLock->_lrLock.lockObject,
			strerror(errno));
		return (1);
	}

	/* debug info lock incremented */

	log_msg(LOG_MSG_DEBUG, MSG_LOCK_INCLOCK_DONE,
		pll->lockRecordNum, pll->lockCount,
		pll->lockObject, pll->lockKey);

	return (0);
}

/*
 * Name:	_validateLock
 * Description:	determine if a specified lock is valid; if the lock is not valid
 *		then remove the lock
 * Arguments:	a_fd - file descriptor opened on the lock file
 *		a_theLock - lock object to validate
 * Returns:	boolean_t
 *			B_TRUE - the lock is valid
 *			B_FALSE - the lock is not valid and has been removed
 */

static boolean_t
_validateLock(int a_fd, LOCK_T *a_theLock, int a_quiet)
{
	ADMINLOCK_T	*pll;
	char		*pld;
	long		pls;
	char		path[MAXPATHLEN];

	/* localize references to lock object */

	pld = &a_theLock->_lrLockData[0];
	pll = &a_theLock->_lrLock;
	pls = sizeof (a_theLock->_lrLockData);

	/* return true if no process i.d. associated with lock */

	if (pll->lockPid <= 0) {
		log_msg(LOG_MSG_DEBUG, MSG_VALID_NOPID, pll->lockObject);
		return (B_TRUE);
	}

	/* see if the zone i.d. matches */

	if (pll->lockZoneId != getzoneid()) {
		log_msg(LOG_MSG_DEBUG, MSG_VALID_BADZID, pll->lockObject,
		pll->lockZoneId, getzoneid());
		return (B_TRUE);
	} else {
		log_msg(LOG_MSG_DEBUG, MSG_VALID_ZIDOK, pll->lockObject,
		pll->lockZoneId, getzoneid());
	}

	/* see if the process is still active */

	pkgstrPrintf_r(path, sizeof (path), "/proc/%d", pll->lockPid);
	if (access(path, F_OK) == 0) {
		log_msg(LOG_MSG_DEBUG, MSG_VALID_OK, pll->lockObject,
			pll->lockPid, path);
		return (B_TRUE);
	}

	log_msg(LOG_MSG_DEBUG, MSG_VALID_NOTOK, pll->lockObject, pll->lockPid,
		path);

	/* delete this lock */

	log_msg(a_quiet ? LOG_MSG_DEBUG : LOG_MSG_WRN,
		MSG_VALID_STALE, pll->lockObject, pll->lockPid,
		pll->lockZoneId);

	_decrementLockCount(a_fd, a_theLock);

	return (B_FALSE);
}

static int
_decrementLockCount(int a_fd, LOCK_T *a_theLock)
{
	ADMINLOCK_T	*pll;
	LOCK_T		tmpLock;
	RECORDNUM_T	lastRecord;
	char		*pld;
	long		pls;
	off_t		lastPos;
	ssize_t		result;
	int		res;

	/* localize references to lock object */

	pld = &a_theLock->_lrLockData[0];
	pll = &a_theLock->_lrLock;
	pls = sizeof (a_theLock->_lrLockData);

	/* decrement lock count */

	pll->lockCount--;

	/* if lock count > 0 then write out and leave locked */

	if (pll->lockCount > 0) {
		log_msg(LOG_MSG_DEBUG, MSG_LOCK_DECLOCK_DECING,
			a_theLock->_lrLock.lockExclusive ?
				MSG_LOCK_EXC : MSG_LOCK_SHR,
			pll->lockRecordNum, pll->lockCount);

		result = pwrite(a_fd, pld, pls, pll->lockRecordNum*pls);
		if (result != pls) {
			log_msg(LOG_MSG_ERR, MSG_LOCK_DECLOCK_PWRITE_FAILURE,
				a_theLock->_lrLock.lockExclusive ?
						MSG_LOCK_EXC : MSG_LOCK_SHR,
				a_theLock->_lrLock.lockObject,
				strerror(errno));
			return (1);
		}

		log_msg(LOG_MSG_DEBUG, MSG_LOCK_DECLOCK_DONE,
			pll->lockRecordNum, pll->lockCount,
			pll->lockObject, pll->lockKey);

		return (0);
	}

	/*
	 * lock count zero - erase the record
	 */

	/* find last record in the lock file */

	lastPos = lseek(a_fd, 0L, SEEK_END);	/* get size of lock file */
	if (lastPos == (off_t)-1) {
		log_msg(LOG_MSG_ERR, MSG_LOCK_DECLOCK_LSEEK_FAILURE,
			a_theLock->_lrLock.lockExclusive ?
					MSG_LOCK_EXC : MSG_LOCK_SHR,
			a_theLock->_lrLock.lockObject,
			strerror(errno));
		return (1);
	}

	lastRecord = (lastPos/pls)-1;	/* convert size to record # */

	log_msg(LOG_MSG_DEBUG, MSG_LOCK_DECLOCK_REMOVE,
		lastPos, lastRecord, pll->lockRecordNum);

	/* see if removing last record of file */

	if (lastRecord == pll->lockRecordNum) {
		/* debug info removing last record */

		log_msg(LOG_MSG_DEBUG, MSG_LOCK_DECLOCK_LASTONE,
			a_theLock->_lrLock.lockExclusive ?
				MSG_LOCK_EXC : MSG_LOCK_SHR,
			lastRecord, lastPos-pls);

		/* removing last record of file, truncate */

		res = ftruncate(a_fd, lastPos-pls);
		if (res == -1) {
			log_msg(LOG_MSG_ERR, MSG_LOCK_DECLOCK_FTRUNCATE_FAILURE,
				a_theLock->_lrLock.lockExclusive ?
						MSG_LOCK_EXC : MSG_LOCK_SHR,
				a_theLock->_lrLock.lockObject,
				strerror(errno));
				return (1);
		}
		return (0);
	}

	/*
	 * not removing last record of file:
	 * read last record, truncate file one record,
	 * replace record to be removed with last record read
	 */

	log_msg(LOG_MSG_DEBUG, MSG_LOCK_DECLOCK_REMOVING,
		pll->lockRecordNum, lastRecord, lastPos-pls);

	/* read in the last record */

	result = pread(a_fd, tmpLock._lrLockData, pls, lastRecord*pls);
	if (result != pls) {
		log_msg(LOG_MSG_ERR, MSG_LOCK_DECLOCK_PREAD_FAILURE,
			a_theLock->_lrLock.lockExclusive ?
					MSG_LOCK_EXC : MSG_LOCK_SHR,
			a_theLock->_lrLock.lockObject,
			strerror(errno));
		return (1);

	}

	/* truncate lock file removing the last record (just read in) */

	res = ftruncate(a_fd, lastPos-pls);
	if (res == -1) {
		log_msg(LOG_MSG_ERR, MSG_LOCK_DECLOCK_FTRUNCATE_FAILURE,
			a_theLock->_lrLock.lockExclusive ?
					MSG_LOCK_EXC : MSG_LOCK_SHR,
			a_theLock->_lrLock.lockObject,
			strerror(errno));
			return (1);
	}

	/* update record to indicate its new position in the lock file */

	tmpLock._lrLock.lockRecordNum = pll->lockRecordNum;

	/* write out the updated record to the new location */

	result = pwrite(a_fd, tmpLock._lrLockData, pls, pll->lockRecordNum*pls);
	if (result != pls) {
		log_msg(LOG_MSG_ERR, MSG_LOCK_DECLOCK_PWRITE_FAILURE,
			a_theLock->_lrLock.lockExclusive ?
					MSG_LOCK_EXC : MSG_LOCK_SHR,
			a_theLock->_lrLock.lockObject,
			strerror(errno));
		return (1);
	}

	return (0);
}

/*
 * Name:	_getUniqueId
 * Description:	Generate a unique ID that can be used as a key for a new lock
 * Arguments:	None
 * Returns:	char *
 *			== NULL - error, no key generated
 *			!= NULL - generated key
 * NOTE:    	Any results returned is placed in new storage for the
 *		calling method. The caller must use 'lu_memFree' to dispose
 *		of the storage once the results are no longer needed.
 */

static char *
_getUniqueId(void)
{
	char		*args[10];
	char		*execResults;
	char		newkey[LOCK_KEY_MAXLEN];
	hrtime_t	hretime;
	int		b;
	int		execStatus;
	struct tm	tstruct;
	time_t		thetime;

	/*
	 * try and use makeuuid to generate a unique i.d. Such a unique i.d.
	 * will look like:
	 *		7814e3c1-1dd2-11b2-9fe8-000d560ddc82
	 */

	args[0] = "makeuuid";
	args[1] = (char *)NULL;

	b = e_ExecCmdList(&execStatus, &execResults, (char *)NULL,
		"/usr/bin/makeuuid", (char *)NULL);

	if ((b == 0) && (execStatus == 0) && (*execResults != '\0')) {
		char	*p;
		p = strpbrk(execResults, " \t\n");
		if (p != (char *)NULL) {
			*p = '\0';
		}
		log_msg(LOG_MSG_DEBUG, MSG_LOCK_GENUID_MAKEUUID,
			execResults);
		return (execResults);
	}

	/*
	 * cannot run makeuuid - generate own unique key - the key is the
	 * same length as unique uid but contains different information that
	 * is as unique as can be made - include current hires time (nanosecond
	 * real timer. Such a unique i.d. will look like:
	 *		0203104092-1145345-0004e94d6af481a0
	 */

	hretime = gethrtime();

	thetime = time((time_t *)NULL);
	(void) localtime_r(&thetime, &tstruct);

	(void) snprintf(newkey, sizeof (newkey),
		"%02d%02d%02d%03d-%02d%02d%02d%d-%016llx", tstruct.tm_mday,
		tstruct.tm_mon, tstruct.tm_year, tstruct.tm_yday,
		tstruct.tm_hour, tstruct.tm_min, tstruct.tm_sec,
		tstruct.tm_wday, hretime);

	log_msg(LOG_MSG_DEBUG, MSG_LOCK_GENUID_INTERNAL, newkey);
	return (strdup(newkey));
}

/*
 * Name:	sigint_handler
 * Synopsis:	SIGINT interrupt handler
 * Description:	Catch the "SIGINT" signal; increment signal_received
 *		global variable,
 * Arguments:	signo - [RO, *RO] - (int)
 *			Signal number that was caught
 * Returns:	void
 */

static void
sigint_handler(int a_signo)
{
	signal_received++;
}

/*
 * Name:	sighup_handler
 * Synopsis:	SIGHUP interrupt handler
 * Description:	Catch the "SIGHUP" signal; increment signal_received
 *		global variable,
 * Arguments:	signo - [RO, *RO] - (int)
 *			Signal number that was caught
 * Returns:	void
 */

static void
sighup_handler(int a_signo)
{
	signal_received++;
}