## root/socloc/sloc.c

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

## DEFINITIONS

This source file includes following definitions.

1. sloc_initialize
2. sloc_end
3. sloc_log_off
4. sloc_log_on
5. sloc_log_status
6. sloc_add
7. sloc_config_add
8. sloc_config_delete
9. sloc_status
10. sloc_service_name
11. sloc_version
12. sloc_delete
13. sloc_find
14. sloc_config_find
15. sloc_find_list
16. sloc_ll_find
17. sloc_ll_find_list
18. sloc_get_list
19. sloc_put_list
20. sloc_config_update
21. sloc_config_get_list
22. sloc_config_put_list
23. sloc_dump_debug
24. sloc_config_dump_debug
25. sloc_terminate
26. sloc_get_active_socloc
27. sloc_set_active_socloc
28. sloc_is_init
29. sloc_trans_num
30. sloc_connect_num
31. sloc_term_api
32. sloc_connect
33. sloc_sr_code
34. sloc_lsr_code
35. sloc_sr_char
36. sloc_lsr_char
37. sloc_sr_int
38. sloc_lsr_int
39. sloc_sr_long
40. sloc_lsr_long
41. sloc_failover
42. sloc_reload_config_list
43. sloc_client_connect
44. sloc_ll_client_connect

    /* A high level client api module for the socket locate service.
       Applies to TCP socket IPC method only.
       Rick Smereka, Copyright (C) 2000-2005.

       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 API is used by both 'socloc' server and client applications.

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

       Changed function 'sloc_log_status' to accept the returned log
       file name in addition to the code. Mar/2000, Rick Smereka

       Corrected 'sloc_client_connect' to obtain the first config
       entry using the function 'sl_config_get_first'. Modified function
       'sloc_failover' to save the current 'spos' count before entering
       the 'sloc_config_delete' loop. Jun/2000, Rick Smereka

       Added include of header 'ip.h' after moving the function 'isip'.
       Jul/2000, Rick Smereka

       Changed name of module global 'sockClientAddr' to
       'slsockClientAddr' to avoid duplicate module global names.
       Jul/2001, Rick Smereka

       Ported to Debian Linux. Nov/2002, Rick Smereka

       Corrected bug in TCP socket connect to the server. The TCP
       socket 'connect' function was being called for each request
       sent to the server.  This caused the socket descriptor pool
       to become exhausted under some Unix's when a large number
       of transactions were being processed. This behavior has been
       changed to connect to the server only upon initialization or
       previous send/receive socket error. This means that the socket
       stays connected even when not in use. It is up to the
       developer to call 'sloc_end' when the socket is no longer
       required.

       The 'socloc' server no longer uses this API. A new API called
       'slocsrv.c' has been created specifically for the server.

       This API will properly talk only to socloc server's that are using
       the new 'ipcsrv' (GPL package version 1.33 or higher).

       Added functions 'sloc_trans_num', 'sloc_connect_num' and
       'sloc_term_api'. May/2003, Rick Smereka

       Changed all logging calls from 'log_file_date' to 'logman'.
       Mar/2004, Rick Smereka

       Fixed bug in 'sloc_ll_find' that caused the system logger
       not to record the server reply in the log file. Reply needed
       double quotes around it. Removed this change as this fix
       did not help. Jan/2005, Rick Smereka */

    #include "stdhead.h"          // standard include
    #include "flsocket.h"         // standard socket defines
    #include "ipcomm.h"           // low level socket send/receive
    #include "sloc.h"             // defines for this module
    #include "socloc.h"           // global socket locator defines
    #include "slconfig.h"         // socket locate config management
    #include "sconnect.h"         // connect string parse/build API
    #include "ip.h"               // IP library routines

    // global (to module) data

    #ifdef OS_WIN32
    SOCKET slclientSocket;        // client socket
    SOCKADDR_IN slsockClientAddr; // client address structure
    LPHOSTENT lpHostEnt;          // host info structure
    #endif

    #ifdef OS_UNIX
    int slclientSocket;
    struct sockaddr_in slsockClientAddr;
    struct hostent *lpHostEnt;
    #endif

    char sloc_hostname[128];      // host name of 'socloc' server
    int sloc_port = 0;            // port 'socloc' is using

    // private functions

    static int sloc_connect(char *, int);
    static int sloc_sr_code(int, char *);
    static int sloc_lsr_code(int, char *);
    static int sloc_sr_char(int, char *, char *);
    static int sloc_lsr_char(int, char *, char *);
    static int sloc_sr_int(int, char *, int *);
    static int sloc_lsr_int(int, char *, int *);
    static int sloc_sr_long(int, char *, long *);
    static int sloc_lsr_long(int, char *, long *);
    static int sloc_failover(void);
    static int sloc_reload_config_list(void);
    static int sloc_client_connect(void);
    static int sloc_ll_client_connect(void);

    int sloc_initialize(void)
    {
       /* Initialize the client for communication with the
          socket locate server. An attempt to get the server status
          is made. Nodeid (host name), port number and optionally the
          IP address are expected in the configuration file
          'SL_CONFIG_FILE_NAME'. The format of this file is discussed
          in 'slconfig.c'. Function returns 'SL_OK' if a successful
          connection was made, a socket locate code otherwise. */

       char mname[] = "sloc_initialize";
       char hname[SL_MAXCOMMAND];
       int ret, port;

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

       if ((ret = sl_config_read(SL_CONFIG_FILE_NAME)) != SL_OK)
          {
          logman("%s:bad rc from 'sl_config_read',rc[%d]", mname, ret);
          return(ret);
          }

       // establish a connection to the first responding 'socloc' server

       if ((ret = sloc_client_connect()) != SL_OK)
          {
          logman("%s:bad rc[%d] from sloc_client_connect", mname, ret);
          return(ret);
          }

       if ((ret = sloc_status()) != SL_OK)
          {
          logman("%s:bad rc from 'status',rc[%d]", mname, ret);
          return(ret);
          }

       logman("%s:normal exit,server responding is '%s' on "
                     "port %d", mname, sloc_hostname, sloc_port);
       return(SL_OK);
    }

    void sloc_end(void)
    {
       /* Stop communication with a 'socloc' server. The client socket
          is closed and the 'socloc' server global details are wiped. */

       char mname[] = "sloc_end";

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

       // if there are no 'socloc' server details, assume no connection

       if (sloc_is_init() != SL_OK)
          return;

    #ifdef OS_WIN32
       (void)closesocket(slclientSocket);
    #else
       close(slclientSocket);
    #endif

       sloc_port = 0;
       sloc_hostname[0] = EOS;
       logman("%s:normal exit", mname);
    }

    int sloc_log_off(void)
    {
       /* Turn 'socloc' server logging off.
          Function returns 'SL_OK' upon success, an error code
          otherwise. */

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

       logman("%s:enter,host=%s,port=%d", mname, sloc_hostname,
                     sloc_port);
       ret = sloc_sr_code(SL_SEND_LOG_OFF, (char *)NULL);
       logman("%s:normal exit,rc[%d]", mname, ret);
       return(ret);
    }

    int sloc_log_on(char *lname)
    {
       /* Turn 'socloc; server logging on. Log file name 'lname'
          is optional. Function returns 'SL_OK'
          upon success, a 'socloc' code otherwise. */

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

       logman("%s:enter", mname);
       ret = sloc_sr_code(SL_SEND_LOG_ON, lname);
       logman("%s:normal exit,rc[%d]", mname, ret);
       return(ret);
    }

    int sloc_log_status(int *lflag, char *lfname)
    {
       /* Get staus of 'socloc' server logging. Function returns
          the log status (0=off,1=on) in 'lflag' and the current
          log file name in 'lfname' upon success. Function returns a 'socloc'
          code. */

       char mname[] = "sloc_log_status";
       char reply[128], flag_char[25];
       int ret, flag;

       logman("%s:enter,host=%s,port=%d", mname, sloc_hostname,
                     sloc_port);

       if (lflag == (int *)NULL)
          {
          logman("%s:null[lflag]", mname);
          return(SL_INVALID_PARAMETER);
          }

       if (lfname == (char *)NULL)
          {
          logman("%s:null[lfname]", mname);
          return(SL_INVALID_PARAMETER);
          }

       lfname[0] = EOS;
       *lflag = 0;

       if ((ret = sloc_sr_char(SL_SEND_LOG_STATUS, (char *)NULL, reply))
           != SL_OK)
          {
          logman("%s:bad rc[%d] from sloc_sr_char", mname, ret);
          return(ret);
          }

       if (!command_word(reply, flag_char, 1))
          {
          logman("%s:error getting flag_char", mname);
          return(SL_INTERNAL_ERROR);
          }

       if (!qatoi(flag_char, &flag))
          {
          logman("%s:status flag is non-numeric", mname);
          return(SL_INTERNAL_ERROR);
          }

       if (flag != 0 && flag != 1)
          {
          logman("%s:status flag has incorrect value", mname);
          return(SL_INTERNAL_ERROR);
          }

       *lflag = flag;

       if (!command_word(reply, lfname, 2))
          {
          logman("%s:error getting lfname", mname);
          return(SL_INTERNAL_ERROR);
          }

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

    int sloc_add(char *sname, char *hname, int port, char *ip_ad)
    {
       /* Add a socket server entry to 'socloc'. The IP address ('ip_ad')
          may be null but if it is not null, it must be a valid
          IP address. Function returns 'SL_OK' upon success,
          a 'socloc' code otherwise. */

       char mname[] = "sloc_add";
       char *parm, *tsname, *thname;
       int len, ret;

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

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

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

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

       /* make sure IP address (if present) is valid */

       if (ip_ad != (char *)NULL)
          if (!isip(ip_ad))
             {
             logman("%s:invalid ip address", mname);
             return(SL_BAD_IP);
             }

       /* calc total length of parameter
          length of service name + host name + port + ip_ad + 5 */

       len = strlen(sname) + strlen(hname) + 30;

       if ((parm = (char *)malloc(len)) == (char *)NULL)
          {
          logman("%s:alloc fail[parm]", mname);
          return(SL_MEMORY_FAIL);
          }

       /* load each string into tmp strings, expanding
          string with quotes if spaces are present */

       if ((tsname = (char *)malloc(len)) == (char *)NULL)
          {
          logman("%s:alloc fail[tsname]", mname);
          free(parm);
          return(SL_MEMORY_FAIL);
          }

       if (words(sname) > 1)
          sprintf(tsname, "'%s'", sname);
       else
          strcpy(tsname, sname);

       if ((thname = (char *)malloc(len)) == (char *)NULL)
          {
          logman("%s:alloc fail[thname]", mname);
          free(parm);
          free(tsname);
          return(SL_MEMORY_FAIL);
          }

       if (words(hname) > 1)
          sprintf(thname, "'%s'", hname);
       else
          strcpy(thname, hname);

       /* build final parameter string, we send a one as the
          first parameter to indicate the originating client */

       if (ip_ad != (char *)NULL)
          sprintf(parm, "1 %s %s %d %s", tsname, thname, port, ip_ad);
       else
          sprintf(parm, "1 %s %s %d", tsname, thname, port);

       free(tsname);
       free(thname);
       logman("%s:parm=%s", mname, parm);
       ret = sloc_sr_code(SL_SEND_ADD, parm);
       logman("%s:normal exit[%d]", mname, ret);
       free(parm);
       return(ret);
    }

    int sloc_config_add(char *hname, int port, char *ip_ad)
    {
       /* Add a config entry to 'socloc'. The IP address ('ip_ad')
          may be null. Function returns 'SL_OK' upon success,
          a 'socloc' code otherwise. */

       char mname[] = "sloc_config_add";
       char *parm, *thname;
       int len, ret;

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

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

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

       /* make sure IP address (if present) is valid */

       if (ip_ad != (char *)NULL)
          if (!isip(ip_ad))
             {
             logman("%s:invalid ip address", mname);
             return(SL_BAD_IP);
             }

       /* calc total length of parameter
          length of host name + port + ip_ad + 5 */

       len = strlen(hname) + 30;

       if ((parm = (char *)malloc(len)) == (char *)NULL)
          {
          logman("%s:alloc fail[parm]", mname);
          return(SL_MEMORY_FAIL);
          }

       /* load host name into tmp strings, expanding
          string with quotes if spaces are present */

       if ((thname = (char *)malloc(len)) == (char *)NULL)
          {
          logman("%s:alloc fail[thname]", mname);
          free(parm);
          return(SL_MEMORY_FAIL);
          }

       if (words(hname) > 1)
          sprintf(thname, "'%s'", hname);
       else
          strcpy(thname, hname);

       /* build final parameter string, we send a one as the
          first parameter to indicate the originating client */

       if (ip_ad != (char *)NULL)
          sprintf(parm, "1 %s %d %s", thname, port, ip_ad);
       else
          sprintf(parm, "1 %s %d", thname, port);

       free(thname);
       ret = sloc_sr_code(SL_SEND_CONFIG_ADD, parm);
       logman("%s:exit[%d]:parm=%s", mname, ret, parm);
       free(parm);
       return(ret);
    }

    int sloc_config_delete(int port)
    {
       /* Delete a config 'socloc' entry. Function returns 'SL_OK'
          upon success, a 'socloc' code otherwise. */

       char mname[] = "sloc_config_delete";
       char parm[25];
       int ret;

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

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

       sprintf(parm,"1 %d", port);
       ret = sloc_sr_code(SL_SEND_CONFIG_DELETE, parm);
       logman("%s:normal exit[%d]", mname, ret);
       return(ret);
    }

    int sloc_status(void)
    {
       /* Get 'socloc' server status.
          Function returns 'SL_OK' upon success, an error code
          otherwise. */

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

       logman("%s:enter,host=%s,port=%d", mname, sloc_hostname,
                     sloc_port);
       ret = sloc_sr_code(SL_SEND_STATUS, (char *)NULL);
       logman("%s:normal exit[%d]", mname, ret);
       return(ret);
    }

    int sloc_service_name(char *sname)
    {
       /* Get the service name of a 'socloc' server.
          Upon success, the service name will be loaded into
          'sname' which must already be allocated to sufficient
          size. Function returns a 'socloc' code. */

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

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

       if (sname == (char *)NULL)
          {
          logman("%s:null[sname]", mname);
          return(SL_INVALID_PARAMETER);
          }

       if ((ret = sloc_sr_char(SL_SEND_SERVICE_NAME, (char *)NULL, sname))
           != SL_OK)
          {
          logman("%s:rc[%d] from sloc_sr_char", mname, ret);
          return(ret);
          }

       return(SL_OK);
    }

    int sloc_version(char *version)
    {
       /* Get the version of a 'socloc' server.
          Upon success, the version string will be loaded into
          'version' which must already be allocated to sufficient
          size. Function returns a 'socloc' code. */

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

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

       if (version == (char *)NULL)
          {
          logman("%s:null[version]", mname);
          return(SL_INVALID_PARAMETER);
          }

       if ((ret = sloc_sr_char(SL_SEND_VERSION, (char *)NULL, version))
           != SL_OK)
          {
          logman("%s:rc[%d] from sloc_sr_char", mname, ret);
          return(ret);
          }

       return(SL_OK);
    }

    int sloc_delete(int port)
    {
       /* Delete a socket server entry. Function returns 'SL_OK'
          upon success, a 'socloc' code otherwise. */

       char mname[] = "sloc_delete";
       char parm[25];
       int ret;

       logman("%s:enter,port=%d", mname, port);

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

       /* indicate a client send with 'orig_flag' set to one */

       sprintf(parm,"1 %d", port);
       ret = sloc_sr_code(SL_SEND_DELETE, parm);
       logman("%s:normal exit[%d]", mname, ret);
       return(ret);
    }

    int sloc_find(char *sname, char *hname, int *port, char *ip_ad)
    {
       /* Find a 'socloc' entry. Only the service name is used.
          The IP address ('ip_ad') may be null. Upon success, details
          are loaded into 'hname', 'port' and optionally 'ip_ad'.
          Function returns a 'socloc' code. */

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

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

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

       if (hname == (char *)NULL)
          {
          logman("%s:null[hname]", mname);
          return(SL_INVALID_PARAMETER);
          }

       if (port == (int *)NULL)
          {
          logman("%s:null[port]", mname);
          return(SL_INVALID_PARAMETER);
          }

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

       if (ip_ad != (char *)NULL)
          ip_ad[0] = EOS;

       ret = sloc_ll_find(sname, hname, port, ip_ad);
       logman("%s:normal exit[%d]", mname, ret);
       return(ret);
    }

    int sloc_config_find(char *hname, int *port, char *ip_ad)
    {
       /* Find a 'socloc' config entry by one or more details.
          At least one detail must be present.Upon success,
          missing details are loaded into 'hname', 'port'
          and optionally 'ip_ad'. Function returns a 'socloc' code. */

       char mname[] = "sloc_config_find";
       char *buildstr, *reply, mes[128];
       int ret, ishname, isport, isip;

       logman("%s:enter", mname);
       ishname = isport = isip = TRUE;

       if (hname == (char *)NULL || !strlen(hname))
          ishname = FALSE;

       if (port == (int *)NULL || *port <= 0)
          isport = FALSE;

       if (ip_ad == (char *)NULL || !strlen(ip_ad))
          isip = FALSE;

       if (!ishname && !isport && !isip)
          {
          logman("%s:no parameters", mname);
          return(SL_INVALID_PARAMETER);
          }

       /* use 'sconnect_build' to form the connect string */

       if ((buildstr = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          logman("%s:alloc fail[buildstr]", mname);
          return(SL_MEMORY_FAIL);
          }

       if ((ret = sconnect_build((char *)NULL, hname, port, ip_ad, buildstr))
            != SP_OK)
          {
          free(buildstr);
          sp_code_string(ret, mes);
          logman("%s:bad rc[%s] from sconnect_build", mname, mes);
          return(SL_INVALID_PARAMETER);
          }

       if ((reply = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          logman("%s:alloc fail[reply]", mname);
          free(buildstr);
          return(SL_MEMORY_FAIL);
          }

       logman("%s:parm=%s,l=%d", mname, buildstr, strlen(buildstr));
       ret = sloc_sr_char(SL_SEND_CONFIG_FIND, buildstr, reply);
       free(buildstr);

       if (ret != SL_OK)
          {
          logman("%s:exit bad rc[%d]", mname, ret);
          free(reply);
          return(ret);
          }

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

       /* take apart the return string and load only the missing pieces */

       if (command_words(reply) < 2)
          {
          logman("%s:incorrect number of words returned", mname);
          free(reply);
          return(SL_INTERNAL_ERROR);
          }

       if ((buildstr = (char *)malloc(strlen(reply) + 1)) == (char *)NULL)
          {
          logman("%s:alloc fail[buildstr]", mname);
          free(reply);
          return(SL_MEMORY_FAIL);
          }

       if (!ishname && hname != (char *)NULL)
          if (!command_word(reply, hname, 1))
             {
             logman("%s:error getting host name", mname);
             free(reply);
             free(buildstr);
             return(SL_INTERNAL_ERROR);
             }

       if (!isport && port != (int *)NULL)
          {
          if (!command_word(reply, buildstr, 2))
             {
             logman("%s:error getting port char", mname);
             free(reply);
             free(buildstr);
             return(SL_INTERNAL_ERROR);
             }

          if (!qatoi(buildstr, port))
             {
             logman("%s:server returned port that is not a number", mname);
             free(reply);
             free(buildstr);
             return(SL_INTERNAL_ERROR);
             }
          }

       if (command_words(reply) > 2)
          if (!isip && ip_ad != (char *)NULL)
             if (!command_word(reply, ip_ad, 3))
                {
                logman("%s:error getting ip address", mname);
                free(reply);
                free(buildstr);
                return(SL_INTERNAL_ERROR);
                }

       free(reply);
       free(buildstr);
       logman("%s:normal exit[0]", mname);
       return(SL_OK);
    }

    int sloc_find_list(char *sname, char *list_out)
    {
       /* Find one or more 'socloc' entries by service name. All entries
          matching the service name will be loaded into 'list_out'.
          The list is delimited by 'SL_LIST_DELIM'. Function returns
          a 'socloc' code. */

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

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

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

       if (list_out == (char *)NULL)
          {
          logman("%s:null[list_out]", mname);
          return(SL_INVALID_PARAMETER);
          }

       ret = sloc_ll_find_list(sname, (char *)NULL, (int *)NULL, (char *)NULL,
                               list_out);
       logman("%s:normal exit[%d]", mname, ret);
       return(ret);
    }

    int sloc_ll_find(char *sname, char *hname, int *port, char *ip_ad)
    {
       /* Find a 'socloc' entry. All parameters are optional. At least
          one parameter must be specified. The lookup will use all
          supplied parameters to perform the lookup. If you just want
          to find by service name, do not call this function, use
          'sloc_find' instead. Upon success, the missing items will
          be loaded. Function returns 'SL_OK' upon success, a
          'socloc' code otherwise. */

       char mname[] = "sloc_ll_find";
       char *buildstr, *parm, *wrd, mes[128];
       int ret;

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

       if (sname == (char *)NULL)
          {
          logman("%s:null[sname]", mname);
          return(SL_INVALID_PARAMETER);
          }

       if (hname == (char *)NULL)
          {
          logman("%s:null[hname]", mname);
          return(SL_INVALID_PARAMETER);
          }

       if (port == (int *)NULL)
          {
          logman("%s:null[port]", mname);
          return(SL_INVALID_PARAMETER);
          }

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

       /* use 'sconnect_build' to form the connect string */

       if ((buildstr = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          logman("%s:alloc fail[buildstr]", mname);
          return(SL_MEMORY_FAIL);
          }

       if ((ret = sconnect_build(sname, hname, port, ip_ad, buildstr))
            != SP_OK)
          {
          free(buildstr);
          sp_code_string(ret, mes);
          logman("%s:bad rc[%s] from sconnect_build", mname, mes);
          return(SL_INVALID_PARAMETER);
          }

       if ((wrd = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          logman("%s:alloc fail[wrd]", mname);
          free(buildstr);
          return(SL_MEMORY_FAIL);
          }

       logman("%s:buildstr=%s,l=%d", mname, buildstr, strlen(buildstr));
       ret = sloc_sr_char(SL_SEND_FIND, buildstr, wrd);
       free(buildstr);

       if (ret != SL_OK)
          {
          logman("%s:exit bad rc[%d]", mname, ret);
          free(wrd);
          return(ret);
          }

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

       /* take apart the return string and load only the missing pieces */

       if (command_words(wrd) < 2)
          {
          logman("%s:incorrect number of words returned", mname);
          logman("%s:wrd=%s,l=%d", mname, wrd, strlen(wrd));
          free(wrd);
          return(SL_INTERNAL_ERROR);
          }

       if ((parm = (char *)malloc(strlen(wrd) + 1)) == (char *)NULL)
          {
          logman("%s:alloc fail[parm]", mname);
          free(wrd);
          return(SL_MEMORY_FAIL);
          }

       if (!strlen(sname))
          if (!command_word(wrd, sname, 1))
             {
             logman("%s:error getting service name", mname);
             free(wrd);
             free(parm);
             return(SL_INTERNAL_ERROR);
             }

       if (!strlen(hname))
          if (!command_word(wrd, hname, 2))
             {
             logman("%s:error getting host name", mname);
             free(wrd);
             free(parm);
             return(SL_INTERNAL_ERROR);
             }

       if (*port == 0)
          {
          if (!command_word(wrd, parm, 3))
             {
             logman("%s:error getting port char", mname);
             free(wrd);
             free(parm);
             return(SL_INTERNAL_ERROR);
             }

          if (!qatoi(parm, port))
             {
             logman("%s:server returned port that is not a number", mname);
             free(wrd);
             free(parm);
             return(SL_INTERNAL_ERROR);
             }
          }

       if (command_words(wrd) > 3)
          if (ip_ad != (char *)NULL && !strlen(ip_ad))
             if (!command_word(wrd, ip_ad, 4))
                {
                logman("%s:error getting ip address", mname);
                free(wrd);
                free(parm);
                return(SL_INTERNAL_ERROR);
                }

       free(wrd);
       free(parm);
       logman("%s:normal exit[0]", mname);
       return(SL_OK);
    }

    int sloc_ll_find_list(char *sname, char *hname, int *port, char *ip_ad,
                          char *list_out)
    {
       /* Find one or more 'socloc' entries. All parameters are optional
          except 'list_out' which is the output list. The output list is
          delimited by 'SL_LIST_DELIM' The lookup will use all supplied
          parameters to perform the lookup. If you just want to find a list
          of socket servers by service name, do not call this function,
          use 'sloc_find_list' instead. Function returns
          'SL_OK' upon success, a 'socloc' code otherwise. */

       char mname[] = "sloc_ll_find_list";
       char *parm, mes[128];
       int ret;

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

       if (sname == (char *)NULL && hname == (char *)NULL && port == (int *)NULL
           && ip_ad == (char *)NULL)
          {
          logman("%s:all null parameters", mname);
          return(SL_INVALID_PARAMETER);
          }

       if (list_out == (char *)NULL)
          {
          logman("%s:null[list_out]", mname);
          return(SL_INVALID_PARAMETER);
          }

       /* use 'sconnect_build' to form the connect string */

       if ((parm = (char *)malloc(SL_MAXCOMMAND)) == (char *)NULL)
          {
          logman("%s:alloc fail[parm]", mname);
          return(SL_MEMORY_FAIL);
          }

       if ((ret = sconnect_build(sname, hname, port, ip_ad, parm))
            != SP_OK)
          {
          free(parm);
          sp_code_string(ret, mes);
          logman("%s:bad rc[%s] from sconnect_build", mname, mes);
          return(SL_INVALID_PARAMETER);
          }

       logman("%s:parm=%s,l=%d", mname, parm, strlen(parm));
       ret = sloc_sr_char(SL_SEND_FIND_LIST, parm, list_out);
       logman("%s:reply list_out=%s,%d", mname, list_out,
                     strlen(list_out));
       free(parm);
       logman("%s:normal exit[%d]", mname, ret);
       return(ret);
    }

    int sloc_get_list(char *list_out)
    {
       /* Get the 'socloc' list. The delimited list is
          returned in 'list_out' which must already be allocated
          by the caller to sufficient size. Function returns
          'SL_OK' upon success, a 'socloc' code otherwise. */

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

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

       if (list_out == (char *)NULL)
          {
          logman("%s:null[list_out]", mname);
          return(SL_INVALID_PARAMETER);
          }

       ret = sloc_sr_char(SL_SEND_GET_LIST, (char *)NULL, list_out);
       logman("%s:normal exit[%d]", mname, ret);
       return(ret);
    }

    int sloc_put_list(char *list_in)
    {
       /* Put/write a 'socloc' list to a server. The delimited
          list (delimited by 'SL_LIST_DELIM') is expected in
          'list_in'. Function returns 'SL_OK' upon success,
          a 'socloc' code otherwise. */

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

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

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

       ret = sloc_sr_code(SL_SEND_PUT_LIST, list_in);
       logman("%s:normal exit[%d]", mname, ret);
       return(ret);
    }

    int sloc_config_update(void)
    {
       /* Get the current config list from the first active
          'socloc' server and use this list to update the
          config list in memory and write a new config file.
          Function returns 'SL_OK' upon success, a 'socloc'
          code otherwise. */

       char mname[]= "sloc_config_update";
       char *thelist;
       int ret;

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

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

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

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

       /* create a new list in memory */

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

       free(thelist);

       /* write this new list to the config file */

       if ((ret = sl_config_write_file()) != SL_OK)
          {
          logman("%s:bad rc[%d] from sl_config_write_file", mname, ret);
          return(ret);
          }

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

    int sloc_config_get_list(char *list_out)
    {
       /* Get the 'socloc' config list. The delimited list is
          returned in 'list_out' which must already be allocated
          by the caller to sufficient size. Function returns
          'SL_OK' upon success, a 'socloc' code otherwise. */

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

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

       if (list_out == (char *)NULL)
          {
          logman("%s:null[list_out]", mname);
          return(SL_INVALID_PARAMETER);
          }

       ret = sloc_sr_char(SL_SEND_CONFIG_GET_LIST, (char *)NULL, list_out);
       logman("%s:normal exit[%d]", mname, ret);
       return(ret);
    }

    int sloc_config_put_list(char *list_in)
    {
       /* Put/write a 'socloc' config list to a server. The delimited
          list (delimited by 'SL_LIST_DELIM') is expected in
          'list_in'. Function returns 'SL_OK' upon success,
          a 'socloc' code otherwise. */

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

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

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

       ret = sloc_sr_code(SL_SEND_CONFIG_PUT_LIST, list_in);
       logman("%s:normal exit[%d]", mname, ret);
       return(ret);
    }

    int sloc_dump_debug(void)
    {
       /* Command server to display and log debug information.
          Function returns 'SL_OK' upon success, an error code
          otherwise. */

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

       logman("%s:enter,host=%s,port=%d", mname, sloc_hostname,
                     sloc_port);
       ret = sloc_sr_code(SL_SEND_DUMP_DEBUG, (char *)NULL);
       logman("%s:normal exit[%d]", mname, ret);
       return(ret);
    }

    int sloc_config_dump_debug(void)
    {
       /* Command server to display and log config debug information.
          Function returns 'SL_OK' upon success, an error code
          otherwise. */

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

       logman("%s:enter,host=%s,port=%d", mname, sloc_hostname,
                     sloc_port);
       ret = sloc_sr_code(SL_SEND_CONFIG_DUMP_DEBUG, (char *)NULL);
       logman("%s:normal exit[%d]", mname, ret);
       return(ret);
    }

    int sloc_terminate(char *passwd)
    {
       /* Terminate a 'socloc' server. Function returns 'SL_OK'
          upon success, a 'socloc' code otherwise. */

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

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

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

       ret = sloc_sr_code(SL_SEND_TERM, passwd);
       logman("%s:normal exit[%d]", mname, ret);
       return(ret);
    }

    void sloc_get_active_socloc(char *hname, int *port)
    {
       /* Get and return the active 'socloc' server host name
          and port number. 'hname' must be allocated by the
          caller to sufficient size. */

       char mname[] = "sloc_get_active_socloc";

       if (hname == (char *)NULL || port == (int *)NULL)
          {
          logman("%s:exit:null[hname or port]", mname);
          return;
          }

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

       if (!strlen(sloc_hostname) || sloc_port == 0)
          {
          logman("%s:no current socloc server", mname);
          return;
          }

       strcpy(hname, sloc_hostname);
       *port = sloc_port;
       logman("%s:normal exit", mname);
    }

    int sloc_set_active_socloc(char *hname, int port)
    {
       /* Set the current active 'socloc' server. Connection is
          tested and service name is verified. Function returns
          a 'socloc' code. */

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

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

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

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

       if ((ret = sloc_connect(hname, port)) != SL_OK)
          {
          logman("%s:bad rc[%d] from sloc_connect", mname, ret);
          return(ret);
          }

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

    int sloc_is_init(void)
    {
       /* Determine whether the 'socloc' API has been initialized.
          The API is considered initialized when there is an active
          host name in 'sloc_hostname' and a positive port number in
          'sloc_port'. Function returns 'SL_OK' if the API has been
          initialized, 'SL_NO_INIT' otherwise. */

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

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

       if (!sloc_port || sloc_hostname == (char *)NULL || !strlen(sloc_hostname))
          ret = SL_NO_INIT;
       else
          ret = SL_OK;

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

    int sloc_trans_num(long *transout)
    {
       /* Get the 'socloc' server transaction count.
          Function returns 'SL_OK' upon success, a 'socloc'
          code otherwise. */

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

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

       if (transout == (long *)NULL)
          {
          logman("%s:null[transout]", mname);
          return(SL_INVALID_PARAMETER);
          }

       ret = sloc_sr_long(SL_SEND_TRANS_NUM, (char *)NULL, transout);
       logman("%s:normal exit[%d]", mname, ret);
       return(ret);
    }

    int sloc_connect_num(int *connectout)
    {
       /* Get the 'socloc' server client connection count.
          Function returns 'SL_OK' upon success, a 'socloc'
          code otherwise. */

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

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

       if (connectout == (int *)NULL)
          {
          logman("%s:null[connectout]", mname);
          return(SL_INVALID_PARAMETER);
          }

       ret = sloc_sr_int(SL_SEND_CONNECT_NUM, (char *)NULL, connectout);
       logman("%s:normal exit[%d]", mname, ret);
       return(ret);
    }

    void sloc_term_api(void)
    {
       /* Shutdown the API. Close the socket in use, wipe the globals
          indicating the server host name and port and delete the
          local 'socloc' config link list. */

       char mname[] = "sloc_term_api";

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

    /* private functions */

    static int sloc_connect(char *hname, int port)
    {
       /* Attempt to connect to any 'socloc' server. Host name is
          expected in 'hname' and TCP/IP port number is expected
          in 'port'. Upon connection, the service name will be
          verified. Upon success, the 'socloc' API
          host name and port number will be set. Function returns a
          'socloc' code. */

       char mname[] = "sloc_connect";
       char thname[128], sname[128];
       int tport, ret;

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

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

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

       /* save current connection values and terminate any existing connection */

       strcpy(thname, sloc_hostname);
       tport = sloc_port;
       sloc_end();

       /* load new values */

       strcpy(sloc_hostname, hname);
       sloc_port = port;

       // attempt a connection

       if ((ret = sloc_ll_client_connect()) != SL_OK)
          {
          // put host name and port back and re-connect to original server

          sloc_end();
          strcpy(sloc_hostname, thname);
          sloc_port = tport;
          (void)sloc_ll_client_connect();
          logman("%s:bad rc[%d] from sloc_ll_client_connect", mname, ret);
          return(ret);
          }

       // get and verify the service name

       if ((ret = sloc_sr_char(SL_SEND_SERVICE_NAME, (char *)NULL, sname))
           != SL_OK)
          {
          sloc_end();
          strcpy(sloc_hostname, thname);
          sloc_port = tport;
          (void)sloc_ll_client_connect();
          logman("%s:bad rc[%d] from sloc_sr_char", mname, ret);
          return(ret);
          }

       /* verify that this is a 'socloc' server */

       if (strcmp(sname, SL_SERVICE_NAME))
          {
          sloc_end();
          strcpy(sloc_hostname, thname);
          sloc_port = tport;
          (void)sloc_ll_client_connect();
          logman("%s:not a socloc server", mname);
          return(SL_NOT_A_SOCLOC_SERVER);
          }

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

    static int sloc_sr_code(int typ, char *parm)
    {
       /* Send and receive a message to the 'socloc' server that
          returns only a code. Function will failover to another
          'socloc' server (if one available) upon a socket
          communication error. Function returns a 'socloc' code. */

       char mname[] = "sloc_sr_code";
       int ret, done = FALSE;

       // make sure there is an active connection

       if ((ret = sloc_is_init()) != SL_OK)
          {
          logman("%s:no active connection", mname);
          return(ret);
          }

       /* loop while there is a socket error and other 'socloc'
          servers are available */

       while(!done)
          {
          /* attempt to send command and receive reply */

          ret = sloc_lsr_code(typ, parm);
          logman("%s:sloc_lsr_code rc[%d]", mname, ret);

          /* if a socket communication error, failover */

          if (ret == SL_VC_ERROR)
             {
             logman("%s:server not responding,will failover", mname);

             /* if failover failed, exit */

             if ((ret = sloc_failover()) != SL_OK)
                {
                logman("%s:bad rc[%d] from sloc_failover", mname, ret);
                done = TRUE;
                }
             }
          else
             done = TRUE;
          }

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

    static int sloc_lsr_code(int typ, char *parm)
    {
       /* Send and recveive a message to the 'socloc' server that returns
          only a return code. Its ok if 'parm' is NULL or empty.
          Function returns a 'socloc' code. */

       char mname[] = "sloc_sr_code", *mess;
       int len, full_len, ret;

       /* if init has not been called, exit error */

       if ((ret = sloc_is_init()) != SL_OK)
          {
          logman("%s:no socloc server details", mname);
          return(ret);
          }

       // estimate length and alloc send/receive buffer

       len = (parm == (char *)NULL) ? 0 : strlen(parm);
       full_len = len + 5;

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

       memset(mess, 0, full_len);

       // format send string

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

       // send message

       if (!ipc_send(slclientSocket, mess))
          {
          logman("%s:bad rc[FALSE] from ipc_send", mname);
          free(mess);
          return(SL_VC_ERROR);
          }

       memset(mess, 0, full_len);

       // receive return code

       if (!ipc_recv(slclientSocket, mess))
          {
          logman("%s:bad rc[FALSE] from ipc_recv", mname);
          free(mess);
          return(SL_VC_ERROR);
          }

       // make sure its a number

       if (!qatoi(mess, &ret))
          {
          logman("%s:bad rc[FALSE] from qatoi[wrd 1]", mname);
          free(mess);
          return(SL_INTERNAL_ERROR);
          }

       free(mess);
       return(ret);
    }

    static int sloc_sr_char(int typ, char *parm, char *char_out)
    {
       /* Send and receive a message to the 'socloc' server that
          returns a char string in addition to the reply code.
          Function will failover to another
          'socloc' server (if one available) upon a socket
          communication error. Function returns a 'socloc' code. */

       char mname[] = "sloc_sr_char";
       int ret, done = FALSE;

       // make sure there is an active connection

       if ((ret = sloc_is_init()) != SL_OK)
          {
          logman("%s:no active connection", mname);
          return(ret);
          }

       /* loop while there is a socket error and other 'socloc'
          servers are available */

       while(!done)
          {
          /* attempt to send command and receive reply */

          ret = sloc_lsr_char(typ, parm, char_out);
          logman("%s:sloc_lsr_char rc[%d]", mname, ret);

          /* if a socket communication error, failover */

          if (ret == SL_VC_ERROR)
             {
             logman("%s:server not responding,will failover", mname);

             /* if failover failed, exit */

             if ((ret = sloc_failover()) != SL_OK)
                {
                logman("%s:bad rc[%d] from sloc_failover", mname, ret);
                done = TRUE;
                }
             }
          else
             done = TRUE;
          }

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

    static int sloc_lsr_char(int typ, char *parm, char *char_out)
    {
       /* Send and recveive a message to the 'socloc' server that returns a
          string as well as a return code. 'char_out' must already
          be allocated to sufficient size for the receiving string.
          Its ok if 'parm' is NULL or empty. Function returns a 'socloc' code. */

       char mname[] = "sloc_sr_char", *mess, tmp[50];
       int len, full_len, nwords, pos, ret;

       /* if init has not been called, exit error */

       if ((ret = sloc_is_init()) != SL_OK)
          {
          logman("%s:no socloc server details", mname);
          return(ret);
          }

       char_out[0] = EOS;

       // estimate length and alloc send/receive buffer

       len = (parm == (char *)NULL) ? 0 : strlen(parm);
       full_len = len + 5;

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

       memset(mess, 0, full_len);

       // format send string

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

       // send message

       if (!ipc_send(slclientSocket, mess))
          {
          logman("%s:bad rc[FALSE] from ipc_send", mname);
          free(mess);
          return(SL_VC_ERROR);
          }

       free(mess);

       // re-allocate 'mess' for receive buffer (max size)

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

       memset(mess, 0, SL_MAXCOMMAND);

       // receive reply

       if (!ipc_recv(slclientSocket, mess))
          {
          logman("%s:bad rc[FALSE] from ipc_recv", mname);
          free(mess);
          return(SL_VC_ERROR);
          }

       // s/b two words in reply

       nwords = command_words(mess);

       // s/b reply code in word one

       if (!command_word(mess, tmp, 1))
          {
          logman("%s:bad rc[FALSE] from command_word[1]", mname);
          free(mess);
          return(SL_INTERNAL_ERROR);
          }

       // make sure its a number

       if (!qatoi(tmp, &ret))
          {
          logman("%s:bad rc[FALSE] from qatoi[wrd 1]", mname);
          free(mess);
          return(SL_INTERNAL_ERROR);
          }

       // if reply is 'ok', load 'char_out'

       if (ret == SL_OK)
          {
          if (nwords < 2)
             {
             logman("%s:expecting at least two words in reply", mname);
             free(mess);
             return(SL_INTERNAL_ERROR);
             }

          // 'char_value' is from the end of the first word

          if ((pos = command_indxword(mess, 2)) == -1)
             {
             logman("%s:bad rc[-1] from command_indxword[2]", mname);
             free(mess);
             return(SL_INTERNAL_ERROR);
             }

          /* if second command word starts with quote backup one */

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

          len = strlen(mess) - pos;
          strncpy(char_out, &mess[pos], len);
          char_out[len] = EOS;
          }

       free(mess);
       return(ret);
    }

    static int sloc_sr_int(int typ, char *parm, int *intout)
    {
       /* Send and receive a message to the 'socloc' server that
          returns an integer in addition to the reply code.
          Function will failover to another
          'socloc' server (if one available) upon a socket
          communication error. Function returns a 'socloc' code. */

       char mname[] = "sloc_sr_int";
       int ret, done = FALSE;

       // make sure there is an active connection

       if ((ret = sloc_is_init()) != SL_OK)
          {
          logman("%s:no active connection", mname);
          return(ret);
          }

       /* loop while there is a socket error and other 'socloc'
          servers are available */

       while(!done)
          {
          /* attempt to send command and receive reply */

          ret = sloc_lsr_int(typ, parm, intout);
          logman("%s:sloc_lsr_int rc[%d]", mname, ret);

          /* if a socket communication error, failover */

          if (ret == SL_VC_ERROR)
             {
             logman("%s:server not responding,will failover", mname);

             /* if failover failed, exit */

             if ((ret = sloc_failover()) != SL_OK)
                {
                logman("%s:bad rc[%d] from sloc_failover", mname, ret);
                done = TRUE;
                }
             }
          else
             done = TRUE;
          }

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

    static int sloc_lsr_int(int typ, char *parm, int *intout)
    {
       /* Send and recveive a message to the 'socloc' server that returns an
          integer as well as a return code. Its ok if 'parm' is NULL or empty.
          Function returns a 'socloc' code. */

       char mname[] = "sloc_lsr_int", *mess, tmp[50];
       int len, full_len, nwords, pos, ret;

       /* if init has not been called, exit error */

       if ((ret = sloc_is_init()) != SL_OK)
          {
          logman("%s:no socloc server details", mname);
          return(ret);
          }

       *intout = 0;

       // estimate length and alloc send/receive buffer

       len = (parm == (char *)NULL) ? 0 : strlen(parm);
       full_len = len + 5;

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

       memset(mess, 0, full_len);

       // format send string

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

       // send message

       if (!ipc_send(slclientSocket, mess))
          {
          logman("%s:bad rc[FALSE] from ipc_send", mname);
          free(mess);
          return(SL_VC_ERROR);
          }

       free(mess);

       // re-allocate 'mess' for receive buffer (max size)

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

       memset(mess, 0, SL_MAXCOMMAND);

       // receive reply

       if (!ipc_recv(slclientSocket, mess))
          {
          logman("%s:bad rc[FALSE] from ipc_recv", mname);
          free(mess);
          return(SL_VC_ERROR);
          }

       // s/b two words in reply

       nwords = words(mess);

       // s/b reply code in word one

       if (!word(mess, tmp, 1))
          {
          logman("%s:bad rc[FALSE] from word[1]", mname);
          free(mess);
          return(SL_INTERNAL_ERROR);
          }

       // make sure its a number

       if (!qatoi(tmp, &ret))
          {
          logman("%s:bad rc[FALSE] from qatoi[wrd 1]", mname);
          free(mess);
          return(SL_INTERNAL_ERROR);
          }

       // if reply is 'ok', load 'intout'

       if (ret == SL_OK)
          {
          if (nwords < 2)
             {
             logman("%s:expecting at least two words in reply", mname);
             free(mess);
             return(SL_INTERNAL_ERROR);
             }

          if (!word(mess, tmp, 2))
             {
             logman("%s:bad rc[FALSE] from word[2]", mname);
             free(mess);
             return(SL_INTERNAL_ERROR);
             }

          if (!qatoi(tmp, intout))
             {
             logman("%s:bad rc[FALSE] from qatoi[wrd 2]", mname);
             free(mess);
             return(SL_INTERNAL_ERROR);
             }
          }

       free(mess);
       return(ret);
    }

    static int sloc_sr_long(int typ, char *parm, long *longout)
    {
       /* Send and receive a message to the 'socloc' server that
          returns a long integer in addition to the reply code.
          Function will failover to another
          'socloc' server (if one available) upon a socket
          communication error. Function returns a 'socloc' code. */

       char mname[] = "sloc_sr_long";
       int ret, done = FALSE;

       // make sure there is an active connection

       if ((ret = sloc_is_init()) != SL_OK)
          {
          logman("%s:no active connection", mname);
          return(ret);
          }

       /* loop while there is a socket error and other 'socloc'
          servers are available */

       while(!done)
          {
          /* attempt to send command and receive reply */

          ret = sloc_lsr_long(typ, parm, longout);
          logman("%s:sloc_lsr_long rc[%d]", mname, ret);

          /* if a socket communication error, failover */

          if (ret == SL_VC_ERROR)
             {
             logman("%s:server not responding,will failover", mname);

             /* if failover failed, exit */

             if ((ret = sloc_failover()) != SL_OK)
                {
                logman("%s:bad rc[%d] from sloc_failover", mname, ret);
                done = TRUE;
                }
             }
          else
             done = TRUE;
          }

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

    static int sloc_lsr_long(int typ, char *parm, long *longout)
    {
       /* Send and recveive a message to the 'socloc' server that returns a
          long integer as well as a return code. Its ok if 'parm' is NULL or empty.
          Function returns a 'socloc' code. */

       char mname[] = "sloc_lsr_long", *mess, tmp[50];
       int len, full_len, nwords, pos, ret;

       /* if init has not been called, exit error */

       if ((ret = sloc_is_init()) != SL_OK)
          {
          logman("%s:no socloc server details", mname);
          return(ret);
          }

       *longout = 0L;

       // estimate length and alloc send/receive buffer

       len = (parm == (char *)NULL) ? 0 : strlen(parm);
       full_len = len + 5;

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

       memset(mess, 0, full_len);

       // format send string

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

       // send message

       if (!ipc_send(slclientSocket, mess))
          {
          logman("%s:bad rc[FALSE] from ipc_send", mname);
          free(mess);
          return(SL_VC_ERROR);
          }

       free(mess);

       // re-allocate 'mess' for receive buffer (max size)

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

       memset(mess, 0, SL_MAXCOMMAND);

       // receive reply

       if (!ipc_recv(slclientSocket, mess))
          {
          logman("%s:bad rc[FALSE] from ipc_recv", mname);
          free(mess);
          return(SL_VC_ERROR);
          }

       // s/b two words in reply

       nwords = words(mess);

       // s/b reply code in word one

       if (!word(mess, tmp, 1))
          {
          logman("%s:bad rc[FALSE] from word[1]", mname);
          free(mess);
          return(SL_INTERNAL_ERROR);
          }

       // make sure its a number

       if (!qatoi(tmp, &ret))
          {
          logman("%s:bad rc[FALSE] from qatoi[wrd 1]", mname);
          free(mess);
          return(SL_INTERNAL_ERROR);
          }

       // if reply is 'ok', load 'longout'

       if (ret == SL_OK)
          {
          if (nwords < 2)
             {
             logman("%s:expecting at least two words in reply", mname);
             free(mess);
             return(SL_INTERNAL_ERROR);
             }

          if (!word(mess, tmp, 2))
             {
             logman("%s:bad rc[FALSE] from word[2]", mname);
             free(mess);
             return(SL_INTERNAL_ERROR);
             }

          if (!qatol(tmp, longout))
             {
             logman("%s:bad rc[FALSE] from qatol[wrd 2]", mname);
             free(mess);
             return(SL_INTERNAL_ERROR);
             }
          }

       free(mess);
       return(ret);
    }

    static int sloc_failover(void)
    {
       /* Failover to another 'socloc' server (if one is available).
          Function returns a 'socloc' code. */

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

       logman("%s:enter:port=%d", mname, sloc_port);

       // delete the current entry from the local config link list

       if ((ret = sl_config_delete(sloc_port)) != SL_OK)
          {
          logman("%s:bad rc[%d] from sl_config_delete", mname, ret);
          return(ret);
          }

       // attempt to connect to the first available 'socloc' server

       if ((ret = sloc_client_connect()) != SL_OK)
          {
          logman("%s:bad rc[%d] from sloc_client_connect", mname, ret);
          return(ret);
          }

       logman("%s:normal exit[0]:port=%d", mname, sloc_port);
       return(SL_OK);
    }

    static int sloc_reload_config_list(void)
    {
       /* Get a fresh copy of the socloc config list
          from the current 'socloc' server and place into
          memory. Function returns a 'socloc' code. */

       char mname[] = "sloc_reload_config_list";
       char *thelist;
       int ret;

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

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

       // get the config list from the current 'socloc' server

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

       // create a new list in memory

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

       free(thelist);
       logman("%s:normal exit[0]", mname);
       return(SL_OK);
    }

    static int sloc_client_connect(void)
    {
       /* Attempt to connect to a 'socloc' server. The host name
          and port number should have already been initialized by
          calling 'sloc_initialize'. If the initialization routine
          has not been called, it is an error. If the current 'socloc'
          server has been found to be not responding, another
          'socloc' server will attempt to be found. Function
          returns 'SL_OK' if a connection was made to any
          'socloc' server, a 'socloc' code otherwise. */

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

       logman("%s:enter,sloc_hostname=%s,sloc_port=%d", mname,
                     sloc_hostname, sloc_port);

       // if there is an active connection, close it

       if ((ret = sloc_is_init()) == SL_OK)
          {
          logman("%s:active connection found,closing it", mname, ret);
          sloc_end();
          }

       /* loop which attempts to connect to currently listed
          'socloc' server, if this fails, get next active and
          try */

       while(1)
          {
          // get top entry in config link list

          if ((ret = sl_config_get_first(sloc_hostname, &sloc_port,
               (char *)NULL)) != SL_OK)
             {
             logman("%s:bad rc[%d] from sl_config_get_first", mname, ret);
             return(ret);
             }

          // open the socket and attempt a connection

          if ((ret = sloc_ll_client_connect()) == SL_OK)
             {
             /* if connection was successful, get a new copy
                of the config link list from the server and
                place it into memory */

             (void)sloc_reload_config_list();
             logman("%s:exit[0],connected to server %s", mname,
                           sloc_hostname);
             return(ret);
             }

          logman("%s:server %s not responding,rc=%d", mname,
                        sloc_hostname, ret);

          // delete the current entry

          if ((ret = sl_config_delete(sloc_port)) != SL_OK)
             {
             logman("%s:bad rc[%d] from sl_config_delete", mname, ret);
             return(ret);
             }

          /* wipe out current host name and port */

          sloc_hostname[0] = EOS;
          sloc_port = 0;
          }

       /* we should never get here, but just in case */

       logman("%s:unanticipated wacky exit SL_NO_SUCH_SOCLOC", mname);
       return(SL_NO_SUCH_SOCLOC);
    }

    static int sloc_ll_client_connect(void)
    {
       /* Connect to the 'socloc' server. An attempt to open the TCP
          socket is made. The host name 'sloc_hostname' and the
          TCP port 'sloc_port' are assumed to be already loaded
          via 'sloc_initialize'. Function returns 'SL_OK' if a
          successful connection was made, a 'socloc' code otherwise.
          Private function. */

       int ret;

       // make sure host name and port are loaded

       if ((ret = sloc_is_init()) != SL_OK)
          return(ret);

       // resolve server host name

       lpHostEnt = gethostbyname(sloc_hostname);

       if (!lpHostEnt)
          return(SL_VC_ERROR);

       // create the socket

    #ifdef OS_WIN32
       slclientSocket = socket(PF_INET, SOCK_STREAM, DEFAULT_PROTOCOL);
    #endif

    #ifdef OS_UNIX
       slclientSocket = socket(AF_INET, SOCK_STREAM, DEFAULT_PROTOCOL);
    #endif

       if (slclientSocket == INVALID_SOCKET)
          return(SL_VC_ERROR);

       // load client address data

       memset(&slsockClientAddr, 0, sizeof(slsockClientAddr));
       slsockClientAddr.sin_family = AF_INET;
       slsockClientAddr.sin_port = htons(sloc_port);

    #ifdef OS_WIN32
       slsockClientAddr.sin_addr = *((LPIN_ADDR)*lpHostEnt->h_addr_list);
    #endif

    #ifdef OS_UNIX
       slsockClientAddr.sin_addr.s_addr = ((struct in_addr *)(lpHostEnt->h_addr))->s_addr;
    #endif

       // connect to server

    #ifdef OS_WIN32
       if (connect(slclientSocket, (LPSOCKADDR)&slsockClientAddr,
                   sizeof(slsockClientAddr)))
    #endif

    #ifdef OS_UNIX
       if (connect(slclientSocket, (SA *)&slsockClientAddr,
                   sizeof(slsockClientAddr)) == SOCKET_ERROR)
    #endif

          return(SL_VC_ERROR);

       return(SL_OK);
    }

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