1 /*
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Authors: Dmitriy Elisov
27 * Credits: Denis Vakatov
28 * @file
29 * File Description:
30 * A client for service discovery API based on LBOS.
31 * LBOS is a client for ZooKeeper cloud-based DB.
32 * LBOS allows to announce, deannounce and resolve services.
33 */
34
35
36 #include "ncbi_ansi_ext.h"
37 #include <connect/ncbi_http_connector.h>
38 #include "ncbi_lbosp.h"
39 #include "ncbi_priv.h"
40 #include <stdlib.h> /* free, realloc, calloc, malloc */
41 #include <ctype.h> /* isdigit */
42 #include "parson.h"
43 #define kHostportStringLength (16+1+5)/**<
44 strlen("255.255.255.255")+
45 strlen(":") +
46 strlen("65535") */
47 #define kMaxLineSize 1024 /**< used to build strings*/
48
49
50 #define NCBI_USE_ERRCODE_X Connect_LBSM /**< Used in
51 CORE_LOG*_X */
52
53 #ifdef NCBI_COMPILER_MSVC
54 # define LBOS_STRTOK strtok_s
55 #else
56 # define LBOS_STRTOK strtok_r
57 #endif
58 /*/////////////////////////////////////////////////////////////////////////////
59 // STATIC VARIABLES //
60 /////////////////////////////////////////////////////////////////////////////*/
61
62 #ifdef NCBI_OS_MSWIN
63 static const char* kLbosresolverFile = "C:\\Apps\\Admin_Installs\\etc"
64 "\\ncbi\\lbosresolver";
65 #else
66 static const char* kLbosresolverFile = "/etc/ncbi/lbosresolver";
67 #endif
68
69 static const char* kLBOSQuery = "/lbos/v3/services/"
70 "?format=json&show=all&q=";
71
72 /*
73 * LBOS registry section for announcement
74 */
75 static const char* kLBOSAnnouncementSection = "LBOS_ANNOUNCEMENT";
76 static const char* kLBOSServiceVariable = "SERVICE";
77 static const char* kLBOSVersionVariable = "VERSION";
78 static const char* kLBOSServerHostVariable = "HOST";
79 static const char* kLBOSPortVariable = "PORT";
80 static const char* kLBOSHealthcheckUrlVariable = "HEALTHCHECK";
81 static const char* kLBOSMetaVariable = "meta";
82
83
84 static SConnNetInfo* s_EmptyNetInfo = NULL; /* Do not change.. */
85 static char* s_LBOS_Lbosresolver = NULL; /* ..after init */
86 static const int kInitialCandidatesCount = 1; /* For initial memory
87 allocation */
88 static int s_LBOS_TurnedOn = 1; /* If LBOS cannot
89 resolve even itself,
90 we turn it off for
91 performance */
92 static int s_LBOS_Init = 0; /* If client has already
93 been initialized */
94
95 static
96 struct SLBOS_AnnounceHandle_Tag*
97 s_LBOS_AnnouncedServers = NULL;
98 static
99 unsigned int s_LBOS_AnnouncedServersNum = 0;
100 static
101 unsigned int s_LBOS_AnnouncedServersAlloc = 0;
102 /*/////////////////////////////////////////////////////////////////////////////
103 // STATIC FUNCTION DECLARATIONS //
104 /////////////////////////////////////////////////////////////////////////////*/
105 static SSERV_Info** s_LBOS_ResolveIPPort (const char* lbos_address,
106 const char* serviceName,
107 SConnNetInfo* net_info);
108 static char * s_LBOS_UrlReadAll (SConnNetInfo* net_info,
109 const char* url,
110 int* status_code,
111 char** status_message);
112 static void s_LBOS_FillCandidates(SLBOS_Data* data,
113 const char* service);
114 static SLBOS_Data* s_LBOS_ConstructData (size_t candidatesCapacity);
115 static void s_LBOS_DestroyData (SLBOS_Data* data);
116 static SSERV_Info* s_LBOS_GetNextInfo (SERV_ITER iter,
117 HOST_INFO* host_info);
118 static void s_LBOS_Initialize (void);
119 static int/*bool*/ s_LBOS_Feedback (SERV_ITER a,
120 double b,
121 int c);
122 static void s_LBOS_Close (SERV_ITER iter);
123 static void s_LBOS_Reset (SERV_ITER iter);
124 static int/*bool*/ s_LBOS_Update (SERV_ITER iter,
125 const char* text,
126 int code);
127 #if defined NCBI_OS_LINUX || defined NCBI_OS_MSWIN
128 static const char* s_LBOS_ReadLbosresolver(void);
129 #endif /* defined NCBI_OS_LINUX || defined NCBI_OS_MSWIN */
130 static
131 EHTTP_HeaderParse s_LBOS_ParseHeader (const char* header,
132 void* /* SLBOS_UserData* */
133 response,
134 int server_error);
135 /*LBOS is intended to answer in 0.5 sec, or is considered dead*/
136 static const STimeout kLBOSTimeout = {10, 500000};
137 static char* s_LBOS_Instance = NULL;
138 static char* s_LBOS_DTABLocal = NULL;
139
140 static
141 unsigned short s_LBOS_Announce(const char* service,
142 const char* version,
143 const char* host,
144 unsigned short port,
145 const char* healthcheck_url,
146 const char* meta,
147 char** lbos_answer,
148 char** http_status_message);
149
150
151 /*/////////////////////////////////////////////////////////////////////////////
152 // UNIT TESTING VIRTUAL FUNCTION TABLE //
153 /////////////////////////////////////////////////////////////////////////////*/
154 static SLBOS_Functions s_LBOS_funcs = {
155 s_LBOS_ResolveIPPort
156 ,CONN_Read
157 ,s_LBOS_FillCandidates
158 ,s_LBOS_DestroyData
159 ,s_LBOS_GetNextInfo
160 ,s_LBOS_Initialize
161 ,s_LBOS_UrlReadAll
162 ,s_LBOS_ParseHeader
163 ,SOCK_GetLocalHostAddress
164 ,s_LBOS_Announce
165 };
166
167
168 /** Get pointer to s_LBOS_MyPort */
169 ///////////////////////////////////////////////////////////////////////////////
170 // INTERNAL STORAGE OF ANNOUNCED SERVERS //
171 ///////////////////////////////////////////////////////////////////////////////
172 /** Add server to list of announced servers.
173 * @attention
174 * Intended to be used inside critical section, with caller having
175 * locked already, so does not lock. */
s_LBOS_FindAnnouncedServer(const char * service,const char * version,unsigned short port,const char * host)176 static int s_LBOS_FindAnnouncedServer(const char* service,
177 const char* version,
178 unsigned short port,
179 const char* host)
180 {
181 /* For convenience, we use some aliases. Not references, because this
182 * is not C++ */
183 struct SLBOS_AnnounceHandle_Tag** arr;
184 unsigned int* count;
185 int retval;
186 unsigned int i = 0;
187
188 arr = &s_LBOS_AnnouncedServers;
189 if (*arr == NULL)
190 return -1;
191 count = &s_LBOS_AnnouncedServersNum;
192 retval = -1;
193
194 /* Just iterate and compare */
195 for (i = 0; i < *count; i++) {
196 if (strcmp(service, (*arr)[i].service) == 0
197 &&
198 strcmp(version, (*arr)[i].version) == 0
199 &&
200 strcmp(host, (*arr)[i].host) == 0
201 &&
202 (*arr)[i].port == port)
203 {
204 retval = i;
205 }
206 }
207
208 return retval;
209 }
210
211
212 /** Remove server from list of announced servers.
213 * @attention
214 * Intended to be used inside critical section, with caller having
215 * locked already, so does not lock. */
216 static
s_LBOS_RemoveAnnouncedServer(const char * service,const char * version,unsigned short port,const char * host)217 int/*bool*/ s_LBOS_RemoveAnnouncedServer(const char* service,
218 const char* version,
219 unsigned short port,
220 const char* host)
221 {
222 int return_code;
223 struct SLBOS_AnnounceHandle_Tag** arr;
224 unsigned int* count;
225 int pos;
226
227
228 /* If server was announced using local IP, it should be deleted
229 * with IP "0.0.0.0" */
230 if (g_LBOS_StringIsNullOrEmpty(host)) {
231 host = "0.0.0.0"; /* we safely reassign, because caller should care
232 about memory leaks */
233 }
234 return_code = 1;
235 /* For convenience, we use some aliases. Not references, because this
236 * is not C++ */
237 arr = &s_LBOS_AnnouncedServers;
238 if (*arr == NULL) {
239 return_code = 0;
240 goto unlock_and_return;
241 }
242 count = &s_LBOS_AnnouncedServersNum;
243
244 /* Find node to delete*/
245 pos = s_LBOS_FindAnnouncedServer(service, version, port, host);
246 if (pos == -1) {/* no such service was announced */
247 return_code = 0;
248 goto unlock_and_return;
249 }
250
251 /* Erase node data */
252 free((*arr)[pos].version);
253 free((*arr)[pos].service);
254 free((*arr)[pos].host);
255
256 /* Move all nodes to fill erased position */
257 memmove(*arr + pos,
258 *arr + pos + 1,
259 sizeof(**arr) * (*count - pos -1 ));
260 (*count)--;
261 unlock_and_return:
262 assert(return_code == 0 || return_code == 1);
263 return return_code;
264 }
265
266
267 /** Add server to list of announced servers.
268 * @attention
269 * Intended to be used inside critical section, with caller having
270 * locked already, so does not lock. */
271 static
s_LBOS_AddAnnouncedServer(const char * service,const char * version,unsigned short port,const char * healthcheck_url)272 int/*bool*/ s_LBOS_AddAnnouncedServer(const char* service,
273 const char* version,
274 unsigned short port,
275 const char* healthcheck_url)
276 {
277 int return_code;
278 struct SLBOS_AnnounceHandle_Tag handle;
279 SConnNetInfo * healthcheck_info;
280 /* For convenience, we use some aliases. Not references, because this
281 * is not C++ */
282 struct SLBOS_AnnounceHandle_Tag** arr = &s_LBOS_AnnouncedServers;
283 unsigned int* count = &s_LBOS_AnnouncedServersNum;
284 unsigned int* alloc = &s_LBOS_AnnouncedServersAlloc;
285
286 /* First we create object without using static variables, and then lock
287 * critical section and copy object to static array. This will allow to
288 * to use most of multithreading */
289 return_code = 1;
290 /* extract host from healthcheck url */
291 healthcheck_info = ConnNetInfo_Clone(s_EmptyNetInfo);
292 healthcheck_info->host[0] = '\0'; /* to be sure that it will be
293 * overridden */
294 ConnNetInfo_ParseURL(healthcheck_info, healthcheck_url);
295
296 /* Create new element of list*/
297 handle.host = strdup(healthcheck_info->host);
298 handle.port = port;
299 handle.version = strdup(version);
300 handle.service = strdup(service);
301
302 /* We search for the same server being already announced */
303 s_LBOS_RemoveAnnouncedServer(service, version, port,
304 healthcheck_info->host);
305 ConnNetInfo_Destroy(healthcheck_info);
306
307 /* Allocate more space, if needed */
308 if (*arr == NULL || *count == *alloc)
309 {
310 int new_size = *alloc*2 + 1;
311 struct SLBOS_AnnounceHandle_Tag* realloc_result =
312 (struct SLBOS_AnnounceHandle_Tag*)realloc(*arr,
313 new_size * sizeof(struct SLBOS_AnnounceHandle_Tag));
314 if (realloc_result != NULL) {
315 *arr = realloc_result;
316 *alloc = new_size;
317 } else {
318 free(handle.version);
319 free(handle.service);
320 free(handle.host);
321 return_code = 0;
322 goto clear_and_exit;
323 }
324 }
325 assert(arr != NULL);
326 /* Copy handle to the end of array*/
327 (*count)++;
328 (*arr)[*count - 1] = handle;
329
330 clear_and_exit:
331 return return_code;
332 }
333
334
335 /*/////////////////////////////////////////////////////////////////////////////
336 // SERVICE MAPPING VIRTUAL FUNCTION TABLE //
337 /////////////////////////////////////////////////////////////////////////////*/
338 static const SSERV_VTable s_lbos_op = {
339 s_LBOS_GetNextInfo, /**< Use open()'ed iterator to get next found
340 * server*/
341 s_LBOS_Feedback, /**< Not used */
342 s_LBOS_Update, /**< Not used */
343 s_LBOS_Reset, /**< Free iterator's 'data' */
344 s_LBOS_Close, /**< Close iterator and free its 'data' */
345 "lbos" /**< name of mapper */
346 };
347
348
349 /*/////////////////////////////////////////////////////////////////////////////
350 // GLOBAL CONVENIENCE FUNCTIONS //
351 /////////////////////////////////////////////////////////////////////////////*/
352 /** Check C-string if it is not NULL and contains at least one symbol */
g_LBOS_StringIsNullOrEmpty(const char * str)353 int/*bool*/ g_LBOS_StringIsNullOrEmpty(const char* str)
354 {
355 if ((str == NULL) || (strlen(str) == 0)) {
356 return 1;
357 }
358 return 0;
359 }
360
361
362 /** Case-insensitive string lookup.
363 * @note
364 * Supposes that the string can be changed! No const modifier for dest
365 * @note
366 * If dest or lookup is NULL or empty, returns NULL */
g_LBOS_strcasestr(const char * dest,const char * lookup)367 const char* g_LBOS_strcasestr(const char* dest, const char* lookup)
368 {
369 char* dest_lwr;
370 char* lookup_lwr;
371 const char* result;
372
373 if (g_LBOS_StringIsNullOrEmpty(dest) || g_LBOS_StringIsNullOrEmpty(lookup))
374 {
375 return NULL;
376 }
377 dest_lwr = strlwr(strdup(dest));
378 lookup_lwr = strlwr(strdup(lookup));
379
380 result = strstr(dest_lwr, lookup_lwr);
381 /* Set the same offset, but in original string */
382 if (result != NULL) {
383 result = dest + (result - dest_lwr);
384 }
385
386 free(dest_lwr);
387 free(lookup_lwr);
388
389 return result;
390 }
391
392
393 /** Concatenate two C-strings and assign result to the left
394 * @param dest[in, out]
395 * Address of string to which you want to append another string.
396 * This address will be realloc()'ed and returned, so dest should point to
397 * heap and can be a result of function like strdup("my string").
398 * If it is NULL, a new string will be created.
399 * @param to_append[in]
400 * String that will be copied to the end of dest.
401 * It will not be changed. If it is NULL or empty, and dest is not NULL,
402 * dest will be returned. If dest is NULL, empty string will be returned.
403 * @param dest_length[in]
404 * Can be NULL. Optimizes performance on sequence of concatenations like
405 * g_LBOS_StringConcat(g_LBOS_StringConcat(str,to_app1,&len), to_app2,&len).
406 * Value of data_length on first call should be 0.
407 * @return
408 * reallocated dest on success, NULL if error happened and dest was
409 * not changed */
g_LBOS_StringConcat(char * dest,const char * to_append,size_t * dest_length)410 char* g_LBOS_StringConcat(char* dest,
411 const char* to_append,
412 size_t* dest_length)
413 {
414 char* realloc_result;
415 size_t dest_length_local = 0; /* not to handle if dest_length is NULL */
416 size_t append_len;
417
418 if (dest_length != NULL) {
419 dest_length_local = *dest_length;
420 }
421 if (dest == NULL) {
422 dest_length_local = 0;
423 } else if (dest_length_local == 0) {
424 dest_length_local = strlen(dest);
425 }
426 append_len = 0;
427 if (!g_LBOS_StringIsNullOrEmpty(to_append)) {
428 /* All done, in this case */
429 append_len = strlen(to_append);
430 }
431 realloc_result = (char*)realloc(dest, dest_length_local + append_len + 1);
432 if (realloc_result == NULL) {
433 CORE_LOG_X(eLBOS_MemAlloc, eLOG_Critical,
434 "g_LBOS_StringConcat: No RAM. Returning NULL.");
435 free(dest);
436 return NULL;
437 }
438 dest = realloc_result;
439 memcpy(dest + dest_length_local, to_append, append_len);
440 dest[dest_length_local + append_len] = '\0';
441 dest_length_local += append_len;
442 if (dest_length != NULL) {
443 *dest_length = dest_length_local;
444 }
445 return dest;
446 }
447
448
g_LBOS_StringNConcat(char * dest,const char * to_append,size_t * dest_length,size_t count)449 char* g_LBOS_StringNConcat(char* dest,
450 const char* to_append,
451 size_t* dest_length,
452 size_t count)
453 {
454 char* buf = (char*)malloc((count + 1) * sizeof(char));
455 char* result;
456
457 if (buf == NULL) {
458 CORE_LOG_X(eLBOS_MemAlloc, eLOG_Critical,
459 "g_LBOS_StringConcat: No RAM. Returning NULL.");
460 free(buf);
461 free(dest);
462 return NULL;
463 }
464 memcpy(buf, to_append, count);
465 buf[count] = '\0';
466 result = g_LBOS_StringConcat(dest, buf, dest_length);
467 free(buf);
468 return result;
469 }
470
471
472 /* Wrapper of CORE_REG_GET that eliminates the need to know the length of the
473 * parameter being read*/
g_LBOS_RegGet(const char * section,const char * name,const char * def_value)474 char* g_LBOS_RegGet(const char* section,
475 const char* name,
476 const char* def_value)
477 {
478 size_t totalBufSize = kMaxLineSize;
479 char* realloc_result = NULL;
480 char* buf = (char*)malloc(totalBufSize * sizeof(char));
481
482 if (buf == NULL) {
483 CORE_LOG_X(eLBOS_MemAlloc, eLOG_Critical,
484 "g_LBOS_RegGet: No RAM. Returning NULL.");
485 return buf;
486 }
487 for (;;) {
488 CORE_REG_GET(section, name, buf, totalBufSize, def_value);
489 /* If we had enough space allocated */
490 if (strlen(buf) < totalBufSize-1) {
491 break;
492 }
493 /* If we (possibly) did not have enough space allocated
494 then add space to buffer */
495 realloc_result = (char*)realloc(buf, sizeof(char)*(totalBufSize * 2));
496 if (realloc_result == NULL) {
497 CORE_LOG_X(eLBOS_MemAlloc, eLOG_Warning,
498 "g_LBOS_RegGet: Buffer overflow while reading from "
499 "registry. Returning string at its maximum size");
500 return buf;
501 } else {
502 buf = realloc_result;
503 totalBufSize *= 2;
504 }
505 }
506 return buf;
507 }
508
509
510 /** Checks iterator, fact that iterator belongs to this mapper, iterator data.
511 * Only debug function. */
g_LBOS_CheckIterator(SERV_ITER iter,ELBOSIteratorCheckType should_have_data)512 int/*bool*/ g_LBOS_CheckIterator(SERV_ITER iter,
513 ELBOSIteratorCheckType should_have_data)
514 {
515 assert(iter != NULL); /* we can do nothing if this happens */
516 if (should_have_data == ELBOSIteratorCheckType_MustHaveData) {
517 if (iter->data == NULL) {
518 return 0;
519 }
520 assert(((SLBOS_Data*)iter->data)->a_cand >=
521 ((SLBOS_Data*)iter->data)->n_cand);
522 assert(((SLBOS_Data*)iter->data)->pos_cand <=
523 ((SLBOS_Data*)iter->data)->n_cand);
524 }
525 if (should_have_data == ELBOSIteratorCheckType_DataMustBeNULL
526 && iter->data != NULL) {
527 return 0;
528 }
529 if (strcmp(iter->op->mapper, s_lbos_op.mapper) != 0) {
530 return 0;
531 }
532 return 1;
533 }
534
535
536 /** This function is needed to get LBOS hostname in different situations.
537 * @param priority_find_method[in]
538 * The first method to try to find LBOS. If it fails, default order of
539 * methods will be used to find LBOS.
540 * Default order is:
541 * 1) registry value of [CONN]->lbos;
542 * 2) 127.0.0.1:8080;
543 * 3) LBOS for current /etc/ncbi/{role, domain}.
544 * To not specify default method, use eLBOSFindMethod_None.
545 * @param lbos_addr[in]
546 * If priority_find_method is eLBOS_FindMethod_CustomHost, then LBOS is
547 * first looked for at hostname:port specified in this variable. */
g_LBOS_GetLBOSAddressEx(ELBOSFindMethod priority_find_method,const char * lbos_addr)548 char* g_LBOS_GetLBOSAddressEx (ELBOSFindMethod priority_find_method,
549 const char* lbos_addr)
550 {
551 #if defined NCBI_OS_LINUX || defined NCBI_OS_MSWIN
552 const char* lbosaddress = NULL; /* for const strings */
553 #endif /* defined NCBI_OS_LINUX || defined NCBI_OS_MSWIN */
554 char* lbosaddress_temp = NULL; /* for non-const strings */
555 char* address = NULL;
556 /* List of methods used, in their order */
557 ELBOSFindMethod find_method_order[] = {
558 priority_find_method /* eLBOSFindMethod_None, if not specified*/
559 , eLBOS_FindMethod_Registry
560 , eLBOS_FindMethod_Lbosresolve
561 };
562 size_t find_method_iter;
563 size_t find_method_count =
564 sizeof(find_method_order) / sizeof(ELBOSFindMethod);
565 CORE_LOG(eLOG_Trace, "Getting LBOS addresses...");
566 /* Iterate through methods of finding LBOS address */
567 for (find_method_iter = 0;
568 find_method_iter < find_method_count;
569 ++find_method_iter)
570 {
571 /* If LBOS address has been filled - we're done */
572 if (address != NULL)
573 break;
574 switch (find_method_order[find_method_iter]) {
575 case eLBOSFindMethod_None :
576 break;
577 case eLBOS_FindMethod_CustomHost :
578 if (g_LBOS_StringIsNullOrEmpty(lbos_addr)) {
579 CORE_LOG_X(1, eLOG_Warning, "Use of custom LBOS address was "
580 "asked for, but no custom address was supplied. "
581 "Using default LBOS.");
582 break;
583 }
584 address = strdup(lbos_addr);
585 if (address == NULL) {
586 CORE_LOG_X(1, eLOG_Warning, "Did not manage to copy custom "
587 "LBOS address. Probably insufficient RAM.");
588 }
589 break;
590 case eLBOS_FindMethod_Lbosresolve :
591 #if defined NCBI_OS_LINUX || defined NCBI_OS_MSWIN
592 lbosaddress = s_LBOS_ReadLbosresolver();
593 if (g_LBOS_StringIsNullOrEmpty(lbosaddress)) {
594 CORE_LOG_X(1, eLOG_Warning, "Trying to find LBOS using "
595 "/etc/ncbi/lbosresolve failed");
596 } else {
597 address = strdup(lbosaddress);
598 }
599 #endif /* defined NCBI_OS_LINUX || defined NCBI_OS_MSWIN */
600 break;
601 case eLBOS_FindMethod_Registry:
602 lbosaddress_temp = g_LBOS_RegGet("CONN", "lbos", NULL);
603 if (g_LBOS_StringIsNullOrEmpty(lbosaddress_temp)) {
604 CORE_LOG_X(1, eLOG_Warning, "Trying to find LBOS in "
605 "registry [CONN]lbos failed. "
606 "Using address in "
607 "/etc/ncbi/lbosresolver");
608 free(lbosaddress_temp); /* just in case */
609 lbosaddress_temp = NULL;
610 break;
611 }
612 address = lbosaddress_temp;
613 break;
614 }
615 }
616 return address;
617 }
618
619
620 /* Get possible addresses of LBOS in default order */
g_LBOS_GetLBOSAddress(void)621 char* g_LBOS_GetLBOSAddress(void)
622 {
623 return g_LBOS_GetLBOSAddressEx(eLBOSFindMethod_None, NULL);
624 }
625
626
627 /*/////////////////////////////////////////////////////////////////////////////
628 // STATIC CONVENIENCE FUNCTIONS //
629 /////////////////////////////////////////////////////////////////////////////*/
630 #if 0 /* deprecated, remove on or after July 9 2016 */
631 /** Get role of current host. Returned string is read-only, may reside in
632 * 'data' memory area */
633 static const char* s_LBOS_ReadRole()
634 {
635 /*
636 * If no role previously read, fill it. Of course, not on MSWIN
637 */
638 #ifdef NCBI_OS_LINUX
639 if (s_LBOS_CurrentRole == NULL) {
640 size_t len;
641 char str[kMaxLineSize];
642 char* read_result; /* during function will become equal either NULL
643 or str, do not free() */
644 FILE* role_file;
645 char *role; /* will be used to set s_LBOS_CurrentRole, do not free() */
646 if ((role_file = fopen(kRoleFile, "r")) == NULL) {
647 /* No role recognized */
648 CORE_LOGF(eLOG_Warning, ("s_LBOS_ReadRole: "
649 "could not open role file %s",
650 kRoleFile));
651 return NULL;
652 }
653 read_result = fgets(str, sizeof(str), role_file);
654 fclose(role_file);
655 if (read_result == NULL) {
656 CORE_LOG(eLOG_Warning, "s_LBOS_ReadRole: "
657 "memory allocation failed");
658 return NULL;
659 }
660 len = strlen(str);
661 assert(len);
662 /*We remove unnecessary '/n' and probably '/r' */
663 if (str[len - 1] == '\n') {
664 if (--len && str[len - 1] == '\r')
665 --len;
666 str[len] = '\0';
667 }
668 if (strncasecmp(str, "try", 3) == 0)
669 role = "try";
670 else {
671 if (strncasecmp(str, "qa", 2) == 0)
672 role = "qa";
673 else {
674 if (strncasecmp(str, "dev", 3) == 0)
675 role = "dev";
676 else {
677 if (strncasecmp(str, "prod", 4) == 0)
678 role = "prod";
679 else {
680 /* No role recognized */
681 CORE_LOGF(eLOG_Warning,
682 ("s_LBOS_ComposeLBOSAddress"
683 ": could not recognize role [%s] in %s",
684 str, kRoleFile));
685 return NULL;
686 }
687 }
688 }
689 }
690 CORE_LOCK_WRITE;
691 /* Check one more time that no other thread managed to fill
692 * static variable ahead of this thread. If this happened,
693 * release memory */
694 if (s_LBOS_CurrentRole == NULL)
695 s_LBOS_CurrentRole = strdup(role);
696 CORE_UNLOCK;
697 }
698 #endif /* #ifdef NCBI_OS_LINUX */
699 return s_LBOS_CurrentRole;
700 }
701
702 #endif /* 0 */
703
704 #if defined NCBI_OS_LINUX || defined NCBI_OS_MSWIN
705 /** Read contents of lbosresolver
706 *
707 * @warning
708 * Do not modify or clear the returned string! Returned string is
709 * read-only
710 */
s_LBOS_ReadLbosresolver(void)711 static const char* s_LBOS_ReadLbosresolver(void)
712 {
713 if (s_LBOS_Lbosresolver != NULL) {
714 return s_LBOS_Lbosresolver;
715 }
716
717 FILE* lbosresolver_file;
718 size_t len;
719 char str[kMaxLineSize];
720 char* read_result; /* during function will become equal either NULL
721 or str, do not free() */
722 if ((lbosresolver_file = fopen(kLbosresolverFile, "r")) == NULL) {
723 CORE_LOGF(eLOG_Warning, ("LBOS mapper: "
724 "could not open file %s",
725 kLbosresolverFile));
726 return NULL;
727 }
728 read_result = fgets(str, sizeof(str), lbosresolver_file);
729 fclose(lbosresolver_file);
730 if (read_result == NULL) {
731 CORE_LOG(eLOG_Warning, "s_LBOS_ReadLBOSResolve: "
732 "memory allocation failed");
733 return NULL;
734 }
735 len = strlen(str);
736 assert(len);
737 if (g_LBOS_StringIsNullOrEmpty(str)) {
738 /* No domain recognized */
739 CORE_LOGF(eLOG_Warning,
740 ("LBOS mapper: file %s is empty, no LBOS address available",
741 kLbosresolverFile));
742 free(read_result);
743 return NULL;
744 }
745 /*We remove unnecessary '/n' and probably '/r' */
746 if (str[len - 1] == '\n') {
747 if (--len && str[len - 1] == '\r') {
748 --len;
749 }
750 str[len] = '\0';
751 }
752 CORE_LOCK_WRITE;
753 /* Check one more time that no other thread managed to fill
754 * static variable ahead of this thread. If this happened,
755 * release memory */
756 if (s_LBOS_Lbosresolver == NULL)
757 /* We skip "http://" and "/lbos" */
758 str[strlen(str) - strlen("/lbos")] = '\0';
759 s_LBOS_Lbosresolver = strdup(str + 7);
760 CORE_UNLOCK;
761
762 return s_LBOS_Lbosresolver;
763 }
764 #endif /* defined NCBI_OS_LINUX || defined NCBI_OS_MSWIN */
765
766
767 /** Take original string and return URL-encoded string.
768 * @attention
769 * Original string is untouched. Caller is responsible for freeing
770 * allocated space.
771 */
s_LBOS_URLEncode(const char * to_encode)772 static char* s_LBOS_URLEncode (const char* to_encode)
773 {
774 /* If all symbols are escape, our string will take triple space */
775 size_t encoded_string_buf_size = strlen(to_encode)*3 + 1;
776 char* encoded_string = (char*)calloc(encoded_string_buf_size,
777 sizeof(char));
778 size_t src_read, dst_written; /* strange things needed by URL_Encode */
779 URL_Encode(to_encode, strlen(to_encode), &src_read,
780 encoded_string, encoded_string_buf_size, &dst_written);
781 return encoded_string;
782 }
783
784
785 /** Function to convert legacy service names for LBOS.
786 * Takes service name and if it does not start with '/', prepends "/Legacy"
787 * to it.
788 * @attention
789 * Original string is untouched. Caller is responsible for freeing
790 * allocated space.
791 * @warning
792 * to_modify MUST be a valid non-empty C-string
793 */
s_LBOS_ModifyServiceName(const char * to_modify)794 char* s_LBOS_ModifyServiceName (const char* to_modify)
795 {
796 /* If all symbols are escape, our string will take triple space */
797 static const char* prefix = "/Legacy/";
798 if (to_modify[0] == '/')
799 return strdup(to_modify);
800 /* We deal with legacy service name. First, get "/Legacy/" prefix, then
801 * change to_modify to lower register, concatenate them and return */
802 char* modified_str = strdup(prefix);
803 char* service_lwr = strlwr(strdup(to_modify));
804 modified_str = g_LBOS_StringConcat(modified_str, service_lwr, NULL);
805 free(service_lwr);
806 return modified_str;
807 }
808
809
810 /** @brief Just connect and return connection
811 *
812 * Internal function to create connection, which simplifies process
813 * by ignoring many settings and making them default
814 */
s_LBOS_ConnectURL(SConnNetInfo * net_info,const char * url,SLBOS_UserData * user_data)815 static CONN s_LBOS_ConnectURL(SConnNetInfo* net_info, const char* url,
816 SLBOS_UserData* user_data)
817 {
818 THTTP_Flags flags = fHTTP_AutoReconnect | fHTTP_Flushable;
819 CONN conn;
820 CONNECTOR connector;
821
822 CORE_LOGF(eLOG_Note, ("Parsing URL \"%s\"", url));
823 if (!ConnNetInfo_ParseURL(net_info, url)) {
824 CORE_LOG(eLOG_Warning, "Cannot parse URL");
825 return NULL;
826 }
827 CORE_LOGF(eLOG_Note, ("Creating HTTP%s connector",
828 &"S"[net_info->scheme != eURL_Https]));
829 if (!(connector = HTTP_CreateConnectorEx(net_info, flags,
830 g_LBOS_UnitTesting_GetLBOSFuncs()->ParseHeader,
831 user_data/*data*/, 0,
832 0/*cleanup*/)))
833 {
834 CORE_LOG(eLOG_Warning, "Cannot create HTTP connector");
835 return NULL;
836 }
837 CORE_LOG(eLOG_Note, "Creating connection");
838 if (CONN_Create(connector, &conn) != eIO_Success) {
839 CORE_LOG(eLOG_Warning, "Cannot create connection");
840 return NULL;
841 }
842 /*
843 * We define very little timeout so that we can quickly iterate
844 * through LBOSes (they should answer in no more than 0.5 second)
845 */
846 CONN_SetTimeout(conn, eIO_Open, &kLBOSTimeout);
847 CONN_SetTimeout(conn, eIO_ReadWrite, &kLBOSTimeout);
848 return conn;
849 }
850
851
852 /** If you are sure that all answer will fit in one char*, use this function
853 * to read all input. Must be free()'d by the caller.
854 */
s_LBOS_UrlReadAll(SConnNetInfo * net_info,const char * url,int * status_code,char ** status_message)855 static char * s_LBOS_UrlReadAll(SConnNetInfo* net_info,
856 const char* url,
857 int* status_code,
858 char** status_message)
859 {
860 SLBOS_UserData user_data; /* used to store HTTP response code and
861 HTTP header, HTTP header only for unit
862 testing */
863 /* Status of reading*/
864 EIO_Status status = eIO_Timeout;
865 /* how much how been read to the moment*/
866 size_t totalRead = 0;
867 /* how much we suppose one line takes*/
868 size_t oneLineSize = kMaxLineSize;
869 /* how much bytes was read in one turn*/
870 size_t bytesRead = 0;
871 /* Connection to LBOS. We do not know if LBOS really exists at url */
872 CONN conn;
873 char* buf;
874 size_t totalBufSize;
875 char* realloc_result;
876
877 /* Not to handle case when 'status_code' is NULL, we use internal variable,
878 * and only try to set 'status_code' in the end of this function (and if it
879 * is NULL, we do not set it) */
880 user_data.http_response_code = 0;
881 /* The same for status_message */
882 user_data.http_status_mesage = NULL;
883 net_info->max_try = 1; /* we do not need to try more than once */
884 /* Set HTTP header for unit testing. Pointer to already existing string. */
885 user_data.header = net_info->http_user_header;
886 /* Set content length to the maximum possible value */
887 user_data.content_length = (size_t)-1;
888 conn = s_LBOS_ConnectURL(net_info, url, &user_data);
889 if (conn == NULL) {
890 return NULL;
891 }
892 CONN_SetUserData(conn, &user_data);
893 buf = (char*)calloc(oneLineSize, sizeof(char));
894 /* Total length of buffer. We already have oneLineSize because of calloc */
895 totalBufSize = oneLineSize;
896 if (buf == NULL)
897 {
898 CORE_LOG(eLOG_Critical, "s_LBOS_UrlReadAll: No RAM. "
899 "Returning NULL.");
900 CONN_Close(conn);
901 return NULL;
902 }
903 do {
904 /* If there is no LBOS, we will know about it here */
905 status = g_LBOS_UnitTesting_GetLBOSFuncs()->
906 Read(conn, buf + strlen(buf),
907 totalBufSize - totalRead - 1 /* for \0 */,
908 &bytesRead, eIO_ReadPlain);
909 if (user_data.http_response_code != 200) {
910 CORE_LOGF(eLOG_Critical, ("s_LBOS_UrlReadAll: LBOS returned "
911 "status code %d",
912 user_data.http_response_code));
913 }
914 if (status_code != NULL) {
915 *status_code = user_data.http_response_code;
916 }
917 if (status_message != NULL && user_data.http_status_mesage != NULL) {
918 *status_message = strdup(user_data.http_status_mesage);
919 }
920 free(user_data.http_status_mesage); /* not needed anymore*/
921 user_data.http_status_mesage = NULL;
922 /* If could not connect, fail now */
923 if (user_data.http_response_code == 0) {
924 free(buf);
925 CONN_Close(conn);
926 return NULL;
927 }
928 totalRead += bytesRead;
929
930 buf[totalRead] = 0; /* force end of string */
931
932 /* IF we still have to read - then add space to buffer, if needed */
933 if ( status == eIO_Success && totalBufSize < totalRead * 2 )
934 {
935 realloc_result = (char*)realloc(buf,
936 sizeof(char) * (totalBufSize * 2));
937 if (realloc_result == NULL) {
938 CORE_LOG(eLOG_Warning, "s_LBOS_UrlReadAll: Buffer "
939 "overflow. Returning string at its "
940 "maximum size");
941 return buf;
942 } else {
943 buf = realloc_result;
944 totalBufSize *= 2;
945 }
946 }
947 } while (status == eIO_Success);
948 /*In the end we shrink buffer to the minimal needed size*/
949 if ( !(realloc_result = (char*) realloc(buf,
950 sizeof(char) * (strlen(buf) + 1))) )
951 {
952 CORE_LOG(eLOG_Warning, "s_LBOS_UrlReadAll: Buffer shrink error, using "
953 "original stirng");
954 } else {
955 buf = realloc_result;
956 }
957 CONN_Close(conn);
958 return buf;
959 }
960
961
962 /** Resolve service name to one of the hosts implementing service.
963 * Uses LBZK at specified IP and port.
964 */
s_LBOS_ResolveIPPort(const char * lbos_address,const char * service_name,SConnNetInfo * net_info)965 static SSERV_Info** s_LBOS_ResolveIPPort(const char* lbos_address,
966 const char* service_name,
967 SConnNetInfo* net_info)
968 {
969 SSERV_Info** infos;
970 size_t infos_count;
971 size_t infos_capacity;
972 char* servicename_url_encoded = NULL;
973 size_t url_length;
974 char * url;
975 char * lbos_answer; /*to write down LBOS's answer*/
976 const char* user_dtab = NULL;
977 size_t length;
978 size_t user_dtab_length;
979 char* new_dtab = NULL;
980 char* user_dtab_end;
981 /* Allocate space for answer (will be expanded later, if needed) */
982 infos = (SSERV_Info**)calloc(2, sizeof(SSERV_Info*));
983 if (infos == NULL) {
984 CORE_LOG(eLOG_Critical, "s_LBOS_ResolveIPPort: No RAM. "
985 "Returning NULL.");
986 return NULL;
987 }
988 infos_count = 0;
989 infos_capacity = 1;
990 /* Update HTTP Header with local DTabs from registry and revert after we
991 * finished reading from URL */
992 /* First, we look if there is DTab-Local already in header*/
993 char* old_header = net_info->http_user_header ?
994 strdup(net_info->http_user_header) : NULL;
995 user_dtab = g_LBOS_strcasestr(net_info->http_user_header, "DTab-local:");
996 /* If there is an already defined local DTab, we mix it with one from
997 * registry */
998 if (user_dtab != NULL)
999 {
1000 /* Move start after name of tag*/
1001 user_dtab += strlen("DTab-Local:");
1002 if (user_dtab[1] == ' ') {
1003 user_dtab++;
1004 }
1005 /* Find end of line */
1006 user_dtab_end = strchr(user_dtab, '\n');
1007 if (user_dtab_end[-1] == '\r') {
1008 user_dtab_end--;
1009 }
1010 /* Create new string that includes first DTabs from registry and then
1011 * DTabs from HTTP requests */
1012 length = 0;
1013 user_dtab_length = user_dtab_end - user_dtab;
1014 new_dtab = NULL;
1015 new_dtab = g_LBOS_StringNConcat(g_LBOS_StringConcat(
1016 g_LBOS_StringConcat(g_LBOS_StringConcat(
1017 /*dest*/ /*to append*/ /*length*/ /*count*/
1018 new_dtab, "DTab-local: ", &length),
1019 s_LBOS_DTABLocal, &length),
1020 ";", &length),
1021 user_dtab, &length, user_dtab_length);
1022 ConnNetInfo_OverrideUserHeader(net_info, new_dtab);
1023 free(new_dtab);
1024 } else {
1025 /* Set default Dtab from registry */
1026 size_t length = 0;
1027 char* new_dtab = NULL;
1028 new_dtab = g_LBOS_StringConcat(g_LBOS_StringConcat(
1029 /*dest*/ /*to append*/ /*length*/ /*count*/
1030 new_dtab, "DTab-local: ", &length),
1031 s_LBOS_DTABLocal, &length);
1032 ConnNetInfo_OverrideUserHeader(net_info, new_dtab);
1033 free(new_dtab);
1034 }
1035 servicename_url_encoded = s_LBOS_ModifyServiceName(service_name);
1036 /*encode service name to url encoding (change ' ' to %20, '/' to %2f, etc.)*/
1037 url_length = strlen("http://") + strlen(lbos_address) +
1038 strlen(kLBOSQuery) + strlen(servicename_url_encoded);
1039 url = (char*)malloc(sizeof(char) * url_length + 1); /** to make up
1040 LBOS query URI to connect*/
1041 if (url == NULL)
1042 {
1043 CORE_LOG(eLOG_Critical, "s_LBOS_ResolveIPPort: No RAM. "
1044 "Returning NULL.");
1045 free(infos);
1046 free(old_header);
1047 free(servicename_url_encoded);
1048 return NULL;
1049 }
1050 sprintf(url, "%s%s%s%s", "http://", lbos_address, kLBOSQuery,
1051 servicename_url_encoded);
1052 lbos_answer = s_LBOS_UrlReadAll(net_info, url, NULL, NULL);
1053 /* Revert header */
1054 ConnNetInfo_OverrideUserHeader(net_info, old_header);
1055 free(old_header);
1056 free(url);
1057 free(servicename_url_encoded);
1058 /* If no connection */
1059 if (lbos_answer == NULL) {
1060 free(infos);
1061 return NULL;
1062 }
1063 x_JSON_Value *root_value;
1064 x_JSON_Object *root_obj;
1065 x_JSON_Object *services;
1066 x_JSON_Array *serviceEndpoints;
1067 x_JSON_Object *serviceEndpoint;
1068 unsigned int j = 0;
1069 root_value = x_json_parse_string(lbos_answer);
1070 if (x_json_value_get_type(root_value) != JSONObject) {
1071 goto clean_and_exit;
1072 }
1073 root_obj = x_json_value_get_object(root_value);
1074 services = x_json_object_get_object(root_obj, "services");
1075 /* Get endpoints for the first service name.
1076 * Note: Multiple service resolution is not supported intentionally
1077 * (yet). */
1078 serviceEndpoints =
1079 x_json_object_get_array(services, x_json_object_get_name(services, 0));
1080 /* Iterate through endpoints */
1081 for (j = 0; j < x_json_array_get_count(serviceEndpoints); j++) {
1082 const char *host, *rate, *extra, *type;
1083 char* server_description;
1084 const char* descr_format = "%s %s:%u %s Regular R=%s L=no T=25";
1085 int port;
1086 serviceEndpoint = x_json_array_get_object(serviceEndpoints, j);
1087 host = x_json_object_dotget_string(serviceEndpoint,
1088 "serviceEndpoint.host");
1089 if (host == NULL) {
1090 continue;
1091 }
1092 port = (int)x_json_object_dotget_number(serviceEndpoint,
1093 "serviceEndpoint.port");
1094 /* -------------rate------------- */
1095 rate = x_json_object_dotget_string(serviceEndpoint,
1096 "meta.rate");
1097 rate = !g_LBOS_StringIsNullOrEmpty(rate) ? rate : "1";
1098 /* -------------type------------- */
1099 type = x_json_object_dotget_string(serviceEndpoint,
1100 "meta.type");
1101 type = !g_LBOS_StringIsNullOrEmpty(type) ? type : "STANDALONE";
1102 /* -------------extra------------- */
1103 extra = x_json_object_dotget_string(serviceEndpoint,
1104 "meta.extra");
1105 extra = !g_LBOS_StringIsNullOrEmpty(extra) ? extra : "";
1106
1107 /* Examples:
1108 HTTP - accn2gi
1109 DNS - alndbasn_lb
1110 STANDALONE - aligndb_dbldd
1111 HTTP_POST - taxservice3test
1112 NCBID - taxservice/mapviewaugust2011*/
1113 size_t length; /* used to count size to allocate for server_description,
1114 and then for g_LBOS_StringConcat*/
1115 /* Occasionally, we are not able to allocate memory */
1116 length = strlen(descr_format) + strlen(type) + strlen(host) +
1117 5 /*length of port*/ + strlen(extra) + strlen(rate);
1118 server_description = malloc(sizeof(char) * length);
1119 sprintf(server_description, descr_format, type, host,
1120 port, extra, rate);
1121 SSERV_Info * info = SERV_ReadInfoEx(server_description,service_name, 0);
1122 free(server_description);
1123 if (info == NULL) {
1124 continue;
1125 }
1126 if (infos_capacity <= infos_count + 1) {
1127 SSERV_Info** realloc_result =
1128 (SSERV_Info**)realloc(infos, sizeof(SSERV_Info*) *
1129 (infos_capacity*2 + 1));
1130 if (realloc_result == NULL) {
1131 /* If error with realloc, return as much as could
1132 * allocate for */
1133 infos_count--; /* Will just rewrite last info with NULL
1134 * to mark end of array*/
1135 free(info);
1136 break;
1137 } else { /* If realloc successful */
1138 infos = realloc_result;
1139 infos_capacity = infos_capacity*2 + 1;
1140 }
1141 }
1142 infos[infos_count++] = info;
1143 }
1144
1145 clean_and_exit:
1146 x_json_value_free(root_value);
1147 free(lbos_answer);
1148 /* Shuffle list with Durstenfeld's shuffle algorithm
1149 * (also credits go to Fisher and Yates, and Knuth) */
1150 if (infos_count > 1) {
1151 size_t i;
1152 for (i = 0; i < infos_count - 1; i++) {
1153 size_t j = i + ( rand() % (infos_count - i) );
1154 if (i == j) continue; /* not swapping the item with itself */
1155 SSERV_Info* t = infos[j];
1156 infos[j] = infos[i];
1157 infos[i] = t;
1158 }
1159 }
1160 /* Set last element this NULL, finalizing the array ...*/
1161 infos[infos_count] = NULL;
1162
1163 return infos;
1164 }
1165
1166
1167 /** Given an empty iterator which has service name in its "name" field
1168 * we fill it with all servers which were found by asking LBOS
1169 * @param[in,out] iter
1170 * We need name from it, then set "data" field with all servers. All
1171 * previous "data" will be overwritten, causing possible memory leak.
1172 */
s_LBOS_FillCandidates(SLBOS_Data * data,const char * service)1173 static void s_LBOS_FillCandidates(SLBOS_Data* data, const char* service)
1174 {
1175 unsigned int i;
1176 SSERV_Info** hostports_array = 0;
1177 char* lbos_address = NULL; /* We copy LBOS address to */
1178
1179 /* We suppose that number of addresses is constant (and so
1180 is position of NULL), so no mutex is necessary */
1181 if (s_LBOS_Instance == NULL) return;
1182 lbos_address = s_LBOS_Instance;
1183 CORE_LOGF_X(1, eLOG_Trace, ("Trying to find servers of \"%s\" with "
1184 "LBOS at %s", service, lbos_address));
1185 hostports_array =
1186 g_LBOS_UnitTesting_GetLBOSFuncs()->ResolveIPPort(lbos_address, service,
1187 data->net_info);
1188 if (hostports_array == NULL) {
1189 CORE_LOGF_X(1, eLOG_Trace, ("Ho servers of \"%s\" found by LBOS",
1190 service));
1191 return;
1192 }
1193 for (i = 0; hostports_array[i] != NULL; i++) continue;
1194 CORE_LOGF_X(1, eLOG_Trace, ("Found %u servers of \"%s\" with "
1195 "LBOS at %s", i, service, lbos_address));
1196 /* If we received answer from LBOS, we fill candidates */
1197 if (hostports_array != NULL) {
1198 SLBOS_Candidate* realloc_result;
1199 /* To allocate space once and forever, let's quickly find the number of
1200 * received addresses...
1201 */
1202 for (i = 0; hostports_array[i] != NULL; ++i) continue;
1203 /* ...and allocate space */
1204 realloc_result = (SLBOS_Candidate*)realloc(data->cand,
1205 sizeof(SLBOS_Candidate) * (i + 1));
1206 if (realloc_result == NULL) {
1207 CORE_LOGF_X(1, eLOG_Warning,
1208 ("s_LBOS_FillCandidates: Could not "
1209 "allocate space for all candidates, "
1210 "will use as much as was allocated "
1211 "initially: %du",
1212 (unsigned int)data->a_cand));
1213 } else {
1214 data->cand = realloc_result;
1215 data->a_cand = i + 1;
1216 }
1217 for (i = 0; hostports_array[i] != NULL && i < data->a_cand; i++) {
1218 data->cand[i].info = hostports_array[i];
1219 data->n_cand++;
1220 }
1221 free(hostports_array);
1222 }
1223 /*If we did not find answer from LBOS, we just finish*/
1224 }
1225
1226
1227
s_LBOS_CheckAnnounceArgs(const char * service,const char * version,const char * host,unsigned short port,const char * healthcheck_url,char ** lbos_answer)1228 static int s_LBOS_CheckAnnounceArgs(const char* service,
1229 const char* version,
1230 const char* host,
1231 unsigned short port,
1232 const char* healthcheck_url,
1233 char** lbos_answer)
1234 {
1235 unsigned short i;
1236 if (g_LBOS_StringIsNullOrEmpty(healthcheck_url)) {
1237 CORE_LOG(eLOG_Critical, "Error with announcement, "
1238 "no healthcheck_url specified.");
1239 return 0;
1240 }
1241 if ((strstr(healthcheck_url, "http://") != healthcheck_url)
1242 &&
1243 (strstr(healthcheck_url, "https://") != healthcheck_url)) {
1244 CORE_LOG(eLOG_Critical, "Error with announcement, missing http:// or "
1245 "https:// in the beginning of healthcheck "
1246 "URL.");
1247 return 0;
1248 }
1249 /* Greedy check for host - host should consist of alphanumerics and dots,
1250 * or be NULL */
1251 if (!g_LBOS_StringIsNullOrEmpty(host)) {
1252 for (i = 0; i < strlen(host); i++) {
1253 if (!isalnum(host[i]) && (host[i] != '.')) {
1254 CORE_LOG(eLOG_Critical, "Error with announcement, "
1255 "ip has incorrect format "
1256 "(only digits and dots are allowed). "
1257 "Please provide resolved IP to "
1258 "avoid this error");
1259 return 0;
1260 }
1261 }
1262 }
1263 if (port < 1 || port > 65535) {
1264 CORE_LOG(eLOG_Critical, "Error with announcement, incorrect port.");
1265 return 0;
1266 }
1267 if (g_LBOS_StringIsNullOrEmpty(version)) {
1268 CORE_LOG(eLOG_Critical, "Error with announcement, "
1269 "no version specified.");
1270 return 0;
1271 }
1272 if (g_LBOS_StringIsNullOrEmpty(service)) {
1273 CORE_LOG(eLOG_Critical, "Error with announcement, "
1274 "no service name specified.");
1275 return 0;
1276 }
1277 if (lbos_answer == NULL) {
1278 CORE_LOG(eLOG_Critical, "Error with announcement, "
1279 "no variable provided to save LBOS answer.");
1280 return 0;
1281 }
1282 return 1;
1283 }
1284
1285
s_LBOS_CheckDeannounceArgs(const char * service,const char * version,const char * host,unsigned short port)1286 static int s_LBOS_CheckDeannounceArgs(const char* service,
1287 const char* version,
1288 const char* host,
1289 unsigned short port)
1290 {
1291 if (!g_LBOS_StringIsNullOrEmpty(host) && strstr(host, ":") != NULL) {
1292 CORE_LOG(eLOG_Critical, "Invalid argument passed for de-announcement, "
1293 "please check that \"host\" parameter does "
1294 "not contain protocol or port");
1295 return 0;
1296 }
1297 if (port < 1 || port > 65535) {
1298 CORE_LOG(eLOG_Critical, "Invalid argument passed for de-announcement, "
1299 "incorrect port.");
1300 return 0;
1301 }
1302 if (g_LBOS_StringIsNullOrEmpty(version)) {
1303 CORE_LOG(eLOG_Critical, "Invalid argument passed for de-announcement, "
1304 "no version specified.");
1305 return 0;
1306 }
1307 if (g_LBOS_StringIsNullOrEmpty(service)) {
1308 CORE_LOG(eLOG_Critical, "Invalid argument passed for de-announcement, "
1309 "no service name specified.");
1310 return 0;
1311 }
1312 return 1;
1313 }
1314
1315
1316 /** Iterator request through LBOSes and return the best answer (ideally, one
1317 * that has status code 200)
1318 * This function is TODO
1319 */
s_LBOS_PerformRequest(const char * request,char ** lbos_answer,char ** http_status_message,TReqMethod req_method,const char * service)1320 static unsigned short s_LBOS_PerformRequest(const char* request,
1321 char** lbos_answer,
1322 char** http_status_message,
1323 TReqMethod req_method,
1324 const char* service)
1325 {
1326 SConnNetInfo* net_info;
1327 char* buf;
1328 char* lbos_address;
1329 int status_code;
1330 char* query;
1331 char* status_message = NULL;
1332 size_t length = 0;
1333 /* We try all LBOSes until we get 200. If we receive error status code
1334 * instead of 200, we save it. So, if no LBOS returns 200, we
1335 * return error code. If we receive nothing (LBOS is not present), then
1336 * we save nothing.
1337 */
1338 net_info = ConnNetInfo_Clone(s_EmptyNetInfo);
1339 net_info->req_method = req_method;
1340 buf = NULL;
1341 status_code = 0;
1342 lbos_address = s_LBOS_Instance;
1343 query = g_LBOS_StringConcat(g_LBOS_StringConcat(
1344 strdup("http://"), lbos_address, &length),
1345 request, &length);
1346 length = strlen(query);
1347 buf = s_LBOS_UrlReadAll(net_info, query, &status_code,
1348 &status_message);
1349 free(query);
1350 if (lbos_answer != NULL && !g_LBOS_StringIsNullOrEmpty(buf)) {
1351 *lbos_answer = strdup(buf);
1352 }
1353 free(buf);
1354 if (http_status_message != NULL && status_message != NULL) {
1355 *http_status_message = strdup(status_message);
1356 }
1357 free(status_message);
1358
1359 if (status_code == 0) {
1360 status_code = eLBOS_LbosNotFound;
1361 }
1362 /* Cleanup */
1363 ConnNetInfo_Destroy(net_info);
1364 return status_code;
1365 }
1366
1367 /** Find "0.0.0.0" in healthcheck_url and replace it with IP of current host
1368 * @attention
1369 * Original string is untouched. Caller is responsible for freeing
1370 * allocated space.
1371 */
s_LBOS_Replace0000WithIP(const char * healthcheck_url)1372 static char* s_LBOS_Replace0000WithIP(const char* healthcheck_url)
1373 {
1374 size_t chars_to_copy;
1375 const char* query;
1376 char hostname[kMaxLineSize];
1377 size_t length;
1378 /* new url with replaced "0.0.0.0" (if needed) */
1379 char* my_healthcheck_url;
1380 unsigned int local_host_ip;
1381 const char* replace_pos; /* to check if there is 0.0.0.0 */
1382 if (healthcheck_url == NULL)
1383 return NULL;
1384 /* By 'const' said that we will not touch healthcheck_url, but actually
1385 * we need to be able to change it, so we copy it */
1386 /* If we need to insert local IP instead of 0.0.0.0, we will */
1387 if ((replace_pos = strstr(healthcheck_url, "0.0.0.0")) == NULL) {
1388 my_healthcheck_url = strdup(healthcheck_url);
1389 return(my_healthcheck_url);
1390 }
1391 my_healthcheck_url = (char*)calloc(kMaxLineSize, sizeof(char));
1392 if (my_healthcheck_url == NULL) {
1393 CORE_LOG(eLOG_Warning, "Failed memory allocation. Most likely, "
1394 "not enough RAM.");
1395 return NULL;
1396 }
1397 chars_to_copy = replace_pos - healthcheck_url;
1398 query = replace_pos + strlen("0.0.0.0");
1399 local_host_ip = g_LBOS_UnitTesting_GetLBOSFuncs()->LocalHostAddr(eDefault);
1400 if (local_host_ip == 0) {
1401 CORE_LOG(eLOG_Warning,
1402 "Error with announcement, cannot find local IP.");
1403 free(my_healthcheck_url);
1404 return NULL;
1405 }
1406 SOCK_HostPortToString(local_host_ip, 0, hostname, kMaxLineSize - 1);
1407 if (strlen(hostname) == 0) {
1408 CORE_LOG(eLOG_Warning,
1409 "Error with announcement, cannot find local IP.");
1410 free(my_healthcheck_url);
1411 return NULL;
1412 }
1413 length = strlen(my_healthcheck_url);
1414 my_healthcheck_url =
1415 g_LBOS_StringConcat(g_LBOS_StringConcat(g_LBOS_StringNConcat(
1416 my_healthcheck_url, healthcheck_url, &length, chars_to_copy),
1417 strlwr(hostname), &length),
1418 query, &length);
1419
1420 return my_healthcheck_url;
1421 }
1422
1423
s_TurnOn()1424 static int s_TurnOn()
1425 {
1426 if (s_LBOS_Init == 0) {
1427 s_LBOS_funcs.Initialize();
1428 }
1429 if (s_LBOS_TurnedOn == 0) {
1430 return 0;
1431 }
1432 return 1;
1433 }
1434
1435 /*/////////////////////////////////////////////////////////////////////////////
1436 // UNIT TESTING //
1437 /////////////////////////////////////////////////////////////////////////////*/
1438 /** Check whether LBOS client is turned ON or OFF
1439 * @return
1440 * address of static variable s_LBOS_TurnedOn
1441 * @see
1442 * SERV_LBOS_Open() */
g_LBOS_UnitTesting_PowerStatus(void)1443 int* g_LBOS_UnitTesting_PowerStatus(void)
1444 {
1445 return &s_LBOS_TurnedOn;
1446 }
1447
1448
g_LBOS_UnitTesting_GetLBOSFuncs(void)1449 SLBOS_Functions* g_LBOS_UnitTesting_GetLBOSFuncs(void)
1450 {
1451 return &s_LBOS_funcs;
1452 }
1453
1454 /** Check whether LBOS client has been initialized already
1455 * @return
1456 * Address of static variable s_LBOS_Init
1457 * @see
1458 * SERV_LBOS_Open() */
g_LBOS_UnitTesting_InitStatus(void)1459 int* g_LBOS_UnitTesting_InitStatus(void)
1460 {
1461 return &s_LBOS_Init;
1462 }
1463
1464
1465 /** List of addresses of LBOS that is maintained in actual state.
1466 * @return
1467 * address of static variable s_LBOS_InstancesList.
1468 * @see
1469 * SERV_LBOS_Open(), s_LBOS_FillCandidates() */
g_LBOS_UnitTesting_Instance(void)1470 char** g_LBOS_UnitTesting_Instance(void)
1471 {
1472 return &s_LBOS_Instance;
1473 }
1474
1475
1476 /** Pointer to s_LBOS_CurrentRole
1477 * @return
1478 * address of static variable s_LBOS_CurrentRole.
1479 * @see */
g_LBOS_UnitTesting_Lbosresolver(void)1480 char** g_LBOS_UnitTesting_Lbosresolver(void)
1481 {
1482 return &s_LBOS_Lbosresolver;
1483 }
1484
1485
g_LBOS_UnitTesting_SetLBOSFindMethod(SERV_ITER iter,ELBOSFindMethod method)1486 int/*bool*/ g_LBOS_UnitTesting_SetLBOSFindMethod (SERV_ITER iter,
1487 ELBOSFindMethod method)
1488 {
1489 SLBOS_Data* data;
1490 assert(g_LBOS_CheckIterator(iter, ELBOSIteratorCheckType_MustHaveData));
1491 data = (SLBOS_Data*) iter->data;
1492 data->find_method = method;
1493 return 1;
1494 }
1495
1496
g_LBOS_UnitTesting_SetLBOSResolverFile(const char * resolverfile)1497 int/*bool*/ g_LBOS_UnitTesting_SetLBOSResolverFile(const char* resolverfile)
1498 {
1499 if (resolverfile != NULL) {
1500 kLbosresolverFile = resolverfile;
1501 return 1;
1502 }
1503 return 0;
1504 }
1505
1506
1507 /** Set custom address for LBOS. Can be either hostname:port or IP:port.
1508 * Intended mostly for testing.
1509 * @param[in] iter
1510 * Where to set address for LBOS. Change is made only for this iterator */
g_LBOS_UnitTesting_SetLBOSaddress(SERV_ITER iter,char * address)1511 int/*bool*/ g_LBOS_UnitTesting_SetLBOSaddress (SERV_ITER iter, char* address) {
1512 SLBOS_Data* data;
1513 assert(g_LBOS_CheckIterator(iter, ELBOSIteratorCheckType_MustHaveData));
1514 data = (SLBOS_Data*) iter->data;
1515 data->lbos_addr = address;
1516 return 1;
1517 }
1518
1519
g_LBOS_UnitTesting_GetAnnouncedServers(void)1520 struct SLBOS_AnnounceHandle_Tag** g_LBOS_UnitTesting_GetAnnouncedServers(void)
1521 {
1522 return &s_LBOS_AnnouncedServers;
1523 }
1524
1525
g_LBOS_UnitTesting_GetAnnouncedServersNum(void)1526 int g_LBOS_UnitTesting_GetAnnouncedServersNum(void)
1527 {
1528 return s_LBOS_AnnouncedServersNum;
1529 }
1530
1531
g_LBOS_UnitTesting_FindAnnouncedServer(const char * service,const char * version,unsigned short port,const char * host)1532 int g_LBOS_UnitTesting_FindAnnouncedServer(const char* service,
1533 const char* version,
1534 unsigned short port,
1535 const char* host)
1536 {
1537 return s_LBOS_FindAnnouncedServer(service, version, port, host);
1538 }
1539
1540
1541 /*/////////////////////////////////////////////////////////////////////////////
1542 // DATA CONSTRUCTOR/DESTRUCTOR //
1543 /////////////////////////////////////////////////////////////////////////////*/
1544 /** Constructor for SLBOS_Data. Returns pointer to new empty initialized
1545 * SLBOS_Data. */
s_LBOS_ConstructData(size_t candidatesCapacity)1546 static SLBOS_Data* s_LBOS_ConstructData(size_t candidatesCapacity)
1547 {
1548 SLBOS_Data* data;
1549
1550 if (!(data = (SLBOS_Data*) calloc(1, sizeof(SLBOS_Data))))
1551 {
1552 CORE_LOG_X(1, eLOG_Error, "Could not allocate memory for LBOS mapper");
1553 return NULL;
1554 }
1555 /*
1556 * We consider that there will never be more than 20 candidates, which
1557 * is not many for us
1558 */
1559 data->a_cand = candidatesCapacity;
1560 data->pos_cand = 0;
1561 data->n_cand = 0;
1562 data->lbos_addr = NULL;
1563 data->find_method = eLBOSFindMethod_None;
1564 data->cand = (SLBOS_Candidate*)calloc(candidatesCapacity,
1565 sizeof(SLBOS_Candidate));
1566 return data;
1567 }
1568
1569
1570 /** Destructor for SLBOS_Data.
1571 * @param[in] data_to_delete
1572 * Please note that you have to set pointer to NULL yourself. */
s_LBOS_DestroyData(SLBOS_Data * data)1573 static void s_LBOS_DestroyData(SLBOS_Data* data)
1574 {
1575 if (data == NULL) {
1576 return;
1577 }
1578 if (data->cand != NULL) {
1579 size_t i;
1580 for (i = data->pos_cand; i < data->n_cand; i++) {
1581 if (data->cand[i].info != NULL) {
1582 free(data->cand[i].info);
1583 data->cand[i].info = NULL;
1584 }
1585 }
1586 free(data->cand);
1587 }
1588 if (data->net_info) {
1589 ConnNetInfo_Destroy(data->net_info);
1590 }
1591 free(data);
1592 }
1593
1594
1595 /*/////////////////////////////////////////////////////////////////////////////
1596 // MAIN MAPPER FUNCTIONS //
1597 /////////////////////////////////////////////////////////////////////////////*/
1598 /** This function tests existence of the application that should always be
1599 * found - LBOS itself. If it is not found, we turn LBOS off. */
s_LBOS_Initialize(void)1600 static void s_LBOS_Initialize(void)
1601 {
1602 const char* service = "/lbos";
1603 SConnNetInfo* net_info;
1604 SERV_ITER iter;
1605 CORE_LOCK_WRITE;
1606 if (s_LBOS_Instance == NULL) {
1607 s_LBOS_Instance = g_LBOS_GetLBOSAddress();
1608 }
1609 if (s_EmptyNetInfo == NULL) {
1610 s_EmptyNetInfo = ConnNetInfo_Create(NULL);
1611 }
1612 CORE_UNLOCK;
1613 s_LBOS_TurnedOn = 1; /* To ensure that initialization does
1614 not depend on this variable */
1615 s_LBOS_Init = 1;
1616
1617 /*
1618 * Load DTAB Local from registry
1619 */
1620 free(s_LBOS_DTABLocal);
1621 s_LBOS_DTABLocal = g_LBOS_RegGet("CONN", "DTAB", NULL);
1622 if (g_LBOS_StringIsNullOrEmpty(s_LBOS_DTABLocal)) {
1623 CORE_LOG_X(1, eLOG_Trace, "No DTAB in registry");
1624 } else {
1625 CORE_LOGF_X(1, eLOG_Trace,
1626 ("DTAB from registry: %s ", s_LBOS_DTABLocal));
1627 }
1628
1629 /* Check On/Off status */
1630 char* lbos_toggle = g_LBOS_RegGet("CONN", "LBOS_ENABLE", NULL);
1631 int lbos_toggled = ConnNetInfo_Boolean(lbos_toggle);
1632 free(lbos_toggle);
1633 if (lbos_toggled) {
1634 CORE_LOG_X(1, eLOG_Note, "LBOS is turned ON in config.");
1635 } else {
1636 CORE_LOG_X(1, eLOG_Warning,
1637 "LBOS is NOT turned ON in config! Please provide "
1638 "[CONN]LBOS_ENABLE=1");
1639 s_LBOS_TurnedOn = 0;
1640 return;
1641 }
1642 /*
1643 * Try to find LBOS
1644 */
1645 iter = (SERV_ITER)calloc(1, sizeof(*iter));
1646 assert(iter != NULL); /* we can do nothing if this happens */
1647 iter->name = service;
1648 net_info = ConnNetInfo_Clone(s_EmptyNetInfo);
1649 iter->op = SERV_LBOS_Open(iter, net_info, NULL);
1650 ConnNetInfo_Destroy(net_info);
1651 if (iter->op == NULL) {
1652 CORE_LOGF_X(1, eLOG_Warning,
1653 ("Could not connect to LBOS, or "
1654 "http://%s/lbos/text/mlresolve?name=%%2flbos "
1655 "is empty. Turning LBOS off in this "
1656 "process.", s_LBOS_Instance));
1657 s_LBOS_TurnedOn = 0;
1658 } else {
1659 s_LBOS_Close(iter);
1660 s_LBOS_TurnedOn = 1;
1661 }
1662 free(iter);
1663
1664 }
1665
1666
1667 /** After we receive answer from dispd.cgi, we parse header
1668 * to get all hosts. */
s_LBOS_ParseHeader(const char * header,void * response,int server_error)1669 static EHTTP_HeaderParse s_LBOS_ParseHeader(const char* header,
1670 void* /* SLBOS_UserData* */
1671 response,
1672 int server_error)
1673 {
1674 SLBOS_UserData* response_output;
1675 int status_code = 0/*success code if any*/;
1676 /* For all we know, status message ends before \r\n */
1677 char* header_end;
1678 char* status_message;
1679 unsigned int content_length;
1680 char* content_length_pos;
1681
1682 if (response == NULL) {
1683 /* We do not intervent in the process by default */
1684 return eHTTP_HeaderSuccess;
1685 }
1686 response_output = (SLBOS_UserData*)response;
1687 header_end = strstr(header, "\r\n");
1688 status_message = (char*)calloc(header_end-header, sizeof(char));
1689
1690 if (sscanf(header, "%*s %d %[^\r]\r\n", &status_code,
1691 status_message) < 1)
1692 {
1693 if (response != NULL) {
1694 response_output->http_response_code = 503; /* server error */
1695 }
1696 free(status_message);
1697 return eHTTP_HeaderError;
1698 }
1699 if (status_code != 200) {
1700 CORE_LOGF(eLOG_Critical, ("s_LBOS_UrlReadAll: LBOS returned status "
1701 "code %d", status_code));
1702 }
1703 char* temp_header = strlwr(strdup(header)); /* we cannot modify
1704 * original */
1705 /* We want to be sure that found "content-length" is the tag itself,
1706 * not a value as if in "Some-Tag: content-length: 2". So we check for
1707 * either \n before tag, or it should be positioned at the very
1708 * beginning of the string
1709 */
1710 content_length_pos = strstr(temp_header, "content-length: ");
1711 if (
1712 content_length_pos != NULL
1713 &&
1714 (
1715 (*(content_length_pos - 1) == '\n')
1716 ||
1717 (content_length_pos == temp_header)
1718 )
1719 )
1720 {
1721 sscanf(content_length_pos + strlen("content-length: "),
1722 "%u", &content_length);
1723 }
1724 /* If we could not read "content-length", we do not have any
1725 * estimation of the upper bound */
1726 else {
1727 content_length = (unsigned int)(-1);
1728 }
1729 free(temp_header);
1730 /* check for empty document */
1731 response_output->http_response_code = status_code;
1732 response_output->http_status_mesage = status_message;
1733 response_output->content_length = content_length;
1734 return eHTTP_HeaderSuccess;
1735 }
1736
1737
s_LBOS_Reset(SERV_ITER iter)1738 static void s_LBOS_Reset(SERV_ITER iter)
1739 {
1740 size_t i;
1741 SLBOS_Data* data;
1742 /*
1743 * First, we need to check if it is our iterator. It would be bad
1744 * to mess with other mapper's iterator
1745 */
1746 assert(g_LBOS_CheckIterator(iter, ELBOSIteratorCheckType_NoCheck));
1747 /*
1748 * Check passed, it is LBOS's iterator. Now we can be sure that we know
1749 * how to reset it
1750 */
1751 data = (SLBOS_Data*) iter->data;
1752 if (data != NULL) {
1753 if (data->cand) {
1754 for (i = data->pos_cand; i < data->n_cand; i++)
1755 free(data->cand[i].info);
1756 free(data->cand);
1757 /*There will hardly be more than 20 candidates, so we allocate
1758 * memory for candidates straight away, no array of pointers
1759 */
1760 data->cand = (SLBOS_Candidate*)calloc(data->a_cand,
1761 sizeof(SLBOS_Candidate));
1762 if (data->cand == NULL) {
1763 CORE_LOG(eLOG_Critical, "s_LBOS_Reset: No RAM. "
1764 "Failed to create iterator.");
1765 data->a_cand = 0;
1766 data->n_cand = 0;
1767 data->pos_cand = 0;
1768 }
1769 #if defined(_DEBUG) && !defined(NDEBUG)
1770 /** We check both that it returns zero and does not crash accessing
1771 * allocated memory */
1772 for (i = 0; i < data->n_cand; i++) {
1773 assert(data->cand + i != NULL);
1774 data->cand[i].info = (SSERV_Info*)malloc(sizeof(SSERV_Info));
1775 free(data->cand[i].info);
1776 data->cand[i].info = NULL;
1777 }
1778 #endif
1779 }
1780 data->n_cand = 0;
1781 data->pos_cand = 0;
1782 }
1783 return;
1784 }
1785
1786
1787 /** Not implemented in LBOS client. */
s_LBOS_Feedback(SERV_ITER a,double b,int c)1788 static int/*bool*/ s_LBOS_Feedback (SERV_ITER a, double b, int c)
1789 {
1790 return 0;
1791 }
1792
1793
1794 /** Not implemented in LBOS client. */
s_LBOS_Update(SERV_ITER iter,const char * text,int code)1795 static int/*bool*/ s_LBOS_Update(SERV_ITER iter, const char* text, int code)
1796 {
1797 return 1;
1798 }
1799
1800
s_LBOS_Close(SERV_ITER iter)1801 static void s_LBOS_Close (SERV_ITER iter)
1802 {
1803 SLBOS_Data* data;
1804 /*
1805 * First, we need to check if it is our iterator. It would be bad
1806 * to mess with other mapper's iterator
1807 */
1808 assert(g_LBOS_CheckIterator(iter, ELBOSIteratorCheckType_MustHaveData));
1809 data = (SLBOS_Data*) iter->data;
1810 if(data->n_cand > 0) {
1811 /*s_Reset() has to be called before*/
1812 s_LBOS_Reset(iter);
1813 }
1814 s_LBOS_DestroyData((SLBOS_Data*)iter->data);
1815 iter->data = NULL;
1816 return;
1817 }
1818
1819
1820 /** 1) If iterator is valid - find and return next server;
1821 * 2) If iterator is outdated or empty - we update it and fill it with hosts
1822 * and go to 1) */
s_LBOS_GetNextInfo(SERV_ITER iter,HOST_INFO * host_info)1823 static SSERV_Info* s_LBOS_GetNextInfo(SERV_ITER iter, HOST_INFO* host_info)
1824 {
1825 SLBOS_Data* data;
1826 /*
1827 * We store snapshot of LBOS answer in memory. So we just go from last
1828 * known position. Position is stored in n_skip.
1829 * If we want to update list - well, we update all the list and start from
1830 * the beginning.
1831 */
1832
1833 /* host_info is implemented only for lbsmd mapper, so we just set it to
1834 * NULL, if it even exists.
1835 */
1836 if (host_info) {
1837 *host_info = NULL; /*No host data*/
1838 }
1839
1840 assert(iter != NULL); /* we can do nothing if this happens */
1841
1842 if (iter->data == NULL) {
1843 iter->data = s_LBOS_ConstructData(kInitialCandidatesCount);
1844 }
1845
1846 data = (SLBOS_Data*)(iter->data); /** set typed variable
1847 for convenience */
1848
1849 if (data->n_cand == 0) { /**< this is possible after reset()*/
1850 g_LBOS_UnitTesting_GetLBOSFuncs()->FillCandidates(data, iter->name);
1851 }
1852 /*
1853 * If there are some servers left to show, we move iterator and show next
1854 * Otherwise, return NULL
1855 */
1856 if (data->pos_cand < data->n_cand) {
1857 data->pos_cand++;
1858 return data->cand[data->pos_cand-1].info;
1859 } else {
1860 return NULL;
1861 }
1862 }
1863
1864
1865 /** Creates iterator and fills it with found servers.
1866 * @param[in,out] iter
1867 * Pointer to iterator. It is read and rewritten
1868 * @param[out] net_info
1869 * Connection information.
1870 * @param[out] info
1871 * Pointer to variable to return pointer to info about server. */
SERV_LBOS_Open(SERV_ITER iter,const SConnNetInfo * net_info,SSERV_Info ** info)1872 const SSERV_VTable* SERV_LBOS_Open( SERV_ITER iter,
1873 const SConnNetInfo* net_info,
1874 SSERV_Info** info )
1875 {
1876 SLBOS_Data* data;
1877 char* new_name = NULL; /* if we need to add dbaf */
1878 const char* orig_serv_name = iter->name; /* we may modify name with dbaf */
1879 if (!s_TurnOn()) {
1880 return NULL;
1881 }
1882 /*
1883 * First, we need to check arguments
1884 */
1885 assert(iter != NULL); /* we can do nothing if this happens */
1886
1887 /* Check that iter is not a mask - LBOS cannot work with masks */
1888 if (iter->ismask) {
1889 CORE_LOG(eLOG_Warning, "Mask was provided instead of service name. "
1890 "Masks are not supported in LBOS.");
1891 return NULL;
1892 }
1893
1894 /* Check that service name is provided - otherwise there is nothing to
1895 * search for */
1896 if (iter->name == NULL) {
1897 CORE_LOG(eLOG_Warning, "\"iter->name\" is null, not able "
1898 "to continue SERV_LBOS_Open");
1899 return NULL;
1900 }
1901
1902 /* If dbaf is defined, we construct new service name and assign it
1903 * to iter */
1904 if ( iter->arg && (strcmp(iter->arg, "dbaf") == 0) && iter->val ) {
1905 size_t length = 0;
1906 new_name =
1907 g_LBOS_StringConcat(g_LBOS_StringConcat(g_LBOS_StringConcat(
1908 NULL, iter->name, &length),
1909 "/", &length),
1910 iter->val, &length);
1911 if (new_name == NULL) {
1912 CORE_LOG(eLOG_Warning, "Could not concatenate dbaf with service "
1913 "name, probably not enough RAM. Searching "
1914 "for service without dbaf");
1915 } else {
1916 iter->name = new_name;
1917 }
1918 }
1919 /*
1920 * Arguments OK, start work
1921 */
1922 if (info != NULL) {
1923 *info = NULL;
1924 }
1925 data = s_LBOS_ConstructData(kInitialCandidatesCount);
1926 if(net_info == NULL) {
1927 CORE_LOG(eLOG_Warning,
1928 "Parameter \"net_info\" is null, creating net info. "
1929 "Please, fix the code and provide net_info.");
1930 data->net_info = ConnNetInfo_Clone(s_EmptyNetInfo);
1931 } else {
1932 data->net_info = ConnNetInfo_Clone(net_info);
1933 }
1934 // Check if CONNECT_Init() has been run before
1935 if (g_CORE_GetRequestDtab == NULL) {
1936 CORE_LOG(eLOG_Critical,
1937 "LBOS FAIL! Please run CONNECT_Init() prior to using LBOS!\n"
1938 "Example:\n"
1939 "CNcbiRegistry& config = CNcbiApplication::Instance()"
1940 "->GetConfig();\n"
1941 "CONNECT_Init(&config);\n"
1942 "LBOS::Announce(...);");
1943 s_LBOS_DestroyData(data);
1944 if (iter->name != orig_serv_name) {
1945 free(new_name);
1946 iter->name = orig_serv_name;
1947 }
1948 return NULL;
1949 }
1950 const char* request_dtab = g_CORE_GetRequestDtab();
1951 if (!g_LBOS_StringIsNullOrEmpty(request_dtab)) {
1952 /* Add a semicolon to separate DTabs */
1953 ConnNetInfo_ExtendUserHeader(data->net_info, "DTab-Local: ;");
1954 ConnNetInfo_ExtendUserHeader(data->net_info, request_dtab);
1955 }
1956 g_LBOS_UnitTesting_GetLBOSFuncs()->FillCandidates(data, iter->name);
1957 /* Connect to LBOS, read what is needed, build iter, info, host_info
1958 */
1959 if (!data->n_cand) {
1960 s_LBOS_DestroyData(data);
1961 if (iter->name != orig_serv_name) {
1962 free(new_name);
1963 iter->name = orig_serv_name;
1964 }
1965 return NULL;
1966 }
1967 /* Something was found, now we can use iter */
1968
1969 /*Just explicitly mention here to do something with it*/
1970 iter->data = data;
1971 if (iter->name != orig_serv_name) {
1972 free(new_name);
1973 iter->name = orig_serv_name;
1974 }
1975 return &s_lbos_op;
1976 }
1977
1978
1979 /*/////////////////////////////////////////////////////////////////////////////
1980 // ANNOUNCEMENT/DEANNOUCEMENT //
1981 /////////////////////////////////////////////////////////////////////////////*/
1982 /** For the unit testing this function is moved from LBOS_Announce
1983 * (interception of
1984 * parameters). For full description see LBOS_Announce
1985 * @sa
1986 * LBOS_Announce() */
1987 static
s_LBOS_Announce(const char * service,const char * version,const char * host,unsigned short port,const char * healthcheck_url,const char * meta_args,char ** lbos_answer,char ** http_status_message)1988 unsigned short s_LBOS_Announce(const char* service,
1989 const char* version,
1990 const char* host,
1991 unsigned short port,
1992 const char* healthcheck_url,
1993 #ifdef LBOS_METADATA
1994 const char* meta_args,
1995 #endif /* LBOS_METADATA */
1996 /* lbos_answer is never NULL */
1997 char** lbos_answer,
1998 char** http_status_message)
1999 {
2000 char* lbos_address;
2001 char* lbos_addr; /* to store address of LBOS that
2002 did the announcement*/
2003 int status_code;
2004 char* status_message;
2005 SConnNetInfo* net_info;
2006 const char* query_format = NULL;
2007 char* buf = NULL; /* for answer from LBOS */
2008 int parsed_symbols = 0;
2009 assert(!g_LBOS_StringIsNullOrEmpty(host));
2010
2011 if (!s_TurnOn())
2012 return eLBOS_Disabled;
2013
2014 lbos_address = s_LBOS_Instance;
2015 status_code = 0;
2016 status_message = NULL;
2017 net_info = ConnNetInfo_Clone(s_EmptyNetInfo);
2018 net_info->req_method = eReqMethod_Put;
2019 /* Format for announcement request. "ip" parameter is optional and
2020 * will be added separately, if provided */
2021 query_format = "http://%s/lbos/v3/services%s?version=%s&"
2022 "port=%hu&"
2023 "check=%s&"
2024 "ip=%s&"
2025 "format=json";
2026 /*
2027 * Let's try announce
2028 */
2029 char* query;
2030 /* We do not count extra 1 byte for \0 because we still have extra
2031 * bytes because of %s placeholders in query_format */
2032 query = (char*)calloc(strlen(query_format) +
2033 strlen(lbos_address) +
2034 strlen(service) + strlen(version) +
2035 5/* port */ + strlen(healthcheck_url) +
2036 strlen(host),
2037 sizeof(char));
2038 sprintf(query, query_format,
2039 lbos_address, service, version, port, healthcheck_url, host);
2040 if (!g_LBOS_StringIsNullOrEmpty(meta_args)) {
2041 query = g_LBOS_StringConcat(g_LBOS_StringConcat(
2042 query, "&", NULL),
2043 meta_args, NULL);
2044 }
2045 buf = s_LBOS_UrlReadAll(net_info, query, &status_code, &status_message);
2046 free(query);
2047 if (!g_LBOS_StringIsNullOrEmpty(buf)) {
2048 /* If this function is not able to parse LBOS output, original LBOS
2049 * response will be available to the caller. Otherwise, content of
2050 * lbos_answer will be replaced with parsed IP address of LBOS watcher
2051 * a bit later */
2052 *lbos_answer = strdup(buf);
2053 }
2054 if (http_status_message != NULL && status_message != NULL) {
2055 *http_status_message = strdup(status_message);
2056 }
2057 free(status_message);
2058 switch (status_code) {
2059 case 0:
2060 /* If no LBOS found */
2061 CORE_LOG(eLOG_Warning, "Announce failed. No LBOS found.");
2062 status_code = eLBOS_LbosNotFound;
2063 break;
2064 case eLBOS_NotFound: case eLBOS_BadRequest: case eLBOS_Server:
2065 /* If announced server has a broken healthcheck */
2066 CORE_LOGF(eLOG_Warning, ("Announce failed. "
2067 "LBOS returned error code %d.", status_code));
2068 break;
2069 case eLBOS_Success:
2070 /* If we announced successfully and status_code is 200,
2071 * let's extract LBOS address */
2072 lbos_addr = (char*)calloc(kMaxLineSize, sizeof(char)); /* will not be
2073 * free()'d */
2074 if (lbos_addr == NULL) {
2075 CORE_LOG(eLOG_Warning, "Failed memory allocation. Most likely, "
2076 "not enough RAM.");
2077 status_code = eLBOS_MemAlloc;
2078 break;
2079 }
2080 if (buf != NULL) {
2081 parsed_symbols = sscanf(buf, "{\"watcher\":\"%[^\"]\"}",
2082 lbos_addr);
2083 }
2084 if (parsed_symbols != 1) {
2085 CORE_LOG(eLOG_Warning, "g_LBOS_Announce: LBOS answered 200 OK, but "
2086 "output could not be parsed");
2087 free(lbos_addr);
2088 status_code = eLBOS_Protocol;
2089 break;
2090 }
2091 /* If announce finished with success, we parsed it to extract LBOS
2092 * ip:port. We free() original output and replace it with ip:port */
2093 free(*lbos_answer);
2094 *lbos_answer = lbos_addr;
2095 /* If we could not announce, it is really bad */
2096 break;
2097 default:
2098 CORE_LOGF(eLOG_Warning, ("Announce failed. "
2099 "LBOS returned error code %d. "
2100 "LBOS answer: %s.", status_code, buf));
2101 }
2102 /* Cleanup */
2103 free(buf);
2104 ConnNetInfo_Destroy(net_info);
2105 return status_code;
2106 }
2107
2108
LBOS_Announce(const char * service,const char * version,const char * host,unsigned short port,const char * healthcheck_url,const char * meta_args,char ** lbos_answer,char ** http_status_message)2109 unsigned short LBOS_Announce(const char* service,
2110 const char* version,
2111 const char* host,
2112 unsigned short port,
2113 const char* healthcheck_url,
2114 #ifdef LBOS_METADATA
2115 const char* meta_args,
2116 #endif /* LBOS_METADATA */
2117 char** lbos_answer,
2118 char** http_status_message)
2119 {
2120 char* my_healthcheck_url = NULL;
2121 char* healthcheck_encoded = NULL;
2122 char* my_host = NULL;
2123 char* service_encoded = NULL;
2124 char* version_encoded = NULL;
2125 unsigned short result;
2126
2127 /*
2128 * First we check input arguments
2129 */
2130 if (s_LBOS_CheckAnnounceArgs(service, version, host, port, healthcheck_url,
2131 lbos_answer) == 0)
2132 {
2133 return eLBOS_InvalidArgs;
2134 }
2135 /*
2136 * Pre-assign variables
2137 */
2138 *lbos_answer = NULL;
2139 /* Check if we need to replace 0.0.0.0 with local IP, and do it if needed*/
2140 my_healthcheck_url = s_LBOS_Replace0000WithIP(healthcheck_url);
2141
2142 /*my_healthcheck_url = strdup(healthcheck_url); */
2143 if (my_healthcheck_url == NULL) {
2144 result = eLBOS_DNSResolve;
2145 goto clean_and_exit;
2146 }
2147 /* If host provided separately from healthcheck URL, check if we need to
2148 * replace 0.0.0.0 with local IP, and do it if needed */
2149 if (!g_LBOS_StringIsNullOrEmpty(host)) {
2150 my_host = s_LBOS_Replace0000WithIP(host);
2151 }
2152 else { /* If host was NOT provided, we append local IP to query,
2153 * just in case */
2154 SConnNetInfo * healthcheck_info;
2155 healthcheck_info = ConnNetInfo_Clone(s_EmptyNetInfo);
2156 healthcheck_info->host[0] = '\0'; /* to be sure that it will be
2157 * overridden */
2158 /* Save info about host */
2159 ConnNetInfo_ParseURL(healthcheck_info, my_healthcheck_url);
2160 my_host = strdup(healthcheck_info->host);
2161 /* If we could not parse healthcheck URL, throw "Invalid Arguments" */
2162 if (g_LBOS_StringIsNullOrEmpty(my_host)) {
2163 ConnNetInfo_Destroy(healthcheck_info);
2164 CORE_LOG_X(eLBOS_InvalidArgs, eLOG_Critical,
2165 "Could not parse host from healthcheck URL. Please set "
2166 "ip of the announced server explicitly.");
2167 result = eLBOS_InvalidArgs;
2168 goto clean_and_exit;
2169 }
2170 ConnNetInfo_Destroy(healthcheck_info);
2171 }
2172
2173 healthcheck_encoded = s_LBOS_URLEncode(my_healthcheck_url);
2174 service_encoded = s_LBOS_ModifyServiceName(service);
2175 version_encoded = s_LBOS_URLEncode(version);
2176
2177 /* Announce */
2178 result =
2179 g_LBOS_UnitTesting_GetLBOSFuncs()->AnnounceEx(service_encoded,
2180 version_encoded,
2181 my_host,
2182 port,
2183 healthcheck_encoded,
2184 meta_args,
2185 lbos_answer,
2186 http_status_message);
2187 if (result == eLBOS_Success) {
2188 CORE_LOCK_WRITE;
2189 s_LBOS_AddAnnouncedServer(service, version, port, healthcheck_url);
2190 CORE_UNLOCK;
2191 }
2192
2193 /* Cleanup */
2194 clean_and_exit:
2195 free(healthcheck_encoded);
2196 free(my_healthcheck_url);
2197 free(my_host);
2198 free(version_encoded);
2199 free(service_encoded);
2200 return result;
2201 }
2202
2203
LBOS_AnnounceFromRegistry(const char * registry_section,char ** lbos_answer,char ** http_status_message)2204 unsigned short LBOS_AnnounceFromRegistry(const char* registry_section,
2205 char** lbos_answer,
2206 char** http_status_message)
2207 {
2208 unsigned short result = eLBOS_Success;
2209 size_t i = 0;
2210 unsigned int port;
2211 char* srvc;
2212 char* vers;
2213 char* port_str;
2214 char* hlth;
2215 char* host;
2216 char* meta;
2217
2218 if (g_LBOS_StringIsNullOrEmpty(registry_section)) {
2219 registry_section = kLBOSAnnouncementSection;
2220 }
2221 srvc = g_LBOS_RegGet(registry_section, kLBOSServiceVariable, NULL);
2222 vers = g_LBOS_RegGet(registry_section, kLBOSVersionVariable, NULL);
2223 port_str = g_LBOS_RegGet(registry_section, kLBOSPortVariable, NULL);
2224 host = g_LBOS_RegGet(registry_section, kLBOSServerHostVariable, NULL);
2225 hlth = g_LBOS_RegGet(registry_section, kLBOSHealthcheckUrlVariable,
2226 NULL);
2227 meta = g_LBOS_RegGet(registry_section, kLBOSMetaVariable,
2228 NULL);
2229
2230 /* Check port that it is a number of max 5 digits and no other symbols */
2231 for (i = 0; i < strlen(port_str); i++) {
2232 if (!isdigit(port_str[i])) {
2233 CORE_LOGF_X(eLBOS_InvalidArgs, eLOG_Warning,
2234 ("Port \"%s\" in section %s is invalid", port_str,
2235 registry_section));
2236 result = eLBOS_InvalidArgs;
2237 goto clean_and_exit;
2238 }
2239 }
2240 if (strlen(port_str) > 5 || (sscanf(port_str, "%d", &port) != 1) ||
2241 port < 1 || port > 65535)
2242 {
2243 result = eLBOS_InvalidArgs;
2244 goto clean_and_exit;
2245 }
2246
2247 /* Announce */
2248 result = LBOS_Announce(srvc, vers, host, (unsigned short)port, hlth, meta,
2249 lbos_answer, http_status_message);
2250 if (result == eLBOS_Success) {
2251 CORE_LOCK_WRITE;
2252 s_LBOS_AddAnnouncedServer(srvc, vers, port, hlth);
2253 CORE_UNLOCK;
2254 }
2255
2256 /* Cleanup */
2257 clean_and_exit:
2258 free(srvc);
2259 free(vers);
2260 free(port_str);
2261 free(hlth);
2262 free(host);
2263 free(meta);
2264 return result;
2265 }
2266
2267 /* Separated from LBOS_Deannounce to easier control memory allocated for
2268 * variables */
s_LBOS_Deannounce(const char * service,const char * version,const char * host,unsigned short port,char ** lbos_answer,char ** http_status_message,SConnNetInfo * net_info)2269 unsigned short s_LBOS_Deannounce(const char* service,
2270 const char* version,
2271 const char* host,
2272 unsigned short port,
2273 char** lbos_answer,
2274 char** http_status_message,
2275 SConnNetInfo* net_info)
2276 {
2277 const char* query_format;
2278 char* lbos_address;
2279 char* status_message = NULL;
2280 char* buf;
2281 int status_code;
2282 lbos_address = s_LBOS_Instance;
2283 status_code = 0;
2284 buf = NULL;
2285 /* ip */
2286 query_format = "http://%s/lbos/v3/services%s?version=%s&"
2287 "port=%hu&"
2288 "ip=%s";
2289 /*
2290 * Try deannounce
2291 */
2292 char* query;
2293 assert(!g_LBOS_StringIsNullOrEmpty(host));
2294 /* We do not count extra 1 byte for \0 because we still have extra
2295 * bytes because of %s placeholders */
2296 query = (char*)calloc(strlen(query_format) +
2297 strlen(lbos_address) + strlen(service) +
2298 strlen(version) + 5/*port*/ + strlen(host),
2299 sizeof(char));
2300 sprintf(query, query_format,
2301 lbos_address, service, version, port, host);
2302 buf = s_LBOS_UrlReadAll(net_info, query, &status_code,
2303 &status_message);
2304 free(query);
2305 if (lbos_answer != NULL && !g_LBOS_StringIsNullOrEmpty(buf)) {
2306 *lbos_answer = strdup(buf);
2307 }
2308 free(buf);
2309 if (http_status_message != NULL && status_message != NULL) {
2310 *http_status_message = strdup(status_message);
2311 }
2312 free(status_message);
2313
2314 if (status_code == 0) {
2315 status_code = eLBOS_LbosNotFound;
2316 }
2317 return status_code;
2318 }
2319
2320
LBOS_Deannounce(const char * service,const char * version,const char * host,unsigned short port,char ** lbos_answer,char ** http_status_message)2321 unsigned short LBOS_Deannounce(const char* service,
2322 const char* version,
2323 const char* host,
2324 unsigned short port,
2325 char** lbos_answer,
2326 char** http_status_message)
2327 {
2328 SConnNetInfo* net_info;
2329 char* service_encoded;
2330 char* version_encoded;
2331 char* my_host;
2332 unsigned short retval;
2333 /*
2334 * First we check input arguments
2335 */
2336 if (s_LBOS_CheckDeannounceArgs(service, version, host, port) == 0) {
2337 return eLBOS_InvalidArgs;
2338 }
2339 /*
2340 * Check if LBOS is ON
2341 */
2342 if (!s_TurnOn()) {
2343 return eLBOS_Disabled;
2344 }
2345 /*
2346 * If we are here, arguments are good!
2347 */
2348 if (!g_LBOS_StringIsNullOrEmpty(host)) {
2349 my_host = s_LBOS_Replace0000WithIP(host);
2350 }
2351 else { /* If host was NOT provided, we append local IP to query,
2352 * just in case */
2353 my_host = s_LBOS_Replace0000WithIP("0.0.0.0");
2354 if (g_LBOS_StringIsNullOrEmpty(my_host)){
2355 CORE_LOG_X(eLBOS_DNSResolve, eLOG_Critical,
2356 "Did not manage to get local IP address.");
2357 free(my_host);
2358 return eLBOS_DNSResolve;
2359 }
2360 }
2361 net_info = ConnNetInfo_Clone(s_EmptyNetInfo);
2362 net_info->req_method = eReqMethod_Delete;
2363 service_encoded = s_LBOS_ModifyServiceName(service);
2364 version_encoded = s_LBOS_URLEncode(version);
2365
2366 retval = s_LBOS_Deannounce(service_encoded, version_encoded,
2367 my_host, port, lbos_answer,
2368 http_status_message, net_info);
2369
2370 /* If eLBOS_NotFound or eLBOS_Success - we delete server from local storage
2371 * as no longer existing */
2372 if (retval == eLBOS_NotFound || retval == eLBOS_Success) {
2373 CORE_LOCK_WRITE;
2374 s_LBOS_RemoveAnnouncedServer(service, version, port, host);
2375 CORE_UNLOCK;
2376 }
2377
2378 /*
2379 * Cleanup
2380 */
2381 free(version_encoded);
2382 free(service_encoded);
2383 free(my_host);
2384 ConnNetInfo_Destroy(net_info);
2385
2386 return retval;
2387 }
2388
2389
2390 /** Deannounce all announced servers.
2391 * @note
2392 * Though this function is mt-safe, you should fully recognize the fact
2393 * results of simultaneous multiple de-announcement of the same service can be
2394 * unpredictable. For example, if service has just been de-announced in
2395 * different thread, this thread will return error "service is not announced".
2396
2397 * @return
2398 * 1 - all services de-announced successfully
2399 * 0 - at least one server could not be de-announced */
LBOS_DeannounceAll()2400 void LBOS_DeannounceAll()
2401 {
2402 struct SLBOS_AnnounceHandle_Tag** arr;
2403 struct SLBOS_AnnounceHandle_Tag* local_arr;
2404 unsigned int servers;
2405 unsigned int i;
2406
2407 CORE_LOCK_READ;
2408 arr = &s_LBOS_AnnouncedServers;
2409 servers = s_LBOS_AnnouncedServersNum;
2410 local_arr =
2411 (struct SLBOS_AnnounceHandle_Tag*)calloc(servers, sizeof(**arr));
2412 if (local_arr == NULL) {
2413 CORE_LOG_X(eLBOS_MemAlloc, eLOG_Warning,
2414 "RAM error. Cancelling deannounce all.");
2415 CORE_UNLOCK;
2416 return;
2417 }
2418 /*
2419 * Copy servers list to local variable in case other thread
2420 * deletes it or wants to announce new server which we would not want to
2421 * deannounce, since it will be announced after call of this function.
2422 */
2423 for (i = 0; i < servers; i++) {
2424 local_arr[i].version = strdup((*arr)[i].version);
2425 local_arr[i].service = strdup((*arr)[i].service);
2426 local_arr[i].port = (*arr)[i].port;
2427 if (strcmp((*arr)[i].host, "0.0.0.0") == 0) {
2428 /* If host is "0.0.0.0", we do not provide it to LBOS in
2429 * HTTP request (because it returns error in this case).
2430 * With no host provided, LBOS understands that we want
2431 * to deannounce server from local host, which is the same
2432 * as 0.0.0.0
2433 */
2434 local_arr[i].host = NULL;
2435 } else {
2436 local_arr[i].host = strdup((*arr)[i].host);
2437 }
2438 }
2439 CORE_UNLOCK;
2440 for (i = 0; i < servers; i++) {
2441 /* Deannounce */
2442 LBOS_Deannounce(local_arr[i].service,
2443 local_arr[i].version,
2444 local_arr[i].host,
2445 local_arr[i].port,
2446 NULL,
2447 NULL);
2448 /* Cleanup */
2449 free(local_arr[i].version);
2450 free(local_arr[i].host);
2451 free(local_arr[i].service);
2452 }
2453 free(local_arr);
2454 return;
2455 }
2456
2457
2458 /*/////////////////////////////////////////////////////////////////////////////
2459 // LBOS CONFIGURATION //
2460 /////////////////////////////////////////////////////////////////////////////*/
s_LBOS_CheckConfArgs(const char * service,const char ** lbos_answer)2461 static int s_LBOS_CheckConfArgs(const char* service, const char** lbos_answer)
2462 {
2463 unsigned int i;
2464 if (g_LBOS_StringIsNullOrEmpty(service) || lbos_answer == NULL) {
2465 CORE_LOG_X(eLBOS_InvalidArgs, eLOG_Warning,
2466 "s_LBOS_CheckConfArgs: service is NULL or lbos_answer "
2467 "is NULL");
2468 return 0;
2469 }
2470 for (i = 0; i < strlen(service); i++) {
2471 if (isspace(service[i])) {
2472 CORE_LOGF_X(eLBOS_InvalidArgs, eLOG_Warning,
2473 ("s_LBOS_CheckConfArgs: service "
2474 "\"%s\" contains invalid character", service));
2475 return 0;
2476 }
2477 }
2478 return 1;
2479 }
2480
2481
2482 /** This request will show currently used version for a requested service.
2483 * Current and previous version will be the same.
2484 * @param service[in]
2485 * Name of service for which to ask default version
2486 * @param lbos_answer[out]
2487 * Variable to be assigned pointer to C-string with LBOS answer
2488 * @param http_status_message[out]
2489 * Variable to be assigned pointer to C-string with status message from LBOS
2490 * @return
2491 * Status code returned by LBOS
2492 */
LBOS_ServiceVersionGet(const char * service,char ** lbos_answer,char ** http_status_message)2493 unsigned short LBOS_ServiceVersionGet(const char* service,
2494 char** lbos_answer,
2495 char** http_status_message)
2496 {
2497 char* service_encoded;
2498 const char* query_format;
2499 char* query;
2500 unsigned short return_code;
2501 /*
2502 * First we check input arguments
2503 */
2504 if (!s_LBOS_CheckConfArgs(service, (const char**)lbos_answer)) {
2505 return eLBOS_InvalidArgs;
2506 }
2507 /*
2508 * Check if LBOS is ON
2509 */
2510 if (!s_TurnOn()) {
2511 return eLBOS_Disabled;
2512 }
2513
2514 /*
2515 * Arguments are good! Let's do the request
2516 */
2517 service_encoded = s_LBOS_ModifyServiceName(service);
2518 query_format = "/lbos/v3/conf%s?format=xml";
2519 query = (char*)calloc(strlen(query_format) +
2520 strlen(service_encoded),
2521 sizeof(char));
2522 sprintf(query, query_format, service_encoded);
2523 return_code = s_LBOS_PerformRequest(query,
2524 lbos_answer,
2525 http_status_message,
2526 eReqMethod_Get,
2527 service);
2528 /*
2529 * Cleanup
2530 */
2531 free(query);
2532 free(service_encoded);
2533 return return_code;
2534 }
2535
2536
2537 /** This request can be used to set new version for a service. Current and
2538 * previous versions show currently set and previously used service versions.
2539 * @param[in] service
2540 * Name of service for which the version is going to be changed
2541 * @param new_version[out]
2542 * Version that will be used by default for specefied service
2543 * @param lbos_answer[out]
2544 * Variable to be assigned pointer to C-string with LBOS answer
2545 * @param http_status_message[out]
2546 * Variable to be assigned pointer to C-string with status message from LBOS
2547 */
LBOS_ServiceVersionSet(const char * service,const char * new_version,char ** lbos_answer,char ** http_status_message)2548 unsigned short LBOS_ServiceVersionSet(const char* service,
2549 const char* new_version,
2550 char** lbos_answer,
2551 char** http_status_message)
2552 {
2553 char* service_encoded;
2554 const char* query_format;
2555 char* query;
2556 unsigned short return_code;
2557 /*
2558 * First we check input arguments
2559 */
2560 if (!s_LBOS_CheckConfArgs(service, (const char**)lbos_answer)) {
2561 return eLBOS_InvalidArgs;
2562 }
2563 if (g_LBOS_StringIsNullOrEmpty(new_version)) {
2564 CORE_LOG_X(eLBOS_InvalidArgs, eLOG_Warning,
2565 "LBOS_ServiceVersionSet: new_version is empty. "
2566 "If you want to delete service config, use "
2567 "LBOS_ServiceVersionDelete");
2568 return eLBOS_InvalidArgs;
2569 }
2570 /*
2571 * Check if LBOS is ON
2572 */
2573 if (!s_TurnOn()) {
2574 return eLBOS_Disabled;
2575 }
2576 /*
2577 * Arguments are good! Let's do the request
2578 */
2579 service_encoded = s_LBOS_ModifyServiceName(service);
2580 query_format = "/lbos/v3/conf%s?version=%s&format=xml";
2581 query = (char*)calloc(strlen(query_format) +
2582 strlen(service_encoded) +
2583 strlen(new_version),
2584 sizeof(char));
2585 sprintf(query, query_format, service_encoded, new_version);
2586 return_code = s_LBOS_PerformRequest(query,
2587 lbos_answer,
2588 http_status_message,
2589 eReqMethod_Put,
2590 service);
2591 /*
2592 * Cleanup
2593 */
2594 free(service_encoded);
2595 free(query);
2596 return return_code;
2597 }
2598
2599 /** This service can be used to remove service from configuration. Current
2600 * version will be empty. Previous version shows deleted version.
2601 */
LBOS_ServiceVersionDelete(const char * service,char ** lbos_answer,char ** http_status_message)2602 unsigned short LBOS_ServiceVersionDelete(const char* service,
2603 char** lbos_answer,
2604 char** http_status_message)
2605 {
2606 char* service_encoded;
2607 const char* query_format;
2608 char* query;
2609 unsigned short return_code;
2610 /*
2611 * First we check input arguments
2612 */
2613 if (!s_LBOS_CheckConfArgs(service, (const char**)lbos_answer)) {
2614 return eLBOS_InvalidArgs;
2615 }
2616 /*
2617 * Check if LBOS is ON
2618 */
2619 if (!s_TurnOn()) {
2620 return eLBOS_Disabled;
2621 }
2622 /*
2623 * Arguments are good! Let's do the request
2624 */
2625 service_encoded = s_LBOS_ModifyServiceName(service);
2626 query_format = "/lbos/v3/conf%s?format=xml";
2627 query = (char*)calloc(strlen(query_format) +
2628 strlen(service_encoded),
2629 sizeof(char));
2630 sprintf(query, query_format, service_encoded);
2631 return_code = s_LBOS_PerformRequest(query,
2632 lbos_answer,
2633 http_status_message,
2634 eReqMethod_Delete,
2635 service);
2636 /*
2637 * Cleanup
2638 */
2639 free(service_encoded);
2640 free(query);
2641 return return_code;
2642 }
2643
2644
2645 #undef NCBI_USE_ERRCODE_X
2646
2647