## root/clib/ipcsrv.c

/* [<][>][^][v][top][bottom][index][help] */

## DEFINITIONS

This source file includes following definitions.

1. ipc_init
2. ipc_close
3. ipc_server_wait
4. ipc_recv_data
5. ipc_send_data
6. ipc_close_client
7. ipc_close
8. ipc_trans_num
9. ipc_connect_num
10. 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

/* [<][>][^][v][top][bottom][index][help] */