smtsimu.c


/*  ----------------------------------------------------------------<Prolog>-
    Name:       smtsimu.c
    Title:      SMT network delay simulation agent.
    Package:    Libero/SMT Kernel 2.x

    Written:    1996/11/28  iMatix SMT kernel team smt@imatix.com
    Revised:    1997/09/08

    Synopsis:   Provides wrappers around the standard TCP/IP, UDP/IP socket
                functions in smtsock and sflsock.

    Copyright:  Copyright (c) 1991-2000 iMatix Corporation
    License:    This is free software; you can redistribute it and/or modify
                it under the terms of the SMT License Agreement as provided
                in the file LICENSE.TXT.  This software is distributed in
                the hope that it will be useful, but without any warranty.
 ------------------------------------------------------------------</Prolog>-*/

#include "smtdefn.h"                    /*  SMT definitions                  */


/*- Definitions -------------------------------------------------------------*/

#define AGENT_NAME      "smtsimu"       /*  Our public name                  */
#define SINGLE_THREADED TRUE            /*  Single-threaded agent            */


typedef struct {                        /*  Request descriptor               */
    dbyte   id;                         /*  Generated request ID             */
    sock_t  handle;                     /*  Which socket to write to?        */
    size_t  length;                     /*  Amount of data to write          */
    struct sockaddr_in                  /*  Socket end-point address         */
            sin;
} SOCKREQ;


/*- Global variables used in this source file only --------------------------*/

static Bool
    trace_flag = FALSE;                 /*  Trace socket activity?           */
static QID
    operq,                              /*  Operator console event queue     */
    timeq,                              /*  Timer event queue                */
    simuq;                              /*  Own agent event queue            */
static dbyte
    last_id;

#include "smtsimu.d"                    /*  Include dialog data              */


/********************   INITIALISE AGENT - ENTRY POINT   *********************/

/*  ---------------------------------------------------------------------[<]-
    Function: smtsimu_init

    Synopsis: Initialises the SMT network delay simulation agent.  Returns
    0 if okay, -1 if there was an error.  You do not need to call this
    function explicitly.  To use SMTSIMU, include "smtsimu.h" in your
    program after "smtlib.h".  This redefines the standard socket functions
    to go through SMTSIMU.  Sends all errors to the SMTOPER agent.
    ---------------------------------------------------------------------[>]-*/

int
smtsimu_init (void)
{
    AGENT   *agent;                     /*  Handle for our agent             */
    THREAD  *thread;                    /*  Handle to console thread         */
#   include "smtsimu.i"                 /*  Include dialog interpreter       */

    /*                      Method name     Event value      Priority        */
    /*  Shutdown event comes from Kernel                                     */
    method_declare (agent, "SHUTDOWN",      shutdown_event,  SMT_PRIORITY_MAX);

    /*  Public methods supported by this agent                               */
    method_declare (agent, "TIME_ALARM",    alarm_event,     0);
    method_declare (agent, "TIME_ERROR",    error_event,     0);

    /*  Ensure that operator console is running, else start it up            */
    smtoper_init ();
    if ((thread = thread_lookup (SMT_OPERATOR, "")) != NULL)
        operq = thread-> queue-> qid;
    else
        return (-1);

    /*  Ensure that timer agent is running, else start it up               */
    smttime_init ();
    if ((thread = thread_lookup (SMT_TIMER, "")) != NULL)
        timeq = thread-> queue-> qid;
    else
        return (-1);

    /*  Create initial thread                                                */
    if ((thread = thread_create (AGENT_NAME, "")) != NULL)
        simuq = thread-> queue-> qid;
    else
        return (-1);

    /*  Initialise static variables */
    last_id = 0;

    /*  Signal okay to caller that we initialised okay                       */
    return (0);
}


/*  ---------------------------------------------------------------------[<]-
    Function: smtsimu_sock_init

    Synopsis: Replaces the normal sock_init() function call.  Initialises
    the socket interface as usual, and then initialises the network delay
    simulation agent.  Returns SOCKET_ERROR if there was an error, else
    returns 0.
    ---------------------------------------------------------------------[>]-*/

int
smtsimu_sock_init (void)
{
    /*  Really start sockets                                                 */
    if (sock_init ())
        return ((int) SOCKET_ERROR);

    /*  Now initialise the SMTSIMU agent, and return 0 or SOCKET_ERROR       */
    if (smtsimu_init ())
        return ((int) SOCKET_ERROR);
    else
        return (0);
}

/*  ---------------------------------------------------------------------[<]-
    Function: smtsimu_smtsock_init

    Synopsis: Replaces the normal smtsock_init() function call.  Calls the
    SMTSOCK agent to initialise, as usual, then initialises the network
    delay simulation agent.  Returns -1 if there was an error, else 0.
    ---------------------------------------------------------------------[>]-*/

int
smtsimu_smtsock_init (void)
{
    /*  Initialise SMTSOCK agent                                             */
    if (smtsock_init ())
        return (-1);

    /*  Now initialise the SMTSIMU agent                                     */
    return (smtsimu_init ());
}


/*  ---------------------------------------------------------------------[<]-
    Function: smtsimu_smtsock_trace

    Synopsis: Replaces the normal smtsock_trace() function call. Calls the
    SMTSOCK agent to enable/disable tracing, then uses the same trace flag
    for the delay simulation.  Tracing is sent to the operator console.
    ---------------------------------------------------------------------[>]-*/

