Intial commit

This commit is contained in:
Mario Fetka
2024-05-27 16:13:40 +02:00
parent f8dc12b10a
commit d71d446104
2495 changed files with 539746 additions and 0 deletions

433
tcl-dp/api/dpApi.c Normal file
View File

@@ -0,0 +1,433 @@
/*
* dpAPI.c - contains a basic C API for accessing Tcl-DP RPC servers
* This is a standalone file and does not require DP or Tcl.
*
* Note that unlike the Tcl and DP sources, this file
* requires an ANSI C compiler.
*
* This API is extremely multithread unsafe.
*
*/
#include <stdio.h>
#include <string.h>
#include "dpApi.h"
/*------------------ DP RPC protocol defines -----------------*/
#define TOK_RPC 'e'
#define TOK_RDO 'd'
#define TOK_RET 'r'
#define TOK_ERR 'x'
/*
* The following strings are used to provide callback and/or error
* catching for RDOs. c = callback, e = onerror, ce = both
*/
/*
* sprintf template when both callback and onerror are specified.
* Params are cmd, onerror, callback
*/
static char *ceCmdTemplate =
"if [catch {%s} dp_rv] {\
dp_RDO $dp_rpcFile set errorInfo \"$errorInfo\n while remotely executing\n%s\"; \
dp_RDO $dp_rpcFile eval \"%s \\{$dp_rv\\}\"\
} else {\
dp_RDO $dp_rpcFile eval \"%s \\{$dp_rv\\}\"\
}";
/*
* sprintf template when just onerror is specified.
* Params are cmd, onerror
*/
static char *eCmdTemplate =
"if [catch {%s} dp_rv] {\
dp_RDO $dp_rpcFile set errorInfo \"$errorInfo\n while remotely executing\n%s\"; \
dp_RDO $dp_rpcFile eval \"%s \\{$dp_rv\\}\"\
}";
/*
* sprintf template when just callback is specified.
* Params are cmd, callback
*/
static char *cCmdTemplate =
"set dp_rv [%s]; dp_RDO $dp_rpcFile eval \"%s \\{$dp_rv\\}\"";
/*----------------------- Globals --------------------------*/
/*
* Holds the latest message received.
* bufPtr points to the free space
*/
#define DP_BUFFER_SIZE 8192
char retStr[DP_BUFFER_SIZE];
static char *bufPtr;
/*-------------------- Internal Routines -------------------*/
static int SendRPCMessage (DPServer server, char token,
int id, char *msgStr);
/*
*--------------------------------------------------------------
*
* Dp_RPC --
*
* Send an RPC message to the given server.
*
* If tv is NULL, there is no timeout, otherwise
* the RPC will timeout once tv's amount of time
* has passed.
*
* Results:
* errorPtr is non-zero if there was an error.
* Returns the Tcl result in a static string.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
char *
Dp_RPC(server, mesgStr, tv, errorPtr)
DPServer server; /* in: TCP socket to send on */
char *mesgStr; /* in: RPC string to send */
struct timeval *tv; /* in: Timeout value for select() */
int *errorPtr; /* out: POSIX errorcode */
{
int rc, len;
int amtRecv = -1, totalAmt = 0;
*errorPtr = 0;
/*
* Send the RPC to the remote server. Note the 12 is
* just a random ID since we don't use IDs.
*/
rc = SendRPCMessage(server, TOK_RPC, 12, mesgStr);
if (rc <= 0) {
*errorPtr = errno;
strcpy(retStr, "Error writing on socket");
return retStr;
}
/*
* Now we want to recv the reply.
* We spin in a loop waiting for the entire message
* to arrive. This gets a bit messy.
*/
bufPtr = retStr;
while (amtRecv < totalAmt) {
char lengthStr[7];
rc = Dp_WaitForSocket(server, tv);
if (rc <= 0) {
if (rc == 0) {
*errorPtr = -1;
strcpy(retStr, "RPC timed out");
return retStr;
} else {
*errorPtr = errno;
strcpy(retStr, "Select error");
return retStr;
}
}
amtRecv = recv(server, bufPtr, (retStr + DP_BUFFER_SIZE) - bufPtr, 0);
if ((amtRecv >= 6) && (totalAmt == 0)) {
/*
* Extract the length field from the incoming message
* so we know when we have recv'd the entire message.
*/
strncpy(lengthStr, retStr, 6);
lengthStr[6] = '\0';
totalAmt = atoi(lengthStr);
} else if (amtRecv == 0) {
/*
* EOF
*/
break;
}
bufPtr += amtRecv;
}
if (retStr[7] == 'x') {
*errorPtr = -1;
}
len = totalAmt - 16;
memcpy(retStr, &retStr[16], len);
retStr[len] = '\0';
return retStr;
}
/*
*--------------------------------------------------------------
*
* Dp_RDOSend --
*
* Send an RDO message to the given server.
* It is the caller's responsibilty to call
* Dp_RDORead if we need a return code or error.
* The size of the RDO is limited to BUFFER_SIZE - 1.
*
* Results:
* Amount sent.
*
* Side effects:
* Destroys previous value of retStr.
*
*--------------------------------------------------------------
*/
int
Dp_RDOSend(server, mesgStr, flags)
DPServer server;
char *mesgStr;
int flags;
{
char bigBuf[8096];
/*
* This is our fake callback/error Tcl script.
* This is necessary for compatibility with DP 4.0.
* Note since we will never evaluate the return
* script, this can be anything.
*/
char *dummy = "mperham";
int len, amt;
switch (flags) {
case DP_REPORT_ERROR:
sprintf(bigBuf, eCmdTemplate, mesgStr, mesgStr, dummy);
break;
case DP_RETURN_VALUE:
sprintf(bigBuf, cCmdTemplate, mesgStr, mesgStr, dummy);
break;
case DP_RETURN_VALUE | DP_REPORT_ERROR:
sprintf(bigBuf, ceCmdTemplate, mesgStr, mesgStr, dummy, dummy);
break;
default:
strcpy(bigBuf, mesgStr);
break;
}
amt = SendRPCMessage(server, TOK_RDO, 0, bigBuf);
/*
* Now we need to hack the amount sent because
* the caller knows nothing about the templates
* used above and thus will think an error
* has happened because amtSent != strlen(mesgStr)
*/
len = strlen(mesgStr);
if (amt >= len) {
return len;
} else {
return amt;
}
}
/*
*--------------------------------------------------------------
*
* Dp_RDORead --
*
* Recv's an RDO response from the given server.
*
* Results:
* Pointer to the string from the DP server or
* NULL with errorPtr set to a POSIX error.
*
* Side effects:
* Destroys previous value of retStr.
*
*--------------------------------------------------------------
*/
char *
Dp_RDORead(server, errorPtr)
DPServer server;
int *errorPtr;
{
int amount, len;
amount = recv(server, retStr, DP_BUFFER_SIZE, 0);
if (amount <= 0) {
if (amount == 0) {
/*
* EOF on socket
*/
*errorPtr = ECONNRESET;
return NULL;
}
*errorPtr = errno;
return NULL;
}
len = amount - 16;
memcpy(retStr, &retStr[16], len);
retStr[len] = '\0';
return retStr;
}
/*
*--------------------------------------------------------------
*
* Dp_WaitForServer --
*
* Waits until the given socket is readable.
*
* Results:
*
* 0 if we timeout before the socket is readable
* < 0 if there is an error.
* > 0 if the socket is now readable.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
int
Dp_WaitForServer(server, tv)
DPServer server;
struct timeval *tv;
{
fd_set readFD;
FD_ZERO(&readFD);
FD_SET(server, &readFD);
return select(1, &readFD, NULL, NULL, tv);
}
/*
*--------------------------------------------------------------
*
* Dp_ConnectToServer --
*
* Creates a TCP connection to a given IP addr and port.
*
* Results:
* returns the socket or -1 on error.
*
* Side effects:
* Creates a new socket. This function can block forever
* at the Dp_WaitForServer() or recv() calls. Be careful.
*
*--------------------------------------------------------------
*/
DPServer
Dp_ConnectToServer(int inetAddr, int port)
{
DPServer sock;
struct sockaddr_in myAddr;
struct sockaddr_in destAddr;
int rc;
char EOL;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
return -1;
}
myAddr.sin_addr.s_addr = INADDR_ANY;
myAddr.sin_family = AF_INET;
myAddr.sin_port = 0;
rc = bind(sock, (struct sockaddr *) &myAddr, sizeof(myAddr));
if (rc < 0) {
return -1;
}
destAddr.sin_addr.s_addr = htonl(inetAddr);
destAddr.sin_family = AF_INET;
destAddr.sin_port = htons((unsigned short)port);
rc = connect(sock, (struct sockaddr *) &destAddr,
sizeof(destAddr));
if (rc < 0) {
return -1;
}
/*
* The Tcl-DP RPC library sends "Connection accepted"
* upon successful linkup with a DP RPC server. We
* need to strip that off now so that future reads
* don't get confused.
*
* There is a slight problem here in that we don't know
* what the EOL indictator is. We'll read in 20, which
* is 19 + 1 character for EOL. If the last char is
* \r, we'll read in another byte since the server
* sent \r\n. This completely overlooks the fact
* that Macs send \r as their EOL, but since DP
* isn't suppose to run on Macs, this is an acceptable
* hack.
*/
Dp_WaitForSocket(sock, NULL);
rc = recv(sock, retStr, 20, 0);
while (rc < 20) {
rc += recv(sock, &retStr[rc], 20 - rc, 0);
}
EOL = retStr[19];
retStr[19] = '\0';
if (strcmp("Connection accepted", retStr)) {
return -1;
}
if (EOL == '\r') {
if (recv(sock, &EOL, 1, 0) != 1) {
return -1;
}
}
return sock;
}
/* ============================================================= *
* =================== Internal Routines ======================= *
* ============================================================= */
/*
*--------------------------------------------------------------
*
* SendRPCMessage --
*
* Send an RPC message on the given socket.
*
* Results:
* Number of bytes sent on the socket.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
static int
SendRPCMessage(server, token, id, msgStr)
DPServer server;
char token;
int id;
char *msgStr;
{
char *bufStr;
int result, totalLength;
totalLength = strlen(msgStr) + 16;
bufStr = malloc(totalLength + 1);
sprintf(bufStr, "%6d %c %6d %s", totalLength, token, id++%1000000,
msgStr);
result = send(server, bufStr, totalLength, 0);
free(bufStr);
if (result >= 16) {
result -= 16;
}
return result;
}

29
tcl-dp/api/dpApi.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef _DPAPI_H
#define _DPAPI_H
#ifdef _WIN32
#include <winsock.h>
typedef int DPServer;
#define ECONNRESET WSAECONNRESET
#else /* Unix */
typedef int DPServer;
#endif
/*
* RDO flags
*/
#define DP_REPORT_ERROR (1<<0)
#define DP_RETURN_VALUE (1<<1)
/*---------------------- Externally visible API ----------------------*/
char *Dp_RPC (DPServer server, char *mesgStr,
struct timeval *tv, int *errorPtr);
int Dp_RDOSend (DPServer server, char *mesgStr,
int flags);
char *Dp_RDORead (DPServer server, int *errorPtr);
int Dp_WaitForServer (DPServer server, struct timeval *tv);
DPServer Dp_ConnectToServer (int inetAddr, int port);
#endif

