744 lines
20 KiB
C
744 lines
20 KiB
C
/*=============================================================================
|
|
|
|
FILE IPCsockets.c
|
|
|
|
Copyright 1991
|
|
Georgia Tech Research Corporation, Atlanta, Georgia 30332
|
|
All Rights Reserved
|
|
|
|
PROJECT ATESSE 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*/
|
|
unsigned int 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
|
|
|
|
=============================================================================*/
|
|
|
|
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 */
|
|
{
|
|
u_long 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 = 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, (unsigned int *)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 ((u_long) 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 */
|