/* lock.c     Handles the semaphore locking interface

   Copyright Feb. 1991, Peter Hardie VE5VA.


   The setbusy/clrbusy/getbusy/unbusy calls are used to protect the 'GM'
   and 'GU' commands so that only the sysop process running the 'GM'
   can be executing at the time that the 'GM' or 'GU' proceeds.
   Each process executes setbusy() when it is active and clrbusy when
   it is going to sleep again (in do_idle usually). A process that
   wants to do a 'GM' or 'GU' must first do a getbusy() which will either
   return a value of 1 to indicate failure or otherwise it is safe to
   proceed. When it has finished it must execute unbusy().
   This is equivalent to the classic case of the "multiple reader -
   single writer" problem.

   The begin_lock and end_lock calls are simple semaphores used to protect
   write calls into the files, mail.dat, bid.mb, and user.dat. They also
   protect the calls to generate the WP message and also the call to the
   stale() routine. In fmsg() they lock the message currently being
   forwarded.
   Ideally, the locks for each file could be different which would allow
   one user writing in the mail file to proceed even if another is causing
   a write into the bid file (for example). I may get around to creating a
   semaphore for each file once this code settles down a bit!

*/


#include <exec/types.h>
#include <exec/exec.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "lock.h"
extern int debug;
/*
struct Message *lock_msg;
struct MsgPort *sema_lock;
*/
struct Message *busy_msg,*mail_msg,*user_msg,*bid_msg;
struct user_sem *sema_busy;   /* Implementation of busy is a bit different
                                 than for lock. user_sem is a semaphore and
                                 a process counter.
                              */
struct file_sem *sema_mail = 0L,*sema_user = 0L, *sema_bid = 0L;
/* Set up both semaphores */
findlock()
{
   /* set up and find the semaphores for begin_lock and end_lock.
      This does not need to look for the locker process as that has
      already been done and we are guaranteed that it exists.
   */
/*
   lock_msg = AllocMem(sizeof(*lock_msg), MEMF_CLEAR | MEMF_PUBLIC);
   if(lock_msg == 0)return(1);
   lock_msg->mn_ReplyPort = CreatePort(0L,0L);
   sema_lock = FindPort(SEM_LOCK);
   if(sema_lock == 0) {
      stoplock();
      return(1);
   }
*/
   /* set up and find the semaphore for set/clr/get/unbusy */

   busy_msg = AllocMem(sizeof(*busy_msg), MEMF_CLEAR | MEMF_PUBLIC);
   if(busy_msg == 0)return(1);
   busy_msg->mn_ReplyPort = CreatePort(0L,0L);
   sema_busy = (struct user_sem *) FindPort(SEM_BUSY);
   if(sema_busy == 0) {
      stoplock();
      return(2);
   }
   /* Create the mail.dat semaphore */
   mail_msg = AllocMem(sizeof(*mail_msg), MEMF_CLEAR | MEMF_PUBLIC);
   if(mail_msg == 0)return(1);
   mail_msg->mn_ReplyPort = CreatePort(0L,0L);
   sema_mail = (struct file_sem *) FindPort(SEM_MAIL);
   if(sema_mail == 0) {
      stoplock();
      return(3);
   }
   /* Create the user.dat semaphore */
   user_msg = AllocMem(sizeof(*user_msg), MEMF_CLEAR | MEMF_PUBLIC);
   if(user_msg == 0)return(1);
   user_msg->mn_ReplyPort = CreatePort(0L,0L);
   sema_user = (struct file_sem *) FindPort(SEM_USER);
   if(sema_user == 0) {
      stoplock();
      return(4);
   }

   /* Create the bid.mb semaphore */
   bid_msg = AllocMem(sizeof(*bid_msg), MEMF_CLEAR | MEMF_PUBLIC);
   if(bid_msg == 0)return(1);
   bid_msg->mn_ReplyPort = CreatePort(0L,0L);
   sema_bid = (struct file_sem *) FindPort(SEM_BID);
   if(sema_bid == 0) {
      stoplock();
      return(5);
   }
   return(0);
}
stoplock()
{
/*
   if(lock_msg) {
      if(lock_msg->mn_ReplyPort)DeletePort(lock_msg->mn_ReplyPort);
      FreeMem(lock_msg,sizeof(*lock_msg));
   }
*/
   if(busy_msg) {
      if(busy_msg->mn_ReplyPort)DeletePort(busy_msg->mn_ReplyPort);
      FreeMem(busy_msg,sizeof(*busy_msg));
   }
   if(mail_msg) {
      if(mail_msg->mn_ReplyPort)DeletePort(mail_msg->mn_ReplyPort);
      FreeMem(mail_msg,sizeof(*mail_msg));
   }
   if(user_msg) {
      if(user_msg->mn_ReplyPort)DeletePort(user_msg->mn_ReplyPort);
      FreeMem(user_msg,sizeof(*user_msg));
   }
   if(bid_msg) {
      if(bid_msg->mn_ReplyPort)DeletePort(bid_msg->mn_ReplyPort);
      FreeMem(bid_msg,sizeof(*bid_msg));
   }
}