104
tcl-dp/api/dpExample.c Normal file
View File

@@ -0,0 +1,104 @@
/*
* api/dpExample.c
*
* This file provides an example of how to use the DP C API
* to perform RPCs and RDOs with a Tcl-DP server.
*/
#include <stdio.h>
#include "dpApi.h"
void main(int argc, char **argv)
{
char *rdoStr = "set s 5";
char *rpcStr = "set s";
int host = 0x7F000001; /* "127.0.0.1" == localhost */
int port = 7878;
DPServer server;
int rc, error;
char *rcStr;
struct timeval tv;
#ifdef _WIN32
/*
* Win32 requires an application to initialize
* the sockets library before using it. I suppose
* this has to do with multithreading...
*/
WORD ver = 0x0101;
WSADATA garbage;
if (WSAStartup(ver, &garbage)) {
return;
}
#endif
/*
* Set our RPC timeout to 5 seconds.
*/
tv.tv_sec = 5;
tv.tv_usec = 0;
/*
* Simple RDO - no options
*/
server = Dp_ConnectToServer(host, port);
if (server == -1) {
printf("Sockets suck.\n");
return;
}
rc = Dp_RDOSend(server, rdoStr, 0);
if (rc != (int)strlen(rdoStr)) {
printf("RDOs suck.\n");
closesocket(server);
return;
}
/*
* Simple RPC --
*
* Error must always be initialized to zero
* so that we can tell if there was an error
* on this call.
*/
error = 0;
rcStr = Dp_RPC(server, rpcStr, NULL, &error);
if (error != 0) {
printf("Error - Dp_RPC returned: %s\n", rcStr);
closesocket(server);
return;
}
printf("%s\n", rcStr);
/*
* Callback RDO
*/
rc = Dp_RDOSend(server, rdoStr, DP_RETURN_VALUE);
if (rc != (int)strlen(rdoStr)) {
printf("Sending RDOs sucks.\n");
closesocket(server);
return;
}
rc = Dp_WaitForServer(server, &tv);
if (rc <= 0) {
printf("Error waiting for reply\n");
closesocket(server);
return;
}
rcStr = Dp_RDORead(server, &error);
if (rcStr == NULL) {
printf("Problem with recv\n");
closesocket(server);
return;
}
puts(rcStr);
closesocket(server);
}

