/* ----------------------------------------------------------------<Prolog>-
Name: smtecho.c
Title: SMT TCP/IP echo agent
Package: Libero/SMT Kernel 2.x
Written: 1996/06/15 iMatix SMT kernel team smt@imatix.com
Revised: 1997/09/08
Synopsis: Handles the TCP/IP echo protocol on port 7.
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.
Changes: 97/02/24 PH: echo_socket_data made into better example.
------------------------------------------------------------------</Prolog>-*/
#include "smtdefn.h" /* SMT definitions */
/*- Definitions -------------------------------------------------------------*/
#define AGENT_NAME SMT_ECHO /* Our public name */
typedef struct { /* Thread context block: */
event_t thread_type; /* Thread type indicator */
sock_t handle; /* Handle for i/o */
} TCB;
/*- Global variables used in this source file only --------------------------*/
static TCB
*tcb; /* Address thread context block */
static QID
operq, /* Operator console event queue */
logq, /* Logging agent event queue */
sockq; /* Socket agent event queue */
static byte
msg_body [LINE_MAX + 32]; /* Message sent to socket agent */
static int
msg_size; /* Size of formatted msg_body */
static DESCR /* Descriptor for exdr_writed */
msg = {sizeof (msg_body), msg_body};
#include "smtecho.d" /* Include dialog data */
/******************** INITIALISE AGENT - ENTRY POINT *********************/
/* ---------------------------------------------------------------------[<]-
Function: smtecho_init
Synopsis: Initialises the SMT echo agent. Returns 0 if initialised
okay, -1 if there was an error. The echo agent handles the TCP ECHO
protocol on port 7. It logs connections to the file smtecho.log. It
sends errors to the SMTOPER agent. If you set the ip_portbase before
calling this function, the echo port is shifted by that amount. A
typical value for ip_portbase is 5000: the echo agent will then handle
connections on port 5007.
---------------------------------------------------------------------[>]-*/
int
smtecho_init (void)
{
AGENT *agent; /* Handle for our agent */
THREAD *thread; /* Handle to various threads */
# include "smtecho.i" /* Include dialog interpreter */
/* Method name Event value Priority */
/* Shutdown event comes from Kernel */
method_declare (agent, "SHUTDOWN", shutdown_event, SMT_PRIORITY_MAX);
/* Reply events from socket agent */
method_declare (agent, "SOCK_INPUT_OK", ok_event, 0);
method_declare (agent, "SOCK_OUTPUT_OK", ok_event, 0);
method_declare (agent, "SOCK_READ_OK", read_ok_event, 0);
method_declare (agent, "SOCK_WRITE_OK", write_ok_event, 0);
method_declare (agent, "SOCK_CLOSED", closed_event, 0);
method_declare (agent, "SOCK_ERROR", error_event, 0);
method_declare (agent, "SOCK_TIMEOUT", error_event, 0);
/* Private methods used to pass initial thread events */
method_declare (agent, "_MASTER", master_event, 0);
method_declare (agent, "_CLIENT", client_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 socket agent is running, else start it up */
smtsock_init ();
if ((thread = thread_lookup (SMT_SOCKET, "")) != NULL)
sockq = thread-> queue-> qid;
else
return (-1);
/* Ensure that logging agent is running, and create new thread */
smtlog_init ();
if ((thread = thread_create (SMT_LOGGING, "")) != NULL)
logq = thread-> queue-> qid; /* Get logging queue id */
else
return (-1);
/* Create initial thread to manage master port */
if ((thread = thread_create (AGENT_NAME, "")) != NULL)
{
SEND (&thread-> queue-> qid, "_MASTER", "");
((TCB *) thread-> tcb)-> thread_type = master_event;
((TCB *) thread-> tcb)-> handle = 0;
}
else
return (-1);
/* Signal okay to caller that we initialised okay */
return (0);
}
/************************* INITIALISE THE THREAD *************************/
MODULE initialise_the_thread (THREAD *thread)
{
/* We don't set the_next_event because we expect an argument event */
/* to supply these. */
tcb = thread-> tcb; /* Point to thread's context */
}
/************************** OPEN AGENT LOG FILE *************************/
MODULE open_agent_log_file (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
sendfmt (&logq, "OPEN", "smtecho.log");
}
/*************************** OPEN MASTER SOCKET **************************/
MODULE open_master_socket (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
tcb-> handle = passive_TCP (SMT_ECHO_PORT, 5);
if (tcb-> handle == INVALID_SOCKET)
{
sendfmt (&operq, "ERROR",
"smtecho: could not open ECHO port %d/%s",
ip_portbase, SMT_ECHO_PORT);
sendfmt (&operq, "ERROR",
connect_errlist [connect_error ()]);
raise_exception (fatal_event);
}
else
sendfmt (&logq, "PUT", "I: opened echo port %s", SMT_ECHO_PORT);
}
/************************* WAIT FOR SOCKET INPUT *************************/
MODULE wait_for_socket_input (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
msg_size = exdr_writed (&msg, SMT_SOCK_INPUT, 0,
tcb-> handle, (qbyte) 0);
event_send (
&sockq, /* Send to socket agent */
&thread-> queue-> qid, /* Queue for reply */
"INPUT", /* Name of event to send */
msg_body, msg_size, /* Event body and size */
NULL, NULL, NULL, /* No response events */
0); /* No timeout */
}
/************************ ACCEPT CLIENT CONNECTION ***********************/
MODULE accept_client_connection (THREAD *thread)
{
sock_t
slave_socket; /* Connected socket */
THREAD
*child_thread; /* Handle to child threads */
tcb = thread-> tcb; /* Point to thread's context */
slave_socket = accept_socket (tcb-> handle);
if (slave_socket != INVALID_SOCKET)
{
child_thread = thread_create (AGENT_NAME, "");
if (child_thread)
{
SEND (&child_thread-> queue-> qid, "_CLIENT", "");
((TCB *) child_thread-> tcb)-> thread_type = client_event;
((TCB *) child_thread-> tcb)-> handle = slave_socket;
}
}
else
if (sockerrno != EAGAIN && sockerrno != EWOULDBLOCK)
{
sendfmt (&operq, "ERROR",
"smtecho: could not accept connection: %s", sockmsg ());
raise_exception (exception_event);
}
}
/********************** READ SOCKET DATA REPEATEDLY **********************/
MODULE read_socket_data_repeatedly (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
/* We ask the socket agent to read what it can from the socket, and
* to return that to us as a message; from 1 to LINE_MAX bytes. We
* want to work with chunks that will fit into the msg buffer.
* Whenever input arrives, we get an event, until the client closes
* the socket, there is an error, or we send a FLUSH to smtsock.
*/
msg_size = exdr_writed (&msg, SMT_SOCK_READ,
0, /* Timeout for request */
tcb-> handle, /* Socket to write to */
LINE_MAX, /* Amount of data to read */
1, /* but we'll accept 1 byte */
(qbyte) 0); /* Request tag */
event_send (
&sockq, /* Send to socket agent */
&thread-> queue-> qid, /* Queue for reply */
"READR", /* Name of event to send */
msg_body, msg_size, /* Event body and size */
NULL, NULL, NULL, /* No response events */
0); /* No timeout */
}
/**************************** ECHO SOCKET DATA ***************************/
MODULE echo_socket_data (THREAD *thread)
{
/* As a decent example, we first unpack the message from smtsock,
* then reformat it for sending back to the client. It could be
* simpler, since the input and output event bodies are exactly
* the same. But the code is more generally reusable like this.
*/
word
timeout, /* Timeout, in seconds, or zero */
read_size; /* Amount of data that was read */
sock_t
handle; /* Socket used in read */
char
*read_data = NULL; /* Buffer will get read data */
qbyte
tag; /* User-defined request tag */
tcb = thread-> tcb; /* Point to thread's context */
/* Get arguments from message. Note that we pass the address of
* 'read_data', which itself contains a null value. exdr_read() will
* allocate a block of memory and set read_data to point to this
* block. We must free the block when we no longer use it, by a
* call to mem_free(). We could also use a fixed block, if we're
* certain about the max. amount of data coming in. Then we must
* pass the address of a variable _that_points_ to read_data
* (not the address of read_data).
*/
exdr_read (thread-> event-> body, SMT_SOCK_READ_OK,
&timeout, &handle, &read_size, &read_data, &tag);
msg_size = exdr_writed (&msg, SMT_SOCK_WRITE,
0, /* Timeout for request */
tcb-> handle, /* Socket to write to */
read_size, /* Amount of data to write */
read_data, /* Address of data block */
0); /* Request tag */
event_send (
&sockq, /* Send to socket agent */
&thread-> queue-> qid, /* Queue for reply */
"WRITE", /* Name of event to send */
msg_body, msg_size, /* Event body and size */
NULL, NULL, NULL, /* No response events */
0); /* No timeout */
mem_free (read_data);
}
/************************** SIGNAL SOCKET ERROR **************************/
MODULE signal_socket_error (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
sendfmt (&operq, "ERROR",
"smtecho: error on socket: %s", thread-> event-> body);
}
/*************************** CHECK SOCKET TYPE ***************************/
MODULE check_socket_type (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
the_next_event = tcb-> thread_type;
}
/************************* CLOSE AGENT LOG FILE *************************/
MODULE close_agent_log_file (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
sendfmt (&logq, "CLOSE", "");
}
/************************ SHUTDOWN THE APPLICATION ***********************/
MODULE shutdown_the_application (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
smt_shutdown (); /* Halt the application */
}
/************************* TERMINATE THE THREAD **************************/
MODULE terminate_the_thread (THREAD *thread)
{
tcb = thread-> tcb; /* Point to thread's context */
if (tcb-> handle)
close_socket (tcb-> handle);
the_next_event = terminate_event;
}
Generated by Framer 1.0 © 1997 iMatix