/* RPCBinding.c 
    RPCBinding primitives
    Last modified by D. Swinehart, November 11, 1982 10:19 am
    L. Stewart December 27, 1982  3:01 PM, flush nested declarations
    L. Stewart January 1, 1983  4:15 PM, flush AllocZero
    L. Stewart January 1, 1983  4:15 PM, flush CStringToString
    L. Stewart January 18, 1983  1:46 PM, formatting
    L. Stewart March 6, 1983, GetFixed
    L. Stewart March 9, 1983  7:52 PM, flush wf
    L. Stewart April 18, 1983  11:34 AM, new ImportInterface
    L. Stewart May 4, 1983  12:08 PM, flush GetFixed
 */

#include <Env.h>
#include <queue.h>
#include <rpc.h>
#include <rpcinternal.h>
#include <rpclupine.h>
#include <rpcpkt.h>
#include <rpcbind.h>
#include <signal.h>

    /* From RPCSignals */
extern int CallFailed;
extern int ExportFailed;
extern int ImportFailed;

    /* From Utils */
extern struct ShortSTRING *CStringToString();
extern int DoubleInc();
extern int EquivalentStrings();
extern Move2();
extern int StringSize();
extern struct ShortSTRING *ShallString();
extern int InstanceToMachine();

    /* From Pup package */
extern struct PBI *GetPBI();
extern int Max();
extern int DoubleEq();
extern ReleasePBI();
extern GenerateNets(); /* initializes enumeration handle */
extern int NextNet(); /* next network in enumeration, 0 when done */

    /* From Time */
extern ReadCalendar();

    /* From OS */
extern MoveBlock();
extern Zero();

extern int *mySoc;  /* Going away */

int diagnoseBinding;
static struct ShortSTRING *binderRope;

/* InstanceInfo: TYPE = { ok, badName, allDown, noAddress }; */
  #define okI 0
  #define badNameI 1
  #define allDownI 2
  #define noAddressI 3

/* Version checking */
  #define okV  0
  #define badV  1
  #define wrongV  2
  #define myMDS 0

struct ExportInstance exportTable[5];
  /* Supports SMALL numbers of exported interfaces */
int used; /* How much of exportTable is full? */
    
static struct DispatcherID lastExportID[1];  /* ← System.GetGreenwichMeanTime(); */

/* UID on this machine */
static  struct  DispatcherID binderID[1]; /* = SUCC(RPCPkt.noDispatcher); */

static int binderHint; /* ExportHandle */
static int binderProc;

struct VersionRange *matchAllVersions;
static struct VersionRange mAVSpace;

static struct DispatcherID noDispatcher[1]; /* = [0, 0] */

struct BinderResult {
  struct VersionRange stubProt;
  struct VersionRange version;
  struct DispatcherDetails dispatcherDet;
  };
 #define lenBinderResult  (sizeof (struct BinderResult)/2)

struct BinderArgs { /* packet substructure */
  num request;
  num type; /* offset in pkt, 0 -> NIL  */
  num instance;
  word data[1];
/* offset in pkt, 0 -> NIL; followed by StringBody values for type, instance  */
  };
 #define lenBinderArgs  ((sizeof (struct BinderArgs)/2)-1)

/* ************* Export Interface ************* */

/* Representation of exports in the GV database: Each interface
    type is a group, such as "Alpine.pa".  Members of the group
    are the interface instances. Each interface instance is an
    individual, such as "MontBlanc.pa".  Connect-site for the
    individual contains the exporting host.  The syntax of
    instances' connect-sites is (all octal): net#host#mds.
    We accept only the string form of the connect site,
    in this implementation.
*/

int ExportInterface(interface, dispatcher, dArgs)
  struct InterfaceName *interface;
  Dispatcher *dispatcher;
  struct DArgs *dArgs; /* UNSPECIFIED */
  {
  int instance;
  struct ExportInstance *expInst;
  instance = used++;
  expInst = &exportTable[instance];
  DoubleInc(lastExportID, 1);
  Move2(&expInst->id, lastExportID);
  expInst->dispatcher = dispatcher;
  expInst->dispatcherArgs = dArgs;
  /* expInst->mds = myMDS */
  MoveBlock(&expInst->name, interface, lenInterfaceName);
  Block();
  return instance;
  };

/***** Service Request to Bind to Exported Interface *****/