204
tcl-dp/api/makefile.win Normal file
View File

@@ -0,0 +1,204 @@
# Microsoft Developer Studio Generated NMAKE File, Format Version 4.20
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Console Application" 0x0103
!IF "$(CFG)" == ""
CFG=api - Win32 Debug
!MESSAGE No configuration specified. Defaulting to api - Win32 Debug.
!ENDIF
!IF "$(CFG)" != "api - Win32 Release" && "$(CFG)" != "api - Win32 Debug"
!MESSAGE Invalid configuration "$(CFG)" specified.
!MESSAGE You can specify a configuration when running NMAKE on this makefile
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "api.mak" CFG="api - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "api - Win32 Release" (based on "Win32 (x86) Console Application")
!MESSAGE "api - Win32 Debug" (based on "Win32 (x86) Console Application")
!MESSAGE
!ERROR An invalid configuration is specified.
!ENDIF
!IF "$(OS)" == "Windows_NT"
NULL=
!ELSE
NULL=nul
!ENDIF
################################################################################
# Begin Project
CPP=cl.exe
RSC=rc.exe
!IF "$(CFG)" == "api - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Target_Dir ""
OUTDIR=.\Release
INTDIR=.\Release
ALL : "$(OUTDIR)\api.exe"
CLEAN :
-@erase "$(INTDIR)\dpApi.obj"
-@erase "$(INTDIR)\dpExample.obj"
-@erase "$(OUTDIR)\api.exe"
"$(OUTDIR)" :
if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c
CPP_PROJ=/nologo /ML /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE"\
/Fp"$(INTDIR)/api.pch" /YX /Fo"$(INTDIR)/" /c
CPP_OBJS=.\Release/
CPP_SBRS=.\.
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
BSC32_FLAGS=/nologo /o"$(OUTDIR)/api.bsc"
BSC32_SBRS= \
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 wsock32.lib /nologo /subsystem:console /machine:I386
LINK32_FLAGS=wsock32.lib /nologo /subsystem:console /incremental:no\
/pdb:"$(OUTDIR)/api.pdb" /machine:I386 /out:"$(OUTDIR)/api.exe"
LINK32_OBJS= \
"$(INTDIR)\dpApi.obj" \
"$(INTDIR)\dpExample.obj"
"$(OUTDIR)\api.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
$(LINK32) @<<
$(LINK32_FLAGS) $(LINK32_OBJS)
<<
!ELSEIF "$(CFG)" == "api - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Target_Dir ""
OUTDIR=.\Debug
INTDIR=.\Debug
ALL : "$(OUTDIR)\api.exe"
CLEAN :
-@erase "$(INTDIR)\dpApi.obj"
-@erase "$(INTDIR)\dpExample.obj"
-@erase "$(INTDIR)\vc40.idb"
-@erase "$(INTDIR)\vc40.pdb"
-@erase "$(OUTDIR)\api.exe"
-@erase "$(OUTDIR)\api.ilk"
-@erase "$(OUTDIR)\api.pdb"
"$(OUTDIR)" :
if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c
CPP_PROJ=/nologo /MLd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE"\
/Fp"$(INTDIR)/api.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c
CPP_OBJS=.\Debug/
CPP_SBRS=.\.
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
BSC32_FLAGS=/nologo /o"$(OUTDIR)/api.bsc"
BSC32_SBRS= \
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386
# ADD LINK32 wsock32.lib /nologo /subsystem:console /debug /machine:I386
LINK32_FLAGS=wsock32.lib /nologo /subsystem:console /incremental:yes\
/pdb:"$(OUTDIR)/api.pdb" /debug /machine:I386 /out:"$(OUTDIR)/api.exe"
LINK32_OBJS= \
"$(INTDIR)\dpApi.obj" \
"$(INTDIR)\dpExample.obj"
"$(OUTDIR)\api.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
$(LINK32) @<<
$(LINK32_FLAGS) $(LINK32_OBJS)
<<
!ENDIF
.c{$(CPP_OBJS)}.obj:
$(CPP) $(CPP_PROJ) $<
.cpp{$(CPP_OBJS)}.obj:
$(CPP) $(CPP_PROJ) $<
.cxx{$(CPP_OBJS)}.obj:
$(CPP) $(CPP_PROJ) $<
.c{$(CPP_SBRS)}.sbr:
$(CPP) $(CPP_PROJ) $<
.cpp{$(CPP_SBRS)}.sbr:
$(CPP) $(CPP_PROJ) $<
.cxx{$(CPP_SBRS)}.sbr:
$(CPP) $(CPP_PROJ) $<
################################################################################
# Begin Target
# Name "api - Win32 Release"
# Name "api - Win32 Debug"
!IF "$(CFG)" == "api - Win32 Release"
!ELSEIF "$(CFG)" == "api - Win32 Debug"
!ENDIF
################################################################################
# Begin Source File
SOURCE=.\dpExample.c
DEP_CPP_DPEXA=\
".\dpApi.h"\
"$(INTDIR)\dpExample.obj" : $(SOURCE) $(DEP_CPP_DPEXA) "$(INTDIR)"
# End Source File
################################################################################
# Begin Source File
SOURCE=.\dpApi.c
DEP_CPP_DPAPI=\
".\dpApi.h"\
"$(INTDIR)\dpApi.obj" : $(SOURCE) $(DEP_CPP_DPAPI) "$(INTDIR)"
# End Source File
# End Target
# End Project
################################################################################

