botsense  3.2.0
RoadNarrows Client-Server Proxied Services Framework
bsLibClient.c
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: BotSense
4 //
5 // Library: libbsclient
6 //
7 // File: bsLibClient.c
8 //
9 /*! \file
10  *
11  * $LastChangedDate: 2012-11-25 10:42:41 -0700 (Sun, 25 Nov 2012) $
12  * $Rev: 2544 $
13  *
14  * \brief Client base functions.
15  *
16  * \author Robin Knight (robin.knight@roadnarrows.com)
17  *
18  * \copyright
19  * \h_copy 2009-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 <libgen.h>
52 #include <string.h>
53 #include <errno.h>
54 #include <pthread.h>
55 #include <limits.h>
56 #include <sys/select.h>
57 #include <sys/time.h>
58 
59 #include "rnr/rnrconfig.h"
60 #include "rnr/new.h"
61 #include "rnr/log.h"
62 #include "rnr/netmsgs.h"
63 
64 #include "botsense/BotSense.h"
65 #include "botsense/libBotSense.h"
66 #include "botsense/bsProxyMsgs.h"
67 
68 #include "bsLibInternal.h"
69 
70 
71 // ---------------------------------------------------------------------------
72 // Private Interface
73 // ---------------------------------------------------------------------------
74 
75 PRAGMA_IGNORED(sign-conversion)
76 /*!
77  * \brief FD_SET() wrapper with no annoying warnings.
78  * \param fd File descriptor to add to set.
79  * \param pset Pointer to fd set.
80  */
81 static inline void fdset_nowarn(int fd, fd_set *pset)
82 {
83  FD_SET(fd, pset);
84 }
85 PRAGMA_WARNING(sign-conversion)
86 
87 
88 // ...........................................................................
89 // Transaction Mutual Exclusion Functions
90 // ...........................................................................
91 
92 /*!
93  * \brief Lock client's transaction mutual exclusion.
94  *
95  * \param pClient \h_botsense client.
96  */
97 static inline void bsTransLock(BsClient_T *pClient)
98 {
99  int rc;
100 
101  if( (rc = pthread_mutex_lock(&(pClient->m_mutexTrans))) != 0 )
102  {
103  errno = rc;
104  BSCLIENT_LOG_SYSERROR(pClient, BS_ECODE_SYS, "pthread_mutex_lock()");
105  }
106 }
107 
108 /*!
109  * \brief Unlock client's transaction mutual exclusion.
110  *
111  * \param pClient \h_botsense client.
112  */
113 static inline void bsTransUnlock(BsClient_T *pClient)
114 {
115  int rc;
116 
117  if( (rc = pthread_mutex_unlock(&(pClient->m_mutexTrans))) != 0 )
118  {
119  errno = rc;
120  BSCLIENT_LOG_SYSERROR(pClient, BS_ECODE_SYS, "pthread_mutex_unlock()");
121  }
122 }
123 
124 /*!
125  * \brief Try to lock client's transaction mutual exclusion.
126  *
127  * \param pClient \h_botsense client.
128  *
129  * \return
130  * Return true if lock acquired. Otherwise false if already locked.
131  */
132 static inline bool_t bsTransTryLock(BsClient_T *pClient)
133 {
134  return pthread_mutex_trylock(&(pClient->m_mutexTrans)) == 0? true: false;
135 }
136 
137 
138 // ...........................................................................
139 // Transaction Caching Functions
140 // ...........................................................................
141 
142 /*!
143  * \brief Mark the start of a client's server transaction in the transaction
144  * cache.
145  *
146  * A client can be multi-threaded, and so the responses from the server
147  * may come out of order with respect to the client thread.
148  *
149  * \param pClient \h_botsense client.
150  * \param uTid Transaction id.
151  */
152 static void bsTransMark(BsClient_T *pClient, BsTid_T uTid)
153 {
154  BsTransInfo_T *pInfo;
155 
156  bsTransLock(pClient);
157 
158  pInfo = pClient->m_tblTransCache[uTid & BSPROXY_TID_MASK];
159 
160  if( pInfo != NULL )
161  {
162  delete(pInfo->m_pMsgHdr);
163  delete(pInfo->m_pBuf);
164  delete(pInfo);
165  }
166 
167  pInfo = NEW(BsTransInfo_T);
168 
169  pInfo->m_pMsgHdr = NULL;
170  pInfo->m_pBuf = NULL;
171  pInfo->m_bCached = false;
172 
173  pClient->m_tblTransCache[uTid & BSPROXY_TID_MASK] = pInfo;
174 
175  bsTransUnlock(pClient);
176 }
177 
178 /*!
179  * \brief Delete any cached transaction state from the cache.
180  *
181  * \param pClient \h_botsense client.
182  * \param uTid Transaction id.
183  */
184 static void bsTransForget(BsClient_T *pClient, BsTid_T uTid)
185 {
186  BsTransInfo_T *pInfo;
187 
188  bsTransLock(pClient);
189 
190  pInfo = pClient->m_tblTransCache[uTid & BSPROXY_TID_MASK];
191 
192  if( pInfo != NULL )
193  {
194  delete(pInfo->m_pMsgHdr);
195  delete(pInfo->m_pBuf);
196  delete(pInfo);
197  pClient->m_tblTransCache[uTid & BSPROXY_TID_MASK] = NULL;
198  }
199 
200  bsTransUnlock(pClient);
201 }
202 
203 /*!
204  * \brief Cache a transaction response.
205  *
206  * Cashing a response occurs when this client thread receives a response
207  * fo another client thread.
208  *
209  * \param pClient \h_botsense client.
210  * \param uTid Transaction id.
211  * \param [in] pMsgHdr Response message header.
212  * \param [in] bufRsp Packed response message buffer.
213  */
214 static void bsTransCacheRsp(BsClient_T *pClient,
215  BsTid_T uTid,
216  BsProxyMsgHdr_T *pMsgHdr,
217  byte_t bufRsp[])
218 {
219  BsTransInfo_T *pInfo;
220  size_t uBodyLen;
221 
222  bsTransLock(pClient);
223 
224  pInfo = pClient->m_tblTransCache[uTid & BSPROXY_TID_MASK];
225 
226  delete(pInfo->m_pMsgHdr);
227  delete(pInfo->m_pBuf);
228 
229  uBodyLen = (size_t)(pMsgHdr->m_hdrBodyLen);
230 
231  pInfo->m_pMsgHdr = new_memdup(sizeof(BsProxyMsgHdr_T), pMsgHdr);
232  pInfo->m_pBuf = uBodyLen>0? new_memdup(uBodyLen, bufRsp): NULL;
233  pInfo->m_bCached = true;
234 
235  LOGDIAG3("%s: Tid=%u, MsgId=%u cached.",
236  bsClientAttrGetName(pClient), uTid, pMsgHdr->m_hdrMsgId);
237 
238  bsTransUnlock(pClient);
239 }
240 
241 /*!
242  * \brief Load cached response from transaction cache.
243  *
244  * Cashing a response occurs when this client thread receives a response
245  * fo another client thread.
246  *
247  * \param pClient \h_botsense client.
248  * \param uTid Transaction id.
249  * \param [out] pMsgHdr Response message header.
250  * \param [out] bufRsp Packed response message buffer.
251  * \param sizeRsp Size of response buffer.
252  *
253  * \return
254  * Returns \h_lt 0 error code if a response was cached but an error occurred.\n
255  * Returns = 0 if no response found in cache.\n
256  * Returns \h_gt 0 if a good cached response was loaded.
257  */
258 static int bsTransLoadCached(BsClient_T *pClient,
259  BsTid_T uTid,
260  BsProxyMsgHdr_T *pMsgHdr,
261  byte_t bufRsp[],
262  size_t sizeRsp)
263 {
264  BsTransInfo_T *pInfo;
265  size_t uBodyLen;
266  int rc = 0;
267 
268  bsTransLock(pClient);
269 
270  pInfo = pClient->m_tblTransCache[uTid & BSPROXY_TID_MASK];
271 
272  if( (pInfo == NULL) || !pInfo->m_bCached )
273  {
274  rc = 0;
275  }
276 
277  else
278  {
279  uBodyLen = (size_t)(pMsgHdr->m_hdrBodyLen);
280  if( uBodyLen > sizeRsp )
281  {
283  "body_len=%zu > buf_size=%zu.", uBodyLen, sizeRsp);
284  rc = -BS_ECODE_MSG_TOO_BIG;
285  }
286 
287  // load from cache
288  else
289  {
290  *pMsgHdr = *(pInfo->m_pMsgHdr);
291  if( uBodyLen > 0 )
292  {
293  memcpy(bufRsp, pInfo->m_pBuf, uBodyLen);
294  }
295 
296  LOGDIAG3("%s: Tid=%u, MsgId=%u loaded from cached.",
297  bsClientAttrGetName(pClient), uTid, pMsgHdr->m_hdrMsgId);
298 
299  rc = 1;
300  }
301  }
302 
303  bsTransUnlock(pClient);
304 
305  return rc;
306 }
307 
308 /*!
309  * \brief Atomically get the next available transaction id.
310  *
311  * \param pClient \h_botsense client.
312  *
313  * return Returns tid.
314  */
315 static BsTid_T bsNextTid(BsClient_T *pClient)
316 {
317  BsTid_T uTid;
318 
319  bsTransLock(pClient);
320 
321  uTid = pClient->m_uTidCounter;
322  pClient->m_uTidCounter = (uTid + 1) & BSPROXY_TID_MASK;
323 
324  bsTransUnlock(pClient);
325 
326  return uTid;
327 }
328 
329 
330 // ...........................................................................
331 // Timer Utilities
332 // ...........................................................................
333 
334 /*!
335  * \brief Mark the current time. Resolution is microseconds.
336  *
337  * \param pTvMark Pointer to timeval structure to be populated with
338  * the current system time in seconds and useconds.
339  */
340 static inline void timer_mark(struct timeval *pTvMark)
341 {
342  if( gettimeofday(pTvMark, NULL) != OK )
343  {
344  LOGSYSERROR("gettimeofday()");
345  timerclear(pTvMark);
346  }
347 }
348 
349 /*!
350  * \brief Calculate the elapsed time between the given time mark and this call.
351  *
352  * \param pTvMark Pointer to timeval holding time mark.
353  *
354  * \return
355  * Number of microseconds elasped. If the marked time is invalid or the current
356  * time cannot be ascertained, UINT_MAX is returned.
357  */
358 static uint_t timer_elapsed(struct timeval *pTvMark)
359 {
360  struct timeval tvEnd, tvDelta;
361 
362  timer_mark(&tvEnd);
363 
364  if( !timerisset(pTvMark) || !timerisset(&tvEnd) )
365  {
366  return UINT_MAX;
367  }
368 
369  tvDelta.tv_sec = tvEnd.tv_sec - pTvMark->tv_sec;
370  if( tvEnd.tv_usec < pTvMark->tv_usec )
371  {
372  tvDelta.tv_sec--;
373  tvEnd.tv_usec += 1000000;
374  }
375  tvDelta.tv_usec = tvEnd.tv_usec - pTvMark->tv_usec;
376 
377  return (uint_t)(tvDelta.tv_sec * 1000000 + tvDelta.tv_usec);
378 }
379 
380 
381 // ...........................................................................
382 // Connection I/O Functions
383 // ...........................................................................
384 
385 /*!
386  * \brief Pack \h_botsense client message header.
387  *
388  * \param pClient \h_botsense client.
389  * \param [in] pMsgHdr Pointer to message header structure.
390  * \param [out] buf Output message buffer.
391  * \param bufSize Size of output buffer.
392  *
393  * \return
394  * On success, returns the number of bytes packed.
395  * On error, returns \h_lt 0.
396  */
397 static inline int bsClientPackMsgHdr(BsClient_T *pClient,
398  BsProxyMsgHdr_T *pMsgHdr,
399  byte_t buf[],
400  size_t bufSize)
401 {
402  BSCLIENT_TRY_EXPR(pClient, (bufSize >= BSPROXY_MSG_HDR_LEN),
403  BS_ECODE_BUF_TOO_SMALL, "bufSize=%zu < %u",
404  bufSize, BSPROXY_MSG_HDR_LEN);
405 
406  return bsPackMsgHdr(pMsgHdr, buf, bufSize);
407 }
408 
409 /*!
410  * \brief Unpack \h_botsense client message header.
411  *
412  * \param pClient \h_botsense client.
413  * \param [in] buf Input message buffer.
414  * \param bufSize Size of input buffer.
415  * \param [out] pMsgHdr Pointer to message header structure.
416  *
417  * \return
418  * On success, returns the number of bytes unpacked.
419  * On error, returns \h_lt 0.
420  */
421 static inline int bsClientUnpackMsgHdr(BsClient_T *pClient,
422  byte_t buf[],
423  size_t bufSize,
424  BsProxyMsgHdr_T *pMsgHdr)
425 {
426  BSCLIENT_TRY_EXPR(pClient, (bufSize >= BSPROXY_MSG_HDR_LEN),
427  BS_ECODE_BUF_TOO_SMALL, "bufSize=%zu < %u",
428  bufSize, BSPROXY_MSG_HDR_LEN);
429 
430  return bsUnpackMsgHdr(buf, bufSize, pMsgHdr);
431 }
432 
433 /*!
434  * \brief Read bytes from socket.
435  *
436  * Read up to <em>count</em> bytes into <em>buf</em> from the socket.
437  * This call is non-blocking if the timeout value <em>usec</em> is greater than
438  * zero. Otherwise the read can block indefinitely.
439  *
440  * Note that on return the bytes read can be less than <em>count</em>.
441  *
442  * \param pClient \h_botsense client.
443  * \param sd Socket descriptor.
444  * \param [out] buf Destination buffer.
445  * \param count Number of bytes to read.
446  * \param usec Timeout in microseconds.\n
447  * If <em>usec</em> \h_gt 0, an upper timeout limit is placed
448  * on the read.
449  * If <em>usec</em> == 0, then the read will block indefinitely
450  * until <em>count</em> bytes are read or an I/O error has
451  * ocurred.
452  *
453  * \return
454  * On success, returns \h_ge 0 number of bytes read.\n
455  * On error, returns \h_lt 0 error code.
456  */
457 static int bsClientRead(BsClient_T *pClient,
458  int sd,
459  byte_t buf[],
460  size_t count,
461  uint_t usec)
462 {
463  bool_t bNonBlocking;
464  struct timeval tstart;
465  uint_t tdelta;
466  fd_set rset;
467  struct timeval timeout;
468  int nFd;
469  ssize_t n;
470  int nBytes = 0;
471 
472  LOGDIAG4CALL(_TINT(sd), _TPTR(buf), _TUINT(count), _TUINT(usec));
473 
474  bNonBlocking = usec > 0? true: false;
475 
476  while( nBytes < count )
477  {
478  FD_ZERO(&rset);
479  fdset_nowarn(sd, &rset);
480 
481  // wait for input with timeout
482  if( bNonBlocking )
483  {
484  timer_mark(&tstart);
485 
486  // (re)load timeout (gets munged after each select())
487  timeout.tv_sec = (time_t)(usec / 1000000);
488  timeout.tv_usec = (time_t)(usec % 1000000);
489 
490  nFd = select(sd+1, &rset, NULL, NULL, &timeout);
491  }
492 
493  // block indefinitely for input
494  else
495  {
496  nFd = select(sd+1, &rset, NULL, NULL, NULL);
497  }
498 
499  // system error occurred on select - interpret
500  if( nFd < 0 )
501  {
502  switch(errno)
503  {
504  case EAGAIN: // non-blocking timeout
505  case EINTR: // read was interrupted
506  break;
507  default:
509  "select(%d,...)", sd);
510  return -BS_ECODE_BAD_RECV;
511  }
512  }
513 
514  // select() timeout occurred
515  else if( nFd == 0 )
516  {
517  LOGDIAG4("select() on read timed out.");
518  break;
519  }
520 
521  // data available from serial device
522  n = read(sd, buf+nBytes, count-(size_t)nBytes);
523 
524  // system error occurred on read - interpret
525  if( n < 0 )
526  {
527  switch(errno)
528  {
529  case EAGAIN: // non-blocking timeout
530  case EINTR: // read was interrupted
531  n = 0;
532  break;
533  default:
535  "select(%d,...)", sd);
536  return -BS_ECODE_BAD_RECV;
537  }
538  }
539 
540  // got some data
541  nBytes += (int)n;
542 
543  // all of the requested bytes have been read
544  if( nBytes == count )
545  {
546  break;
547  }
548 
549  // determine time left for non-blocking read timeout
550  if( bNonBlocking && ((size_t)nBytes < count) )
551  {
552  tdelta = timer_elapsed(&tstart);
553  if( tdelta >= usec )
554  {
555  LOGDIAG4("%s() timed out.", LOGFUNCNAME);
556  break;
557  }
558  else
559  {
560  usec -= tdelta;
561  }
562  }
563  }
564 
565  LOGDIAG4("%s(): %d bytes read.", LOGFUNCNAME, nBytes);
566 
567  return nBytes;
568 }
569 
570 /*!
571  * \brief Write bytes to socket.
572  *
573  * Write up to <em>count</em> bytes from <em>buf</em> to the socket.
574  * This call is non-blocking if the timeout value <em>usec</em> is greater than
575  * zero. Otherwise the read can block indefinitely.
576  *
577  * Note that the number of bytes written can be less than the <em>count</em>.
578  *
579  * \param pClient \h_botsense client.
580  * \param sd Socket descriptor.
581  * \param buf Destination buffer.
582  * \param count Number of bytes to read.
583  * \param usec Timeout in microseconds.\n
584  * If <em>usec</em> \h_gt 0, an upper timeout limit is placed
585  * on the read.
586  * If <em>usec</em> == 0, then the read will block indefinitely
587  * until <em>count</em> bytes are read or an I/O error has
588  * ocurred.
589  *
590  * \return
591  * On success, returns \h_ge 0 number of bytes written.\n
592  * On error, returns \h_lt 0 error code.
593  */
594 static int bsClientWrite(BsClient_T *pClient,
595  int sd,
596  byte_t buf[],
597  size_t count,
598  uint_t usec)
599 {
600  bool_t bNonBlocking;
601  struct timeval tstart;
602  uint_t tdelta;
603  fd_set wset;
604  struct timeval timeout;
605  int nFd;
606  ssize_t n;
607  int nBytes = 0;
608 
609  LOGDIAG4CALL(_TINT(sd), _TPTR(buf), _TUINT(count), _TUINT(usec));
610 
611  bNonBlocking = usec > 0? true: false;
612 
613  while( nBytes < count )
614  {
615  FD_ZERO(&wset);
616  fdset_nowarn(sd, &wset);
617 
618  // wait for input with timeout
619  if( bNonBlocking )
620  {
621  timer_mark(&tstart);
622 
623  // (re)load timeout (gets munged after each select())
624  timeout.tv_sec = (time_t)(usec / 1000000);
625  timeout.tv_usec = (time_t)(usec % 1000000);
626 
627  nFd = select(sd+1, NULL, &wset, NULL, &timeout);
628  }
629 
630  // block indefinitely for input
631  else
632  {
633  nFd = select(sd+1, NULL, &wset, NULL, NULL);
634  }
635 
636  // system error occurred on select - interpret
637  if( nFd < 0 )
638  {
639  switch(errno)
640  {
641  case EAGAIN: // non-blocking timeout
642  case EINTR: // write was interrupted
643  break;
644  default:
646  "select(%d,...)", sd);
647  return -BS_ECODE_BAD_RECV;
648  }
649  }
650 
651  // select() timeout occurred
652  else if( nFd == 0 )
653  {
654  LOGDIAG4("select() on write timed out.");
655  break;
656  }
657 
658  // data available from serial device
659  n = write(sd, buf+nBytes, count-(size_t)nBytes);
660 
661  // system error occurred on write - interpret
662  if( n < 0 )
663  {
664  switch(errno)
665  {
666  case EAGAIN: // non-blocking timeout
667  case EINTR: // write was interrupted
668  n = 0;
669  break;
670  default:
672  "select(%d,...)", sd);
673  return -BS_ECODE_BAD_RECV;
674  }
675  }
676 
677  // sent some data
678  nBytes += (int)n;
679 
680  // all of the requested bytes have been written
681  if( nBytes == count )
682  {
683  break;
684  }
685 
686  // determine time left for non-blocking write timeout
687  if( bNonBlocking && ((size_t)nBytes < count) )
688  {
689  tdelta = timer_elapsed(&tstart);
690  if( tdelta >= usec )
691  {
692  LOGDIAG4("%s() timed out.", LOGFUNCNAME);
693  break;
694  }
695  else
696  {
697  usec -= tdelta;
698  }
699  }
700  }
701 
702  LOGDIAG4("%s(): %d bytes written.", LOGFUNCNAME, nBytes);
703 
704  return nBytes;
705 }
706 
707 /*!
708  * \brief Flush input of <em>count</em> bytes.
709  *
710  * \param pClient \h_botsense client.
711  * \param count Number of bytes to flush.
712  */
713 static void bsFlushInput(BsClient_T *pClient, size_t count)
714 {
715  int sd;
716  byte_t buf[1024];
717  size_t block;
718  int n;
719 
720  sd = SocketAttrGetSd(pClient->m_pSocket);
721 
722  while( count > 0 )
723  {
724  block = sizeof(buf) >= count? count: sizeof(buf);
725  n = bsClientRead(pClient, sd, buf, block, BSCLIENT_T_FLUSH);
726  if( n <= 0 )
727  {
728  return;
729  }
730  count -= (size_t)n;
731  }
732 }
733 
734 /*!
735  * \brief Resync client with server.
736  *
737  * Lost message alignment with the server (if this is possible). Perfomr a
738  * series of flush-request cycles to try to get back in sync.
739  *
740  * \param pClient \h_botsense client.
741  */
742 static void bsResync(BsClient_T *pClient)
743 {
744  int nMaxTries = 5;
745  int nTries;
746  int rc;
747 
748  for(nTries=0; nTries<nMaxTries; ++nTries)
749  {
751  if( (rc = bsServerReqLoopback(pClient, "resync")) == BS_OK )
752  {
753  return;
754  }
755  }
757 }
758 
759 /*!
760  * \brief Send a request message to the server.
761  *
762  * \param pClient \h_botsense client.
763  * \param [in] pMsgHdr Filled-in request message header.
764  * \param [in] bufReq Buffer with empty header bytes plus packed request
765  * message body.
766  * \param bufSize Size of request buffer.
767  *
768  * \return Returns number of bytes sent on success.\n
769  * Else returns \h_lt 0 error code.
770  */
771 static int bsClientSendReq(BsClient_T *pClient,
772  BsProxyMsgHdr_T *pMsgHdr,
773  byte_t bufReq[],
774  size_t bufSize)
775 {
776  int sd;
777  size_t uBodyLen;
778  uint_t uMsgId;
779  int nBytes;
780  int n;
781 
782  sd = SocketAttrGetSd(pClient->m_pSocket);
783  uMsgId = pMsgHdr->m_hdrMsgId;
784  uBodyLen = (size_t)(pMsgHdr->m_hdrBodyLen);
785  nBytes = BSPROXY_MSG_HDR_LEN + (int)uBodyLen;
786 
787  _BS_LOG_MSGHDR(pClient, "Tx Req", pMsgHdr);
788 
789  BSCLIENT_TRY_EXPR(pClient, (bufSize >= BSPROXY_MSG_HDR_LEN),
790  BS_ECODE_BUF_TOO_SMALL, "MsgId=%u: buf_size=%zu", bufSize);
791 
792  BSCLIENT_TRY_EXPR(pClient, (uBodyLen <= BSPROXY_MSG_BODY_MAX),
793  BS_ECODE_MSG_TOO_BIG, "MsgId=%u: Message body_len=%zu > max=%zu",
794  uMsgId, uBodyLen, BSPROXY_MSG_BODY_MAX);
795 
796  // prepend header to request buffer
797  n = bsClientPackMsgHdr(pClient, pMsgHdr, bufReq, BSPROXY_MSG_HDR_LEN);
798 
799  BSCLIENT_TRY_ECODE(pClient, n,
800  "MsgId=%u: Failed to pack message header", uMsgId);
801 
802  bsTransLock(pClient);
803 
804  // send the message to the server
805  n = bsClientWrite(pClient, sd, bufReq, (size_t)nBytes,
806  pClient->m_uReqTimeout);
807 
808  bsTransUnlock(pClient);
809 
810  BSCLIENT_TRY_EXPR(pClient, (n == nBytes), BS_ECODE_BAD_SEND,
811  "MsgId=%u: Failed to send message", uMsgId);
812 
813  LOGDIAG3("MsgId=%u: Sent %d byte request.", uMsgId, nBytes);
814 
815  return nBytes;
816 }
817 
818 /*!
819  * \brief Read response message from server.
820  *
821  * \param pClient \h_botsense client.
822  * \param [out] pMsgHdr Response message header.
823  * \param [out] bufRsp Buffer with packed response message body.
824  * \param bufSize Size of response buffer.
825  *
826  * \return Returns \h_lt 0 on error,
827  * or \h_gt 0 number of bytes for successful read.
828  */
829 static int bsClientRecvRsp(BsClient_T *pClient,
830  BsProxyMsgHdr_T *pMsgHdr,
831  byte_t bufRsp[],
832  size_t bufSize)
833 
834 {
835  int sd;
836  size_t uBodyLen;
837  uint_t uMsgId;
838  int nBytes;
839  byte_t bufHdr[BSPROXY_MSG_HDR_LEN];
840  int n;
841 
842  // socket file descriptor
843  sd = SocketAttrGetSd(pClient->m_pSocket);
844 
845  bsTransLock(pClient);
846 
847  // read response header
848  n = bsClientRead(pClient, sd, bufHdr, BSPROXY_MSG_HDR_LEN,
849  pClient->m_uRspTimeout);
850 
851  bsTransUnlock(pClient);
852 
854  "Failed to receive response message header, %d bytes received.", n);
855 
856  // unpack message header
857  n = bsClientUnpackMsgHdr(pClient, bufHdr, (size_t)BSPROXY_MSG_HDR_LEN,
858  pMsgHdr);
859 
860  BSCLIENT_TRY_ECODE(pClient, n,
861  "Received bad response message header, discarding.");
862 
863  _BS_LOG_MSGHDR(pClient, "Rx Rsp", pMsgHdr);
864 
865  uBodyLen = (size_t)(pMsgHdr->m_hdrBodyLen);
866  uMsgId = pMsgHdr->m_hdrMsgId;
867  nBytes = BSPROXY_MSG_HDR_LEN + (int)uBodyLen;
868 
869  // validate magic
870  if( pMsgHdr->m_hdrMagic != BSPROXY_MSG_MAGIC )
871  {
873  "MsgId=%u: "
874  "Received bad magic 0x%04x in response message header, resyncing.",
875  uMsgId, pMsgHdr->m_hdrMagic);
876  bsResync(pClient);
877  return -BS_ECODE_MSG_BAD_HDR;
878  }
879 
880  // message too long
881  if( uBodyLen > bufSize )
882  {
884  "MsgId=%u: Message body_len=%zu > buf_size=%zu, discarding.",
885  uMsgId, uBodyLen, bufSize);
886  bsFlushInput(pClient, uBodyLen);
887  return -BS_ECODE_MSG_TOO_BIG;
888  }
889 
890  // read response body
891  if( uBodyLen > 0 )
892  {
893  // read response body
894  n = bsClientRead(pClient, sd, bufRsp, uBodyLen, pClient->m_uRspTimeout);
895 
896  BSCLIENT_TRY_EXPR(pClient, (n == (int)uBodyLen), BS_ECODE_BAD_RECV,
897  "MsgId=%u: Failed to receive response message body.", uMsgId);
898  }
899 
900  LOGDIAG3("MsgId=%u: Received %d byte response.", uMsgId, nBytes);
901 
902  return nBytes;
903 }
904 
905 
906 // ---------------------------------------------------------------------------
907 // Public Interface
908 // ---------------------------------------------------------------------------
909 
910 /*!
911  * \brief Get client name.
912  *
913  * \param pClient \h_botsense client.
914  *
915  * \return
916  * On success, returns client name string. Else returns NULL.
917  */
918 const char *bsClientAttrGetName(BsClient_P pClient)
919 {
920  return pClient!=NULL? pClient->m_sClientName: NULL;
921 }
922 
923 /*!
924  * \brief Get client request (write) and response (read) timeouts.
925  *
926  * \param pClient \h_botsense client.
927  * \param [out] pReqTimeout Request (write) timeout in seconds.
928  * \param [out] pRspTimeout Response (read) timeout in seconds.
929  */
931  uint_t *pReqTimeout,
932  uint_t *pRspTimeout)
933 {
934  if( pClient != NULL )
935  {
936  *pReqTimeout = pClient->m_uReqTimeout / 1000000;
937  *pRspTimeout = pClient->m_uRspTimeout / 1000000;
938  }
939 }
940 
941 /*!
942  * \brief Set client request (write) and response (read) timeouts.
943  *
944  * A timeout value of 0 means block forever until i/o operation is complete.
945  *
946  * \param pClient \h_botsense client.
947  * \param [in] pReqTimeout Request (write) timeout in seconds.
948  * \param [in] pRspTimeout Response (read) timeout in seconds.
949  */
951  uint_t uReqTimeout,
952  uint_t uRspTimeout)
953 {
954  if( pClient != NULL )
955  {
956  pClient->m_uReqTimeout = uReqTimeout * 1000000;
957  pClient->m_uRspTimeout = uRspTimeout * 1000000;
958  }
959 }
960 
961 /*!
962  * \brief Get client virtual connection trace state.
963  *
964  * \param pClient \h_botsense client.
965  * \param hndVConn Virtual connection handle.
966  *
967  * \return
968  * Returns true or false.
969  */
971 {
972  if( pClient == NULL )
973  {
974  return false;
975  }
976  else if( hndVConn == BSPROXY_VCONN_SERVER )
977  {
978  return pClient->m_bTraceServer;
979  }
980  else if( bsClientAttrHasVConn(pClient, hndVConn) )
981  {
982  return bsGetVConn(pClient, hndVConn)->m_bTrace;
983  }
984  else
985  {
986  return false;
987  }
988 }
989 
990 /*!
991  * \brief Get the number of active virtual connections for this client.
992  *
993  * \param pClient \h_botsense client.
994  *
995  * \return
996  * Number of virtual connections.
997  */
999 {
1000  if( pClient == NULL )
1001  {
1002  return 0;
1003  }
1004  else
1005  {
1006  return pClient->m_nVConnCount;
1007  }
1008 }
1009 
1010 /*!
1011  * \brief Test if client has a virtual connection identified by the handle.
1012  *
1013  * \param pClient \h_botsense client.
1014  * \param hndVConn Virtual connection handle.
1015  *
1016  * \return
1017  * Returns true or false.
1018  */
1020 {
1021  int index;
1022 
1023  if( pClient == NULL )
1024  {
1025  return false;
1026  }
1027  else if( !BSCLIENT_IS_VCONN_HANDLE(hndVConn) )
1028  {
1029  return false;
1030  }
1031  else if( (index = pClient->m_tblHndIndex[hndVConn]) == BSPROXY_VCONN_UNDEF )
1032  {
1033  return false;
1034  }
1035  else if( pClient->m_tblVConn[index] == NULL )
1036  {
1037  return false;
1038  }
1039  else
1040  {
1041  return true;
1042  }
1043 }
1044 
1045 /*!
1046  * \brief Get client virtual connection device name.
1047  *
1048  * \param pClient \h_botsense client.
1049  * \param hndVConn Virtual connection handle.
1050  *
1051  * \return
1052  * On success, returns device name string. Else returns NULL.
1053  */
1054 const char *bsClientAttrGetDevName(BsClient_P pClient, BsVConnHnd_T hndVConn)
1055 {
1056  if( pClient == NULL )
1057  {
1058  return NULL;
1059  }
1060  else if( hndVConn == BSPROXY_VCONN_SERVER )
1061  {
1062  return "server";
1063  }
1064  else if( !bsClientAttrHasVConn(pClient, hndVConn) )
1065  {
1066  return NULL;
1067  }
1068  else
1069  {
1070  return pClient->m_tblVConn[hndVConn]->m_sDevName;
1071  }
1072 }
1073 
1074 /*!
1075  * \brief Get client virtual connection interface module name.
1076  *
1077  * \param pClient \h_botsense client.
1078  * \param hndVConn Virtual connection handle.
1079  *
1080  * \return
1081  * On success, returns interface module name string. Else returns NULL.
1082  */
1083 const char *bsClientAttrGetModName(BsClient_P pClient, BsVConnHnd_T hndVConn)
1084 {
1085  if( pClient == NULL )
1086  {
1087  return NULL;
1088  }
1089  else if( hndVConn == BSPROXY_VCONN_SERVER )
1090  {
1091  return "server";
1092  }
1093  else if( !bsClientAttrHasVConn(pClient, hndVConn) )
1094  {
1095  return NULL;
1096  }
1097  else
1098  {
1099  return pClient->m_tblVConn[hndVConn]->m_sModName;
1100  }
1101 }
1102 
1103 /*!
1104  * \brief Set client's diagnostics logging threshold.
1105  *
1106  * \param pClient \h_botsense client.
1107  * \param nLevel New logging threshold level.
1108  */
1109 void bsClientAttrSetLogging(BsClient_P pClient, int nLevel)
1110 {
1111  LOG_SET_THRESHOLD(nLevel);
1112 }
1113 
1114 /*!
1115  * \brief Get client's connection state.
1116  *
1117  * \param pClient \h_botsense client.
1118  * \param [out] pConnState Pointer to connection state.
1119  */
1121  BsClientConnState_T *pConnState)
1122 {
1123  static BsClientConnState_T noconn = {false, ""};
1124 
1125  Socket_T *pSocket = pClient->m_pSocket;
1126 
1127  if( (pSocket == NULL) || !SocketStateIsOpen(pSocket) )
1128  {
1129  *pConnState = noconn;
1130  }
1131 
1132  else
1133  {
1134  pConnState->m_bIsConnected = true;
1135  pConnState->m_sServerHostName = SocketAttrGetRemoteName(pSocket);
1136  }
1137 }
1138 
1139 /*!
1140  * \brief Fill in message header.
1141  *
1142  * \param pClient \h_botsense client.
1143  * \param hndVConn Virtual connection handle.
1144  * \param uMsgId Virtual connection unique message id.
1145  * \param uBodyLen Message body length.
1146  * \param [out] pMsgHdr Filled message header.
1147  */
1149  int hndVConn,
1150  uint_t uMsgId,
1151  size_t uBodyLen,
1152  BsProxyMsgHdr_T *pMsgHdr)
1153 {
1154  pMsgHdr->m_hdrMagic = (ushort_t)BSPROXY_MSG_MAGIC;
1155  pMsgHdr->m_hdrTid = (byte_t)bsNextTid(pClient);
1156  pMsgHdr->m_hdrVConn = (byte_t)hndVConn;
1157  pMsgHdr->m_hdrMsgId = (ushort_t)uMsgId;
1158  pMsgHdr->m_hdrBodyLen = (ushort_t)uBodyLen;
1159 }
1160 
1161 /*!
1162  * \brief Get the message name.
1163  *
1164  * For each (virtual connection, message id) 2-tuple, there can be a known
1165  * name string (provided the id is valid and an application provides the
1166  * information).
1167  *
1168  * \param pClient \h_botsense client.
1169  * \param hndVConn Virtual connection handle.
1170  * \param uMsgId Message id.
1171  *
1172  * \return
1173  * Returns message name if it can be determined. Otherwise returns "unknown".
1174  */
1175 const char *bsClientGetMsgName(BsClient_P pClient,
1176  BsVConnHnd_T hndVConn,
1177  uint_t uMsgId)
1178 {
1179  static const char *sUnknown = "unknown";
1180  const NMMsgDef_T *pMsgDef;
1181  BsVConn_T *pVConn;
1182  const BsClientAppInfo_T *pAppInfo;
1183 
1184  if( hndVConn == BSPROXY_VCONN_SERVER )
1185  {
1186  pMsgDef = BsProxyLookupMsgDef((BsProxyMsgId_T)uMsgId);
1187  return pMsgDef!=NULL? pMsgDef->m_sMsgName: sUnknown;
1188  }
1189  else if( !bsClientAttrHasVConn(pClient, hndVConn) )
1190  {
1191  return sUnknown;
1192  }
1193  else if( (pVConn = bsGetVConn(pClient, hndVConn)) == NULL )
1194  {
1195  return sUnknown;
1196  }
1197  else if( (pAppInfo = pVConn->m_pAppInfo) == NULL )
1198  {
1199  return sUnknown;
1200  }
1201  else if( pAppInfo->fnGetMsgName == NULL )
1202  {
1203  return sUnknown;
1204  }
1205  else
1206  {
1207  return pAppInfo->fnGetMsgName(pClient, hndVConn, uMsgId);
1208  }
1209 }
1210 
1211 #ifdef LOG
1212 /*!
1213  * \brief Log [pre/un]packed message header.
1214  *
1215  * \param pClient \h_botsense client.
1216  * \param sPreface Preface string.
1217  * \param pMsgHdr Pointer to message header structure.
1218  */
1220  const char *sPreface,
1221  BsProxyMsgHdr_T *pMsgHdr)
1222 {
1223  FILE *fp;
1224  const char *sMsgName;
1225 
1226  fp = LOG_GET_LOGFP();
1227 
1228  if( pMsgHdr != NULL )
1229  {
1230  sMsgName = bsClientGetMsgName(pClient,
1231  (BsVConnHnd_T)pMsgHdr->m_hdrVConn,
1232  (uint_t)pMsgHdr->m_hdrMsgId);
1233 
1234  fprintf(fp, "%s MsgHdr = {\n", sPreface);
1235  fprintf(fp, " Magic: 0x%04x\n", pMsgHdr->m_hdrMagic);
1236  fprintf(fp, " Tid: %u\n", (uint_t)(pMsgHdr->m_hdrTid));
1237  fprintf(fp, " VConn: %u\n", (uint_t)(pMsgHdr->m_hdrVConn));
1238  fprintf(fp, " MsgId: %u %s \n", pMsgHdr->m_hdrMsgId, sMsgName);
1239  fprintf(fp, " BodyLen: %u\n", pMsgHdr->m_hdrBodyLen);
1240  fprintf(fp, "}\n");
1241  }
1242  else
1243  {
1244  fprintf(fp, "%s MsgHdr: (null)\n", sPreface);
1245  }
1246 }
1247 #endif // LOG
1248 
1249 /*!
1250  * \brief Execute a request - response transaction with the server.
1251  *
1252  * \note The request and response buffer can be the same buffer if packed
1253  * request contents does not need to be preserved.
1254  *
1255  * \warning There must be exactly \ref BSPROXY_MSG_HDR_LEN bytes at the front
1256  * of the request buffer available to pack the \h_botsense header. (Reduces the
1257  * number of buffer copies.)
1258  *
1259  * \par Request Format:
1260  * msghdr msgbody
1261  *
1262  * \par Response Format:
1263  * msghdr msgbody
1264  *
1265  * \param pClient \h_botsense client.
1266  * \param hndVConn Virtual connection handle.
1267  * \param uReqMsgId Virtual connection unique request message id.
1268  * \param [in] bufReq Buffer with empty header plus packed request
1269  * message body.
1270  * \param uReqBodyLen Length of packed request message body.
1271  * \param uRspMsgId Virtual connection unique expected response message id.
1272  * \param [out] bufRsp Buffer with received packed response message body.
1273  * \param sizeRspBuf Size of response buffer (number of bytes).
1274  *
1275  * \return
1276  * On success, returns \h_ge 0 the number of response body bytes received.\n
1277  * \copydoc doc_return_ecode
1278  */
1280  int hndVConn,
1281  uint_t uReqMsgId,
1282  byte_t bufReq[],
1283  size_t uReqBodyLen,
1284  uint_t uRspMsgId,
1285  byte_t bufRsp[],
1286  size_t sizeRspBuf)
1287 {
1288  BsProxyMsgHdr_T hdrReq; // request message header
1289  BsProxyMsgHdr_T hdrRsp; // response message header
1290  BsTid_T tidReq; // request transaction id
1291  BsProxyRspErr_T msgRspErr; // common error response message
1292  bool_t bPending; // resposne is [not] pending
1293  int n; // number of bytes/return code
1294 
1295  // fill request header
1296  bsClientFillMsgHdr(pClient, hndVConn, uReqMsgId, uReqBodyLen, &hdrReq);
1297 
1298  // this request's transaction id
1299  tidReq = hdrReq.m_hdrTid;
1300 
1301  // mark request for tracking
1302  bsTransMark(pClient, tidReq);
1303 
1304  // send request
1305  n = bsClientSendReq(pClient, &hdrReq, bufReq,
1306  BSPROXY_MSG_HDR_LEN+uReqBodyLen);
1307 
1308  BSCLIENT_TRY_ECODE(pClient, n,
1309  "MsgId=%u: Failed to send request.", uReqMsgId);
1310 
1311  // log successful request sent event
1312  _BS_LOG_REQ(pClient, &hdrReq);
1313 
1314  bPending = true;
1315 
1316  // receive response
1317  while( bPending )
1318  {
1319  // check transaction cache for any cached response.
1320  n = bsTransLoadCached(pClient, tidReq, &hdrRsp, bufRsp, sizeRspBuf);
1321 
1322  BSCLIENT_TRY_ECODE(pClient, n,
1323  "MsgId=%u: Cached response failed for request.", uReqMsgId);
1324 
1325  // found a good, cached response
1326  if( n > 0 )
1327  {
1328  bsTransForget(pClient, tidReq);
1329  bPending = false;
1330  }
1331 
1332  // receive a response
1333  else
1334  {
1335  n = (int)bsClientRecvRsp(pClient, &hdrRsp, bufRsp, sizeRspBuf);
1336 
1337  BSCLIENT_TRY_ECODE(pClient, n,
1338  "MsgId=%u: Receive response failed for request.", uReqMsgId);
1339 
1340  // response was for this request
1341  if( tidReq == hdrRsp.m_hdrTid )
1342  {
1343  bsTransForget(pClient, tidReq);
1344  bPending = false;
1345  }
1346 
1347  // response is for another thread - cache it
1348  else
1349  {
1350  bsTransCacheRsp(pClient, tidReq, &hdrRsp, bufRsp);
1351  }
1352  }
1353  }
1354 
1355  n = (int)hdrRsp.m_hdrBodyLen;
1356 
1357  // received common error response
1358  if( (hdrRsp.m_hdrVConn == BSPROXY_VCONN_SERVER) &&
1359  (hdrRsp.m_hdrMsgId == BsProxyMsgIdRspErr) )
1360  {
1361  n = BsProxyUnpackRspErr(bufRsp, (size_t)n, &msgRspErr, false);
1362 
1363  BSCLIENT_TRY_ECODE(pClient, n,
1364  "MsgId=%u: Failed to unpack error response message body.",
1365  hdrRsp.m_hdrMsgId);
1366 
1367  n = -(int)(msgRspErr.m_ecode);
1368 
1369  BSCLIENT_LOG_ERROR(pClient, n, "%s", msgRspErr.m_emsg);
1370 
1371  return n;
1372  }
1373 
1374  // received application-specific response
1375  else
1376  {
1377  BSCLIENT_TRY_EXPR(pClient, (hdrRsp.m_hdrMsgId == uRspMsgId),
1379  "MsgId=%u: Unexpected response message id, expected MsgId=%u.",
1380  hdrRsp.m_hdrMsgId, uRspMsgId);
1381  }
1382 
1383  // log response received event
1384  _BS_LOG_RSP(pClient, &hdrRsp);
1385 
1386  return n;
1387 }
1388 
1389 /*!
1390  * \brief Create a new unconnected proxied client.
1391  *
1392  * \param sClientName Proxied client (robot)'s name.
1393  *
1394  * \return Returns pointer to new client on success. Else returns NULL.
1395  */
1396 BsClient_T *bsClientNew(const char *sClientName)
1397 {
1398  BsClient_T *pClient = NEW(BsClient_T);
1399  size_t i;
1400 
1401  pClient->m_sClientName = new_strdup(sClientName);
1402  pClient->m_pSocket = NULL;
1403 
1404  pthread_mutex_init(&(pClient->m_mutexTrans), NULL);
1405 
1406  pClient->m_uReqTimeout = BSCLIENT_T_WRITE;
1407  pClient->m_uRspTimeout = BSCLIENT_T_READ;
1408  pClient->m_uTidCounter = 0;
1409  pClient->m_bTraceServer = false;
1410 
1411  for(i=0; i<arraysize(pClient->m_tblTransCache); ++i)
1412  {
1413  pClient->m_tblTransCache[i] = NULL;
1414  }
1415 
1416  pClient->m_nVConnCount = 0;
1417 
1418  for(i=0; i<arraysize(pClient->m_tblHndIndex); ++i)
1419  {
1420  pClient->m_tblHndIndex[i] = BSPROXY_VCONN_UNDEF;
1421  }
1422 
1423  for(i=0; i<arraysize(pClient->m_tblVConn); ++i)
1424  {
1425  pClient->m_tblVConn[i] = NULL;
1426  }
1427 
1428  return pClient;
1429 }
1430 
1431 /*!
1432  * \brief Delete a proxied client.
1433  *
1434  * \warning The client should disconnect prior to deletion.
1435  *
1436  * \param pClient \h_botsense proxied client.
1437  */
1439 {
1440  size_t i;
1441 
1442  if( pClient == NULL )
1443  {
1444  return;
1445  }
1446 
1447  if( SocketStateIsOpen(pClient->m_pSocket) )
1448  {
1450  "Connection still open, auto-disconnecting.");
1451  (void)bsServerDisconnect(pClient);
1452  }
1453 
1454  for(i=0; i<arraysize(pClient->m_tblTransCache); ++i)
1455  {
1456  bsTransForget(pClient, (BsTid_T)i);
1457  }
1458 
1459  bsVConnClearAll(pClient);
1460 
1461  pthread_mutex_destroy(&(pClient->m_mutexTrans));
1462 
1463  delete((void *)(pClient->m_sClientName));
1464 
1465  delete(pClient);
1466 }
1467 
1468 
1469 // ---------------------------------------------------------------------------
1470 // Internal Interface
1471 // ---------------------------------------------------------------------------
1472 
1473 /*!
1474  * \brief Remove and delete all of a client's vConnections.
1475  *
1476  * \param pClient \h_botsense proxied client.
1477  */
1479 {
1480  size_t nHnd;
1481  int index;
1482 
1483  for(nHnd=0; nHnd<arraysize(pClient->m_tblHndIndex); ++nHnd)
1484  {
1485  index = (int)pClient->m_tblHndIndex[nHnd];
1486  if( index != BSPROXY_VCONN_UNDEF )
1487  {
1488  bsVConnRemove(pClient, (BsVConnHnd_T)nHnd);
1489  bsVConnDelete(pClient, index);
1490  }
1491  }
1492 }
1493 
1494 /*!
1495  * \brief Add a created vConnection to the client's tblHndIndex table.
1496  *
1497  * \note The new vConnection should have been created and reserved prior
1498  * to adding.
1499  *
1500  * \param pClient \h_botsense client.
1501  * \param hndVConn New virtual connection proxied device handle.
1502  * \param index Internal vConnection index.
1503  *
1504  * \copydoc doc_return_std
1505  */
1506 int bsVConnAdd(BsClient_T *pClient,
1507  BsVConnHnd_T hndVConn,
1508  int index)
1509 {
1510  int i;
1511  int rc;
1512 
1513  BSCLIENT_TRY_EXPR(pClient, BSCLIENT_IS_VCONN_HANDLE(hndVConn),
1514  BS_ECODE_INTERNAL, "VConn=%d: out-of-range.", hndVConn);
1515 
1516  BSCLIENT_TRY_EXPR(pClient, (index < BSPROXY_VCONN_CLIENT_MAX),
1517  BS_ECODE_INTERNAL, "index=%d: out-of-range.", index);
1518 
1519  bsTransLock(pClient);
1520 
1521  i = (int)pClient->m_tblHndIndex[hndVConn];
1522 
1523  if( i != BSPROXY_VCONN_UNDEF )
1524  {
1526  "VConn=%d: index=%d: handle already assigned: "
1527  "Internal vconn tables corrupted.", hndVConn, i);
1528  rc = -BS_ECODE_INTERNAL;
1529  }
1530 
1531  else if( pClient->m_tblVConn[index] == NULL )
1532  {
1534  "VConn=%d: index=%d: No vConnection present: "
1535  "Internal vconn tables corrupted.", hndVConn, index);
1536  rc = -BS_ECODE_INTERNAL;
1537  }
1538 
1539  else
1540  {
1541  pClient->m_tblVConn[index]->m_hndVConn = hndVConn;
1542  pClient->m_tblHndIndex[hndVConn] = (byte_t)index;
1543  pClient->m_nVConnCount++;
1544  rc = BS_OK;
1545  }
1546 
1547  bsTransUnlock(pClient);
1548 
1549  return rc;
1550 }
1551 
1552 /*!
1553  * \brief Remove a vConnection from the client's tblHndIndex table.
1554  *
1555  * \note The vConnection stills needs to be deleted.
1556  *
1557  * \param pClient \h_botsense client.
1558  * \param hndVConn New virtual connection proxied device handle.
1559  *
1560  * \return
1561  * On success, returns the removed, but still reserved, internal vConnection
1562  * index.\n
1563  * \copydoc doc_return_ecode
1564  */
1565 int bsVConnRemove(BsClient_T *pClient, BsVConnHnd_T hndVConn)
1566 {
1567  int index;
1568  int rc;
1569 
1570  BSCLIENT_TRY_EXPR(pClient, BSCLIENT_IS_VCONN_HANDLE(hndVConn),
1571  BS_ECODE_INTERNAL, "VConn=%d: out-of-range.", hndVConn);
1572 
1573  bsTransLock(pClient);
1574 
1575  index = pClient->m_tblHndIndex[hndVConn];
1576 
1577  if( index == BSPROXY_VCONN_UNDEF )
1578  {
1580  "VConn=%d: vConnection not present in table.", hndVConn);
1581  rc = -BS_ECODE_INTERNAL;
1582  }
1583 
1584  else
1585  {
1586  pClient->m_tblVConn[index]->m_hndVConn = BSPROXY_VCONN_UNDEF;
1587  pClient->m_tblHndIndex[hndVConn] = BSPROXY_VCONN_UNDEF;
1588  pClient->m_nVConnCount--;
1589 
1590  rc = index;
1591  }
1592 
1593  bsTransUnlock(pClient);
1594 
1595  return rc;
1596 }
1597 
1598 /*!
1599  * \brief Reserve a new client vConnection (and proxied device) in the
1600  * client's tblVConn table.
1601  *
1602  * \note The new vConnection still needs to be added.
1603  *
1604  * \param pClient \h_botsense client.
1605  * \param sDevName Proxied device file path name.
1606  * \param sModName Interface module file path name.
1607  * \param pAppInfo Static application-specific information and callbacks.
1608  * \param bTrace Do [not] enable message tracing.
1609  *
1610  * \return
1611  * On success, returns reserved internal vConnection index.\n
1612  * \copydoc doc_return_ecode
1613  */
1614 int bsVConnNew(BsClient_T *pClient,
1615  const char *sDevName,
1616  const char *sModName,
1617  const BsClientAppInfo_T *pAppInfo,
1618  bool_t bTrace)
1619 {
1620  int index;
1621  BsVConn_T *pVConn;
1622  int rc = BS_OK;
1623 
1624  bsTransLock(pClient);
1625 
1627  {
1629  "Client has the maximum=%d virtual connections.",
1631  rc = -BS_ECODE_NO_RSRC;
1632  }
1633 
1634  else
1635  {
1636  for(index=0; index<BSPROXY_VCONN_CLIENT_MAX; ++index)
1637  {
1638  if( pClient->m_tblVConn[index] == NULL )
1639  {
1640  break;
1641  }
1642  }
1643 
1644  if( index >= BSPROXY_VCONN_CLIENT_MAX )
1645  {
1647  "Internal vconn tables corrupted.");
1648  rc = -BS_ECODE_INTERNAL;
1649  }
1650  }
1651 
1652  if( rc == BS_OK )
1653  {
1654  pVConn = NEW(BsVConn_T);
1655 
1656  pVConn->m_hndVConn = BSPROXY_VCONN_UNDEF;
1657  pVConn->m_bTrace = bTrace;
1658  pVConn->m_sDevName = new_strdup(sDevName);
1659  pVConn->m_sModName = new_strdup(sModName);
1660  pVConn->m_pAppInfo = pAppInfo;
1661 
1662  // reserve slot
1663  pClient->m_tblVConn[index] = pVConn;
1664 
1665  rc = index;
1666  }
1667 
1668  bsTransUnlock(pClient);
1669 
1670  return rc;
1671 }
1672 
1673 /*!
1674  * \brief Delete a vConnection (and proxied device) from the client's
1675  * tblVConn table.
1676  *
1677  * \note The vConnection should be removed first prior to deletion.
1678  *
1679  * \param pClient \h_botsense client.
1680  * \param index Internal vConnection index.
1681  *
1682  * \copydoc doc_return_std
1683  */
1684 int bsVConnDelete(BsClient_T *pClient, int index)
1685 {
1686  BsVConn_T *pVConn;
1687  int rc;
1688 
1689  BSCLIENT_TRY_EXPR(pClient, (index < BSPROXY_VCONN_CLIENT_MAX),
1690  BS_ECODE_INTERNAL, "index=%d: out-of-range.", index);
1691 
1692  bsTransLock(pClient);
1693 
1694  pVConn = pClient->m_tblVConn[index];
1695 
1696  if( pVConn == NULL )
1697  {
1699  "index=%u: vConnection not present in table.", index);
1700  rc = -BS_ECODE_INTERNAL;
1701  }
1702 
1703  else
1704  {
1705  delete((char *)(pVConn->m_sDevName));
1706  delete((char *)(pVConn->m_sModName));
1707  delete(pVConn);
1708 
1709  // free slot
1710  pClient->m_tblVConn[index] = NULL;
1711 
1712  rc = BS_OK;
1713  }
1714 
1715  bsTransUnlock(pClient);
1716 
1717  return rc;
1718 }
BsVConnHnd_T m_hndVConn
virtual connection handle
Definition: bsLibInternal.h:86
const BsClientAppInfo_T * m_pAppInfo
app-specific info (optional)
Definition: bsLibInternal.h:90
int bsClientAttrGetVConnCount(BsClient_P pClient)
Get the number of active virtual connections for this client.
Definition: bsLibClient.c:998
const char * bsClientAttrGetModName(BsClient_P pClient, BsVConnHnd_T hndVConn)
Get client virtual connection interface module name.
Definition: bsLibClient.c:1083
static uint_t timer_elapsed(struct timeval *pTvMark)
Calculate the elapsed time between the given time mark and this call.
Definition: bsLibClient.c:358
BsVConn_T * m_tblVConn[BSPROXY_VCONN_CLIENT_MAX]
table of virtual connections
#define _BS_LOG_REQ(pClient, pMsgHdr)
Log successful request recieved event.
int bsVConnNew(BsClient_T *pClient, const char *sDevName, const char *sModName, const BsClientAppInfo_T *pAppInfo, bool_t bTrace)
Reserve a new client vConnection (and proxied device) in the client&#39;s tblVConn table.
Definition: bsLibClient.c:1614
const char * m_sModName
interface module path name
Definition: bsLibInternal.h:89
#define BSCLIENT_IS_VCONN_HANDLE(hnd)
Test if the handle is in the valid client virtual connection range.
Definition: libBotSense.h:274
static void bsResync(BsClient_T *pClient)
Resync client with server.
Definition: bsLibClient.c:742
const NMMsgDef_T * BsProxyLookupMsgDef(BsProxyMsgId_T eMsgId)
Look up the message definition associated with the message id.
Definition: bsProxyMsgs.c:874
bool_t bsClientAttrHasVConn(BsClient_P pClient, BsVConnHnd_T hndVConn)
Test if client has a virtual connection identified by the handle.
Definition: bsLibClient.c:1019
#define BSCLIENT_TRY_ECODE(pClient, ecode, efmt,...)
Check if <b><i>BotSense</i></b> return value is not an error ( &lt; 0).
Definition: libBotSense.h:214
BotSense client application - bsProxy server-terminated core messages.
BsProxyMsgHdr_T * m_pMsgHdr
cached message header
Definition: bsLibInternal.h:77
static int bsClientPackMsgHdr(BsClient_T *pClient, BsProxyMsgHdr_T *pMsgHdr, byte_t buf[], size_t bufSize)
Pack <b><i>BotSense</i></b> client message header.
Definition: bsLibClient.c:397
The libBotSense internal declarations.
uint_t m_uReqTimeout
client request timeout
static void bsTransForget(BsClient_T *pClient, BsTid_T uTid)
Delete any cached transaction state from the cache.
Definition: bsLibClient.c:184
#define BS_ECODE_GEN
general, unspecified error
Definition: BotSense.h:68
int bsClientTrans(BsClient_P pClient, int hndVConn, uint_t uReqMsgId, byte_t bufReq[], size_t uReqBodyLen, uint_t uRspMsgId, byte_t bufRsp[], size_t sizeRspBuf)
Execute a request - response transaction with the server.
Definition: bsLibClient.c:1279
Socket_T * m_pSocket
IP socket to server.
Definition: bsLibInternal.h:99
Client Virtual Connection Info Structure.
Definition: bsLibInternal.h:84
#define BS_ECODE_BAD_RESYNC
bad resync with server/client
Definition: BotSense.h:71
#define BS_ECODE_BAD_RECV
bad receive
Definition: BotSense.h:69
#define BSCLIENT_T_WRITE
5 second write timeout default
Definition: bsLibInternal.h:67
void bsClientAttrSetTimeouts(BsClient_P pClient, uint_t uReqTimeout, uint_t uRspTimeout)
Set client request (write) and response (read) timeouts.
Definition: bsLibClient.c:950
uint_t BsTid_T
client transaction id type [0-255].
Definition: BotSense.h:172
int bsVConnRemove(BsClient_T *pClient, BsVConnHnd_T hndVConn)
Remove a vConnection from the client&#39;s tblHndIndex table.
Definition: bsLibClient.c:1565
bool_t bsClientAttrGetTraceState(BsClient_P pClient, BsVConnHnd_T hndVConn)
Get client virtual connection trace state.
Definition: bsLibClient.c:970
#define BSPROXY_VCONN_SERVER
handle for server-terminated msgs
Definition: BotSense.h:140
bool_t m_bIsConnected
client is [not] connected to server
Definition: libBotSense.h:115
static void fdset_nowarn(int fd, fd_set *pset)
FD_SET() wrapper with no annoying warnings.
Definition: bsLibClient.c:81
#define BS_ECODE_BAD_SEND
bad send
Definition: BotSense.h:70
#define BSCLIENT_LOG_SYSERROR(pClient, ecode, efmt,...)
Log System Error.
Definition: libBotSense.h:196
#define BSPROXY_TID_MASK
transaction id mask
Definition: BotSense.h:170
Cashed Transaction Information Structure.
Definition: bsLibInternal.h:74
pthread_mutex_t m_mutexTrans
client transaction mutex
#define BSCLIENT_LOG_ERROR(pClient, ecode, efmt,...)
Log Error.
Definition: libBotSense.h:170
The Client Structure Type.
Definition: bsLibInternal.h:96
uint_t m_uRspTimeout
client response timeout
#define BSCLIENT_T_FLUSH
5 second flush timeout default
Definition: bsLibInternal.h:68
static bool_t bsTransTryLock(BsClient_T *pClient)
Try to lock client&#39;s transaction mutual exclusion.
Definition: bsLibClient.c:132
static void bsTransLock(BsClient_T *pClient)
Lock client&#39;s transaction mutual exclusion.
Definition: bsLibClient.c:97
#define BS_OK
not an error, success
Definition: BotSense.h:66
const char * m_sServerHostName
server host name
Definition: libBotSense.h:116
static BsTid_T bsNextTid(BsClient_T *pClient)
Atomically get the next available transaction id.
Definition: bsLibClient.c:315
#define BS_ECODE_SYS
system (errno) error
Definition: BotSense.h:92
#define BSCLIENT_LOG_WARN(pClient, ecode, wfmt,...)
Log Warning.
Definition: libBotSense.h:156
INLINE_IN_H int BsProxyUnpackRspErr(byte_t buf[], size_t uMsgLen, BsProxyRspErr_T *pStruct, bool_t bTrace)
Unpack a BsProxyRspErr ITV message in big-endian byte order from the input buffer.
Definition: bsProxyMsgs.h:366
static int bsClientUnpackMsgHdr(BsClient_T *pClient, byte_t buf[], size_t bufSize, BsProxyMsgHdr_T *pMsgHdr)
Unpack <b><i>BotSense</i></b> client message header.
Definition: bsLibClient.c:421
#define BSPROXY_VCONN_UNDEF
undefined virtual connection handle
Definition: BotSense.h:139
void bsClientAttrGetTimeouts(BsClient_P pClient, uint_t *pReqTimeout, uint_t *pRspTimeout)
Get client request (write) and response (read) timeouts.
Definition: bsLibClient.c:930
INLINE_IN_H BsVConn_T * bsGetVConn(BsClient_T *pClient, BsVConnHnd_T hndVConn)
void bsClientLogMsgHdr(BsClient_P pClient, const char *sPreface, BsProxyMsgHdr_T *pMsgHdr)
Log [pre/un]packed message header.
Definition: bsLibClient.c:1219
int bsVConnAdd(BsClient_T *pClient, BsVConnHnd_T hndVConn, int index)
Add a created vConnection to the client&#39;s tblHndIndex table.
Definition: bsLibClient.c:1506
#define _BS_LOG_MSGHDR(pClient, sPreface, pMsgHdr)
Log [pre/un]packed message header.
void bsClientFillMsgHdr(BsClient_P pClient, int hndVConn, uint_t uMsgId, size_t uBodyLen, BsProxyMsgHdr_T *pMsgHdr)
Fill in message header.
Definition: bsLibClient.c:1148
char m_emsg[(NMFVAL_LEN_MAX_STRING)+1]
emsg
Definition: bsProxyMsgs.h:75
<b><i>BotSense</i></b> client library declarations.
const char * m_sDevName
device path name
Definition: bsLibInternal.h:88
uint_t m_uTidCounter
transaction id counter
BsTransInfo_T * m_tblTransCache[BSPROXY_TID_NUMOF]
transaction cache
ushort_t m_hdrBodyLen
message body length
Definition: BotSense.h:284
ushort_t m_hdrMsgId
message id (vConnection unique)
Definition: BotSense.h:283
#define BS_ECODE_MSG_TOO_BIG
message too big
Definition: BotSense.h:74
const char *(* fnGetMsgName)(BsClient_P pClient, BsVConnHnd_T hndVConn, uint_t uMsgId)
Get the message name callback function.
Definition: libBotSense.h:105
<b><i>BotSense</i></b> client library information and callbacks to application-specific data...
Definition: libBotSense.h:86
byte_t * m_pBuf
cached packed message body buffer
Definition: bsLibInternal.h:78
#define BSCLIENT_T_READ
10 second read timeout default
Definition: bsLibInternal.h:66
static void bsFlushInput(BsClient_T *pClient, size_t count)
Flush input of count bytes.
Definition: bsLibClient.c:713
static int bsClientWrite(BsClient_T *pClient, int sd, byte_t buf[], size_t count, uint_t usec)
Write bytes to socket.
Definition: bsLibClient.c:594
#define _BS_LOG_RSP(pClient, pMsgHdr)
Log successful repsonse recieved event.
bool_t m_bTraceServer
do [not] trace server-ended msgs
int bsVConnDelete(BsClient_T *pClient, int index)
Delete a vConnection (and proxied device) from the client&#39;s tblVConn table.
Definition: bsLibClient.c:1684
#define BS_ECODE_MSG_BAD_HDR
bad message header
Definition: BotSense.h:72
#define BS_ECODE_NO_RSRC
no resource available
Definition: BotSense.h:85
void bsVConnClearAll(BsClient_T *pClient)
Remove and delete all of a client&#39;s vConnections.
Definition: bsLibClient.c:1478
const char * bsClientAttrGetName(BsClient_P pClient)
Get client name.
Definition: bsLibClient.c:918
#define BSPROXY_MSG_MAGIC
message magic pattern
Definition: BotSense.h:261
const char * m_sClientName
client name
Definition: bsLibInternal.h:98
bool_t m_bCached
this transaction is [not] cached
Definition: bsLibInternal.h:76
#define BSPROXY_MSG_HDR_LEN
message header length (bytes)
Definition: BotSense.h:258
static void bsTransUnlock(BsClient_T *pClient)
Unlock client&#39;s transaction mutual exclusion.
Definition: bsLibClient.c:113
#define BS_ECODE_BUF_TOO_SMALL
buffer too small
Definition: BotSense.h:75
ushort_t m_hdrMagic
"unique" magic pattern
Definition: BotSense.h:280
#define BSCLIENT_TRY_EXPR(pClient, expr, ecode, efmt,...)
Check if expression evaluates to true.
Definition: libBotSense.h:259
BsProxyMsgId_T
Definition: bsProxyMsgs.h:35
BotSense Proxy Message Header Structure.
Definition: BotSense.h:278
const char * bsClientGetMsgName(BsClient_P pClient, BsVConnHnd_T hndVConn, uint_t uMsgId)
Get the message name.
Definition: bsLibClient.c:1175
#define BSPROXY_VCONN_CLIENT_MAX
max number of virtual conn/client
Definition: BotSense.h:122
void bsClientAttrSetLogging(BsClient_P pClient, int nLevel)
Set client&#39;s diagnostics logging threshold.
Definition: bsLibClient.c:1109
#define BSPROXY_MSG_BODY_MAX
maximum msg body length (sans header)
Definition: BotSense.h:123
byte_t m_ecode
ecode
Definition: bsProxyMsgs.h:74
#define BS_ECODE_INTERNAL
internal error (bug)
Definition: BotSense.h:93
byte_t m_hdrTid
transaction id
Definition: BotSense.h:281
static void bsTransCacheRsp(BsClient_T *pClient, BsTid_T uTid, BsProxyMsgHdr_T *pMsgHdr, byte_t bufRsp[])
Cache a transaction response.
Definition: bsLibClient.c:214
BsClient_T * bsClientNew(const char *sClientName)
Create a new unconnected proxied client.
Definition: bsLibClient.c:1396
const char * bsClientAttrGetDevName(BsClient_P pClient, BsVConnHnd_T hndVConn)
Get client virtual connection device name.
Definition: bsLibClient.c:1054
#define BSPROXY_MSG_MAX_LEN
total message maximum length
Definition: BotSense.h:259
void bsClientDelete(BsClient_P pClient)
Delete a proxied client.
Definition: bsLibClient.c:1438
static int bsClientRead(BsClient_T *pClient, int sd, byte_t buf[], size_t count, uint_t usec)
Read bytes from socket.
Definition: bsLibClient.c:457
static void timer_mark(struct timeval *pTvMark)
Mark the current time. Resolution is microseconds.
Definition: bsLibClient.c:340
byte_t m_tblHndIndex[BSPROXY_VCONN_MOD_NUMOF]
handle to vconn index table
int m_nVConnCount
number of active v. connections
byte_t m_hdrVConn
virtual connection handle (server unique)
Definition: BotSense.h:282
static int bsClientSendReq(BsClient_T *pClient, BsProxyMsgHdr_T *pMsgHdr, byte_t bufReq[], size_t bufSize)
Send a request message to the server.
Definition: bsLibClient.c:771
static void bsTransMark(BsClient_T *pClient, BsTid_T uTid)
Mark the start of a client&#39;s server transaction in the transaction cache.
Definition: bsLibClient.c:152
void bsClientAttrGetConnState(BsClient_P pClient, BsClientConnState_T *pConnState)
Get client&#39;s connection state.
Definition: bsLibClient.c:1120
static int bsTransLoadCached(BsClient_T *pClient, BsTid_T uTid, BsProxyMsgHdr_T *pMsgHdr, byte_t bufRsp[], size_t sizeRsp)
Load cached response from transaction cache.
Definition: bsLibClient.c:258
bool_t m_bTrace
do [not] trace this vconn msgs
Definition: bsLibInternal.h:87
<b><i>BotSense</i></b> package top-level, unifying header declarations.
int BsVConnHnd_T
virtual connection handle type
Definition: BotSense.h:151
static int bsClientRecvRsp(BsClient_T *pClient, BsProxyMsgHdr_T *pMsgHdr, byte_t bufRsp[], size_t bufSize)
Read response message from server.
Definition: bsLibClient.c:829