1 /* $Id: ncbi_monkey.cpp 590725 2019-08-03 02:06:11Z lavr $
2 * ===========================================================================
3 *
4 *                            PUBLIC DOMAIN NOTICE
5 *               National Center for Biotechnology Information
6 *
7 *  This software/database is a "United States Government Work" under the
8 *  terms of the United States Copyright Act.  It was written as part of
9 *  the author's official duties as a United States Government employee and
10 *  thus cannot be copyrighted.  This software/database is freely available
11 *  to the public for use. The National Library of Medicine and the U.S.
12 *  Government have not placed any restriction on its use or reproduction.
13 *
14 *  Although all reasonable efforts have been taken to ensure the accuracy
15 *  and reliability of the software and data, the NLM and the U.S.
16 *  Government do not and cannot warrant the performance or results that
17 *  may be obtained by using this software or data. The NLM and the U.S.
18 *  Government disclaim all warranties, express or implied, including
19 *  warranties of performance, merchantability or fitness for any particular
20 *  purpose.
21 *
22 *  Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Author:  Dmitriy Elisov
27 *
28 * File Description:
29 *   Chaos Monkey - a library that is hooked up with ncbi_socket.c to simulate
30 *   problems in network connectivity - losses, bad data, delays.
31 *
32 */
33 
34 #include <ncbi_pch.hpp>
35 
36 #include "ncbi_priv.h"
37 
38 #ifdef NCBI_MONKEY
39 #  include <connect/ncbi_connutil.h>
40 #  include <corelib/ncbi_system.hpp>
41 #  include <corelib/ncbireg.hpp>
42 #  include <corelib/ncbithr.hpp>
43 #  include <corelib/env_reg.hpp>
44 #  include <corelib/ncbiapp.hpp>
45 #  include <corelib/ncbimisc.hpp>
46 #  include "ncbi_monkeyp.hpp"
47 #  include <list>
48 #  include <vector>
49 
50 /* OS-dependent way to set socket errors */
51 #  ifdef NCBI_OS_MSWIN
52 #    define MONKEY_SET_SOCKET_ERROR(error)  WSASetLastError(error)
53 #    define MONKEY_ENOPROTOOPT              WSAENOPROTOOPT
54 #  else
55 #    define MONKEY_SET_SOCKET_ERROR(error)  errno = error
56 #    define MONKEY_ENOPROTOOPT              ENOPROTOOPT
57 #  endif /* NCBI_OS_MSWIN */
58 
59 /* Include OS-specific headers */
60 #  ifdef NCBI_OS_MSWIN
61 #    include <regex>
62 #  else
63 #    include <arpa/inet.h>
64 #    include <sys/types.h>
65 #    include <sys/socket.h>
66 #    include <sys/un.h>
67 #  endif /* NCBI_OS_MSWIN */
68 
69 
70 BEGIN_NCBI_SCOPE
71 
72 DEFINE_STATIC_FAST_MUTEX(s_ConfigMutex);
73 DEFINE_STATIC_FAST_MUTEX(s_NetworkDataCacheMutex);
74 DEFINE_STATIC_FAST_MUTEX(s_SeedLogConfigMutex);
75 DEFINE_STATIC_FAST_MUTEX(s_SingletonMutex);
76 DEFINE_STATIC_FAST_MUTEX(s_KnownConnMutex);
77 static CSocket*          s_TimeoutingSock = NULL;
78 static CSocket*          s_PeerSock = NULL;
79 const  int               kRandCount = 100;
80 /* Registry names */
81 const string             kMonkeyMainSect = "CHAOS_MONKEY";
82 const string             kEnablField     = "enabled";
83 const string             kSeedField      = "seed";
84 
85 #define PARAM_TWICE_EXCEPTION(param)                                        \
86     throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),            \
87                            NULL, CMonkeyException::e_MonkeyInvalidArgs,     \
88                            string("Parameter \"" param "\" is set "         \
89                            "twice in [") + GetSection() + "]" +             \
90                            s_RuleTypeString(GetActionType()));
91 
92 
93 /*/////////////////////////////////////////////////////////////////////////////
94 //                              MOCK DEFINITIONS                             //
95 /////////////////////////////////////////////////////////////////////////////*/
96 #  ifdef NCBI_MONKEY_TESTS
97 #    undef DECLARE_MONKEY_MOCK
98 /* Declare a static variable and global getter&setter for it */
99 #    define DECLARE_MONKEY_MOCK(ty,name,def_val)                        \
100     static ty s_Monkey_ ## name = def_val;                              \
101     ty g_MonkeyMock_Get ## name() {                                     \
102         return s_Monkey_ ## name;                                       \
103     }                                                                   \
104     void g_MonkeyMock_Set ## name(const ty& val) {                      \
105         s_Monkey_ ## name = val;                                        \
106     }
107 
108 /* This macro contains the list of variables to be mocked.
109  * Needed mocks will be created with DECLARE_MONKEY_MOCK */
110 MONKEY_MOCK_MACRO()
111 
112 string CMonkeyMocks::MainMonkeySection = "CHAOS_MONKEY";
113 
114 
Reset()115 void CMonkeyMocks::Reset()
116 {
117     MainMonkeySection = "CHAOS_MONKEY";
118 }
119 
GetMainMonkeySection()120 string CMonkeyMocks::GetMainMonkeySection()
121 {
122     return MainMonkeySection;
123 }
SetMainMonkeySection(string section)124 void CMonkeyMocks::SetMainMonkeySection(string section)
125 {
126     MainMonkeySection = section;
127 }
128 
129 
130 /*/////////////////////////////////////////////////////////////////////////////
131 //                           CMonkeyException                                //
132 /////////////////////////////////////////////////////////////////////////////*/
what() const133 const char* CMonkeyException::what() const throw()
134 {
135 
136     return CException::GetMsg().c_str();
137 }
138 
139 
140 #  endif /* NCBI_MONKEY_TESTS */
141 
142 
143 /*/////////////////////////////////////////////////////////////////////////////
144 //                    STATIC CONVENIENCE FUNCTIONS                           //
145 /////////////////////////////////////////////////////////////////////////////*/
s_Monkey_Split(const string & s,char delim,vector<string> & elems)146 static vector<string>& s_Monkey_Split(const string   &s,
147                                       char           delim,
148                                       vector<string> &elems)
149 {
150     stringstream ss(s);
151     string item;
152     while (getline(ss, item, delim)) {
153         elems.push_back(item);
154     }
155     return elems;
156 }
157 
158 
159 /* Trash collector for Thread Local Storage */
160 template<class T>
s_TlsCleanup(T * p_value,void *)161 static void s_TlsCleanup(T* p_value, void* /* data */)
162 {
163     delete p_value;
164 }
165 
166 
s_TimeoutingSocketInit(void)167 static void s_TimeoutingSocketInit(void)
168 {
169     unsigned short i = 8080;
170     CListeningSocket* server_socket;
171 
172     for (;  i < 8100;  i++) {
173         /* Initialize a timing out socket */
174         try {
175             server_socket = new CListeningSocket(i);
176         } catch (CException) {
177             continue;
178         }
179         STimeout         accept_timeout = { 0, 20000 };
180         STimeout         connect_timeout = { 1, 20000 };
181         if (server_socket->GetStatus() != eIO_Success) {
182             server_socket->Close();
183             delete server_socket;
184             continue;
185         }
186         try {
187             s_TimeoutingSock = new CSocket(CSocketAPI::gethostbyaddr(0), i,
188                                            &connect_timeout);
189         } catch (CException) {
190             continue;
191         }
192         s_PeerSock       = new CSocket;
193         if (server_socket->Accept(*s_PeerSock, &accept_timeout) == eIO_Success) {
194             server_socket->Close();
195             delete server_socket;
196             char buf[1024];
197             size_t n_read;
198             s_TimeoutingSock->SetTimeout(eIO_Read, &accept_timeout);
199             s_TimeoutingSock->SetTimeout(eIO_Write, &accept_timeout);
200             s_TimeoutingSock->Read((void*)buf, 1024, &n_read);
201             return;
202         } else {
203             s_TimeoutingSock->Close();
204             delete s_TimeoutingSock;
205             s_PeerSock->Close();
206             delete s_PeerSock;
207             server_socket->Close();
208             delete server_socket;
209             continue;
210         }
211         /* If we got here, everything works fine */
212     }
213     throw CMonkeyException(CDiagCompileInfo(), NULL,
214                             CMonkeyException::e_MonkeyUnknown,
215                             "Could not create a peer socket for the "
216                             "timeouting socket. Tried ports 8080-8100",
217                             ncbi::EDiagSev::eDiagSevMin);
218 }
219 
220 
s_TimeoutingSocketDestroy(void)221 static void s_TimeoutingSocketDestroy(void)
222 {
223     if (s_TimeoutingSock != NULL) {
224         s_TimeoutingSock->Close();
225         delete s_TimeoutingSock;
226         s_TimeoutingSock = NULL;
227     }
228     if (s_PeerSock != NULL) {
229         s_PeerSock->Close();
230         delete s_PeerSock;
231         s_PeerSock = NULL;
232     }
233 }
234 
235 
s_GetMonkeySection()236 static string s_GetMonkeySection()
237 {
238     CNcbiRegistry& config = CNcbiApplication::Instance()->GetConfig();
239     return config.Get(CMonkeyMocks::GetMainMonkeySection(), "config");
240 }
241 
242 
s_Monkey_Split(const string & s,char delim)243 static vector<string> s_Monkey_Split(const string &s, char delim) {
244     vector<string> elems;
245     s_Monkey_Split(s, delim, elems);
246     return elems;
247 }
248 
249 
s_MONKEY_GenRandomString(char * s,const size_t len)250 static void s_MONKEY_GenRandomString(char *s, const size_t len) {
251     static const char alphabet[] =
252         "0123456789"
253         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
254         "abcdefghijklmnopqrstuvwxyz"
255         " !@#$%^&*()-=_+/,.{}[];':`~<>"
256         "\\\n\"";
257 
258     for (size_t i = 0; i < len - 1; ++i) {
259         s[i] = alphabet[rand() % (sizeof(alphabet) - 1)];
260     }
261 
262     s[len-1] = 0;
263 }
264 
265 
x_GetSocketDestinations(MONKEY_SOCKTYPE sock,string * fqdn,string * IP,unsigned short * my_port,unsigned short * peer_port)266 void CMonkey::x_GetSocketDestinations(MONKEY_SOCKTYPE sock,
267                                              string*         fqdn,
268                                              string*         IP,
269                                              unsigned short* my_port,
270                                              unsigned short* peer_port)
271 {
272     struct sockaddr_in sock_addr;
273 #ifdef NCBI_OS_MSWIN
274     int len_inet = sizeof(sock_addr);
275 #else
276     socklen_t len_inet = sizeof(sock_addr);
277 #endif
278     if (my_port != NULL) {
279         getsockname(sock, (struct sockaddr*)&sock_addr, &len_inet);
280         *my_port = ntohs(sock_addr.sin_port);
281     }
282     /* If we need peer information for at least one value */
283     if (peer_port || fqdn || IP)
284         getpeername(sock, (struct sockaddr*)&sock_addr, &len_inet);
285     if (peer_port != NULL)
286         *peer_port = ntohs(sock_addr.sin_port);
287 #ifndef NCBI_OS_MSWIN
288     uint32_t addr = sock_addr.sin_addr.s_addr;
289 #else
290     u_long addr = sock_addr.sin_addr.S_un.S_addr;
291 #endif
292     SFqdnIp&& net_data = x_GetFqdnIp(addr);
293     if (fqdn != NULL)
294         *fqdn = net_data.fqdn;
295     if (IP != NULL)
296         *IP = net_data.ip;
297 }
298 
299 
300 #if 0
301 #   ifdef NCBI_OS_MSWIN
302 static
303 int s_MONKEY_GetTimeOfDay(struct timeval* tv)
304 {
305     FILETIME         systime;
306     unsigned __int64 sysusec;
307 
308     if (!tv)
309         return -1;
310 
311     GetSystemTimeAsFileTime(&systime);
312 
313     sysusec = systime.dwHighDateTime;
314     sysusec <<= 32;
315     sysusec |= systime.dwLowDateTime;
316     sysusec += 5;
317     sysusec /= 10;
318 
319     tv->tv_usec = (long)(sysusec % 1000000);
320     tv->tv_sec = (long)(sysusec / 1000000 - 11644473600Ui64);
321 
322     return 0;
323 }
324 
325 #   else
326 
327 #       define s_MONKEY_GetTimeOfDay(tv)  gettimeofday(tv, 0)
328 
329 #   endif
330 
331 static
332 double s_MONKEY_TimeDiff(const struct timeval* end,
333 const struct timeval* beg)
334 {
335     if (end->tv_sec < beg->tv_sec)
336         return 0.0;
337     if (end->tv_usec < beg->tv_usec) {
338         if (end->tv_sec == beg->tv_sec)
339             return 0.0;
340         return (end->tv_sec - beg->tv_sec - 1)
341             + (end->tv_usec - beg->tv_usec + 1000000) / 1000000.0;
342     }
343     return (end->tv_sec - beg->tv_sec)
344         + (end->tv_usec - beg->tv_usec) / 1000000.0;
345 }
346 #endif
347 
348 
349 /*//////////////////////////////////////////////////////////////////////////////
350 //                             CMonkeySeedKey                                 //
351 //////////////////////////////////////////////////////////////////////////////*/
Key()352 const CMonkeySeedKey& CMonkeySeedAccessor::Key()
353 {
354     static CMonkeySeedKey key;
355     return key;
356 }
357 
358 
359 /*//////////////////////////////////////////////////////////////////////////////
360 //                             CMonkeyRuleBase                                //
361 //////////////////////////////////////////////////////////////////////////////*/
s_RuleTypeString(EMonkeyActionType type)362 static string s_RuleTypeString(EMonkeyActionType type)
363 {
364     switch (type) {
365     case eMonkey_Connect:
366         return "connect";
367     case eMonkey_Poll:
368         return "poll";
369     case eMonkey_Recv:
370         return "read";
371     case eMonkey_Send:
372         return "write";
373     default:
374         throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
375                                NULL, CMonkeyException::e_MonkeyInvalidArgs,
376                                string("Unknown EMonkeyActionType value"));
377     }
378 }
379 
380 
s_SocketCallString(EMonkeyActionType action)381 static string s_SocketCallString(EMonkeyActionType action)
382 {
383     switch (action) {
384     case eMonkey_Recv:
385         return "recv()";
386     case eMonkey_Send:
387         return "send()";
388     case eMonkey_Poll:
389         return "poll()";
390     case eMonkey_Connect:
391         return "connect()";
392     default:
393         throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
394                                NULL, CMonkeyException::e_MonkeyInvalidArgs,
395                                string("Unknown EMonkeyActionType value"));
396     }
397 }
398 
399 
s_EIOStatusString(EIO_Status status)400 static string s_EIOStatusString(EIO_Status status)
401 {
402     switch (status) {
403     case eIO_Timeout:
404         return "eIO_Timeout";
405     case eIO_Closed:
406         return "eIO_Closed";
407     case eIO_Unknown:
408         return "eIO_Unknown";
409     case eIO_Interrupt:
410         return "eIO_Interrupt";
411     default:
412         break;
413     }
414 }
415 
416 
CMonkeyRuleBase(EMonkeyActionType action_type,string section,const vector<string> & params)417 CMonkeyRuleBase::CMonkeyRuleBase(EMonkeyActionType     action_type,
418                                  string                section,
419                                  const vector<string>& params)
420     : m_ReturnStatus(-1), m_RepeatType(eMonkey_RepeatNone), m_Delay (0),
421       m_RunsSize(0), m_ActionType(action_type), m_Section(section)
422 {
423     /** If there are no-interception runs before repeating the cycle,
424     * we know that from m_RunsSize */
425     for (unsigned int i = 0; i < params.size(); i++) {
426         vector<string> name_value = s_Monkey_Split(params[i], '=');
427         string name = name_value[0];
428         string value = name_value[1];
429         if (name == "return_status") {
430             x_ReadEIOStatus(value);
431         } else if (name == "runs") {
432             x_ReadRuns(value);
433         } else if (name == "delay") {
434             m_Delay = NStr::StringToULong(value);
435         }
436     }
437 }
438 
439 
AddSocket(MONKEY_SOCKTYPE sock)440 void CMonkeyRuleBase::AddSocket(MONKEY_SOCKTYPE sock)
441 {
442     /* Element has to exist */
443     m_RunPos[sock];
444 }
445 
446 /** Check that the rule should trigger on this run */
CheckRun(MONKEY_SOCKTYPE sock,unsigned short rule_probability,unsigned short probability_left) const447 bool CMonkeyRuleBase::CheckRun(MONKEY_SOCKTYPE sock,
448                                unsigned short  rule_probability,
449                                unsigned short  probability_left) const
450 {
451     bool isRun = false;
452 
453     int rand_val = CMonkey::Instance()->GetRand(Key()) % 100;
454     isRun = (rand_val) < rule_probability * 100 / probability_left;
455     LOG_POST(Note << "[CMonkeyRuleBase::CheckRun]  Checking if the rule "
456         << "for " << s_SocketCallString(m_ActionType)
457                   << " will be run this time. Random value is "
458                   << rand_val << ", probability threshold is "
459                   << rule_probability * 100 / probability_left);
460     LOG_POST(Note << "[CMonkeyRuleBase::CheckRun]  The rule will be "
461                   << (isRun ? "" : "NOT ") << "run");
462     return isRun;
463 }
464 
465 
GetProbability(MONKEY_SOCKTYPE sock) const466 unsigned short CMonkeyRuleBase::GetProbability(MONKEY_SOCKTYPE sock) const
467 {
468     if (m_RunPos.find(sock) == m_RunPos.end()) {
469         throw CMonkeyException(
470             CDiagCompileInfo(__FILE__, __LINE__),
471             NULL, CMonkeyException::e_MonkeyInvalidArgs,
472             "The socket provided has not been registered with current rule");
473     }
474     /* If 'runs' not set, the rule always engages */
475     if (m_RunsSize == 0) {
476         return 100;
477     }
478     if (m_RunMode == eMonkey_RunProbability) {
479         return static_cast<unsigned short>(m_Runs.at(m_RunPos.at(sock)) * 100);
480     } else {
481         /* If current run is more than the maximum run in the rule, we have to
482          * start over, or stop*/
483         if ((double)(m_RunPos.at(sock) + 1) > m_Runs.back()) {
484             switch (m_RepeatType) {
485             case eMonkey_RepeatNone:
486                 return 0;
487             case eMonkey_RepeatLast:
488                 return 100;
489             }
490         }
491         /* If m_Runs has the exact number of current run, the rule triggers */
492         return std::binary_search(m_Runs.begin(),
493                                   m_Runs.end(),
494                                   m_RunPos.at(sock) + 1) ? 100 : 0;
495     }
496     /* If "runs" is not set, rule always triggers */
497     if (m_Runs.empty()) return 100;
498 
499 }
500 
501 
x_ReadEIOStatus(const string & eIOStatus_in)502 void CMonkeyRuleBase::x_ReadEIOStatus(const string& eIOStatus_in)
503 {
504     /* Check that 'runs' has not been set before */
505     if (m_ReturnStatus != -1) {
506         PARAM_TWICE_EXCEPTION("return_status");
507     }
508     string eIOStatus = eIOStatus_in;
509     NStr::ToLower(eIOStatus);
510     if (eIOStatus == "eio_closed") {
511         m_ReturnStatus = eIO_Closed;
512     } else if (eIOStatus == "eio_invalidarg") {
513         throw CMonkeyException(
514             CDiagCompileInfo(__FILE__, __LINE__),
515             NULL, CMonkeyException::e_MonkeyInvalidArgs,
516             string("Unsupported 'return_status': ") + eIOStatus_in);
517     } else if (eIOStatus == "eio_interrupt") {
518         m_ReturnStatus = eIO_Interrupt;
519     } else if (eIOStatus == "eio_success") {
520         m_ReturnStatus = eIO_Success;
521     } else if (eIOStatus == "eio_timeout") {
522         m_ReturnStatus = eIO_Timeout;
523     } else if (eIOStatus == "eio_unknown") {
524         m_ReturnStatus = eIO_Unknown;
525     } else if (eIOStatus == "eio_notsupported") {
526         m_ReturnStatus = eIO_NotSupported;
527     } else {
528         throw CMonkeyException(
529             CDiagCompileInfo(__FILE__, __LINE__),
530             NULL, CMonkeyException::e_MonkeyInvalidArgs,
531             string("Could not parse 'return_status': ") + eIOStatus_in);
532     }
533 }
534 
535 
x_ReadRuns(const string & runs)536 void CMonkeyRuleBase::x_ReadRuns(const string& runs)
537 {
538     /* Check that 'runs' has not been set before */
539     if (m_RunsSize != 0) {
540         PARAM_TWICE_EXCEPTION("runs");
541     }
542     /* We get the string already without whitespaces and only have to
543        split it on commas*/
544     vector<string> runs_list = s_Monkey_Split(runs, ',');
545     if (runs_list.size() == 0)
546         throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
547                                NULL, CMonkeyException::e_MonkeyInvalidArgs,
548                                string("Parameter \"runs\" is empty in [")
549                                + m_Section + "]" +
550                                s_RuleTypeString(m_ActionType));
551     m_RunMode = runs_list[0][runs_list[0].length() - 1] == '%'
552                 ? eMonkey_RunProbability : eMonkey_RunNumber;
553 
554     if (runs_list.size() == 1 && m_RunMode == eMonkey_RunProbability)
555         runs_list.push_back("...");
556     m_RepeatType = eMonkey_RepeatNone;
557     if ( *runs_list.rbegin() == "repeat" ) {
558         m_RepeatType = eMonkey_RepeatAgain;
559     } else if ( *runs_list.rbegin() == "..." ) {
560         m_RepeatType = eMonkey_RepeatLast;
561     }
562 
563     ERunFormat run_format = runs_list[0].find_first_of(':') != string::npos ?
564                                                           eMonkey_RunRanges :
565                                                           eMonkey_RunSequence;
566 
567     unsigned int end_pos = (m_RepeatType == eMonkey_RepeatNone)
568                            ? runs_list.size() : runs_list.size() - 1;
569 
570     for ( unsigned int i = 0;  i < end_pos;  i++ ) {
571         string& run = runs_list[i];
572         double prob;
573         if (m_RunMode == eMonkey_RunProbability) {
574             if (*run.rbegin() != '%') {
575                 throw CMonkeyException(
576                         CDiagCompileInfo(__FILE__, __LINE__),
577                         NULL, CMonkeyException::e_MonkeyInvalidArgs,
578                         "Value is not percentage: " + run +
579                         ", values have to be either only percentages or "
580                                "only numbers of tries, not mixed");
581             }
582             switch (run_format) {
583             default:
584                 assert(0);
585                 /* In release build - fall through to eMonkey_RunSequence */
586             case eMonkey_RunSequence:
587                 prob = NStr::StringToDouble(run.substr(0, run.length() - 1));
588                 m_Runs.push_back(prob / 100);
589                 break;
590             case eMonkey_RunRanges:
591                 size_t prob_start = run.find_first_of(':');
592                 size_t step = NStr::StringToSizet(run.substr(0, prob_start));
593                 size_t last_step = m_Runs.size();
594                 if (last_step == 0 && step != 1) {
595                     throw CMonkeyException(
596                             CDiagCompileInfo(__FILE__, __LINE__),
597                             NULL, CMonkeyException::e_MonkeyInvalidArgs,
598                             "In the string of runs: " + runs + " the first "
599                             "element MUST set value for the first run");
600                 }
601                 prob = NStr::StringToDouble(run.substr(prob_start + 1,
602                                             run.length() - prob_start - 2));
603                 for (size_t j = last_step+1; j > 0 && j < step; j++) {
604                     m_Runs.push_back(*m_Runs.rbegin());
605                 }
606                 m_Runs.push_back(prob / 100);
607                 break;
608             }
609         } else {
610             assert(run_format == eMonkey_RunSequence);
611             if (*run.rbegin() == '%') {
612                 throw CMonkeyException(
613                         CDiagCompileInfo(__FILE__, __LINE__),
614                         NULL, CMonkeyException::e_MonkeyInvalidArgs,
615                         string("Value is percentage: ") + run +
616                         string(", values have to be either only percentages or "
617                                "only numbers of tries, not mixed"));
618             }
619             int val = NStr::StringToInt(run);
620             if (m_Runs.size() > 0 && val <= *m_Runs.rbegin()) {
621                 throw CMonkeyException(
622                     CDiagCompileInfo(__FILE__, __LINE__),
623                     NULL, CMonkeyException::e_MonkeyInvalidArgs,
624                     string("\"runs\" should contain values in "
625                            "increasing order"));
626             }
627             m_Runs.push_back(val);
628         }
629     }
630     m_RunsSize = m_Runs.size();
631 }
632 
633 
GetReturnStatus() const634 int /* EIO_Status or -1 */ CMonkeyRuleBase::GetReturnStatus() const
635 {
636     return m_ReturnStatus;
637 }
638 
639 
GetDelay() const640 unsigned long CMonkeyRuleBase::GetDelay() const
641 {
642     return m_Delay;
643 }
644 
645 
GetSection(void) const646 std::string CMonkeyRuleBase::GetSection(void) const
647 {
648     return m_Section;
649 }
650 
651 
GetActionType(void) const652 EMonkeyActionType CMonkeyRuleBase::GetActionType(void) const
653 {
654     return m_ActionType;
655 }
656 
657 
IterateRun(MONKEY_SOCKTYPE sock)658 void CMonkeyRuleBase::IterateRun(MONKEY_SOCKTYPE sock)
659 {
660     if (m_Runs.empty())
661         return;
662     m_RunPos[sock]++;
663     /* In probability mode each item of m_Runs is a probability of each run */
664     if (m_RunMode == eMonkey_RunProbability) {
665         assert(m_RunPos[sock] <= m_Runs.size());
666         if (m_RunPos[sock] == m_Runs.size()) {
667             switch (m_RepeatType) {
668             case eMonkey_RepeatNone: case eMonkey_RepeatLast:
669                 m_RunPos[sock]--;
670             case eMonkey_RepeatAgain:
671                 m_RunPos[sock] = 0;
672                 break;
673             }
674         }
675         return;
676     }
677     /* In "number of the run" mode each item in m_Runs is a specific number of
678     run when the rule should trigger */
679     else if ((m_RunPos.at(sock) + 1) > *m_Runs.rbegin()) {
680         switch (m_RepeatType) {
681         case eMonkey_RepeatAgain:
682             m_RunPos[sock] = 0;
683             break;
684         }
685     }
686     return;
687 }
688 
689 
690 /*//////////////////////////////////////////////////////////////////////////////
691 //                              CMonkeyRWRuleBase                             //
692 //////////////////////////////////////////////////////////////////////////////*/
CMonkeyRWRuleBase(EMonkeyActionType action_type,string section,const vector<string> & params)693 CMonkeyRWRuleBase::CMonkeyRWRuleBase(EMonkeyActionType           action_type,
694                                      string                      section,
695                                      const vector<string>&       params)
696     : CMonkeyRuleBase(action_type, section, params)
697 {
698 }
699 
700 
701 //////////////////////////////////////////////////////////////////////////
702 // CMonkeyWriteRule
703 //////////////////////////////////////////////////////////////////////////
CMonkeyWriteRule(string section,const vector<string> & params)704 CMonkeyWriteRule::CMonkeyWriteRule(string section, const vector<string>& params)
705     : CMonkeyRWRuleBase(eMonkey_Send, section, params)
706 {
707 }
708 
709 
Run(MONKEY_SOCKTYPE sock,const MONKEY_DATATYPE data,MONKEY_LENTYPE size,int flags,SOCK * sock_ptr)710 MONKEY_RETTYPE CMonkeyWriteRule::Run(MONKEY_SOCKTYPE        sock,
711                                      const MONKEY_DATATYPE  data,
712                                      MONKEY_LENTYPE         size,
713                                      int                    flags,
714                                      SOCK*                  sock_ptr)
715 {
716 #ifdef NCBI_MONKEY_TESTS
717     g_MonkeyMock_SetInterceptedSend(true);
718 #endif /* NCBI_MONKEY_TESTS */
719     int return_status = GetReturnStatus();
720     LOG_POST(Error << "[CMonkeyReadRule::Run]  CHAOS MONKEY ENGAGE!!! "
721                       "INTERCEPTED send(). " <<
722                       (return_status != -1
723                        ? s_EIOStatusString((EIO_Status)return_status)
724                        : string()));
725     if (return_status != eIO_Success && return_status != -1) {
726         switch (return_status) {
727         case eIO_Timeout:
728             *sock_ptr = s_TimeoutingSock->GetSOCK();
729             MONKEY_SET_SOCKET_ERROR(SOCK_EWOULDBLOCK);
730             return -1;
731         case eIO_Closed:
732             MONKEY_SET_SOCKET_ERROR(SOCK_ENOTCONN);
733             return -1;
734         case eIO_Unknown:
735             MONKEY_SET_SOCKET_ERROR(MONKEY_ENOPROTOOPT);
736             return -1;
737         case eIO_Interrupt:
738             MONKEY_SET_SOCKET_ERROR(SOCK_EINTR);
739             return -1;
740         default:
741             break;
742         }
743     }
744 
745     return send(sock, (const char*)data, size, flags);
746 }
747 
748 
749 //////////////////////////////////////////////////////////////////////////
750 // CMonkeyReadRule
751 //////////////////////////////////////////////////////////////////////////
CMonkeyReadRule(string section,const vector<string> & params)752 CMonkeyReadRule::CMonkeyReadRule(string section, const vector<string>& params)
753     : CMonkeyRWRuleBase(eMonkey_Recv, section, params)
754 {
755     STimeout r_timeout = { 1, 0 };
756 }
757 
758 
Run(MONKEY_SOCKTYPE sock,MONKEY_DATATYPE buf,MONKEY_LENTYPE size,int flags,SOCK * sock_ptr)759 MONKEY_RETTYPE CMonkeyReadRule::Run(MONKEY_SOCKTYPE sock,
760                                     MONKEY_DATATYPE buf,
761                                     MONKEY_LENTYPE  size,
762                                     int             flags,
763                                     SOCK*           sock_ptr)
764 {
765 #ifdef NCBI_MONKEY_TESTS
766     g_MonkeyMock_SetInterceptedRecv(true);
767 #endif /* NCBI_MONKEY_TESTS */
768     int return_status = GetReturnStatus();
769     LOG_POST(Error << "[CMonkeyReadRule::Run]  CHAOS MONKEY ENGAGE!!! "
770                       "INTERCEPTED recv(). " <<
771                       (return_status != -1
772                        ? s_EIOStatusString((EIO_Status)return_status)
773                        : string()));
774     if (return_status != eIO_Success && return_status != -1) {
775         switch (return_status) {
776         case eIO_Timeout:
777             *sock_ptr = s_TimeoutingSock->GetSOCK();
778             MONKEY_SET_SOCKET_ERROR(SOCK_EWOULDBLOCK);
779             return -1;
780         case eIO_Closed:
781             MONKEY_SET_SOCKET_ERROR(SOCK_ENOTCONN);
782             return -1;
783         case eIO_Unknown:
784             MONKEY_SET_SOCKET_ERROR(MONKEY_ENOPROTOOPT);
785             return -1;
786         case eIO_Interrupt:
787             MONKEY_SET_SOCKET_ERROR(SOCK_EINTR);
788         default:
789             break;
790         }
791     }
792 
793     if (size == 0)
794         return 0;
795 
796     /* So we decided to override */
797     MONKEY_RETTYPE bytes_read = recv(sock, buf, size, flags);
798 
799     return bytes_read;
800 }
801 
802 
803 //////////////////////////////////////////////////////////////////////////
804 // CMonkeyConnectRule
805 //////////////////////////////////////////////////////////////////////////
806 
CMonkeyConnectRule(string section,const vector<string> & params)807 CMonkeyConnectRule::CMonkeyConnectRule(string                section,
808                                        const vector<string>& params)
809     : CMonkeyRuleBase(eMonkey_Connect, section, params)
810 {
811     bool allow_set = false;
812     for (unsigned int i = 0; i < params.size(); i++) {
813         vector<string> name_value = s_Monkey_Split(params[i], '=');
814         string name = name_value[0];
815         string value = name_value[1];
816         if (name == "allow") {
817             if (allow_set) {
818                 throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
819                                        NULL,
820                                        CMonkeyException::e_MonkeyInvalidArgs,
821                                        string("Parameter \"allow\" is set "
822                                        "twice in [") + GetSection() + "]" +
823                                        s_RuleTypeString(GetActionType()));
824             }
825             m_Allow = ConnNetInfo_Boolean(value.c_str()) == 1;
826             allow_set = true;
827         }
828     }
829     if (!allow_set)
830         throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
831                                NULL, CMonkeyException::e_MonkeyInvalidArgs,
832                                string("Parameter \"allow\" not set in [")
833                                + GetSection() + "]" +
834                                s_RuleTypeString(GetActionType()));
835 }
836 
837 
Run(MONKEY_SOCKTYPE sock,const struct sockaddr * name,MONKEY_SOCKLENTYPE namelen)838 int CMonkeyConnectRule::Run(MONKEY_SOCKTYPE        sock,
839                             const struct sockaddr* name,
840                             MONKEY_SOCKLENTYPE     namelen)
841 {
842 #ifdef NCBI_MONKEY_TESTS
843     g_MonkeyMock_SetInterceptedConnect(true);
844 #endif /* NCBI_MONKEY_TESTS */
845     int return_status = GetReturnStatus();
846     LOG_POST(Error << "[CMonkeyReadRule::Run]  CHAOS MONKEY ENGAGE!!! "
847                       "INTERCEPTED connect(). " <<
848                       (return_status != -1
849                        ? s_EIOStatusString((EIO_Status)return_status)
850                        : string()));
851     if (return_status != eIO_Success && return_status != -1) {
852         switch (return_status) {
853         case eIO_Timeout:
854             MONKEY_SET_SOCKET_ERROR(SOCK_EWOULDBLOCK);
855             return -1;
856         case eIO_Closed:
857             MONKEY_SET_SOCKET_ERROR(SOCK_ECONNREFUSED);
858             return -1;
859         case eIO_Unknown:
860             MONKEY_SET_SOCKET_ERROR(MONKEY_ENOPROTOOPT);
861             return -1;
862         case eIO_Interrupt:
863             MONKEY_SET_SOCKET_ERROR(SOCK_EINTR);
864             return -1;
865         default:
866             break;
867         }
868     }
869     if (GetDelay() > 0) {
870         SleepMilliSec(GetDelay(), EInterruptOnSignal::eInterruptOnSignal);
871     }
872     if (m_Allow) {
873         return connect(sock, name, namelen);
874     }
875     else {
876         struct sockaddr_in addr;
877         /* Connect to a non-existing host */
878         addr.sin_family = AF_INET;
879         addr.sin_port = 65511;
880         /* 3232235631 is 192.168.0.111 - does not exist at NCBI, which is
881            enough for the goal of test */
882 #ifdef NCBI_OS_MSWIN
883         addr.sin_addr.S_un.S_addr = htonl(3232235631);
884 #else
885         addr.sin_addr.s_addr = htonl(3232235631);
886 #endif /* NCBI_OS_MSWIN */
887         return connect(sock, (struct sockaddr*)&addr, namelen);
888     }
889 }
890 
891 
892 //////////////////////////////////////////////////////////////////////////
893 // CMonkeyPollRule
894 //////////////////////////////////////////////////////////////////////////
CMonkeyPollRule(string section,const vector<string> & params)895 CMonkeyPollRule::CMonkeyPollRule(string section, const vector<string>& params)
896     : CMonkeyRuleBase(eMonkey_Poll, section, params)
897 {
898     bool ignore_set = false;
899     for ( unsigned int i = 0;  i < params.size();  i++ ) {
900         vector<string> name_value = s_Monkey_Split(params[i], '=');
901         string name  = name_value[0];
902         string value = name_value[1];
903         if (name == "ignore") {
904             if (ignore_set) {
905                 throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
906                                        NULL,
907                                        CMonkeyException::e_MonkeyInvalidArgs,
908                                        string("Parameter \"ignore\" is set "
909                                        "twice in [") + GetSection() + "]" +
910                                        s_RuleTypeString(GetActionType()));
911             }
912             m_Ignore = ConnNetInfo_Boolean(value.c_str()) == 1;
913             ignore_set = true;
914         }
915     }
916     if (!ignore_set) {
917         throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
918                                NULL, CMonkeyException::e_MonkeyInvalidArgs,
919                                string("Parameter \"ignore\" not set in [")
920                                + GetSection() + "]" +
921                                s_RuleTypeString(GetActionType()));
922     }
923 }
924 
925 
Run(size_t * n,SOCK * sock,EIO_Status * return_status)926 bool CMonkeyPollRule::Run(size_t*     n,
927                           SOCK*       sock,
928                           EIO_Status* return_status)
929 {
930 #ifdef NCBI_MONKEY_TESTS
931     g_MonkeyMock_SetInterceptedPoll(true);
932 #endif /* NCBI_MONKEY_TESTS */
933     LOG_POST(Error << "[CMonkeyReadRule::Run]  CHAOS MONKEY ENGAGE!!! "
934                       "INTERCEPTED poll(). " <<
935                       (m_Ignore ? "Ignoring poll" : "NOT ignoring poll"));
936     if (m_Ignore) {
937         return true;
938     }
939     return false;
940 }
941 
942 
943 //////////////////////////////////////////////////////////////////////////
944 // CMonkeyPlan
945 //////////////////////////////////////////////////////////////////////////
CMonkeyPlan(const string & section)946 CMonkeyPlan::CMonkeyPlan(const string& section)
947     : m_Probability(100), m_HostRegex(".*"), m_Name(section)
948 {
949     /* Plan settings */
950     CNcbiRegistry& config = CNcbiApplication::Instance()->GetConfig();
951     string probability = config.Get(section, "probability");
952     if (probability != "") {
953         if (probability.find('%') != string::npos) {
954             probability = probability.substr(0, probability.length() - 1);
955             m_Probability = static_cast<unsigned short>(
956                 NStr::StringToUInt(probability));
957         } else {
958             m_Probability = static_cast<unsigned short>(
959                 NStr::StringToDouble(probability) * 100);
960         }
961     }
962     m_HostRegex = NStr::Replace(NStr::Replace(
963             config.Get(section, "host_match"), " ",  ""),
964                                                "\t", "");
965 
966     x_ReadPortRange(config.Get(section, "port_match"));
967 
968     /* Write rules */
969     x_LoadRules(section, "write",   m_WriteRules);
970     /* Read rules */
971     x_LoadRules(section, "read",    m_ReadRules);
972     /* Connect rules */
973     x_LoadRules(section, "connect", m_ConnectRules);
974     /* Poll rules */
975     x_LoadRules(section, "poll",    m_PollRules);
976 }
977 
978 
979 template<class Rule_Ty>
x_LoadRules(const string & section,const string & rule_type_str,vector<Rule_Ty> & container)980 void CMonkeyPlan::x_LoadRules(const string&    section,
981                               const string&    rule_type_str,
982                               vector<Rule_Ty>& container)
983 {
984     CNcbiRegistry& config = CNcbiApplication::Instance()->GetConfig();
985     bool multi_rule = false;
986     string rule_str = config.Get(section, rule_type_str);
987     if (rule_str == "") {
988         /* Try check if there are multiple rules defined */
989         if ((rule_str = config.Get(section, rule_type_str + "1")) != "")
990             multi_rule = true;
991     }
992     if ( rule_str == "" )
993         return;
994     /* If a rule was read - parse it */
995     string temp_conf = NStr::Replace(rule_str, string(" "), string(""));
996     vector<string> params = s_Monkey_Split(temp_conf, ';');
997     container.push_back(Rule_Ty(section, params));
998     if ( multi_rule ) {
999         int rule_num = 2;
1000         string val_name = rule_type_str + NStr::IntToString(rule_num++);
1001         while ( (rule_str = config.Get(section, val_name)) != "" ) {
1002             temp_conf = NStr::Replace(rule_str, string(" "), string(""));
1003             params = s_Monkey_Split(temp_conf, ';');
1004             container.push_back(Rule_Ty(section, params));
1005             val_name = rule_type_str+ NStr::IntToString(rule_num++);
1006         }
1007     }
1008 }
1009 
1010 
1011 #ifndef NCBI_OS_MSWIN
1012 /** Regex-like check (might use some refactoring) */
s_MatchRegex(const string & to_match,const string & regex)1013 static bool s_MatchRegex(const string& to_match, const string& regex)
1014 {
1015     /* Convert "match regex" task to "haystack and needle" task. We check
1016      * whether there is a wildcard in the beginning and/or in the end of
1017      * regex (other cases are not supported). And then check if to_match
1018      * starts with, end with or contains the needle */
1019 
1020     /* First - compatibility check. Functionality is limited. */
1021     /* Check for special characters and combinations that cannot be processed.
1022     * Method - remove combinations of characters that are supported and check
1023     * the rest */
1024     string exception_message = string("Pattern") + regex + " "
1025         "cannot be processed because regular expressions "
1026         "are not fully supported. You can use .* in the "
1027         "beginning and in the end of a pattern and | to separate "
1028         "patterns. Stop mark is set with \\\\.\n"
1029         "Example:\n"
1030         "host_match = .*nlm\\\\.nih.*|.*gov|10\\\\.55.*\n";
1031         string filtered = NStr::Replace(NStr::Replace(regex, "\\.", ""),
1032                                                              ".*" , "");
1033     if (filtered.find_first_of("[]()+^?{}$.*\\") != string::npos) {
1034         throw CMonkeyException(
1035             CDiagCompileInfo(__FILE__, __LINE__),
1036             NULL,
1037             CMonkeyException::e_MonkeyInvalidArgs,
1038             exception_message);
1039     }
1040     size_t pos = 0;
1041     size_t last_find = 0;
1042     bool start_wildcard = false, end_wildcard = false;
1043     while ((last_find = NStr::Find(regex, ".*", pos)) != NPOS &&
1044         pos < regex.length()) {
1045         pos = last_find + 1;
1046         if (last_find == 0) {
1047             start_wildcard = true;
1048         }
1049         else if (last_find == regex.length() - strlen(".*")) {
1050             end_wildcard = true;
1051         }
1052         else {
1053             throw CMonkeyException(
1054                 CDiagCompileInfo(__FILE__, __LINE__),
1055                 NULL,
1056                 CMonkeyException::e_MonkeyInvalidArgs,
1057                 exception_message);
1058         }
1059     }
1060     /*
1061      * Run the "Needle in a haystack" task
1062      */
1063     string needle = NStr::Replace(NStr::Replace(regex, ".*", ""), "\\.", ".");
1064     if (start_wildcard && !end_wildcard) {
1065         if (!NStr::StartsWith(to_match, needle)) {
1066             return false;
1067         }
1068     }
1069     else if (!start_wildcard && end_wildcard) {
1070         if (!NStr::EndsWith(to_match, needle)) {
1071             return false;
1072         }
1073     }
1074     else if (start_wildcard && end_wildcard) {
1075         if (NStr::Find(to_match, needle) == NPOS) {
1076             return false;
1077         }
1078     }
1079     return true;
1080 }
1081 #endif /* NCBI_OS_MSWIN */
1082 
1083 
1084 /* Compares supplied parameters to */
Match(const string & sock_host,unsigned short sock_port,const string & sock_url,unsigned short probability_left)1085 bool CMonkeyPlan::Match(const string&  sock_host,
1086                         unsigned short sock_port,
1087                         const string&  sock_url,
1088                         unsigned short probability_left)
1089 {
1090     /* Check that regex works and that at least one of hostname and IP
1091      * is not empty*/
1092     if ( !m_HostRegex.empty()  &&
1093          (sock_host.length() + sock_url.length() > 0) ) {
1094 #ifdef NCBI_OS_MSWIN
1095         regex reg(m_HostRegex);
1096         smatch find;
1097         if (!regex_match(sock_host, find, reg) &&
1098             !regex_match(sock_url,  find, reg)) {
1099             LOG_POST(Note << "[CMonkeyPlan::Match]  Plan " << m_Name << " was "
1100                           << "NOT matched. Reason: "
1101                           << "[host regex mismatch, host=" << sock_host
1102                           << ", regex = \"" << m_HostRegex << "\"]");
1103             return false;
1104         }
1105 #else
1106     /*
1107      * Regex is not supported - work with simple simulation - only .* in
1108      * beginning and end of pattern and | to separate patterns
1109      */
1110         vector<string> patterns = s_Monkey_Split(m_HostRegex, '|');
1111         auto it = patterns.begin();
1112         bool match_found = false;
1113         for (  ;  it != patterns.end();  it++) {
1114             if (s_MatchRegex(sock_url, *it) || s_MatchRegex(sock_host, *it)) {
1115                 match_found = true;
1116                 break;
1117             }
1118         }
1119         if (!match_found) {
1120             LOG_POST(Note << "[CMonkeyPlan::Match]  Plan " << m_Name << " was "
1121                           << "NOT matched. Reason: "
1122                           << "[host regex mismatch, host=" << sock_host
1123                           << ", regex = \"" << m_HostRegex << "\"]");
1124             return false;
1125         }
1126 #endif /* NCBI_OS_MSWIN */
1127     }
1128     /* If port match pattern is not set, any port is good */
1129     bool port_match = m_PortRanges.empty();
1130     auto port_iter = m_PortRanges.begin();
1131     for (; port_iter != m_PortRanges.end(); port_iter++) {
1132         if (sock_port >= *port_iter && sock_port <= *++port_iter) {
1133             port_match = true;
1134             break;
1135         }
1136     }
1137     int rand_val = CMonkey::Instance()->GetRand(Key()) % 100;
1138     bool prob_match = rand_val < m_Probability * 100 / probability_left;
1139     LOG_POST(Note << "[CMonkeyPlan::Match]  Checking if plan " << m_Name
1140                   << " will be matched. Random value is "
1141                   << rand_val << ", plan probability is "
1142                   << m_Probability << "%, probability left is "
1143                   << probability_left << "%, so probability threshold is "
1144                   << m_Probability * 100 / probability_left << "%");
1145     bool match = port_match && prob_match;
1146     LOG_POST(Note << "[CMonkeyPlan::Match]  Plan " << m_Name << " was "
1147                   << (match ? "" : "NOT ") << "matched"
1148                   << (!match ? ". Reason: " : "")
1149                   << (port_match ? "" : "[invalid port range] ")
1150                   << (prob_match ? "" : "[probability threshold]"));
1151     return port_match ? (rand_val < m_Probability * 100 / probability_left)
1152                                   : false;
1153 }
1154 
1155 
WriteRule(MONKEY_SOCKTYPE sock,const MONKEY_DATATYPE data,MONKEY_LENTYPE size,int flags,MONKEY_RETTYPE * bytes_written,SOCK * sock_ptr)1156 bool CMonkeyPlan::WriteRule(MONKEY_SOCKTYPE        sock,
1157                             const MONKEY_DATATYPE  data,
1158                             MONKEY_LENTYPE         size,
1159                             int                    flags,
1160                             MONKEY_RETTYPE*        bytes_written,
1161                             SOCK*                  sock_ptr)
1162 {
1163     short probability_left = 100;
1164     int res = -1;
1165     for (unsigned int i = 0;  i < m_WriteRules.size();  i++) {
1166         m_WriteRules[i].AddSocket(sock);
1167         unsigned short rule_prob = m_WriteRules[i].GetProbability(sock);
1168         if (m_WriteRules[i].CheckRun(sock, rule_prob, probability_left)) {
1169             *bytes_written = m_WriteRules[i].Run(sock, data, size, flags,
1170                                                  sock_ptr);
1171             res = 1;
1172             break;
1173         }
1174         /* If this rule did not engage, we go to the next rule, and
1175            remember to normalize probability of next rule */
1176         probability_left -= rule_prob;
1177         if (probability_left <= 0) {
1178             stringstream ss;
1179             ss << "Probability below zero for write rule in plan " << m_Name
1180                 << ". Check config!";
1181             throw CMonkeyException(
1182                         CDiagCompileInfo(__FILE__, __LINE__),
1183                         NULL, CMonkeyException::e_MonkeyInvalidArgs,
1184                         ss.str());
1185         }
1186     }
1187     // If no rules engaged, return 0
1188     if (res == -1)
1189         res = 0;
1190 
1191     // Iterate run in all rules
1192     for (unsigned int i = 0;  i < m_WriteRules.size();  i++) {
1193         m_WriteRules[i].IterateRun(sock);
1194     }
1195 
1196     return res == 0 ? false : true;
1197 }
1198 
1199 
ReadRule(MONKEY_SOCKTYPE sock,MONKEY_DATATYPE buf,MONKEY_LENTYPE size,int flags,MONKEY_RETTYPE * bytes_read,SOCK * sock_ptr)1200 bool CMonkeyPlan::ReadRule(MONKEY_SOCKTYPE        sock,
1201                            MONKEY_DATATYPE        buf,
1202                            MONKEY_LENTYPE         size,
1203                            int                    flags,
1204                            MONKEY_RETTYPE*        bytes_read,
1205                            SOCK*                  sock_ptr)
1206 {
1207     short probability_left = 100;
1208     int res = -1;
1209     for (unsigned int i = 0;  i < m_ReadRules.size();  i++) {
1210         m_ReadRules[i].AddSocket(sock);
1211         unsigned short rule_prob = m_ReadRules[i].GetProbability(sock);
1212         if (m_ReadRules[i].CheckRun(sock, rule_prob, probability_left)) {
1213             *bytes_read = m_ReadRules[i].Run(sock, buf, size, flags, sock_ptr);
1214             res = 1;
1215             break;
1216         }
1217         /* If this rule did not engage, we go to the next rule, and
1218            and remember to normalize probability of next rule */
1219         probability_left -= rule_prob;
1220         if (probability_left <= 0) {
1221             stringstream ss;
1222             ss << "Probability below zero for write rule in plan " << m_Name
1223                << ". Check config!";
1224             throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
1225                                    NULL, CMonkeyException::e_MonkeyInvalidArgs,
1226                                    ss.str());
1227         }
1228     }
1229 
1230     // If no rules engaged, return 0
1231     if (res == -1)
1232         res = 0;
1233 
1234     // Iterate run in all rules
1235     for (unsigned int i = 0;  i < m_ReadRules.size();  i++) {
1236         m_ReadRules[i].IterateRun(sock);
1237     }
1238 
1239     return res == 0 ? false : true;
1240 }
1241 
1242 
ConnectRule(MONKEY_SOCKTYPE sock,const struct sockaddr * name,MONKEY_SOCKLENTYPE namelen,int * result)1243 bool CMonkeyPlan::ConnectRule(MONKEY_SOCKTYPE        sock,
1244                               const struct sockaddr* name,
1245                               MONKEY_SOCKLENTYPE     namelen,
1246                               int*                   result)
1247 {
1248     short probability_left = 100;
1249     /* We are fine with losing precision. Just need a unique ID*/
1250     MONKEY_SOCKTYPE x_sock = CMonkey::Instance()->GetSockBySocketid(sock);
1251     int res = -1;
1252     for (unsigned int i = 0; i < m_ConnectRules.size(); i++) {
1253         m_ConnectRules[i].AddSocket(x_sock);
1254         unsigned short rule_prob =
1255             m_ConnectRules[i].GetProbability(x_sock);
1256         /* Check if the rule will trigger on this run. If not - we go to the
1257            next rule in plan */
1258         if (m_ConnectRules[i].CheckRun(x_sock, rule_prob, probability_left)) {
1259             /* 'result' is the result of connect() launched in the rule. It can
1260                even be result of original connect() with real parameters.
1261                Or, it can be an error code of a failed fake connect() */
1262             *result = m_ConnectRules[i].Run(sock, name, namelen);
1263             res = 1;
1264             break;
1265         }
1266         /* If this rule did not engage, we go to the next rule, and
1267            and remember to normalize probability of next rule */
1268         probability_left -= rule_prob;
1269         if (probability_left <= 0) {
1270             stringstream ss;
1271             ss << "Probability below zero for write rule in plan " << m_Name
1272                 << ". Check config!";
1273             throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
1274                                    NULL, CMonkeyException::e_MonkeyInvalidArgs,
1275                                    ss.str());
1276         }
1277     }
1278 
1279     // If no rules engaged, return 0
1280     if (res == -1)
1281         res = 0;
1282 
1283     // Iterate run in all rules
1284     for (unsigned int i = 0;  i < m_ConnectRules.size();  i++) {
1285         m_ConnectRules[i].IterateRun(x_sock);
1286     }
1287 
1288     return res == 0 ? false : true;
1289 }
1290 
1291 
PollRule(size_t * n,SOCK * sock,EIO_Status * return_status)1292 bool CMonkeyPlan::PollRule(size_t*     n,
1293                            SOCK*       sock,
1294                            EIO_Status* return_status)
1295 {
1296     short probability_left = 100;
1297     int res = -1;
1298     for (unsigned int i = 0;  i < m_PollRules.size();  i++) {
1299         m_PollRules[i].AddSocket((*sock)->sock);
1300         unsigned short rule_prob = m_PollRules[i].GetProbability((*sock)->sock);
1301         if (m_PollRules[i].CheckRun((*sock)->sock, rule_prob, probability_left))
1302         {
1303             res = m_PollRules[i].Run(n, sock, return_status) ? 1 : 0;
1304             break;
1305         }
1306         LOG_POST(Error << "[CMonkeyPlan::PollRule]  CHAOS MONKEY NOT "
1307                           "ENGAGED!!! poll() passed");
1308         /* If this rule did not engage, we go to the next rule, and
1309            and remember to normalize probability of next rule */
1310         probability_left -= rule_prob;
1311         if (probability_left <= 0) {
1312             stringstream ss;
1313             ss << "Probability below zero for write rule in plan " << m_Name
1314                 << ". Check config!";
1315             throw CMonkeyException(
1316                         CDiagCompileInfo(__FILE__, __LINE__),
1317                         NULL, CMonkeyException::e_MonkeyInvalidArgs,
1318                         ss.str());
1319         }
1320     }
1321 
1322     // If no rules triggered, return 0
1323     if (res == -1) {
1324         res = 0;
1325         *return_status = EIO_Status::eIO_Success;
1326     }
1327 
1328     // Iterate run in all rules
1329     for (unsigned int i = 0;  i < m_PollRules.size();  i++) {
1330         m_PollRules[i].IterateRun((*sock)->sock);
1331     }
1332 
1333     return res == 0 ? false : true;
1334 }
1335 
1336 
GetProbabilty(void)1337 unsigned short CMonkeyPlan::GetProbabilty(void)
1338 {
1339     return m_Probability;
1340 }
1341 
1342 
x_ReadPortRange(const string & conf)1343 void CMonkeyPlan::x_ReadPortRange(const string& conf)
1344 {
1345     vector<string> ranges = s_Monkey_Split(conf, ',');
1346     unsigned short from, to;
1347     auto it = ranges.begin();
1348     for (  ;  it != ranges.end();  it++  )  {
1349         if (it->find('-') != string::npos) {
1350             vector<string> ports = s_Monkey_Split(*it, '-');
1351             from = static_cast<unsigned short>(NStr::StringToUInt(ports[0]));
1352             to   = static_cast<unsigned short>(NStr::StringToUInt(ports[1]));
1353         } else {
1354             from = to = static_cast<unsigned short>(NStr::StringToUInt(*it));
1355         }
1356         /* From*/
1357         m_PortRanges.push_back(from);
1358         /* Until */
1359         m_PortRanges.push_back(to);
1360     }
1361 }
1362 
1363 
1364 //////////////////////////////////////////////////////////////////////////
1365 // CMonkey
1366 //////////////////////////////////////////////////////////////////////////
1367 CMonkey*          CMonkey::sm_Instance   = NULL;
1368 FMonkeyHookSwitch CMonkey::sm_HookSwitch = NULL;
1369 
1370 
CMonkey()1371 CMonkey::CMonkey() : m_Probability(100), m_Enabled(false)
1372 {
1373     if (sm_HookSwitch == NULL) {
1374         throw CMonkeyException(
1375             CDiagCompileInfo(__FILE__, __LINE__),
1376             NULL, CMonkeyException::e_MonkeyInvalidArgs,
1377             "Launch CONNECT_Init() before initializing CMonkey instance");
1378     }
1379     m_TlsToken = new CTls<int>;
1380     m_TlsRandList = new CTls<vector<int> >;
1381     m_TlsRandListPos = new CTls<int>;
1382     srand((unsigned int)time(NULL));
1383     m_Seed = rand();
1384     LOG_POST(Note << "[CMonkey::CMonkey()]  Chaos Monkey seed is: " << m_Seed);
1385     ReloadConfig();
1386 }
1387 
1388 
Instance()1389 CMonkey* CMonkey::Instance()
1390 {
1391     CFastMutexGuard spawn_guard(s_SingletonMutex);
1392 
1393     if (sm_Instance == NULL) {
1394         sm_Instance = new CMonkey;
1395     }
1396     return sm_Instance;
1397 }
1398 
1399 
IsEnabled()1400 bool CMonkey::IsEnabled()
1401 {
1402     return m_Enabled;
1403 }
1404 
1405 
ReloadConfig(const string & config)1406 void CMonkey::ReloadConfig(const string& config)
1407 {
1408     assert(sm_HookSwitch != NULL);
1409     CFastMutexGuard spawn_guard(s_ConfigMutex);
1410     string          rules;
1411     string          monkey_section = config.empty() ? s_GetMonkeySection() :
1412                                                       config;
1413     list<string>    sections;
1414     m_Plans.clear();
1415     CNcbiRegistry&  reg = CNcbiApplication::Instance()->GetConfig();
1416     if (ConnNetInfo_Boolean(reg.Get(kMonkeyMainSect, kEnablField).c_str()) != 1)
1417     {
1418         LOG_POST(Note << "[CMonkey::ReloadConfig]  Chaos Monkey is disabled "
1419                       << "in [" << kMonkeyMainSect << "]");
1420         m_Enabled = false;
1421         return;
1422     }
1423     string seed = reg.Get(kMonkeyMainSect, kSeedField).c_str();
1424     if (seed != "") {
1425         LOG_POST(Note << "[CMonkey::ReloadConfig]  Chaos Monkey seed is set "
1426                       << "to " << seed << " in config");
1427         SetSeed(NStr::StringToInt(seed));
1428     }
1429     reg.EnumerateSections(&sections);
1430     /* If the section does not exist */
1431     if (find(sections.begin(), sections.end(), monkey_section)
1432         == sections.end()) {
1433         LOG_POST(Error << "[CMonkey::ReloadConfig]  Config section [" <<
1434                           monkey_section << "] does not exist, Chaos Monkey "
1435                           "is NOT active!");
1436         m_Enabled = false;
1437         return;
1438     }
1439     if (ConnNetInfo_Boolean(reg.Get(monkey_section, kEnablField).c_str()) != 1)
1440     {
1441         LOG_POST(Note << "[CMonkey::ReloadConfig]  Chaos Monkey is disabled "
1442                       << "in [" << monkey_section << "]");
1443         m_Enabled = false;
1444         return;
1445     }
1446     m_Enabled = true;
1447     LOG_POST(Error << "[CMonkey::ReloadConfig]  Chaos Monkey is active!");
1448     string prob_str = reg.Get(monkey_section, "probability");
1449     if (prob_str != "") {
1450         prob_str = NStr::Replace(prob_str, " ", "");
1451         int prob;
1452         try {
1453             if (*prob_str.rbegin() == '%') {
1454                 string prob_tmp = prob_str.substr(0, prob_str.length() - 1);
1455                 prob = NStr::StringToInt(prob_tmp);
1456             }
1457             else {
1458                 prob = (int)(NStr::StringToDouble(prob_str) * 100);
1459             }
1460             if (prob < 0 || prob > 100) {
1461                 throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
1462                                        NULL,
1463                                        CMonkeyException::e_MonkeyInvalidArgs,
1464                                        "Parameter \"probability\"=" + prob_str
1465                                        + " for section [" + monkey_section
1466                                        + "] is not a valid value "
1467                                        "(valid range is 0%-100% or 0.0-1.0)");
1468             }
1469             m_Probability = (unsigned short)prob;
1470         }
1471         catch (const CStringException&) {
1472             throw CMonkeyException(CDiagCompileInfo(__FILE__, __LINE__),
1473                                    NULL, CMonkeyException::e_MonkeyInvalidArgs,
1474                                    "Parameter \"probability\"=" + prob_str
1475                                    + " for section [" + monkey_section
1476                                    + "] could not be parsed");
1477         }
1478     }
1479     // Disable hooks while Monkey initializes
1480     sm_HookSwitch(eMonkeyHookSwitch_Disabled);
1481     unsigned short total_probability = 0;
1482     for (int i = 1;  ;  ++i) {
1483         string section = monkey_section + "_PLAN" + NStr::IntToString(i);
1484         if (find(sections.begin(), sections.end(), section) ==
1485             sections.end()) {
1486             break;
1487         }
1488         m_Plans.push_back(CMonkeyPlan(section));
1489         total_probability += (*m_Plans.rbegin()).GetProbabilty();
1490     }
1491     /* Check that sum of probabilities for plans is less than 100%, otherwise -
1492      * throw an exception with an easy-to-read message */
1493     if (total_probability > 100) {
1494         stringstream ss;
1495         ss << "Total probability for plans in configuration " <<
1496             monkey_section << " exceeds 100%. Please check that summary " <<
1497             "probability for plans stays under 100% (where the remaining "
1498             "percents go to connections that are not intercepted by any plan)."
1499             "\nTurning Chaos Monkey off";
1500         LOG_POST(Critical << ss.str());
1501         m_Enabled = false;
1502     }
1503     if (m_Enabled) {
1504         s_TimeoutingSocketInit();
1505     }
1506     else {
1507         s_TimeoutingSocketDestroy();
1508     }
1509     sm_HookSwitch(m_Enabled ? eMonkeyHookSwitch_Enabled
1510                             : eMonkeyHookSwitch_Disabled);
1511 }
1512 
1513 
Send(MONKEY_SOCKTYPE sock,const MONKEY_DATATYPE data,MONKEY_LENTYPE size,int flags,SOCK * sock_ptr)1514 MONKEY_RETTYPE CMonkey::Send(MONKEY_SOCKTYPE        sock,
1515                              const MONKEY_DATATYPE  data,
1516                              MONKEY_LENTYPE         size,
1517                              int                    flags,
1518                              SOCK*                  sock_ptr)
1519 {
1520     string host_fqdn, host_IP;
1521     unsigned short peer_port;
1522     if (m_Enabled) {
1523         x_GetSocketDestinations(sock, &host_fqdn, &host_IP, NULL, &peer_port);
1524         LOG_POST(Note << "[CMonkey::Send]  For connection with port "
1525                       << peer_port << ", host " << host_IP
1526                       << " and hostname " << host_fqdn << ".");
1527         CMonkeyPlan* sock_plan = x_FindPlan(sock, host_fqdn,host_IP,peer_port);
1528 
1529         if (sock_plan != NULL) {
1530             MONKEY_RETTYPE bytes_written;
1531             /* Plan may decide to leave connection untouched */
1532             if (sock_plan->WriteRule(sock, data, size, flags, &bytes_written,
1533                                      sock_ptr) )
1534                 return bytes_written;
1535         }
1536     }
1537     return send(sock, (const char*)data, size, flags);
1538 }
1539 
1540 
Recv(MONKEY_SOCKTYPE sock,MONKEY_DATATYPE buf,MONKEY_LENTYPE size,int flags,SOCK * sock_ptr)1541 MONKEY_RETTYPE CMonkey::Recv(MONKEY_SOCKTYPE        sock,
1542                              MONKEY_DATATYPE        buf,
1543                              MONKEY_LENTYPE         size,
1544                              int                    flags,
1545                              SOCK*                  sock_ptr)
1546 {
1547     string host_fqdn, host_IP;
1548     unsigned short peer_port;
1549     if (m_Enabled) {
1550         x_GetSocketDestinations(sock, &host_fqdn, &host_IP, NULL, &peer_port);
1551         LOG_POST(Note << "[CMonkey::Recv]  For connection with port "
1552                       << peer_port << ", host " << host_IP
1553                       << " and hostname " << host_fqdn << ".");
1554 
1555         CMonkeyPlan* sock_plan = x_FindPlan(sock, host_fqdn, host_IP,peer_port);
1556 
1557         if (sock_plan != NULL) {
1558             MONKEY_RETTYPE bytes_read;
1559             if (sock_plan->ReadRule(sock, buf, size, flags, &bytes_read,
1560                                     sock_ptr))
1561                 return bytes_read;
1562         }
1563     }
1564     return recv(sock, buf, size, flags);
1565 }
1566 
1567 
Connect(MONKEY_SOCKTYPE sock,const struct sockaddr * name,MONKEY_SOCKLENTYPE namelen)1568 int CMonkey::Connect(MONKEY_SOCKTYPE        sock,
1569                      const struct sockaddr* name,
1570                      MONKEY_SOCKLENTYPE     namelen)
1571 {
1572     union {
1573         struct sockaddr    sa;
1574         struct sockaddr_in in;
1575 #ifdef NCBI_OS_UNIX
1576         struct sockaddr_un un;
1577 #endif /*NCBI_OS_UNIX*/
1578     } addr;
1579     if (m_Enabled) {
1580         addr.sa = *name;
1581         unsigned int host = addr.in.sin_addr.s_addr;
1582         string host_fqdn, host_IP;
1583         unsigned short peer_port = ntohs(addr.in.sin_port);
1584         SFqdnIp&&    net_data = x_GetFqdnIp(host);
1585         LOG_POST(Note << "[CMonkey::Connect]  For connection with port "
1586                       << peer_port << ", host " << host_IP
1587                       << " and hostname " << host_fqdn << ".");
1588         CMonkeyPlan* sock_plan = x_FindPlan(CMonkey::GetSockBySocketid(sock),
1589                                             net_data.fqdn, net_data.ip,
1590                                             peer_port);
1591         if (sock_plan != NULL) {
1592             int result;
1593             if ( sock_plan->ConnectRule(sock, name, namelen, &result) )
1594                 return result;
1595         }
1596     }
1597     return connect(sock, name, namelen);
1598 }
1599 
1600 
Poll(size_t * n,SSOCK_Poll ** polls,EIO_Status * return_status)1601 bool CMonkey::Poll(size_t*      n,
1602                    SSOCK_Poll** polls,
1603                    EIO_Status*  return_status)
1604 {
1605     if (m_Enabled) {
1606         size_t polls_iter       = 0;
1607         SSOCK_Poll* new_polls   =
1608             static_cast<SSOCK_Poll*>(calloc(*n, sizeof(SSOCK_Poll)));
1609         int polls_count         = 0;
1610         while (polls_iter < *n) {
1611             SOCK&        sock      = (*polls)[polls_iter].sock;
1612             if (sock == NULL) continue;
1613             LOG_POST(Note << "[CMonkey::Poll]  For connection with port "
1614                           << sock->myport << ", host <empty> and "
1615                              "hostname <empty>.");
1616             if (sock != nullptr)
1617             {
1618                 CMonkeyPlan* sock_plan = x_FindPlan(sock->id, "", "",
1619                                                     sock->myport);
1620                 if (sock_plan == NULL ||
1621                     (sock_plan != NULL && !sock_plan->PollRule(n, &sock,
1622                                                                return_status)) )
1623                 {
1624                     new_polls[polls_count++] = (*polls)[polls_iter];
1625                 }
1626             } else { /* something unreadable by Monkey, but still valid */
1627                 new_polls[polls_count++] = (*polls)[polls_iter];
1628             }
1629             polls_iter++;
1630         }
1631         *polls = new_polls;
1632         *n = polls_count;
1633     }
1634     return false;
1635 }
1636 
1637 
Close(MONKEY_SOCKTYPE sock)1638 void CMonkey::Close(MONKEY_SOCKTYPE sock)
1639 {
1640     auto sock_plan = m_KnownSockets.find(sock);
1641     if (sock_plan != m_KnownSockets.end()) {
1642         m_KnownSockets.erase(sock_plan);
1643     }
1644     auto sock_iter = m_SocketMemory.begin();
1645     for (  ;  sock_iter != m_SocketMemory.end()  ;  )
1646     {
1647         if (sock_iter->first == sock)
1648             m_SocketMemory.erase(sock_iter++);
1649         else
1650             ++sock_iter;
1651     }
1652 }
1653 
1654 
SockHasSocket(SOCK sock,MONKEY_SOCKTYPE socket)1655 void CMonkey::SockHasSocket(SOCK sock, MONKEY_SOCKTYPE socket)
1656 {
1657     m_SocketMemory[socket] = sock;
1658 }
1659 
1660 
GetSockBySocketid(MONKEY_SOCKTYPE socket)1661 MONKEY_SOCKTYPE CMonkey::GetSockBySocketid(MONKEY_SOCKTYPE socket)
1662 {
1663     union SSockSocket
1664     {
1665         SOCK sock;
1666         MONKEY_SOCKTYPE socket;
1667     };
1668     SSockSocket sock_socket;
1669     sock_socket.sock = m_SocketMemory[socket];
1670     return sock_socket.socket;
1671 }
1672 
1673 
MonkeyHookSwitchSet(FMonkeyHookSwitch hook_switch_func)1674 void CMonkey::MonkeyHookSwitchSet(FMonkeyHookSwitch hook_switch_func)
1675 {
1676     sm_HookSwitch = hook_switch_func;
1677 }
1678 
1679 
1680 /** Return plan for the socket, new or already assigned one. If the socket
1681  * is ignored by Chaos Monkey, NULL is returned */
x_FindPlan(MONKEY_SOCKTYPE sock,const string & hostname,const string & host_IP,unsigned short port)1682 CMonkeyPlan* CMonkey::x_FindPlan(MONKEY_SOCKTYPE sock,  const string& hostname,
1683                                  const string& host_IP, unsigned short port)
1684 {
1685     CFastMutexGuard spawn_guard(s_KnownConnMutex);
1686     auto sock_plan = m_KnownSockets.find(sock);
1687     if (sock_plan != m_KnownSockets.end()) {
1688         return sock_plan->second;
1689     }
1690     /* Plan was not found. First roll the dice to know if Monkey will process
1691      * current socket */
1692     int rand_val = CMonkey::Instance()->GetRand(Key()) % 100;
1693     LOG_POST(Note << "[CMonkey::x_FindPlan]  Checking if connection "
1694                      "with port " << port << ", host " << host_IP
1695                   << " and hostname " << hostname << " will be "
1696                   << "intercepted by Chaos Monkey. Random value is "
1697                   << rand_val << ", probability threshold is "
1698                   << m_Probability);
1699     LOG_POST(Note << "[CMonkey::x_FindPlan]  The connection will "
1700                   << ((rand_val >= m_Probability) ? "NOT " : "")
1701                   << "be processed.");
1702     if (rand_val >= m_Probability) {
1703         return NULL;
1704     }
1705     /* Now we can find a plan */
1706     bool match_found = false;
1707     unsigned short probability_left = 100; /* If we tried to match a plan with
1708                                               n% probability with no luck,
1709                                               next plan only has (100-n)%
1710                                               fraction of connections to match
1711                                               against, so if its probability to
1712                                               all connections is m%, its
1713                                               probability to connections left
1714                                               is m/(100-n)*100%. To count this,
1715                                               we need probability_left */
1716     for (unsigned int i = 0; i < m_Plans.size() && !match_found; i++) {
1717         /* Match includes probability of plan*/
1718         if (m_Plans[i].Match(host_IP, port, hostname, probability_left)) {
1719             // 3. If found plan - use it and assign to this socket
1720             m_KnownSockets[sock] = &m_Plans[i];
1721             return m_KnownSockets[sock];
1722         }
1723         probability_left -= m_Plans[i].GetProbabilty();
1724     }
1725     /* If no plan triggered, then this socket will be always ignored */
1726     m_KnownSockets[sock] = NULL;
1727     return NULL;
1728 }
1729 
1730 
x_GetFqdnIp(unsigned int host)1731 CMonkey::SFqdnIp CMonkey::x_GetFqdnIp(unsigned int host)
1732 {
1733     CFastMutexGuard guard(s_NetworkDataCacheMutex);
1734     auto iter = m_NetworkDataCache.find(host);
1735     if (iter != m_NetworkDataCache.end())
1736         return iter->second;
1737     string       host_fqdn = CSocketAPI::gethostbyaddr(host);
1738     string       host_IP = CSocketAPI::HostPortToString(host, 0);
1739     return m_NetworkDataCache[host] = SFqdnIp(host_fqdn, host_IP);
1740 }
1741 
1742 
RegisterThread(int token)1743 bool CMonkey::RegisterThread(int token)
1744 {
1745     if (!m_Enabled) {
1746         ERR_POST(Error << "[CMonkey::RegisterThread]  Chaos Monkey is "
1747                        << "disabled, the thread with token "
1748                        << token << " was not registered");
1749         return false;
1750     }
1751     CFastMutexGuard guard(s_SeedLogConfigMutex);
1752     LOG_POST(Note << "[CMonkey::RegisterThread]  Registering thread with "
1753                   << "token " << token);
1754 
1755     stringstream ss;
1756     ss << "[CMonkey::RegisterThread]  Token " << token
1757        << " has been already registered in CMonkey and cannot be used again";
1758     if (m_RegisteredTokens.find(token) != m_RegisteredTokens.end()) {
1759         throw CMonkeyException(
1760             CDiagCompileInfo(__FILE__, __LINE__),
1761             NULL, CMonkeyException::e_MonkeyInvalidArgs,
1762             ss.str());
1763     }
1764     m_RegisteredTokens.insert(token);
1765 
1766     /* Remember token */
1767     m_TlsToken->SetValue(new int, s_TlsCleanup<int>);
1768     *m_TlsToken->GetValue() = token;
1769 
1770     vector<int>* rand_list = new vector<int>();
1771     srand(m_Seed + token);
1772     stringstream rand_list_str;
1773     for (unsigned int i = 0; i < kRandCount; ++i) {
1774         rand_list->push_back(rand());
1775         rand_list_str << *rand_list->rbegin() << " ";
1776     }
1777     LOG_POST(Note << "[CMonkey::RegisterThread]  Random list for token "
1778                   << token << " is " << rand_list_str.str());
1779     m_TlsRandList->SetValue(rand_list, s_TlsCleanup< vector<int> >);
1780 
1781     m_TlsRandListPos->SetValue(new int, s_TlsCleanup<int>);
1782     *m_TlsRandListPos->GetValue() = 0;
1783 
1784     return true;
1785 }
1786 
1787 
GetSeed()1788 int CMonkey::GetSeed()
1789 {
1790     /* save m_seedlog to file */
1791     return m_Seed;
1792 }
1793 
1794 
SetSeed(int seed)1795 void CMonkey::SetSeed(int seed)
1796 {
1797     /* load m_seedlog from file */
1798     m_Seed = seed;
1799     LOG_POST(Info << "[CMonkey::SetSeed]  Chaos Monkey seed was manually "
1800                      "changed to: " << m_Seed);
1801 }
1802 
1803 
GetRand(const CMonkeySeedKey &)1804 int CMonkey::GetRand(const CMonkeySeedKey& /* key */)
1805 {
1806     if (m_TlsToken->GetValue() == NULL) {
1807         return rand();
1808     }
1809     int& list_pos = *m_TlsRandListPos->GetValue();
1810     if (++list_pos == kRandCount) {
1811         list_pos = 0;
1812     }
1813     int next_list_pos = (list_pos + 1 == kRandCount) ? 0 : list_pos + 1;
1814     LOG_POST(Note << "[CMonkey::GetRand]  Getting random value "
1815                   << (*m_TlsRandList->GetValue())[list_pos]
1816                   << " for thread " << *m_TlsToken->GetValue()
1817                   << ". Next random value is "
1818                   << (*m_TlsRandList->GetValue())[next_list_pos]);
1819     return (*m_TlsRandList->GetValue())[list_pos];
1820 }
1821 
1822 
1823 END_NCBI_SCOPE
1824 
1825 #endif /* #ifdef NCBI_MONKEY */
1826