56
tcl-dp/api/readme.api Normal file
View File

@@ -0,0 +1,56 @@
Tcl-DP API for C
----------------
The file, dpApi.c, contains functions that can be used to contact
a Tcl-DP server and for sending RPCs and RDOs for evaluation by the
server.
Overview
--------
DpServer Dp_ConnectToServer(int inetAddr, int port);
Opens a connection to a Tcl-DP RPC server. Note that the remote
server must have been opened using dp_MakeRPCServer or this function
will fail. -1 will be returned if there was an error. Both arguments
should be given in host byte order.
char *Dp_RPC(DpServer s, char *msg, struct timeval *tv, int *errorPtr);
This is the function used to send an RPC to server s. msg is the
Tcl string to evaluate and tv is the timeout length (or NULL for infinite).
errorPtr will be non-zero when the function returns if there was an
error and the return value will be an error message. Otherwise the
return value is the return value of the remote evaluation of the RPC.
int Dp_RDOSend(DPServer s, char *rdoStr, int flags);
Sends an RDO, rdoStr, to a Tcl-DP RPC server s. flags is a combonation
of DP_REPORT_ERROR and DP_RETURN_VALUE. These act the same as the
-callback and -error flags in the DP API. Returns the amount of data
sent to the server (should be strlen(rdoStr)).
char *Dp_RDORead(DPServer s, int *errorPtr);
Receives a response from a previous RDO. This will block forever
if there is no response to an RDO (e.g. asking for an error return
but there is no error). Returns the Tcl return string or, if
errorPtr is non-zero, an error message.
int Dp_WaitForServer(DPServer s, struct timeval *tv);
Sleeps until there is a message from the server or tv expires.
If tv is NULL, we will block until data is received from the server.
Returns 0 if we timed out, < 0 if there was an error or > 0 if
the socket is now readable.
Bugs (oh no!)
-------------
Mail tcl-dp@cs.cornell.edu.