📄 ipc.c
字号:
/*============================================================================FILE IPC.cMEMBER OF process XSPICECopyright 1991Georgia Tech Research CorporationAtlanta, Georgia 30332All Rights ReservedPROJECT A-8503AUTHORS 9/12/91 Steve TynorMODIFICATIONS 6/13/92 Bill Kuhn Added some commentsSUMMARY 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============================================================================*/#ifndef NDEBUG#include <stdio.h>#endif#include <sys/file.h> /* Specific to BSD - Use sys/fcntl.h for sys5 */#include <assert.h>#include <ctype.h>#include <string.h>#include <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 "IPC.h"#include "IPCtiein.h"#include "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 200static 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 80static char fmt_buffer [FMT_BUFFER_SIZE];/*---------------------------------------------------------------------------*/static Ipc_Boolean_t kw_match (keyword, str) 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_serverThis function creates the interprocess communication channelserver mailbox or socket.*/Ipc_Status_t ipc_initialize_server (server_name, m, p) 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_serverThis function deallocates the interprocess communication channelmailbox or socket.*/Ipc_Status_t ipc_terminate_server (){ return ipc_transport_terminate_server ();}/*---------------------------------------------------------------------------*//*ipc_get_lineThis function gets a SPICE deck input line from the interprocesscommunication channel. Any special control commands in the deckbeginning with a ``>'' or ``#'' character are processed internally bythis function and not returned to SPICE.*/Ipc_Status_t ipc_get_line (str, len, wait) 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(*tok2)) { *tok2 = '\0'; tok2++; break; } } for(tok3 = tok2; *tok3; tok3++) { if(isspace(*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_flushThis function flushes the interprocess communication channelbuffer contents.*/Ipc_Status_t ipc_flush () /* * 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], 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;}/*---------------------------------------------------------------------------*/static Ipc_Status_t ipc_send_line_binary (str, len) 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, 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -