home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Libraries / DCLAP 4j / network / nsclilib / ni_lib.c < prev    next >
Encoding:
Text File  |  1995-12-17  |  83.1 KB  |  2,542 lines  |  [TEXT/R*ch]

  1. /*
  2. * ===========================================================================
  3. *
  4. *                            PUBLIC DOMAIN NOTICE
  5. *               National Center for Biotechnology Information
  6. *
  7. *  This software/database is a "United States Government Work" under the
  8. *  terms of the United States Copyright Act.  It was written as part of
  9. *  the author's official duties as a United States Government employee and
  10. *  thus cannot be copyrighted.  This software/database is freely available
  11. *  to the public for use. The National Library of Medicine and the U.S.
  12. *  Government have not placed any restriction on its use or reproduction.
  13. *
  14. *  Although all reasonable efforts have been taken to ensure the accuracy
  15. *  and reliability of the software and data, the NLM and the U.S.
  16. *  Government do not and cannot warrant the performance or results that
  17. *  may be obtained by using this software or data. The NLM and the U.S.
  18. *  Government disclaim all warranties, express or implied, including
  19. *  warranties of performance, merchantability or fitness for any particular
  20. *  purpose.
  21. *
  22. *  Please cite the author in any work or product based on this material.
  23. *
  24. * ===========================================================================
  25. *
  26. * File Name:    ni_lib.c
  27. *
  28. * Author:       Beatty, Gish, Epstein
  29. *
  30. * Version Creation Date:        1/1/92
  31. *
  32. * $Revision: 1.36 $
  33. *
  34. * File Description:
  35. *   This file is a library of functions to be used by server application
  36. *   and client software, using the NCBI "network services" paradigm.
  37. *
  38. *
  39. * Modifications:
  40. * --------------------------------------------------------------------------
  41. * Date     Name        Description of modification
  42. * -------  ----------  -----------------------------------------------------
  43. * 4/27/92  Epstein     Added extensive in-line commentary, and removed all tabs.
  44. * 5/11/92  Epstein     Removed unused function NI_SVCRequestGet(); added support
  45. *                      for the connection ID to be written to a CONID file each
  46. *                      time that the value of conid is updated; in practice,
  47. *                      only dispatcher will update a CONID file.
  48. * 6/22/92  Epstein     For UNIX signals, catch the SIGPIPE error which can
  49. *                      occur when writing to a socket which is no longer
  50. *                      connected.
  51. * 7/06/92  Epstein     Changed sokselectw() to examine the SO_ERROR socket option
  52. *                      after select()-ing a socket to which we were attempting a
  53. *                      connection. This eliminates "false connects", i.e.,
  54. *                      unsuccessful connection attempts which look successful
  55. *                      because the select() call returns 1.
  56. * 7/14/92  Epstein     Changed NI_SetDispatcher() and NI_InitServices() to use
  57. *                      a configurable timeout parameter, and in the process
  58. *                      also changed sokselectw() to have a timeout parameter,
  59. * 1/21/93  Epstein     Add dispatcher-list support, and add dispatcher-list
  60. *                      parameter to NI_InitServices().
  61. * 2/12/93  Epstein     Use new boolean parameter to MsgMakeHandle(), indicating
  62. *                      whether or not it should create a socket.  This was
  63. *                      an attempted fix for a Mac problem ... it later
  64. *                      turned out to be an incorrect problem-fix, but also
  65. *                      does no harm.
  66. * 2/24/93  Epstein     Fix long-standing Mac bug, by correctly destroying
  67. *                      services handle and hence closing an open socket.
  68. * 3/02/93  Epstein     Add functions to write dispatcher-configuration info
  69. *                      to a config file.  This provides a standardized
  70. *                      mechanisms which applications may use for net services
  71. *                      configuration.  Also added platform functions, so
  72. *                      that dispatcher/server complex can know what type
  73. *                      of platform a client is running on, assuming that the
  74. *                      client is telling the truth.
  75. * 3/03/93  Epstein     Cleanup variable initialization.
  76. * 3/08/93  Epstein     Improve error messages & cleanup to NI_InitServices,
  77. *                      include reason in login failure message, and add
  78. *                      client platform to service request.
  79. * 3/09/93  Epstein     Add HaltServices() function to simplify cleanup.
  80. * 3/22/93  Epstein     Fix typecast for getsockopt(), and, more importantly,
  81. *                      remember to return the computed value in NI_GetPlatform.
  82. * 3/23/93  Epstein     Support VMS/TGV, and add NETP_INET_ prefixes to 
  83. *                      conditional-compilation symbols.
  84. * 3/24/93  Epstein     Clear the caller's pointer in NI_SetDispConfig().
  85. * 3/31/93  Epstein     Add dispatcher pointer as context for all network
  86. *                      services operations; this allows an application
  87. *                      to use more than one dispatcher at a time, at the
  88. *                      expense of slightly greater complexity.  Also add
  89. *                      a "Generic Init" function, which can be used by
  90. *                      an application to obtain network-services in a
  91. *                      simplified, standardized manner.
  92. * 3/31/93  Epstein     Move debug and module variables to their correct home.
  93. * 4/02/93  Epstein     Add WinSock support.
  94. * 4/12/93  Schuler     Add MAKEWORD macro.
  95. * 4/21/93  Schuler     Removed function prototypes for NI_AsnRead, NI_AsnWrite
  96. * 5/07/93  Epstein     Move WSAStartup() code to a better place, add workaround
  97. *                      for connection attempt on a non-blocking socket in PC-NFS
  98. *                      4.0, add more platform definitions.
  99. * 5/24/93  Epstein     Add separate error codes for TCP/IP initialization
  100. *                      failure and inability to resolve local host name.
  101. * 5/25/93  Epstein     Add configuration-file workaround for PC-NFS 5.0 bug,
  102. *                      where NIS sometimes fails on the PC's own host name.
  103. * 5/27/93  Epstein     Incorporate pragmatic "Gestalt" code for Vibrant
  104. *                      scrolling workaround for WinSock under Windows 3.1,
  105. *                      add add SOCK_INDEX_ERRNO macro to workaround another
  106. *                      WinSock pecularity.
  107. * 6/02/93  Schuler     Change "Handle" to "MonitorPtr" for Monitors.
  108. * 6/07/93  Epstein     Added generic timer functions.
  109. *                      Also add missing revision history, derived from
  110. *                      RCS file.
  111. * 6/09/93  Epstein     Added activity hook to report network activity back
  112. *                      to an application.
  113. * 6/14/93  Epstein     Changed "Generic" logic to cause UNIX/VMS loginname
  114. *                      to override loginname, rather than vice versa.  Also
  115. *                      setup DispatchConnect() logic to set client's declared
  116. *                      IP address to 0.0.0.0, rather than causing an error,
  117. *                      in the case where the client cannot resolve its own
  118. *                      host name.  In this case, the dispatcher will set its
  119. *                      own opinion of the client address based upon
  120. *                      getpeername().
  121. * 6/15/93  Epstein     Eliminate "Gestalt" code for Vibrant scrolling
  122. *                      workaround for WinSock under Windows 3.1, since the
  123. *                      solution for this problem does not require its use.
  124. * 6/25/93  Epstein     Fix activity-hook action for service disconnection (had
  125. *                      erroneously announced dispatcher-disconnection), and
  126. *                      add logic to try to avoid getservbyname() by looking
  127. *                      up dispatcher port # and (loport,hiport) in NCBI
  128. *                      configuration file instead of in NIS.  As a last resort,
  129. *                      look up the name in NIS if the entry in the NCBI
  130. *                      config. file is non-numeric.  Also, change the client
  131. *                      port lookup mechanism for Macintoshes to add a configured
  132. *                      "delta" value to the low port number.  This results in
  133. *                      allowing several Network Services applications to run
  134. *                      concurrently on a Mac without port conflicts.
  135. * 7/08/93  Epstein     Fix list traversal error in NI_ProcessTimers()
  136. * 7/08/93  Epstein     Added a counter as a failsafe mechanism in
  137. *                      NI_ProcessTimers(), since previous fix attempt failed.
  138. * 7/09/93  Epstein     Changed a few #define names to avoid Alpha compilation
  139. *                      warnings, and added reference count to dispatcher data
  140. *                      structure.
  141. * 8/09/93  Epstein     Improve diagnostics when a listen() call fails
  142. * 8/23/93  Epstein     Add currentDisp variable so that the currently-attached
  143. *                      dispatcher is used when the parameter to NI_SetDispatcher
  144. *                      is NULL.
  145. * 8/31/93  Epstein     Fix host vs. network order when comparing port numbers.
  146. * 9/08/93  Epstein     Added new stackDescription variable, to be able to
  147. *                      report to the dispatcher the identity of the vendor
  148. *                      of the WinSock stack
  149. * 9/09/93  Epstein     Fix use of currentDisp variable to correctly compare
  150. *                      new dispatcher request again current dispatcher.
  151. * ==========================================================================
  152. */
  153.  
  154. #define _NCBINET_LOCAL_VARS
  155. #define __NI_LIB__
  156. #include "ncbinet.h"
  157. #include "ni_lib.h"
  158. #include "ni_msg.h"
  159. #ifdef OS_UNIX
  160. #include <signal.h>
  161. #endif /* OS_UNIX */
  162. #ifdef OS_MAC
  163. #include <neterrno.h> /* include missing error numbers */
  164. #endif /* OS_MAC */
  165. #ifdef OS_VMS
  166. #include <perror.h>
  167. #endif /* OS_VMS */
  168.  
  169.  
  170. #ifdef NETP_INET_NEWT
  171.  
  172. #define SIN_ADDR        sin_addr.S_un.S_addr
  173. #define H_ADDR_TYPE     Uint4Ptr
  174. #else
  175. #define SIN_ADDR        sin_addr
  176. #define H_ADDR_TYPE     struct in_addr *
  177. #endif
  178.  
  179. #ifdef WIN16
  180. #ifndef MAKEWORD
  181. #define MAKEWORD(a,b)   ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) <<8)) 
  182. #endif 
  183. #endif
  184.  
  185.  
  186. typedef struct NI_Timer {
  187.     time_t timeout;
  188.     NI_TimeoutHook hook;
  189.     Pointer hookParam;
  190. } NI_Timer, PNTR NI_TimerPtr;
  191.  
  192.  
  193. /* GLOBALS */
  194. static FILE             *conid_fp = NULL;           /* File pointer for CONID */
  195. static NodePtr timerHead = NULL;                    /* list of timers */
  196. static NI_NetServHook activityHook = NULL;
  197. static NI_DispatcherPtr currentDisp = NULL;
  198. static CharPtr stackDescription = NULL;
  199.  
  200.  
  201. #ifdef NETP_INET_WSOCK
  202. static Int4 wsaStartupCount = 0;
  203. #endif
  204.  
  205. NILoginPtr              NI_MakeMsgLogin PROTO((void));
  206. static Int2             SetIdentity PROTO((NI_DispatcherPtr disp, CharPtr user, CharPtr group, CharPtr domain));
  207. static void             HaltServices PROTO((NI_DispatcherPtr disp));
  208. static NI_HandPtr       DispatchConnect PROTO((NI_DispatcherPtr disp, CharPtr host, CharPtr name, int timeout));
  209. static Uint2            bindPort PROTO((int sok, struct sockaddr_in PNTR sokadr, Int2 loport, Int2 hiport));
  210. static Int2             CopyIdentity PROTO((NI_DispatcherPtr disp, NI_UidPtr uid));
  211.  
  212. /** ( these are prototyped in ni_msg.h   [GDS] )
  213. Int2                    NI_AsnRead PROTO((Pointer fd, CharPtr buf, Uint2 len));
  214. Int2                    NI_AsnWrite PROTO((Pointer fd, CharPtr buf, Uint2 len));
  215. **/
  216. int                     sokselectr PROTO((int fd));
  217. int                     sokselectw PROTO((int fd, int timeout));
  218.  
  219. int                     getAsnError PROTO((char * str));
  220. void                    SetConFilePtr PROTO((FILE *fp));
  221. void                    CloseConFile PROTO((void));
  222.  
  223.  
  224.  
  225. /*
  226.  * Purpose:     Specify which dispatcher a client should try to connect to
  227.  *
  228.  * Parameters:
  229.  *   disp           Usually NULL, the pointer to a pre-existing Dispatcher
  230.  *                  structure
  231.  *   host           Name of the host (Fully Qualified Domain Name) to use
  232.  *   svc            Name of the "service" to try to use on that host
  233.  *   dispserialnum  Serial number of dispatcher-list. Use -1 if no response
  234.  *                  list is desired, or 0 if the serial number is not known.
  235.  *
  236.  *
  237.  * Description:
  238.  *              Set up the dispatcher name which should be used, and the
  239.  *              name of the service on that dispatcher. If other parameters
  240.  *              have been specified previously, free the memory associated
  241.  *              with those names.
  242.  *
  243.  * Note:
  244.  *              There are useful defaults for "svc". When in doubt, call
  245.  *              this function with a second arguement of NULL.
  246.  */
  247.  
  248. NI_DispatcherPtr
  249. NI_SetDispatcher(NI_DispatcherPtr disp, CharPtr host, CharPtr svc, int timeout,
  250.                  Int4 dispserialnum)
  251. {
  252.     if (disp == NULL) {
  253.         if (currentDisp != NULL)
  254.         { /* use current dispatcher if it matches what the caller wants */
  255.             if (StringCmp(host, currentDisp->dispServiceName) == 0 &&
  256.                 StringCmp(svc, currentDisp->dispHostName) == 0) {
  257.                 return currentDisp;
  258.             }
  259.         }
  260.  
  261.         disp = (NI_DispatcherPtr) MemNew(sizeof(NI_Dispatcher));
  262.         if (disp == NULL)
  263.             return NULL;
  264.         disp->reqResponse = NULL;
  265.         disp->dispHostName = NULL;
  266.         disp->dispServiceName = NULL;
  267.         disp->dispSerialNo = 0;
  268.         disp->localHostAddr[0] = '\0';
  269.         disp->dispHP = NULL;
  270.         disp->svcsHP = NULL;
  271.         disp->clientPort = 0;
  272.         disp->identity = NULL;
  273.         disp->dispTimeout = 0;
  274.         disp->referenceCount = 0;
  275.     }
  276.     if (disp->dispHostName != NULL) {
  277.         MemFree(disp->dispHostName);
  278.         disp->dispHostName = NULL;
  279.     }
  280.     if (disp->dispServiceName != NULL) {
  281.         MemFree(disp->dispServiceName);
  282.         disp->dispServiceName = NULL;
  283.     }
  284.     if (host != NULL)
  285.         disp->dispHostName = StringSave(host);
  286.     if (svc != NULL)
  287.         disp->dispServiceName = StringSave(svc);
  288.  
  289.     disp->dispSerialNo = dispserialnum;
  290.     disp->dispTimeout = timeout;
  291.  
  292.     return disp;
  293. } /* NI_SetDispatcher */
  294.  
  295.  
  296.  
  297. /*
  298.  * Purpose:     Try to establish a connection to the dispatcher
  299.  *
  300.  * Parameters:
  301.  *   disp           A pointer to the dispatcher structure
  302.  *   user           User name to try on the dispatcher
  303.  *   group          Group name to try on the dispatcher
  304.  *   password       Password for this user name
  305.  *   dip            A pointer to the caller's list of dispatchers; this should
  306.  *                     be used by the caller to update its information
  307.  *                     regarding which dispatchers to try in the future
  308.  *                     (if dip == NULL, then no retries will be made to get
  309.  *                      alternate dispatchers)
  310.  *
  311.  * Returns:
  312.  *                 -1, if something failed (ni_errno indicates the nature of
  313.  *                     the problem)
  314.  *                 0, if everything was successful
  315.  *                 1, if we are connected to the dispatcher which we requested,
  316.  *                     but the list of current dispatchers has changed
  317.  *                 2, if we are connected to a dispatcher, but not the one
  318.  *                     which we requested
  319.  *
  320.  *
  321.  * Description:
  322.  *              Connect to the dispatcher
  323.  *              Set-up a socket for an incoming connection from a server
  324.  *                  application process
  325.  *              Send a LOGIN message to the dispatcher
  326.  *              Wait for an ACK or NACK response from the dispatcher (or for
  327.  *                  a timeout to occur)
  328.  *              If the response was a NACK due to the dispatcher being a
  329.  *                  backup dispatcher, then try the dispatcher which it
  330.  *                  directs us to
  331.  */
  332.  
  333. Int2
  334. NI_InitServices(NI_DispatcherPtr disp, CharPtr user, CharPtr group, CharPtr password, NI_DispInfoPtr PNTR dip)
  335. {
  336.     NIMsgPtr            mp, imp;
  337.     NILoginPtr          loginp;
  338.     struct sockaddr_in  svcsAddr;
  339.     struct timeval      timeout;
  340.     int                 ready;
  341.     NIDispInfoPtr       dispinfo = NULL;
  342.     Boolean             newDispToTry;
  343.     Int2                altDispTries = 0;
  344.     Int2                retval = 0;
  345.     int                 status;
  346.     fd_set              readfds;
  347.  
  348. #ifdef NETP_INET_WSOCK
  349.     WSADATA wsaData;
  350.  
  351.     status = WSAStartup(MAKEWORD(1,1),&wsaData);
  352.     /* Try WinSock 1.1 and 1.0 in that order of preference */
  353.     if (status != 0 && (status = WSAStartup(MAKEWORD(1,0),&wsaData)) != 0)
  354.     {
  355.         TRACE("WSAStartup failed (code %d)\n", status);
  356.         ni_errno = NIE_TCPINITFAIL;
  357.         return -1;
  358.     }
  359.     TRACE("%s\n", wsaData.szDescription);
  360.     if (stackDescription != NULL)
  361.     {
  362.         MemFree(stackDescription);
  363.     }
  364.     stackDescription = StringSave(wsaData.szDescription);
  365.     wsaStartupCount++;
  366.  
  367. #endif
  368.  
  369.     if (disp == NULL)
  370.     {
  371.         ni_errno = NIE_MISC;
  372.         return -1;
  373.     }
  374.  
  375.     if (disp->referenceCount > 0 && disp->dispHP != NULL)
  376.     { /* already connected */
  377.         disp->referenceCount++;
  378.         return 0;
  379.     }
  380.  
  381.     if (disp->dispHostName == NULL)
  382.         disp->dispHostName = StringSave(NI_DEFAULT_HOST);
  383.     if (disp->dispServiceName == NULL)
  384.         disp->dispServiceName = StringSave(NI_DEFAULT_SERVICE);
  385.  
  386.     do {
  387.         newDispToTry = FALSE;
  388.         disp->svcsHP = NULL;
  389.         if ((disp->dispHP = DispatchConnect(disp, disp->dispHostName, disp->dispServiceName, disp->dispTimeout))
  390.             == NULL) {
  391.             NI_DestroyDispInfo(dispinfo);
  392.             HaltServices (disp);
  393.             ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: Unable to connect to host <%s>, error <%s>", disp->dispHostName, ni_errlist[ni_errno]);
  394.             return -1;      /* ni_errno remains set */
  395.         }
  396.  
  397.         if ((disp->svcsHP = MsgMakeHandle(TRUE)) == NULL) {
  398.             NI_DestroyDispInfo(dispinfo);
  399.             HaltServices (disp);
  400.             ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: Unable to allocate resources to communicate with %s", disp->dispHostName);
  401.             return -1;
  402.         }
  403.  
  404.         if (disp->dispTimeout > 0)
  405.         {
  406.             MsgSetReadTimeout(disp->svcsHP, disp->dispTimeout);
  407.         }
  408.         if ((disp->clientPort = bindPort(disp->svcsHP->sok, &svcsAddr, disp->loport, disp->hiport)) == 0) {
  409.             MsgDestroyHandle(disp->svcsHP);
  410.             disp->svcsHP = NULL;
  411.             ni_errno = NIE_NOBIND;                  /* can't bind a free application socket */
  412.             NI_DestroyDispInfo(dispinfo);
  413.             HaltServices (disp);
  414.             ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s>", ni_errlist[ni_errno]);
  415.             return -1;
  416.         }
  417.         if ((status = listen(disp->svcsHP->sok, 5)) < 0) {
  418. #ifdef NETP_INET_NEWT
  419.             SOCK_ERRNO = ABS(status);
  420. #endif
  421.             StringCpy(ni_errtext, sys_errlist[SOCK_INDEX_ERRNO]);
  422.             ni_errno = NIE_NOLISTEN;
  423.             NI_DestroyDispInfo(dispinfo);
  424.             HaltServices (disp);
  425.             ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s> <port %d, errno %d>", ni_errlist[ni_errno], (int) disp->clientPort, (int) SOCK_ERRNO);
  426.             return -1;
  427.         }
  428.  
  429.         SetIdentity(disp, user, group, NI_DEFAULT_DOMAIN);
  430.  
  431.         loginp = NI_MakeMsgLogin();
  432.         NI_DestroyUid(loginp->uid);
  433.         loginp->uid = NI_MakeUid();
  434.         loginp->seqno = disp->dispHP->seqno++;
  435.         loginp->dispserialno = disp->dispSerialNo;
  436.         CopyIdentity(disp, loginp->uid);
  437.         if (password != NULL)
  438.             loginp->password = StringSave(password);
  439.         mp = MsgBuild(NI_LOGIN, disp->dispHP->conid, (VoidPtr) loginp);
  440.  
  441.         if (MsgWrite(disp->dispHP, mp) < 0) {
  442.             if (getAsnError(ni_errtext) == ECONNRESET)
  443.                 ni_errno = NIE_MAXCONNS;
  444.             else
  445.                 ni_errno = NIE_MSGWRITE;
  446.             MsgDestroyHandle(disp->svcsHP);
  447.             disp->svcsHP = NULL;
  448.             NI_DestroyDispInfo(dispinfo);
  449.             HaltServices (disp);
  450.             ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s>", ni_errlist[ni_errno]);
  451.             return -1;
  452.         }
  453.  
  454.         /* blocks until ACK or ERROR from dispatcher or TIMEOUT */
  455.  
  456.         timeout.tv_sec = (Uint4) NI_TIMEOUT_SECS;
  457.         timeout.tv_usec = 0;
  458.         FD_ZERO(&readfds);
  459.         FD_SET(disp->dispHP->sok, &readfds);
  460.         while ((ready = NI_select(FD_SETSIZE, &readfds, NULL, NULL, &timeout)) < 0) {
  461.             if (SOCK_ERRNO == EINTR)
  462.                 ;                   /* repeat while interrupted */
  463.             else {
  464.                 MsgDestroyHandle(disp->svcsHP);
  465.                 disp->svcsHP = NULL;
  466.                 ni_errno = NIE_SELECT;              /* select error */
  467.                 NI_DestroyDispInfo(dispinfo);
  468.                 ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s>", ni_errlist[ni_errno]);
  469.                 return -1;
  470.             }
  471.         }
  472.  
  473.         if (FD_ISSET(disp->dispHP->sok, &readfds) != 0) {
  474.             if ((imp = MsgRead(disp->dispHP, FALSE)) == NULL) {
  475.                 if (getAsnError(ni_errtext) == ECONNRESET)
  476.                     ni_errno = NIE_MAXCONNS;
  477.                 else
  478.                     ni_errno = NIE_MSGREAD;
  479.  
  480.                 MsgDestroyHandle(disp->svcsHP);
  481.                 disp->svcsHP = NULL;
  482.                 NI_DestroyDispInfo(dispinfo);
  483.                 HaltServices (disp);
  484.                 ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s>", ni_errlist[ni_errno]);
  485.                 return -1;
  486.             }
  487.             switch (imp->type) {
  488.               case NI_ACK:
  489.                 /************************************************************/
  490.                 /* even though we connected successfully to the dispatcher, */
  491.                 /* it may have given us more up-to-date information on the  */
  492.                 /* latest list of dispatchers which should be tried; if so, */
  493.                 /* pass the updated list back to the caller                 */
  494.                 /************************************************************/
  495.                 if (imp->msun.ack->dispinfo != NULL) {
  496.                     if (dispinfo != NULL)
  497.                     {
  498.                         NI_DestroyDispInfo(dispinfo);
  499.                         dispinfo = NULL;
  500.                     }
  501.                     dispinfo = imp->msun.ack->dispinfo;
  502.                     imp->msun.ack->dispinfo = NULL; /* for clean free */
  503.                 }
  504.                 if (dispinfo != NULL && dip != NULL) {
  505.                     if (*dip != NULL)
  506.                         NI_DestroyDispInfo((NIDispInfoPtr) *dip);
  507.                     *dip = (NI_DispInfoPtr) dispinfo;
  508.                     dispinfo = NULL;
  509.                     retval = 1;
  510.                 }
  511.                 else {
  512.                     NI_DestroyDispInfo(dispinfo);
  513.                 }
  514.                 MsgDestroy(imp);
  515. #ifdef OS_UNIX
  516.                 signal(SIGPIPE, SIG_IGN); /* catch socket errors */
  517. #endif /* OS_UNIX */
  518.                 disp->referenceCount++;
  519.                 if (currentDisp == NULL)
  520.                 {
  521.                     currentDisp = disp;
  522.                 }
  523.                 return retval;   /* only good return */
  524.  
  525.               case NI_NACK:
  526.                 ni_errno = (enum ni_error) imp->msun.nack->code;
  527.                 if (imp->msun.nack->reason != NULL)
  528.                 {
  529.                     StringCpy(ni_errtext, imp->msun.nack->reason);
  530.                 } else {
  531.                     ni_errtext[0] = '\0';
  532.                 }
  533.                 if (dispinfo != NULL)
  534.                 {
  535.                     NI_DestroyDispInfo(dispinfo);
  536.                     dispinfo = NULL;
  537.                 }
  538.                 dispinfo = imp->msun.nack->dispinfo;
  539.                 imp->msun.nack->dispinfo = NULL; /* for clean free */
  540.                 if (ni_errno == NIE_BACKUPDISP && dispinfo != NULL &&
  541.                     dispinfo->numdispatchers > 0 && dip != NULL &&
  542.                     ++altDispTries < MAX_ALT_DISP_TRIES)
  543.                 {
  544.                     MsgDestroy(imp);
  545.                     HaltServices (disp);
  546.                     NI_SetDispatcher(disp, dispinfo->displist[0], disp->dispServiceName,
  547.                                      disp->dispTimeout, dispinfo->serialno);
  548.                     newDispToTry = TRUE;
  549.                     retval = 2;
  550.                     break;
  551.                 }
  552.                 MsgDestroy(imp);
  553.                 MsgDestroyHandle(disp->svcsHP);
  554.                 disp->svcsHP = NULL;
  555.                 NI_DestroyDispInfo(dispinfo);
  556.                 HaltServices (disp);
  557.                 ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s> %s", ni_errlist[ni_errno], ni_errtext);
  558.                 return -1;
  559.  
  560.               default:
  561.                 MsgDestroy(imp);
  562.                 ni_errno = NIE_MSGUNK;
  563.                 MsgDestroyHandle(disp->svcsHP);
  564.                 disp->svcsHP = NULL;
  565.                 NI_DestroyDispInfo(dispinfo);
  566.                 HaltServices (disp);
  567.                 ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s>", ni_errlist[ni_errno]);
  568.                 return -1;
  569.             }
  570.         }
  571.     } while (newDispToTry);
  572.  
  573.     MsgDestroyHandle(disp->svcsHP);
  574.     disp->svcsHP = NULL;
  575.     ni_errno = NIE_LOGTIMEOUT;          /* TIMEOUT */
  576.     NI_DestroyDispInfo(dispinfo);
  577.     HaltServices (disp);
  578.     ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s>", ni_errlist[ni_errno]);
  579.     return -1;
  580. } /* NI_InitServices */
  581.  
  582.  
  583. /*
  584.  * Purpose:     Init network services based on information in config file
  585.  *
  586.  * Parameters:
  587.  *   configFile     Name of NCBI-style configuration file.  If NULL, defaults
  588.  *                     to "NCBI"
  589.  *   configSection  Section with NCBI-style configuration file.  If NULL,
  590.  *                     defaults to "NET_SERV"
  591.  *   showMonitor    Boolean; if TRUE, display a monitor while re-trying
  592.  *                     for an alternate dispatcher
  593.  *   lastDispatcher Pointer to where this function should store the name
  594.  *                  of the dispatcher which was actually used (may be NULL
  595.  *                  if the caller does not care about this value)
  596.  *   lastDispLen    Maximum length of lastDispatcher
  597.  *
  598.  * Returns:
  599.  *                 NULL, if unable to contact dispatcher
  600.  *                 a pointer to the Dispatcher structure, otherwise
  601.  *
  602.  *
  603.  * Description:
  604.  *              Extracts a dispatcher name and a user name from a configuration
  605.  *              file.  If necessary, tries other dispatchers, in order, as
  606.  *              listed in configuration file.
  607.  *
  608.  *
  609.  * Note:
  610.  *              This function is provided as a convenience to developers who
  611.  *              wish to use Network Services.  Use of this function is not
  612.  *              integral to the use of Network Services ... it is merely a
  613.  *              convenience.
  614.  */
  615.  
  616. NI_DispatcherPtr
  617. NI_GenericInit (CharPtr configFile, CharPtr configSection, Boolean showMonitor, CharPtr lastDispatcher, Int2 lastDispLen)
  618. {
  619.     char *def_user;
  620.     char username[64];
  621.     char groupname[20];
  622.     char password[20];
  623.     char dispname[60];
  624.     char disp_config[10];
  625.     char disp_msg[50];
  626.     char buf[60];
  627.     Boolean more_disps;
  628.     int alternate = 1;
  629.     int disp_timeout;
  630.     Int4 disp_serialno;
  631.     Monitor *mon = NULL;
  632.     NI_DispInfoPtr dip = NULL;
  633.     NI_DispatcherPtr disp = NULL;
  634. #ifdef OS_UNIX
  635.     char *getlogin PROTO((void));
  636. #endif
  637.  
  638.     /******************* open the network connnection *********/
  639. #define NI_DISP_NAME "dispatch1.nlm.nih.gov"
  640. #define NI_USER_NAME "anonymous"
  641. #define NI_GROUP_NAME "GUEST"
  642.  
  643.     def_user = NI_USER_NAME;
  644.  
  645.     if (configFile == NULL)
  646.         configFile = "NCBI";
  647.     if (configSection == NULL)
  648.         configSection = "NET_SERV";
  649.  
  650.     GetAppParam(configFile, configSection, "DISP_USERNAME", NI_USER_NAME, username,
  651.                 sizeof username);
  652.     /* the user's login name overrides the config file */
  653.     /* for UNIX or VMS systems (or, for the future, any system where the      */
  654.     /* user name can be determined), use the user's login name as the default */
  655.     def_user = NULL;
  656. #ifdef OS_UNIX
  657.     def_user = getlogin();
  658. #endif
  659. #ifdef OS_VMS
  660.     def_user = getenv("USER");
  661. #endif
  662.     if (def_user != NULL)
  663.     {
  664.         StrNCpy(username, def_user, sizeof username);
  665.     }
  666.  
  667.     GetAppParam(configFile, configSection, "DISP_GROUPNAME", NI_GROUP_NAME, groupname,
  668.                 sizeof groupname);
  669.     GetAppParam(configFile, configSection, "DISP_PASSWORD", "", password,
  670.                 sizeof password); /* default = NONE */
  671.  
  672.     GetAppParam(configFile, configSection, "DISP_TIMEOUT", "0", buf, sizeof buf);
  673.     disp_timeout = atoi(buf);
  674.  
  675.     GetAppParam(configFile, configSection, "DISPSERIALNO", "0", buf, sizeof buf);
  676.     disp_serialno = atoi(buf);
  677.  
  678.     GetAppParam(configFile, configSection, "DISPATCHER", NI_DISP_NAME, dispname,
  679.                 sizeof dispname);
  680.  
  681.     do {
  682.         if (alternate == 2 && showMonitor)
  683.         {
  684.             mon = MonitorStrNew("Unable to contact primary dispatcher", 35);
  685.         }
  686.         if (alternate >= 2)
  687.         {
  688.             sprintf(disp_msg, "Trying dispatcher #%d <", alternate);
  689.             StrCat(disp_msg, dispname);
  690.             StrCat(disp_msg, ">");
  691.             if (showMonitor) {
  692.                 MonitorStrValue(mon, disp_msg);
  693.             }
  694.         }
  695.  
  696.         if (lastDispatcher != NULL) {
  697.                 StrNCpy(lastDispatcher, dispname, lastDispLen);
  698.         }
  699.  
  700.         disp = NI_SetDispatcher (NULL, dispname, NULL, disp_timeout, disp_serialno);
  701.  
  702.         if (NI_InitServices(disp, username, groupname[0] == '\0' ? NULL : groupname,
  703.                             password[0] == '\0' ? NULL : password, &dip) >= 0)
  704.         {
  705.             if (dip != NULL && dip->serialno != disp_serialno) {
  706.                 NI_SetDispConfig (&dip, dispname, sizeof dispname);
  707.             }
  708.             if (mon != NULL)
  709.                 MonitorFree(mon);
  710.             return disp;
  711.         }
  712.         ErrShow ();
  713.         NI_EndServices (disp);
  714.         sprintf(disp_config, "DISP_ALT_%d", alternate++);
  715.         more_disps = GetAppParam(configFile, configSection, disp_config, "", dispname,
  716.                                  sizeof dispname);
  717.     } while (more_disps);
  718.  
  719.     if (mon != NULL)
  720.         MonitorFree(mon);
  721.  
  722.     return NULL;
  723. }
  724.  
  725.  
  726. /*
  727.  * Purpose:     Get a network service based on information in config file
  728.  *
  729.  * Parameters:
  730.  *   disp           Pointer to the dispatcher structure obtained from a
  731.  *                  previous call to NI_SetDispatcher or NI_GenericInit
  732.  *   configFile     Name of NCBI-style configuration file.  If NULL, defaults
  733.  *                     to "NCBI"
  734.  *   defService     The default service/resource/resource-type name, if
  735.  *                  not specified otherwise in configuration file.
  736.  *   hasResource    Boolean; if TRUE, ask for a resource when requesting
  737.  *                     service
  738.  *
  739.  * Returns:
  740.  *                 NULL, if unable to obtain service
  741.  *                 a pointer to the service-structure, otherwise
  742.  *
  743.  *
  744.  * Description:
  745.  *              Extracts a service name and other service data from a
  746.  *              configuration file, and attempts to obtain that service.
  747.  *
  748.  *
  749.  * Note:
  750.  *              This function is provided as a convenience to developers who
  751.  *              wish to use Network Services.  Use of this function is not
  752.  *              integral to the use of Network Services ... it is merely a
  753.  *              convenience.
  754.  */
  755.  
  756. NI_HandPtr
  757. NI_GenericGetService (NI_DispatcherPtr disp, CharPtr configFile, CharPtr configSection, CharPtr defService, Boolean hasResource)
  758. {
  759.     char buf[40];
  760.     char service[40];
  761.     char resource[40];
  762.     char res_type[40];
  763.     int serv_min;
  764.     int serv_max;
  765.     int res_min;
  766.     int res_max;
  767.  
  768.     if (configFile == NULL)
  769.         configFile = "NCBI";
  770.  
  771.     GetAppParam(configFile, configSection, "SERVICE_NAME", defService,
  772.             service, sizeof service);
  773.     GetAppParam(configFile, configSection, "SERV_VERS_MIN", "1",
  774.             buf, sizeof buf);
  775.     serv_min = atoi(buf);
  776.     GetAppParam(configFile, configSection, "SERV_VERS_MAX", "0",
  777.             buf, sizeof buf);
  778.     serv_max = atoi(buf);
  779.  
  780.     res_min = 1;
  781.     res_max = 0;
  782.  
  783.     if (hasResource) {
  784.         GetAppParam(configFile, configSection, "RESOURCE_NAME", defService,
  785.                 resource, sizeof resource);
  786.         GetAppParam(configFile, configSection, "RESOURCE_TYPE", defService,
  787.                 res_type, sizeof res_type);
  788.         GetAppParam(configFile, configSection, "RES_VERS_MIN", "1",
  789.                 buf, sizeof buf);
  790.         res_min = atoi(buf);
  791.         GetAppParam(configFile, configSection, "RES_VERS_MAX", "0",
  792.                 buf, sizeof buf);
  793.         res_max = atoi(buf);
  794.     }
  795.  
  796.     return NI_ServiceGet(disp, service, serv_min, serv_max,
  797.                          hasResource ? resource : NULL, res_type, res_min,
  798.                          res_max);
  799. }
  800.  
  801.  
  802. /*
  803.  * Purpose:     Write dispatcher-configuration information to a config file
  804.  *
  805.  * Parameters:
  806.  *   dipp           A pointer to the caller's list of dispatchers, obtained
  807.  *                     from NI_InitServices()
  808.  *   dispatcher     The caller's dispatcher string
  809.  *   dispLen        Length of the caller's dispatcher string
  810.  *
  811.  * Returns:
  812.  *                 0, if bad parameters were provided
  813.  *                 the dispatcher-list serial number, otherwise
  814.  *
  815.  *
  816.  * Description:
  817.  *              Sets up the "NCBI" configuration file with the following
  818.  *              entries in the "NET_SERV" section:
  819.  *              * DISPATCHER is the primary dispatcher name
  820.  *              * DISP_ALT_n for every alternate dispatcher, 1 <= n, a smaller
  821.  *                n indicates a higher priority alternate dispatcher
  822.  *              * DISPSERIALNO is the serial number of the dispatcher list
  823.  *                obtained from a remote dispatcher. This serial number should
  824.  *                be unique for all time ... the dispatcher's serial number
  825.  *                must be changed whenever the master list is modified.
  826.  *
  827.  * Note:
  828.  *              This configuration mechanism is only _one_ recommended
  829.  *              mechanism for network services dispatcher configuration. The
  830.  *              application may perform this configuration in any manner
  831.  *              deemed appropriate by the application programmer.
  832.  *
  833.  *              The value returned by this function is the recommended value
  834.  *              for the dispserialno parameter in a subsequent call to
  835.  *              NI_InitDispatcher().
  836.  */
  837.  
  838. Int4
  839. NI_SetDispConfig(NI_DispInfoPtr PNTR dipp, CharPtr dispatcher, Int2 dispLen)
  840. {
  841.   Int2 num;
  842.   Char dispConfig[20];
  843.   char buf[10];
  844.   Int4 retval;
  845.   NI_DispInfoPtr dip;
  846.  
  847.   if (dipp == NULL || (dip = *dipp) == NULL)
  848.   {
  849.     if (dispatcher != NULL)
  850.     {
  851.       dispatcher[0] = '\0';
  852.     }
  853.     return 0;
  854.   }
  855.  
  856.   if (dip->numdispatchers > 0 && dip->displist != NULL)
  857.   {
  858.     StringNCpy (dispatcher, dip->displist[0], dispLen);
  859.     SetAppParam ("NCBI", "NET_SERV", "DISPATCHER", dip->displist[0]);
  860.   }
  861.  
  862.   for (num = 1; num < dip->numdispatchers; num++)
  863.   {
  864.     sprintf (dispConfig, "DISP_ALT_%d", num);
  865.     SetAppParam ("NCBI", "NET_SERV", dispConfig, dip->displist[num]);
  866.   }
  867.  
  868.   /* wipe out any extraneous old configuration */
  869.   for (num = dip->numdispatchers; num < 100; num++)
  870.   {
  871.     sprintf (dispConfig, "DISP_ALT_%d", num);
  872.     if (GetAppParam("NCBI", "NET_SERV", dispConfig, "", buf, sizeof buf) <= 0)
  873.     {
  874.       break;
  875.     }
  876.     SetAppParam ("NCBI", "NET_SERV", dispConfig, NULL);
  877.   }
  878.  
  879.   retval = dip->serialno;
  880.   sprintf (buf, "%ld", (long) dip->serialno);
  881.   SetAppParam ("NCBI", "NET_SERV", "DISPSERIALNO", buf);
  882.  
  883.   NI_DestroyDispInfo ((NIDispInfoPtr) dip);
  884.   *dipp = NULL;
  885.  
  886.   return retval;
  887. }
  888.  
  889.  
  890. /*
  891.  * Purpose:     End use of network services
  892.  *
  893.  * Parameters:
  894.  *   disp           A pointer to the dispatcher structure
  895.  *
  896.  * Returns:
  897.  *                 0 (always)
  898.  *
  899.  *
  900.  * Description:
  901.  *              Tear down the sockets and data structures associated with
  902.  *              the dispatcher and a server, and free all memory associated
  903.  *              with data structures.
  904.  */
  905.  
  906. Int2
  907. NI_EndServices(NI_DispatcherPtr disp)
  908. {
  909.     if (disp == NULL)
  910.         return 0;
  911.  
  912.     if (disp->referenceCount > 0)
  913.         disp->referenceCount--;
  914.  
  915.     if (disp == currentDisp)
  916.     {
  917.         currentDisp = NULL;
  918.     }
  919.  
  920.     HaltServices (disp);
  921.     NI_SetDispatcher(disp, NULL, NULL, 0, 0);               /* free mem */
  922.  
  923.     if (stackDescription != NULL)
  924.     {
  925.         MemFree(stackDescription);
  926.         stackDescription = NULL;
  927.     }
  928.  
  929.     MemFree(disp);
  930.  
  931.     return 0;
  932. } /* NI_EndServices */
  933.  
  934.  
  935.  
  936. /*
  937.  * Purpose:     Request a catalog from the dispatcher
  938.  *
  939.  * Parameters:
  940.  *   disp           A pointer to the dispatcher structure
  941.  *
  942.  * Returns:
  943.  *                 NULL, if unable to obtain the catalog
  944.  *                 a pointer to the received catalog data structure, otherwise
  945.  *
  946.  *
  947.  * Description:
  948.  *              Send a request to the dispatcher, requesting a catalog, and
  949.  *              wait (up to some timeout) for a response. The dispatcher's
  950.  *              response should either be that catalog, or a NACK.
  951.  */
  952.  
  953. NICatalogPtr
  954. NI_GetCatalog(NI_DispatcherPtr disp)
  955. {
  956.     NICatalogPtr        catp;
  957.     NIMsgPtr            mp, imp;
  958.     NICmdPtr            cmdp;
  959.     struct timeval      timeout;
  960.     int                 ready;
  961.     fd_set              readfds;
  962.  
  963.     if (disp == NULL)
  964.         return NULL;
  965.  
  966.     cmdp = (NICmdPtr) NI_MakeMsgCmd();
  967.     cmdp->seqno = disp->dispHP->seqno++;
  968.     cmdp->code = NI_SEND_CATALOG;
  969.     if ((mp = MsgBuild(NI_COMMAND, disp->dispHP->conid, (VoidPtr) cmdp)) == NULL) {
  970.         ni_errno = NIE_MISC;    /* unable to alloc mem for Msg */
  971.         return NULL;
  972.     }
  973.     if (MsgWrite(disp->dispHP, mp) < 0) {
  974.         ni_errno = NIE_MSGWRITE;
  975.         return NULL;
  976.     }
  977.  
  978.     /* blocks until response from dispatcher or TIMEOUT */
  979.  
  980.     timeout.tv_sec = (Uint4) NI_TIMEOUT_SECS;
  981.     timeout.tv_usec = 0;
  982.     FD_ZERO(&readfds);
  983.     FD_SET(disp->dispHP->sok, &readfds);
  984.     while ((ready = NI_select(FD_SETSIZE, &readfds, NULL, NULL, &timeout)) < 0) {
  985.         if (SOCK_ERRNO == EINTR)
  986.             ;                   /* repeat while interrupted */
  987.         else {
  988.             ni_errno = NIE_SELECT;              /* select error */
  989.             return NULL;
  990.         }
  991.     }
  992.  
  993.     if (FD_ISSET(disp->dispHP->sok, &readfds) != 0) {
  994.         if ((imp = MsgRead(disp->dispHP, FALSE)) == NULL) {
  995.             NI_CLOSESOCKET(disp->dispHP->sok);
  996.             ni_errno = NIE_MSGREAD;
  997.             return NULL;
  998.         }
  999.         switch (imp->type) {
  1000.           case NI_CATALOG:
  1001.             catp = imp->msun.catalog;
  1002.             imp->msun.catalog = NULL;
  1003.             ni_errno = NIE_NO_ERROR;
  1004.             MsgDestroy(imp);
  1005.             return catp;
  1006.             break;
  1007.  
  1008.           case NI_NACK:
  1009.             ni_errno = (enum ni_error) imp->msun.nack->code;
  1010.             if (imp->msun.nack->reason != NULL)
  1011.                 StringCpy(ni_errtext, imp->msun.nack->reason);
  1012.             else
  1013.                 ni_errtext[0] = '\0';
  1014.             MsgDestroy(imp);
  1015.             return NULL;
  1016.  
  1017.           default:
  1018.             MsgDestroy(imp);
  1019.             ni_errno = NIE_MSGUNK;      /* Unknown MSG type */
  1020.             return NULL;
  1021.         }
  1022.     }
  1023.     ni_errno = NIE_CMDTIMEOUT;          /* TIMEOUT */
  1024.     return NULL;
  1025. } /* NI_GetCatalog */
  1026.  
  1027.  
  1028.  
  1029. /*
  1030.  * Purpose:     Create the data structure for a service request
  1031.  *
  1032.  * Parameters:
  1033.  *   disp           A pointer to the dispatcher structure
  1034.  *
  1035.  * Returns:
  1036.  *                 a pointer to the newly created data structure
  1037.  *
  1038.  *
  1039.  * Description:
  1040.  *              Allocate the memory for a service request data structure,
  1041.  *              and fill in some of the fields.
  1042.  * Note:
  1043.  *              There are two ways for a program to issue a service request:
  1044.  *              (1) Multi-step, general method (like IRS form 1040)
  1045.  *                * Build a request with NI_SVCRequestBuild()
  1046.  *                * Populate the request with a specific service request using
  1047.  *                  NI_RequestSetService()
  1048.  *                * Populate the request with zero or more resource requests
  1049.  *                  calling NI_RequestAddResource() once for every resource
  1050.  *                * Send the request with NI_ServiceRequest(), and (hopefully)
  1051.  *                  obtain a connection to a service provider
  1052.  *                * At some later time, delete the request (to save memory)
  1053.  *              (2) One-stop shopping, for simple requirement (like form 1040EZ)
  1054.  *                * Do everything for a service and up to one resource using
  1055.  *                  NI_ServiceGet()
  1056.  */
  1057.  
  1058. NI_ReqPtr
  1059. NI_SVCRequestBuild(NI_DispatcherPtr disp)
  1060. {
  1061.     NI_ReqPtr   reqp;
  1062.  
  1063.     if (disp == NULL)
  1064.         return NULL;
  1065.  
  1066.     reqp = (NI_ReqPtr) NI_MakeRequest();
  1067.     reqp->clientPort = (Uint2) disp->clientPort;
  1068.     reqp->clientAddr = StringSave(disp->localHostAddr);
  1069.     reqp->dispatcher = disp; /* should not be freed when destroying Req */
  1070.  
  1071.     return  reqp;
  1072. } /* NI_SVCRequestBuild */
  1073.  
  1074.  
  1075.  
  1076. /*
  1077.  * Purpose:     Destroy a service request data structure
  1078.  *
  1079.  * Parameters:
  1080.  *   reqp         A pointer to the data structure to be destroyed
  1081.  *
  1082.  *
  1083.  * Description:
  1084.  *              Free all the resources associated with a service request
  1085.  */
  1086.  
  1087. void
  1088. NI_SVCRequestDestroy(NI_ReqPtr reqp)
  1089. {
  1090.     NI_DestroyRequest(reqp);
  1091. } /* NI_SVCRequestDestroy */
  1092.  
  1093.  
  1094.  
  1095. /*
  1096.  * Purpose:     Make a service request for a service and up to one resource
  1097.  *
  1098.  * Parameters:
  1099.  *   disp         A pointer to the dispatcher structure
  1100.  *   svc          Name of requested service
  1101.  *   svcvermin    Minimum version number requested for this service
  1102.  *   svcvermax    Maximum version number requested for this service
  1103.  *   res          Name of requested resource (possibly NULL)
  1104.  *   resvermin    Minimum version number requested for this resource
  1105.  *   resvermax    Maximum version number requested for this resource
  1106.  *
  1107.  * Returns:
  1108.  *                The result of the service request
  1109.  *
  1110.  *
  1111.  * Description:
  1112.  *              Create and issue a service request for the specified
  1113.  *              parameters.
  1114.  */
  1115.  
  1116. NI_HandPtr
  1117. NI_ServiceGet(NI_DispatcherPtr disp, CharPtr svc, Uint2 svcvermin, Uint2 svcvermax, CharPtr res, CharPtr restype, Uint2 resvermin, Uint2 resvermax)
  1118. {
  1119.     NI_ReqPtr   reqp;
  1120.  
  1121.     if (disp == NULL)
  1122.         return NULL;
  1123.  
  1124.     reqp = NI_SVCRequestBuild(disp);
  1125.     NI_RequestSetService(reqp, svc, svcvermin, svcvermax);
  1126.     if (res != NULL)
  1127.         NI_RequestAddResource(reqp, res, restype, resvermin, resvermax);
  1128.  
  1129.     return NI_ServiceRequest(reqp);
  1130. } /* NI_ServiceGet */
  1131.  
  1132.  
  1133.  
  1134. /*
  1135.  * Purpose:     Issue the specified service request
  1136.  *
  1137.  * Parameters:
  1138.  *   req          The pre-formatted service request
  1139.  *
  1140.  * Returns:
  1141.  *               A message handle to the server which is servicing our request,
  1142.  *                   if successful
  1143.  *               NULL, otherwise (ni_errno will indicate a more precise cause)
  1144.  *
  1145.  *
  1146.  * Description:
  1147.  *              Create and issue a service request for the specified
  1148.  *              service request, as follows:
  1149.  *              * Create and listen on a socket on which the server should
  1150.  *                respond
  1151.  *              * Send the service request to the dispatcher
  1152.  *              * Wait for the following two events, in either order:
  1153.  *                (1) A response from the dispatcher, which is either a
  1154.  *                    SVC_RESPONSE (good), or a NACK (bad) {or a timeout}
  1155.  *                (2) A connection request from the server, which we then
  1156.  *                    accept()
  1157.  *              * If both of the two events occur successfully, return with
  1158.  *                success, else, return with failure.
  1159.  */
  1160.  
  1161. NI_HandPtr
  1162. NI_ServiceRequest(NI_ReqPtr req)
  1163. {
  1164.     NI_HandPtr          sconnhp;
  1165. #ifdef OS_MAC
  1166.     Int4                sconnlen;
  1167. #else
  1168.     int                 sconnlen;
  1169. #endif
  1170.     struct sockaddr_in  sconnaddr;
  1171.     NIMsgPtr            mp, imp;
  1172.     NISvcReqPtr         svcreqp;
  1173.     struct timeval      timeout;
  1174.     int                 ready;
  1175.     Boolean             disp_contact = FALSE, serv_contact = FALSE;
  1176.     Uint4               this_req;
  1177.     fd_set              readfds;
  1178.     NI_DispatcherPtr    disp = req->dispatcher;
  1179.  
  1180.     ni_errtext[0] = '\0';
  1181.     if ((sconnhp = MsgMakeHandle(FALSE)) == NULL) {
  1182.         ni_errno = NIE_MAKEHAND;
  1183.         return NULL;
  1184.     }
  1185.  
  1186.     if (activityHook != NULL)
  1187.     {
  1188.         activityHook(NULL, NetServHook_svcreq, 0);
  1189.     }
  1190.  
  1191.     svcreqp = NI_MakeMsgSvcreq();
  1192.     svcreqp->seqno = disp->dispHP->seqno++;
  1193.     svcreqp->platform = (Uint4) NI_GetPlatform();
  1194.     if (stackDescription != NULL)
  1195.     {
  1196.         svcreqp->applId = StringSave(stackDescription);
  1197.     }
  1198.     this_req = svcreqp->seqno;
  1199.     CopyIdentity(disp, svcreqp->uid);
  1200.     NI_DestroyRequest(svcreqp->request);
  1201.     svcreqp->request = req;
  1202.     if ((mp = MsgBuild(NI_SVC_REQUEST, disp->dispHP->conid, (VoidPtr) svcreqp)) == NULL) {
  1203.         NI_DestroyRequest(req);
  1204.         MsgDestroyHandle(sconnhp);
  1205.         ni_errno = NIE_MISC;            /* unable to alloc mem for Msg */
  1206.         return NULL;
  1207.     }
  1208.  
  1209.     if (MsgWrite(disp->dispHP, mp) < 0) {
  1210.         MsgDestroyHandle(sconnhp);
  1211.         ni_errno = NIE_MSGWRITE;
  1212.         return NULL;
  1213.     }
  1214.  
  1215.     /* blocks until SVC_RESPONSE from dispatcher and service or NACK or TIMEOUT */
  1216.  
  1217.     while (!disp_contact || !serv_contact) {
  1218.         timeout.tv_sec = (Uint4) NI_Tscriptor from a "message handle"
  1219.  *
  1220.  * Parameters:
  1221.  *   handp        Message handle
  1222.  *
  1223.  * Returns:
  1224.  *               Socket associated with message handle
  1225.  *
  1226.  *
  1227.  * Description:
  1228.  *              Get the write file desciptor from a message handle. This
  1229.  *              might be useful, for example, when wishing to perform
  1230.  *              "direct" I/O to the socket after a connection has been
  1231.  *              established with a server/client.
  1232.  */
  1233.  
  1234. int
  1235. NI_ServiceGetWriteFd(NI_HandPtr handp)
  1236. {
  1237.     return handp->sok;
  1238. } /* NI_ServiceGetWriteFd */
  1239.  
  1240.  
  1241.  
  1242. /*
  1243.  * Purpose:     Populate a service request with a service name and version #s
  1244.  *
  1245.  * Parameters:
  1246.  *   req          Service request
  1247.  *   name         Service name
  1248.  *   vermin       Minimum version number for this service
  1249.  *   vermax       Maximum version number for this service
  1250.  *
  1251.  * Returns:
  1252.  *               -1, if the name is a NULL pointer
  1253.  *               0, otherwise
  1254.  *
  1255.  *
  1256.  * Description:
  1257.  *              Populate the service request with the specified service name
  1258.  *              and version numbers, dynamically allocating space for the
  1259.  *              service name.
  1260.  */
  1261.  
  1262. Int2
  1263. NI_RequestSetService(NI_ReqPtr req, CharPtr name, Uint2 vermin, Uint2 vermax)
  1264. {
  1265.     if (name == NULL) {
  1266.         ni_errno = NIE_INVAL;
  1267.         return -1;
  1268.     }
  1269.     req->service->name = StringSave(name);
  1270.     req->service->minVersion = vermin;
  1271.     req->service->maxVersion = vermax;
  1272.     req->service->typeL = NULL;
  1273.     return 0;
  1274. } /* NI_RequestSetService */
  1275.  
  1276.  
  1277.  
  1278. /*
  1279.  * Purpose:     Populate a service request with an additional resource
  1280.  *
  1281.  * Parameters:
  1282.  *   req          Service request
  1283.  *   name         Resource name
  1284.  *   type         Service type
  1285.  *   vermin       Minimum version number for this resource
  1286.  *   vermax       Maximum version number for this resource
  1287.  *
  1288.  * Returns:
  1289.  *               -1, if the name is a NULL pointer
  1290.  *               0, otherwise
  1291.  *
  1292.  *
  1293.  * Description:
  1294.  *              Insert the information for this resource into a list of
  1295.  *              resources associated with this service request. This
  1296.  *              function may be called one or more times (or, not at all) to
  1297.  *              populate a service request with one or more resources.
  1298.  */
  1299.  
  1300. Int2
  1301. NI_RequestAddResource(NI_ReqPtr req, CharPtr name, CharPtr type, Uint2 vermin, Uint2 vermax)
  1302.  
  1303. {
  1304.     NIResPtr    resp;
  1305.  
  1306.     if (name == NULL) {
  1307.         ni_errno = NIE_INVAL;
  1308.         return -1;
  1309.     }
  1310.     resp = NI_MakeResource();
  1311.     resp->name = StringSave(name);
  1312.     if (type != NULL)
  1313.         resp->type = StringSave(type);
  1314.     resp->minVersion = vermin;
  1315.     resp->maxVersion = vermax;
  1316.     req->resourceL = ListInsertPrev((VoidPtr) resp, req->resourceL);    /* add to end of list */
  1317.     return 0;
  1318. } /* NI_RequestAddResource */
  1319.  
  1320.  
  1321.  
  1322. /* THESE FUNCTIONS NOT VISIBLE TO API USER */
  1323.  
  1324. /*
  1325.  * Purpose:     Partially halt Network Services
  1326.  *
  1327.  * Parameters:
  1328.  *   disp         A pointer to the dispatcher structure
  1329.  *
  1330.  * Description:
  1331.  *              Halt network services, except refrain from freeing the
  1332.  *              parameters which are set by NI_SetDispatcher().
  1333.  */
  1334.  
  1335. static void
  1336. HaltServices (NI_DispatcherPtr disp)
  1337. {
  1338.     if (disp == NULL)
  1339.         return;
  1340.  
  1341.     if (disp->referenceCount > 0)
  1342.         return;
  1343.  
  1344.     if (activityHook != NULL)
  1345.     {
  1346.         activityHook((NI_HandPtr) disp, NetServHook_dispdisconn, 0);
  1347.     }
  1348.  
  1349.     MsgDestroyHandle(disp->dispHP);
  1350.     MsgDestroyHandle(disp->svcsHP);
  1351.     NI_DestroyRequest(disp->reqResponse);
  1352.     if (disp->identity != NULL) {
  1353.         MemFree (disp->identity->username);
  1354.         MemFree (disp->identity->group);
  1355.         MemFree (disp->identity->domain);
  1356.         MemFree (disp->identity);
  1357.         disp->identity = NULL;
  1358.     }
  1359.     disp->dispHP = NULL;
  1360.     disp->svcsHP = NULL;
  1361.     disp->reqResponse = NULL;
  1362.  
  1363. #ifdef NETP_INET_WSOCK
  1364.     /* we have an obligation to perform one cleanup call for every Startup */
  1365.     while (wsaStartupCount-- > 0)
  1366.     {
  1367.         WSACleanup();
  1368.     }
  1369. #endif
  1370. }
  1371.  
  1372.  
  1373. /*
  1374.  * Purpose:     Lookup a port # in config file and possible NIS
  1375.  *
  1376.  * Parameters:
  1377.  *   service      Name of config. file entry
  1378.  *   networkOrder Boolean, indicates whether value should be returned in host
  1379.  *                order or network order.
  1380.  *
  1381.  * Description:
  1382.  *              Look up the specified entry in the NCBI config. file, and
  1383.  *              lookup in NIS the name obtained from the config file if it's
  1384.  *              non-numeric.
  1385.  *
  1386.  * Note:
  1387.  *              The intent of this function is that, in most cases, the
  1388.  *              GetAppParam() entry will not be present, and a default value
  1389.  *              will be used instead.  The getservbyname() call is intended
  1390.  *              to be a last resort, because this may be slow on some systems.
  1391.  */
  1392.  
  1393. static Uint2
  1394. GetByConfigOrServ(CharPtr service, Boolean networkOrder)
  1395. {
  1396.     struct servent PNTR portEntry;
  1397.     Char                buf[50];
  1398.     Uint2               port;
  1399.  
  1400.     if (GetAppParam("NCBI", "NET_SERV", service, "", buf, sizeof buf) <= 0)
  1401.     {
  1402.         port = 0;
  1403.     } else {
  1404.         if (StrSpn(buf, "0123456789") == StrLen(buf))
  1405.         { /* all numeric */
  1406.             port = atoi(buf);
  1407.             if (networkOrder)
  1408.                port = htons(port);
  1409.         } else {
  1410.             /* entry from configuration file is name to use in getservbyname */
  1411.             if ((portEntry = getservbyname(buf, "tcp")) == NULL)
  1412.             {
  1413.                 port = 0;
  1414.             } else  {
  1415.                 port = portEntry->s_port;
  1416.                 if (! networkOrder)
  1417.                     port = ntohs(port);
  1418.             }
  1419.         }
  1420.     }
  1421.  
  1422.     return port;
  1423. }
  1424.  
  1425.  
  1426. /*
  1427.  * Purpose:     Connect to the dispatcher
  1428.  *
  1429.  * Parameters:
  1430.  *   disp         A pointer to the dispatcher structure
  1431.  *   host         Name of the host on which dispatcher resides
  1432.  *   service      Name of the "service" (i.e., port) to which we should connect
  1433.  *   timeout      How long to wait for dispatcher to respond, 0 ==> use default
  1434.  *
  1435.  * Returns:
  1436.  *               NULL, if the attempt to connect failed
  1437.  *               a pointer to the "Msg" structure for the dispatcher, otherwise
  1438.  *
  1439.  *
  1440.  * Description:
  1441.  *               Connect to the dispatcher on the specified hostname on the
  1442.  *               specified service (where a service maps to a port number).
  1443.  *               This is done by establishing a socket to the dispatcher,
  1444.  *               and then connect()ing to that socket; the dispatcher should
  1445.  *               be listen()ing on that socket, and should subsequently accept()
  1446.  *               the connection request.
  1447.  *
  1448.  *               While doing this, also obtain other useful information;
  1449.  *               namely, the dotted IP address of the local host, and the
  1450.  *               high and low port numbers to be used when attempting
  1451.  *               dispatcher connections. This global information is used
  1452.  *               elsewhere.
  1453.  */
  1454.  
  1455. #ifndef INADDR_NONE
  1456. #define INADDR_NONE             -1
  1457. #endif /* INADDR_NONE */
  1458.  
  1459. static NI_HandPtr
  1460. DispatchConnect(NI_DispatcherPtr disp, CharPtr host, CharPtr service, int timeout)
  1461. {
  1462.     struct hostent      PNTR dispHost, PNTR localHost;
  1463.     struct sockaddr_in  serv_addr;
  1464.     NI_HandPtr          dHP;
  1465.     Uint2               disp_port;
  1466.     Uint4               srvadd;
  1467.     Char                servInetAddr[INETADDR_SIZ], localHostName[SVC_HOST_SIZ];
  1468.     Char                t_service[64];
  1469.     int                 status;
  1470.  
  1471.     if (disp == NULL)
  1472.         return NULL;
  1473.  
  1474.  
  1475.     serv_addr.sin_family = AF_INET;
  1476.  
  1477.     srvadd = inet_addr(host);
  1478.     if ((Int4)srvadd != INADDR_NONE)    /* malformed request */
  1479.         MemCopy((VoidPtr) &serv_addr.sin_addr, (VoidPtr) &srvadd, sizeof(srvadd));
  1480.     else {
  1481.         if ((dispHost = gethostbyname(host)) == NULL) {
  1482.             ni_errno = NIE_NOHOSTENT;
  1483.             return NULL;
  1484.         }
  1485. /*      MemCopy((VoidPtr)&serv_addr.sin_addr, (VoidPtr)(dispHost->h_addr), dispHost->h_length);*/
  1486.         MemCopy(&serv_addr.sin_addr, dispHost->h_addr, dispHost->h_length);
  1487.     }
  1488.     StringCpy(servInetAddr, inet_ntoa(serv_addr.SIN_ADDR));
  1489.  
  1490.     if ((disp_port = GetByConfigOrServ(service, TRUE)) == 0)
  1491.     {
  1492.         if (service)
  1493.             StringCpy(t_service, service);      /* because Windows barfs on the pointer */
  1494.         else
  1495.             t_service[0] = 0;
  1496.         if ((disp_port = htons(atoi(t_service))) == 0)
  1497.             disp_port = htons(NI_DFLT_SVC_PORT);
  1498.     }
  1499.     if (ntohs(disp_port) <= NI_LAST_RESERVED_PORT) {
  1500.         ni_errno = NIE_NOSERVENT;
  1501.         return NULL;
  1502.     }
  1503.  
  1504.     /* get the Internet address of the "local host" */
  1505. #ifdef OS_MAC
  1506.     /* simpler solution to avoid the hazards of gethostname() */
  1507.     {
  1508.         unsigned long localHostId;
  1509.  
  1510.         localHostId = gethostid();
  1511.         StringCpy(disp->localHostAddr, inet_ntoa(* (H_ADDR_TYPE) &localHostId));
  1512.     }
  1513. #else
  1514.     gethostname(localHostName, SVC_HOST_SIZ);
  1515.     if ((localHost = gethostbyname(localHostName)) == NULL) {
  1516.         /* GetAppParam() workaround for PC-NFS 5.0 bug */
  1517.         if (GetAppParam("NCBI", "NET_SERV", "HOST_ADDRESS", "",
  1518.                         disp->localHostAddr, sizeof(disp->localHostAddr)) <= 0)
  1519.         { /* use a bogus address which the dispatcher will try to fix */
  1520.             StringCpy(disp->localHostAddr, "0.0.0.0");
  1521.  
  1522.         }
  1523.     } else {
  1524.         StringCpy(disp->localHostAddr, inet_ntoa(* (H_ADDR_TYPE) localHost->h_addr));
  1525.     }
  1526. #endif /* OS_MAC */
  1527.  
  1528.     if ((disp->loport = GetByConfigOrServ(NI_CLIENT_PORT_LO_NAME, FALSE)) == 0)
  1529.     {
  1530.         if ((disp->loport = atoi(NI_CLIENT_PORT_LO_NAME)) == 0)
  1531.             disp->loport = NI_DFLT_CLILO_PORT;
  1532.     }
  1533.     if (disp->loport <= NI_LAST_RESERVED_PORT) {
  1534.         ni_errno = NIE_BADPORT;         /* bad low client port */
  1535.         return NULL;
  1536.     }
  1537.  
  1538.     if ((disp->hiport = GetByConfigOrServ(NI_CLIENT_PORT_HI_NAME, FALSE)) == 0)
  1539.     {
  1540.         if ((disp->hiport = atoi(NI_CLIENT_PORT_HI_NAME)) == 0)
  1541.             disp->hiport = NI_DFLT_CLIHI_PORT;
  1542.     }
  1543.     if (disp->hiport <= NI_LAST_RESERVED_PORT) {
  1544.         ni_errno = NIE_BADPORT;         /* bad high client port */
  1545.         return NULL;
  1546.     }
  1547.  
  1548.     MemFill((VoidPtr) &serv_addr, '\0', sizeof(serv_addr));
  1549.     serv_addr.sin_family = AF_INET;
  1550.     serv_addr.sin_addr.s_addr = inet_addr(servInetAddr);
  1551.     serv_addr.sin_port = disp_port;
  1552.  
  1553.     if ((dHP = MsgMakeHandle(TRUE)) == NULL)
  1554.         return NULL;
  1555.     MsgSetLJError(dHP);
  1556.     if (timeout > 0)
  1557.         MsgSetReadTimeout(dHP, timeout);
  1558.  
  1559.     if (activityHook != NULL)
  1560.     {
  1561.         activityHook((NI_HandPtr) disp, NetServHook_dispconn, 0);
  1562.     }
  1563.  
  1564.   RETRY:
  1565. #ifndef NETP_INET_NEWT
  1566.     if ((status = connect(dHP->sok, (struct sockaddr PNTR) &serv_addr, sizeof(serv_addr))) < 0) { /* } */
  1567. #else
  1568.     if ((status = connect(dHP->sok, &serv_addr, sizeof(serv_addr))) < 0) {
  1569.         SOCK_ERRNO = ABS(status);
  1570. #endif
  1571.         switch (SOCK_ERRNO) {
  1572.           case EINTR:
  1573.             goto RETRY;
  1574.  
  1575. #ifdef NETP_INET_PCNFS
  1576.           /* This is apparently a bug in PC-NFS 4.0 ... a connection attempt */
  1577.           /* on a non-blocking socket yields errno == 0                      */
  1578.           case 0:
  1579. #endif /* NETP_INET_PCNFS */
  1580.           case EWOULDBLOCK:
  1581.           case EINPROGRESS:
  1582.             /* if the connect()ion is not established immediately, a         */
  1583.             /* select() can be performed where the corresponding "write"     */
  1584.             /* file descriptor will be enabled once the connect()ion has been*/
  1585.             /* established                                                   */
  1586.             if (sokselectw(dHP->sok, timeout) == 0) {
  1587.                 dHP->state = NI_CONNECTED;
  1588.                 return dHP;
  1589.             }
  1590.             break;
  1591.  
  1592.           default:
  1593.             break;
  1594.         }
  1595.         MsgDestroyHandle(dHP);
  1596.         ni_errno = NIE_DISPCONN;        /* can't connect to dispatcher */
  1597.         return NULL;
  1598.     }
  1599.     dHP->state = NI_CONNECTED;
  1600.     return dHP;
  1601. } /* DispatchConnect */
  1602.  
  1603.  
  1604. /*
  1605.  * Purpose:     Get the platform on which this client is running
  1606.  *
  1607.  * Parameters:
  1608.  *                none
  1609.  *
  1610.  * Returns:
  1611.  *               the client's platform, or NI_PLATFORM_UNKNOWN
  1612.  *
  1613.  *
  1614.  * Description:
  1615.  *               Calculate what platform this client is running on.
  1616.  *
  1617.  *
  1618.  * Note:
  1619.  *               Although the initial implementation of this function
  1620.  *               calculates the platform-type at compile-time, it is
  1621.  *               legitimate to perform some computation at run time, e.g.,
  1622.  *               to determine whether this client is using a particular
  1623.  *               low-level driver.
  1624.  *
  1625.  *               The dispatcher and servers should not rely on the
  1626.  *               information which is received for platform-type, because
  1627.  *               the client may be lying, either because of a coding error
  1628.  *               or malice on the part of a client developer.
  1629.  */
  1630.  
  1631. Int2
  1632. NI_GetPlatform (void)
  1633. {
  1634.     static Boolean alreadyInited = FALSE;
  1635.     static Int2 retval;
  1636.  
  1637.     if (alreadyInited)
  1638.     {
  1639.       return retval;
  1640.     }
  1641.  
  1642.     alreadyInited = TRUE;
  1643.  
  1644.     retval = NI_PLATFORM_UNKNOWN;
  1645.  
  1646. #ifdef OS_MAC
  1647.     retval = NI_PLATFORM_MAC;
  1648. #endif
  1649.  
  1650. #ifdef OS_VMS
  1651. #ifdef NETP_INET_TGV
  1652.     retval = NI_PLATFORM_VMS_TGV;
  1653. #endif
  1654. #ifdef NETP_INET_TWG
  1655.     retval = NI_PLATFORM_VMS_TWG;
  1656. #endif
  1657. #ifdef NETP_INET_UCX
  1658.     retval = NI_PLATFORM_VMS_UCX;
  1659. #endif
  1660. #ifdef OS_AXP_VMS
  1661.     retval = NI_PLATFORM_AXP_OPENVMS;
  1662. #endif
  1663. #endif /* OS_VMS */
  1664.  
  1665. #ifdef OS_UNIX
  1666.     retval = NI_PLATFORM_GENERIC_UNIX;
  1667. #ifdef PROC_IBM370
  1668.     retval = NI_PLATFORM_IBM370AIX;
  1669. #endif
  1670. #ifdef OS_UNIX_SUN
  1671.     retval = NI_PLATFORM_SUN;
  1672. #endif
  1673. #if defined(OS_UNIX_OSF1) && defined(PROC_ALPHA)
  1674.     retval = NI_PLATFORM_ALPHA_OSF1;
  1675. #endif
  1676. #ifdef COMP_AUX
  1677.     retval = NI_PLATFORM_AUX;
  1678. #endif
  1679. #if defined(COMP_CRAY) && defined(PROC_YMP)
  1680.     retval = NI_PLATFORM_CRAY;
  1681. #endif
  1682. #ifdef PROC_CONVEX
  1683.     retval = NI_PLATFORM_CONVEX;
  1684. #endif
  1685. #ifdef PROC_HPPA
  1686.     retval = NI_PLATFORM_HPUX;
  1687. #endif
  1688. #ifdef OS_UNIX_NEXT
  1689.     retval = NI_PLATFORM_NEXT;
  1690. #endif
  1691. #ifdef PROC_MIPS
  1692.     retval = NI_PLATFORM_SGI;
  1693. #endif
  1694. #ifdef OS_UNIX_ULTRIX
  1695.     retval = NI_PLATFORM_ULTRIX;
  1696. #endif
  1697. #if defined(OS_UNIX_SYSV) && defined(PROC_SPARC)
  1698.     retval = NI_PLATFORM_SYSV_ON_SPARC;
  1699. #endif
  1700. #endif /* OS_UNIX */
  1701.  
  1702. #ifdef OS_DOS
  1703.     retval = NI_PLATFORM_DOS;
  1704. #ifdef WIN16
  1705.     retval = NI_PLATFORM_WIN16;
  1706. #endif
  1707. #ifdef NETP_INET_NEWT
  1708.     retval = NI_PLATFORM_WIN_NEWT;
  1709. #endif
  1710. #ifdef NETP_INET_PCNFS
  1711.     retval = NI_PLATFORM_WIN_PCNFS;
  1712. #endif
  1713. #ifdef WINSOCK
  1714.     retval = NI_PLATFORM_WIN_WINSOCK;
  1715. #endif
  1716. #endif /* OS_DOS */
  1717.  
  1718. #ifdef OS_WINNT
  1719.     retval = NI_PLATFORM_WINNT;
  1720. #endif
  1721.  
  1722.     return retval;
  1723. }
  1724.  
  1725.  
  1726. /*
  1727.  * Purpose:     Set the "identity" of this client
  1728.  *
  1729.  * Parameters:
  1730.  *   disp         A pointer to the dispatcher structure
  1731.  *   user         New Username
  1732.  *   group        New Groupname
  1733.  *   domain       New DomainName
  1734.  *
  1735.  * Returns:
  1736.  *               0, always
  1737.  *
  1738.  *
  1739.  * Description:
  1740.  *               Allocate the space for the "UID" structure, if not already
  1741.  *               allocated, and populate it with the user name, group name,
  1742.  *               and domain name.
  1743.  */
  1744.  
  1745. static Int2
  1746. SetIdentity(NI_DispatcherPtr disp, CharPtr user, CharPtr group, CharPtr domain)
  1747. {
  1748.     if (disp == NULL)
  1749.         return 0;
  1750.  
  1751.     if (disp->identity == NULL)
  1752.         disp->identity = NI_MakeUid();
  1753.  
  1754.     if (disp->identity->username != NULL)
  1755.         MemFree(disp->identity->username);
  1756.     disp->identity->username = StringSave(user);
  1757.     if (disp->identity->group != NULL)
  1758.         MemFree(disp->identity->group);
  1759.     if (group != NULL)
  1760.         disp->identity->group = StringSave(group);
  1761.     else
  1762.         disp->identity->group = NULL;
  1763.     if (disp->identity->domain != NULL)
  1764.         MemFree(disp->identity->domain);
  1765.     disp->identity->domain = StringSave(domain);
  1766.     return 0;
  1767. } /* SetIdentity */
  1768.  
  1769.  
  1770.  
  1771. /*
  1772.  * Purpose:     Copy from the "identity" UID to the specified UID data struct
  1773.  *
  1774.  * Parameters:
  1775.  *   disp         A pointer to the dispatcher structure
  1776.  *   uid          UID structure to be copied into
  1777.  *
  1778.  * Returns:
  1779.  *               -1, if invalid arguments
  1780.  *               0, otherwise
  1781.  *
  1782.  *
  1783.  * Description:
  1784.  *              Copy fields from the "identity" UID data structure into the
  1785.  *              UID data structure provided by the caller.
  1786.  */
  1787.  
  1788. static Int2
  1789. CopyIdentity(NI_DispatcherPtr disp, NI_UidPtr uid)
  1790. {
  1791.     if (disp == NULL || disp->identity == NULL || uid == NULL)
  1792.         return -1;
  1793.     if  (uid->username != NULL)
  1794.         MemFree(uid->username);
  1795.     uid->username = StringSave(disp->identity->username);
  1796.     if  (uid->group != NULL)
  1797.         MemFree(uid->group);
  1798.     uid->group = StringSave(disp->identity->group);
  1799.     if  (uid->domain != NULL)
  1800.         MemFree(uid->domain);
  1801.     uid->domain = StringSave(disp->identity->domain);
  1802.     return 0;
  1803. } /* CopyIdentity */
  1804.  
  1805.  
  1806.  
  1807. /*
  1808.  * Purpose:     Select the next available port within the given range,
  1809.  *              and bind a socket to it.
  1810.  *
  1811.  * Parameters:
  1812.  *   sok          Socket to be bound to a port (INPUT)
  1813.  *   sokadr       Socket data structure to be populated (OUTPUT)
  1814.  *   loport       Minimum acceptable port number
  1815.  *   hiport       Maximum acceptable port number
  1816.  *
  1817.  * Returns:
  1818.  *               0, if unable to bind to a port
  1819.  *               the selected ("bound") port number, otherwise
  1820.  *
  1821.  *
  1822.  * Description:
  1823.  *              Iterate through the range of acceptable port numbers, until
  1824.  *              an unused port number can be selected to which the socket
  1825.  *              can be bound.
  1826.  */
  1827.  
  1828. static Uint2
  1829. bindPort(int sok, struct sockaddr_in PNTR sokadr, Int2 loport, Int2 hiport)
  1830. {
  1831.     int                 status;
  1832. #ifdef OS_MAC
  1833.     int                 delta = 0;
  1834.     Char                buf[20];
  1835. #endif
  1836.  
  1837.     if (hiport == 0)
  1838.         hiport = loport;
  1839.     if (loport > hiport)
  1840.         return 0;
  1841.  
  1842. #ifdef OS_MAC
  1843.     /* use a hint from the configuration file to avoid port # conflicts */
  1844.     if (hiport > loport && GetAppParam("NCBI", "NET_SERV", "PORT_DELTA", "0",
  1845.         buf, sizeof buf) > 0)
  1846.     {
  1847.         delta = atoi(buf);
  1848.         loport += delta % (hiport - loport);
  1849.         sprintf (buf, "%d", delta + 1);
  1850.         SetAppParam("NCBI", "NET_SERV", "PORT_DELTA", buf);
  1851.     }
  1852. #endif
  1853.  
  1854.     MemFill((VoidPtr) sokadr, '\0', sizeof(struct sockaddr_in));
  1855.     sokadr->sin_family = AF_INET;
  1856.     sokadr->sin_addr.s_addr = INADDR_ANY;
  1857.  
  1858.     while (loport <= hiport) {
  1859.         sokadr->sin_port = htons(loport);
  1860. #ifdef NETP_INET_NEWT
  1861.         if ((status = bind(sok, sokadr, sizeof(struct sockaddr_in))) == 0)
  1862. #else
  1863.         if ((status = bind(sok, (struct sockaddr PNTR) sokadr, sizeof(struct sockaddr_in))) == 0)
  1864. #endif /* NETP_INET_NEWT */
  1865.             return (Uint2) ntohs(sokadr->sin_port);
  1866.         else {
  1867. #ifdef NETP_INET_NEWT
  1868.             SOCK_ERRNO = ABS(status);
  1869. #endif
  1870.             loport++;
  1871.         }
  1872.     }
  1873.     return 0;
  1874. } /* bindPort */
  1875.  
  1876.  
  1877.  
  1878. /* SERVER FUNCTIONS */
  1879.  
  1880. static int      writepipe PROTO((int fd, char *buf, int len));
  1881.  
  1882. /*
  1883.  * Purpose:     Write a message on the pipe from a child server application
  1884.  *              process to its parent NCBID.
  1885.  *
  1886.  * Parameters:
  1887.  *   fd           Pipe file descriptor
  1888.  *   buf          Buffer to be written
  1889.  *   len          Length of buffer
  1890.  *
  1891.  * Returns:
  1892.  *                0, if unable to write because the pipe is full
  1893.  *                number of bytes written, otherwise
  1894.  *
  1895.  *
  1896.  * Description:
  1897.  *              Write the specified number of bytes to a pipe, and handle
  1898.  *              multiple write attempts if necessary, to handle the case where
  1899.  *              a write() may be interrupted by a signal.
  1900.  *
  1901.  * Note:
  1902.  *              This routine is only used by a child process after it has been
  1903.  *              forked and before it has been execed.
  1904.  */
  1905.  
  1906. static int
  1907. writepipe(int fd, char *buf, int len)
  1908. {
  1909.     int         byteswrit;
  1910.  
  1911.   WriteAgain:
  1912.     if ((byteswrit = write(fd, buf, len)) < 0) {
  1913.         switch (errno) {
  1914.           case EINTR:
  1915.             goto WriteAgain;
  1916.  
  1917.           case EWOULDBLOCK:
  1918.           default:
  1919.             return 0;
  1920.         }
  1921.     }
  1922.  
  1923.     return byteswrit;
  1924. } /* writepipe */
  1925.  
  1926.  
  1927.  
  1928. /*
  1929.  * Purpose:     Send an "ACK" from a child server application process to its
  1930.  *              parent NCBID.
  1931.  *
  1932.  * Returns:
  1933.  *                0, if the ACK was sent successfully
  1934.  *                -1, otherwise
  1935.  *
  1936.  *
  1937.  * Description:
  1938.  *              Write an "ACK" from a child server application process to its
  1939.  *              parent NCBID, on the pipe connecting the two processes.
  1940.  *
  1941.  * Note:
  1942.  *              This routine should be called by a child process after it has
  1943.  *              determined that it has started successfully. At most one
  1944.  *              of NI_ServerACK() and NI_ServerNACK() may be called.
  1945.  */
  1946.  
  1947. #define TEMP_BUF_SIZ    256
  1948.  
  1949. int
  1950. NI_ServerACK(void)
  1951. {
  1952.     int         wstat;
  1953.     Char        temp_buf[TEMP_BUF_SIZ];
  1954.  
  1955.     sprintf(temp_buf, PIPE_MSG_FMT, NIE_SERVACK, "OK");
  1956.     if ((wstat = writepipe(STDPIPE, temp_buf, strlen(temp_buf))) <= 0) {
  1957.         ni_errno = NIE_PIPEIO;
  1958.         strcpy(ni_errtext, (wstat == 0) ? "EWOULDBLOCK" : sys_errlist[errno]);
  1959.         return -1;
  1960.     }
  1961.     return 0;
  1962. } /* NI_ServerACK */
  1963.  
  1964.  
  1965.  
  1966. /*
  1967.  * Purpose:     Send an "NACK" from a child server application process to its
  1968.  *              parent NCBID.
  1969.  *
  1970.  * Returns:
  1971.  *                0, if the NACK was sent successfully
  1972.  *                -1, otherwise
  1973.  *
  1974.  *
  1975.  * Description:
  1976.  *              Write an "NACK" from a child server application process to its
  1977.  *              parent NCBID, on the pipe connecting the two processes.
  1978.  *
  1979.  * Note:
  1980.  *              This routine should be called by a child process after it has
  1981.  *              determined that it will be unable to start successfully. In
  1982.  *              the event that this routine is not called (or is unable to
  1983.  *              perform its function), a timeout mechanism must be relied
  1984.  *              upon for the NCBID to realize that a child has started
  1985.  *              unsuccessfully.
  1986.  *
  1987.  *              At most one of NI_ServerACK() and NI_ServerNACK() may be called.
  1988.  */
  1989.  
  1990. int
  1991. NI_ServerNACK(CharPtr err_text)
  1992. {
  1993.     int         wstat;
  1994.     Char        temp_buf[TEMP_BUF_SIZ];
  1995.  
  1996.     sprintf(temp_buf, PIPE_MSG_FMT, NIE_SERVNACK, err_text);
  1997.     if ((wstat = writepipe(STDPIPE, temp_buf, strlen(temp_buf))) <= 0) {
  1998.         ni_errno = NIE_PIPEIO;
  1999.         strcpy(ni_errtext, (wstat == 0) ? "EWOULDBLOCK" : sys_errlist[errno]);
  2000.         return -1;
  2001.     }
  2002.     return 0;
  2003. } /* NI_ServerNACK */
  2004.  
  2005.  
  2006.  
  2007. /*
  2008.  * Purpose:     Open the stream to be used for ASN I/O between a server
  2009.  *              application process and its client.
  2010.  *
  2011.  * Returns:
  2012.  *                NULL, if something went wrong
  2013.  *                a pointer to the Msg structure, otherwise
  2014.  *
  2015.  *
  2016.  * Description:
  2017.  *              Create a "Msg" structure for ASN I/O, and associate the Msg's
  2018.  *              socket with the standard input file descriptor (STDIN), which is
  2019.  *              the communication socket between the server application process
  2020.  *              and its client.
  2021.  *
  2022.  * Note:
  2023.  *              This routine should only be called by a child application
  2024.  *              process (not by a client).
  2025.  */
  2026.  
  2027. NI_HandPtr
  2028. NI_OpenASNIO(void)
  2029. {
  2030.     NI_HandPtr  hp;
  2031.  
  2032.     if ((hp = MsgMakeHandle(FALSE)) == NULL)
  2033.         return NULL;
  2034.  
  2035.     MsgSetReadTimeout(hp, NI_SERV_LISTEN_TIMEOUT);      /* set default for servers to listen */
  2036.  
  2037.     if ((hp->sok = dup(STDIN)) == -1) {         /* STDOUT points to same socket */
  2038.         MsgDestroyHandle(hp);
  2039.         return NULL;
  2040.     }
  2041.     return hp;
  2042. } /* NI_OpenASNIO */
  2043.  
  2044.  
  2045.  
  2046. /*
  2047.  * Purpose:     Close the ASN stream between a server application process and
  2048.  *              its client.
  2049.  *
  2050.  * Returns:
  2051.  *                -1 if something went wrong
  2052.  *                0, otherwise
  2053.  *
  2054.  *
  2055.  * Description:
  2056.  *              Close the stream by closing the socket and deleting the
  2057.  *              associated data structures.
  2058.  *
  2059.  * Note:
  2060.  *              This routine should only be called by a child application
  2061.  *              process (not by a client).
  2062.  */
  2063.  
  2064. Int2
  2065. NI_CloseASNIO(NI_HandPtr hp)
  2066. {
  2067.     return MsgDestroyHandle(hp);
  2068. } /* NI_CloseANSIO */
  2069.  
  2070.  
  2071.  
  2072. /* MISC FUNCTIONS */
  2073.  
  2074. /* sokselectr and sokselectw are not prototyped in ni_lib.h */
  2075.  
  2076. /*
  2077.  * Purpose:     Wait for a "read" socket to become ready to read, or for
  2078.  *              a timeout to occur.
  2079.  *
  2080.  * Returns:
  2081.  *                -1 if something went wrong
  2082.  *                0, otherwise
  2083.  *
  2084.  *
  2085.  * Description:
  2086.  *              Wait for the indicated "read" socket to be marked as
  2087.  *              "selected" by a socket() call.
  2088.  *
  2089.  * Note:
  2090.  *              This routine is presently unused.
  2091.  *
  2092.  *              The timeout mechanism is not exactly enforced, because
  2093.  *              received signals could result in a longer timeout period.
  2094.  */
  2095.  
  2096. int
  2097. sokselectr(int fd)
  2098. {
  2099.     fd_set      rfds;
  2100.     int         ready;
  2101.     struct timeval      timeout;
  2102.  
  2103.     FD_ZERO(&rfds);
  2104.     FD_SET(fd, &rfds);
  2105.     timeout.tv_sec = NI_SELECT_TIMEOUT;
  2106.     timeout.tv_usec = 0;
  2107.     while ((ready = select(fd+1, &rfds, NULL, NULL, &timeout)) == -1) {
  2108.         switch (SOCK_ERRNO) {
  2109.           case EINTR:
  2110.             continue;
  2111.  
  2112.           default:
  2113.             ni_errno = NIE_MISC;
  2114.             sprintf(ni_errtext, "%s", sys_errlist[SOCK_INDEX_ERRNO]);
  2115.             return -1;
  2116.         }
  2117.     }
  2118.     if (ready == 0) {
  2119.         strcpy(ni_errtext, ni_errlist[ni_errno]);
  2120.         ni_errno = NIE_TIMEOUT;
  2121.         return -1;
  2122.     }
  2123.     if (FD_ISSET(fd, &rfds))
  2124.         return 0;
  2125.     else
  2126.         return -1;
  2127. } /* sokselectr */
  2128.  
  2129.  
  2130.  
  2131. /*
  2132.  * Purpose:     Wait for a "write" socket to become ready to write, or for
  2133.  *              a timeout to occur.
  2134.  *
  2135.  * Returns:
  2136.  *                -1 if something went wrong
  2137.  *                0, otherwise
  2138.  *
  2139.  *
  2140.  * Description:
  2141.  *              Wait for the indicated "write" socket to be marked as
  2142.  *              "selected" by a socket() call.
  2143.  *
  2144.  * Note:
  2145.  *              This routine can be used when waiting for a connect() to go
  2146.  *              through successfully.
  2147.  *
  2148.  *              The timeout mechanism is not exactly enforced, because
  2149.  *              received signals could result in a longer timeout period.
  2150.  */
  2151.  
  2152. int
  2153. sokselectw(int fd, int seconds)
  2154. {
  2155.     fd_set      wfds;
  2156.     int         ready;
  2157.     struct timeval      timeout;
  2158.  
  2159.     FD_ZERO(&wfds);
  2160.     FD_SET(fd, &wfds);
  2161.     timeout.tv_sec = NI_SELECT_TIMEOUT;
  2162.     if (seconds > 0) /* override default */
  2163.         timeout.tv_sec = seconds;
  2164.     timeout.tv_usec = 0;
  2165.     while ((ready = select(fd+1, NULL, &wfds, NULL, &timeout)) == -1) {
  2166.         switch (SOCK_ERRNO) {
  2167.           case EINTR:
  2168.             continue;
  2169.  
  2170.           default:
  2171.             ni_errno = NIE_MISC;
  2172.             sprintf(ni_errtext, "%s", sys_errlist[SOCK_INDEX_ERRNO]);
  2173.             return -1;
  2174.         }
  2175.     }
  2176.     if (ready == 0) {
  2177.         strcpy(ni_errtext, ni_errlist[ni_errno]);
  2178.         ni_errno = NIE_TIMEOUT;
  2179.         return -1;
  2180.     }
  2181.     if (FD_ISSET(fd, &wfds))
  2182.     {
  2183. #ifdef OS_UNIX
  2184.         int err;
  2185.         int optlen;
  2186.  
  2187.         optlen = sizeof(int);
  2188.         if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *) &err, &optlen) >= 0 &&
  2189.             err != 0) /* check for an error */
  2190.             return -1; /* got some error */
  2191. #endif /* OS_UNIX */
  2192.         return 0;
  2193.     }
  2194.     else
  2195.         return -1;
  2196. } /* sokselectw */
  2197.  
  2198.  
  2199.  
  2200. /*
  2201.  * Purpose:     Parse the error number from an ASN error string which was
  2202.  *              formatted at a low level.
  2203.  *
  2204.  * Returns:
  2205.  *                -1, if unable to parse the string
  2206.  *                the parsed error number, otherwise
  2207.  *
  2208.  *
  2209.  * Description:
  2210.  *              Parse the error number from an ASN error string which was
  2211.  *              prepared by the ASN tools, and formatted at a low level.
  2212.  *
  2213.  * Note:
  2214.  *              The parsing mechanism is dependent upon any future format
  2215.  *              changes which may occur in the ASN tools.
  2216.  */
  2217.  
  2218. int
  2219. getAsnError(char *str)
  2220. {
  2221.     int         errnum;
  2222.  
  2223.     if (sscanf(str, "%*s %*s %*s [-%d]", &errnum) < 1)
  2224.         errnum = -1;
  2225.     return errnum;
  2226. } /* getAsnError */
  2227.  
  2228.  
  2229. /*
  2230.  * Purpose:     Set the Connection ID file pointer
  2231.  *
  2232.  * Parameters:
  2233.  *   fp           The new value for the file descriptor
  2234.  *
  2235.  *
  2236.  * Description:
  2237.  *              Set the Connection ID file pointer. This is used to update
  2238.  *              the connection ID each time it is updated, to keep the
  2239.  *              value current.
  2240.  *
  2241.  * Note:
  2242.  *              In reality, this should only be called by the dispatcher.
  2243.  */
  2244.  
  2245. void
  2246. SetConFilePtr (FILE *fp)
  2247. {
  2248.     conid_fp = fp;
  2249. }
  2250.  
  2251.  
  2252. /*
  2253.  * Purpose:     Update the connection ID file
  2254.  *
  2255.  * Parameters:
  2256.  *   conid        The new connection ID value
  2257.  *
  2258.  *
  2259.  * Description:
  2260.  *              Update the connection ID file, and be sure to flush the stream,
  2261.  *              to try to ensure that output really occurs.
  2262.  *
  2263.  * Note:
  2264.  *              Should be called every time the "next" connection ID is
  2265.  *              modified.
  2266.  */
  2267.  
  2268. void
  2269. WriteConFile (Uint4 conid)
  2270. {
  2271.     if (conid_fp != NULL) {
  2272.         (void) fseek(conid_fp, 0L, SEEK_SET);
  2273.         (void) FileWrite((CharPtr) &conid, 1, sizeof(conid), conid_fp);
  2274.         (void) fflush (conid_fp);
  2275.     }
  2276. }
  2277.  
  2278.  
  2279. /*
  2280.  * Purpose:     Close the connection ID file
  2281.  *
  2282.  *
  2283.  * Description:
  2284.  *              Close the connection ID file.
  2285.  *              to try to ensure that output really occurs.
  2286.  *
  2287.  * Note:
  2288.  *              In reality, this should only be called by the dispatcher.
  2289.  */
  2290.  
  2291. void
  2292. CloseConFile (void)
  2293. {
  2294.     if (conid_fp != NULL) {
  2295.         fclose (conid_fp);
  2296.         conid_fp = NULL;
  2297.     }
  2298. }
  2299.  
  2300.  
  2301.  
  2302. /*
  2303.  * Purpose:     Check for expired timers
  2304.  *
  2305.  *
  2306.  * Description:
  2307.  *              For every expired timer, call the specified timer
  2308.  *              callback function, which is in turn responsible for cancelling
  2309.  *              the timer.
  2310.  *
  2311.  * Note:
  2312.  *              Timer checks only take place when this function is called.
  2313.  *              Therefore, it is the responsibility of an application to
  2314.  *              intermitently call this function.  This could be done, e.g.
  2315.  *              using the UNIX alarm clock mechanism, or inside of an event
  2316.  *              loop.
  2317.  *
  2318.  *              The order of operations is significant here, because the
  2319.  *              hook function must cancel the timer.  To perform the linked
  2320.  *              list traversal in a less careful manner could result in
  2321.  *              illegal memory accesses.
  2322.  *
  2323.  *              The timer list in managed in a very unsophisticated manner;
  2324.  *              if lots of timers were anticipated, the list would be
  2325.  *              maintained sorted by time, and all of the timer functions
  2326.  *              would need to maintain and traverse the timer list based
  2327.  *              upon this criterion.
  2328.  *
  2329.  *              A count is used as a failsafe mechanism against infinite loops.
  2330.  */
  2331.  
  2332. #define NI_MAX_TIMERS 1000
  2333.  
  2334. void
  2335. NI_ProcessTimers(void)
  2336. {
  2337.     NodePtr t;
  2338.     NodePtr tnew;
  2339.     NI_TimerPtr timer;
  2340.     time_t curtime;
  2341.     int count = NI_MAX_TIMERS;
  2342.  
  2343.     if ((t = timerHead) == NULL)
  2344.     {
  2345.         return;
  2346.     }
  2347.  
  2348.     curtime = GetSecs();
  2349.  
  2350.     do {
  2351.         timer = (NI_TimerPtr) t->elem;
  2352.         tnew = ListGetNext(t);
  2353.         if (timer != NULL && timer->timeout != NULLB &&
  2354.             timer->timeout <= curtime)
  2355.         { /* fire timer */
  2356.             /* mark the timer so it won't fire again */
  2357.             timer->timeout = NULLB;
  2358.             if (timer->hook != NULL)
  2359.             {
  2360.                 timer->hook(timer->hookParam);
  2361.             }
  2362.         }
  2363.         if (t == tnew)
  2364.         { /* data structure error, time to bail out */
  2365.             break;
  2366.         }
  2367.         t = tnew;
  2368.     } while (t != timerHead && t != NULL && --count > 0);
  2369. }
  2370.  
  2371.  
  2372. /*
  2373.  * Purpose:     Return the time when the next timeout will occur
  2374.  *
  2375.  * Returns:     The time, in seconds, when the next scheduled timeout will
  2376.  *              occur, or NULLB, if there are no timers set.
  2377.  *
  2378.  * Description:
  2379.  *              Return the time when the next timer timeout will occur.
  2380.  *              This information is typically used with the select()
  2381.  *              system call, to ensure that a timeout parameter is passed
  2382.  *              to select() which is sufficiently short to ensure that
  2383.  *              the application will call NI_ProcessTimers() at an
  2384.  *              appropriate time.
  2385.  *
  2386.  * Note:
  2387.  *              The timer list in managed in a very unsophisticated manner;
  2388.  *              if lots of timers were anticipated, the list would be
  2389.  *              maintained sorted by time, and all of the timer functions
  2390.  *              would need to maintain and traverse the timer list based
  2391.  *              upon this criterion.
  2392.  */
  2393.  
  2394. time_t
  2395. NI_GetNextWakeup(void)
  2396. {
  2397.     time_t next_wakeup = NULLB;
  2398.     NodePtr t;
  2399.     NI_TimerPtr timer;
  2400.  
  2401.     NI_ProcessTimers();
  2402.  
  2403.     if ((t = timerHead) == NULL)
  2404.     {
  2405.         return NULLB;
  2406.     }
  2407.  
  2408.     do {
  2409.         t = ListGetNext(t);
  2410.         timer = (NI_TimerPtr) t->elem;
  2411.         if (next_wakeup == NULLB || (timer->timeout != NULLB &&
  2412.             timer->timeout < next_wakeup))
  2413.         {
  2414.             next_wakeup = timer->timeout;
  2415.         }
  2416.     } while (t != timerHead && t != NULL);
  2417.  
  2418.     return next_wakeup;
  2419. }
  2420.  
  2421.  
  2422. /*
  2423.  * Purpose:     Set a timer
  2424.  *
  2425.  * Parameters:
  2426.  *   timeout      The time in seconds when the timer should expire
  2427.  *   hook         Callback to be called when (if) the timer expires
  2428.  *   hookParam    Parameter to be passed to caller's hook when the timer expires
  2429.  *
  2430.  *
  2431.  * Returns:     The "timer ID", really a pointer to the timer data structure
  2432.  *
  2433.  *
  2434.  * Description:
  2435.  *              Sets a timer with the appropriate parameters.
  2436.  *
  2437.  * Note:
  2438.  *              The timer list in managed in a very unsophisticated manner;
  2439.  *              if lots of timers were anticipated, the list would be
  2440.  *              maintained sorted by time, and all of the timer functions
  2441.  *              would need to maintain and traverse the timer list based
  2442.  *              upon this criterion.
  2443.  *
  2444.  *              It is the responsibility of the application (usually the
  2445.  *              hook function) to cancel the timer.
  2446.  */
  2447.  
  2448. NodePtr
  2449. NI_SetTimer(time_t timeout, NI_TimeoutHook hook, Pointer hookParam)
  2450. {
  2451.     NodePtr t;
  2452.     NI_TimerPtr timer;
  2453.  
  2454.     timer = (NI_TimerPtr) MemNew(sizeof(NI_Timer));
  2455.     timer->timeout = timeout;
  2456.     timer->hook = hook;
  2457.     timer->hookParam = hookParam;
  2458.     t = ListInsert(timer, timerHead);
  2459.     timerHead = t;
  2460.  
  2461.     return t;
  2462. }
  2463.  
  2464.  
  2465. /*
  2466.  * Purpose:     Cancel a timer
  2467.  *
  2468.  * Parameters:
  2469.  *   timerID      The ID of the timer
  2470.  *
  2471.  *
  2472.  * Description:
  2473.  *              Cancel the specified timer by deleting the entry and its
  2474.  *              associated data structure.
  2475.  *
  2476.  * Note:
  2477.  *              The timer list in managed in a very unsophisticated manner;
  2478.  *              if lots of timers were anticipated, the list would be
  2479.  *              maintained sorted by time, and all of the timer functions
  2480.  *              would need to maintain and traverse the timer list based
  2481.  *              upon this criterion.
  2482.  */
  2483.  
  2484. void
  2485. NI_CancelTimer(NodePtr timerId)
  2486. {
  2487.     if (timerId != NULL)
  2488.     {
  2489.         MemFree (timerId->elem);
  2490.         timerHead = ListDelete(timerId);
  2491.     }
  2492.  
  2493.     NI_ProcessTimers();
  2494. }
  2495.  
  2496.  
  2497. /*
  2498.  * Purpose:     Set an activity hook, to inform the application of key events
  2499.  *
  2500.  * Parameters:
  2501.  *   hook         The hook (callback function)
  2502.  *
  2503.  *
  2504.  * Description:
  2505.  *              Setup a hook function which will subsequently be used to
  2506.  *              inform the application of various events; these currently
  2507.  *              include:
  2508.  *                * Connection to dispatcher
  2509.  *                * Disconnection from dispatcher
  2510.  *                * Service connection
  2511.  *                * Service disconnection
  2512.  *                * Bytes written
  2513.  *                * Bytes read
  2514.  *
  2515.  * Note:
  2516.  *              This hook is global for the running application.
  2517.  */
  2518.  
  2519. void
  2520. NI_SetActivityHook (NI_NetServHook hook)
  2521. {
  2522.     activityHook = hook;
  2523. }
  2524.  
  2525.  
  2526. /*
  2527.  * Purpose:     Return the current activity hook
  2528.  *
  2529.  *
  2530.  * Description:
  2531.  *              Return the current activity hook.  This is only intended
  2532.  *              to be used internally by the Network Services library.
  2533.  *              This function is used to avoid making activityHook into a
  2534.  *              global variable.
  2535.  */
  2536.  
  2537. NI_NetServHook
  2538. NI_ActivityHook (void)
  2539. {
  2540.     return activityHook;
  2541. }
  2542.