/*
 * libnqs/queue/openqacc.c
 * 
 * DESCRIPTION:
 *
 *	Open the queue access file for the specified queue descriptor,
 *	if queue access is by gid and uid.
 *
 *	Original Author:
 *	-------
 *	Robert W. Sandstrom, Sterling Software Incorporated.
 *	February 27, 1986.
 */

#include <libnqs/license.h>
#include <libnqs/proto.h>
#include <libnqs/nqsxdirs.h>		/* NQS directories */

/*
 *	Configurable parameters.
 */
#define	MAX_RETRY	50	/* Maximum queue access file retry limit */

/*
 *	External functions.
 */

/*** openqacc
 *
 *
 *	int openqacc():
 *
 *	Open the queue access file for the specified queue descriptor,
 *	if access is by gid and uid.
 *
 *	Returns:
 *	      >=0: if queue access is by gid and uid, and the queue access
 *		   file was successfully opened.  The value returned
 *		   is the file descriptor opened to read the queue
 *		   access file.
 *	       -1: if queue access is unrestricted (no queue access
 *		   file is opened).
 *	       -2: if queue access IS by gid and uid, but some terrible
 *		   error prevented us from opening the queue access
 *		   file.
 *	       -3: if the queue has been deleted.
 */
int openqacc (struct confd *queuefile, struct gendescr *descr)
{
	char path [MAX_PATHNAME+1];	/* Queue access file pathname */
	char quename [MAX_QUEUENAME+1];	/* Name of queue */
	register int fd;		/* Queue access file descriptor */
	int retry;			/* Race condition retry */
	long position;			/* Offset in NQS database file of */
					/* the queue file descriptor */
	long blockpos;			/* The block offset in the NQS */
					/* queue file descriptor file of the */
					/* queue descriptor */

  	assert (queuefile != NULL);
  	assert (descr     != NULL);
  
	position = telldb (queuefile);	/* Remember offset of descriptor */
	blockpos = blockdb (position) * ATOMICBLKSIZ;
	strcpy (quename, descr->v.que.namev.name);
	/*
	 *  Open the queue access file.
	 */
	retry = 0;			/* Retry count */
	fd = -1;			/* Queue access file not open */
	while (retry <= MAX_RETRY &&
	      (descr->v.que.status & QUE_BYGIDUID)) {
		sprintf (path, "%s/q%08lx%04x", Nqs_qaccess,
			 telldb (queuefile), descr->v.que.accessversion);
		if ((fd = open (path, O_RDONLY)) == -1) {
			/*
			 *  We did not succeed in opening the queue
			 *  access file (because a queue update JUST
			 *  occurred)....  We may get in a race here
			 *  with the daemon.  However the likelihood
			 *  of this race going on for more than 1 or
			 *  2 iterations is VERY low.  We are simply
			 *  paying the price for not using locks (which
			 *  would also slow everyone down all of the
			 *  time, and make this whole thing less portable....
			 */
			seekdb (queuefile, blockpos);	/* Seek back to load */
							/* block */
			descr = nextdb (queuefile);	/* Read descr */
			while (descr != (struct gendescr *)0 &&
			       telldb (queuefile) < position) {
				descr = nextdb (queuefile);
			}
			if (descr == (struct gendescr *)0 ||
			    telldb(queuefile) > position ||
			    strcmp (descr->v.que.namev.name, quename)) {
				/*
				 *  The queue has been deleted, or
				 *  replaced by another queue!
				 */
				return (-3);		/* No such queue */
			}
			else retry += 1;		/* Keep counter */
		} 
		else retry = MAX_RETRY+1;		/* Set for exit */
	}
	if (fd == -1) {			/* Queue access file not open */
		if (descr->v.que.status & QUE_BYGIDUID) {
			/*
			 *  Something is wrong since the queue exists and
			 *  access is by gid and uid, but the queue access
			 *  file was not opened.
			 */
			return (-2);	/* Unable to open queue access file! */
		}
		return (-1);		/* Queue access is unrestricted */
	}
	return (fd);			/* Return the opened file descriptor */
}