/* begin_lock and end_lock are not used in the multi-user CBBS any more
   so make them dummy routines
*/
begin_lock()
{
/*
   if(Procure((struct Semaphore *)sema_lock,lock_msg) == 0) {
      WaitPort(lock_msg->mn_ReplyPort);
      GetMsg(lock_msg->mn_ReplyPort);
   }
*/
}
end_lock()
{
/*
   Vacate((struct Semaphore *)sema_lock);
*/
}

setbusy()
{
   if(Procure((struct Semaphore *)sema_busy,busy_msg) == 0) {
      WaitPort(busy_msg->mn_ReplyPort);
      GetMsg(busy_msg->mn_ReplyPort);
   }
   sema_busy->count++;
   Vacate((struct Semaphore *)sema_busy);
}
clrbusy()
{
   if(Procure((struct Semaphore *)sema_busy,busy_msg) == 0) {
      WaitPort(busy_msg->mn_ReplyPort);
      GetMsg(busy_msg->mn_ReplyPort);
   }
   sema_busy->count--;
   Vacate((struct Semaphore *)sema_busy);
}
/* This returns 1 if there's more than one user active (itself).
   The setbusy/clrbusy/getbusy/unbusy are used only to lock out users
   while either a 'GM' or 'GU' are being done.
*/
getbusy()
{
   if(Procure((struct Semaphore *)sema_busy,busy_msg) == 0) {
      WaitPort(busy_msg->mn_ReplyPort);
      GetMsg(busy_msg->mn_ReplyPort);
   }
   /* NOTE here that if the count is one, then we return success and
      do NOT Vacate the semaphore. The process MUST call unbusy() at
      a later time to clear the semaphore
   */
   if(sema_busy->count == 1) {
      return(0);
   }

   /* The count is wrong so just Vacate the semaphore and return failure
   */
   Vacate((struct Semaphore *)sema_busy);
   return(1);
}

unbusy()
{
   Vacate((struct Semaphore *)sema_busy);
}
lock_bid()
{
   if(Procure((struct Semaphore *)sema_bid,bid_msg) == 0) {
      WaitPort(bid_msg->mn_ReplyPort);
      GetMsg(bid_msg->mn_ReplyPort);
   }
}
unlock_bid()
{
   Vacate((struct Semaphore *)sema_bid);
}
lock_user()
{
   if(Procure((struct Semaphore *)sema_user,user_msg) == 0) {
      WaitPort(user_msg->mn_ReplyPort);
      GetMsg(user_msg->mn_ReplyPort);
   }
}
unlock_user()
{
   Vacate((struct Semaphore *)sema_user);
}
/* Process must have the semaphore already to do these next two */
set_user(i)
long i;
{
   sema_user->filesize = i;
}
long get_user()
{
   return(sema_user->filesize);
}
lock_mail()
{
   if(Procure((struct Semaphore *)sema_mail,mail_msg) == 0) {
      WaitPort(mail_msg->mn_ReplyPort);
      GetMsg(mail_msg->mn_ReplyPort);
   }
}
unlock_mail()
{
   Vacate((struct Semaphore *)sema_mail);
}
set_mail(i)
long i;
{
   sema_mail->filesize = i;
}
long get_mail()
{
   return(sema_mail->filesize);
}