void
smtsimu_smtsock_trace (Bool trace_value)
{
    trace_flag = trace_value;
    smtsock_trace (trace_value);
}


/*  ---------------------------------------------------------------------[<]-
    Function: smtsimu_write_UDP

    Synopsis: Replaces the normal write_UDP() function call.  The data is
    sent, after a small but significant delay.  Returns the length of the
    data packet.  There is no guarantee that the packet will be sent and
    received successfully.  Returns SOCKET_ERROR if there was an error, such
    as insufficient memory.
    ---------------------------------------------------------------------[>]-*/

int
smtsimu_write_UDP (
    sock_t handle,                      /*  Socket handle                    */
    void  *buffer,                      /*  Buffer containing data           */
    size_t length,                      /*  Amount of data to write          */
    struct sockaddr_in *sin             /*  Address to send to, or null      */
)
{
    SOCKREQ *request;                   /*  New socket transfer request      */
    dbyte    req_size;                  /*  Size of transfer request         */
    byte    *msg_body;                  /*  Event to send via delay timer    */
    int      msg_size;                  /*  Size of event body               */
    qbyte    csecs = 100;               /*  Delay time in csecs              */

    /*  Allocate a new transfer request                                      */
    req_size = sizeof (SOCKREQ) + length;
    if ((request = mem_alloc (req_size)) == NULL)
      {
        sendfmt (&operq, "ERROR", "smtsimu: out of memory");
        return ((int) SOCKET_ERROR);
      }
    request-> id     = ++last_id;
    request-> handle = handle;
    request-> length = length;
    request-> sin    = *sin;
    memcpy ((byte *) request + sizeof (SOCKREQ), buffer, length);

    /*  Calculate size required for message body                             */
    msg_size = exdr_write (
        NULL,                           /*  No body - just get size          */
        SMT_TIME_ALARM,                 /*  Format event for smttime         */
        0, csecs,                       /*  Delay time                       */
        req_size, request);             /*  Request info                     */

    /*  Now allocate and format the buffer                                   */
    msg_body = mem_alloc  (msg_size);
    msg_size = exdr_write (
        msg_body,                       /*  Event body                       */
        SMT_TIME_ALARM,                 /*  Format event for smttime         */
        0, csecs,                       /*  Delay time                       */
        req_size, request);             /*  Request info                     */

    event_send (
        &timeq,                         /*  Send to specified queue          */
        &simuq,                         /*  No queue for reply               */
        "ALARM",                        /*  Name of event to send            */
        msg_body,                       /*  Event body contents              */
        msg_size,                       /*  Event body size                  */
        NULL, NULL, NULL,               /*  No response events               */
        0);                             /*  No timeout                       */

    mem_free (msg_body);
    mem_free (request);

    if (trace_flag)
        sendfmt (&operq, "INFO",
                 "smtsimu: packet %u delayed by %u csecs", last_id, csecs);

    return (length);
}


/*************************   INITIALISE THE THREAD   *************************/

MODULE initialise_the_thread (THREAD *thread)
{
    the_next_event = ok_event;
}


/****************************   MAKE SOCKET CALL   ***************************/

MODULE make_socket_call (THREAD *thread)
{
    SOCKREQ *request = NULL;            /*  Request received in event        */
    byte    *req_buffer;
    dbyte   req_size;
    int     rc;

    /*  Get request from event body                                          */
    exdr_read (thread-> event-> body, SMT_TIME_REPLY, &req_size, &request);
    req_buffer = (byte *) request + sizeof (SOCKREQ);

    /*  Now do the UDP write call                                            */
    rc = write_UDP (request-> handle,
                    req_buffer, request-> length,
                    &request-> sin);

    /*  Handle any errors                                                    */
    if (rc != (int) request-> length)
        sendfmt (&operq, "ERROR",
                 "smtsimu: could not write to socket: %s", sockmsg ());
    else
    if (trace_flag)
        sendfmt (&operq, "INFO",
                 "smtsimu: packet %u sent, size=%u",
                  request-> id, request-> length);

    mem_free (request);
}


/***********************   GET NEXT EVENT FROM QUEUE   ***********************/

MODULE get_next_event_from_queue (THREAD *thread)
{
    AGENT   *agent;                     /*  This agent                       */
    QUEUE   *queue;                     /*  Thread's event queue             */
    EVENT   *event;                     /*  Event information block          */
    METHOD  *method;                    /*  Method information block         */

    /*  Get next event off queue                                             */
    queue = thread-> queue;
    agent = queue-> agent;
    event = event_iterate (queue, NULL);
    if (event)
      {
        method = method_lookup (agent, event-> name);
        if (method == NULL)             /*  Not a method we accept           */
            event_reject (queue, event);
        else
          {
            if (thread-> event)         /*  If thread was sitting on an      */
              {                         /*    event, release it              */
                event_destroy (thread-> event);
                thread-> event = NULL;
              }
            /*  Get event off queue; it now belongs to the thread            */
            thread-> event = event_accept (queue, event);
            the_next_event = method-> event_number;
          }
      }
}


/*************************   TERMINATE THE THREAD   **************************/

MODULE terminate_the_thread (THREAD *thread)
{
    the_next_event = terminate_event;
}

Generated by Framer 1.0 © 1997 iMatix