## root/source/socloc.c

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

## DEFINITIONS

This source file includes following definitions.

1. main
2. parse_comline
3. srv_initialize
4. process_request
5. do_comm
6. s_log_off
7. s_log_on
8. s_log_status
9. s_add
10. s_delete
11. s_config_add
12. s_config_delete
13. s_find
14. s_config_find
15. s_find_list
16. s_get_list
17. s_put_list
18. s_config_get_list
19. s_config_put_list
20. s_terminate
21. s_trans_num
22. s_connect_num
23. do_reply_int
24. do_reply_long
25. do_reply_code
26. do_reply_char
27. s_ll_put_list
28. s_ll_get_first
29. s_ll_get_nentries
30. s_ll_get_nth
31. s_ll_add
32. s_ll_port_exist
33. s_ll_delete
34. s_ll_delete_all
35. s_ll_debug
36. s_config_debug
37. term_app

    /* socloc - A socket locate server. This TCP connection oriented
       iterative socket server will register the name, port and IP
       address of other socket servers. Client applications can then
       easily find the host name, port and IP address of these socket
       servers. 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

       This socket locate server maintains two singly linked lists. One
       (called the config list) is the list of currently available
       socket locate servers. The other is a list of currently
       available socket servers. All additions and deletions (of
       both lists) are populated to all known 'socloc' servers giving
       automatic dynamic 'failover' protection.

       You can have as many 'socloc' servers running as you like. The only
       limitations are that each 'socloc' server must occupy a unique
       TCP port and not more than one 'socloc' server can be running
       on each host machine. You may also have as many socket servers
       running as you desire. Each socket server is identified by a
       'service name'. You may seach just by service name (in which
       case, the first entry with the same service name will match).
       You may also qualify the search with a host name, TCP port
       or the IP address. Throughout 'socloc', the IP
       address is completly optional. The only limitation is that each
       socket server must be occupy a unique TCP port. You may
       even have multiple socket servers with the same 'service
       name' running on the same host machine.

       Note that this socket server (unlike my other socket servers)
       does not use the 'sys_log' system logging server. This
       is due to the fact that it is assumed that this server is the
       first one started and 'sys_log' will not be running. This server
       logs all information into the private log file 'SL_LOG_FILE' by
       default.

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

       Modified to have server log status query return the name of the
       current log file if active. Mar/2000, Rick Smereka

       Re-compiled after re-structuring the definition of the common
       send and repy codes. Added '-l' command line option to specify
       the output log file and '-q' quiet command line option to
       supress console output. Program syntax now is:

          socloc [-q] [-l log_file]

       Apr/2000, Rick Smereka

       Modified function 'srv_initialize' to obtain the local machine
       IP address and pass this to 'sloc_bcast_config_add'. Changed
       function 'do_comm' to send a reply code when encountering a
       send code that is out of range. Jul/2000, Rick Smereka

       Re-compiled based on new version of 'socloc' API.
       Jul/2001, Rick Smereka

       Fixed bug in 's_ll_put_list' which called 'log_file_date' with an
       improper number of parameters. Feb/2002, Rick Smereka

       Fixed bug in 's_find' that incorrectly calculated the length of the
       reply string. Mar/2002, 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'.
       Changed the method that this server uses to talk to other
       socloc and socket servers. This server now uses the
       'slocsrv' API. May/2003, Rick Smereka

       Added function 'term_app' to perform server shutdown.
       Jun/2003, Rick Smereka

       Code cleanup. Dec/2004, 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 "socloc.h"                   /* socket locate defines */
    #include "slconfig.h"                 /* socket locate config management */
    #include "ipcsrv.h"                   /* TCP server routines */
    #include "ip.h"                       /* IP library routines */
    #include "slocsrv.h"                  /* client API for socloc server */
    #include "sconnect.h"                 /* 'sconnect' parse functions */
    #include "appinit.h"                  /* app init API */

    #define VERSION "1.08.01-2006.02.23"  /* version ID */
    #define SL_TERM_PASSWD "rdsWin32"     /* terminate server password */
    #define SL_LOG_FILE "socloc.log"      /* default log file name */

    /* WinSock specific global data structure */

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

    /* global data */

    int server_port = 0;                  /* TCP port that server is using */
    int console_output = TRUE;            /* echo messages to console? */
    char s_log_file[128];                 /* current log file */

    struct sloc_entry                     /* link list of socket servers */
       {
       char *service_name;                /* name of socket service */
       char *host_name;                   /* machine host name */
       int port_number;                   /* port that is being serviced */
       char *ip_addr;                     /* IP address (might be null) */
       struct sloc_entry *next;           /* next link */
       };

    struct sloc_entry *sloc_base;         /* root/base node pointer */

    #define SLOC_NULL ((struct sloc_entry *)NULL)

    /* function prototypes */

    int main(int, char **);
    int parse_comline(int, char **);
    int srv_initialize(void);
    int process_request(void);
    int do_comm(int, char *);
    void s_log_off(void);
    void s_log_on(char *);
    void s_log_status(void);
    void s_add(char *);
    void s_delete(char *);
    void s_config_add(char *);
    void s_config_delete(char *);
    void s_find(char *);
    void s_config_find(char *);
    void s_find_list(char *);
    void s_get_list(void);
    void s_put_list(char *);
    void s_config_get_list(void);
    void s_config_put_list(char *);
    int s_terminate(char *);
    void s_trans_num(void);
    void s_connect_num(void);
    int do_reply_int(int, int);
    int do_reply_long(int, long);
    int do_reply_code(int);
    int do_reply_char(int, char *char_value);
    int s_ll_put_list(char *);
    int s_ll_get_first(char *, char *, int *, char *);
    int s_ll_get_nentries(int *);
    int s_ll_get_nth(int, char *, char *, int *, char *);
    int s_ll_add(char *, char *, int, char *);
    int s_ll_port_exist(int);
    int s_ll_delete(int);
    void s_ll_delete_all(void);
    void s_ll_debug(void);
    void s_config_debug(void);
    void term_app(void);

    int main(int argc, char **argv)
    {
       char localHost[128];
       int isterm;

       /* indicate initialization in progress */

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

       /* set default log file name */

       strcpy(s_log_file, SL_LOG_FILE);

       /* parse any command line parameters */

       if (argc > 1)
          if (!parse_comline(argc, argv))
             {
             (void)appinit_stop(SL_SERVICE_NAME);
             return(0);
             }

       /* initialize private logging */

       if (!log_start(s_log_file))
          {
          printf("%s:error initializing log file. Program abort\n",
                 SL_SERVICE_NAME);
          (void)appinit_stop(SL_SERVICE_NAME);
          return(0);
          }

       /* set logger console output flag */

       log_console(console_output);

       /* build logo string based on platform */

    #ifndef OS_UNIX
       /* non-Unix */

       log_file_date("%s Socket Locate Server for %s Version %s",
                     SL_SERVICE_NAME, PLATFORM_STRING, VERSION);
    #else
       /* Unix */

       log_file_date("%s Socket Locate Server for %s Version %s",
                     SL_SERVICE_NAME, SUB_PLATFORM_STRING,VERSION);
    #endif

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

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

       if (!srv_initialize())
          {
          (void)appinit_stop(SL_SERVICE_NAME);
          term_app();
          return(4);
          }

       /* initialize the server on the TCP socket level, note that
          the hostname 'localhost' is returned to us */

       if (!ipc_init(localHost, server_port))
          {
          (void)appinit_stop(SL_SERVICE_NAME);
          term_app();
          return(6);
          }

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

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

       //log_end();

       /* indicate initialization complete */

       (void)appinit_stop(SL_SERVICE_NAME);

       // 'ipc_server_wait' is an infinite loop

       ipc_server_wait();
       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. */

       int parms = 1, done = FALSE;

       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 file switch, "
                             "ignoring\n", SL_SERVICE_NAME);

                   parms++;

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

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

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

                default:
                   printf("%s:unknown switch[%s], program abort\n",
                          SL_SERVICE_NAME, c_parm[parms]);
                   return(FALSE);
                };
             }
          else
             {
             printf("%s:unknown parameter[%s], program abort\n",
                    SL_SERVICE_NAME, c_parm[parms]);
             return(FALSE);
             }

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

       return(TRUE);
    }

    int srv_initialize(void)
    {
       /* Initialize the server by reading the list of socket
          locate servers and making sure there is an entry
          for this server. Function returns 'TRUE' upon
          success, 'FALSE' otherwise. */

       char mname[] = "srv_initialize";
       char localhost[128], mes[128], hname[128], ip_ad[50];
       char *thelist;
       int i, ret, nentries, port_num;

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

       /* load list of socket locate servers */

       if ((ret = sl_config_read(SL_CONFIG_FILE_NAME)) != SL_OK)
          {
          sl_code_string(ret, mes);
          log_file_date("%s:error reading socket locate "
                        "server list,rc=%s. Program abort", mname, mes);
          return(FALSE);
          }

       /* startup WinSock (if Windoze) */

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

            /* get host name of this machine */

       if (gethostname(localhost, 127))
          {
          log_file_date("%s:unable to resolve local "
                        "host name. Program abort", mname);
          return(FALSE);
          }

       log_file_date("%s:host name is '%s'", mname, localhost);

       /* get the number of socket locate servers from config file */

       if ((ret = sl_config_get_nentries(&nentries)) != SL_OK)
          {
          sl_code_string(ret, mes);
          log_file_date("%s:error getting number of "
                        "socket locate servers. Program "
                        "abort,rc=%s", mname, mes);
          return(FALSE);
          }

       log_file_date("%s:number of config entries is %d", mname, nentries);

       /* go through the list looking for a host name match */

       for(i = 1; i <= nentries; i++)
          {
          if ((ret = sl_config_get_nth(i, hname, &port_num, ip_ad)) != SL_OK)
             {
             sl_code_string(ret, mes);
             log_file_date("%s:error socket locate server "
                           "entry #%d. Program abort,rc=%s", mname, i, mes);
             return(FALSE);
             }

          /* if the host name matches, load the port number used */

          if (!strcmp(hname, localhost))
             server_port = port_num;
          }

       /* if all socket locate servers were checked and no host
          name match, error */

       if (server_port == 0)
          {
          log_file_date("%s:no matching host name found in '%s'. "
                        "Program abort.", mname,
                        SL_CONFIG_FILE_NAME);
          return(FALSE);
          }

       /* tell config API that this is the local server */

       if ((ret = sl_config_set_ils(server_port, TRUE)) != SL_OK)
          {
          sl_code_string(ret, mes);
          log_file_date("%s:bad rc from 'sl_config_set_ils',rc=%s",
                        mname, mes);
          return(FALSE);
          }

       sloc_base = SLOC_NULL;

       /* use 'socloc' client API to find first active 'socloc' server
          its okay if there are no other 'socloc' servers present */

       if ((ret = sloc_find_first_active()) != SL_OK)
          {
          sl_code_string(ret, mes);
          log_file_date("%s:no other active 'socloc' servers,rc=%s",
                        mname, mes);
          }
       else
          {
          /* when other 'socloc' servers are present, don't trust their
             'ipc' config files, send a config add command to each active
             'socloc' server then update this server's 'ipc' config file
             and list from the current active 'socloc' server, re-set
             the 'is_local_server' flag for this server and finally,
             get and load the current socket server list */

          log_file_date("%s:found other active 'socloc' servers", mname);

          /* get this machine IP address */

          if (!ip_host2ip(localhost, ip_ad))
             {
             log_file_date("%s:unable to obtain this machine IP address",
                           mname);
             return(FALSE);
             }

          (void)sloc_bcast_config_add(localhost, server_port, ip_ad);

          /* commented out the update of the config file after noticing
             some cases of the config file being updated from a
             more incomplete version */

          /* if ((ret = sloc_config_update()) != SL_OK)
             {
             sl_code_string(ret, mes);
             log_file_date("%s:bad rc from 'sloc_config_update',rc=%s",
                           mname, mes);
             return(FALSE);
             } */

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

          /* get the config list from the first active server */

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

          /* create a new list in memory */

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

          if ((ret = sl_config_set_ils(server_port, TRUE)) != SL_OK)
             {
             sl_code_string(ret, mes);
             log_file_date("%s:bad rc from 'sl_config_set_ils',rc=%s",
                           mname, mes);
             return(FALSE);
             }

          /* if list is empty, that's ok */

          if ((ret = sloc_get_list(thelist)) != SL_OK)
             if (ret == SL_NOT_FOUND)
                log_file_date("%s:socket server list is empty", mname);
             else
                {
                free(thelist);
                sl_code_string(ret, mes);
                log_file_date("%s:bad rc[%s] from 'sloc_get_list'", mname, mes);
                return(FALSE);
                }

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

          free(thelist);
          }

       sl_config_debug();
       s_ll_debug();
       log_file_date("%s:normal exit[TRUE]", mname);
       return(TRUE);
    }

    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;

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

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

       memset(messbuf, 0, SL_MAXCOMMAND);

       // get the message from the client

       if ((nbytes = ipc_recv_data(messbuf)) == 0)
          {
          free(messbuf);
          log_file_date("%s:connection closed by client", mname);
          return(-1);
          }

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

       // no command words, empty string

       if (!nwords)
          {
          (void)do_reply_code(SL_INVALID_FUNCTION);
          free(messbuf);
          log_file_date("%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(SL_INTERNAL_ERROR);
          log_file_date("%s:error getting first command word", mname);
          return(FALSE);
          }

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

       /* pass entire string including command number */

       ret = do_comm(mestype, messbuf);
       free(messbuf);
       log_file_date("%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;

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

       switch(mestype)
          {
          case SL_SEND_ADD:
             s_add(slm);
             break;

          case SL_SEND_DELETE:
             s_delete(slm);
             break;

          case SL_SEND_CONFIG_ADD:
             s_config_add(slm);
             break;

          case SL_SEND_CONFIG_DELETE:
             s_config_delete(slm);
             break;

          case SL_SEND_FIND:
             s_find(slm);
             break;

          case SL_SEND_CONFIG_FIND:
             s_config_find(slm);
             break;

          case SL_SEND_FIND_LIST:
             s_find_list(slm);
             break;

          case SL_SEND_GET_LIST:
             s_get_list();
             break;

          case SL_SEND_PUT_LIST:
             s_put_list(slm);
             break;

          case SL_SEND_CONFIG_GET_LIST:
             s_config_get_list();
             break;

          case SL_SEND_CONFIG_PUT_LIST:
             s_config_put_list(slm);
             break;

          case SL_SEND_STATUS:
             (void)do_reply_code(SL_OK);
             break;

          case SL_SEND_LOG_OFF:
             s_log_off();
             break;

          case SL_SEND_LOG_ON:
             s_log_on(slm);
             break;

          case SL_SEND_LOG_STATUS:
             s_log_status();
             break;

          case SL_SEND_SERVICE_NAME:
             (void)do_reply_char(SL_OK, SL_SERVICE_NAME);
             break;

          case SL_SEND_VERSION:
             (void)do_reply_char(SL_OK, VERSION);
             break;

          case SL_SEND_DUMP_DEBUG:
             s_ll_debug();
             (void)do_reply_code(SL_OK);
             break;

          case SL_SEND_CONFIG_DUMP_DEBUG:
             s_config_debug();
             (void)do_reply_code(SL_OK);
             break;

          case SL_SEND_TRANS_NUM:
             s_trans_num();
             break;

          case SL_SEND_CONNECT_NUM:
             s_connect_num();
             break;

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

             break;

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

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

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

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

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

       if (!is_log_active())
          ret = SL_LOG_ALREADY_OFF;
       else
          {
          log_end();
          ret = SL_OK;
          }

       (void)do_reply_code(ret);
       log_file_date("%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;

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

       if (is_log_active())
          ret = SL_LOG_ALREADY_ON;
       else
          {
          if (command_words(comm) > 1)
             if (!command_word(comm, s_log_file, 2))
                {
                log_file_date("%s:error getting log file name", mname);
                (void)do_reply_code(SL_INTERNAL_ERROR);
                return;
                }

          if (!log_start(s_log_file))
             {
             (void)do_reply_code(SL_LOG_ERROR);
             log_file_date("%s:error starting log", mname);
             return;
             }

          ret = SL_OK;
          }

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

    void s_log_status(void)
    {
       /* Get the current log status (on or off) along with the
          current log file name. */

       char mname[] = "s_log_status";
       char reply[128];

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

       if (is_log_active())
          {
          if (words(s_log_file) > 1)
             sprintf(reply, "1 '%s'", s_log_file);
          else
             sprintf(reply, "1 %s", s_log_file);

          (void)do_reply_char(SL_OK, reply);
          }
       else
          {
          if (words(s_log_file) > 1)
             sprintf(reply, "0 '%s'", s_log_file);
          else
             sprintf(reply, "0 %s", s_log_file);

          (void)do_reply_char(SL_OK, reply);
          }

       log_file_date("%s:normal exit rc[0],reply=%s", mname, reply);
    }

    void s_add(char *comm)
    {
       /* Add an entry to the list of available socket servers.
          Syntax:

             SL_SEND_ADD orig_flag service_name host_name port_number[ ip_addr]

          Where 'orig_flag' indicates the origin (0=server,1=client).
          If the origin is the client, a broadcast will be sent to
          all other known 'socloc' servers. Line will be parsed with
          'command_word' functions so any parameter with a space must
          be quoted. */

       char mname[] = "s_add";
       char *sname, *hname, *port_char, *ip_ad;
       char orig_flag_char[25], mess[150];
       int orig_flag, port, len, ret, sockrc;

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

       /* must be at least 5 command words */

       if (command_words(comm) < 5)
          {
          log_file_date("%s:incorrect number of parameters", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          return;
          }

       /* get 'orig_flag' */

       if (!command_word(comm, orig_flag_char, 2))
          {
          log_file_date("%s:parse error[orig_flag_char]", mname);
          (void)do_reply_code(SL_INTERNAL_ERROR);
          return;
          }

       if (!qatoi(orig_flag_char, &orig_flag))
          {
          log_file_date("%s:'orig_flag' not numeric", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          return;
          }

       if (orig_flag != 0 && orig_flag != 1)
          {
          log_file_date("%s:'orig_flag' must be 0 or 1", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          return;
          }

       /* get service name */

       if ((len = command_wordlen(comm, 3)) <= 0)
          {
          log_file_date("%s:service name invalid", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          return;
          }

       if ((sname = (char *)malloc(len + 1)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[sname]", mname);
          (void)do_reply_code(SL_MEMORY_FAIL);
          return;
          }

       if (!command_word(comm, sname, 3))
          {
          log_file_date("%s:parse error[sname]", mname);
          (void)do_reply_code(SL_INTERNAL_ERROR);
          return;
          }

       //log_file_date("%s:service name=%s", mname, sname);

       /* get host name */

       if ((len = command_wordlen(comm, 4)) <= 0)
          {
          log_file_date("%s:host name invalid", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          free(sname);
          return;
          }

       if ((hname = (char *)malloc(len + 1)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[hname]", mname);
          (void)do_reply_code(SL_MEMORY_FAIL);
          free(sname);
          return;
          }

       if (!command_word(comm, hname, 4))
          {
          log_file_date("%s:parse error[hname]", mname);
          free(sname);
          free(hname);
          (void)do_reply_code(SL_INTERNAL_ERROR);
          return;
          }

       //log_file_date("%s:host name=%s", mname, hname);

       /* get port number */

       if ((len = command_wordlen(comm, 5)) <= 0)
          {
          log_file_date("%s:port number invalid", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          free(sname);
          free(hname);
          return;
          }

       if ((port_char = (char *)malloc(len + 1)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[port_char]", mname);
          (void)do_reply_code(SL_MEMORY_FAIL);
          free(sname);
          free(hname);
          return;
          }

       if (!command_word(comm, port_char, 5))
          {
          log_file_date("%s:parse error[port_char]", mname);
          free(sname);
          free(hname);
          free(port_char);
          (void)do_reply_code(SL_INTERNAL_ERROR);
          return;
          }

       if (!qatoi(port_char, &port))
          {
          log_file_date("%s:port not numeric", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          free(sname);
          free(hname);
          free(port_char);
          }

       free(port_char);

       /* get ip address (if present) */

       if (command_words(comm) > 5)
          {
          if ((len = command_wordlen(comm, 6)) <= 0)
             {
             log_file_date("%s:ip address invalid", mname);
             (void)do_reply_code(SL_INVALID_PARAMETER);
             free(sname);
             free(hname);
             return;
             }

          if ((ip_ad = (char *)malloc(len + 1)) == (char *)NULL)
             {
             log_file_date("%s:alloc fail[ip_ad]", mname);
             (void)do_reply_code(SL_MEMORY_FAIL);
             free(sname);
             free(hname);
             return;
             }

          if (!command_word(comm, ip_ad, 6))
             {
             log_file_date("%s:parse error[ip_ad]", mname);
             free(sname);
             free(hname);
             free(ip_ad);
             (void)do_reply_code(SL_INTERNAL_ERROR);
             return;
             }

          if (!isip(ip_ad))
             {
             log_file_date("%s:invalid ip address", mname);
             free(sname);
             free(hname);
             free(ip_ad);
             (void)do_reply_code(SL_INVALID_PARAMETER);
             return;
             }
          }
       else
          ip_ad = (char *)NULL;

       ret = s_ll_add(sname, hname, port, ip_ad);

       if ((sockrc = do_reply_code(ret)) != SL_OK)
          log_file_date("%s:bad rc[%d] from 'do_reply_code'", mname, sockrc);

       /* if add was successful and the 'orig_flag' indicates
          a client source, broadcast this addition to
          all other 'socloc' servers */

       if (ret == SL_OK && orig_flag)
          if ((sockrc = sloc_bcast_add(sname, hname, port, ip_ad)) != SL_OK)
             log_file_date("%s:bad rc[%d] from 'sloc_bcast_add'", mname, sockrc);

       if (ip_ad != (char *)NULL)
          free(ip_ad);

       free(sname);
       free(hname);
       s_ll_debug();
       sl_code_string(ret, mess);
       log_file_date("%s:normal exit[%s]", mname, mess);
    }

    void s_delete(char *comm)
    {
       /* Delete a socket server entry.
          Syntax:

             SL_SEND_DELETE orig_flag port_number

        */

       char mname[] = "s_delete";
       char *port_char;
       char orig_flag_char[25], mess[150];
       int orig_flag, port, len, ret, sockrc;

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

       /* must be at least 3 command words */

       if (command_words(comm) < 3)
          {
          log_file_date("%s:incorrect number of parameters", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          return;
          }

       /* get 'orig_flag' */

       if (!command_word(comm, orig_flag_char, 2))
          {
          log_file_date("%s:parse error[orig_flag_char]", mname);
          (void)do_reply_code(SL_INTERNAL_ERROR);
          return;
          }

       if (!qatoi(orig_flag_char, &orig_flag))
          {
          log_file_date("%s:'orig_flag' not numeric", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          return;
          }

       if (orig_flag != 0 && orig_flag != 1)
          {
          log_file_date("%s:'orig_flag' must be 0 or 1", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          return;
          }

       /* get port number */

       if ((len = command_wordlen(comm, 3)) <= 0)
          {
          log_file_date("%s:port number invalid", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          return;
          }

       if ((port_char = (char *)malloc(len + 1)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[port_char]", mname);
          (void)do_reply_code(SL_MEMORY_FAIL);
          return;
          }

       if (!command_word(comm, port_char, 3))
          {
          log_file_date("%s:parse error[port_char]", mname);
          free(port_char);
          (void)do_reply_code(SL_INTERNAL_ERROR);
          return;
          }

       if (!qatoi(port_char, &port))
          {
          log_file_date("%s:port not numeric", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          free(port_char);
          }

       free(port_char);
       ret = s_ll_delete(port);

       if ((sockrc = do_reply_code(ret)) != SL_OK)
          log_file_date("%s:bad rc[%d] from 'do_reply_code'", mname, sockrc);

       /* if delete was successful and the 'orig_flag' indicates
          a client source, broadcast this deletion to all
          other 'socloc' servers */

       if (ret == SL_OK && orig_flag)
          if ((sockrc = sloc_bcast_delete(port)) != SL_OK)
             log_file_date("%s:bad rc[%d] from 'sloc_bcast_delete'", mname, sockrc);

       s_ll_debug();
       sl_code_string(ret, mess);
       log_file_date("%s:normal exit,rc[%s]", mname, mess);
    }

    void s_config_add(char *comm)
    {
       /* Add an entry to the config list of available socloc servers.
          Syntax:

             SL_SEND_CONFIG_ADD orig_flag host_name port_number[ ip_addr]

          Line will be parsed with 'command_word' functions so any
          parameter with a space must be quoted. */

       char mname[] = "s_config_add";
       char *hname, *port_char, *ip_ad;
       char mess[150], orig_flag_char[50];
       int port, len, ret, orig_flag, sockrc;

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

       /* must be at least 4 command words */

       if (command_words(comm) < 4)
          {
          log_file_date("%s:incorrect number of parameters", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          return;
          }

       /* get 'orig_flag' */

       if (!command_word(comm, orig_flag_char, 2))
          {
          log_file_date("%s:parse error[orig_flag_char]", mname);
          (void)do_reply_code(SL_INTERNAL_ERROR);
          return;
          }

       if (!qatoi(orig_flag_char, &orig_flag))
          {
          log_file_date("%s:'orig_flag' not numeric", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          return;
          }

       if (orig_flag != 0 && orig_flag != 1)
          {
          log_file_date("%s:'orig_flag' must be 0 or 1", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          return;
          }

       /* get host name */

       if ((len = command_wordlen(comm, 3)) <= 0)
          {
          log_file_date("%s:host name invalid", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          return;
          }

       if ((hname = (char *)malloc(len + 1)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[hname]", mname);
          (void)do_reply_code(SL_MEMORY_FAIL);
          return;
          }

       if (!command_word(comm, hname, 3))
          {
          log_file_date("%s:parse error[hname]", mname);
          free(hname);
          (void)do_reply_code(SL_INTERNAL_ERROR);
          return;
          }

       //log_file_date("%s:host name=%s", mname, hname);

       /* get port number */

       if ((len = command_wordlen(comm, 4)) <= 0)
          {
          log_file_date("%s:port number invalid", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          free(hname);
          return;
          }

       if ((port_char = (char *)malloc(len + 1)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[port_char]", mname);
          (void)do_reply_code(SL_MEMORY_FAIL);
          free(hname);
          return;
          }

       if (!command_word(comm, port_char, 4))
          {
          log_file_date("%s:parse error[port_char]", mname);
          free(hname);
          free(port_char);
          (void)do_reply_code(SL_INTERNAL_ERROR);
          return;
          }

       if (!qatoi(port_char, &port))
          {
          log_file_date("%s:port not numeric", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          free(hname);
          free(port_char);
          }

       free(port_char);

       /* get ip address (if present) */

       if (command_words(comm) > 4)
          {
          if ((len = command_wordlen(comm, 5)) <= 0)
             {
             log_file_date("%s:ip address invalid", mname);
             (void)do_reply_code(SL_INVALID_PARAMETER);
             free(hname);
             return;
             }

          if ((ip_ad = (char *)malloc(len + 1)) == (char *)NULL)
             {
             log_file_date("%s:alloc fail[ip_ad]", mname);
             (void)do_reply_code(SL_MEMORY_FAIL);
             free(hname);
             return;
             }

          if (!command_word(comm, ip_ad, 5))
             {
             log_file_date("%s:parse error[ip_ad]", mname);
             free(hname);
             free(ip_ad);
             (void)do_reply_code(SL_INTERNAL_ERROR);
             return;
             }

          if (!isip(ip_ad))
             {
             log_file_date("%s:invalid ip address", mname);
             free(hname);
             free(ip_ad);
             (void)do_reply_code(SL_INVALID_PARAMETER);
             return;
             }
          }
       else
          ip_ad = (char *)NULL;

       /* since 'sl_config_add' only checks for duplicate 'socloc'
          ports, we check for duplicate in socket server list  */

       if ((ret = s_ll_port_exist(port)) != SL_OK)
          {
          log_file_date("%s:bad ret from s_ll_port_exist,rc=%d",
                        mname, ret);
          free(hname);
          free(ip_ad);
          (void)do_reply_code(ret);
          return;
          }

       ret = sl_config_add(hname, port, ip_ad);

       if ((sockrc = do_reply_code(ret)) != SL_OK)
          log_file_date("%s:bad rc[%d] from 'do_reply_code'", mname, sockrc);

       if (ret == SL_OK && orig_flag)
          if ((sockrc = sloc_bcast_config_add(hname, port, ip_ad)) != SL_OK)
             log_file_date("%s:bad rc[%d] from 'sloc_bcast_config_add'",
                           mname, sockrc);

       free(hname);

       if (ip_ad != (char *)NULL)
          free(ip_ad);

       sl_code_string(ret, mess);
       log_file_date("%s:normal exit,rc=%s", mname, mess);
       sl_config_debug();
    }

    void s_config_delete(char *comm)
    {
       /* Delete a config socket server entry.
          Syntax:

             SL_SEND_CONFIG_DELETE orig_flag port_number

        */

       char mname[] = "s_config_delete";
       char *port_char;
       char mess[150], orig_flag_char[50];
       int port, len, ret, orig_flag, sockrc;

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

       /* must be at least 3 command words */

       if (command_words(comm) < 3)
          {
          log_file_date("%s:incorrect number of parameters", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          return;
          }

       /* get 'orig_flag' */

       if (!command_word(comm, orig_flag_char, 2))
          {
          log_file_date("%s:parse error[orig_flag_char]", mname);
          (void)do_reply_code(SL_INTERNAL_ERROR);
          return;
          }

       if (!qatoi(orig_flag_char, &orig_flag))
          {
          log_file_date("%s:'orig_flag' not numeric", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          return;
          }

       if (orig_flag != 0 && orig_flag != 1)
          {
          log_file_date("%s:'orig_flag' must be 0 or 1", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          return;
          }

       /* get port number */

       if ((len = command_wordlen(comm, 3)) <= 0)
          {
          log_file_date("%s:port number invalid", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          return;
          }

       if ((port_char = (char *)malloc(len + 1)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[port_char]", mname);
          (void)do_reply_code(SL_MEMORY_FAIL);
          return;
          }

       if (!command_word(comm, port_char, 3))
          {
          log_file_date("%s:parse error[port_char]", mname);
          free(port_char);
          (void)do_reply_code(SL_INTERNAL_ERROR);
          return;
          }

       if (!qatoi(port_char, &port))
          {
          log_file_date("%s:port not numeric", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          free(port_char);
          }

       free(port_char);
       ret = sl_config_delete(port);

       if ((sockrc = do_reply_code(ret)) != SL_OK)
          log_file_date("%s:bad rc[%d] from 'do_reply_code'", mname, sockrc);

       if (ret == SL_OK && orig_flag)
          if ((sockrc = sloc_bcast_config_delete(port)) != SL_OK)
             log_file_date("%s:bad rc[%d] from 'sloc_bcast_config_delete'",
                           mname, sockrc);

       sl_code_string(ret, mess);
       log_file_date("%s:normal exit,rc[%s]", mname, mess);
       sl_config_debug();
    }

    void s_find(char *comm)
    {
       /* Find a socket server by one or more details. All parameters
          are optional but at least one parameter must be supplied. Syntax:

          SL_SEND_FIND [service sname][ host hname][ port port_num][ ip ip_ad]

          Any parameter containing spaces must be quoted. The first server
          matching the supplied parameters will be used.

          Function replies with the following string upon success:

             'service_name' 'host_name' port_number[ ip_addr]

          Note that the string is space delimited. Since there is a
          possibility of the host name having
          spaces, this word will always be quoted. Function will check
          the status of the matching server. If the server is not responding,
          the entry will be deleted from this and all other 'socloc'
          servers. */

       char mname[] = "s_find";
       char *reply, *sname, *hname, *ip_ad;
       char mess[150];
       int pos, nwords, port, len, ret, i, found = FALSE;

       log_file_date("%s:enter", mname);
       // s_ll_debug();
       // s_config_debug();
       nwords = command_words(comm);

       /* must be at least 3 command words */

       if (nwords < 3)
          {
          log_file_date("%s:incorrect number of parameters", mname);
          (void)do_reply_char(SL_INVALID_PARAMETER, "nok");
          return;
          }

       /* alloc details */

       if ((sname = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[sname]", mname);
          (void)do_reply_char(SL_MEMORY_FAIL, "nok");
          return;
          }

       if ((hname = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[hname]", mname);
          (void)do_reply_char(SL_MEMORY_FAIL, "nok");
          free(sname);
          return;
          }

       if ((ip_ad = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[ip_ad]", mname);
          (void)do_reply_char(SL_MEMORY_FAIL, "nok");
          free(sname);
          free(hname);
          return;
          }

       /* set starting position in command string of parameters */

       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--;

       /* loop looking for a match and checking the status of the
          matching server, since the same vars are used from parse
          and match, we re-parse every iteration (sloppy, I know) */

       while(!found)
          {
          sname[0] = EOS;
          hname[0] = EOS;
          ip_ad[0] = EOS;
          port = 0;

          if ((ret = sconnect_parse(&comm[pos], TRUE, sname, hname,
                                    &port, ip_ad)) != SP_OK)
             {
             sp_code_string(ret, mess);
             log_file_date("%s:bad rc[%s] from 'sconnect_parse", mname, mess);
             (void)do_reply_char(SL_INVALID_PARAMETER, mess);
             free(sname);
             free(hname);
             free(ip_ad);
             return;
             }

          if ((ret = s_ll_get_first(sname, hname, &port, ip_ad)) != SL_OK)
             {
             sl_code_string(ret, mess);
             log_file_date("%s:s_ll_get_first failed,rc=%s", mname, mess);
             (void)do_reply_char(ret, mess);
             free(sname);
             free(hname);
             free(ip_ad);
             return;
             }

          log_file_date("%s:found match,hname=%s,port=%d", mname, hname, port);

          /* we have found a match, check the server status */

          if ((ret = sloc_custom_status(hname, port)) != SL_OK)
             {
             sl_code_string(ret, mess);
             log_file_date("%s:bad rc[%s] from 'sloc_custom_status'", mname,
                           mess);

             /* status failed, delete the entry and broadcast
                this delete to all 'socloc' servers */

             if (s_ll_delete(port) == SL_OK)
                if ((i = sloc_bcast_delete(port)) != SL_OK)
                   log_file_date("%s:bad rc[%d] from 'sloc_bcast_delete'",
                                 mname, i);
             }
          else
             found = TRUE;
          }

       len = strlen(sname) + strlen(hname) + strlen(ip_ad) + 25;

       if ((reply = (char *)malloc(len + 1)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[reply]", mname);
          (void)do_reply_char(SL_INTERNAL_ERROR, "nok");
          free(sname);
          free(hname);
          free(ip_ad);
          return;
          }

       if (!strlen(ip_ad))
          sprintf(reply, "'%s' '%s' %d", sname, hname, port);
       else
          sprintf(reply, "'%s' '%s' %d %s", sname, hname, port, ip_ad);

       free(sname);
       (void)do_reply_char(ret, reply);
       free(hname);
       free(ip_ad);
       free(reply);
       sl_code_string(ret, mess);
       log_file_date("%s:normal exit,rc=%s", mname, mess);
    }

    void s_config_find(char *comm)
    {
       /* Find a 'socloc' server by one or more details. At least one
          optional parameter must be present. Syntax:

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

          Any parameter containing spaces must be quoted.

          Function replies with the following string upon success:

             'host_name' port_number[ ip_addr]

          Note that the string is space delimited. Since there is a
          possibility of the host name having spaces, this word will
          always be quoted. Since it is possible that the find will
          match this 'socloc' server, the status of the matched server
          is not checked. */

       char mname[] = "s_config_find";
       char *reply, *hname, *ip_ad;
       char mess[150];
       int pos, nwords, port, len, ret, found = FALSE;

       log_file_date("%s:enter", mname);
       nwords = command_words(comm);

       /* must be at least 3 command words */

       if (nwords < 3)
          {
          log_file_date("%s:incorrect number of parameters", mname);
          (void)do_reply_char(SL_INVALID_PARAMETER, "nok");
          return;
          }

       /* alloc details */

       if ((hname = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[hname]", mname);
          (void)do_reply_char(SL_MEMORY_FAIL, "nok");
          return;
          }

       if ((ip_ad = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[ip_ad]", mname);
          (void)do_reply_char(SL_MEMORY_FAIL, "nok");
          free(hname);
          return;
          }

       /* set starting position in command string of parameters */

       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--;

       hname[0] = EOS;
       ip_ad[0] = EOS;
       port = 0;

       /* detect parameters */

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

       if ((ret = sl_config_find(hname, &port, ip_ad)) != SL_OK)
          {
          sl_code_string(ret, mess);
          log_file_date("%s:bad rc[%s] from 'sl_config_find'", mname, mess);
          (void)do_reply_char(ret, mess);
          free(hname);
          free(ip_ad);
          return;
          }

       log_file_date("%s:found match,hname=%s,port=%d", mname, hname, port);
       len = strlen(hname) + strlen(ip_ad) + 10;

       if ((reply = (char *)malloc(len + 1)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[reply]", mname);
          (void)do_reply_char(SL_INTERNAL_ERROR, "nok");
          free(hname);
          free(ip_ad);
          return;
          }

       if (!strlen(ip_ad))
          sprintf(reply, "'%s' %d", hname, port);
       else
          sprintf(reply, "'%s' %d %s", hname, port, ip_ad);

       (void)do_reply_char(ret, reply);
       free(hname);
       free(ip_ad);
       free(reply);
       sl_code_string(ret, mess);
       log_file_date("%s:normal exit,rc=%s", mname, mess);
    }

    void s_find_list(char *comm)
    {
       /* Find one or more socket servers by one or more details. Syntax:

          SL_SEND_FIND_LIST[ service service_name][ host host_name]
                           [ port port_num][ ip ip_ad]

          Any parameter containing spaces must be quoted. At least one
          keyword and its value must be present. Function will locate and
          build a list of all socket servers matching the details.

          Function replies with a list of socket servers matching. Each
          list entry consists of the following string:

             'host_name' port_number[ ip_addr]

          List entries are delimited by 'SL_LIST_DELIM'. Since there is a
          possibility of the host name having
          spaces, this word will always be quoted. */

       struct sloc_entry *rov = sloc_base;
       char mname[] = "s_find_list";
       char *anentry, *reply, *sname, *hname, *ip_ad;
       char mess[150];
       int pos, nwords, port, len, ret, i, found;
       int nentries = 0, out_pos = 0;

       log_file_date("%s:enter", mname);
       nwords = command_words(comm);

       /* must be at least 3 command words (command code, keyword and value) */

       if (nwords < 3)
          {
          log_file_date("%s:incorrect number of parameters", mname);
          (void)do_reply_char(SL_INVALID_PARAMETER, "nok");
          return;
          }

       /* make sure list is not empty */

       if (rov == SLOC_NULL)
          {
          log_file_date("%s:locate server list empty", mname);
          (void)do_reply_char(SL_NOT_FOUND, "nok");
          return;
          }

       /* alloc details */

       if ((sname = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[sname]", mname);
          (void)do_reply_char(SL_MEMORY_FAIL, "nok");
          return;
          }

       if ((hname = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[hname]", mname);
          (void)do_reply_char(SL_MEMORY_FAIL, "nok");
          free(sname);
          return;
          }

       if ((ip_ad = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[ip_ad]", mname);
          (void)do_reply_char(SL_MEMORY_FAIL, "nok");
          free(sname);
          free(hname);
          return;
          }

       sname[0] = EOS;
       hname[0] = EOS;
       ip_ad[0] = EOS;

       /* 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 details */

       if ((ret = sconnect_parse(&comm[pos], TRUE, sname, hname, &port,
            ip_ad)) != SP_OK)
          {
          sp_code_string(ret, mess);
          log_file_date("%s:bad rc[%s] from 'sconnect_parse", mname, mess);
          free(sname);
          free(hname);
          free(ip_ad);
          (void)do_reply_char(SL_INVALID_PARAMETER, mess);
          return;
          }

       if ((reply = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[reply]", mname);
          (void)do_reply_char(SL_INTERNAL_ERROR, "nok");
          free(sname);
          free(hname);
          free(ip_ad);
          return;
          }

       if ((anentry = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          log_file_date("%s:alloc error[anentry]", mname);
          free(sname);
          free(hname);
          free(ip_ad);
          free(reply);
          (void)do_reply_char(SL_MEMORY_FAIL, "nok");
          return;
          }

       reply[0] = EOS;

       /* loop through list looking for matches */

       while(rov != SLOC_NULL)
          {
          found = TRUE;

          if (strlen(sname))
             if (!strcmp(sname, rov->service_name))
                found = TRUE;
             else
                found = FALSE;

          if (found && strlen(hname))
             if (!strcmp(hname, rov->host_name))
                found = TRUE;
             else
                found = FALSE;

          if (found && port > 0)
             if (port == rov->port_number)
                found = TRUE;
             else
                found = FALSE;

          if (found && ip_ad != (char *)NULL && strlen(ip_ad))
             if (rov->ip_addr == (char *)NULL)
                found = FALSE;
             else
                if (!strlen(rov->ip_addr))
                   found = FALSE;
                else
                   if (!strcmp(ip_ad, rov->ip_addr))
                      found = TRUE;
                   else
                       found = FALSE;

          if (found)
             {
             nentries++;

             if (rov->ip_addr != (char *)NULL && strlen(rov->ip_addr))
                sprintf(anentry, "'%s' '%s' %d %s", rov->service_name,
                        rov->host_name, rov->port_number, rov->ip_addr);
             else
                sprintf(anentry, "'%s' '%s' %d", rov->service_name,
                        rov->host_name, rov->port_number);

             if (nentries > 1)
                reply[out_pos++] = SL_LIST_DELIM;

             /* load this entry into 'reply' */

             len = strlen(anentry);

             for(i = 0; i < len; i++, out_pos++)
                reply[out_pos] = anentry[i];
             }

          rov = rov->next;
          }

       if (!nentries)
          {
          log_file_date("%s:no match", mname);
          (void)do_reply_char(SL_NOT_FOUND, "nok");
          free(sname);
          free(hname);
          free(ip_ad);
          free(reply);
          free(anentry);
          return;
          }

       free(sname);
       free(hname);
       free(ip_ad);
       free(anentry);
       (void)do_reply_char(ret, reply);
       free(reply);
       sl_code_string(ret, mess);
       log_file_date("%s:normal exit,rc[%s]", mname, mess);
    }

    void s_get_list(void)
    {
       /* Get the 'socloc' list. The list is transmitted with
          each entry delimited by 'SL_LIST_DELIM'. Syntax:

             SL_SEND_GET_LIST */

       char mname[] = "s_get_list";
       char *reply, *anentry, *sname, *hname, ip_ad[25];
       int ret, nentries, i, j, port, len, out_pos = 0;

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

       if ((ret = s_ll_get_nentries(&nentries)) != SL_OK)
          {
          log_file_date("%s:bad rc from 's_ll_get_nentries',rc=%d",
                        mname, ret);
          (void)do_reply_char(ret, "nok");
          return;
          }

       if ((reply = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          log_file_date("%s:alloc error[reply]", mname);
          (void)do_reply_char(SL_MEMORY_FAIL, "nok");
          return;
          }

       if ((anentry = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          log_file_date("%s:alloc error[anentry]", mname);
          free(reply);
          (void)do_reply_char(SL_MEMORY_FAIL, "nok");
          return;
          }

       if ((sname = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          log_file_date("%s:alloc error[sname]", mname);
          free(reply);
          free(anentry);
          (void)do_reply_char(SL_MEMORY_FAIL, "nok");
          return;
          }

       if ((hname = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          log_file_date("%s:alloc error[hname]", mname);
          free(reply);
          free(anentry);
          free(sname);
          (void)do_reply_char(SL_MEMORY_FAIL, "nok");
          return;
          }

       reply[0] = EOS;

       for(i = 1; i <= nentries; i++)
          {
          if ((ret = s_ll_get_nth(i, sname, hname, &port, ip_ad)) != SL_OK)
             {
             log_file_date("%s:bad rc from 's_ll_get_nth',entry=%d,rc=%d",
                           mname, i, ret);
             free(reply);
             free(anentry);
             free(sname);
             free(hname);
             (void)do_reply_char(ret, "nok");
             return;
             }

          if (strlen(ip_ad))
             sprintf(anentry, "'%s' '%s' %d %s", sname, hname, port, ip_ad);
          else
             sprintf(anentry, "'%s' '%s' %d", sname, hname, port);

          /* load this entry into 'reply' */

          len = strlen(anentry);

          for(j = 0; j < len; j++, out_pos++)
             reply[out_pos] = anentry[j];

          if (i < nentries)
             reply[out_pos++] = SL_LIST_DELIM;
          }

       reply[out_pos] = EOS;
       free(anentry);
       free(sname);
       free(hname);
       (void)do_reply_char(SL_OK, reply);
       free(reply);
       log_file_date("%s:normal exit,rc=SL_OK", mname);
    }

    void s_put_list(char *comm)
    {
       /* Take the received 'socloc' list and write/put it.
          A list entry is expected in the format:

             sname hname port[ ip_ad]

          Where 'sname' is the service name, 'hname' is the
          host name, port is the TCP port number and the
          optional 'ip_ad' is the IP address. If the
          service name or host name contains spaces, they
          must be quoted.

          Each entry is delimited by 'SL_LIST_DELIM'.
          Syntax:

             SL_SEND_PUT_LIST list */

       char mname[] = "s_put_list";
       char *thelist;
       int pos, len, ret;

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

       if (command_words(comm) < 2)
          {
          log_file_date("%s:no list", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          return;
          }

       /* load just the list */

       pos = command_indxword(comm, 2);

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

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

       len = strlen(comm) - pos;

       if ((thelist = (char *)malloc(len + 1)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[thelist]", mname);
          (void)do_reply_code(SL_MEMORY_FAIL);
          return;
          }

       strcpy(thelist, &comm[pos]);
       ret = s_ll_put_list(thelist);
       free(thelist);
       (void)do_reply_code(ret);
       log_file_date("%s:normal exit reply[%d]", mname, ret);
    }

    void s_config_get_list(void)
    {
       /* Get the 'socloc' config list. The list is transmitted with
          each entry delimited by 'SL_LIST_DELIM'. Syntax:

             SL_SEND_GET_LIST */

       char mname[] = "s_config_get_list";
       char *reply;
       int ret;

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

       if ((reply = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          log_file_date("%s:alloc error[reply]", mname);
          (void)do_reply_char(SL_MEMORY_FAIL, "nok");
          return;
          }

       if ((ret = sl_config_get_list(reply)) != SL_OK)
          (void)do_reply_char(ret, "nok");
       else
          (void)do_reply_char(ret, reply);

       free(reply);
       log_file_date("%s:normal exit,rc=%d", mname, ret);
    }

    void s_config_put_list(char *comm)
    {
       /* Put/write the 'socloc' config list. Syntax:

             SL_SEND_CONFIG_PUT_LIST list

          Where 'list' is one or more delimited entries (delimited
          by 'SL_LIST_DELIM') with each being:

             hname port[ ip_ad]

          where 'hname' is the host name. If it contains spaces, it
          must be quoted. 'Port' is the decimal TCP port number
          and 'ip_ad' is the optional IP address. A reply code
          (without any other value) is sent. */

       char mname[] = "s_config_put_list";
       char *thelist;
       int pos, len, ret;

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

       if (command_words(comm) < 2)
          {
          log_file_date("%s:no list", mname);
          (void)do_reply_code(SL_INVALID_PARAMETER);
          return;
          }

       /* load just the list */

       pos = command_indxword(comm, 2);

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

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

       len = strlen(comm) - pos;

       if ((thelist = (char *)malloc(len + 1)) == (char *)NULL)
          {
          log_file_date("%s:alloc fail[thelist]", mname);
          (void)do_reply_code(SL_MEMORY_FAIL);
          return;
          }

       strcpy(thelist, &comm[pos]);
       ret = sl_config_put_list(thelist);
       free(thelist);
       (void)do_reply_code(ret);
       log_file_date("%s:normal exit,rc=%d", mname, 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;

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

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

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

       if (!strcmp(wrd, SL_TERM_PASSWD))
          {
          (void)do_reply_code(SL_OK);
          return(TRUE);
          }

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

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

             SL_SEND_TRANS_NUM */

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

       log_file_date("%s:enter", mname);
       count = ipc_trans_num();
       (void)do_reply_long(SL_OK, count);
       log_file_date("%s:normal exit,rc=0", mname);
    }

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

             SL_SEND_CONNECT_NUM */

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

       log_file_date("%s:enter", mname);
       count = ipc_connect_num();
       (void)do_reply_int(SL_OK, count);
       log_file_date("%s:normal exit,rc=0", mname);
    }

    /* IPC primatives */

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

       char reply_char[50];
       int anint = 0;

       if (reply_code == SL_OK)
          anint = int_value;

       memset(reply_char, 0, 50);
       sprintf(reply_char, "%d %d", reply_code, anint);
       log_file_date("do_reply_int:reply=%s,l=%d", reply_char,
                     strlen(reply_char));

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

       return(SL_OK);
    }

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

       char reply_char[50];
       int along = 0L;

       if (reply_code == SL_OK)
          along = long_value;

       memset(reply_char, 0, 50);
       sprintf(reply_char, "%d %ld", reply_code, along);
       log_file_date("do_reply_long:reply=%s,l=%d", reply_char, strlen(reply_char));

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

       return(SL_OK);
    }

    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);
       log_file_date("do_reply_code:reply=%s,l=%d", reply_char,
                     strlen(reply_char));

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

       return(SL_OK);
    }

    int do_reply_char(int reply_code, char *char_value)
    {
       /* Send a reply to a request that returns a string as well as
          the reply code. Function returns 'SL_OK' upon success, an
          error code otherwise. */

       char *reply_char;
       int len;

       if (char_value == (char *)NULL)
          return(SL_INVALID_FUNCTION);

       len = strlen(char_value);

       // alloc reply string, note that 'char_value' might be empty

       if ((reply_char = (char *)malloc(len + 10)) == (char *)NULL)
          return(SL_MEMORY_FAIL);

       memset(reply_char, 0, len + 10);
       sprintf(reply_char, "%d %s", reply_code, char_value);
       log_file_date("do_reply_char:reply=%s,l=%d", reply_char, strlen(reply_char));

       if (ipc_send_data(reply_char) == 0)
          {
          free(reply_char);
          return(SL_VC_ERROR);
          }

       free(reply_char);
       return(SL_OK);
    }

    /* low level functions */

    int s_ll_put_list(char *comm)
    {
       /* Take the input 'socloc' list and write/put it.
          A list entry is expected in the format:

             sname hname port[ ip_ad]

          Where 'sname' is the service name, 'hname' is the
          host name, port is the TCP port number and the
          optional 'ip_ad' is the IP address. If the
          service name or host name contains spaces, they
          must be quoted.

          Each entry is delimited by 'SL_LIST_DELIM'. */

       char mname[] = "s_ll_put_list";
       char *entry, *sname, *hname, *port_char, *ip_ad;
       int nentries, nwords, port, len, i, ret;

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

       if (comm == (char *)NULL || !strlen(comm))
          {
          log_file_date("%s:[comm] null or empty", mname);
          return(SL_INVALID_PARAMETER);
          }

       len = strlen(comm);

       /* get number of delimited entries */

       nentries = ll_words(comm, SL_LIST_DELIM);
       log_file_date("%s:found %d entries in input list", mname, nentries);

       if (nentries < 1)
          {
          log_file_date("%s:wacky value from 'll_words'", mname);
          return(SL_INTERNAL_ERROR);
          }

       /* delete the existing list in memory */

       s_ll_delete_all();

       /* add each entry in the list */

       for(i = 1; i <= nentries; i++)
          {
          len = ll_wordlen(comm, i, SL_LIST_DELIM);
          log_file_date("%s:entry[%d]:len=%d", mname, i, len);

          if (len < 1)
             {
             log_file_date("%s:entry[%d]:wacky value from 'll_wordlen", mname, i);
             return(SL_INTERNAL_ERROR);
             }

          if ((entry = (char *)malloc(len + 1)) == (char *)NULL)
             {
             log_file_date("%s:entry[%d]:alloc fail[entry]", mname, i);
             return(SL_MEMORY_FAIL);
             }

          if (!ll_word(comm, entry, i, SL_LIST_DELIM))
             {
             log_file_date("%s:entry[%d]:bad rc from 'll_word'", mname, i);
             free(entry);
             return(SL_INTERNAL_ERROR);
             }

          log_file_date("%s:entry[%d]=%s", mname, i, entry);

          /* must have at least 3 command words in each entry */

          if ((nwords = command_words(entry)) < 3)
             {
             log_file_date("%s:entry[%d]:wrong number of parms", mname, i);
             free(entry);
             return(SL_INVALID_PARAMETER);
             }

          /* since no single entry value can exceed the length of
             the entry, we get sloppy and allocate to length
             of entry */

          if ((sname = initstring(entry)) == (char *)NULL)
             {
             log_file_date("%s:entry[%d]NULL from 'initstring'[sname]", mname, i);
             free(entry);
             return(SL_MEMORY_FAIL);
             }

          if ((hname = initstring(entry)) == (char *)NULL)
             {
             log_file_date("%s:entry[%d]:NULL from 'initstring'[hname]", mname, i);
             free(entry);
             free(sname);
             return(SL_MEMORY_FAIL);
             }

          if ((port_char = initstring(entry)) == (char *)NULL)
             {
             log_file_date("%s:entry[%d]:NULL from 'initstring'[port_char]", mname, i);
             free(entry);
             free(sname);
             free(hname);
             return(SL_MEMORY_FAIL);
             }

          if ((ip_ad = initstring(entry)) == (char *)NULL)
             {
             log_file_date("%s:entry[%d]:NULL from 'initstring'[ip_ad]", mname, i);
             free(entry);
             free(sname);
             free(hname);
             free(port_char);
             return(SL_MEMORY_FAIL);
             }

          sname[0] = EOS;
          hname[0] = EOS;
          port_char[0] = EOS;
          ip_ad[0] = EOS;

          if (!command_word(entry, sname, 1))
             {
             log_file_date("%s:entry[%d]:bad ret from 'command_word'[sname]",
                           mname, i);
             free(entry);
             free(sname);
             free(hname);
             free(port_char);
             free(ip_ad);
             return(SL_INTERNAL_ERROR);
             }

          log_file_date("%s:entry[%d]:service name=%s", mname, i, sname);

          if (!command_word(entry, hname, 2))
             {
             log_file_date("%s:entry[%d]:bad ret from 'command_word'[hname]",
                           mname, i);
             free(entry);
             free(sname);
             free(hname);
             free(port_char);
             free(ip_ad);
             return(SL_INTERNAL_ERROR);
             }

          log_file_date("%s:entry[%d]:host name=%s", mname, i, hname);

          if (!command_word(entry, port_char, 3))
             {
             log_file_date("%s:entry[%d]bad ret from 'command_word'[port_char]",
                           mname, i);
             free(entry);
             free(sname);
             free(hname);
             free(port_char);
             free(ip_ad);
             return(SL_INTERNAL_ERROR);
             }

          log_file_date("%s:entry[%d]:port_char=%s", mname, i, port_char);

          if (!qatoi(port_char, &port))
             {
             log_file_date("%s:entry[%d]:port not numeric", mname, i);
             free(entry);
             free(sname);
             free(hname);
             free(port_char);
             free(ip_ad);
             return(SL_INVALID_PARAMETER);
             }

          free(port_char);

          if (nwords > 3)
             {
             if (!command_word(entry, ip_ad, 4))
                {
                log_file_date("%s:entry[%d]:bad ret from 'command_word'[port_char]",
                              mname, i);
                free(entry);
                free(sname);
                free(hname);
                free(ip_ad);
                return(SL_INTERNAL_ERROR);
                }

             if (!isip(ip_ad))
                {
                log_file_date("%s:entry[%d]:IP address invalid", mname, i);
                free(entry);
                free(sname);
                free(hname);
                free(ip_ad);
                return(SL_INVALID_PARAMETER);
                }

             log_file_date("%s:entry[%d]:ip=%s", mname, i, ip_ad);
             }
          else
             log_file_date("%s:entry[%d]:no ip present", mname, i);

          /* finally, after all that, we are ready to add the entry */

          if ((ret = s_ll_add(sname, hname, port, ip_ad)) != SL_OK)
             {
             log_file_date("%s:entry[%d]:bad rc[%d] from 's_ll_add'", mname,
                           i, ret);
             free(entry);
             free(sname);
             free(hname);
             free(ip_ad);
             return(ret);
             }

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

       log_file_date("%s:normal exit,rc[SL_OK]", mname);
       return(SL_OK);
    }

    /* link list management functions */

    int s_ll_get_first(char *sname, char *hname, int *port_num,
                       char *ip_ad)
    {
       /* Return the first matching socket server.At least one parameter
          must be supplied. Function will locate first match based on
          supplied parameters. 'ip_ad' is the only parameter that may
          be NULL. Upon success, missing details will be loaded. Service
          name, host name and IP address (if present) must be allocated
          to sufficient size by the caller. Function returns 'SL_OK' upon
          success, a socket locate code otherwise. */

       struct sloc_entry *rov = sloc_base;
       char mname[] = "s_ll_get_first";
       int found;

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

       if (sname == (char *)NULL || hname == (char *)NULL ||
           port_num == (int *)NULL)
          {
          log_file_date("%s:null parm", mname);
          return(SL_INVALID_PARAMETER);
          }

       if (!strlen(sname) && !strlen(hname) && *port_num <= 0 &&
           (ip_ad == (char *)NULL || !strlen(ip_ad)))
          {
          log_file_date("%s:at least one parameter must be supplied", mname);
          return(SL_INVALID_PARAMETER);
          }

       if (rov == SLOC_NULL)
          {
          log_file_date("%s:locate server list empty", mname);
          return(SL_NOT_FOUND);
          }

       while(rov != SLOC_NULL)
          {
          found = TRUE;

          if (strlen(sname))
             if (!strcmp(sname, rov->service_name))
                found = TRUE;
             else
                found = FALSE;

          if (found && strlen(hname))
             if (!strcmp(hname, rov->host_name))
                found = TRUE;
             else
                found = FALSE;

          if (found && *port_num > 0)
             if (*port_num == rov->port_number)
                found = TRUE;
             else
                found = FALSE;

          if (found && ip_ad != (char *)NULL && strlen(ip_ad))
             if (rov->ip_addr == (char *)NULL)
                found = FALSE;
             else
                if (!strlen(rov->ip_addr))
                   found = FALSE;
                else
                   if (!strcmp(ip_ad, rov->ip_addr))
                      found = TRUE;
                   else
                       found = FALSE;

          if (found)
             {
             if (!strlen(sname))
                strcpy(sname, rov->service_name);

             if (!strlen(hname))
                strcpy(hname, rov->host_name);

             if (*port_num == 0)
                *port_num = rov->port_number;

             if (ip_ad != (char *)NULL && !strlen(ip_ad))
                if (rov->ip_addr != (char *)NULL && strlen(rov->ip_addr))
                   strcpy(ip_ad, rov->ip_addr);
                else
                   ip_ad[0] = EOS;

             break;
             }
          else
             rov = rov->next;
          }

       if (rov == SLOC_NULL)
          {
          log_file_date("%s:no match", mname);
          return(SL_NOT_FOUND);
          }

       log_file_date("%s:found match", mname);
       return(SL_OK);
    }

    int s_ll_get_nentries(int *nentries)
    {
       /* Get the number of entries in the link list. Function returns
          'SL_OK' with the number of entries loaded into
          'nentries' upon success, a socket locate error
          otherwise. */

       struct sloc_entry *rov = sloc_base;
       char mname[] = "s_ll_get_nentries";
       int cnt = 0;

       log_file_date("%s:enter", mname);
       *nentries = 0;

       while(rov != SLOC_NULL)
          {
          cnt++;
          rov = rov->next;
          }

       if (!cnt)
          {
          log_file_date("%s:locate server list empty", mname);
          return(SL_NOT_FOUND);
          }

       *nentries = cnt;
       log_file_date("%s:normal exit,nentries=%d", mname, cnt);
       return(SL_OK);
    }

    int s_ll_get_nth(int pos, char *sname, char *hname, int *port_num,
                     char *ip_ad)
    {
       /* Get a specific socket server entry. Function returns
          'SL_OK' upon success with the service name loaded into
          'sname', host name loaded into 'hname',
          the port number loaded into 'port_num' and the IP address loaded
          into 'ip_ad'. These character variables must previously be
          allocated to sufficient size by the caller. Function returns
          a socket locate config error upon failure. */

       struct sloc_entry *rov;
       char mname[] = "s_ll_get_nth";
       int i, nentries, ret;

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

       /* get total number of entries and validate */

       if ((ret = s_ll_get_nentries(&nentries)) != SL_OK)
          {
          log_file_date("%s:bad ret from s_ll_get_nentries,rc=%d",
                        mname, ret);
          return(ret);
          }

       if (pos > nentries)
          {
          log_file_date("%s:request out of range", mname);
          return(SL_NOT_FOUND);
          }

       for(rov = sloc_base, i = 1; i <= pos; rov = rov->next, i++)
          if (i == pos)
             {
             strcpy(sname, rov->service_name);
             strcpy(hname, rov->host_name);
             *port_num = rov->port_number;

             /* if the ip address parameter is null, do not attempt
                to load the ip address even if there is one */

             if (ip_ad == (char *)NULL)
                break;

             if (rov->ip_addr != (char *)NULL)
                strcpy(ip_ad, rov->ip_addr);
             else
                /* if no ip address is present in the entry and the
                   ip address parameter is not null, zero out the
                   ip address parameter */

                ip_ad[0] = EOS;

             break;
             }

       log_file_date("%s:normal exit", mname);
       return(SL_OK);
    }

    int s_ll_add(char *sname, char *hname, int port_num, char *ip_ad)
    {
       /* Add a socket server entry. Function returns
          'SL_OK' upon success, a socket locate error code otherwise. */

       struct sloc_entry *sloc;
       struct sloc_entry *rov;
       char mname[] = "s_ll_add";
       int ret, len, size;

       log_file_date("%s:enter", mname);
       size = sizeof(struct sloc_entry);

       /* reasonableness checks */

       if (sname == (char *)NULL || !strlen(sname))
          {
          log_file_date("%s:null or empty service name", mname);
          return(SL_INVALID_PARAMETER);
          }

       if (hname == (char *)NULL || !strlen(hname))
          {
          log_file_date("%s:null or empty host name", mname);
          return(SL_INVALID_PARAMETER);
          }

       if (port_num <= 0)
          {
          log_file_date("%s:port out of range", mname);
          return(SL_INVALID_PARAMETER);
          }

       /* check for duplicate port number */

       if ((ret = s_ll_port_exist(port_num)) != SL_OK)
          {
          log_file_date("%s:bad ret from s_ll_port_exist,rc=%d",
                        mname, ret);
          return(ret);
          }

       if ((sloc = (struct sloc_entry *)malloc(size)) == SLOC_NULL)
          {
          log_file_date("%s:alloc fail[sloc]", mname);
          return(SL_MEMORY_FAIL);
          }

       len = strlen(sname);

       if ((sloc->service_name = (char *)malloc(len + 1)) == (char *)NULL)
          {
          free(sloc);
          log_file_date("%s:alloc fail[sloc->service_name]", mname);
          return(SL_MEMORY_FAIL);
          }

       strcpy(sloc->service_name, sname);
       len = strlen(hname);

       if ((sloc->host_name = (char *)malloc(len + 1)) == (char *)NULL)
          {
          free(sloc->service_name);
          free(sloc);
          log_file_date("%s:alloc fail[sloc->host_name]", mname);
          return(SL_MEMORY_FAIL);
          }

       strcpy(sloc->host_name, hname);
       sloc->port_number = port_num;

       if (ip_ad != (char *)NULL)
          {
          len = strlen(ip_ad);

          if ((sloc->ip_addr = (char *)malloc(len + 1)) == (char *)NULL)
             {
             free(sloc->service_name);
             free(sloc->host_name);
             free(sloc);
             log_file_date("%s:alloc fail[sloc->ip_addr]", mname);
             return(SL_MEMORY_FAIL);
             }

          strcpy(sloc->ip_addr, ip_ad);
          }
       else
          sloc->ip_addr = (char *)NULL;

       sloc->next = SLOC_NULL;

       /* set head of list if this is the first entry */

       if (sloc_base == SLOC_NULL)
          {
          sloc_base = sloc;
          log_file_date("%s:normal exit with head of list", mname);
          return(SL_OK);
          }

       /* find last entry and hook into it */

       rov = sloc_base;

       while(rov->next != SLOC_NULL)
          rov = rov->next;

       rov->next = sloc;
       log_file_date("%s:normal exit", mname);
       return(SL_OK);
    }

    int s_ll_port_exist(int port_num)
    {
       /* Determine whether there already is a socket server or
          'socloc' server using the specified port number. Check
          port used by socket servers and 'socloc' servers.
          Function returns 'SL_OK' if the port does not already
          exist and 'SL_PORT_EXIST' if the port does already
          exist/in use. */

       struct sloc_entry *rov = sloc_base;
       char mname[] = "s_ll_port_exist";
       int ret = SL_OK;

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

       while(rov != SLOC_NULL)
          {
          if (port_num == rov->port_number)
             {
             ret = SL_PORT_EXIST;
             log_file_date("%s:port already in use by socket server", mname);
             break;
             }

           rov = rov->next;
           }

       if (ret == SL_OK)
          if ((ret = sl_config_port_exist(port_num)) != SL_OK)
             log_file_date("%s:port already in use by 'socloc'", mname);

       log_file_date("%s:normal exit,rc=%d", mname, ret);
       return(ret);
    }

    int s_ll_delete(int port_num)
    {
       /* Delete a socket server entry. Since the TCP
          port is unique, this is used as the entry key.
          Function returns 'SL_OK' upon success, a
          socket locate config error otherwise. */

       struct sloc_entry *rov;
       struct sloc_entry *prev;
       char mname[] = "s_ll_delete";

       log_file_date("%s:enter,port=%d", mname, port_num);
       rov = prev = sloc_base;

       while(rov != SLOC_NULL)
          {
          if (port_num == rov->port_number)
             break;
          else
             {
             prev = rov;
             rov = rov->next;
             }
          }

       /* if no match, error */

       if (rov == SLOC_NULL)
          {
          log_file_date("%s:exit,no match", mname);
          return(SL_NOT_FOUND);
          }

       /* if we are deleting the head entry, set new head */

       if (rov == sloc_base)
          sloc_base = rov->next;
       else
          prev->next = rov->next;

       free(rov->service_name);
       free(rov->host_name);

       if (rov->ip_addr != (char *)NULL)
          free(rov->ip_addr);

       free(rov);
       s_ll_debug();
       log_file_date("%s:normal exit,rc[0]", mname);
       return(SL_OK);
    }

    void s_ll_delete_all(void)
    {
       /* Delete all entries. */

       struct sloc_entry *rov = sloc_base;
       struct sloc_entry *tmp;
       char mname[] = "s_ll_delete_all";

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

       while(rov != SLOC_NULL)
          {
          free(rov->service_name);
          free(rov->host_name);

          if (rov->ip_addr != (char *)NULL)
             free(rov->ip_addr);

          tmp = rov;
          rov = rov->next;
          free(tmp);
          }

       log_file_date("%s:normal exit", mname);
       sloc_base = SLOC_NULL;
    }

    void s_ll_debug(void)
    {
       /* Print the contents of the link list. Log file is
          forced active. */

       struct sloc_entry *rov = sloc_base;
       char mname[] = "s_ll_debug";
       int i = 1, islog;

       if ((islog = is_log_active()) == FALSE)
          log_start(s_log_file);

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

       while(rov != SLOC_NULL)
          {
          log_file_date("%s[%d]:sname='%s',host='%s',port=%d,ip='%s'",
                        mname, i, rov->service_name, rov->host_name,
                        rov->port_number, rov->ip_addr);
          rov = rov->next;
          i++;
          }

       if (i == 1)
          log_file_date("%s:list empty", mname);

       log_file_date("%s:normal exit", mname);

       if (!islog)
          log_end();
    }

    void s_config_debug(void)
    {
       /* Print the contents of the config link list. Log file is
          forced active. */

       int islog;

       if ((islog = is_log_active()) == FALSE)
          log_start(s_log_file);

       sl_config_debug();

       if (!islog)
          log_end();
    }

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

       (void)sloc_bcast_config_delete(server_port);
       s_ll_delete_all();
    #ifdef OS_WIN32
       WSACleanup();
    #endif

       log_file_date("term_app:server down");

       if (is_log_active())
          log_end();
    }

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