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