## root/source/dumsocks.c

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

## DEFINITIONS

This source file includes following definitions.

1. main
2. parse_comline
3. srv_initialize
4. s_log_off
5. s_log_on
6. s_log_status
7. process_request
8. do_comm
9. s_terminate
10. s_trans_num
11. s_connect_num
12. do_reply_code
13. do_reply_char
14. do_reply_int
15. do_reply_long
16. term_app
17. usage

    /* dumsocks - A dumb server. This TCP or QNX message passing connection
       oriented iterative server is used for testing.
       Rick Smereka, Copyright (C) 2000-2006.

       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

       Like the name says...dumb. This server will accept only a
       generic status request, a termination request and log on/off
       requests. This server is coded to use 'socloc'. It is a fatal
       error if there are no 'socloc' servers running. Syntax:

          dumsocks [-l log_file] port

       Where 'port' is the TCP port to listen and '-l log_file'
       is the optional name of the server log file. If no log file
       is given, the log file name will be 'DUM_LOG_FILE' in the
       current directory. Note that the switch character ('-')
       is based on the platform (see include file 'stdhead.h').

       Original Windows 32bit version under CodeWarrior V4. Feb/2000,
       Rick Smereka

       Re-compiled after re-structuring the definition of the common
       send and repy codes. Added '-q' comand line option to suppress
       console/screen output. Program syntax now:

          dumsocks [-q] [-l log_file] port

       Apr/2000, Rick Smereka

       Modified the function 'srv_initialize' to obtain the IP address
       and pass this to the 'sloc_add' socloc function. Jul/2000,
       Rick Smereka

       Corrected multiple definitions of 'localHost'. Jul/2001,
       Rick Smereka

       Modified for use with both sockets and QNX message passing IPC.
       Ported to QNX V4.23a. Since the QNX message passing variant does
       not require a port number, this parameter is absent. QNX message
       passing syntax is:

          dumsocks [-q] [-l log_file]

       Oct/2001, Rick Smereka

       Coded to use the 'appinit' API. May/2002, Rick Smereka

       Ported to Debian Linux. Jan/2003, Rick Smereka

       Changed to in accordance with the modifications in 'ipcsrv.c'.
       Modified for use with the updated socloc client API ('sloc.c').
       May/2003, Rick Smereka

       Added function 'term_app' to perform all shutdown tasks.
       Added support for the send codes 'DM_SEND_TRANS_NUM' and
       'DM_SEND_CONNECT_NUM'. Jun/2003, Rick Smereka

       Changed all logging calls to use 'logman' API. Rewrote function
       'term_app'. Dec/2004, Rick Smereka

       Re-compile after changing the 'socloc' API.
       Jan/2005, Rick Smereka

       Re-compile after modifications to low level TCP socket communication
       module (ipcomm.c). Feb/2006, Rick Smereka */

    #include "stdhead.h"
    #include "flsocket.h"                 /* generic socket definitions */
    #include "appinit.h"                  /* application init API */
    #include "ipcsrv.h"                   /* IPC server routines */
    #include "dumsocks.h"                 /* dumb socket server defines */
    #ifdef IPC_TCP
    #include "socloc.h"                   /* socket locate defines */
    #include "ip.h"                       /* IP library routines */
    #include "sloc.h"                     /* 'socloc' client API */
    #endif

    #define VERSION "1.09.01-2006.02.23"  /* version ID */
    #define DM_TERM_PASSWD "rdsWin32"     /* terminate server password */
    #define APNAME "dumsocks"

    // default log file definition

    #define DUM_LOG_FILE "dumsocks.log"

    /* WinSock specific global data structure */

    #ifdef OS_WIN32
    WSADATA wsaData;                      /* struct used by 'WSAStartup()' */
    #endif

    /* global data */

    int console_output = TRUE;            /* log messages to screen? */
    char d_log_file[256];                 /* path/name of log file */
    #ifdef IPC_TCP
    int server_port = 0;                  /* TCP port that server is using */
    char localHost[256];                  /* host name of this machine */
    #else
    // QNX server transaction count

    long server_trans_num = 0L;
    #endif

    /* function prototypes */

    int main(int, char **);
    int parse_comline(int, char **);
    int srv_initialize(void);
    int process_request(void);
    int do_comm(int, char *);
    int s_terminate(char *);
    void s_log_off(void);
    void s_log_on(char *);
    void s_log_status(void);
    void s_trans_num(void);
    void s_connect_num(void);
    int do_reply_code(int);
    int do_reply_char(int, char *);
    int do_reply_int(int, int);
    int do_reply_long(int, long);
    void term_app(void);
    #ifdef IPC_TCP
    void usage();
    #endif

    int main(int argc, char **argv)
    {
       int isterm;

       /* indicate that we are initializing */

       if (!appinit_start(DM_SERVICE_NAME))
          {
          printf("%s:error creating 'appinit' lock file. Program abort\n",
                 APNAME);
          return(0);
          }

       /* check for 'socloc' server (TCP) initializing */

    #ifdef IPC_TCP
       if (!appinit_waitfor(SL_SERVICE_NAME, 5))
          {
          printf("%s:error waiting for 'socloc' to initialize. "
                 "Program abort\n", APNAME);
          (void)appinit_stop(DM_SERVICE_NAME);
          return(0);
          }
    #endif

    #ifdef IPC_TCP
       if (argc == 1)
          {
          (void)appinit_stop(DM_SERVICE_NAME);
          usage();
          return(0);
          }
    #endif

       if (!parse_comline(argc, argv))
          {
          (void)appinit_stop(DM_SERVICE_NAME);
          return(0);
          }

       /* read IPC config file and validate other 'socloc' servers
          (if any) */

       if (!srv_initialize())
          {
          printf("%s:program abort\n", APNAME);
          (void)appinit_stop(DM_SERVICE_NAME);
          term_app();
          return(0);
          }

       // set console output

       logman_console(console_output);

       /* initialize logging */

       if (logman_start(d_log_file, APNAME))
          {
          printf("%s:error staring server logging. Program abort\n",
                 APNAME);
          (void)appinit_stop(DM_SERVICE_NAME);
          return(0);
          }

       /* build logo string based on platform */

    #ifndef OS_UNIX
       /* non-Unix */

       logman("%s Server for %s Version %s", APNAME, PLATFORM_STRING, VERSION);
    #else
       /* Unix */

       logman("%s Server for %s Version %s", APNAME, SUB_PLATFORM_STRING,VERSION);
    #endif

       logman_nf("By Rick Smereka, Copyright (c) 2000-2006");

    #ifdef IPC_TCP
       logman("%s:server on \"%s\" servicing TCP port %d (decimal)",
              APNAME, localHost, server_port);
    #endif
       logman("%s:server started", APNAME);
       logman("%s comes with ABSOLUTELY NO WARRANTY", APNAME);
       logman_nf("This is free software, and you are welcome to redistribute it");
       logman_nf("under certain conditions; see \"gpl.txt\" for information.");

       /* turn off log as this is used only for debugging */

       // logman_end();

       /* indicate that the server has completed initialization */

       (void)appinit_stop(DM_SERVICE_NAME);

    #ifdef IPC_TCP
       ipc_server_wait();
    #else
       // QNX, process each connection (infinite loop)

       while(1)
          {
          isterm = process_request();
          server_trans_num++;

          if (isterm)
             break;
          }
    #endif

       term_app();
       return(0);
    }

    int parse_comline(int c_count, char **c_parm)
    {
       /* Parse the command line for parameters. Function
          returns 'TRUE' if no error was detected, 'FALSE'
          otherwise. */

       char mname[] = "parse_comline";
       int parms = 1, done = FALSE;

       /* set default log file */

       strcpy(d_log_file, DUM_LOG_FILE);

    #ifdef IPC_QNX
       if (c_count == 1)
          return(TRUE);
    #endif

       while(!done)
          {
          if (c_parm[parms][0] == SWITCH_CHAR)
             {
             switch(toupper(c_parm[parms][1]))
                {
                case 'L':
                   if (strlen(c_parm[parms]) > 2)
                      printf("%s:extraneous input with log switch, ignoring\n",
                             mname);

                   parms++;

                   if (parms >= c_count)
                      {
                      printf("%s:log switch with no file name, "
                             "program abort\n", mname);
                      return(FALSE);
                      }

                   strcpy(d_log_file, c_parm[parms]);
                   parms++;
                   break;

                case 'Q':
                   console_output = FALSE;
                   parms++;
                   break;

                default:
                   printf("%s:unknown switch[%s], program abort\n",
                          mname, c_parm[parms]);
                   return(FALSE);
                };
             }
    #ifdef IPC_TCP
          else
             {
             if (!qatoi(c_parm[parms], &server_port))
                {
                printf("%s:server port is non-numeric, "
                       "program abort\n", mname);
                return(FALSE);
                }

             if (server_port <= 0)
                {
                printf("%s:server port is out of range, "
                       "program abort\n", mname);
                return(FALSE);
                }

             parms++;
             }
    #endif

          if (parms >= c_count)
             done = TRUE;
          }

    #ifdef IPC_TCP
       if (server_port == 0)
          {
          printf("%s:server port missing, program abort\n", mname);
          return(FALSE);
          }
    #endif

       return(TRUE);
    }

    int srv_initialize(void)
    {
       /* Initialize the server. Function returns 'TRUE' upon
          success, 'FALSE' otherwise. */

       char mname[] = "srv_initialize";
       char mes[128];
       int ret;

       /* startup WinSock (if Windoze) */

    #ifdef OS_WIN32
       if (WSAStartup(WINSOCK_VERSION, &wsaData))
          {
          printf("%s:unable to start WinSock\n", mname);
          return(FALSE);
          }
    #endif

       /* initialize the server on the IPC level, note that
          the hostname 'localhost' is returned to us (TCP only) */

    #ifdef IPC_TCP
       if (!ipc_init(localHost, server_port))
    #else
       if (!ipc_init(DM_SERVICE_NAME))
    #endif
          {
          printf("%s:ipc_init initialization failure\n", mname);
          return(FALSE);
          }

       /* initialize 'socloc' client API */

    #ifdef IPC_TCP
       if ((ret = sloc_initialize()) != SL_OK)
          {
          sl_code_string(ret, mes);
          printf("%s:bad rc[%s] from 'sloc_initialize'\n", mname, mes);
          return(FALSE);
          }

        /* get IP address of this machine */

       if (!ip_host2ip(localHost, mes))
          {
          printf("%s:unable to obtain IP address of this machine\n", mname);
          return(FALSE);
          }

       /* register with 'socloc' */

       if ((ret = sloc_add(DM_SERVICE_NAME, localHost, server_port,
           mes)) != SL_OK)
          {
          sl_code_string(ret, mes);
          printf("%s:bad rc[%s] from 'sloc_add'\n", mname, mes);
          return(FALSE);
          }
    #endif

       return(TRUE);
    }

    void s_log_off(void)
    {
       /* Turn all loggging off. Of course, check for logging
          already off. */

       char mname[] = "s_log_off";
       int ret;

       logman("%s:enter", mname);

       if (!is_log_active())
          ret = DM_LOG_ALREADY_OFF;
       else
          {
          logman_end();
          ret = DM_OK;
          }

       (void)do_reply_code(ret);
       logman("%s:normal exit rc[%d]", mname, ret);
    }

    void s_log_on(char *comm)
    {
       /* Turn server logging on using either the supplied log file name
          or the global log file name. Of course, check for logging
          already on. */

       char mname[] = "s_log_on";
       int ret;

       logman("%s:enter", mname);

       if (is_log_active())
          ret = DM_LOG_ALREADY_ON;
       else
          {
          if (command_words(comm) > 1)
             {
             if (!command_word(comm, d_log_file, 2))
                {
                logman("%s:error getting log file name", mname);
                (void)do_reply_code(DM_INTERNAL_ERROR);
                return;
                }
             }
          else
             strcpy(d_log_file, DUM_LOG_FILE);

          if (logman_start(d_log_file, APNAME))
             {
             (void)do_reply_code(DM_LOG_ERROR);
             return;
             }

          ret = DM_OK;
          }

       (void)do_reply_code(ret);
       logman("%s:normal exit rc[%d]", mname, ret);
    }

    void s_log_status(void)
    {
       /* Get the status of the log including the flag value and the
          log name. */

       char mname[] = "s_log_status", *reply;
       int ret;

       logman("%s:enter", mname);
       ret = logman_is_active();

       if ((reply = (char *)malloc(strlen(d_log_file) + 3)) == (char *)NULL)
          {
          logman("%s:alloc fail[reply]", mname);
          (void)do_reply_char(DM_MEMORY_FAIL, "");
          return;
          }

       sprintf(reply, "%d %s", ret, d_log_file);
       (void)do_reply_char(DM_OK, reply);
       free(reply);
       logman("%s:normal exit rc[0]", mname);
    }

    int process_request(void)
    {
       /* Process a single request. Function returns 'FALSE' if
          termination signal was not encountered, 'TRUE'
          otherwise. */

       char mname[] = "process_request";
       char *messbuf, tmp[128];
       int ret, nbytes, nwords, mestype;

       logman("%s:enter", mname);

       if ((messbuf = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
          {
          (void)do_reply_code(DM_MEMORY_FAIL);
          logman("%s:alloc fail[messbuf]", mname);
          return(FALSE);
          }

       memset(messbuf, 0, DM_MAXCOMMAND);

       // get the message from the client

    #ifdef IPC_TCP
       if ((nbytes = ipc_recv_data(messbuf)) == 0)
    #else
       if ((nbytes = ipc_recv_data(messbuf, DM_MAXCOMMAND)) == 0)
    #endif
          {
          free(messbuf);
          logman("%s:connection closed by client", mname);
    #ifdef IPC_TCP
          return(-1);
    #else
          return(FALSE);
    #endif
          }

       messbuf[nbytes] = EOS;
       logman("%s:recv %s,l=%d", mname, messbuf, strlen(messbuf));
       nwords = command_words(messbuf);

       // no command words, empty string

       if (!nwords)
          {
          (void)do_reply_code(DM_INVALID_FUNCTION);
          free(messbuf);
          logman("%s:empty command", mname);
          return(FALSE);
          }

       // first command word must be a number

       if (!command_word(messbuf, tmp, 1))
          {
          free(messbuf);
          (void)do_reply_code(DM_INTERNAL_ERROR);
          logman("%s:error getting first command word", mname);
          return(FALSE);
          }

       if (!qatoi(tmp, &mestype))
          {
          (void)do_reply_code(DM_INVALID_FUNCTION);
          logman("%s:command signature not a number", mname);
          free(messbuf);
          return(FALSE);
          }

       /* pass entire string including command number */

       ret = do_comm(mestype, messbuf);
       free(messbuf);
       logman("%s:normal exit,rc=%d", mname, ret);
       return(ret);
    }

    int do_comm(int mestype, char *slm)
    {
       /* High level server command dispatcher.
          Function returns 'TRUE' if a terminate
          request was encountered, 'FALSE'
          otherwise. */

       char mname[] = "do_comm";
       int ret;

       logman("%s:enter", mname);
       ret = FALSE;

       switch(mestype)
          {
          case DM_SEND_STATUS:
             (void)do_reply_code(DM_OK);
             break;

          case DM_SEND_TERM:
             if (s_terminate(slm))
                ret = TRUE;

             break;

          case DM_SEND_LOG_OFF:
             s_log_off();
             break;

          case DM_SEND_LOG_ON:
             s_log_on(slm);
             break;

          case DM_SEND_LOG_STATUS:
             s_log_status();
             break;

          case DM_SEND_SERVICE_NAME:
             (void)do_reply_char(DM_OK, DM_SERVICE_NAME);
             break;

          case DM_SEND_VERSION:
             (void)do_reply_char(DM_OK, VERSION);
             break;

          case DM_SEND_TRANS_NUM:
             s_trans_num();
             break;

          case DM_SEND_CONNECT_NUM:
             s_connect_num();
             break;

          default:
             logman("%s:received unknown code=%d", mname, mestype);
             (void)do_reply_code(DM_INVALID_FUNCTION);
             break;
          };

       logman("%s:exit[%d]", mname, ret);
       return(ret);
    }

    int s_terminate(char *comm)
    {
       /* Terminate the server. Check the supplied password first.
          Syntax:

             SL_SEND_TERM password

          Function returns 'TRUE' if correct password was supplied,
          'FALSE' otherwise. */

       char mname[] = "s_terminate";
       char wrd[256];
       int ret;

       logman("%s:enter", mname);

       if (command_words(comm) < 2)
          {
          (void)do_reply_code(DM_INVALID_FUNCTION);
          logman("%s:no password", mname);
          return(FALSE);
          }

       if (!command_word(comm, wrd, 2))
          {
          (void)do_reply_code(DM_INTERNAL_ERROR);
          logman("%s:error getting password", mname);
          return(FALSE);
          }

       if (!strcmp(wrd, DM_TERM_PASSWD))
          {
          (void)do_reply_code(DM_OK);
          return(TRUE);
          }

       logman("%s:illegal terminate request", mname);
       (void)do_reply_code(DM_ACCESS_DENIED);
       return(FALSE);
    }

    void s_trans_num(void)
    {
       /* Get the current transaction count. Syntax:

             DM_SEND_TRANS_NUM */

       char mname[] = "s_trans_num";
       long count;

       logman("%s:enter", mname);

    #ifdef IPC_TCP
       count = ipc_trans_num();
    #else
       count = server_trans_num;
    #endif

       (void)do_reply_long(DM_OK, count);
       logman("%s:normal exit", mname);
    }

    void s_connect_num(void)
    {
       /* Get the current connection count. Syntax:

             DM_SEND_CONNECT_NUM */

       char mname[] = "s_connect_num";
       int count;

       logman("%s:enter", mname);

    #ifdef IPC_TCP
       count = ipc_connect_num();
       (void)do_reply_int(DM_OK, count);
    #else
       do_reply_int(DM_NOT_IMPLEMENTED, 0);
    #endif

       logman("%s:normal exit", mname);
    }

    int do_reply_code(int reply_code)
    {
       /* Send a reply to a request which returns the reply code. Function
          returns 'SL_OK' upon success, an error code otherwise. */

       char reply_char[50];

       memset(reply_char, 0, 50);
       sprintf(reply_char, "%d", reply_code);
       logman("do_reply_code:reply=%s,l=%d", reply_char,
                     strlen(reply_char));

       if (ipc_send_data(reply_char) == 0)
          return(DM_VC_ERROR);

       return(DM_OK);
    }

    int do_reply_char(int reply_code, char *parm)
    {
       /* Send a reply to a request which returns the reply code and
          possibly a string. Function returns 'SL_OK' upon success,
          an error code otherwise. */

       char *reply;

       if ((reply = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
          return(DM_MEMORY_FAIL);

       memset(reply, 0, DM_MAXCOMMAND);

       if (parm != (char *)NULL && strlen(parm))
          sprintf(reply, "%d '%s'", reply_code, parm);
       else
          sprintf(reply, "%d", reply_code);

       logman("do_reply_char:reply=%s,l=%d", reply, strlen(reply));

       if (ipc_send_data(reply) == 0)
          {
          free(reply);
          return(DM_VC_ERROR);
          }

       free(reply);
       return(DM_OK);
    }

    int do_reply_int(int reply_code, int ival)
    {
       /* Send a reply to a request which returns the an integer as
          well as the reply code. Function returns 'SL_OK' upon
          success, an error code otherwise. */

       char reply_char[100];

       memset(reply_char, 0, 100);
       sprintf(reply_char, "%d %d", reply_code, ival);
       logman("do_reply_int:reply=%s,l=%d", reply_char, strlen(reply_char));

       if (ipc_send_data(reply_char) == 0)
          return(DM_VC_ERROR);

       return(DM_OK);
    }

    int do_reply_long(int reply_code, long lval)
    {
       /* Send a reply to a request which returns the a long integer as
          well as the reply code. Function returns 'SL_OK' upon
          success, an error code otherwise. */

       char reply_char[100];

       memset(reply_char, 0, 100);
       sprintf(reply_char, "%d %ld", reply_code, lval);
       logman("do_reply_long:reply=%s,l=%d", reply_char, strlen(reply_char));

       if (ipc_send_data(reply_char) == 0)
          return(DM_VC_ERROR);

       return(DM_OK);
    }

    void term_app(void)
    {
       // Shutdown anything left open.

       logman("term_app:server down");

       if (logman_is_active())
          logman_end();

    #ifdef IPC_TCP
       (void)sloc_delete(server_port);
       sloc_term_api();
    #ifdef OS_WIN32
       WSACleanup();
    #endif
    #else
       (void)ipc_close();
    #endif

       (void)appinit_remove_name();
    }

    #ifdef IPC_TCP
    void usage(void)
    {
       /* Display program usage. */

       printf("usage:%s [%cq] [%cl log_file] port\n", DM_SERVICE_NAME,
              SWITCH_CHAR, SWITCH_CHAR);
    }
    #endif

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