/* Details of the exported dispatcher are kept only in the
    exporting machine.  The importer obtains the details
    via RPC call to dispatcher 1 on that machine.  "Bind"
    fields the call; "Binder" is dispatcher 1 on every machine.
    Returns noDispatcher for unbound instances.
 */

int /* returnLength */
Binder(pkt, callLength, localConversation, unusedArgs)
  struct PBI *pkt;
  int callLength;
  struct Conversation *localConversation;
  int unusedArgs;
  {
  struct BinderArgs *args;
  struct BinderResult *binderResult;
  struct Header *hdr;
  struct ShortSTRING *type, *instance;
  int i, anyBind;
  struct ExportInstance *inst;
  hdr = pkt->pup;
  binderResult = (struct BinderResult *) &hdr->callData;
  args = (struct BinderArgs *) binderResult;
  if (args->request != binderProc) return (lenBinderResult); /* ??? */
  type = PktString(args, args->type, callLength);
  instance = PktString(args, args->instance, callLength);
  anyBind = (instance == 0 || instance->length == 0 || instance->text[0] == '*');
  for (i = 1; i<used; ++i) {
    inst = &exportTable[i];
    if ((DoubleEq(&inst->id, noDispatcher) == 0) &&
       (type == 0 || EquivalentStrings(type, inst->name.type)) &&
       (anyBind != 0 || EquivalentStrings(instance, inst->name.instance))) {
      Move2(&binderResult->version, &inst->name.version);
      binderResult->dispatcherDet.mds = myMDS;
      Move2(&binderResult->dispatcherDet.dispatcherID, &inst->id);
      binderResult->dispatcherDet.dispatcherHint = swab(i);
      break;
      };
    };
  if (i==used) Zero(binderResult, lenBinderResult); /* not found */
/*
  Free(myZone, type);
  Free(myZone, instance);
 */
  Block();
  return (lenBinderResult);
  };

static struct ShortSTRING
*PktString(args, n, callLength)
  int args[], n, callLength;
  {
  /* in packets, string lengths are nums;
      return true ShortSTRING
   */
  struct ShortSTRING *ss, *res;
  if (((n=swab(n)) < lenBinderArgs) || ((n+lenShortSTRING) > callLength)) return (0);
  return (ShallString(0, &args[n], 2));
  };

/* ********** Import (bind to) Remote Interface *********** */

static union Machine candidateMachine;

static int IfBusy(s, code, seal)
  int s,code, seal;
  {
  return ((code == busy )? RETRY: CONTINUE);
  };

static int IfFailed(s, code, seal)
  int s,code, seal;
  {
  return ((candidateMachine.w == 0 && code != badCommunications) ? RETRY: REJECT);
  };

ImportInterface(serverMachine, interface, res)
  int serverMachine;
  struct InterfaceName *interface;
  struct ImportInstance *res;
  {
  struct Seal sl;
  struct Seal slf;
  union Machine host;
  struct BinderResult expDetails;
  int handle[2];
  if ((!serverMachine && (interface->instance != 0)) || (serverMachine && (interface->instance == 0))) SIGNAL(ImportFailed, badInstance);
  if (interface->type == 0) SIGNAL(ImportFailed, badType);
  candidateMachine.w = serverMachine;
  if (candidateMachine.w == 0) GenerateNets(handle);
  /* implied loop until ENABLE succeeds */
  ENABLE(ImportFailed, &IfFailed, &slf);
  if (ENABLE(CallFailed, &IfBusy, &sl))
    SIGNAL(ImportFailed, interfaceUnbound);
  else {
    switch (LocateInstance(serverMachine, &host, handle)) {
      case badNameI: {
        SIGNAL(ImportFailed, badInstance);
         break;
         };
      case allDownI: {
        SIGNAL(ImportFailed, badCommunications);
        break; 
         };
      default: SIGNAL(ERROR, 0);
      case okI: break;
      };
    host.w = RemoteBind(host.w, interface->type, interface->instance, &expDetails);
    };
  DISABLE(&sl);
  Block();
  if (DoubleEq(&expDetails.dispatcherDet.dispatcherID, noDispatcher))
      SIGNAL(ImportFailed, interfaceUnbound);
  switch (CheckVersions(&interface->version, &expDetails.version)) {
    case badV: SIGNAL(ImportFailed, badVersion); break;
    case wrongV: SIGNAL (ImportFailed, wrongVersion);
    default: break;
    };
  /* Don't allocate until danger of signalling has disappeared */
  Zero(res, lenImportInstance);
  MoveBlock(&res->dispatcherDet, &expDetails.dispatcherDet, lenDispatcherDetails);
  res->host.w = host.w;
  res->currentConversation = unencrypted;
  Block();
  };

