## root/source/dumsockc.c

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

## DEFINITIONS

This source file includes following definitions.

1. main
2. d_initialize
3. do_comm
4. d_term
5. d_status
6. d_client_log_on
7. d_client_log_off
8. d_client_log_status
9. d_server_log_on
10. d_server_log_off
11. d_server_log_status
12. d_socloc_log_on
13. d_socloc_log_off
14. d_socloc_log_status
15. d_connect
16. d_socloc_get_list
17. d_socloc_config_get_list
18. d_socloc_get
19. d_socloc_version
20. d_version
21. d_trans_num
22. d_connect_num
23. d_socloc_trans_num
24. d_socloc_connect_num
25. d_tcp_only
26. d_sr_code
27. d_sr_int
28. d_sr_long
29. d_sr_char
30. d_sr
31. is_connected
32. d_failover
33. d_code_string
34. get_token
35. d_header
36. d_out
37. term_app

    /* dumsockc - A command line for the dumb server 'dumsocks'.
       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

       Original Windows 32bit version Feb/2000, Rick Smereka

       Re-compiled after re-structuring the definition of the common
       send and repy codes. Apr/2000, Rick Smereka

       Re-compiled after adding the new 'clib' module 'ip.c'.
       Jul/2000, Rick Smereka

       Modified for use with both sockets and QNX message passing IPC.
       Removed manual socket interface. Re-coded to use 'ipclient'
       API. Ported to QNX V4.23a. Oct/2001, Rick Smereka

       Ported to Debian Linux. Jan/2003, Rick Smereka

       Modified for the updated 'socloc' API ('sloc.c').
       May/2003, Rick Smereka

       Added function 'term_app' to perform shutdown tasks.
       Added support for the commands 'DMCOM_TRANS_NUM',
       'DMCOM_CONNECT_NUM', 'DMCOM_SOCLOC_TRANS_NUM' and
       'DMCOM_SOCLOC_CONNECT_NUM'. Jun/2003, Rick Smereka

       Added includes 'appinit.h' and 'sliocode.h'.
       Added define 'APNAME'. Changed all logging calls to use 'logman'.
       Used 'appinit' to register and remove application name.
       May/2004, Rick Smereka

       Changed call in 'main' from 'gets' to 'get_rec' and changed
       command line buffer size to 'BUFSIZE' (defined in 'fio.h').
       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"
    #include "appinit.h"
    #include "ipclient.h"                 /* IPC client routines */
    #include "dumsocks.h"                 /* dumb socket server defines */
    #ifdef IPC_TCP
    #include "socloc.h"                   /* socket locate defines */
    #include "sloc.h"                     /* 'socloc' client API */
    #include "sconnect.h"                 /* parse connect string */
    #include "sliocode.h"                 // 'socloc' error translation
    #endif

    #define VERSION "1.08.01-2006.02.23"  /* version ID */
    // #define DEBUG 1

    /* default private log file definition */

    #define DUM_LOG_FILE "dumsockc.log"
    #define APNAME "dumsockc"

    /* global data */

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

    #ifdef IPC_TCP
    int serverport = 0;                  /* TCP port that server is using */
    int socloc_port;                     /* TCP port that 'socloc' is using */
    char serverhost[256];                /* 'dumsocks' server host name */
    #else
    int connected_flag = FALSE;
    #endif
    char d_log_file[256];                /* name of client log file */

    int main(void);
    int d_initialize(void);
    void d_term(char *);
    void d_status(void);
    void d_client_log_on(char *);
    void d_client_log_off(void);
    void d_client_log_status(void);
    void d_server_log_on(char *);
    void d_server_log_off(void);
    void d_server_log_status(void);
    void d_connect(char *);
    void d_version(void);
    void d_connect_num(void);
    void d_trans_num(void);
    void d_socloc_trans_num(void);
    void d_socloc_connect_num(void);
    void d_code_string(int, char *);
    int get_token(char *);
    void d_header(char *);
    void d_out(char *,...);
    int d_sr_code(int, char *);
    int d_sr_int(int, char *, int *);
    int d_sr_long(int, char *, long *);
    int d_sr_char(int, char *, char *);
    int is_connected(void);
    void term_app(void);
    #ifdef IPC_TCP
    void d_socloc_log_on(char *);
    void d_socloc_log_off(void);
    void d_socloc_log_status(void);
    void d_socloc_get_list(void);
    void d_socloc_config_get_list(void);
    void d_socloc_get(void);
    void d_socloc_version(void);
    int d_sr(int, char *, char *);
    void d_failover(int);
    #else
    int d_sr(int, char *, char *, int);
    void d_tcp_only(void);
    #endif

    int main(void)
    {
       char *comm;
       int done = FALSE;

       /* allocate command line buffer */

       if ((comm = malloc(BUFSIZE + 1)) == NULL)
          {
          printf("dumsockc:insufficient memory to run program\n");
          return(0);
          }

       /* build logo string based on platform */

    #ifndef OS_UNIX
       /* non-Unix */

       sprintf(comm, "dumsockc for %s Version %s", PLATFORM_STRING,
               VERSION);
    #else
       /* Unix */

       sprintf(comm, "dumsockc for %s Version %s", SUB_PLATFORM_STRING,
               VERSION);
    #endif

       printf("%s\n", comm);
       printf("By Rick Smereka, Copyright (c) 2000-2006\n");

       // register application name with 'appinit'

       if (!appinit_register_name(APNAME))
          {
          free(comm);
          printf("%s:fatal error registering app name with appinit\n", APNAME);
          return(0);
          }

    #ifdef DEBUG
       if (logman_start(d_log_file, APNAME))
          {
          printf("error starting log file\n");
          free(comm);
          return(0);
          }
    #endif

       /* initialize */

       if (!d_initialize())
          {
          free(comm);
          term_app();
          return(0);
          }

       printf("dumsockc comes with ABSOLUTELY NO WARRANTY\n");
       printf("This is free software, and you are welcome to redistribute it\n");
       printf("under certain conditions; see 'gpl.txt' for information.\n");

       /* interpret loop */

       while(!done)
          {
    #ifdef IPC_TCP
          printf("dumsockc/%s[%d]> ", serverhost, serverport);
    #else
          printf("dumsockc> ");
    #endif
          get_rec(stdin, comm);

          if (!do_comm(comm))
             done = TRUE;
          }

       free(comm);
       term_app();
       printf("exit dumsockc\n");
       return(0);
    }

    int d_initialize(void)
    {
       /* Start socket communication, locate a 'socloc' server
          and connect to a 'dumsocks' server. Function returns
          'TRUE' if the initialization was successful, 'FALSE'
          otherwise. */

       char mname[] = "d_initialize";
       char *thelist, mes[128];
       int ret;

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

       /* startup WinSock (if Windoze) */

    #ifdef OS_WIN32
       if (WSAStartup(WINSOCK_VERSION, &wsaData))
          {
          d_out("%s:unable to start WinSock. "
                "Program abort", mname);
          return(FALSE);
          }
    #endif

       /* attempt to connect to a 'socloc' server */

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

       /* locate a 'dumsocks' server */

       if ((ret = sloc_find(DM_SERVICE_NAME, serverhost, &serverport,
            (char *)NULL)) != SL_OK)
          {
          sl_code_string(ret, mes);
          d_out("%s:bad rc[%s] from 'sloc_find'. Program abort", mname, mes);
          return(FALSE);
          }

       d_out("%s:found 'dumsocks' server on host '%s' port %d", mname,
             serverhost, serverport);

       /* get config list from 'socloc' server and update
          the client list with it */

       if ((thelist = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
          {
          d_out("%s:alloc fail[thelist]", mname);
          return(FALSE);
          }

       if ((ret = sloc_config_get_list(thelist)) != SL_OK)
          {
          free(thelist);
          sl_code_string(ret, mes);
          d_out("%s:bad rc[%s] from 'sloc_config_get_list'", mname, mes);
          return(FALSE);
          }

       if ((ret = sl_config_put_list(thelist)) != SL_OK)
          {
          sl_code_string(ret, mes);
          d_out("%s:bad rc[%s] from 'sl_config_put_list'",
                        mname, mes);
          free(thelist);
          return(FALSE);
          }

       /* load the port that the current 'socloc' server is
          using */

       sloc_get_active_socloc(thelist, &socloc_port);
       free(thelist);

       // connect to the 'dumsocks' server

       if (!ipc_set_active(serverhost, serverport))
          {
          d_out("%s:bad rc[FALSE] from 'ipc_set_active'", mname);
          return(FALSE);
          }
    #else
       if (!ipc_set_active(DM_SERVICE_NAME))
          {
          d_out("%s:bad rc[FALSE] from 'ipc_set_active'", mname);
          return(FALSE);
          }

       connected_flag = TRUE;
    #endif

       strcpy(d_log_file, DUM_LOG_FILE);
       return(TRUE);
    }

    int do_comm(char *comm)
    {
       /* Dispatch a 'dumsockc' command. Function always returns
          'TRUE' except when the 'off' command is detected. */

       char mname[] = "do_comm";
       char *keywrd;                 /* command keyword */
       int token;                    /* keyword token */
       int nwords;                   /* number of words */
       int len;                      /* length of command */

       logman("%s:enter", mname);
       len = strlen(comm);

       if (!len)
          {
          d_out("empty command");
          return(TRUE);
          }

       /* allocate to max as this string is re-used for the return
          status string */

       if ((keywrd = malloc(DM_MAXCOMMAND + 1)) == NULL)
          {
          d_out("%s:alloc fail[keywrd]", mname);
          return(TRUE);
          }

       if (!word(comm, keywrd, 1))
          {
          d_out("%s:error while retreving keyword, line skipped", mname);
          free(keywrd);
          return(TRUE);
          }

       nwords = words(comm);

       if ((token = get_token(keywrd)) == DMCOM_NOT_FOUND)
          {
          d_out("%s:unknown keyword[%s]", mname, keywrd);
          free(keywrd);
          return(TRUE);
          }

       /* process command */

       switch(token)
          {
          case DMCOM_TERM:
             d_term(comm);
             break;

          case DMCOM_STATUS:
             d_status();
             break;

          case DMCOM_CLIENT_LOG_ON:
             d_client_log_on(comm);
             break;

          case DMCOM_CLIENT_LOG_OFF:
             d_client_log_off();
             break;

          case DMCOM_CLIENT_LOG_STATUS:
             d_client_log_status();
             break;

          case DMCOM_SERVER_LOG_ON:
             d_server_log_on(comm);
             break;

          case DMCOM_SERVER_LOG_OFF:
             d_server_log_off();
             break;

          case DMCOM_SERVER_LOG_STATUS:
             d_server_log_status();
             break;

          case DMCOM_CONNECT:
             d_connect(comm);
             break;

          case DMCOM_SOCLOC_GET_LIST:
    #ifdef IPC_TCP
             d_socloc_get_list();
    #else
             d_tcp_only();
    #endif
             break;

          case DMCOM_SOCLOC_CONFIG_GET_LIST:
    #ifdef IPC_TCP
             d_socloc_config_get_list();
    #else
             d_tcp_only();
    #endif
             break;

          case DMCOM_SOCLOC_GET:
    #ifdef IPC_TCP
             d_socloc_get();
    #else
             d_tcp_only();
    #endif
             break;

          case DMCOM_SOCLOC_LOG_OFF:
    #ifdef IPC_TCP
             d_socloc_log_off();
    #else
             d_tcp_only();
    #endif
             break;

          case DMCOM_SOCLOC_LOG_ON:
    #ifdef IPC_TCP
             d_socloc_log_on(comm);
    #else
             d_tcp_only();
    #endif
             break;

          case DMCOM_SOCLOC_LOG_STATUS:
    #ifdef IPC_TCP
             d_socloc_log_status();
    #else
             d_tcp_only();
    #endif
             break;

          case DMCOM_SOCLOC_VERSION:
    #ifdef IPC_TCP
             d_socloc_version();
    #else
             d_tcp_only();
    #endif
             break;

          case DMCOM_VERSION:
             d_version();
             break;

          case DMCOM_TRANS_NUM:
             d_trans_num();
             break;

          case DMCOM_CONNECT_NUM:
             d_connect_num();
             break;

          case DMCOM_SOCLOC_TRANS_NUM:
    #ifdef IPC_TCP
             d_socloc_trans_num();
    #else
             d_tcp_only();
    #endif
             break;

          case DMCOM_SOCLOC_CONNECT_NUM:
    #ifdef IPC_TCP
             d_socloc_connect_num();
    #else
             d_tcp_only();
    #endif
             break;

          case DMCOM_OFF:
             free(keywrd);
             return(FALSE);
             break;

          default:
             d_out("%s:command not found", mname);
             free(keywrd);
             return(TRUE);
          };

       free(keywrd);
       return(TRUE);
    }

    void d_term(char *comm)
    {
       /* Terminate a 'dumsocks' server. Syntax:

             term passwd

          Where 'passwd' is the termination password. */

       char mname[] = "d_term";
       char passwd[128], parm[128], reply[25];
       int ret;

       d_header(mname);

       if (command_words(comm) < 2)
          {
          d_out("%s:no password,access denied", mname);
          return;
          }

       if (!command_word(comm, passwd, 2))
          {
          d_out("%s:error getting password", mname);
          return;
          }

       if (words(passwd) > 1)
          sprintf(parm, "'%s'", passwd);
       else
          strcpy(parm, passwd);

       ret = d_sr_code(DM_SEND_TERM, parm);

       /* if shutdown was successful, failover to
          next 'dumsocks' server */

       if (ret == DM_OK)
          {
          d_out("%s:'dumsocks' server is down", mname);
    #ifdef IPC_TCP
          d_failover(FALSE);
    #endif
          }

       if (logman_is_active())
          logman("%s:normal exit rc[%d]", mname, ret);
       else
          {
          d_code_string(ret, parm);
          printf("%s\n", parm);
          }
    }

    void d_status(void)
    {
       /* Get 'dumsocks' server status. Syntax:

             status */

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

       d_header(mname);
       ret = d_sr_code(DM_SEND_STATUS, (char *)NULL);

       if (logman_is_active())
          logman("%s:normal exit rc[%d]", mname, ret);
       else
          {
          d_code_string(ret, mes);
          printf("%s\n", mes);
          }
    }

    void d_client_log_on(char *comm)
    {
       /* Turn client logging on with optional log file name.
          Syntax:

             client.log.on[ log.fname]

          Where 'log.fname' is the optional log file name. If no
          log file name is given 'DUM_LOG_FILE' will be used.
          It is an error if client logging is already on. */

       char mname[] = "d_client_log_on";

       d_header(mname);

       if (logman_is_active())
          {
          d_out("%s:logging already on", mname);
          return;
          }

       if (command_words(comm) > 1)
          {
          if (!command_word(comm, d_log_file, 2))
             {
             d_out("%s:error getting log file name", mname);
             return;
             }
           }
        else
           strcpy(d_log_file, DUM_LOG_FILE);

       if (logman_start(d_log_file, APNAME))
          {
          d_out("%s:error starting logging", mname);
          return;
          }

       if (logman_is_active())
          logman("%s:normal exit[0]", mname);
       else
          printf("ok\n");
    }

    void d_client_log_off(void)
    {
       /* Turn client logging off. Syntax:

             client.log.off

          It is an error if client logging is already off. */

       char mname[] = "d_client_log_off";

       d_header(mname);

       if (!logman_is_active())
          {
          d_out("%s:log already off", mname);
          return;
          }

       if (logman_is_active())
          logman("%s:normal exit[0]", mname);
       else
          printf("ok\n");

       logman_end();
    }

    void d_client_log_status(void)
    {
       /* Display status of client logging. Syntax:

             client.log.status */

       char mname[] = "d_client_log_status";

       d_header(mname);

       if (logman_is_active())
          logman("%s:client log is on", mname);
       else
          printf("%s:client log is off\nok\n", mname);
    }

    void d_server_log_on(char *comm)
    {
       /* Tell server to turn its logging on. Syntax:

             server.log.on[ log_fname]

          If no 'log_fname' is given, the default sever log file
          name will be used. It is an error if server logging is
          already on. */

       char mname[] = "d_server_log_on";
       char tmp[256], slog[256];
       int ret;

       d_header(mname);

       if (command_words(comm) > 1)
          {
          if (!command_word(comm, tmp, 2))
             {
             d_out("%s:error getting server log file name", mname);
             return;
             }

          if (words(tmp) > 1)
             sprintf(slog, "'%s'", tmp);
          else
             strcpy(slog, tmp);

          ret = d_sr_code(DM_SEND_LOG_ON, slog);
          }
       else
          ret = d_sr_code(DM_SEND_LOG_ON, (char *)NULL);

       if (logman_is_active())
          logman("%s:normal exit rc[%d]", mname, ret);
       else
          {
          d_code_string(ret, tmp);
          printf("%s\n", tmp);
          }
    }

    void d_server_log_off(void)
    {
       /* Tell server to turn its logging off. Syntax:

             server.log.off

          It is an error if the server log is already off. */

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

       d_header(mname);
       ret = d_sr_code(DM_SEND_LOG_OFF, (char *)NULL);

       if (logman_is_active())
          logman("%s:normal exit rc[%d]", mname, ret);
       else
          {
          d_code_string(ret, mes);
          printf("%s\n", mes);
          }
    }

    void d_server_log_status(void)
    {
       /* Get status of server logging. Syntax:

             server.log.status */

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

       d_header(mname);
       ret = d_sr_char(DM_SEND_LOG_STATUS, (char *)NULL, mes);

        if (ret == DM_OK)
           d_out("%s:log status is '%s'", mname, mes);

       d_code_string(ret, mes);

       if (logman_is_active())
          logman("%s:normal exit,rc[%d]", mname, ret);
       else
          {
          d_code_string(ret, mes);
          printf("%s\n", mes);
          }
    }

    #ifdef IPC_TCP
    void d_socloc_log_on(char *comm)
    {
       /* Tell 'socloc' server to turn its logging on. Syntax:

             socloc.log.on[ log_fname]

          If no 'log_fname' is given, the default sever log file
          name will be used. It is an error if server logging is
          already on. */

       char mname[] = "d_socloc_log_on";
       char tmp[256], slog[256];
       int ret;

       d_header(mname);

       if (command_words(comm) > 1)
          {
          if (!command_word(comm, tmp, 2))
             {
             d_out("%s:error getting server log file name", mname);
             return;
             }

          if (words(tmp) > 1)
             sprintf(slog, "'%s'", tmp);
          else
             strcpy(slog, tmp);

          ret = sloc_log_on(slog);
          }
       else
          ret = sloc_log_on((char *)NULL);

       if (logman_is_active())
          logman("%s:normal exit rc[%d]", mname, ret);
       else
          {
          sl_code_string(ret, tmp);
          printf("%s\n", tmp);
          }
    }
    #endif

    #ifdef IPC_TCP
    void d_socloc_log_off(void)
    {
       /* Tell 'socloc' server to turn its logging off. Syntax:

             socloc.log.off

          It is an error if the server log is already off. */

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

       d_header(mname);

       ret = sloc_log_off();

       if (logman_is_active())
          logman("%s:normal exit rc[%d]", mname, ret);
       else
          {
          sl_code_string(ret, mes);
          printf("%s\n", mes);
          }
    }
    #endif

    #ifdef IPC_TCP
    void d_socloc_log_status(void)
    {
       /* Get status of 'socloc' server logging. Syntax:

             socloc.log.status */

       char mname[] = "d_socloc_log_status";
       char mes[128];
       int ret, lflag;

       d_header(mname);

       ret = sloc_log_status(&lflag, mes);

       if (ret != SL_OK)
          {
          if (logman_is_active())
             logman("%s:normal exit,rc[%d]", mname, ret);
          else
             {
             sl_code_string(ret, mes);
             printf("%s\n", mes);
             }

          return;
          }

       if (lflag)
          d_out("%s:socloc server log is active,log is %s", mname, mes);
       else
          d_out("%s:socloc server log is inactive,log is %s", mname, mes);

       if (logman_is_active())
          logman("%s:normal exit,rc[0]", mname);
    }
    #endif

    void d_connect(char *comm)
    {
       /* Connect to any 'dumsocks' server. TCP syntax is:

             connect [host host_name][ port port_num][ ip ip_ad]

          QNX message passing syntax is:

             connect

          All parameters except 'connect' keyword are optional (TCP oly). If no
          server details are supplied, a re-connect will be attempted
          to the first 'dumsocks' server. */

       char mname[] = "d_connect";
       char mes[128], *hname, *ip_ad;
       int pos, ret, port, nwords;

       d_header(mname);
    #ifdef IPC_TCP
       nwords = command_words(comm);

       if (nwords < 2)
          {
          /* if only 'connect' keyword, re-lookup first 'dumsocks' server */

          if ((ret = sloc_find(DM_SERVICE_NAME, serverhost, &serverport,
               (char *)NULL)) != SL_OK)
             {
             d_out("%s:bad rc[%d] from 'sloc_find'", mname, ret);
             return;
             }

          d_status();
          return;
          }

       /* alloc details */

       if ((hname = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
          {
          d_out("%s:alloc fail[hname]", mname);
          return;
          }

       if ((ip_ad = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
          {
          d_out("%s:alloc fail[ip_ad]", mname);
          free(hname);
          return;
          }

       /* get the position of the second command word */

       pos = command_indxword(comm, 2);

       /* if the first char in the second word is a quote,
          'command_indxword' will skip over this, we need to
          backup one position */

       if (comm[pos - 1] == '\"' || comm[pos - 1] == '\'')
          pos--;

       /* parse the connect string */

       if ((ret = sconnect_parse(&comm[pos], FALSE, (char *)NULL, hname,
                                 &port, ip_ad)) != SP_OK)
          {
          sp_code_string(ret, mes);
          d_out("%s:bad rc[%d,%s] from 'sconnect_parse'", mname, ret, mes);
          free(hname);
          free(ip_ad);
          return;
          }

       /* locate the 'dumsocks' server based on supplied details */

       if ((ret = sloc_ll_find(DM_SERVICE_NAME, hname, &port, ip_ad)) != SL_OK)
          {
          sl_code_string(ret, mes);
          d_out("%s:bad rc[%d,%s] from 'sloc_ll_find'", mname, ret, mes);
          free(hname);
          free(ip_ad);
          return;
          }

       strcpy(serverhost, hname);
       serverport = port;

       if (!ipc_set_active(serverhost, serverport))
          {
          d_out("%s:bad rc[FALSE] from 'ipc_set_active'", mname);
          free(hname);
          free(ip_ad);
          return;
          }
    #else
       if (!ipc_set_active(DM_SERVICE_NAME))
          {
          d_out("%s:bad rc[FALSE] from 'ipc_set_active'", mname);
          return;
          }

       connected_flag = TRUE;
    #endif

       d_status();

    #ifdef IPC_TCP
       free(hname);
       free(ip_ad);
    #endif

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

    #ifdef IPC_TCP
    void d_socloc_get_list(void)
    {
       /* Get the list of available 'dumsocks' servers from 'socloc'.
          Syntax:

             socloc.get.list */

       char mname[] = "d_socloc_get_list";
       char *list_out, *entry, *hname, *port_char, *ip_ad;
       int ret, nentries, i;

       d_header(mname);

       if ((list_out = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
          {
          d_out("%s:alloc fail[list_out]", mname);
          return;
          }

       ret = sloc_find_list(DM_SERVICE_NAME, list_out);

       if (ret == DM_OK)
          {
          d_out("current 'dumsocks' servers");
          nentries = ll_words(list_out, SL_LIST_DELIM);

          if ((entry = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
             {
             d_out("%s:alloc fail[entry]", mname);
             free(list_out);
             return;
             }

          if ((hname = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
             {
             d_out("%s:alloc fail[hname]", mname);
             free(list_out);
             free(entry);
             return;
             }

          if ((port_char = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
             {
             d_out("%s:alloc fail[port_char]", mname);
             free(list_out);
             free(entry);
             free(hname);
             return;
             }

          if ((ip_ad = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
             {
             d_out("%s:alloc fail[ip_ad]", mname);
             free(list_out);
             free(entry);
             free(hname);
             free(port_char);
             return;
             }

          for(i = 1; i <= nentries; i++)
             {
             if (!ll_word(list_out, entry, i, SL_LIST_DELIM))
                {
                d_out("%s:error geting list entry[%d]", mname, i);
                free(list_out);
                free(entry);
                free(hname);
                free(port_char);
                free(ip_ad);
                return;
                }

             /* service name is first command word, we bypass this */

             if (!command_word(entry, hname, 2))
                {
                d_out("%s:error getting host from entry[%d]", mname, i);
                free(list_out);
                free(entry);
                free(hname);
                free(port_char);
                free(ip_ad);
                return;
                }

             if (!command_word(entry, port_char, 3))
                {
                d_out("%s:error getting port from entry[%d]", mname, i);
                free(list_out);
                free(entry);
                free(hname);
                free(port_char);
                free(ip_ad);
                return;
                }

             if (command_words(entry) > 3)
                {
                if (!command_word(entry, ip_ad, 4))
                   {
                   d_out("%s:error getting ip from entry[%d]", mname, i);
                   free(list_out);
                   free(entry);
                   free(hname);
                   free(port_char);
                   free(ip_ad);
                   return;
                   }
                }
             else
                ip_ad[0] = EOS;

             if (strlen(ip_ad))
                d_out("dumsocks[%d]:host=%s,port=%s,ip=%s", i, hname,
                      port_char, ip_ad);
             else
                d_out("dumsocks[%d]:host=%s,port=%s", i, hname, port_char);
             }

          free(entry);
          free(hname);
          free(port_char);
          free(ip_ad);
          }

       if (logman_is_active())
          logman("%s:normal exit rc[%d]", mname, ret);
       else
          {
          sl_code_string(ret, list_out);
          printf("%s\n", list_out);
          }

       free(list_out);
    }
    #endif

    #ifdef IPC_TCP
    void d_socloc_config_get_list(void)
    {
       /* Get the list of available 'socloc' servers.
          Syntax:

             socloc.config.get.list */

       char mname[] = "d_socloc_config_get_list";
       char *list_out, *entry, *hname, *port_char, *ip_ad;
       int ret, nentries, i;

       d_header(mname);

       if ((list_out = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
          {
          d_out("%s:alloc fail[list_out]", mname);
          return;
          }

       ret = sloc_config_get_list(list_out);

       if (ret == DM_OK)
          {
          d_out("current 'socloc' servers");
          nentries = ll_words(list_out, SL_LIST_DELIM);

          if ((entry = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
             {
             d_out("%s:alloc fail[entry]", mname);
             free(list_out);
             return;
             }

          if ((hname = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
             {
             d_out("%s:alloc fail[hname]", mname);
             free(list_out);
             free(entry);
             return;
             }

          if ((port_char = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
             {
             d_out("%s:alloc fail[port_char]", mname);
             free(list_out);
             free(entry);
             free(hname);
             return;
             }

          if ((ip_ad = (char *)malloc(DM_MAXCOMMAND)) == (char *)NULL)
             {
             d_out("%s:alloc fail[ip_ad]", mname);
             free(list_out);
             free(entry);
             free(hname);
             free(port_char);
             return;
             }

          for(i = 1; i <= nentries; i++)
             {
             if (!ll_word(list_out, entry, i, SL_LIST_DELIM))
                {
                d_out("%s:error geting list entry[%d]", mname, i);
                free(list_out);
                free(entry);
                free(hname);
                free(port_char);
                free(ip_ad);
                return;
                }

             if (!command_word(entry, hname, 1))
                {
                d_out("%s:error getting host from entry[%d]", mname, i);
                free(list_out);
                free(entry);
                free(hname);
                free(port_char);
                free(ip_ad);
                return;
                }

             if (!command_word(entry, port_char, 2))
                {
                d_out("%s:error getting port from entry[%d]", mname, i);
                free(list_out);
                free(entry);
                free(hname);
                free(port_char);
                free(ip_ad);
                return;
                }

             if (command_words(entry) > 2)
                {
                if (!command_word(entry, ip_ad, 3))
                   {
                   d_out("%s:error getting ip from entry[%d]", mname, i);
                   free(list_out);
                   free(entry);
                   free(hname);
                   free(port_char);
                   free(ip_ad);
                   return;
                   }
                }
             else
                ip_ad[0] = EOS;

             if (strlen(ip_ad))
                d_out("socloc[%d]:host=%s,port=%s,ip=%s", i, hname,
                      port_char, ip_ad);
             else
                d_out("socloc[%d]:host=%s,port=%s", i, hname, port_char);
             }

          free(entry);
          free(hname);
          free(port_char);
          free(ip_ad);
          }

       if (logman_is_active())
          logman("%s:normal exit rc[%d]", mname, ret);
       else
          {
          sl_code_string(ret, list_out);
          printf("%s\n", list_out);
          }

       free(list_out);
    }
    #endif

    #ifdef IPC_TCP
    void d_socloc_get(void)
    {
       /* Get and display the current 'socloc' server. Syntax:

             socloc.get */

       char mname[] = "d_socloc_get";
       char hname[256];
       int port;

       d_header(mname);
       hname[0] = EOS;
       port = 0;
       sloc_get_active_socloc(hname, &port);

       if (strlen(hname))
          d_out("connected to 'socloc' server '%s' on port %d", hname, port);
       else
          d_out("no current 'socloc' server");

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

    #ifdef IPC_TCP
    void d_socloc_version(void)
    {
       /* Get the version ID of the current 'socloc' server.
          Syntax:

             socloc.version */

       char mname[] = "d_socloc_version";
       char versionid[128];
       int ret;

       d_header(mname);
       ret = sloc_version(versionid);

       if (ret == SL_OK)
          d_out("'socloc' version ID is '%s'", versionid);
       else
          {
          sl_code_string(ret, versionid);
          d_out("%s", versionid);
          }

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

    void d_version(void)
    {
       /* Get the 'dumsocks' server version ID. Syntax:

             version */

       char mname[] = "d_version";
       char versionid[128];
       int ret;

       d_header(mname);
       ret = d_sr_char(DM_SEND_VERSION, (char *)NULL, versionid);

       if (ret == DM_OK)
          d_out("'dumsocks' version ID is '%s'", versionid);
       else
          {
          d_code_string(ret, versionid);
          d_out("%s", versionid);
          }

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

    void d_trans_num(void)
    {
       /* Get the 'dumsocks' server transaction count. Syntax:

             trans.num */

       char mname[] = "d_trans_num", mes[150];
       long trans_num;
       int ret;

       d_header(mname);
       ret = d_sr_long(DM_SEND_TRANS_NUM, (char *)NULL, &trans_num);

       if (ret == DM_OK)
          d_out("dumsocks transaction count is %ld", trans_num);
       else
          {
          d_code_string(ret, mes);
          d_out("%s", mes);
          }

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

    void d_connect_num(void)
    {
       /* Get the 'dumsocks' server connection count. Syntax:

             connect.num */

       char mname[] = "d_connect_num", mes[150];
       int ret, connect_num;

       d_header(mname);
       ret = d_sr_int(DM_SEND_CONNECT_NUM, (char *)NULL, &connect_num);

       if (ret == DM_OK)
          d_out("dumsocks connection count is %d", connect_num);
       else
          {
          d_code_string(ret, mes);
          d_out("%s", mes);
          }

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

    #ifdef IPC_TCP
    void d_socloc_trans_num(void)
    {
       /* Get the current transaction count from the 'socloc' server. Syntax:

             socloc.trans.num */

       char mname[] = "d_socloc_trans_num", mes[128];
       long trans_num;
       int ret;

       d_header(mname);
       ret = sloc_trans_num(&trans_num);

       if (ret != SL_OK)
          {
          if (logman_is_active())
             logman("%s:normal exit,rc[%d]", mname, ret);
          else
             {
             sl_code_string(ret, mes);
             d_out("%s", mes);
             }

          return;
          }

       d_out("%s:current socloc transaction count is %ld", mname, trans_num);

       if (logman_is_active())
          logman("%s:normal exit,rc[0]", mname);
    }

    void d_socloc_connect_num(void)
    {
       /* Get the current connection count from the 'socloc' server. Syntax:

             socloc.connect.num */

       char mname[] = "d_socloc_connect_num", mes[128];
       int ret, connect_num;

       d_header(mname);
       ret = sloc_connect_num(&connect_num);

       if (ret != SL_OK)
          {
          if (logman_is_active())
             logman("%s:normal exit,rc[%d]", mname, ret);
          else
             {
             sl_code_string(ret, mes);
             d_out("%s", mes);
             }

          return;
          }

       d_out("%s:current socloc connection count is %d", mname, connect_num);

       if (logman_is_active())
          logman("%s:normal exit,rc[0]", mname);
    }
    #endif

    #ifdef IPC_QNX
    void d_tcp_only(void)
    {
       d_out("applies to TCP IPC method only");
    }
    #endif

    int d_sr_code(int typ, char *parm)
    {
       /* Send a message to the server which will return just a code. */

       char reply[50], mname[] = "d_sr_code";
       int ret;

       d_header(mname);

    #ifdef IPC_TCP
       ret = d_sr(typ, parm, reply);
    #else
       ret = d_sr(typ, parm, reply, 50);
    #endif

       if (ret == DM_OK)
          if (!qatoi(reply, &ret))
             return(DM_INTERNAL_ERROR);

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

    int d_sr_int(int typ, char *parm, int *ival)
    {
       /* Send a message to the server which will return an integer
          value as well as the 'dumsocks' code. */

       char reply[50], anint[50];
       int ret;

    #ifdef IPC_TCP
       ret = d_sr(typ, parm, reply);
    #else
       ret = d_sr(typ, parm, reply, 50);
    #endif

       if (ret == DM_OK)
          {
          if (words(reply) < 2)
             return(DM_INTERNAL_ERROR);

          if (!word(reply, anint, 1))
             return(DM_INTERNAL_ERROR);

          if (!qatoi(anint, &ret))
             return(DM_INTERNAL_ERROR);

          if (!word(reply, anint, 2))
             return(DM_INTERNAL_ERROR);

          if (!qatoi(anint, ival))
             return(DM_INTERNAL_ERROR);
          }

       return(ret);
    }

    int d_sr_long(int typ, char *parm, long *lval)
    {
       /* Send a message to the server which will return a long integer
          value as well as the 'dumsocks' code. */

       char reply[100], along[50];
       int ret;

    #ifdef IPC_TCP
       ret = d_sr(typ, parm, reply);
    #else
       ret = d_sr(typ, parm, reply, 100);
    #endif

       if (ret == DM_OK)
          {
          if (words(reply) < 2)
             return(DM_INTERNAL_ERROR);

          if (!word(reply, along, 1))
             return(DM_INTERNAL_ERROR);

          if (!qatoi(along, &ret))
             return(DM_INTERNAL_ERROR);

          if (!word(reply, along, 2))
             return(DM_INTERNAL_ERROR);

          if (!qatol(along, lval))
             return(DM_INTERNAL_ERROR);
          }

       return(ret);
    }

    int d_sr_char(int typ, char *parm, char *reply)
    {
       /* Send a message to the server which will return a string in
          addition to the 'dumsocks' code. */

       char *tmp, ival[25], mname[] = "d_sr_char";
       int ret;

       d_header(mname);

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

    #ifdef IPC_TCP
       ret = d_sr(typ, parm, tmp);
    #else
       ret = d_sr(typ, parm, tmp, DM_MAXCOMMAND);
    #endif

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

       if (ret == DM_OK)
          {
          if (command_words(tmp) < 2)
             {
             free(tmp);
             return(DM_INTERNAL_ERROR);
             }

          if (!command_word(tmp, ival, 1))
             {
             free(tmp);
             return(DM_INTERNAL_ERROR);
             }

          if (!qatoi(ival, &ret))
             {
             free(tmp);
             return(DM_INTERNAL_ERROR);
             }

          if (!command_word(tmp, reply, 2))
             {
             free(tmp);
             return(DM_INTERNAL_ERROR);
             }
          }

       d_code_string(ret, tmp);
       logman("%s:normal exit[%s]", mname, tmp);
       free(tmp);
       return(ret);
    }

    #ifdef IPC_TCP
    int d_sr(int typ, char *parm, char *reply)
    #else
    int d_sr(int typ, char *parm, char *reply, int size_reply)
    #endif
    {
       /* Send and receive a message to the server. Its ok if 'parm'
          is NULL or empty. Upon success, server reply load loaded
          into 'reply' which must be already allocated to
          sufficient size. Function returns a 'dumsocks' code. */

       char mname[] = "d_sr";
       char *mess;

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

       /* make sure we are connected */

       if (!is_connected())
          {
          logman("%s:not connected to any 'dumsocks' server", mname);
          return(DM_NO_SERVER);
          }

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

       memset(mess, 0, DM_MAXCOMMAND);

       // format send string

       if (parm == (char *)NULL || !strlen(parm))
          sprintf(mess, "%d", typ);
       else
          sprintf(mess, "%d %s", typ, parm);

       logman("%s:send buf=%s,l=%d", mname, mess, strlen(mess));

    #ifdef IPC_TCP
       if (!ipc_send_receive(mess, reply))
    #else
       if (!ipc_send_receive(mess, reply, size_reply))
    #endif
          {
          logman("%s:error communicating with server", mname);
          free(mess);
    #ifdef IPC_TCP
          d_failover(TRUE);
    #else
          connected_flag = FALSE;
    #endif
          return(DM_VC_ERROR);
          }

       free(mess);
       logman("%s:normal exit,rc[ok],reply=%s,l=%d", mname,
                     reply, strlen(reply));
       return(DM_OK);
    }

    int is_connected(void)
    {
       /* Determine whether a 'dumsocks' server is active.
          Function returns 'TRUE' if a server is active,
          'FALSE' otherwise. */

    #ifdef IPC_TCP
       if (serverport > 0)
          return(TRUE);
    #else
       if (connected_flag)
          return(TRUE);
    #endif

       return(FALSE);
    }

    #ifdef IPC_TCP
    void d_failover(int dflag)
    {
       /* Failover to another 'dumsocks' server. The flag 'dflag'
          indicates whether we should get 'socloc' to delete the
          old entry. */

       char mname[] = "d_failover";
       char mes[138];
       int ret;

       d_header(mname);

       if (dflag)
          if ((ret = sloc_delete(serverport)) != SL_OK)
             {
             sl_code_string(ret, mes);
             d_out("%s:bad rc[%s] from 'sloc_delete'", mname, mes);
             }

       if ((ret = sloc_find(DM_SERVICE_NAME, serverhost, &serverport,
            (char *)NULL)) != SL_OK)
          {
          sl_code_string(ret, mes);
          d_out("%s:bad rc[%s] from 'sloc_find'", mname, mes);
          return;
          }

       if (!ipc_set_active(serverhost, serverport))
          {
          d_out("%s:bad rc[FALSE] from 'ipc_set_active'", mname);
          return;
          }

       if (logman_is_active())
          logman("%s:normal exit,failover ok,h=%s,p=%d", mname,
                        serverhost, serverport);
       else
          printf("failover to host '%s' on port %d successful\n",
                 serverhost, serverport);
    }
    #endif

    void d_code_string(int ret, char *mes)
    {
       /* Translate a 'dumsocks' return code to English. 'mes' must
          already be allocated to sufficient size. */

       switch(ret)
          {
          case DM_OK:
             strcpy(mes, "ok");
             break;

          case DM_ACCESS_DENIED:
             strcpy(mes, "access denied");
             break;

          case DM_MEMORY_FAIL:
             strcpy(mes, "memory allocation error");
             break;

          case DM_INVALID_FUNCTION:
             strcpy(mes, "invalid function");
             break;

          case DM_INTERNAL_ERROR:
             strcpy(mes, "internal server error");
             break;

          case DM_INVALID_PARAMETER:
             strcpy(mes, "invalid parameter");
             break;

          case DM_VC_ERROR:
             strcpy(mes, "socket communication error");
             break;

          case DM_LOG_ALREADY_OFF:
             strcpy(mes, "log already off");
             break;

          case DM_LOG_ALREADY_ON:
             strcpy(mes, "log already on");
             break;

          case DM_NO_SERVER:
             strcpy(mes, "not connected to dumsocks server");
             break;

          case DM_NOT_FOUND:
             strcpy(mes, "not found");
             break;

          case DM_LOG_ERROR:
             strcpy(mes, "error writing to log file");
             break;

          case DM_NOT_IMPLEMENTED:
             strcpy(mes, "not implemented");
             break;

          default:
             strcpy(mes, "unknown code");
          };
    }

    int get_token(char *keywrd)
    {
       /* Get keyword token. */

       char mname[] = "get_token";
       int i;
       int token = DMCOM_NOT_FOUND;

       /* static array of command words, notice the dummy zero position */

       static char *coms[] = { " ", "TERM", "STATUS", "CLIENT.LOG.OFF",
                               "CLIENT.LOG.ON", "CLIENT.LOG.STATUS",
                               "SERVER.LOG.OFF", "SERVER.LOG.ON",
                               "SERVER.LOG.STATUS", "CONNECT",
                               "SOCLOC.GET.LIST", "SOCLOC.CONFIG.GET.LIST",
                               "SOCLOC.GET", "SOCLOC.LOG.OFF",
                               "SOCLOC.LOG.ON", "SOCLOC.LOG.STATUS",
                               "SOCLOC.VERSION", "VERSION", "TRANS.NUM",
                               "CONNECT.NUM", "SOCLOC.TRANS.NUM",
                               "SOCLOC.CONNECT.NUM" };

       logman("%s:enter,keywrd='%s'", mname, keywrd);

       /* check for 'OFF' keyword separately */

       if (!stricmp(keywrd, "OFF"))
          {
          logman("%s:found 'OFF' keyword", mname);
          return(DMCOM_OFF);
          }

       for(i = 1; i <= DMCOM_MAXCOM; i++)
          if (!stricmp(keywrd, coms[i]))
             {
             token = i;
             break;
             }

       logman("%s:normal exit,token=%d", mname, token);
       return(token);
    }

    void d_header(char *mname)
    {
       /* Output the function enter header if logging. */

    #ifdef IPC_TCP
       if (strlen(serverhost) && serverport != 0)
          logman("%s:enter,sh=%s,sp=%d", mname, serverhost, serverport);
       else
          logman("%s:enter,no connection", mname);
    #else
       logman("%s:enter", mname);
    #endif
    }

    void d_out(char *fmt,...)
    {
       /* Output a message to the screen or use 'logman' to
          log the message. Output method depends on personal (client)
          logging status. Do not put a cr/lf at the end of the message.
          This will be automatically written to the end of the message. */

       char mes[MAXMES];
       va_list argptr;

       va_start(argptr, fmt);
       vsprintf(mes, fmt, argptr);

       if (logman_is_active())
          logman_nf(mes);
       else
          printf("%s\n", mes);
    }

    void term_app(void)
    {
       // Shutdown all API's.

       if (logman_is_active())
          logman_end();

    #ifdef IPC_TCP
       ipc_close();
       sloc_term_api();
    #ifdef OS_WIN32
       WSACleanup();
    #endif
    #endif

       (void)appinit_remove_name();
    }

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