botsense  3.2.0
RoadNarrows Client-Server Proxied Services Framework
bsProxyVConn.c
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: BotSense
4 //
5 // Program: bsProxy
6 //
7 // File: bsProxyVConn.c
8 //
9 /*! \file
10  *
11  * $LastChangedDate: 2010-08-20 11:36:38 -0600 (Fri, 20 Aug 2010) $
12  * $Rev: 568 $
13  *
14  * \brief Virtual connections management operations.
15  *
16  * \author Robin Knight (robin.knight@roadnarrows.com)
17  *
18  * \copyright
19  * \h_copy 2010-2017. RoadNarrows LLC.\n
20  * http://www.roadnarrows.com\n
21  * All Rights Reserved
22  */
23 // Permission is hereby granted, without written agreement and without
24 // license or royalty fees, to use, copy, modify, and distribute this
25 // software and its documentation for any purpose, provided that
26 // (1) The above copyright notice and the following two paragraphs
27 // appear in all copies of the source code and (2) redistributions
28 // including binaries reproduces these notices in the supporting
29 // documentation. Substantial modifications to this software may be
30 // copyrighted by their authors and need not follow the licensing terms
31 // described here, provided that the new terms are clearly indicated in
32 // all files where they apply.
33 //
34 // IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY MEMBERS/EMPLOYEES
35 // OF ROADNARROW LLC OR DISTRIBUTORS OF THIS SOFTWARE BE LIABLE TO ANY
36 // PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
37 // DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
38 // EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN ADVISED OF
39 // THE POSSIBILITY OF SUCH DAMAGE.
40 //
41 // THE AUTHOR AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
42 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
43 // FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
44 // "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
45 // PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
46 //
47 ////////////////////////////////////////////////////////////////////////////////
48 
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <limits.h>
52 #include <string.h>
53 #include <errno.h>
54 #include <pthread.h>
55 
56 #include "rnr/rnrconfig.h"
57 #include "rnr/log.h"
58 #include "rnr/new.h"
59 
60 #include "botsense/BotSense.h"
61 #include "botsense/bsProxyModIF.h"
62 
63 #include "bsProxy.h"
64 
65 
66 // ---------------------------------------------------------------------------
67 // Private Interface
68 // ---------------------------------------------------------------------------
69 
70 //
71 // Local data
72 //
73 static pthread_mutex_t BsVConnBusyMutex; ///< operations mutex
74 static pthread_cond_t BsVConnBusyCond; ///< block condition
76  ///< table of virtual connections
77 static uint_t BsVConnActiveCnt; ///< Number of active v. connections
78 
79 
80 //.............................................................................
81 // Mutual Exclusion Functions
82 //.............................................................................
83 
84 /*!
85  * \brief Lock virtual connection's global busy mutual exclusion.
86  */
87 static inline void VConnLockBusy()
88 {
89  int rc;
90 
91  if( (rc = pthread_mutex_lock(&BsVConnBusyMutex)) != 0 )
92  {
93  errno = rc;
94  LOGSYSERROR("pthread_mutex_lock()");
95  }
96 }
97 
98 /*!
99  * \brief Unlock virtual connection's global busy mutual exclusion.
100  */
101 static inline void VConnUnlockBusy()
102 {
103  int rc;
104 
105  if( (rc = pthread_mutex_unlock(&BsVConnBusyMutex)) != 0 )
106  {
107  errno = rc;
108  LOGSYSERROR("pthread_mutex_unlock()");
109  }
110 }
111 
112 /*!
113  * \brief Try to lock virtual connection's global busy mutual exclusion.
114  *
115  * \return
116  * Returns true if lock is acquired. Otherwise returns false if mutex already
117  * locked.
118  */
119 static inline bool_t VConnBusyTryLock()
120 {
121  return pthread_mutex_trylock(&BsVConnBusyMutex) == 0? true: false;
122 }
123 
124 /*!
125  * \brief Broadcast that a virtual connection has been freed or deleted.
126  *
127  * A broadcast will unblock all threads currently blocked on the busy condition
128  * variable. Only those thread waiting on virtual connections whose condition
129  * has changed will run. If multiple threads are waiting on the same
130  * virtual connection, then only one is schedule to run.
131  */
132 static inline void VConnBroadcastNotBusy()
133 {
134  int rc;
135 
136  if( (rc = pthread_cond_broadcast(&BsVConnBusyCond)) != 0 )
137  {
138  errno = rc;
139  LOGSYSERROR("pthread_cond_broadcast()");
140  }
141 }
142 
143 /*!
144  * \brief Wait on a virtual connection to become's free.
145  */
146 static inline void VConnWaitNotBusy()
147 {
148  int rc;
149 
150  if( (rc = pthread_cond_wait(&BsVConnBusyCond, &BsVConnBusyMutex)) != 0 )
151  {
152  errno = rc;
153  LOGSYSERROR("pthread_cond_wait()");
154  }
155 }
156 
157 
158 // ---------------------------------------------------------------------------
159 // Public Interface
160 // ---------------------------------------------------------------------------
161 
162 /*!
163  * \brief The bsProxy virtual connections one-time initialization.
164  */
166 {
167  // create mutex
168  pthread_mutex_init(&BsVConnBusyMutex, NULL);
169 
170  // create condition
171  pthread_cond_init(&BsVConnBusyCond, NULL);
172 }
173 
174 /*!
175  * \brief Create a new, allocated virtual connection entry.
176  *
177  * The new virtual connection is added to the virtual connection table. It is
178  * marked busy (i.e. locked). The caller must call VConnRelease() or
179  * VConnDelete() to free the entry.
180  *
181  * \return Allocated virtual connection on success, NULL on failure.
182  */
184 {
185  BsProxyVConn_T *pVConn = NULL;
186  BsVConnHnd_T hndVConn;
187 
188  // lock virtual connection table
189  VConnLockBusy();
190 
191  // find an empty virtual connection table slot
193  {
194  for(hndVConn=0; hndVConn<BSPROXY_VCONN_MOD_NUMOF; ++hndVConn)
195  {
196  if( BsVConnTbl[hndVConn] == NULL )
197  {
198  pVConn = NEW(BsProxyVConn_T);
199  pVConn->m_bBusy = true;
200  pVConn->m_hndVConn = hndVConn;
201  BsVConnTbl[hndVConn] = pVConn;
203  break;
204  }
205  }
206  }
207 
208  // unlock virtual connection table
209  VConnUnlockBusy();
210 
211  return pVConn;
212 }
213 
214 /*!
215  * \brief Delete allocated virtual connection entry.
216  *
217  * The deletion event is broadcasted to all blocked threads waiting on this
218  * connection.
219  */
221 {
222  VConnLockBusy();
223 
224  if( pVConn != NULL )
225  {
226  if( BSPROXY_CHK_VCONN_HND(pVConn->m_hndVConn) )
227  {
228  BsVConnTbl[pVConn->m_hndVConn] = NULL;
230  }
231  delete(pVConn);
232  }
233 
235 
236  VConnUnlockBusy();
237 }
238 
239 /*!
240  * \brief Get the virtual connection entry associated with the handle.
241  *
242  * \warning The virtual connection is not locked.
243  *
244  * \param hndVConn \h_botsense virtual connection handle.
245  *
246  * \return Virtual connection entry on success, NULL on failure.
247  */
249 {
250  if( !BSPROXY_CHK_VCONN_HND(hndVConn) )
251  {
252  return NULL;
253  }
254  else
255  {
256  return BsVConnTbl[hndVConn];
257  }
258 }
259 
260 /*!
261  * \brief Open special server virtual connection.
262  *
263  * \copydoc doc_return_std
264  */
266 {
267  BsProxyThCtl_T *pThCtl; // server service thread control
268  BsProxyVConn_T *pVConn; // new virtual connection entry
269  BsVConnHnd_T hndVConn = BSPROXY_VCONN_SERVER; // reserved server handle
270  int rc; // return code
271 
272  //
273  // Create the one server thread.
274  //
275  if( (pThCtl = ThCreateServerThread()) == NULL )
276  {
277  rc = -BS_ECODE_NO_EXEC;
278  LOGERROR("%s: Could not create server service thread.", ServerHasName());
279  }
280 
281  //
282  // Add this special virtual connection to the table. No mutual exclusion is
283  // done since the server should be started prior to servicing an client
284  // requests.
285  //
286  else
287  {
288  pVConn = NEW(BsProxyVConn_T);
289  pVConn->m_bBusy = false;
290  pVConn->m_hndClient = -1;
291  pVConn->m_hndVConn = hndVConn;
292  pVConn->m_pThCtl = pThCtl;
293  pVConn->m_pModIF = NULL;
294  pVConn->m_rd = -1;
295 
296  BsVConnTbl[hndVConn] = pVConn;
298 
299  rc = BS_OK;
300  }
301 
302  return rc;
303 }
304 
305 /*!
306  * \brief Open a virtual device connection.
307  *
308  * To open a virtual connection:
309  * \li A new entry is created.
310  * \li The associated device thread is located or created.
311  * \li The associated interface module is located or loaded.
312  * \li The module's exported ModOpen() routine is called.
313  *
314  * \param hndClient \h_botsense client handle.
315  * \param sDevName Device path name.
316  * \param sModName Interface module path name.
317  * \param argbuf Packed argument buffer specific to interface module's
318  * ModOpen().
319  * \param uArgLen Number of packed bytes in argument buffer.
320  * \param bTrace Initial module message tracing state.
321  *
322  * \return On success, returns virtual connection handle.\n
323  * \copydoc doc_return_ecode
324  */
326  const char *sDevName,
327  const char *sModName,
328  byte_t argbuf[],
329  size_t uArgLen,
330  bool_t bTrace)
331 {
332  BsProxyVConn_T *pVConn = NULL; // new virtual connection entry
333  char *sModUri = NULL; // module URI
334  char *sDevUri = NULL; // device URI
335  BsProxyModIF_T *pModIF = NULL; // interface module interface
336  BsProxyThCtl_T *pThCtl = NULL; // device thread control block
337  int rc; // return code
338  int rd; // resource descriptor
339 
340  //
341  // Allocate a new virtual connection. If no more virtual connections are
342  // available, NULL is returned. The return virtual connection is left in
343  // the acquired state which requires releasing prior to becoming available
344  // for normal operations.
345  //
346  if( (pVConn = VConnNew()) == NULL )
347  {
348  rc = -BS_ECODE_NO_RSRC;
349  BSPROXY_LOG_ERROR(hndClient, rc,
350  "No more virtual connections are available.");
351  }
352 
353  //
354  // Convert module path name to canonical uniform resource identifier.
355  //
356  else if( (sModUri = ModNewModUri(sModName)) == NULL )
357  {
358  rc = -BS_ECODE_BAD_VAL;
359  BSPROXY_LOG_ERROR(hndClient, rc, "\"%s\": Bad module name.", sModName);
360  }
361 
362  //
363  // Convert device path name to canonical uniform resource identifier.
364  //
365  else if( (sDevUri = ThNewDevUri(sDevName)) == NULL )
366  {
367  rc = -BS_ECODE_BAD_VAL;
368  BSPROXY_LOG_ERROR(hndClient, rc, "\"%s\": Bad device name.", sDevName);
369  }
370 
371  //
372  // Dynamically load and initialize library interface module and return the
373  // module's interface. If the module already exists, then the same interface
374  // is returned. The module interface maintains a reference count which is
375  // incremented.
376  //
377  else if( (pModIF = ModLoad(sModUri)) == NULL )
378  {
379  rc = -BS_ECODE_NO_MOD;
380  BSPROXY_LOG_ERROR(hndClient, rc,
381  "Could not load or attach to \"%s\" interface module.", sModUri);
382  }
383 
384  //
385  // Call the interface module's open() function to open the device and allocate
386  // any internal resources. The return resource descriptor is typically a
387  // file or socket descript, but may be any unique non-negative integer
388  // defined by the module.
389  //
390  else if( (rd = pModIF->m_fnModOpen(pVConn->m_hndVConn, bTrace,
391  sDevUri, argbuf, uArgLen)) < 0 )
392  {
393  rc = -BS_ECODE_NO_EXEC;
394  BSPROXY_LOG_ERROR(hndClient, rd,
395  "Could not open \"%s\" interface module.", pModIF->m_sModUri);
396  }
397 
398  //
399  // Create a device thread returning the thread's control. If the thread
400  // already exists, then the same thread control is returned. The thread
401  // control maintains a reference count which is incremented.
402  //
403  else if( (pThCtl = ThCreateDevThread(sDevUri)) == NULL )
404  {
405  rc = -BS_ECODE_NO_EXEC;
406  BSPROXY_LOG_ERROR(hndClient, rc,
407  "Could not create or attach to \"%s\" device service thread.",
408  sDevUri);
409  }
410 
411  //
412  // Success.
413  //
414  // Populate the virtual connection's data. All allocated data are not owned by
415  // the virtual connection. That is, any allocated data will not be deleted
416  // when the virtual connection is deleted.
417  //
418  else
419  {
420  pVConn->m_hndClient = hndClient;
421  pVConn->m_pThCtl = pThCtl;
422  pVConn->m_pModIF = pModIF;
423  pVConn->m_rd = rd;
424 
425  VConnRelease(pVConn->m_hndVConn);
426 
427  rc = (int)pVConn->m_hndVConn;
428 
429  LOGDIAG1("%s: %s: Virtual connection %d opened: %s('%s',rd=%d).",
430  ServerHasName(), ClientHasName(hndClient), pVConn->m_hndVConn,
431  sModUri, sDevUri, rd);
432 
433  LOGDIAG2("%s: %u active module virtual connections.",
435  }
436 
437  //
438  // Error occurred, so clean up before returning.
439  //
440  if( rc < 0 )
441  {
442  if( pThCtl != NULL )
443  {
444  ThDestroyThread(pThCtl);
445  }
446  if( pModIF != NULL )
447  {
448  ModUnload(pModIF);
449  }
450  if( pVConn != NULL )
451  {
452  VConnDelete(pVConn);
453  }
454  }
455 
456  //
457  // Fixed clean up.
458  //
459  delete(sModUri);
460  delete(sDevUri);
461 
462  return rc;
463 }
464 
465 /*!
466  * \brief Close the virtual connection.
467  *
468  * Closing a virtual connections may close the resource (file) descriptor
469  * associated with the device, unload the interface module associated with
470  * the device, and terminate the device thread. These actions occur only when
471  * the reference counts are zero.
472  *
473  * \param hndClient \h_botsense client handle.
474  * \param hndVConn \h_botsense virtual connection handle.
475  *
476  * \copydoc doc_return_std
477  */
479 {
480  BsProxyVConn_T *pVConn;
481  int rc;
482 
483  // Only virtual connections associated with devices can be closed.
484  if( !BSPROXY_CHK_MOD_VCONN_HND(hndVConn) )
485  {
487  }
488 
489  //
490  // Acquire virtual connection associated with handle. Aquiring a virtual
491  // connections locks out other thread access to the vConnection until it
492  // is released.
493  //
494  else if( (pVConn = VConnAcquire(hndVConn)) == NULL )
495  {
496  rc = -BS_ECODE_NO_VCONN;
497  BSPROXY_LOG_ERROR(hndClient, rc, "VConn=%d", hndVConn);
498  }
499 
500  else
501  {
502  //
503  // Destroy the device thread. The thread reference count is decremented and
504  // only when it drops to zero is the thread actually terminated.
505  //
506  ThDestroyThread(pVConn->m_pThCtl);
507 
508  //
509  // Close the device associated with virtual connection. The actual device
510  // may or may not be closed, depending on the device and module
511  // implementation. For example, for an \h_i2c Bus device, the device is
512  // only closed when the last reference to that device is closed.
513  //
514  pVConn->m_pModIF->m_fnModClose(pVConn->m_hndVConn);
515 
516  //
517  // Dynamically unload the interface module from the bsProxy application. The
518  // module's reference count is decremented and only when it drops to zero is
519  // the module actually unloaded.
520  //
521  ModUnload(pVConn->m_pModIF);
522 
523  LOGDIAG1("%s: %s: Virtual connection %d closed.",
524  ServerHasName(), ClientHasName(hndClient), pVConn->m_hndVConn);
525 
526  //
527  // Delete the virtual connection. The slot is now available for a new
528  // virtual connection.
529  //
530  VConnDelete(pVConn);
531 
532  LOGDIAG2("%s: %u active module virtual connections.",
534 
535  rc = BS_OK;
536  }
537 
538  return rc;
539 }
540 
541 /*!
542  * \brief Acquire virtual connection, locking it from other threads.
543  *
544  * The calling thread is blocked until the virtual connection is availble or
545  * has been deleted.
546  *
547  * \param hndVConn \h_botsense virtual connection handle.
548  *
549  * \return On success, returns pointer to locked virtual connection.
550  * On failure, NULL is returned.
551  */
553 {
554  BsProxyVConn_T *pVConn;
555 
556  if( !BSPROXY_CHK_VCONN_HND(hndVConn) )
557  {
558  return NULL;
559  }
560 
561  VConnLockBusy();
562 
563  while( (BsVConnTbl[hndVConn] != NULL) && BsVConnTbl[hndVConn]->m_bBusy )
564  {
566  }
567 
568  if( (pVConn = BsVConnTbl[hndVConn]) != NULL )
569  {
570  pVConn->m_bBusy = true;
571  }
572 
573  VConnUnlockBusy();
574 
575  return pVConn;
576 }
577 
578 /*!
579  * \brief Release the locked virtual client.
580  *
581  * A broadcast is sent to all blocking threads on the freed event.
582  *
583  * \param hndVConn \h_botsense virtual connection handle.
584  */
586 {
587  if( !BSPROXY_CHK_VCONN_HND(hndVConn) )
588  {
589  return;
590  }
591 
592  VConnLockBusy();
593 
594  if( BsVConnTbl[hndVConn] != NULL )
595  {
596  BsVConnTbl[hndVConn]->m_bBusy = false;
597  }
598 
600 
601  VConnUnlockBusy();
602 }
static uint_t BsVConnActiveCnt
Number of active v. connections.
Definition: bsProxyVConn.c:77
static void VConnUnlockBusy()
Unlock virtual connection&#39;s global busy mutual exclusion.
Definition: bsProxyVConn.c:101
static bool_t VConnBusyTryLock()
Try to lock virtual connection&#39;s global busy mutual exclusion.
Definition: bsProxyVConn.c:119
BsProxyVConn_T * VConnNew()
Create a new, allocated virtual connection entry.
Definition: bsProxyVConn.c:183
<b><i>BotSense</i></b> bsProxy IP server declarations.
static pthread_cond_t BsVConnBusyCond
block condition
Definition: bsProxyVConn.c:74
#define BS_ECODE_BAD_VAL
bad value
Definition: BotSense.h:77
int m_rd
module resource descriptor
Definition: bsProxy.h:227
BsProxyThCtl_T * ThCreateServerThread()
Create the special server service thread.
static BsProxyVConn_T * BsVConnTbl[BSPROXY_VCONN_NUMOF]
table of virtual connections
Definition: bsProxyVConn.c:75
static void VConnWaitNotBusy()
Wait on a virtual connection to become&#39;s free.
Definition: bsProxyVConn.c:146
char * ThNewDevUri(const char *sDevName)
Convert the device name to a quasi Uniform Resource Id.
int VConnOpenDev(BsProxyClientHnd_T hndClient, const char *sDevName, const char *sModName, byte_t argbuf[], size_t uArgLen, bool_t bTrace)
Open a virtual device connection.
Definition: bsProxyVConn.c:325
#define BSPROXY_VCONN_NUMOF
total number of module handles
Definition: BotSense.h:144
static void VConnBroadcastNotBusy()
Broadcast that a virtual connection has been freed or deleted.
Definition: bsProxyVConn.c:132
BsProxyThCtl_T * ThCreateDevThread(const char *sDevUri)
Create a device service thread.
#define BSPROXY_VCONN_SERVER
handle for server-terminated msgs
Definition: BotSense.h:140
BsProxyThCtl_T * m_pThCtl
service thread
Definition: bsProxy.h:225
void VConnRelease(BsVConnHnd_T hndVConn)
Release the locked virtual client.
Definition: bsProxyVConn.c:585
int VConnOpenServer()
Open special server virtual connection.
Definition: bsProxyVConn.c:265
INLINE_IN_H const char * ServerHasName()
Get the <b><i>BotSense</i></b> server&#39;s official name.
Definition: bsProxy.h:485
static pthread_mutex_t BsVConnBusyMutex
operations mutex
Definition: bsProxyVConn.c:73
#define BS_OK
not an error, success
Definition: BotSense.h:66
#define BS_ECODE_BAD_VCONN_HND
bad virtual connection handle
Definition: BotSense.h:79
BsModCloseFunc_P m_fnModClose
device close
Definition: bsProxy.h:128
int BsProxyClientHnd_T
bsProxy server client handle
Definition: bsProxy.h:114
char * ModNewModUri(const char *sModName)
Convert the module name to a quasi Uniform Resource Id.
Definition: bsProxyMod.c:1042
static void VConnLockBusy()
Lock virtual connection&#39;s global busy mutual exclusion.
Definition: bsProxyVConn.c:87
#define BS_ECODE_NO_EXEC
cannot execute
Definition: BotSense.h:88
void ThDestroyThread(BsProxyThCtl_T *pThCtl)
Destroy service thread.
BsProxyVConn_T * VConnGet(BsVConnHnd_T hndVConn)
Get the virtual connection entry associated with the handle.
Definition: bsProxyVConn.c:248
BsProxyClientHnd_T m_hndClient
proxied client handle
Definition: bsProxy.h:224
BsProxyModIF_T * ModLoad(const char *sModUri)
Load the interface module.
Definition: bsProxyMod.c:1089
#define BS_ECODE_NO_RSRC
no resource available
Definition: BotSense.h:85
#define BSPROXY_CHK_VCONN_HND(hndVConn)
Definition: bsProxy.h:428
#define BSPROXY_LOG_ERROR(hndClient, ecode, efmt,...)
Log Proxy Server Error.
Definition: bsProxy.h:288
BsProxyVConn_T * VConnAcquire(BsVConnHnd_T hndVConn)
Acquire virtual connection, locking it from other threads.
Definition: bsProxyVConn.c:552
#define BS_ECODE_NO_MOD
no interface module
Definition: BotSense.h:83
const char * m_sModUri
module Uniform Resource Id
Definition: bsProxy.h:122
BsVConnHnd_T m_hndVConn
self reference
Definition: bsProxy.h:223
bool_t m_bBusy
virtual connection is [not] busy
Definition: bsProxy.h:222
void VConnDelete(BsProxyVConn_T *pVConn)
Delete allocated virtual connection entry.
Definition: bsProxyVConn.c:220
<b><i>BotSense</i></b> bsProxy Dynamically Linked Library module interface.
#define BSPROXY_CHK_MOD_VCONN_HND(hndVConn)
Definition: bsProxy.h:441
#define BS_ECODE_NO_VCONN
virtual connection not found
Definition: BotSense.h:80
#define BSPROXY_VCONN_MOD_NUMOF
number of module-specific handles
Definition: BotSense.h:143
BsModOpenFunc_P m_fnModOpen
device open
Definition: bsProxy.h:127
INLINE_IN_H const char * ClientHasName(BsProxyClientHnd_T hndClient)
Get the <b><i>BotSense</i></b> client official name.
Definition: bsProxy.h:588
int VConnClose(BsProxyClientHnd_T hndClient, BsVConnHnd_T hndVConn)
Close the virtual connection.
Definition: bsProxyVConn.c:478
void VConnOneTimeInit()
The bsProxy virtual connections one-time initialization.
Definition: bsProxyVConn.c:165
BsProxyModIF_T * m_pModIF
interface module I/F
Definition: bsProxy.h:226
<b><i>BotSense</i></b> package top-level, unifying header declarations.
int BsVConnHnd_T
virtual connection handle type
Definition: BotSense.h:151
void ModUnload(BsProxyModIF_T *pModIF)
Unload the interface module.
Definition: bsProxyMod.c:1191