/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- ipc_init
- ipc_close
- ipc_server_wait
- ipc_recv_data
- ipc_send_data
- ipc_close_client
- ipc_close
- ipc_trans_num
- ipc_connect_num
- ipc_display_client
/* Server API for either a TCP connection oriented or a QNX
message passing IPC methods. Both types are iterative.
Rick Smereka, Copyright (C) 1998-2003.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, get a copy via the Internet at
http://gnu.org/copyleft/gpl.html or write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston,
MA 02111-1307 USA
You can contact the author via email at rsmereka@future-lab.com
Original version for CodeWarrior V4 under Windows 32bit.
Dec/98, Rick Smereka
Ported to HP-UX under GNU C 2.8.1.
Jan/99, Rick Smereka
Ported to Red Hat Linux 5.2, Jul/99, Rick Smereka
Modified for Windows 32bit only. Removed the 'WSADATA' structure
and all 'WSAStartup()' and WSACleanup()' function calls after I
realized that the 'WSACleanup()' call would shutdown all WinSock
operations. This causes trouble for socket servers which also
perform client socket operations before replying to the cient.
It is now the responsibility of the socket server to call
'WSAStartup()' and 'WSACleanup()'. Also renamed 'clientSocket'
to 'srvclientSocket' since CodeWarrior gets 'confused' with
module globals with the same name. Dec/99, Rick Smereka
Modified for use with both TCP sockets and QNX message passing
IPC. Sep/2001, Rick Smereka
Ported to Debian Linux. Nov/2002, Rick Smereka
Changed call to 'bind' and 'accept' under Unix
to use the 'SA' define (in 'flsocket.h').
Apr/2003
Complete re-write using TCP 'select' function (TCP IPC method only).
The client socket is no longer closed once per transaction. Once the
client connects, the socket is left open until a socket
communication error or the client closes the socket. Model for this
API taken from pages 162-167, section 6.8 of Unix Network Programming
Volume 1, Networking API's: Sockets and XTI second edition by
W. Richard Stevens. The 'select' TCP function is completly supported
by WinSock, the only difference is that WinSock does not require
or use the first parameter to 'select' which is the depth of
the 'fd_set'. Along with the 'select' function, a set
of socket descriptors is managed using the 'fd_set' data type
and the 'FD_' macros which are completly portable within both Unix
and Windows. Servers using this API are limited to the maximum
number of client sockets in use defined by 'FD_SETSIZE' (in the
OS socket include file).
This API has been re-structured so that the function 'ipc_server_wait'
is now an infinite loop and should only be called once by the
main server code. When a request is sent by the client,
'ipc_server_wait' will call the function 'process_request' to read
the request data, process the request and send the reply back. Your
server must supply this function which must return zero normally,
one upon valid request of server termination and -1 upon some
sort of socket communication error (refer to 'socloc.c', 'sys_log.c'
and 'dbsrv.c' for good server examples of how to use this API).
This API has also been modified to keep track of the current number
of transactions processed and the current number of client
connections (functions 'ipc_trans_num' and 'ipc_connect_num').
Note that all changes in this entry apply to TCP socket servers
only. May/2003, Rick Smereka
Renamed function 'ipc_end' (QNX message passing only) to 'ipc_close'.
Jun/2003, Rick Smereka */
#include "stdhead.h"
#include "flsocket.h"
#ifdef IPC_TCP
#include "ipcomm.h"
#endif
#include "ipcsrv.h"
// global (to module) socket data
#ifdef IPC_TCP
static int ipcsrv_maxfd; // largest descriptor
static int ipcsrv_maxi; // largest index into 'ipcsrv_client'
static fd_set ipcsrv_allset; // set of 'fd's
static fd_set ipcsrv_rset;
static long ipcsrv_trans_no; // transaction counter
#ifdef OS_WIN32
SOCKET serverSocket; // main server socket
SOCKET srvclientSocket; // current client socket
SOCKET ipcsrv_client[FD_SETSIZE];
SOCKADDR_IN sockServerAddr; // server address structure
#endif
#ifdef OS_UNIX
int ipcsrv_client[FD_SETSIZE]; // array of 'accept'ed fd's
int serverSocket, srvclientSocket;
struct sockaddr_in sockServerAddr;
#endif
#else
// QNX message passing module globals
int server_name_id;
pid_t server_pid;
#endif
#ifdef IPC_TCP
int ipc_init(char *host_name, int port_number)
#else
int ipc_init(char *sname)
#endif
{
/* Initialize the server for communication. The port that the server
'listen's to is expected in 'port_number'
for TCP servers. QNX message passing servers require
the 'nameloc' service name (in 'sname'). Function returns 'TRUE'
upon success,'FALSE' otherwise. Note that upon success,
the local host name is returned in 'host_name' (TCP only)
which must be already allocated to sufficient size. */
char mname[] = "ipc_init";
#ifdef IPC_QNX
/* initialize QNX server, start by requiring execution by
the superuser */
if (getuid() != 0)
{
log_file_date("%s:must be executed by superuser. Program abort", mname);
return(FALSE);
}
if (sname == NULL || !strlen(sname))
{
log_file_date("%s:null or empty[sname]", mname);
return(FALSE);
}
if ((server_name_id = qnx_name_attach(0, sname)) == -1)
{
log_file_date("%s:unable to attach name", mname);
return(FALSE);
}
return(TRUE);
#else
char *localHost;
int i;
if (port_number <= 0)
{
log_file_date("%s:invalid port. Program abort", mname);
return(FALSE);
}
if (host_name == (char *)NULL)
{
log_file_date("%s:host name parm is NULL pointer", mname);
return(FALSE);
}
host_name[0] = EOS;
if ((localHost = (char *)malloc(1024)) == (char *)NULL)
{
log_file_date("%s:alloc fail 'localHost'. Program abort.", mname);
return(FALSE);
}
// attempt to resolve the local host name
if (gethostname(localHost, 1023))
{
free(localHost);
log_file_date("%s:unable to resolve local host name. Program abort",
mname);
return(FALSE);
}
log_file_date("%s:server on host %s, port is %d", mname, localHost,
port_number);
#ifdef OS_WIN32
serverSocket = socket(PF_INET, SOCK_STREAM, DEFAULT_PROTOCOL);
#endif
#ifdef OS_UNIX
serverSocket = socket(AF_INET, SOCK_STREAM, DEFAULT_PROTOCOL);
#endif
if (serverSocket == INVALID_SOCKET)
{
log_file_date("%s:unable to create server socket. Program abort",
mname);
free(localHost);
return(FALSE);
}
// populate server address structure
memset(&sockServerAddr, 0, sizeof(sockServerAddr));
sockServerAddr.sin_family = AF_INET;
sockServerAddr.sin_addr.s_addr = INADDR_ANY;
sockServerAddr.sin_port = htons(port_number);
// bind the server socket
#ifdef OS_WIN32
if (bind(serverSocket,(LPSOCKADDR)&sockServerAddr,sizeof(sockServerAddr))
== SOCKET_ERROR)
#endif
#ifdef OS_UNIX
if (bind(serverSocket, (SA *)&sockServerAddr, sizeof(sockServerAddr))
== SOCKET_ERROR)
#endif
{
free(localHost);
log_file_date("%s:unable to bind server socket.", mname);
return(FALSE);
}
// listen on port
if (listen(serverSocket, QUEUE_SIZE) == SOCKET_ERROR)
{
free(localHost);
log_file_date("%s:unable to listen. Program abort", mname);
return(FALSE);
}
/* set max fd and prepare the fd set for 'select' */
#ifdef OS_WIN32
ipcsrv_maxfd = (int)serverSocket;
#else
ipcsrv_maxfd = serverSocket;
#endif
ipcsrv_maxi = -1;
for(i = 0; i < FD_SETSIZE; i++)
ipcsrv_client[i] = INVALID_SOCKET;
FD_ZERO(&ipcsrv_allset);
FD_SET(serverSocket, &ipcsrv_allset);
strcpy(host_name, localHost);
free(localHost);
ipcsrv_trans_no = 0;
return(TRUE);
#endif
}
#ifdef IPC_QNX
int ipc_close(void)
{
/* Terminate server listening. QNX message passing
only. Detaches name from 'nameloc'. Returns
'TRUE' upon success, 'FALSE' otherwise. */
if (qnx_name_detach(0, server_name_id) == -1)
return(FALSE);
return(TRUE);
}
#endif
#ifdef IPC_TCP
void ipc_server_wait(void)
{
/* Accept client connections. Function
returns only upon termination
request or fatal error. Applies to TCP IPC only.
This function calls a 'process_request'
function to read the data, process the request
and send a reply back. */
char mname[] = "ipc_server_wait";
int nready, len, i, ret;
#ifdef OS_WIN32
SOCKET tempfd;
SOCKADDR_IN cliaddr;
#else
struct sockaddr_in cliaddr;
int tempfd;
#endif
/* main server socket 'select' and 'accept' loop (infinite except for
sever termination */
for(;;)
{
// create a copy of the main fd set and use it
ipcsrv_rset = ipcsrv_allset;
/* Windows 'select' does not use first parameter but we
supply it anyway */
if ((nready = select(ipcsrv_maxfd + 1, &ipcsrv_rset, NULL, NULL, NULL))
<= 0)
log_file_date("%s:bad rc[<= 0] from select", mname);
// if there are new client connections, 'accept' them
if (FD_ISSET(serverSocket, &ipcsrv_rset))
{
len = sizeof(cliaddr);
#ifdef OS_WIN32
tempfd = accept(serverSocket, (LPSOCKADDR)&cliaddr, (LPINT)&len);
#endif
#ifdef OS_UNIX
tempfd = accept(serverSocket, (SA *)&cliaddr, &len);
#endif
if (tempfd == INVALID_SOCKET)
log_file_date("%s:error accepting a connection", mname);
/* place fd into an open element of the client array */
for(i = 0; i < FD_SETSIZE; i++)
#ifdef OS_WIN32
if ((int)ipcsrv_client[i] < 0)
#else
if (ipcsrv_client[i] < 0)
#endif
{
ipcsrv_client[i] = tempfd;
break;
}
// too many clients is fatal
if (i == FD_SETSIZE)
{
log_file_date("%s:detected too many active clients", mname);
return;
}
// add new fd to set
FD_SET(tempfd, &ipcsrv_allset);
// set new max fd if required
#ifdef OS_WIN32
if ((int)tempfd > ipcsrv_maxfd)
ipcsrv_maxfd = (int)tempfd;
#else
if (tempfd > ipcsrv_maxfd)
ipcsrv_maxfd = tempfd;
#endif
// set new index into 'client' array if required
if (i > ipcsrv_maxi)
ipcsrv_maxi = i;
if (--nready <= 0)
continue;
}
// check all clients for data
for(i = 0; i <= ipcsrv_maxi; i++)
{
if ((srvclientSocket = ipcsrv_client[i]) == INVALID_SOCKET)
continue;
if (FD_ISSET(srvclientSocket, &ipcsrv_rset))
{
// call server module to read request, process and send reply
ret = process_request();
/* 'process_request' returns zero normal, one on termination
request and -1 on socket error */
switch(ret)
{
case 1:
// close all sockets upon termination
(void)ipc_close();
return;
break;
case -1:
log_file_date("%s:connection closed by client", mname);
(void)ipc_close_client();
FD_CLR(srvclientSocket, &ipcsrv_allset);
ipcsrv_client[i] = INVALID_SOCKET;
break;
default:
ipcsrv_trans_no++;
};
if (--nready <= 0)
break;
}
}
}
}
#endif
#ifdef IPC_TCP
int ipc_recv_data(char *buf)
#else
int ipc_recv_data(char *buf, int size_buf)
#endif
{
/* Receive server data into 'buf' which must be allocated
to sufficient size. Function returns the number of
bytes read upon success, zero otherwise. The QNX
message passing variant will read block in this
function until a message is received. */
#ifdef IPC_QNX
if ((server_pid = Receive(0, buf, size_buf)) == -1)
return(0);
return(strlen(buf));
#else
return(ipc_recv(srvclientSocket, buf));
#endif
}
int ipc_send_data(char *buf)
{
/* Send client data. All data in 'buf' will be sent.
Function returns the number of bytes sent upon
success, zero otherwise. */
#ifdef IPC_QNX
if (Reply(server_pid, buf, strlen(buf) + 1) == -1)
return(0);
return(strlen(buf));
#else
return(ipc_send(srvclientSocket, buf));
#endif
}
/* following functions apply to TCP IPC only */
#ifdef IPC_TCP
int ipc_close_client(void)
{
/* Close the client socket in use by the server.
Function returns 'TRUE' upon success, 'FALSE'
otherwise. */
if (srvclientSocket == INVALID_SOCKET)
return(FALSE);
#ifdef OS_WIN32
if (closesocket(srvclientSocket) == SOCKET_ERROR)
return(FALSE);
#endif
#ifdef OS_UNIX
close(srvclientSocket);
#endif
// srvclientSocket = INVALID_SOCKET;
return(TRUE);
}
int ipc_close(void)
{
/* Close all sockets in use by the server.
Function returns 'TRUE' upon success, 'FALSE'
otherwise. */
int i;
#ifdef OS_WIN32
SOCKET tempfd;
#else
int tempfd;
#endif
for(i = 0; i <= ipcsrv_maxi; i++)
{
if ((tempfd = ipcsrv_client[i]) == INVALID_SOCKET)
continue;
#ifdef OS_WIN32
(void)closesocket(tempfd);
#endif
#ifdef OS_UNIX
close(tempfd);
#endif
}
#ifdef OS_WIN32
if (closesocket(serverSocket) == SOCKET_ERROR)
return(FALSE);
#endif
#ifdef OS_UNIX
close(serverSocket);
#endif
return(TRUE);
}
long ipc_trans_num(void)
{
// Return the current transaction count (TCP only).
return(ipcsrv_trans_no);
}
int ipc_connect_num(void)
{
// Return the current number of clients connected (TCP only).
int i, connect_count;
for(i = connect_count = 0; i < FD_SETSIZE; i++)
if (ipcsrv_client[i] != INVALID_SOCKET)
connect_count++;
return(connect_count);
}
void ipc_display_client(void)
{
// Display client socket pointer. For debuging.
#ifdef OS_WIN32
printf("ipc_display_client:%p\n", (void *)srvclientSocket);
#endif
#ifdef OS_UNIX
printf("ipc_display_client:%d\n", srvclientSocket);
#endif
}
#endif