botsense  3.2.0
RoadNarrows Client-Server Proxied Services Framework
bsProxy.c
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: BotSense
4 //
5 // Program: bsProxy
6 //
7 // File: bsProxy.c
8 //
9 /*! \file
10  *
11  * $LastChangedDate: 2010-09-13 10:25:05 -0600 (Mon, 13 Sep 2010) $
12  * $Rev: 581 $
13  *
14  * \brief \h_botsense proxy IP server.
15  *
16  * \sa \ref man_bsproxy
17  *
18  * \author Robin Knight (robin.knight@roadnarrows.com)
19  *
20  * \copyright
21  * \h_copy 2007-2017. RoadNarrows LLC.\n
22  * http://www.roadnarrows.com\n
23  * All Rights Reserved
24  */
25 // Permission is hereby granted, without written agreement and without
26 // license or royalty fees, to use, copy, modify, and distribute this
27 // software and its documentation for any purpose, provided that
28 // (1) The above copyright notice and the following two paragraphs
29 // appear in all copies of the source code and (2) redistributions
30 // including binaries reproduces these notices in the supporting
31 // documentation. Substantial modifications to this software may be
32 // copyrighted by their authors and need not follow the licensing terms
33 // described here, provided that the new terms are clearly indicated in
34 // all files where they apply.
35 //
36 // IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY MEMBERS/EMPLOYEES
37 // OF ROADNARROW LLC OR DISTRIBUTORS OF THIS SOFTWARE BE LIABLE TO ANY
38 // PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
39 // DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
40 // EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN ADVISED OF
41 // THE POSSIBILITY OF SUCH DAMAGE.
42 //
43 // THE AUTHOR AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
44 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
45 // FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
46 // "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
47 // PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
48 //
49 ////////////////////////////////////////////////////////////////////////////////
50 
51 #include <sys/time.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <libgen.h>
55 #include <string.h>
56 #include <limits.h>
57 #include <errno.h>
58 
59 #include "rnr/rnrconfig.h"
60 #include "rnr/log.h"
61 #include "rnr/opts.h"
62 #include "rnr/dlistvoid.h"
63 #include "rnr/dliststr.h"
64 #include "rnr/new.h"
65 #include "rnr/sock.h"
66 #include "rnr/sockset.h"
67 
68 #include "botsense/bsProxyMsgs.h"
69 
70 #include "version.h"
71 
72 #include "bsProxy.h"
73 
74 
75 // ---------------------------------------------------------------------------
76 // Private Interface
77 // ---------------------------------------------------------------------------
78 
79 const char *Argv0; ///< command base name
80 
81 //
82 // The bsProxy Options.
83 //
84 static char *BsOptListenIFDft = NULL; ///< default listen interface
85 static DListVoid_T *BsOptDListListenIF = NULL; ///< list of listen i/f's
86 static DListStr_T *BsOptDListLibPath = NULL; ///< list of lib search paths
87 
88 //
89 // Forward declarations.
90 //
91 static int OptsCvtArgInterface(const char *argv0, const char *sOptName,
92  char *optarg, void *pOptVal);
93 static int OptsCvtArgLibPath(const char *argv0, const char *sOptName,
94  char *optarg, void *pOptVal);
95 
96 /*!
97  * Program Information
98  */
99 static OptsPgmInfo_T BsProxyPgmInfo =
100 {
101  .synopsis = "BotSense IP Proxy Server.",
102  .long_desc =
103  "The BotSense Proxy Server (%P) provides an IP interace to any proxied "
104  "(pseudo) device. %P is multi-threaded by device to optimize client "
105  "responsiveness. For each (pseudo) device, there must exist a dynamically "
106  "linked library that %P will load as needed, plus a client side library. "
107  "The two libraries typically communicate through a defined set of NetMsgs "
108  "network messages. The Message Exchange Patterns (MEP) support by %P "
109  "are client request - server response and server output streaming "
110  "(e.g. video).\n"
111  "\n"
112  "A wide range of RoadNarrows support robots use %P."
113 };
114 
115 /*!
116  * Command Line Options Information.
117  */
118 static OptsInfo_T BsProxyOptsInfo[] =
119 {
120  // -i, --interface Proxy IP Port Number
121  {
122  .long_opt = "interface",
123  .short_opt = 'i',
124  .has_arg = required_argument,
125  .opt_addr = &BsOptListenIFDft,
126  .has_default= true,
127  .fn_cvt = OptsCvtArgInterface,
128  .fn_fmt = OptsFmtStr,
129  .arg_name = "[<addr>][:<port>]",
130  .opt_desc = "Server passive socket interface. The server will listen "
131  "on this interface to accept new incoming connection "
132  "requests."
133  },
134 
135  // -L, --libpath Library serach path
136  {
137  .long_opt = "libpath",
138  .short_opt = 'L',
139  .has_arg = required_argument,
140  .opt_addr = NULL,
141  .has_default= false,
142  .fn_cvt = OptsCvtArgLibPath,
143  .fn_fmt = OptsFmtStr,
144  .arg_name = "<dir>",
145  .opt_desc = "Add library directory path to search for dynamic linked "
146  "library plugins."
147  },
148 
149  {NULL, }
150 };
151 
152 /*!
153  * \brief Log Proxy Server Specific Error.
154  *
155  * \param ecode \h_botsense error code.
156  * \param efmt Error output format string literal.
157  * \param ... Error variable arguments.
158  */
159 #define SERVER_LOG_ERROR(ecode, efmt, ...) \
160  LOGERROR("Server %s:%s(ecode=%d): " efmt, \
161  ServerHasName(), \
162  bsStrError(ecode), (ecode>=0? ecode: -ecode), \
163  ##__VA_ARGS__)
164 
165 /*!
166  * \brief Log Proxy Server Specific System Error.
167  *
168  * \param efmt Error output format string litteral.
169  * \param ... Error variable arguments.
170  */
171 #define SERVER_LOG_SYSERROR(efmt, ...) \
172  LOGERROR("Server %s: %s(ecode=%d): %s(errno=%d)" efmt, \
173  ServerHasName(), \
174  bsStrError(BS_ECODE_SYS), BS_ECODE_SYS, \
175  strerror(errno), errno, ##__VA_ARGS__)
176 
177 /*!
178  * \brief Lock server's mutual exclusion.
179  */
180 static inline void ServerLock()
181 {
182  int rc;
183 
184  if( (rc = pthread_mutex_lock(&BsServerCtl->m_mutex)) != 0 )
185  {
186  errno = rc;
187  SERVER_LOG_SYSERROR("pthread_mutex_lock()");
188  }
189 }
190 
191 /*!
192  * \brief Unlock server's mutual exclusion.
193  */
194 static inline void ServerUnlock()
195 {
196  int rc;
197 
198  if( (rc = pthread_mutex_unlock(&BsServerCtl->m_mutex)) != 0 )
199  {
200  errno = rc;
201  SERVER_LOG_SYSERROR("pthread_mutex_unlock()");
202  }
203 }
204 
205 /*!
206  * \brief Try to lock global mutual exclusion.
207  *
208  * \return
209  * Returns true if lock is acquired. Otherwise returns false if mutex already
210  * locked.
211  */
212 static inline bool_t ServerTryLock()
213 {
214  return pthread_mutex_trylock(&BsServerCtl->m_mutex) == 0? true: false;
215 }
216 
217 /*!
218  * \brief Register a new client with the server.
219  *
220  * \param pSockClient Accecpted client socket.
221  *
222  * \return Returns client handle.
223  */
224 static BsProxyClientHnd_T BsProxyClientRegister(Socket_T *pSockClient)
225 {
226  BsProxyServerCtl_T *pServer;
227  BsProxyClientCtl_T *pClient;
228  int sd;
229 
230  pClient = ClientNew(pSockClient);
231 
232  // get the server instance
233  pServer = ServerGet();
234 
235  //
236  // The client handle is also the opened socket descriptor.
237  //
238  sd = SocketAttrGetSd(pSockClient);
239 
240  //
241  // Add client socket to server's socket set. The client socket is added
242  // only to the active read set, not the write set.
243  //
244  SockSetAdd(pServer->m_pServerSockSet, pClient->m_pClientSock, true, false);
245 
246  pServer->m_pServerRegClient[sd] = pClient;
247  pServer->m_uServerRegClientCount++;
248 
249  return ServerClientSd2Hnd(sd);
250 }
251 
252 /*!
253  * \brief Unregister a client with the server.
254  *
255  * Okay, this is messy. A client may disconnect from the server at any time,
256  * leaving many virtual connections open. The server thread could be processing
257  * open and close requests on this zombie client while device threads could be
258  * sending responses on the same client. Hopefully, all will work out with only
259  * a few diagnotics messages printed.
260  *
261  * \par Execution Context:
262  * Server thread (main)
263 
264  * \param hndClient \h_botsense client handle.
265  */
267 {
268  BsProxyServerCtl_T *pServer;
269  BsProxyClientCtl_T *pClient;
270  BsVConnHnd_T hndVConn;
271  BsProxyVConn_T *pVConn;
272 
273  //
274  // Lock the client.
275  //
276  if( (pClient = ClientAcquire(hndClient)) == NULL )
277  {
278  return;
279  }
280 
281  // mark client zombie state (stop thread actions on this client)
283 
284  // get the server instance
285  pServer = ServerGet();
286 
287  //
288  // Loop through the device virtual connections, closing all opened connections
289  // associated with this client.
290  //
291  for(hndVConn=BSPROXY_VCONN_MOD_MIN;
292  hndVConn<=BSPROXY_VCONN_MOD_MAX;
293  ++hndVConn)
294  {
295  if( (pVConn = VConnGet(hndVConn)) != NULL )
296  {
297  // pointers point to the same client allocated object
298  if( pVConn->m_hndClient == hndClient )
299  {
300  VConnClose(hndClient, hndVConn);
301  pClient->m_uRefCnt--;
302  }
303  }
304  }
305 
306  //
307  // Remove the client from server reqistered set.
308  //
309  SockSetRemove(pServer->m_pServerSockSet, pClient->m_pClientSock);
310  pServer->m_pServerRegClient[ServerClientHnd2Sd(hndClient)] = NULL;
311  pServer->m_uServerRegClientCount--;
312 
313  //
314  // Now delete the client (don't need to release since the client no longer
315  // exists).
316  //
317  ClientDelete(pClient);
318 }
319 
320 /*!
321  * \brief Delete a server control block.
322  *
323  * \param pServer \h_botsense server.
324  */
326 {
327  BsProxyClientHnd_T hndClient;
328 
329  if( pServer != NULL )
330  {
331  for(hndClient=0; hndClient<BSPROXY_FD_SETSIZE; ++hndClient)
332  {
333  if( pServer->m_pServerRegClient[ServerClientHnd2Sd(hndClient)] != NULL )
334  {
335  BsProxyClientUnregister(hndClient);
336  }
337  }
338  SocketDelete(pServer->m_pServerSockListener);
339  SockSetDelete(pServer->m_pServerSockSet);
340  delete((char *)pServer->m_sServerName);
341  pthread_mutex_destroy(&pServer->m_mutex);
342  delete(pServer);
343  }
344 }
345 
346 /*!
347  * \brief Create new server control block.
348  *
349  * \param sPrefix Server name prefix.
350  * \param sLocalHostName Local host interface to bind to.
351  * \param nPort Server listener TCP port number.
352  *
353  * \return Returns a newly allocated and intialized server control block.
354  */
355 static BsProxyServerCtl_T *BsProxyServerNew(const char *sPrefix,
356  const char *sLocalHostName,
357  int nPort)
358 {
359  BsProxyServerCtl_T *pServer;
360  char *bufName;
361  int rc = BS_OK;
362 
363  bufName = NEWSTR(strlen(sPrefix)+16);
364  sprintf(bufName, "%s_%d", sPrefix, nPort);
365 
366  pServer = NEW(BsProxyServerCtl_T);
367  pServer->m_sServerName = bufName;
368  pServer->m_nServerPort = nPort;
369  pServer->m_pServerSockSet = SockSetNew();
370  pServer->m_pServerSockListener = SocketOpenTcpListener(nPort,
371  sLocalHostName);
372  pServer->m_uServerRegClientCount = 0;
373 
374  // create server mutex
375  pthread_mutex_init(&pServer->m_mutex, NULL);
376 
377  if( pServer->m_pServerSockListener == NULL )
378  {
379  LOGERROR("%s: Failed to open server listener socket.",
380  pServer->m_sServerName);
381  rc = -BS_ECODE_SYS;
382  }
383 
384  else
385  {
386  rc = SockSetAdd(pServer->m_pServerSockSet, // server client socket set
387  pServer->m_pServerSockListener, // server listener socket
388  true, // add listener to active read
389  true); // add listener to active write
390 
391  if( rc < 0 )
392  {
393  LOGERROR("%s: Failed to add server listener socket to set.",
394  pServer->m_sServerName);
395  rc = -BS_ECODE_INTERNAL;
396  }
397  }
398 
399  //
400  // Error clean up.
401  //
402  if( rc < 0 )
403  {
404  BsProxyServerDelete(pServer);
405  pServer = NULL;
406  }
407 
408  return pServer;
409 }
410 
411 /*!
412  * \brief Dispatch client request.
413  *
414  * The client request is queued in the appropriate service thread.
415  *
416  * \note This dispatcher owns the allocated message buffer until it is
417  * successfully queue.
418  *
419  * \param hndClient \h_botsense client handle.
420  * \param pMsgHdr Received, unpacked request message header.
421  * \param bufReq Received, packed allocated message body.
422  */
423 static void BsProxyDispatch(BsProxyClientHnd_T hndClient,
424  BsProxyMsgHdr_T *pMsgHdr,
425  byte_t bufReq[])
426 {
427  BsProxyVConn_T *pVConn;
428  BsVConnHnd_T hndVConn;
429  BsTid_T uTid;
430  BsMsgId_T uMsgId;
431  int rc;
432 
433  hndVConn= (BsVConnHnd_T)pMsgHdr->m_hdrVConn;
434  uTid = (BsTid_T)pMsgHdr->m_hdrTid;
435  uMsgId = (BsMsgId_T)pMsgHdr->m_hdrMsgId;
436 
437  //
438  // Check if handle is valid.
439  //
440  if( !BSPROXY_CHK_VCONN_HND(hndVConn) )
441  {
442  delete(bufReq);
443  BSPROXY_SEND_ERROR_RSP(hndClient, hndVConn, uTid,
444  BS_ECODE_BAD_VCONN_HND, "VConn=%d", hndVConn);
445  return;
446  }
447 
448  //
449  // Lock the virtual connection.
450  //
451  else if( (pVConn = VConnAcquire(hndVConn)) == NULL )
452  {
453  delete(bufReq);
454  BSPROXY_SEND_ERROR_RSP(hndClient, hndVConn, uTid,
455  BS_ECODE_NO_VCONN, "VConn=%d", hndVConn);
456  return;
457  }
458 
459  //
460  // Queue the request
461  //
462  else
463  {
464  rc = ThQueue(pVConn->m_pThCtl,
465  hndClient, hndVConn, uTid, uMsgId,
466  bufReq, (size_t)pMsgHdr->m_hdrBodyLen);
467 
468  // release the virtual connection
469  VConnRelease(hndVConn);
470 
471  if( rc < 0 )
472  {
473  delete(bufReq);
474  BSPROXY_SEND_ERROR_RSP(hndClient, hndVConn, uTid,
475  BS_ECODE_NO_RSRC, "MsgId=%u", uMsgId);
476  }
477  }
478 }
479 
480 /*!
481  * \brief Server mainloop.
482  *
483  * Processes all client registrations, requests, and unregistrations.
484  *
485  * \param pServer \h_botsense server.
486  *
487  * \copydoc doc_return_std
488  */
489 static int BsProxyServer(BsProxyServerCtl_T *pServer)
490 {
491  struct timeval tvSelect; // select() timeout
492  Socket_T *pSockClient; // new client socket
493  BsProxyClientCtl_T *pClient; // client
494  Socket_T *pSock; // working socket
495  SockSetIter_T iterSockSet; // socket set iterator
496  int nCntSd; // number of active sockets
497  int sdListener; // socket descriptor
498  int sd; // socket descriptor
499  BsProxyClientHnd_T hndClient; // client handle
500  int nMsgLen; // message length/return code
501  BsProxyMsgHdr_T msgHdr; // request message header
502  byte_t *pBuf; // allocated message body
503 
504  LOGDIAG1("%s: Listening on interface %d", ServerHasName(), ServerHasPort());
505 
506  sdListener = SocketAttrGetSd(pServer->m_pServerSockListener);
507 
508  tvSelect.tv_sec = BSPROXY_TUNE_T_SELECT / 1000000;
509  tvSelect.tv_usec = BSPROXY_TUNE_T_SELECT % 1000000;
510 
511  //
512  // Loop forever for client events.
513  //
514  while( true )
515  {
516  // block w/o timout on select() with active sockets
517  nCntSd = SockSetSelect(pServer->m_pServerSockSet, &tvSelect);
518 
519  //
520  // One or more clients I/O operations are available.
521  //
522  if( nCntSd > 0 )
523  {
524  //
525  // Iterate through read events.
526  //
527  for(pSock=SockSetIterFirst(pServer->m_pServerSockSet,
528  SOCK_SET_SELECTED, SOCK_IO_READ,
529  &iterSockSet);
530  pSock!=NULL;
531  pSock=SockSetIterNext(&iterSockSet))
532  {
533  // socket descriptor
534  sd = SocketAttrGetSd(pSock);
535 
536  //
537  // New bsProxy client.
538  //
539  if( sd == sdListener )
540  {
541  // accept new, non-blocking connection
542  pSockClient = SocketAccept(pServer->m_pServerSockListener, true);
543 
544  if( pSockClient == NULL )
545  {
546  SERVER_LOG_ERROR(BS_ECODE_GEN, "accept() failed.");
547  return -BS_ECODE_GEN;
548  }
549 
550  // maximum number of clients exceeded
552  (sd >= BSPROXY_FD_SETSIZE) )
553  {
555  "%s: Connection denied, maximum %d clients are "
556  "already registered.",
557  SocketAttrGetRemoteName(pSockClient), BSPROXY_REG_CLIENT_MAX);
558  SocketClose(pSockClient);
559  SocketDelete(pSockClient);
560  }
561 
562  // register accepted client with this server
563  else
564  {
565  hndClient = BsProxyClientRegister(pSockClient);
566  LOGDIAG1("%s: %s: Connection accepted, client registered.",
567  ServerHasName(), ClientHasName(hndClient));
568  LOGDIAG2("%s: %u registered clients.",
570  }
571  }
572 
573  //
574  // A registered bsProxy client sent a request.
575  //
576  else
577  {
578  // map socket descriptor to handle
579  hndClient = ServerClientSd2Hnd(sd);
580 
581  // get the (unlocked) client
582  pClient = ServerGetClient(hndClient);
583 
584  if( pClient == NULL )
585  {
587  "%d: No registered client for select().", sd);
588  close(sd);
589  continue;
590  }
591 
592  // receive client request
593  nMsgLen = ClientRecvReq(sd, &msgHdr, &pBuf);
594 
595  // unrecoverable connection error or client has disconnected
596  if( nMsgLen <= 0 )
597  {
598  LOGDIAG1("%s: %s: Connnection closed, client unregistered.",
599  ServerHasName(), ClientHasName(hndClient));
600  BsProxyClientUnregister(hndClient);
601  delete(pBuf);
602  LOGDIAG2("%s: %u registered clients.",
604  }
605 
606  // dispatch the client request
607  else if( nMsgLen > 0 )
608  {
609  BsProxyDispatch(hndClient, &msgHdr, pBuf);
610  }
611  }
612  }
613 
614  //
615  // Clear any write events. For now, nothing to do.
616  //
617  for(pSock=SockSetIterFirst(pServer->m_pServerSockSet,
618  SOCK_SET_SELECTED, SOCK_IO_WRITE,
619  &iterSockSet);
620  pSock!=NULL;
621  pSock=SockSetIterNext(&iterSockSet))
622  {
623  sd = SocketAttrGetSd(pSock);
624  }
625  }
626 
627  //
628  // Timed out. Check client connections.
629  //
630  // Note: This is a sanity check. When a client disconnects, typically a
631  // read event with zero bytes will occur. The select() will detect
632  // this event and will be processed in the above code.
633  //
634  else if( nCntSd == 0 )
635  {
636  for(pSock=SockSetIterFirst(pServer->m_pServerSockSet,
637  SOCK_SET_ACTIVE, SOCK_IO_READ,
638  &iterSockSet);
639  pSock!=NULL;
640  pSock=SockSetIterNext(&iterSockSet))
641  {
642  // socket descriptor
643  sd = SocketAttrGetSd(pSock);
644 
645  if( (sd != sdListener) && SocketStateIsErrored(pSock) )
646  {
647  hndClient = ServerClientSd2Hnd(sd);
648  LOGDIAG1("%s: %s: Connnection closed, client unregistered.",
649  ServerHasName(), ClientHasName(hndClient));
650  BsProxyClientUnregister(hndClient);
651  LOGDIAG2("%s: %u registered clients.",
653  }
654  }
655  }
656 
657  //
658  // Internal error.
659  //
660  else
661  {
662  SERVER_LOG_ERROR(BS_ECODE_INTERNAL, "select() failed.");
663  return -BS_ECODE_INTERNAL;
664  }
665  }
666 
667  return BS_OK;
668 }
669 
670 /*!
671  * \brief Get and set environment.
672  */
673 static void MainEnv()
674 {
675 }
676 
677 /*!
678  * \brief Get configuration.
679  */
680 static void MainCfg()
681 {
682 }
683 
684 /*!
685  * \brief Convert command-line listen interface option string to
686  * network name/number and port number.
687  *
688  * \par Option Argument Syntax:
689  * [addr][:port]
690  *
691  * \param argv0 Command name.
692  * \param sOptName Option name.
693  * \param optarg Parsed option argument to convert (optional).
694  * \param[out] pOptVal Pointer to converted option value (not used).
695  *
696  * \exception OptsInvalid()
697  *
698  * \return If returns, then returns BS_OK.
699  */
700 static int OptsCvtArgInterface(const char *argv0,
701  const char *sOptName,
702  char *optarg,
703  void *pOptVal)
704 {
705  char *sInet;
706  char *sSepField;
707  char *sPort;
708  int nPort;
709  BsProxyListenIF_T *pIF;
710 
711  sInet = new_strdup(optarg);
712 
713  sSepField = strchr(sInet, ':');
714 
715  if( sSepField )
716  {
717  *sSepField = 0;
718  sPort = sSepField+1;
719  nPort = (int)atol(sPort);
720  if( (nPort <= 0) || (nPort > 0xffff) )
721  {
722  OptsInvalid(Argv0, "'%s': Invalid '%s' argument port value.",
723  optarg, sOptName);
724  }
725  }
726  else
727  {
728  nPort = BSPROXY_LISTEN_PORT_DFT;
729  }
730 
731  if( *sInet == 0 )
732  {
733  delete(sInet);
734  sInet = NULL;
735  }
736 
737  pIF = NEW(BsProxyListenIF_T);
738  pIF->m_sAddr = sInet;
739  pIF->m_nPort = nPort;
740  DListVoidAppend(BsOptDListListenIF, pIF);
741 
742  return BS_OK;
743 }
744 
745 /*!
746  * \brief Append command-line library search path option to list of search
747  * paths.
748  *
749  * \par Option Argument Syntax:
750  * dir
751  *
752  * \param argv0 Command name.
753  * \param sOptName Option name.
754  * \param optarg Parsed option argument to convert (optional).
755  * \param[out] pOptVal Pointer to converted option value (not used).
756  *
757  * \exception OptsInvalid()
758  *
759  * \return If returns, then returns BS_OK.
760  */
761 static int OptsCvtArgLibPath(const char *argv0,
762  const char *sOptName,
763  char *optarg,
764  void *pOptVal)
765 {
766  DListVoidAppend(BsOptDListLibPath, optarg);
767  return BS_OK;
768 }
769 
770 /*!
771  * \brief Main initialization.
772  *
773  * Parses command-line options and arguments and environment variables to
774  * configure the BotsSense Proxy server.
775  *
776  * \param argc Count of command-line options and arguments.
777  * \param argv Array of command-line options and arguments.
778  */
779 static void MainInit(int argc, char *argv[])
780 {
781  BsProxyListenIF_T *pIF;
782  char buf[32];
783 
784  Argv0 = basename(argv[0]);
785 
786  sprintf_s(buf, sizeof(buf), ":%d", BSPROXY_LISTEN_PORT_DFT);
787  BsOptListenIFDft = new_strdup(buf);
788  BsOptDListListenIF = DListVoidNew(NULL, NULL);
789  BsOptDListLibPath = DListStrNew(NULL, NULL);
790 
791  // Parse input options
792  argv = OptsGet(Argv0, &PkgInfo, &BsProxyPgmInfo,
793  BsProxyOptsInfo, true, &argc, argv);
794 
795  // The environment
796  MainEnv();
797 
798  // The configuration file(s)
799  MainCfg();
800 
801  //
802  // Final checks and tweaks.
803  //
804  if( DListVoidCount(BsOptDListListenIF) == 0 )
805  {
806  pIF = NEW(BsProxyListenIF_T);
807  pIF->m_sAddr = NULL;
809  DListVoidAppend(BsOptDListListenIF, pIF);
810  }
811 
812  if( DListVoidCount(BsOptDListListenIF) > 1 )
813  {
814  LOGDIAG2("Warning: Currently only one listen interface is supported. "
815  "First I/F will be used.");
816  }
817 }
818 
819 
820 // ---------------------------------------------------------------------------
821 // Public Interface
822 // ---------------------------------------------------------------------------
823 
824 /*!
825  * \brief The server control block.
826  *
827  * \note In a future bsProxy implementation, multiple server instance threads
828  * could be running in the same application. But currently there is only one
829  * server per application instance.
830  */
832 
833 /*!
834  * \brief bsProxy main()
835  *
836  * \param argc Count of command-line options and arguments.
837  * \param argv Array of command-line options and arguments.
838  *
839  * \return Exit value.
840  */
841 int main(int argc, char *argv[])
842 {
843  BsProxyListenIF_T *pIF;
844  int rc;
845 
846  // Process command-line, environment, and configuration file data.
847  MainInit(argc, argv);
848 
849  LOGDIAG1("%s v%s %s", Argv0, PkgInfo.m_sPkgVersion, PkgInfo.m_sPkgTimeStamp);
850 
851  // Interface module control one-time initialization.
853 
854  // Service thread control one-time initialization.
855  ThOneTimeInit();
856 
857  // Client control one-time initialization.
859 
860  // Virtual connection control one-time initialization.
862 
863  //
864  // FUTURE: If multiple listen interfaces are specified, then fork extra
865  // copies of bsProxy here.
866  //
867 
868  //
869  // Create a server control block to listen on the first interface.
870  //
871  pIF = (BsProxyListenIF_T *)DListStrGetDataAt(BsOptDListListenIF, 0);
872  BsServerCtl = BsProxyServerNew(Argv0, pIF->m_sAddr, pIF->m_nPort);
873 
874  if( BsServerCtl == NULL )
875  {
876  return EC_ERROR;
877  }
878 
879  //
880  // Open the server-terminated virtual connection. The server service thread
881  // is started.
882  //
883  if( (rc = VConnOpenServer()) < 0 )
884  {
885  return EC_ERROR;
886  }
887 
888  LOGDIAG1("BotSense IP Proxy Server %s started", ServerHasName());
889 
890  // Run.
891  rc = BsProxyServer(BsServerCtl);
892 
893  return rc == BS_OK? 0: EC_ERROR;
894 }
int ClientRecvReq(BsProxyClientHnd_T hndClient, BsProxyMsgHdr_T *pMsgHdr, byte_t **addrBuf)
Receive a request message from client.
uint_t BsMsgId_T
client message id type [0-64k].
Definition: BotSense.h:188
Socket_T * m_pServerSockListener
server listener socket
Definition: bsProxy.h:210
<b><i>BotSense</i></b> bsProxy IP server declarations.
BsProxyClientCtl_T * ClientAcquire(BsProxyClientHnd_T hndClient)
Acquire client, locking it from other threads.
#define BSPROXY_LISTEN_PORT_DFT
default bsProxy passive socket
Definition: BotSense.h:120
static BsProxyServerCtl_T * BsProxyServerNew(const char *sPrefix, const char *sLocalHostName, int nPort)
Create new server control block.
Definition: bsProxy.c:355
BotSense client application - bsProxy server-terminated core messages.
void VConnRelease(BsVConnHnd_T hndVConn)
Release the locked virtual client.
Definition: bsProxyVConn.c:585
static void ServerLock()
Lock server&#39;s mutual exclusion.
Definition: bsProxy.c:180
#define BS_ECODE_GEN
general, unspecified error
Definition: BotSense.h:68
static BsProxyClientHnd_T BsProxyClientRegister(Socket_T *pSockClient)
Register a new client with the server.
Definition: bsProxy.c:224
BsProxyClientCtl_T * m_pServerRegClient[256]
registered clients
Definition: bsProxy.h:212
uint_t m_uServerRegClientCount
num of reg. clients
Definition: bsProxy.h:211
uint_t BsTid_T
client transaction id type [0-255].
Definition: BotSense.h:172
Socket_T * m_pClientSock
client opened TCP socket
Definition: bsProxy.h:196
BsProxyThCtl_T * m_pThCtl
service thread
Definition: bsProxy.h:225
static void BsProxyDispatch(BsProxyClientHnd_T hndClient, BsProxyMsgHdr_T *pMsgHdr, byte_t bufReq[])
Dispatch client request.
Definition: bsProxy.c:423
static void BsProxyClientUnregister(BsProxyClientHnd_T hndClient)
Unregister a client with the server.
Definition: bsProxy.c:266
INLINE_IN_H int ServerHasPort()
Get the <b><i>BotSense</i></b> server&#39;s listening port.
Definition: bsProxy.h:505
static int BsProxyServer(BsProxyServerCtl_T *pServer)
Server mainloop.
Definition: bsProxy.c:489
#define BSPROXY_FD_SETSIZE
server socket set size
Definition: bsProxy.h:103
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 void BsProxyServerDelete(BsProxyServerCtl_T *pServer)
Delete a server control block.
Definition: bsProxy.c:325
#define BS_OK
not an error, success
Definition: BotSense.h:66
static void MainEnv()
Get and set environment.
Definition: bsProxy.c:673
static int OptsCvtArgLibPath(const char *argv0, const char *sOptName, char *optarg, void *pOptVal)
Append command-line library search path option to list of search paths.
Definition: bsProxy.c:761
#define BSPROXY_VCONN_MOD_MIN
minimum module-specific handle value
Definition: BotSense.h:141
INLINE_IN_H BsProxyServerCtl_T * ServerGet()
Get the <b><i>BotSense</i></b> server.
Definition: bsProxy.h:475
static DListStr_T * BsOptDListLibPath
list of lib search paths
Definition: bsProxy.c:86
#define BS_ECODE_BAD_VCONN_HND
bad virtual connection handle
Definition: BotSense.h:79
#define BSPROXY_VCONN_MOD_MAX
maximum module-specific handle value
Definition: BotSense.h:142
#define BS_ECODE_SYS
system (errno) error
Definition: BotSense.h:92
int BsProxyClientHnd_T
bsProxy server client handle
Definition: bsProxy.h:114
int VConnClose(BsProxyClientHnd_T hndClient, BsVConnHnd_T hndVConn)
Close the virtual connection.
Definition: bsProxyVConn.c:478
INLINE_IN_H BsProxyClientHnd_T ServerClientSd2Hnd(int sd)
Convert the <b><i>BotSense</i></b> server client socket descriptor to client handle.
Definition: bsProxy.h:537
void VConnOneTimeInit()
The bsProxy virtual connections one-time initialization.
Definition: bsProxyVConn.c:165
INLINE_IN_H int ServerClientHnd2Sd(BsProxyClientHnd_T hndClient)
Convert the <b><i>BotSense</i></b> server client handle to client socket descriptor.
Definition: bsProxy.h:521
const char * m_sServerName
server&#39;s name
Definition: bsProxy.h:207
#define BSPROXY_REG_CLIENT_MAX
max number of simultaneous clients
Definition: BotSense.h:121
#define BSPROXY_TUNE_T_SELECT
5.0s send select time out
Definition: bsProxy.h:96
pthread_mutex_t m_mutex
server mutex lock
Definition: bsProxy.h:214
static void MainCfg()
Get configuration.
Definition: bsProxy.c:680
static int OptsCvtArgInterface(const char *argv0, const char *sOptName, char *optarg, void *pOptVal)
Convert command-line listen interface option string to network name/number and port number...
Definition: bsProxy.c:700
BsProxyVConn_T * VConnAcquire(BsVConnHnd_T hndVConn)
Acquire virtual connection, locking it from other threads.
Definition: bsProxyVConn.c:552
ushort_t m_hdrBodyLen
message body length
Definition: BotSense.h:284
#define BSPROXY_SEND_ERROR_RSP(hndClient, hndVConn, uTid, ecode, efmt,...)
Log <b><i>BotSense</i></b> Error and Send Error Response.
Definition: bsProxy.h:330
ushort_t m_hdrMsgId
message id (vConnection unique)
Definition: BotSense.h:283
disconnected or fatal, not deleted
Definition: bsProxy.h:186
static bool_t ServerTryLock()
Try to lock global mutual exclusion.
Definition: bsProxy.c:212
SockSet_T * m_pServerSockSet
server socket set
Definition: bsProxy.h:209
static void ServerUnlock()
Unlock server&#39;s mutual exclusion.
Definition: bsProxy.c:194
BsProxyClientHnd_T m_hndClient
proxied client handle
Definition: bsProxy.h:224
static const PkgInfo_T PkgInfo
Definition: version.h:45
#define SERVER_LOG_ERROR(ecode, efmt,...)
Log Proxy Server Specific Error.
Definition: bsProxy.c:159
INLINE_IN_H BsProxyClientCtl_T * ServerGetClient(BsProxyClientHnd_T hndClient)
Get the <b><i>BotSense</i></b> server client.
Definition: bsProxy.h:551
static OptsInfo_T BsProxyOptsInfo[]
Definition: bsProxy.c:118
static void MainInit(int argc, char *argv[])
Main initialization.
Definition: bsProxy.c:779
#define BS_ECODE_NO_RSRC
no resource available
Definition: BotSense.h:85
#define BSPROXY_CHK_VCONN_HND(hndVConn)
Definition: bsProxy.h:428
uint_t m_uRefCnt
vconn ref count for this client
Definition: bsProxy.h:198
BsProxyVConn_T * VConnGet(BsVConnHnd_T hndVConn)
Get the virtual connection entry associated with the handle.
Definition: bsProxyVConn.c:248
void ClientSetState(BsProxyClientCtl_T *pClient, BsProxyClientState_T eNewState)
Set client&#39;s state.
Package version information.
static DListVoid_T * BsOptDListListenIF
list of listen i/f&#39;s
Definition: bsProxy.c:85
#define SERVER_LOG_SYSERROR(efmt,...)
Log Proxy Server Specific System Error.
Definition: bsProxy.c:171
void ClientOneTimeInit()
The <b><i>BotSense</i></b> bsProxy server one-time client control initialization.
const char * Argv0
command base name
Definition: bsProxy.c:79
BotSense Proxy Message Header Structure.
Definition: BotSense.h:278
BsProxyServerCtl_T * BsServerCtl
The server control block.
Definition: bsProxy.c:831
BsProxyClientCtl_T * ClientNew(Socket_T *pSockClient)
Create new client control structure.
#define BS_ECODE_SERVER_BAD_CLIENT
server detected bad client
Definition: BotSense.h:91
int m_nPort
listen port number
Definition: bsProxy.h:249
static char * BsOptListenIFDft
default listen interface
Definition: bsProxy.c:84
#define BS_ECODE_INTERNAL
internal error (bug)
Definition: BotSense.h:93
int ThQueue(BsProxyThCtl_T *pThCtl, BsProxyClientHnd_T hndClient, BsVConnHnd_T hndVConn, BsTid_T uTid, BsMsgId_T uMsgId, byte_t bufReq[], size_t uReqLen)
Queue a request for the given service thread.
byte_t m_hdrTid
transaction id
Definition: BotSense.h:281
#define BS_ECODE_NO_VCONN
virtual connection not found
Definition: BotSense.h:80
void ThOneTimeInit()
The service thread one-time global initialization.
#define BS_ECODE_SERVER_CONN_DENY
server denied connection
Definition: BotSense.h:90
static OptsPgmInfo_T BsProxyPgmInfo
Definition: bsProxy.c:99
void ModOneTimeInit(DListStr_T *pDListLibPath)
The bsProxy interface module handling one-time initialization.
Definition: bsProxyMod.c:1013
int main(int argc, char *argv[])
bsProxy main()
Definition: bsProxy.c:841
byte_t m_hdrVConn
virtual connection handle (server unique)
Definition: BotSense.h:282
INLINE_IN_H const char * ClientHasName(BsProxyClientHnd_T hndClient)
Get the <b><i>BotSense</i></b> client official name.
Definition: bsProxy.h:588
void ClientDelete(BsProxyClientCtl_T *pClient)
Delete a client with the server.
int BsVConnHnd_T
virtual connection handle type
Definition: BotSense.h:151
char * m_sAddr
listen local address or hostname
Definition: bsProxy.h:248
int m_nServerPort
server&#39;s listener port
Definition: bsProxy.h:208