Browse Source
Remove all entries connected to IPC, an outdated (>30 years)
Remove all entries connected to IPC, an outdated (>30 years)
method to link ngspice to a now defunct controller (ATESSE).pre-master-46
25 changed files with 55 additions and 3393 deletions
-
1configure.ac
-
3src/Makefile.am
-
11src/frontend/inp.c
-
29src/frontend/inpcom.c
-
8src/frontend/resource.c
-
16src/frontend/runcoms.c
-
54src/spicelib/analysis/acan.c
-
38src/spicelib/analysis/cktdojob.c
-
23src/spicelib/analysis/dcop.c
-
77src/spicelib/analysis/dcpss.c
-
72src/spicelib/analysis/dctran.c
-
29src/spicelib/analysis/dctrcurv.c
-
42src/spicelib/analysis/noisean.c
-
57src/spicelib/analysis/span.c
-
2src/xspice/Makefile.am
-
250src/xspice/evt/evtdump.c
-
17src/xspice/ipc/Makefile.am
-
978src/xspice/ipc/ipc.c
-
309src/xspice/ipc/ipcaegis.c
-
756src/xspice/ipc/ipcsockets.c
-
77src/xspice/ipc/ipcstdio.c
-
520src/xspice/ipc/ipctiein.c
-
7visualc/sharedspice.vcxproj
-
7visualc/vngspice-fftw.vcxproj
-
7visualc/vngspice.vcxproj
@ -1,17 +0,0 @@ |
|||
## Process this file with automake to produce Makefile.in
|
|||
#
|
|||
# JW 3/9/01 - had a go and makeing an autoconf script.
|
|||
|
|||
noinst_LTLIBRARIES = libipcxsp.la |
|||
|
|||
libipcxsp_la_SOURCES = \
|
|||
ipcaegis.c \
|
|||
ipc.c \
|
|||
ipcsockets.c \
|
|||
ipcstdio.c \
|
|||
ipctiein.c |
|||
|
|||
AM_CPPFLAGS = @AM_CPPFLAGS@ -I$(top_srcdir)/src/include -I$(top_srcdir)/src/spicelib/devices |
|||
AM_CFLAGS = $(STATIC) |
|||
|
|||
MAINTAINERCLEANFILES = Makefile.in |
|||
@ -1,978 +0,0 @@ |
|||
/*============================================================================ |
|||
FILE IPC.c |
|||
|
|||
MEMBER OF process XSPICE |
|||
|
|||
Public Domain |
|||
|
|||
Georgia Tech Research Corporation |
|||
Atlanta, Georgia 30332 |
|||
PROJECT A-8503 |
|||
|
|||
AUTHORS |
|||
|
|||
9/12/91 Steve Tynor |
|||
|
|||
MODIFICATIONS |
|||
|
|||
6/13/92 Bill Kuhn Added some comments |
|||
|
|||
SUMMARY |
|||
|
|||
Provides compatibility for the new SPICE simulator to both the MSPICE user |
|||
interface and BCP (via ATESSE v.1 style AEGIS mailboxes) and the new ATESSE |
|||
v.2 Simulator Interface and BCP (via Bsd Sockets). |
|||
|
|||
The Interprocess Communications package provides functions |
|||
called to receive XSPICE decks from the ATESSE Simulator Interface |
|||
or Batch Control processes, and to return results to those |
|||
processes. Functions callable from the simulator packages include: |
|||
|
|||
ipc_initialize_server |
|||
ipc_terminate_server |
|||
ipc_get_line |
|||
ipc_send_line |
|||
ipc_send_data_prefix |
|||
ipc_send_data_suffix |
|||
ipc_send_dcop_prefix |
|||
ipc_send_dcop_suffix |
|||
ipc_send_evtdict_prefix |
|||
ipc_send_evtdict_suffix |
|||
ipc_send_evtdata_prefix |
|||
ipc_send_evtdata_suffix |
|||
ipc_send_errchk |
|||
ipc_send_end |
|||
ipc_send_boolean |
|||
ipc_send_int |
|||
ipc_send_double |
|||
ipc_send_complex |
|||
ipc_send_event |
|||
ipc_flush |
|||
|
|||
These functions communicate with a set of transport-level functions |
|||
that implement the interprocess communications under one of |
|||
the following protocol types determined by a compile-time option: |
|||
|
|||
BSD UNIX Sockets |
|||
HP/Apollo Mailboxes |
|||
|
|||
For each transport protocol, the following functions are written: |
|||
|
|||
ipc_transport_initialize_server |
|||
ipc_transport_get_line |
|||
ipc_transport_terminate_server |
|||
ipc_transport_send_line |
|||
|
|||
|
|||
|
|||
============================================================================*/ |
|||
|
|||
#include "ngspice/ngspice.h" |
|||
|
|||
#include <assert.h> |
|||
#include "ngspice/memory.h" /* NOTE: I think this is a Sys5ism (there is not man |
|||
* page for it under Bsd, but it's in /usr/include |
|||
* and it has a BSD copyright header. Go figure. |
|||
*/ |
|||
|
|||
#include "ngspice/ipc.h" |
|||
#include "ngspice/ipctiein.h" |
|||
#include "ngspice/ipcproto.h" |
|||
|
|||
|
|||
/* |
|||
* Conditional compilation sanity check: |
|||
*/ |
|||
#if !defined (IPC_AEGIS_MAILBOXES) && !defined (IPC_UNIX_SOCKETS)\ |
|||
&& !defined (IPC_DEBUG_VIA_STDIO) |
|||
" compiler error - must specify a transport mechanism"; |
|||
#endif |
|||
|
|||
/* |
|||
* static 'globals' |
|||
*/ |
|||
|
|||
/* typedef unsigned char Buffer_Char_t; */ |
|||
typedef char Buffer_Char_t; |
|||
|
|||
#define OUT_BUFFER_SIZE 1000 |
|||
#define MAX_NUM_RECORDS 200 |
|||
static int end_of_record_index [MAX_NUM_RECORDS]; |
|||
static int num_records; |
|||
static Buffer_Char_t out_buffer [OUT_BUFFER_SIZE]; |
|||
static int fill_count; |
|||
|
|||
static Ipc_Mode_t mode; |
|||
static Ipc_Protocol_t protocol; |
|||
static Ipc_Boolean_t end_of_deck_seen; |
|||
static int batch_fd; |
|||
|
|||
#define FMT_BUFFER_SIZE 80 |
|||
static char fmt_buffer [FMT_BUFFER_SIZE]; |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
Ipc_Boolean_t |
|||
kw_match (char *keyword, char *str) |
|||
/* |
|||
* returns IPC_TRUE if the first `strlen(keyword)' characters of `str' match |
|||
* the ones in `keyword' - case sensitive |
|||
*/ |
|||
{ |
|||
char *k = keyword; |
|||
char *s = str; |
|||
|
|||
/* |
|||
* quit if we run off the end of either string: |
|||
*/ |
|||
while (*s && *k) { |
|||
if (*s != *k) { |
|||
return IPC_FALSE; |
|||
} |
|||
s++; |
|||
k++; |
|||
} |
|||
/* |
|||
* if we get this far, it sould be because we ran off the end of the |
|||
* keyword else we didn't match: |
|||
*/ |
|||
return (*k == '\0'); |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_initialize_server |
|||
|
|||
This function creates the interprocess communication channel |
|||
server mailbox or socket. |
|||
*/ |
|||
|
|||
|
|||
Ipc_Status_t |
|||
ipc_initialize_server ( |
|||
char *server_name, /* Mailbox path or host/portnumber pair */ |
|||
Ipc_Mode_t m, /* Interactive or batch */ |
|||
Ipc_Protocol_t p ) /* Type of IPC protocol */ |
|||
/* |
|||
* For mailboxes, `server_name' would be the mailbox pathname; for |
|||
* sockets, this needs to be a host/portnumber pair. Maybe this should be |
|||
* automatically generated by the routine... |
|||
*/ |
|||
{ |
|||
Ipc_Status_t status; |
|||
char batch_filename [1025]; |
|||
|
|||
mode = m; |
|||
protocol = p; |
|||
end_of_deck_seen = IPC_FALSE; |
|||
|
|||
num_records = 0; |
|||
fill_count = 0; |
|||
|
|||
status = ipc_transport_initialize_server (server_name, m, p, |
|||
batch_filename); |
|||
|
|||
if (status != IPC_STATUS_OK) { |
|||
fprintf (stderr, "ERROR: IPC: error initializing server\n"); |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
|
|||
if (mode == IPC_MODE_BATCH) { |
|||
#ifdef IPC_AEGIS_MAILBOXES |
|||
strcat (batch_filename, ".log"); |
|||
#endif |
|||
batch_fd = open (batch_filename, O_WRONLY | O_CREAT, 0666); |
|||
if (batch_fd < 0) { |
|||
/* fprintf (stderr, "ERROR: IPC: Error opening batch output file: %s\n",batch_filename); */ |
|||
perror ("IPC"); |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
} |
|||
return status; |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_terminate_server |
|||
|
|||
This function deallocates the interprocess communication channel |
|||
mailbox or socket. |
|||
*/ |
|||
|
|||
|
|||
|
|||
Ipc_Status_t |
|||
ipc_terminate_server (void) |
|||
{ |
|||
return ipc_transport_terminate_server (); |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_get_line |
|||
|
|||
This function gets a SPICE deck input line from the interprocess |
|||
communication channel. Any special control commands in the deck |
|||
beginning with a ``>'' or ``#'' character are processed internally by |
|||
this function and not returned to SPICE. |
|||
*/ |
|||
|
|||
Ipc_Status_t |
|||
ipc_get_line ( |
|||
char *str, /* Text retrieved from IPC channel */ |
|||
int *len, /* Length of text string */ |
|||
Ipc_Wait_t wait ) /* Select blocking or non-blocking */ |
|||
/* |
|||
* Reads one SPICE line from the connection. Strips any control lines |
|||
* which cannot be interpretted by the simulator (e.g. >INQCON) and |
|||
* processes them. If such a line is read, it is processed and the next |
|||
* line is read. `ipc_get_line' does not return until a non-interceptable |
|||
* line is read or end of file. |
|||
* |
|||
* If `wait' is IPC_NO_WAIT and there is no data available on the |
|||
* connection, `ipc_get_line' returns IPC_STATUS_NO_DATA. If `wait' is |
|||
* IPC_WAIT, `ipc_get_line' will not return until there is data available |
|||
* or and end of file condition is reached or an error occurs. |
|||
* |
|||
* Intercepts and processes the following commands: |
|||
* #RETURNI, #MINTIME, #VTRANS, |
|||
* >PAUSE, >CONT, >STOP, >INQCON, >NETLIST, >ENDNET |
|||
* Other > records are silently ignored. |
|||
* |
|||
* Intercepts old-style .TEMP card generated by MSPICE |
|||
* |
|||
* Returns: |
|||
* IPC_STATUS_OK - for successful reads |
|||
* IPC_STATUS_NO_DATA - when NO_WAIT and no data available |
|||
* IPC_STATUS_END_OF_DECK - at end of deck (>ENDNET seen) |
|||
* IPC_STATUS_ERROR - otherwise |
|||
*/ |
|||
{ |
|||
Ipc_Status_t status; |
|||
Ipc_Boolean_t need_another = IPC_TRUE; |
|||
|
|||
do { |
|||
|
|||
status = ipc_transport_get_line (str, len, wait); |
|||
|
|||
switch (status) { |
|||
case IPC_STATUS_NO_DATA: |
|||
case IPC_STATUS_ERROR: |
|||
need_another = IPC_FALSE; |
|||
break; |
|||
case IPC_STATUS_END_OF_DECK: |
|||
assert (0); /* should never get this from the low-level get-line */ |
|||
status = IPC_STATUS_ERROR; |
|||
need_another = IPC_FALSE; |
|||
break; |
|||
case IPC_STATUS_OK: |
|||
/* |
|||
* Got a good line - check to see if it's one of the ones we need to |
|||
* intercept |
|||
*/ |
|||
if (str[0] == '>') { |
|||
if (kw_match (">STOP", str)) { |
|||
ipc_handle_stop(); |
|||
} else if (kw_match (">PAUSE", str)) { |
|||
/* assert (need_another); */ |
|||
/* |
|||
* once more around the loop to do a blocking wait for the >CONT |
|||
*/ |
|||
need_another = IPC_TRUE; |
|||
wait = IPC_WAIT; |
|||
} else if (kw_match (">INQCON", str)) { |
|||
ipc_send_line (">ABRTABL"); |
|||
ipc_send_line (">PAUSABL"); |
|||
ipc_send_line (">KEEPABL"); |
|||
status = ipc_flush (); |
|||
if (IPC_STATUS_OK != status) { |
|||
need_another = IPC_FALSE; |
|||
} |
|||
} else if (kw_match (">ENDNET", str)) { |
|||
end_of_deck_seen = IPC_TRUE; |
|||
need_another = IPC_FALSE; |
|||
status = IPC_STATUS_END_OF_DECK; |
|||
} else { |
|||
/* silently ignore */ |
|||
} |
|||
} else if (str[0] == '#') { |
|||
if (kw_match ("#RETURNI", str)) { |
|||
ipc_handle_returni (); |
|||
} else if (kw_match ("#MINTIME", str)) { |
|||
double d1/*,d2*/; |
|||
if (1 != sscanf (&str[8], "%lg", &d1)) { |
|||
status = IPC_STATUS_ERROR; |
|||
need_another = IPC_FALSE; |
|||
} else { |
|||
ipc_handle_mintime (d1); |
|||
} |
|||
} else if (kw_match ("#VTRANS", str)) { |
|||
char *tok1; |
|||
char *tok2; |
|||
char *tok3; |
|||
|
|||
tok1 = &str[8]; |
|||
for (tok2 = tok1; *tok2; tok2++) { |
|||
if (isspace_c(*tok2)) { |
|||
*tok2 = '\0'; |
|||
tok2++; |
|||
break; |
|||
} |
|||
} |
|||
for(tok3 = tok2; *tok3; tok3++) { |
|||
if(isspace_c(*tok3)) { |
|||
*tok3 = '\0'; |
|||
break; |
|||
} |
|||
} |
|||
ipc_handle_vtrans (tok1, tok2); |
|||
} else { |
|||
/* silently ignore */ |
|||
} |
|||
} else if (str[0] == '.') { |
|||
if (kw_match (".TEMP", str)) { |
|||
/* don't pass .TEMP card to caller */ |
|||
printf("Old-style .TEMP card found - ignored\n"); |
|||
} |
|||
else { |
|||
/* pass all other . cards to the caller */ |
|||
need_another = IPC_FALSE; |
|||
} |
|||
} else { |
|||
/* |
|||
* Not a '>' or '#' record - let the caller deal with it |
|||
*/ |
|||
need_another = IPC_FALSE; |
|||
} |
|||
break; |
|||
default: |
|||
/* |
|||
* some unknown status value! |
|||
*/ |
|||
assert (0); |
|||
status = IPC_STATUS_ERROR; |
|||
need_another = IPC_FALSE; |
|||
break; |
|||
} |
|||
} while (need_another); |
|||
|
|||
return status; |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_flush |
|||
|
|||
This function flushes the interprocess communication channel |
|||
buffer contents. |
|||
*/ |
|||
|
|||
Ipc_Status_t |
|||
ipc_flush (void) |
|||
/* |
|||
* Flush all buffered messages out the connection. |
|||
*/ |
|||
{ |
|||
Ipc_Status_t status; |
|||
int last = 0; |
|||
/*int bytes;*/ |
|||
int i; |
|||
|
|||
/* if batch mode */ |
|||
if (mode == IPC_MODE_BATCH) { |
|||
|
|||
assert (batch_fd >= 0); |
|||
|
|||
/* for number of records in buffer */ |
|||
for (i = 0; i < num_records; i++) { |
|||
|
|||
/* write the records to the .log file */ |
|||
if ((end_of_record_index [i] - last) != |
|||
write (batch_fd, &out_buffer[last], (size_t) (end_of_record_index [i] - last))) { |
|||
/* fprintf (stderr,"ERROR: IPC: Error writing to batch output file\n"); */ |
|||
perror ("IPC"); |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
|
|||
/* If the record is one of the batch simulation status messages, */ |
|||
/* send it over the ipc channel too */ |
|||
if( kw_match("#ERRCHK", &out_buffer[last]) || |
|||
kw_match(">ENDANAL", &out_buffer[last]) || |
|||
kw_match(">ABORTED", &out_buffer[last]) ) { |
|||
|
|||
status = ipc_transport_send_line (&out_buffer[last], |
|||
end_of_record_index [i] - last); |
|||
if (IPC_STATUS_OK != status) { |
|||
return status; |
|||
} |
|||
} |
|||
last = end_of_record_index [i]; |
|||
} |
|||
|
|||
/* else, must be interactive mode */ |
|||
} else { |
|||
/* send the full buffer over the ipc channel */ |
|||
status = ipc_transport_send_line (&out_buffer[0], |
|||
end_of_record_index [num_records - 1]); |
|||
if (IPC_STATUS_OK != status) { |
|||
return status; |
|||
} |
|||
} |
|||
|
|||
/* reset counts to zero and return */ |
|||
num_records = 0; |
|||
fill_count = 0; |
|||
return IPC_STATUS_OK; |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
Ipc_Status_t |
|||
ipc_send_line_binary ( |
|||
char *str, |
|||
int len ) |
|||
/* |
|||
* Same as `ipc_send_line' except does not expect the str to be null |
|||
* terminated. Sends exactly `len' characters. Use this for binary data |
|||
* strings that may have embedded nulls. |
|||
* |
|||
* Modified by wbk to append newlines for compatibility with |
|||
* ATESSE 1.0 |
|||
* |
|||
*/ |
|||
{ |
|||
int length = len + 1; |
|||
/*int diff;*/ |
|||
Ipc_Status_t status; |
|||
|
|||
/* |
|||
* If we can't add the whole str to the buffer, or if there are no more |
|||
* record indices free, flush the buffer: |
|||
*/ |
|||
if (((fill_count + length) >= OUT_BUFFER_SIZE) || |
|||
(num_records >= MAX_NUM_RECORDS)) { |
|||
status = ipc_flush (); |
|||
if (IPC_STATUS_OK != status) { |
|||
return status; |
|||
} |
|||
} |
|||
|
|||
/* |
|||
* make sure that the str will fit: |
|||
*/ |
|||
if (length + fill_count > OUT_BUFFER_SIZE) { |
|||
/* fprintf (stderr,"ERROR: IPC: String too long to fit in output buffer (> %d bytes) - truncated\n",OUT_BUFFER_SIZE); */ |
|||
length = OUT_BUFFER_SIZE - fill_count; |
|||
} |
|||
|
|||
/* |
|||
* finally, concatenate the str to the end of the buffer and add the newline: |
|||
*/ |
|||
memcpy (&out_buffer[fill_count], str, (size_t) len); |
|||
fill_count += len; |
|||
|
|||
out_buffer[fill_count] = '\n'; |
|||
fill_count++; |
|||
|
|||
end_of_record_index [num_records++] = fill_count; |
|||
|
|||
return IPC_STATUS_OK; |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_send_line |
|||
|
|||
This function sends a line of text over the interprocess |
|||
communication channel. |
|||
*/ |
|||
|
|||
|
|||
Ipc_Status_t |
|||
ipc_send_line (char *str ) /* The text to send */ |
|||
{ |
|||
int len; |
|||
int send_len; |
|||
|
|||
char *s; |
|||
|
|||
Ipc_Status_t status= IPC_STATUS_OK; |
|||
|
|||
|
|||
len = (int) strlen(str); |
|||
|
|||
/* if short string, send it immediately */ |
|||
if(len < 80) |
|||
status = ipc_send_line_binary (str, len); |
|||
else { |
|||
/* otherwise, we have to send it as multiple strings */ |
|||
/* because Mspice cannot handle things longer than 80 chars */ |
|||
s = str; |
|||
while(len > 0) { |
|||
if(len < 80) |
|||
send_len = len; |
|||
else |
|||
send_len = 79; |
|||
status = ipc_send_line_binary (str, send_len); |
|||
if(status != IPC_STATUS_OK) |
|||
break; |
|||
s += send_len; |
|||
len -= send_len; |
|||
} |
|||
} |
|||
|
|||
return(status); |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_send_data_prefix |
|||
|
|||
This function sends a ``>DATAB'' line over the interprocess |
|||
communication channel to signal that this is the beginning of a |
|||
results dump for the current analysis point. |
|||
*/ |
|||
|
|||
Ipc_Status_t |
|||
ipc_send_data_prefix (double time ) /* The analysis point for this data set */ |
|||
{ |
|||
char buffer[40]; |
|||
|
|||
sprintf (buffer, ">DATAB %.5E", time); |
|||
return ipc_send_line (buffer); |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_send_data_suffix |
|||
|
|||
This function sends a ``>ENDDATA'' line over the interprocess |
|||
communication channel to signal that this is the end of a results |
|||
dump from a particular analysis point. |
|||
*/ |
|||
|
|||
|
|||
Ipc_Status_t |
|||
ipc_send_data_suffix (void) |
|||
{ |
|||
Ipc_Status_t status; |
|||
|
|||
status = ipc_send_line (">ENDDATA"); |
|||
|
|||
if(status != IPC_STATUS_OK) |
|||
return(status); |
|||
|
|||
return(ipc_flush()); |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_send_dcop_prefix |
|||
|
|||
This function sends a ``>DCOPB'' line over the interprocess |
|||
communication channel to signal that this is the beginning of a |
|||
results dump from a DC operating point analysis. |
|||
*/ |
|||
|
|||
Ipc_Status_t |
|||
ipc_send_dcop_prefix (void) |
|||
{ |
|||
return ipc_send_line (">DCOPB"); |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_send_dcop_suffix |
|||
|
|||
This function sends a ``>ENDDATA'' line over the interprocess |
|||
communication channel to signal that this is the end of a results |
|||
dump from a particular analysis point. |
|||
*/ |
|||
|
|||
|
|||
Ipc_Status_t |
|||
ipc_send_dcop_suffix (void) |
|||
{ |
|||
Ipc_Status_t status; |
|||
|
|||
status = ipc_send_line (">ENDDCOP"); |
|||
|
|||
if(status != IPC_STATUS_OK) |
|||
return(status); |
|||
|
|||
return(ipc_flush()); |
|||
} |
|||
|
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_send_evtdict_prefix |
|||
|
|||
This function sends a ``>EVTDICT'' line over the interprocess |
|||
communication channel to signal that this is the beginning of an |
|||
event-driven node dictionary. |
|||
|
|||
The line is sent only if the IPC is configured |
|||
for UNIX sockets, indicating use with the V2 ATESSE SI process. |
|||
*/ |
|||
|
|||
Ipc_Status_t |
|||
ipc_send_evtdict_prefix (void) |
|||
{ |
|||
#ifdef IPC_AEGIS_MAILBOXES |
|||
return IPC_STATUS_OK; |
|||
#else |
|||
return ipc_send_line (">EVTDICT"); |
|||
#endif |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_send_evtdict_suffix |
|||
|
|||
This function sends a ``>ENDDICT'' line over the interprocess |
|||
communication channel to signal that this is the end of an |
|||
event-driven node dictionary. |
|||
|
|||
The line is sent only if the IPC is configured |
|||
for UNIX sockets, indicating use with the V2 ATESSE SI process. |
|||
*/ |
|||
|
|||
|
|||
Ipc_Status_t |
|||
ipc_send_evtdict_suffix (void) |
|||
{ |
|||
#ifdef IPC_AEGIS_MAILBOXES |
|||
return IPC_STATUS_OK; |
|||
#else |
|||
Ipc_Status_t status; |
|||
|
|||
status = ipc_send_line (">ENDDICT"); |
|||
|
|||
if(status != IPC_STATUS_OK) |
|||
return(status); |
|||
|
|||
return(ipc_flush()); |
|||
#endif |
|||
} |
|||
|
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_send_evtdata_prefix |
|||
|
|||
This function sends a ``>EVTDATA'' line over the interprocess |
|||
communication channel to signal that this is the beginning of an |
|||
event-driven node data block. |
|||
|
|||
The line is sent only if the IPC is configured |
|||
for UNIX sockets, indicating use with the V2 ATESSE SI process. |
|||
*/ |
|||
|
|||
Ipc_Status_t |
|||
ipc_send_evtdata_prefix (void) |
|||
{ |
|||
#ifdef IPC_AEGIS_MAILBOXES |
|||
return IPC_STATUS_OK; |
|||
#else |
|||
return ipc_send_line (">EVTDATA"); |
|||
#endif |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_send_evtdata_suffix |
|||
|
|||
This function sends a ``>ENDDATA'' line over the interprocess |
|||
communication channel to signal that this is the end of an |
|||
event-driven node data block. |
|||
|
|||
The line is sent only if the IPC is configured |
|||
for UNIX sockets, indicating use with the V2 ATESSE SI process. |
|||
*/ |
|||
|
|||
|
|||
Ipc_Status_t |
|||
ipc_send_evtdata_suffix (void) |
|||
{ |
|||
#ifdef IPC_AEGIS_MAILBOXES |
|||
return IPC_STATUS_OK; |
|||
#else |
|||
Ipc_Status_t status; |
|||
|
|||
status = ipc_send_line (">ENDDATA"); |
|||
|
|||
if(status != IPC_STATUS_OK) |
|||
return(status); |
|||
|
|||
return(ipc_flush()); |
|||
#endif |
|||
} |
|||
|
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_send_errchk |
|||
|
|||
This function sends a ``\ERRCHK [GO|NOGO]'' message over the |
|||
interprocess communication channel to signal that the initial |
|||
parsing of the input deck has been completed and to indicate |
|||
whether or not errors were detected. |
|||
*/ |
|||
|
|||
|
|||
Ipc_Status_t |
|||
ipc_send_errchk(void) |
|||
{ |
|||
char str[IPC_MAX_LINE_LEN+1]; |
|||
Ipc_Status_t status; |
|||
|
|||
if(g_ipc.errchk_sent) |
|||
return(IPC_STATUS_OK); |
|||
|
|||
if(g_ipc.syntax_error) |
|||
sprintf(str, "#ERRCHK NOGO"); |
|||
else |
|||
sprintf(str, "#ERRCHK GO"); |
|||
|
|||
g_ipc.errchk_sent = IPC_TRUE; |
|||
|
|||
status = ipc_send_line(str); |
|||
if(status != IPC_STATUS_OK) |
|||
return(status); |
|||
|
|||
return(ipc_flush()); |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_send_end |
|||
|
|||
This function sends either an ``>ENDANAL'' or an ``>ABORTED'' message |
|||
over the interprocess communication channel together with the |
|||
total CPU time used to indicate whether or not the simulation |
|||
completed normally. |
|||
*/ |
|||
|
|||
|
|||
Ipc_Status_t |
|||
ipc_send_end(void) |
|||
{ |
|||
char str[IPC_MAX_LINE_LEN+1]; |
|||
Ipc_Status_t status; |
|||
|
|||
if(g_ipc.syntax_error || g_ipc.run_error) |
|||
sprintf(str, ">ABORTED %.4f", g_ipc.cpu_time); |
|||
else |
|||
sprintf(str, ">ENDANAL %.4f", g_ipc.cpu_time); |
|||
|
|||
status = ipc_send_line(str); |
|||
if(status != IPC_STATUS_OK) |
|||
return(status); |
|||
|
|||
return(ipc_flush()); |
|||
} |
|||
|
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
int |
|||
stuff_binary_v1 ( |
|||
double d1, double d2, /* doubles to be stuffed */ |
|||
int n, /* how many of d1, d2 ( 1 <= n <= 2 ) */ |
|||
char *buf, /* buffer to stuff to */ |
|||
int pos ) /* index at which to stuff */ |
|||
{ |
|||
union { |
|||
float float_val[2]; |
|||
char ch[32]; |
|||
} trick; |
|||
int i, j; |
|||
|
|||
assert (protocol == IPC_PROTOCOL_V1); |
|||
assert (sizeof(float) == 4); |
|||
assert (sizeof(char) == 1); |
|||
assert ((n >= 1) && (n <= 2)); |
|||
|
|||
trick.float_val[0] = (float)d1; |
|||
if (n > 1) { |
|||
trick.float_val[1] = (float)d2; |
|||
} |
|||
for (i = 0, j = pos; i < n * (int) sizeof(float); j++, i++) |
|||
buf[j] = trick.ch[i]; |
|||
buf[0] = (char) ('A' + j - 1); |
|||
return j; |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
|
|||
/* |
|||
ipc_send_double |
|||
|
|||
This function sends a double data value over the interprocess |
|||
communication channel preceded by a character string that |
|||
identifies the simulation variable. |
|||
*/ |
|||
|
|||
Ipc_Status_t |
|||
ipc_send_double ( |
|||
char *tag, /* The node or instance */ |
|||
double value ) /* The data value to send */ |
|||
{ |
|||
int len = 0; |
|||
|
|||
switch (protocol) { |
|||
case IPC_PROTOCOL_V1: |
|||
strcpy (fmt_buffer, " "); /* save room for the length byte */ |
|||
strcat (fmt_buffer, tag); |
|||
strcat (fmt_buffer, " "); |
|||
|
|||
/* If talking to Mentor tools, must force upper case for Mspice 7.0 */ |
|||
strtoupper(fmt_buffer); |
|||
|
|||
len = stuff_binary_v1 (value, 0.0, 1, fmt_buffer, (int) strlen(fmt_buffer)); |
|||
break; |
|||
case IPC_PROTOCOL_V2: |
|||
break; |
|||
} |
|||
return ipc_send_line_binary (fmt_buffer, len); |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_send_complex |
|||
|
|||
This function sends a complex data value over the interprocess |
|||
communication channel preceded by a character string that |
|||
identifies the simulation variable. |
|||
*/ |
|||
|
|||
|
|||
Ipc_Status_t |
|||
ipc_send_complex ( |
|||
char *tag, /* The node or instance */ |
|||
Ipc_Complex_t value ) /* The data value to send */ |
|||
{ |
|||
int len=0; |
|||
|
|||
switch (protocol) { |
|||
case IPC_PROTOCOL_V1: |
|||
strcpy (fmt_buffer, " "); /* save room for the length byte */ |
|||
strcat (fmt_buffer, tag); |
|||
strcat (fmt_buffer, " "); |
|||
|
|||
/* If talking to Mentor tools, must force upper case for Mspice 7.0 */ |
|||
strtoupper(fmt_buffer); |
|||
|
|||
len = stuff_binary_v1 (value.real, value.imag, 2, fmt_buffer, |
|||
(int) strlen(fmt_buffer)); |
|||
break; |
|||
case IPC_PROTOCOL_V2: |
|||
break; |
|||
} |
|||
return ipc_send_line_binary (fmt_buffer, len); |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_send_event |
|||
|
|||
This function sends data from an event-driven node over the interprocess |
|||
communication channel. The data is sent only if the IPC is configured |
|||
for UNIX sockets, indicating use with the V2 ATESSE SI process. |
|||
*/ |
|||
|
|||
|
|||
Ipc_Status_t |
|||
ipc_send_event ( |
|||
int ipc_index, /* Index used in EVTDICT */ |
|||
double step, /* Analysis point or timestep (0.0 for DC) */ |
|||
double plot_val, /* The value for plotting purposes */ |
|||
char *print_val, /* The value for printing purposes */ |
|||
void *ipc_val, /* The binary representation of the node data */ |
|||
int len ) /* The length of the binary representation */ |
|||
{ |
|||
#ifdef IPC_AEGIS_MAILBOXES |
|||
return IPC_STATUS_OK; |
|||
#else |
|||
char buff[OUT_BUFFER_SIZE]; |
|||
int i; |
|||
int buff_len; |
|||
char *buff_ptr; |
|||
char *temp_ptr; |
|||
float fvalue; |
|||
|
|||
/* Report error if size of data is too big for IPC channel block size */ |
|||
if((len + (int) strlen(print_val) + 100) >= OUT_BUFFER_SIZE) { |
|||
printf("ERROR - Size of event-driven data too large for IPC channel\n"); |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
|
|||
/* Place the index into the buffer with a trailing space */ |
|||
sprintf(buff, "%d ", ipc_index); |
|||
|
|||
assert(sizeof(float) == 4); |
|||
assert(sizeof(int) == 4); |
|||
|
|||
/* Put the analysis step bytes in */ |
|||
buff_len = (int) strlen(buff); |
|||
buff_ptr = buff + buff_len; |
|||
fvalue = (float)step; |
|||
temp_ptr = (char *) &fvalue; |
|||
for(i = 0; i < 4; i++) { |
|||
*buff_ptr = temp_ptr[i]; |
|||
buff_ptr++; |
|||
buff_len++; |
|||
} |
|||
|
|||
/* Put the plot value in */ |
|||
fvalue = (float)plot_val; |
|||
temp_ptr = (char *) &fvalue; |
|||
for(i = 0; i < 4; i++) { |
|||
*buff_ptr = temp_ptr[i]; |
|||
buff_ptr++; |
|||
buff_len++; |
|||
} |
|||
|
|||
/* Put the length of the binary representation in */ |
|||
temp_ptr = (char *) &len; |
|||
for(i = 0; i < 4; i++) { |
|||
*buff_ptr = temp_ptr[i]; |
|||
buff_ptr++; |
|||
buff_len++; |
|||
} |
|||
|
|||
/* Put the binary representation bytes in last */ |
|||
temp_ptr = (char*) ipc_val; |
|||
for(i = 0; i < len; i++) |
|||
buff_ptr[i] = temp_ptr[i]; |
|||
buff_ptr += len; |
|||
buff_len += len; |
|||
|
|||
/* Put the print value in */ |
|||
strcpy(buff_ptr, print_val); |
|||
buff_ptr += strlen(print_val); |
|||
buff_len += (int) strlen(print_val); |
|||
|
|||
/* Send the data to the IPC channel */ |
|||
return ipc_send_line_binary(buff, buff_len); |
|||
|
|||
#endif |
|||
} |
|||
|
|||
|
|||
@ -1,309 +0,0 @@ |
|||
/*============================================================================ |
|||
FILE IPCaegis.c |
|||
|
|||
MEMBER OF process XSPICE |
|||
|
|||
Public Domain |
|||
|
|||
Georgia Tech Research Corporation |
|||
Atlanta, Georgia 30332 |
|||
PROJECT A-8503 |
|||
|
|||
AUTHORS |
|||
|
|||
9/12/91 Steve Tynor |
|||
|
|||
MODIFICATIONS |
|||
|
|||
<date> <person name> <nature of modifications> |
|||
|
|||
SUMMARY |
|||
|
|||
Provides compatibility for the new XSPICE simulator to both the MSPICE user |
|||
interface and BCP via ATESSE v.1 style AEGIS mailboxes. |
|||
|
|||
INTERFACES |
|||
|
|||
|
|||
REFERENCED FILES |
|||
|
|||
None. |
|||
|
|||
NON-STANDARD FEATURES |
|||
|
|||
None. |
|||
|
|||
============================================================================*/ |
|||
|
|||
#ifdef IPC_AEGIS_MAILBOXES |
|||
|
|||
#include <assert.h> |
|||
#include <apollo/base.h> |
|||
#include <apollo/mbx.h> |
|||
#include <apollo/error.h> |
|||
#include "ngspice/memory.h" |
|||
|
|||
#include "ngspice/ipc.h" |
|||
|
|||
|
|||
typedef unsigned char Buffer_char_t; |
|||
|
|||
static status_$t status; |
|||
typedef enum { |
|||
IPC_MBX_UNINITIALIZED, |
|||
IPC_MBX_INITIALIZED, |
|||
IPC_MBX_CONNECTED_TO_CLIENT, |
|||
} Ipc_Mbx_State_t; |
|||
|
|||
static void *mbx_handle; |
|||
static Ipc_Mbx_State_t mbx_state = IPC_MBX_UNINITIALIZED; |
|||
static mbx_$server_msg_t mbx_send_msg_buf; |
|||
static mbx_$server_msg_t mbx_recieve_msg_buf; |
|||
static mbx_$server_msg_t *mbx_ret_ptr; |
|||
static int mbx_ret_len; |
|||
static short mbx_chan; |
|||
|
|||
#include "ngspice/ipcproto.h" |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_transport_initialize_server |
|||
|
|||
This function creates an Aegis mailbox, and if successful, |
|||
calls ipc_get_line to wait for the first record sent which is |
|||
assumed to be the batch output filename. |
|||
*/ |
|||
|
|||
|
|||
|
|||
Ipc_Status_t ipc_transport_initialize_server (server_name, m, p, |
|||
batch_filename) |
|||
char *server_name; /* The mailbox pathname */ |
|||
Ipc_Mode_t m; /* Mode - interactive or batch */ |
|||
Ipc_Protocol_t p; /* Protocol type */ |
|||
char *batch_filename; /* Batch filename returned */ |
|||
{ |
|||
int len; |
|||
/* extern void *malloc(); */ |
|||
|
|||
assert (p == IPC_PROTOCOL_V1); |
|||
|
|||
mbx_$create_server (server_name, strlen (server_name), mbx_$serv_msg_max, |
|||
1, &mbx_handle, &status); |
|||
|
|||
if (status.all != status_$ok) { |
|||
fprintf (stderr, |
|||
"ERROR: IPC: Error creating mailbox server \"%s\"\n", |
|||
server_name); |
|||
error_$print (status); |
|||
mbx_state = IPC_MBX_UNINITIALIZED; |
|||
return IPC_STATUS_ERROR; |
|||
} else { |
|||
mbx_state = IPC_MBX_INITIALIZED; |
|||
/* |
|||
* First record is the name of the batch filename - whether we're in |
|||
* batch mode or not: |
|||
*/ |
|||
return ipc_get_line (batch_filename, &len, IPC_WAIT); |
|||
} |
|||
/* |
|||
* shouldn't get here |
|||
*/ |
|||
assert (0); |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
/*---------------------------------------------------------------------------*/ |
|||
Ipc_Status_t extract_msg (str, len) |
|||
char *str; |
|||
int *len; |
|||
{ |
|||
*len = mbx_ret_len - mbx_$serv_msg_hdr_len; |
|||
assert (*len >= 0); |
|||
|
|||
/* |
|||
* null terminate before copy: |
|||
*/ |
|||
mbx_ret_ptr->data [*len] = '\0'; |
|||
strcpy (str, mbx_ret_ptr->data); |
|||
|
|||
return IPC_STATUS_OK; |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_transport_get_line |
|||
|
|||
This function reads data sent by a client over the mailbox |
|||
channel. It also handles the initial opening of the |
|||
mailbox channel when requested by a client. |
|||
*/ |
|||
|
|||
|
|||
|
|||
Ipc_Status_t ipc_transport_get_line (str, len, wait) |
|||
char *str; /* The string text read from IPC channel */ |
|||
int *len; /* The length of str */ |
|||
Ipc_Wait_t wait; /* Blocking or non-blocking */ |
|||
{ |
|||
if (mbx_state == IPC_MBX_UNINITIALIZED) { |
|||
fprintf (stderr, |
|||
"ERROR: IPC: Attempted to read from non-initialized mailbox\n"); |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
|
|||
assert ((mbx_state == IPC_MBX_CONNECTED_TO_CLIENT) || |
|||
(mbx_state == IPC_MBX_INITIALIZED)); |
|||
|
|||
for (;;) { |
|||
if (wait == IPC_WAIT) { |
|||
mbx_$get_rec (mbx_handle, &mbx_recieve_msg_buf, mbx_$serv_msg_max, |
|||
&mbx_ret_ptr, &mbx_ret_len, &status); |
|||
} else { |
|||
mbx_$get_conditional (mbx_handle, &mbx_recieve_msg_buf, |
|||
mbx_$serv_msg_max, &mbx_ret_ptr, &mbx_ret_len, |
|||
&status); |
|||
if (status.all == mbx_$channel_empty) { |
|||
return IPC_STATUS_NO_DATA; |
|||
} |
|||
} |
|||
|
|||
if (status.all != status_$ok) { |
|||
fprintf (stderr, "ERROR: IPC: Error reading from mailbox\n"); |
|||
error_$print (status); |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
|
|||
switch (mbx_ret_ptr->mt) { |
|||
case mbx_$channel_open_mt: |
|||
if (mbx_state == IPC_MBX_CONNECTED_TO_CLIENT) { |
|||
/* |
|||
* we're already connected to a client... refuse the connection |
|||
*/ |
|||
mbx_send_msg_buf.mt = mbx_$reject_open_mt; |
|||
} else { |
|||
mbx_send_msg_buf.mt = mbx_$accept_open_mt; |
|||
mbx_state = IPC_MBX_CONNECTED_TO_CLIENT; |
|||
} |
|||
mbx_send_msg_buf.cnt = mbx_$serv_msg_hdr_len; |
|||
mbx_chan = mbx_ret_ptr->chan; |
|||
mbx_send_msg_buf.chan = mbx_chan; |
|||
|
|||
mbx_$put_rec (mbx_handle, &mbx_send_msg_buf, mbx_$serv_msg_hdr_len, |
|||
&status); |
|||
|
|||
if (status.all != status_$ok) { |
|||
fprintf (stderr, "ERROR: IPC: Error writing to mailbox\n"); |
|||
error_$print (status); |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
|
|||
/* |
|||
* check to see if there was a message buried in the open request: |
|||
*/ |
|||
if (mbx_ret_len > mbx_$serv_msg_hdr_len) { |
|||
return extract_msg (str, len); |
|||
} |
|||
break; |
|||
case mbx_$eof_mt: |
|||
mbx_chan = mbx_ret_ptr->chan; |
|||
mbx_$deallocate(mbx_handle, mbx_chan, &status); |
|||
|
|||
if (status.all != status_$ok) { |
|||
fprintf (stderr, "ERROR: IPC: Error deallocating mailbox\n"); |
|||
error_$print (status); |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
|
|||
mbx_state = IPC_MBX_INITIALIZED; |
|||
return IPC_STATUS_EOF; |
|||
break; |
|||
case mbx_$data_mt: |
|||
assert (mbx_state == IPC_MBX_CONNECTED_TO_CLIENT); |
|||
return extract_msg (str, len); |
|||
break; |
|||
case mbx_$data_partial_mt: |
|||
fprintf (stderr, "ERROR: IPC: Recieved partial data message - ignored\n"); |
|||
break; |
|||
default: |
|||
fprintf (stderr, "ERROR: IPC: Bad message type (0x%x) recieved\n", |
|||
mbx_ret_ptr->mt); |
|||
} |
|||
} |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_transport_terminate_server |
|||
|
|||
This function calls ipc\_transport\_get\_line until it |
|||
receives an EOF from the client, which concludes the |
|||
communication. |
|||
*/ |
|||
|
|||
|
|||
Ipc_Status_t ipc_transport_terminate_server () |
|||
{ |
|||
char buffer[300]; |
|||
int len; |
|||
Ipc_Status_t status; |
|||
|
|||
do { |
|||
status = ipc_transport_get_line (buffer, &len, IPC_WAIT); |
|||
} while ((status != IPC_STATUS_ERROR) && |
|||
(status != IPC_STATUS_EOF)); |
|||
return status; |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
|
|||
/* |
|||
ipc_transport_send_line |
|||
|
|||
This function sends a message to the current client through |
|||
the mailbox channel. |
|||
*/ |
|||
|
|||
|
|||
Ipc_Status_t ipc_transport_send_line (str, len) |
|||
char *str; /* The bytes to send */ |
|||
int len; /* The number of bytes from str to send */ |
|||
{ |
|||
long cnt; |
|||
|
|||
if (mbx_state != IPC_MBX_CONNECTED_TO_CLIENT) { |
|||
fprintf (stderr, |
|||
"ERROR: IPC: Attempted to write to non-open mailbox\n"); |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
|
|||
mbx_send_msg_buf.mt = mbx_$data_mt; |
|||
if (mbx_$serv_msg_hdr_len + len > mbx_$serv_msg_max) { |
|||
fprintf (stderr, |
|||
"ERROR: IPC: send_line message too long - truncating\n"); |
|||
len = mbx_$serv_msg_max - mbx_$serv_msg_hdr_len; |
|||
} |
|||
|
|||
mbx_send_msg_buf.cnt = mbx_$serv_msg_hdr_len + len; |
|||
mbx_send_msg_buf.chan = mbx_chan; |
|||
memcpy (mbx_send_msg_buf.data, str, len); |
|||
|
|||
cnt = mbx_send_msg_buf.cnt; |
|||
mbx_$put_rec (mbx_handle, &mbx_send_msg_buf, cnt, &status); |
|||
|
|||
if (status.all != status_$ok) { |
|||
fprintf (stderr, "ERROR: IPC: Error writing to mailbox\n"); |
|||
error_$print (status); |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
return IPC_STATUS_OK; |
|||
} |
|||
|
|||
#else |
|||
|
|||
int intDummy1; |
|||
|
|||
#endif /* IPC_AEGIS_MAILBOXES */ |
|||
@ -1,756 +0,0 @@ |
|||
/*============================================================================= |
|||
|
|||
FILE IPCsockets.c |
|||
|
|||
Public Domain |
|||
|
|||
Georgia Tech Research Corporation |
|||
Atlanta, Georgia 30332 |
|||
PROJECT A-8503 |
|||
|
|||
AUTHOR |
|||
Stefan Roth July 1991 |
|||
|
|||
MODIFICATIONS |
|||
none |
|||
|
|||
SUMMARY |
|||
Generic Interprocess Communication module |
|||
Provides compatibility for the new SPICE simulator to both the MSPICE user |
|||
interface and BCP (via ATESSE v.1 style AEGIS mailboxes) and the new ATESSE |
|||
v.2 Simulator Interface and BCP (via BSD Sockets). This file contains the |
|||
BSD sockets version. |
|||
The Simulator is the server, while the SI and BCP will be the clients. |
|||
|
|||
|
|||
INTERFACES |
|||
|
|||
FILE ROUTINE CALLED |
|||
|
|||
IPC.c ipc_get_line(); |
|||
|
|||
|
|||
REFERENCED FILES |
|||
|
|||
Outputs to stderr. |
|||
|
|||
|
|||
=============================================================================*/ |
|||
|
|||
|
|||
/*============================================================================= |
|||
|
|||
DESCRIPTION OF FUNCTIONALITY: |
|||
|
|||
Outline of Initialize_Server function: |
|||
create socket; |
|||
bind name to socket; |
|||
getsockname; |
|||
listen; |
|||
sock_state = IPC_SOCK_INITIALIZED; |
|||
return ipc_get_line (); |
|||
|
|||
|
|||
Format of a message line: |
|||
bytes description |
|||
----- ------------------- |
|||
0 recognition character for beginning of line; value is BOL_CHAR. |
|||
1-4 message length (not including bytes 0-4); 32 bits in htonl |
|||
format; |
|||
if value = -1, then EOF and socket should be closed. |
|||
5-N+5 message body of length specified in bytes 1-4. |
|||
|
|||
The bytes before the message body are the message header. The header |
|||
length is specified as SOCK_MSG_HDR_LEN bytes. |
|||
|
|||
|
|||
Outline of Get_Line function: |
|||
read 5 characters; |
|||
verify that first char is BOL_CHAR; |
|||
interpret message length (N) from bytes 1-4; |
|||
do error checking on message header bytes; |
|||
read N characters as message body; |
|||
do error checking on message body read; |
|||
|
|||
|
|||
Outline of Send_Line function: |
|||
write BOL_CHAR; |
|||
write 4-byte message body length |
|||
write message body (N bytes) |
|||
do error checking after each write operation |
|||
|
|||
|
|||
Outline of Terminate_Server function: |
|||
Continue to read lines (with ipc_transport_get_line) and ignore |
|||
them until socket EOF is reached; |
|||
Close the socket. |
|||
|
|||
|
|||
=============================================================================*/ |
|||
|
|||
#include "ngspice/ngspice.h" |
|||
|
|||
#ifdef IPC_UNIX_SOCKETS |
|||
|
|||
/*=== INCLUDE FILES ===*/ |
|||
|
|||
#include <assert.h> |
|||
#include <errno.h> |
|||
|
|||
#include "ngspice/ipc.h" |
|||
#include "ngspice/ipctiein.h" |
|||
|
|||
|
|||
/*=== TYPE DEFINITIONS ===*/ |
|||
|
|||
typedef enum { |
|||
IPC_SOCK_UNINITIALIZED, |
|||
IPC_SOCK_INITIALIZED, |
|||
IPC_SOCK_CONNECTED_TO_CLIENT |
|||
} Ipc_Sock_State_t; |
|||
|
|||
|
|||
/*=== LOCAL VARIABLES ===*/ |
|||
|
|||
static int sock_desc; /* socket descriptor */ |
|||
static int msg_stream; /* socket stream */ |
|||
static Ipc_Sock_State_t sock_state = IPC_SOCK_UNINITIALIZED; |
|||
|
|||
|
|||
/*=== INCLUDE FILES ===*/ |
|||
|
|||
#include "ngspice/ipcproto.h" |
|||
|
|||
/*============================================================================= |
|||
|
|||
FUNCTION ipc_transport_initialize_server |
|||
|
|||
AUTHORS |
|||
|
|||
July 1991 Stefan Roth |
|||
|
|||
MODIFICATIONS |
|||
|
|||
NONE |
|||
|
|||
SUMMARY |
|||
|
|||
Creates and opens the BSD socket of the server. Listens for requests |
|||
by a client and then reads the first line message. |
|||
|
|||
INTERFACES |
|||
|
|||
Called by: (IPC.c) ipc_initialize_server(); |
|||
|
|||
RETURNED VALUE |
|||
|
|||
Ipc_Status_t - returns status of the socket connection. |
|||
|
|||
GLOBAL VARIABLES |
|||
|
|||
NONE |
|||
|
|||
NON-STANDARD FEATURES |
|||
|
|||
NONE |
|||
|
|||
=============================================================================*/ |
|||
|
|||
Ipc_Status_t |
|||
ipc_transport_initialize_server ( |
|||
char *server_name, /* not used */ |
|||
Ipc_Mode_t mode, /* not used */ |
|||
Ipc_Protocol_t protocol, /* IN - only used in assert */ |
|||
char *batch_filename ) /* OUT - returns a value */ |
|||
/* Note that unused parameters are required to maintain compatibility */ |
|||
/* with version 1 (mailboxes) functions of the same names. */ |
|||
{ |
|||
struct sockaddr_in server; /* Server specifications for socket*/ |
|||
socklen_t server_length; /* Size of server structure */ |
|||
int port_num; /* Port number converted from server_name */ |
|||
|
|||
NG_IGNORE(mode); |
|||
NG_IGNORE(protocol); |
|||
|
|||
/* assert (protocol == IPC_PROTOCOL_V2); */ /* allow v1 protocol - wbk */ |
|||
assert (sock_state == IPC_SOCK_UNINITIALIZED); |
|||
|
|||
/* convert server_name (from atesse_xspice invocation line) to a port */ |
|||
/* number */ |
|||
port_num = atoi(server_name); |
|||
if((port_num > 0) && (port_num < 1024)) { |
|||
/* Reserved port number */ |
|||
perror ("ERROR: IPC Port numbers below 1024 are reserved"); |
|||
sock_state = IPC_SOCK_UNINITIALIZED; |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
|
|||
|
|||
sock_desc = socket (AF_INET, SOCK_STREAM, 0); |
|||
|
|||
if (sock_desc < 0) { |
|||
/* Unsuccessful socket opening */ |
|||
perror ("ERROR: IPC Creating socket"); |
|||
sock_state = IPC_SOCK_UNINITIALIZED; |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
|
|||
/* Socket opened successfully */ |
|||
|
|||
server.sin_family = AF_INET; |
|||
server.sin_addr.s_addr = INADDR_ANY; |
|||
server.sin_port = SOCKET_PORT; |
|||
|
|||
server_length = sizeof (server); |
|||
if (bind (sock_desc, (struct sockaddr *)&server, server_length) |
|||
< 0) { |
|||
fprintf (stderr, "ERROR: IPC: Bind unsuccessful\n"); |
|||
perror ("ERROR: IPC"); |
|||
sock_state = IPC_SOCK_UNINITIALIZED; |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
|
|||
if (getsockname (sock_desc, (struct sockaddr *)&server, &server_length) |
|||
< 0) { |
|||
fprintf (stderr, "ERROR: IPC: getting socket name\n"); |
|||
perror ("ERROR: IPC"); |
|||
sock_state = IPC_SOCK_UNINITIALIZED; |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
|
|||
fprintf (stderr, "Socket port %d.\n", ntohs(server.sin_port)); |
|||
|
|||
listen (sock_desc, 5); |
|||
|
|||
sock_state = IPC_SOCK_INITIALIZED; |
|||
|
|||
/* Socket ok to use now */ |
|||
|
|||
/* |
|||
* First record is the name of the batch filename if we're in batch mode. |
|||
*/ |
|||
|
|||
if(g_ipc.mode == IPC_MODE_BATCH) { |
|||
int len; |
|||
return ipc_get_line (batch_filename, &len, IPC_WAIT); |
|||
} |
|||
|
|||
/* Return success */ |
|||
return IPC_STATUS_OK; |
|||
|
|||
} /* end ipc_transport_initialize_server */ |
|||
|
|||
|
|||
|
|||
/*============================================================================= |
|||
|
|||
FUNCTION bytes_to_integer |
|||
|
|||
AUTHORS |
|||
|
|||
July 1991 Stefan Roth |
|||
|
|||
MODIFICATIONS |
|||
|
|||
NONE |
|||
|
|||
SUMMARY |
|||
|
|||
Convert four bytes at START in the string STR |
|||
to a 32-bit unsigned integer. The string is assumed |
|||
to be in network byte order and the returned value |
|||
is converted to host byte order (with ntohl). |
|||
|
|||
INTERFACES |
|||
|
|||
Local to this file. |
|||
Called by: ipc_transport_get_line(); |
|||
|
|||
RETURNED VALUE |
|||
|
|||
u_long - unsigned 32 bit integer |
|||
|
|||
GLOBAL VARIABLES |
|||
|
|||
NONE |
|||
|
|||
NON-STANDARD FEATURES |
|||
|
|||
NONE |
|||
|
|||
=============================================================================*/ |
|||
|
|||
/* FIXME, |
|||
* this is seriously broken, |
|||
* once it was probably based upon htonl(), |
|||
* yet with broken types |
|||
* then the game has changed and strtoul() was used |
|||
* with a ascii representation of the length |
|||
* (probably as a hacky workaround, because it proved unreliable) |
|||
* but the buffer is not terminated properly |
|||
* Fix this when needed, currently this functionality looks like |
|||
* an unused ancient artefact |
|||
* Fix it with regard to ipc_transport_get_line() and ipc_transport_send_line() |
|||
* and in concert with the actual user at the other side of the socket |
|||
*/ |
|||
static u_long |
|||
bytes_to_integer ( |
|||
char *str, /* IN - string that contains the bytes to convert */ |
|||
int start ) /* IN - index into string where bytes are */ |
|||
{ |
|||
uint32_t u; /* Value to be returned */ |
|||
char buff[4]; /* Transfer str into buff to word align reqd data */ |
|||
int index; /* Index into str and buff for transfer */ |
|||
|
|||
/* Get the str+start character and cast it into a u_long and put |
|||
the value through the network-to-host-short converter and store |
|||
it in the variable u. */ |
|||
|
|||
index = 0; |
|||
while (index < (int) sizeof(u)) { |
|||
buff[index] = str[index+start]; |
|||
index++; |
|||
} |
|||
/* u = ntohl (*((u_long *) buff)); */ |
|||
u = (uint32_t) strtoul(buff, NULL, 10); |
|||
|
|||
return u; |
|||
} /* end bytes_to_integer */ |
|||
|
|||
|
|||
|
|||
/*============================================================================= |
|||
|
|||
FUNCTION handle_socket_eof |
|||
|
|||
AUTHORS |
|||
|
|||
July 1991 Stefan Roth |
|||
|
|||
MODIFICATIONS |
|||
|
|||
NONE |
|||
|
|||
SUMMARY |
|||
|
|||
Do processing when the socket reaches EOF or when a message from the |
|||
client states that EOF has been reached. |
|||
|
|||
INTERFACES |
|||
|
|||
Local to this file. |
|||
Called by: ipc_transport_get_line(); |
|||
|
|||
RETURNED VALUE |
|||
|
|||
Ipc_Status_t - always IPC_STATUS_EOF |
|||
|
|||
GLOBAL VARIABLES |
|||
|
|||
NONE |
|||
|
|||
NON-STANDARD FEATURES |
|||
|
|||
NONE |
|||
|
|||
=============================================================================*/ |
|||
|
|||
|
|||
static Ipc_Status_t |
|||
handle_socket_eof (void) |
|||
{ |
|||
close (msg_stream); |
|||
close (sock_desc); |
|||
|
|||
sock_state = IPC_SOCK_UNINITIALIZED; |
|||
|
|||
return IPC_STATUS_EOF; |
|||
} /* handle_socket_eof */ |
|||
|
|||
|
|||
|
|||
/*============================================================================= |
|||
|
|||
FUNCTION read_sock |
|||
|
|||
AUTHORS |
|||
|
|||
July 1991 Stefan Roth |
|||
|
|||
MODIFICATIONS |
|||
|
|||
NONE |
|||
|
|||
SUMMARY |
|||
|
|||
Read N bytes from a socket. Only returns when the read had an error, |
|||
when 0 bytes (EOF) could be read, or LENGTH bytes are read. |
|||
|
|||
INTERFACES |
|||
|
|||
Local to this file. |
|||
Called by: ipc_transport_get_line(); |
|||
|
|||
RETURNED VALUE |
|||
|
|||
int - Returns the total number of bytes read. |
|||
|
|||
GLOBAL VARIABLES |
|||
|
|||
NONE |
|||
|
|||
NON-STANDARD FEATURES |
|||
|
|||
NONE |
|||
|
|||
=============================================================================*/ |
|||
|
|||
|
|||
static int |
|||
read_sock ( |
|||
int stream, /* IN - Socket stream */ |
|||
char *buffer, /* OUT - buffer to store incoming data */ |
|||
int length, /* IN - Number of bytes to be read */ |
|||
Ipc_Wait_t wait, /* IN - type of read operation */ |
|||
int flags ) /* IN - Original socket flags for blocking read */ |
|||
{ |
|||
int count; /* Number of bytes read with last `read` */ |
|||
int totalcount; /* total number of bytes read */ |
|||
char *buf2; |
|||
|
|||
/* count = 0; */ |
|||
/* while (count < length) { */ |
|||
/* buffer[count] = 'x'; */ |
|||
/* count++; */ |
|||
/* } */ |
|||
count = (int) read (stream, buffer, (size_t) length); |
|||
if (wait == IPC_NO_WAIT) { |
|||
fcntl (stream, F_SETFL, flags); /* Revert to blocking read */ |
|||
} |
|||
if ((count <= 0) || (count == length)) { |
|||
/* If error or if read in reqd number of bytes: */ |
|||
return count; |
|||
} else { |
|||
/* Only got some of the bytes requested */ |
|||
totalcount = count; |
|||
buf2 = &buffer[totalcount]; |
|||
length = length - count; |
|||
while (length > 0) { |
|||
count = (int) read (stream, buf2, (size_t) length); |
|||
if (count <= 0) /* EOF or read error */ |
|||
break; |
|||
totalcount = totalcount + count; |
|||
buf2 = &buffer[totalcount]; |
|||
length = length - count; |
|||
} |
|||
if (length != 0) { |
|||
fprintf (stderr, "WARNING: READ_SOCK read %d bytes instead of %d\n", |
|||
totalcount, totalcount + length); |
|||
} |
|||
return totalcount; |
|||
} |
|||
} /* end read_sock */ |
|||
|
|||
|
|||
|
|||
/*============================================================================= |
|||
|
|||
FUNCTION ipc_transport_get_line |
|||
|
|||
AUTHORS |
|||
|
|||
July 1991 Stefan Roth |
|||
|
|||
MODIFICATIONS |
|||
|
|||
NONE |
|||
|
|||
SUMMARY |
|||
|
|||
Main function for reading one line from a socket. Requires that the |
|||
socket be open. Lines are mostly SPICE code, but commands may also |
|||
be embedded in the socket data and they are interpreted by this function. |
|||
Therefore, this function may cause the socket to be closed. |
|||
|
|||
INTERFACES |
|||
|
|||
Called by: ipc_transport_terminate_server(); |
|||
(IPC.c) ipc_get_line(); |
|||
|
|||
RETURNED VALUE |
|||
|
|||
Ipc_Status_t - returns status of the read operation |
|||
|
|||
GLOBAL VARIABLES |
|||
|
|||
NONE |
|||
|
|||
NON-STANDARD FEATURES |
|||
|
|||
NONE |
|||
|
|||
=============================================================================*/ |
|||
|
|||
|
|||
Ipc_Status_t |
|||
ipc_transport_get_line ( |
|||
char *str, /* returns the result, null terminated */ |
|||
int *len, /* length of str passed IN and passed OUT */ |
|||
Ipc_Wait_t wait ) /* IN - wait or dont wait on incoming msg */ |
|||
{ |
|||
int count = 0; /* number of bytes read */ |
|||
int message_length; /* extracted from message header */ |
|||
|
|||
if (sock_state == IPC_SOCK_UNINITIALIZED) { |
|||
fprintf (stderr, |
|||
"ERROR: IPC: Attempted to read from uninitialized socket\n"); |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
|
|||
assert ((sock_state == IPC_SOCK_CONNECTED_TO_CLIENT) || |
|||
(sock_state == IPC_SOCK_INITIALIZED)); |
|||
|
|||
if (sock_state == IPC_SOCK_INITIALIZED) { |
|||
/* We have an open socket but have not connected to a client. */ |
|||
/* Accept a connection from a client. */ |
|||
msg_stream = accept (sock_desc, (struct sockaddr *)0, (socklen_t*)0); |
|||
|
|||
if (msg_stream == -1) { |
|||
fprintf (stderr, "ERROR: IPC: Server accepting request\n"); |
|||
perror ("ERROR: IPC"); |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
sock_state = IPC_SOCK_CONNECTED_TO_CLIENT; |
|||
} |
|||
/*-----------------------------------------------------------------------*/ |
|||
/* First read in the message header. */ |
|||
{ |
|||
int flags; |
|||
flags = fcntl(msg_stream, F_GETFL, NULL); /* Blocking read mode */ |
|||
|
|||
if (wait == IPC_WAIT) { |
|||
/* Block here and wait for the next message */ |
|||
count = read_sock (msg_stream, str, SOCK_MSG_HDR_LEN, wait, flags); |
|||
if (count == 0) { |
|||
/* EOF, will this ever happen? */ |
|||
/* fprintf (stderr, "WARNING: IPC: Reached eof on socket\n"); */ |
|||
return handle_socket_eof (); |
|||
} |
|||
} else if (wait == IPC_NO_WAIT) { |
|||
/* Read message, but do not wait if none available. */ |
|||
|
|||
fcntl (msg_stream, F_SETFL, flags | O_NDELAY); |
|||
count = read_sock (msg_stream, str, SOCK_MSG_HDR_LEN, wait, flags); |
|||
|
|||
if (count == 0) { |
|||
/* EOF, will this ever happen? */ |
|||
/* fprintf (stderr, "WARNING: IPC: Reached eof on socket\n"); */ |
|||
return handle_socket_eof (); |
|||
} else if (count == -1) { |
|||
if (errno == EWOULDBLOCK) { |
|||
return IPC_STATUS_NO_DATA; |
|||
} |
|||
} |
|||
|
|||
} else { |
|||
/* Serious problem, since it is not reading anything. */ |
|||
fprintf (stderr, |
|||
"ERROR: IPC: invalid wait arg to ipc_transport_get_line\n"); |
|||
} |
|||
} |
|||
|
|||
/* Do more error checking on the read in values of the message header: */ |
|||
if (count == -1) { |
|||
fprintf (stderr, "ERROR: IPC: Reading from socket\n"); |
|||
perror ("ERROR: IPC"); |
|||
return IPC_STATUS_ERROR; |
|||
} else if (str[0] != BOL_CHAR) { |
|||
fprintf (stderr, |
|||
"ERROR: IPC: Did not find beginning of message header (%c)\n", |
|||
str[0]); |
|||
return IPC_STATUS_ERROR; |
|||
} else if ((message_length = (int) bytes_to_integer (str, 1)) == -1) { |
|||
/* fprintf (stderr, "WARNING: IPC: Reached eof on socket\n"); */ |
|||
return handle_socket_eof (); |
|||
} else if (message_length == 0) { |
|||
*len = 0; |
|||
return IPC_STATUS_NO_DATA; |
|||
|
|||
/* Invalid test... delete - wbk |
|||
} else if (message_length > *len) { |
|||
fprintf (stderr, |
|||
"ERROR: IPC: Buffer (%d) is too short for message (%d)\n", |
|||
*len, message_length); |
|||
return IPC_STATUS_ERROR; |
|||
*/ |
|||
|
|||
} |
|||
|
|||
/*-----------------------------------------------------------------------*/ |
|||
/* Now read in the message body. */ |
|||
/* Always block here since the message header was already read and */ |
|||
/* we must get the body. */ |
|||
|
|||
*len = message_length; |
|||
count = read_sock (msg_stream, str, message_length, IPC_WAIT, 0); |
|||
if (count == 0) { |
|||
/* EOF, will this ever happen? */ |
|||
/* fprintf (stderr, */ |
|||
/* "WARNING: IPC: Reached eof in message body on socket\n");*/ |
|||
return handle_socket_eof (); |
|||
} else if (count == -1) { |
|||
fprintf (stderr, "ERROR: IPC: reading message body from socket\n"); |
|||
perror ("ERROR: IPC"); |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
|
|||
/* Looks like we have a valid message here. Put in the string terminator. */ |
|||
*len = count; |
|||
str[count] = '\0'; |
|||
|
|||
return IPC_STATUS_OK; |
|||
|
|||
} /* end ipc_transport_get_line */ |
|||
|
|||
|
|||
|
|||
/*============================================================================= |
|||
|
|||
FUNCTION ipc_transport_send_line |
|||
|
|||
AUTHORS |
|||
|
|||
July 1991 Stefan Roth |
|||
|
|||
MODIFICATIONS |
|||
|
|||
NONE |
|||
|
|||
SUMMARY |
|||
Send a line of information. First sends a message header and |
|||
then the actual message body. |
|||
Error checking is done to make reasonably sure that the data was sent. |
|||
|
|||
|
|||
INTERFACES |
|||
|
|||
Called by: (IPC.c) ipc_flush (); |
|||
|
|||
RETURNED VALUE |
|||
|
|||
Ipc_Status_t - returns status of the send operation (typically |
|||
IPC_STATUS_ERROR or IPC_STATUS_OK). |
|||
|
|||
|
|||
GLOBAL VARIABLES |
|||
|
|||
NONE |
|||
|
|||
NON-STANDARD FEATURES |
|||
|
|||
NONE |
|||
|
|||
=============================================================================*/ |
|||
|
|||
|
|||
Ipc_Status_t |
|||
ipc_transport_send_line ( |
|||
char *str, /* IN - String to write */ |
|||
int len ) /* IN - Number of characters out of STR to write */ |
|||
{ |
|||
int count; /* Counts how many bytes were actually written */ |
|||
u_long u; /* 32-bit placeholder for transmission of LEN */ |
|||
char hdr_buff[5]; /* Buffer for building header message in */ |
|||
int i; /* Temporary counter */ |
|||
char *char_ptr; /* Pointer for int to bytes conversion */ |
|||
|
|||
if (sock_state != IPC_SOCK_CONNECTED_TO_CLIENT) { |
|||
fprintf (stderr, "ERROR: IPC: Attempt to write to non-open socket\n"); |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
|
|||
/* Write message body header with length: */ |
|||
hdr_buff[0] = BOL_CHAR; |
|||
u = htonl ((uint32_t) len); |
|||
char_ptr = (char *) &u; |
|||
for(i = 0; i < 4; i++) |
|||
hdr_buff[i+1] = char_ptr[i]; |
|||
|
|||
count = (int) write (msg_stream, hdr_buff, 5); |
|||
if (count != 5) { |
|||
fprintf (stderr, "ERROR: IPC: (%d) send line error 1\n", count); |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
|
|||
/* Write message body: */ |
|||
count = (int) write (msg_stream, str, (size_t) len); |
|||
if (count != len) { |
|||
fprintf (stderr, "ERROR: IPC: (%d) send line error 2\n", count); |
|||
return IPC_STATUS_ERROR; |
|||
} |
|||
|
|||
return IPC_STATUS_OK; |
|||
} /* end ipc_transport_send_line */ |
|||
|
|||
|
|||
|
|||
/*============================================================================= |
|||
|
|||
FUNCTION ipc_transport_terminate_server |
|||
|
|||
AUTHORS |
|||
|
|||
July 1991 Stefan Roth |
|||
|
|||
MODIFICATIONS |
|||
|
|||
NONE |
|||
|
|||
SUMMARY |
|||
|
|||
This function reads all pending incoming messages and discards them. |
|||
Reading continues until a read error occurs or EOF is reached, at which |
|||
time the socket is closed. |
|||
Note that this function does not actually close the socket. This is |
|||
done in ipc_transport_get_line, which is called in this function. |
|||
|
|||
In this function, the incoming line length is limited. See buffer below. |
|||
|
|||
INTERFACES |
|||
|
|||
Called by: (IPC.c) ipc_terminate_server(); |
|||
|
|||
RETURNED VALUE |
|||
|
|||
Ipc_Status_t - returns status of last read operation (always |
|||
IPC_STATUS_ERROR or IPC_STATUS_EOF). |
|||
|
|||
GLOBAL VARIABLES |
|||
|
|||
NONE |
|||
|
|||
NON-STANDARD FEATURES |
|||
|
|||
NONE |
|||
|
|||
=============================================================================*/ |
|||
|
|||
|
|||
Ipc_Status_t |
|||
ipc_transport_terminate_server (void) |
|||
{ |
|||
char buffer[17000]; /* temp buffer for incoming data */ |
|||
int len; /* placeholder var to as arg to function */ |
|||
Ipc_Status_t status; /* value to be returned from function */ |
|||
int max_size; /* Max length of buffer */ |
|||
|
|||
max_size = sizeof (buffer); |
|||
do { |
|||
len = max_size; |
|||
status = ipc_transport_get_line (buffer, &len, IPC_WAIT); |
|||
} while ((status != IPC_STATUS_ERROR) && |
|||
(status != IPC_STATUS_EOF)); |
|||
return status; |
|||
} |
|||
|
|||
#endif /* IPC_UNIX_SOCKETS */ |
|||
@ -1,77 +0,0 @@ |
|||
/* |
|||
* Steve Tynor |
|||
* |
|||
* Generic Interprocess Communication module |
|||
* |
|||
* Used for debugging in absense of IPC interface. |
|||
* |
|||
*/ |
|||
|
|||
#include "ngspice/ngspice.h" |
|||
|
|||
#ifdef IPC_DEBUG_VIA_STDIO |
|||
|
|||
#include <stdio.h> |
|||
|
|||
|
|||
#include "ngspice/ipc.h" |
|||
|
|||
#include "ngspice/ipcproto.h" |
|||
|
|||
#include <assert.h> /* 12/1/97 jg */ |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
Ipc_Status_t ipc_transport_initialize_server ( |
|||
char *server_name, |
|||
Ipc_Mode_t m, |
|||
Ipc_Protocol_t p, |
|||
char *batch_filename ) |
|||
{ |
|||
NG_IGNORE(server_name); |
|||
NG_IGNORE(p); |
|||
NG_IGNORE(batch_filename); |
|||
|
|||
assert (m == IPC_MODE_INTERACTIVE); |
|||
printf ("INITIALIZE_SERVER\n"); |
|||
return IPC_STATUS_OK; |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
Ipc_Status_t ipc_transport_get_line ( |
|||
char *str, |
|||
int *len, |
|||
Ipc_Wait_t wait ) |
|||
{ |
|||
NG_IGNORE(wait); |
|||
|
|||
printf ("GET_LINE\n"); |
|||
fgets (str, 512, stdin); |
|||
char *tmp = strchr(str, '\n'); |
|||
if (tmp) |
|||
*tmp = '\0'; |
|||
*len = (int) strlen (str); |
|||
return IPC_STATUS_OK; |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
Ipc_Status_t ipc_transport_send_line ( |
|||
char *str, |
|||
int len ) |
|||
{ |
|||
int i; |
|||
|
|||
printf ("SEND_LINE: /"); |
|||
for (i = 0; i < len; i++) |
|||
putchar (str[i]); |
|||
printf ("/\n"); |
|||
return IPC_STATUS_OK; |
|||
} |
|||
|
|||
/*---------------------------------------------------------------------------*/ |
|||
Ipc_Status_t ipc_transport_terminate_server (void) |
|||
{ |
|||
return IPC_STATUS_OK; |
|||
} |
|||
|
|||
|
|||
#endif /* IPC_DEBUG_VIA_STDIO */ |
|||
@ -1,520 +0,0 @@ |
|||
/*============================================================================ |
|||
FILE IPCtiein.c |
|||
|
|||
MEMBER OF process XSPICE |
|||
|
|||
Public Domain |
|||
|
|||
Georgia Tech Research Corporation |
|||
Atlanta, Georgia 30332 |
|||
PROJECT A-8503 |
|||
|
|||
AUTHORS |
|||
|
|||
9/12/91 Bill Kuhn |
|||
|
|||
MODIFICATIONS |
|||
|
|||
<date> <person name> <nature of modifications> |
|||
|
|||
SUMMARY |
|||
|
|||
Provides a protocol independent interface between the simulator |
|||
and the IPC method used to interface to CAE packages. |
|||
|
|||
INTERFACES |
|||
|
|||
g_ipc (global variable) |
|||
|
|||
ipc_handle_stop() |
|||
ipc_handle_returni() |
|||
ipc_handle_mintime() |
|||
ipc_handle_vtrans() |
|||
ipc_send_stdout() |
|||
ipc_send_stderr() |
|||
ipc_send_std_files() |
|||
ipc_screen_name() |
|||
ipc_get_devices() |
|||
ipc_free_devices() |
|||
ipc_check_pause_stop() |
|||
|
|||
REFERENCED FILES |
|||
|
|||
None. |
|||
|
|||
NON-STANDARD FEATURES |
|||
|
|||
None. |
|||
|
|||
============================================================================*/ |
|||
|
|||
|
|||
#define CONFIG |
|||
|
|||
#include "ngspice/ngspice.h" |
|||
#include "ngspice/inpdefs.h" |
|||
#include "ngspice/gendefs.h" |
|||
#include "ngspice/cktdefs.h" |
|||
#include "bjt/bjtdefs.h" |
|||
#include "jfet/jfetdefs.h" |
|||
#include "mos1/mos1defs.h" |
|||
#include "mos2/mos2defs.h" |
|||
#include "mos3/mos3defs.h" |
|||
#include "ngspice/mifproto.h" |
|||
#include "ngspice/ipc.h" |
|||
#include "ngspice/ipctiein.h" |
|||
|
|||
|
|||
|
|||
/* |
|||
Global variable g_ipc is used by the SPICE mods that take care of |
|||
interprocess communications activities. |
|||
*/ |
|||
|
|||
|
|||
Ipc_Tiein_t g_ipc = { |
|||
IPC_FALSE, /* enabled */ |
|||
IPC_MODE_INTERACTIVE, /* mode */ |
|||
IPC_ANAL_DCOP, /* analysis mode */ |
|||
IPC_FALSE, /* parse_error */ |
|||
IPC_FALSE, /* run_error */ |
|||
IPC_FALSE, /* errchk_sent */ |
|||
IPC_FALSE, /* returni */ |
|||
0.0, /* mintime */ |
|||
0.0, /* lasttime */ |
|||
0.0, /* cpu time */ |
|||
NULL, /* send array */ |
|||
NULL, /* log file */ |
|||
{ /* vtrans struct */ |
|||
0, /* size */ |
|||
NULL, /* vsrc_name array */ |
|||
NULL, /* device_name array */ |
|||
}, |
|||
IPC_FALSE, /* stop analysis */ |
|||
}; |
|||
|
|||
|
|||
|
|||
/* |
|||
ipc_handle_stop |
|||
|
|||
This function sets a flag in the g_ipc variable to signal that |
|||
a stop message has been received over the IPC channel. |
|||
*/ |
|||
|
|||
void ipc_handle_stop(void) |
|||
{ |
|||
g_ipc.stop_analysis = IPC_TRUE; |
|||
} |
|||
|
|||
|
|||
/* |
|||
ipc_handle_returni |
|||
|
|||
This function sets a flag in the g_ipc variable to signal that |
|||
a message has been received over the IPC channel specifying that |
|||
current values are to be returned in the results data sets. |
|||
*/ |
|||
|
|||
void ipc_handle_returni(void) |
|||
{ |
|||
g_ipc.returni = IPC_TRUE; |
|||
} |
|||
|
|||
|
|||
/* |
|||
ipc_handle_mintime |
|||
|
|||
This function sets a value in the g_ipc variable that specifies |
|||
how often data is to be returned as it is computed. If the |
|||
simulator takes timestep backups, data may still be returned |
|||
more often that that specified by 'mintime' so that glitches |
|||
are not missed. |
|||
*/ |
|||
|
|||
void ipc_handle_mintime(double time) |
|||
{ |
|||
g_ipc.mintime = time; |
|||
} |
|||
|
|||
|
|||
|
|||
/* |
|||
ipc_handle_vtrans |
|||
|
|||
This function processes arguments from a #VTRANS card received over |
|||
the IPC channel. The data on the card specifies that a particular |
|||
zero-valued voltage source name should be translated to the specified |
|||
instance name for which it was setup to monitor currents. |
|||
*/ |
|||
|
|||
void ipc_handle_vtrans( |
|||
char *vsrc, /* The name of the voltage source to be translated */ |
|||
char *dev) /* The device name the vsource name should be translated to */ |
|||
{ |
|||
int i; |
|||
int size; |
|||
|
|||
|
|||
if(g_ipc.vtrans.size == 0) { |
|||
g_ipc.vtrans.size = 1; |
|||
g_ipc.vtrans.vsrc_name = TMALLOC(char *, 1); |
|||
g_ipc.vtrans.device_name = TMALLOC(char *, 1); |
|||
g_ipc.vtrans.vsrc_name[0] = MIFcopy(vsrc); |
|||
g_ipc.vtrans.device_name[0] = MIFcopy(dev); |
|||
} |
|||
else { |
|||
g_ipc.vtrans.size++; |
|||
|
|||
size = g_ipc.vtrans.size; |
|||
i = g_ipc.vtrans.size - 1; |
|||
|
|||
g_ipc.vtrans.vsrc_name = TREALLOC(char *, g_ipc.vtrans.vsrc_name, size); |
|||
g_ipc.vtrans.device_name = TREALLOC(char *, g_ipc.vtrans.device_name, size); |
|||
g_ipc.vtrans.vsrc_name[i] = MIFcopy(vsrc); |
|||
g_ipc.vtrans.device_name[i] = MIFcopy(dev); |
|||
} |
|||
} |
|||
|
|||
|
|||
|
|||
/* |
|||
ipc_send_stdout |
|||
|
|||
This function sends the data written to stdout over the IPC channel. |
|||
This stream was previously redirected to a temporary file during |
|||
the simulation. |
|||
*/ |
|||
|
|||
void ipc_send_stdout(void) |
|||
{ |
|||
int c; |
|||
int len; |
|||
|
|||
char buf[IPC_MAX_LINE_LEN+1]; |
|||
|
|||
/* rewind the redirected stdout stream */ |
|||
rewind(stdout); |
|||
|
|||
/* Begin reading from the top of file and send lines */ |
|||
/* over the IPC channel. */ |
|||
|
|||
/* Don't send newlines. Also, if line is > IPC_MAX_LINE_LEN chars */ |
|||
/* we must wrap it because Mspice can't handle it */ |
|||
|
|||
len = 0; |
|||
while( (c=fgetc(stdout)) != EOF) { |
|||
if(c != '\n') { |
|||
buf[len] = (char) c; |
|||
len++; |
|||
} |
|||
if((c == '\n') || (len == IPC_MAX_LINE_LEN)) { |
|||
buf[len] = '\0'; |
|||
ipc_send_line(buf); |
|||
len = 0; |
|||
} |
|||
} |
|||
if(len > 0) { |
|||
buf[len] = '\0'; |
|||
ipc_send_line(buf); |
|||
} |
|||
|
|||
/* Finally, rewind file again to discard the data already sent */ |
|||
rewind(stdout); |
|||
} |
|||
|
|||
|
|||
/* |
|||
ipc_send_stderr |
|||
|
|||
This function sends the data written to stderr over the IPC channel. |
|||
This stream was previously redirected to a temporary file during |
|||
the simulation. |
|||
*/ |
|||
|
|||
void ipc_send_stderr(void) |
|||
{ |
|||
int c; |
|||
int len; |
|||
|
|||
char buf[IPC_MAX_LINE_LEN+1]; |
|||
|
|||
/* rewind the redirected stderr stream */ |
|||
rewind(stderr); |
|||
|
|||
/* Begin reading from the top of file and send lines */ |
|||
/* over the IPC channel. */ |
|||
|
|||
/* Don't send newlines. Also, if line is > IPC_MAX_LINE_LEN chars */ |
|||
/* we must wrap it because Mspice can't handle it */ |
|||
|
|||
len = 0; |
|||
while( (c=fgetc(stderr)) != EOF) { |
|||
if(c != '\n') { |
|||
buf[len] = (char) c; |
|||
len++; |
|||
} |
|||
if((c == '\n') || (len == IPC_MAX_LINE_LEN)) { |
|||
buf[len] = '\0'; |
|||
ipc_send_line(buf); |
|||
len = 0; |
|||
} |
|||
} |
|||
if(len > 0) { |
|||
buf[len] = '\0'; |
|||
ipc_send_line(buf); |
|||
} |
|||
|
|||
/* Finally, rewind file again to discard the data already sent */ |
|||
rewind(stderr); |
|||
} |
|||
|
|||
|
|||
/* |
|||
ipc_send_std_files |
|||
|
|||
This function sends the data written to stdout and stderr over the |
|||
IPC channel. These streams were previously redirected to temporary |
|||
files during the simulation. |
|||
*/ |
|||
|
|||
Ipc_Status_t ipc_send_std_files(void) |
|||
{ |
|||
ipc_send_stdout(); |
|||
ipc_send_stderr(); |
|||
|
|||
return(ipc_flush()); |
|||
} |
|||
|
|||
|
|||
|
|||
/* |
|||
ipc_screen_name |
|||
|
|||
This function screens names of instances and nodes to limit the |
|||
data returned over the IPC channel. |
|||
*/ |
|||
|
|||
Ipc_Boolean_t ipc_screen_name(char *name, char *mapped_name) |
|||
{ |
|||
char *endp; |
|||
int i; |
|||
int len; |
|||
long l; |
|||
|
|||
/* Return FALSE if name is in a subcircuit */ |
|||
for(i = 0; name[i] != '\0'; i++) { |
|||
if(name[i] == ':') |
|||
return(IPC_FALSE); |
|||
} |
|||
|
|||
/* Determine if name is numeric and what value is */ |
|||
l = strtol(name, &endp, 10); |
|||
|
|||
/* If numeric */ |
|||
if(*endp == '\0') { |
|||
/* Return FALSE if >100,000 -> added by ms_server in ATESSE 1.0 */ |
|||
if(l >= 100000) |
|||
return(IPC_FALSE); |
|||
/* Otherwise, copy name to mapped name and return true */ |
|||
else { |
|||
strcpy(mapped_name,name); |
|||
return(IPC_TRUE); |
|||
} |
|||
} |
|||
|
|||
/* If node is an internal node from a semiconductor (indicated by a */ |
|||
/* trailing #collector, #source, ...), do not return its current. */ |
|||
/* Otherwise, map to upper case and eliminate trailing "#branch" if any. */ |
|||
for(i = 0; name[i]; i++) { |
|||
if(name[i] == '#') { |
|||
if(strcmp(name + i, "#branch") == 0) |
|||
break; |
|||
else |
|||
return(IPC_FALSE); |
|||
} |
|||
else { |
|||
if(islower_c(name[i])) |
|||
mapped_name[i] = toupper_c(name[i]); |
|||
else |
|||
mapped_name[i] = name[i]; |
|||
} |
|||
} |
|||
mapped_name[i] = '\0'; |
|||
len = i; |
|||
|
|||
/* If len != 8 or 6'th char not equal to $, then doesn't need vtrans */ |
|||
/* Otherwise, translate to device name that it monitors */ |
|||
if(len != 8) |
|||
return(IPC_TRUE); |
|||
else if(name[5] != '$') |
|||
return(IPC_TRUE); |
|||
else { |
|||
/* Scan list of prefixes in VTRANS table and convert name */ |
|||
for(i = 0; i < g_ipc.vtrans.size; i++) { |
|||
if(strncmp(mapped_name, g_ipc.vtrans.vsrc_name[i], 5) == 0) { |
|||
strcpy(mapped_name, g_ipc.vtrans.device_name[i]); |
|||
return(IPC_TRUE); |
|||
} |
|||
} |
|||
return(IPC_TRUE); |
|||
} |
|||
|
|||
} |
|||
|
|||
|
|||
|
|||
/* |
|||
ipc_get_devices |
|||
|
|||
This function is used to setup the OUTinterface data structure that |
|||
determines what instances will have data returned over the IPC channel. |
|||
*/ |
|||
|
|||
|
|||
int ipc_get_devices( |
|||
CKTcircuit *ckt, /* The circuit structure */ |
|||
char *device, /* The device name as it appears in the info struct */ |
|||
char ***names, /* Array of name strings to be built */ |
|||
double **modtypes) /* Array of types to be built */ |
|||
{ |
|||
int index; |
|||
int num_instances; |
|||
GENmodel *model; |
|||
GENinstance *here; |
|||
char *inst_name; |
|||
int inst_name_len; |
|||
int i; |
|||
|
|||
BJTmodel *BJTmod; |
|||
JFETmodel *JFETmod; |
|||
MOS1model *MOS1mod; |
|||
MOS2model *MOS2mod; |
|||
MOS3model *MOS3mod; |
|||
|
|||
/* Initialize local variables */ |
|||
num_instances = 0; |
|||
|
|||
/* Get the index into the circuit structure linked list of models */ |
|||
index = INPtypelook(device); |
|||
|
|||
/* Iterate through all models of this type */ |
|||
for(model = ckt->CKThead[index]; model; model = model->GENnextModel) { |
|||
|
|||
/* Iterate through all instance of this model */ |
|||
for(here = model->GENinstances; here; here = here->GENnextInstance) { |
|||
|
|||
/* Get the name of the instance */ |
|||
inst_name = here->GENname; |
|||
inst_name_len = (int) strlen(inst_name); |
|||
|
|||
/* Skip if it is a inside a subcircuit */ |
|||
for(i = 0; i < inst_name_len; i++) |
|||
if(inst_name[i] == ':') |
|||
break; |
|||
if(i < inst_name_len) |
|||
continue; |
|||
|
|||
/* Otherwise, add the name to the list */ |
|||
num_instances++; |
|||
if(num_instances == 1) |
|||
*names = TMALLOC(char *, 1); |
|||
else |
|||
*names = TREALLOC(char *, *names, num_instances); |
|||
(*names)[num_instances-1] = MIFcopy(inst_name); |
|||
|
|||
/* Then get the type if it is a Q J or M */ |
|||
if(num_instances == 1) |
|||
*modtypes = TMALLOC(double, 1); |
|||
else |
|||
*modtypes = TREALLOC(double, *modtypes, num_instances); |
|||
|
|||
if(strcmp(device,"BJT") == 0) { |
|||
BJTmod = (BJTmodel *) model; |
|||
(*modtypes)[num_instances-1] = BJTmod->BJTtype; |
|||
} |
|||
else if(strcmp(device,"JFET") == 0) { |
|||
JFETmod = (JFETmodel *) model; |
|||
(*modtypes)[num_instances-1] = JFETmod->JFETtype; |
|||
} |
|||
else if(strcmp(device,"Mos1") == 0) { |
|||
MOS1mod = (MOS1model *) model; |
|||
(*modtypes)[num_instances-1] = MOS1mod->MOS1type; |
|||
} |
|||
else if(strcmp(device,"Mos2") == 0) { |
|||
MOS2mod = (MOS2model *) model; |
|||
(*modtypes)[num_instances-1] = MOS2mod->MOS2type; |
|||
} |
|||
else if(strcmp(device,"Mos3") == 0) { |
|||
MOS3mod = (MOS3model *) model; |
|||
(*modtypes)[num_instances-1] = MOS3mod->MOS3type; |
|||
} |
|||
else { |
|||
(*modtypes)[num_instances-1] = 1.0; |
|||
} |
|||
|
|||
} /* end for all instances */ |
|||
} /* end for all models */ |
|||
|
|||
return(num_instances); |
|||
} |
|||
|
|||
|
|||
|
|||
/* |
|||
ipc_free_devices |
|||
|
|||
This function frees temporary data created by ipc_get_devices(). |
|||
*/ |
|||
|
|||
|
|||
void ipc_free_devices( |
|||
int num_items, /* Number of things to free */ |
|||
char **names, /* Array of name strings to be built */ |
|||
double *modtypes) /* Array of types to be built */ |
|||
{ |
|||
int i; |
|||
|
|||
for(i = 0; i < num_items; i++) |
|||
{ |
|||
FREE(names[i]); |
|||
names[i] = NULL; |
|||
} |
|||
|
|||
if(num_items > 0) |
|||
{ |
|||
FREE(names); |
|||
FREE(modtypes); |
|||
|
|||
names = NULL; |
|||
modtypes = NULL; |
|||
} |
|||
} |
|||
|
|||
|
|||
/* |
|||
ipc_check_pause_stop |
|||
|
|||
This function is called at various times during a simulation to check |
|||
for incoming messages of the form >STOP or >PAUSE signaling that |
|||
simulation should be stopped or paused. Processing of the messages |
|||
is handled by ipc_get_line(). |
|||
*/ |
|||
|
|||
void ipc_check_pause_stop(void) |
|||
{ |
|||
char buf[1025]; |
|||
int len; |
|||
|
|||
/* If already seen stop analysis, don't call ipc_get_line, just return. */ |
|||
/* This is provided so that the function can be called multiple times */ |
|||
/* during the process of stopping */ |
|||
if(g_ipc.stop_analysis) |
|||
return; |
|||
|
|||
/* Otherwise do a non-blocking call to ipc_get_line() to check for messages. */ |
|||
/* We assume that the only possible messages at this point are >PAUSE */ |
|||
/* and >STOP, so we don't do anything with the returned text if any */ |
|||
ipc_get_line(buf, &len, IPC_NO_WAIT); |
|||
} |
|||
|
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue