#ifndef lint
static char *RCSid = "$Header: lock.c,v 1.6 90/04/13 12:25:01 mr-frog Exp $";
#endif

/*
 * lock.c
 *
 * set and unset locks on given files by-user.
 *
 * Dave Pare, 1986
 */

#include <stdio.h>
#include "misc.h"
#include <netinet/in.h>
#include <errno.h>
#include <signal.h>
#include "user.h"
#include "tm.h"
#include "file.h"
#include "queue.h"

struct shlock {
	struct qelem queue;
	struct user *who;	/* who is locking */
};

struct	qelem locks[EF_MAX];

initlock()
{
	int	i;

	for (i=0; i<EF_MAX; i++)
		initque(&locks[i]);
}

/*
 * user places a particular kind
 * of lock on a batch of files
 */
int
lock(user, mask)
	struct	user *user;
	int	mask;
{
	extern	int seq;
	struct	shlock *lp;
	int	n;

	if (mask == 0) {
		logerror("lock: nothing to lock!");
		sendnum(user->u_iop, seq, TM_PANIC);
		return 0;
	}
	while ((n = ffs(mask)-1) >= 0) {
		if (n < 0 || n >= EF_MAX) {
			logerror("lock: request (%d) out of bounds", n);
			sendnum(user->u_iop, seq, TM_PANIC);
			return 0;
		}
		lp = (struct shlock *) malloc(sizeof(struct shlock));
		if (lp == (struct shlock *)0)
			logerror("malloc failed in lock");
		insque(&lp->queue, &locks[n]);
		lp->who = user;
		mask &= ~bit(n);
	}
	return 1;
}

/*
 * User removes a lock from a batch of files
 */
int
unlock(user, mask)
	struct	user *user;
	int	mask;
{
	extern	int seq;
	register struct shlock *lp;
	register struct qelem *qp;
	int	n;
	struct	qelem *head;

	if (mask == 0) {
		logerror("unlock: nothing to unlock");
		return 0;
	}
	while ((n = ffs(mask)-1) >= 0) {
		if (n < 0 || n >= EF_MAX) {
			logerror("unlock file request (%d) out of bounds", n);
			sendnum(user->u_iop, seq, TM_PANIC);
			return 0;
		}
		head = &locks[n];
		for (qp = head->q_forw; qp != head; qp = qp->q_forw) {
			lp = (struct shlock *) qp;
			if (lp->who == user) {
				remque(&lp->queue);
				free((s_char *)lp);
				break;
			}
		}
		mask &= ~bit(n);
	}
	return 1;
}

/*
 * Sends HUP signal to all processes who have locks
 * on the specified file.
 * Returns true if all locks were broken.
 */
int
breaklock(type)
	int	type;
{
	register struct qelem *qp;
	register struct qelem *head;
	register struct shlock *lp;

	if (type < 0 || type >= EF_MAX)
		return 0;
	head = &locks[type];
	while ((qp = head->q_forw) != head) {
		remque(qp);
		lp = (struct shlock *) qp;
		sendnum(lp->who->u_iop, 0, TM_ABORT);
		logerror("sent abort to %d, pid %d",
			lp->who->u_cnum, lp->who->u_pid);
		free((s_char *)lp);
	}
	return 1;
}

int
listlock(type)
	int	type;
{
	if (type < 0 || type >= EF_MAX)
		return 0;
	return QEMPTY(&locks[type]);
}

int
unlock_clean(up)
	struct	user *up;
{
	int	i;

	for (i=0; i<EF_MAX; i++) {
		(void) unlock(up, bit(i));
	}
}