static int /* InstanceInfo */
*LocateInstance(serverMachine, lvHost, handle)
  int serverMachine;
  union Machine *lvHost;
  int handle[/*2*/];
  {
  int net;
  if (serverMachine) {
    *lvHost = serverMachine;
    return okI;
    };
  if ((net = NextNet(handle))==0) return (allDownI);
  lvHost->b.net = net;
  lvHost->b.host = 0;
  Block();
  return (okI);
  };

static RBU(s,c,sl)
  int s,c;
  struct Seal1 *sl;
  {
  struct PBI *pbi;
  if (pbi = (struct PBI *) sl->data[0])  ReleasePBI(pbi);
  };

static int /* union Machine */
RemoteBind(host, type, instance, binderResult)
  /* (returns host with target machine number in it,
      if broadcast binding)
   */
  int /* union Machine */ host;
  struct ShortSTRING *type, *instance;
  struct BinderResult *binderResult;
  {
  struct ImportInstance binderInterface;
  int pktSize, argsUsed, *ptr;
  struct BinderArgs *args;
  struct PBI *pkt;
  struct Header *hdr;
  struct Seal1 sl;
  sl.data[0] = 0;
  binderInterface.host.w = host;
  binderInterface.dispatcherDet.mds = 0
  Move2(&binderInterface.dispatcherDet.dispatcherID, binderID);
  binderInterface.dispatcherDet.dispatcherHint = binderHint;
  pktSize = Max(lenBinderArgs+2*34 /*max arg string*/, lenBinderResult);
  sl.data[0] = (int) (pkt = GetPBI(mySoc));
  ENABLE(UNWIND, &RBU/*just below*/, &sl);
  hdr = pkt->pup;
  args = (struct BinderArgs *) &hdr->callData;
  StartCall(pkt, &binderInterface, unencrypted);
  argsUsed=0;
  Zero(args, lenBinderArgs);
  args->request = binderProc;
  if (type)  {
    args->type = swab(argsUsed+lenBinderArgs);
    ShallString(&args->data[argsUsed], type, 1);
    argsUsed+=StringSize(type);
    };
  if (instance)  {
    args->instance = swab(argsUsed+lenBinderArgs);
    ShallString(&args->data[argsUsed], instance, 1);
    argsUsed+=StringSize(instance);
    };
  argsUsed+=lenBinderArgs;
      /* now they tell me!! */
  if (argsUsed > pktSize) SIGNAL(ERROR, 0);
  Call(pkt, argsUsed, pktSize);
  MoveBlock(binderResult, args, lenBinderResult);
  host = hdr->srceHost.w
  ReleasePBI(pkt);
  Block();
  return (host);
  };

static int
CheckVersions(v1, v2)
  struct VersionRange *v1, *v2;
  {
  int m1, m2, v1f, v2f, v1l, v2l;
  m1 = DoubleEq(v1, matchAllVersions);
  m2 = DoubleEq(v2, matchAllVersions);
  v1f=swab(v1->first);
  v1l=swab(v1->last);
  v2f=swab(v2->first);
  v2l=swab(v2->last);
  if (m1==false && v1f > v1l) return (badV);
  if (m2==false && v2f > v2l) return (badV);
  if ( m1 || m2 || (v1f <= v2l && v1l >= v2f)) return (okV);
  return (wrongV);
  };

/* ******** Initialization ********  */

BindingInitialize()
  {
  struct ExportInstance *binderInst;
  diagnoseBinding = false;
  binderHint = 0;
  binderProc = 0;
  Zero(noDispatcher, 2);
  Move2(binderID, noDispatcher);
  DoubleInc(binderID, 1);
  matchAllVersions = &mAVSpace;
  matchAllVersions->first = swapped1;
  matchAllVersions->last = 0;
  binderInst = &exportTable[binderHint];
  Move2(&binderInst->id, binderID);
  binderInst->dispatcher = &Binder;
  binderInst->name.type = binderRope;
  used = 1;
  ReadCalendar(lastExportID);
  };

BindingRopes()
  {
  binderRope = CStringToString("Binder");
  };

BindingRestart()
  {
  };