1 /***********************************************************************************************************************************
2 Protocol Helper
3 ***********************************************************************************************************************************/
4 #include "build.auto.h"
5
6 #include <string.h>
7
8 #include "common/crypto/common.h"
9 #include "common/debug.h"
10 #include "common/exec.h"
11 #include "common/memContext.h"
12 #include "config/config.intern.h"
13 #include "config/exec.h"
14 #include "config/parse.h"
15 #include "config/protocol.h"
16 #include "postgres/version.h"
17 #include "protocol/helper.h"
18 #include "version.h"
19
20 /***********************************************************************************************************************************
21 Constants
22 ***********************************************************************************************************************************/
23 STRING_EXTERN(PROTOCOL_SERVICE_LOCAL_STR, PROTOCOL_SERVICE_LOCAL);
24 STRING_EXTERN(PROTOCOL_SERVICE_REMOTE_STR, PROTOCOL_SERVICE_REMOTE);
25
26 /***********************************************************************************************************************************
27 Local variables
28 ***********************************************************************************************************************************/
29 typedef struct ProtocolHelperClient
30 {
31 Exec *exec; // Executed client
32 ProtocolClient *client; // Protocol client
33 } ProtocolHelperClient;
34
35 static struct
36 {
37 MemContext *memContext; // Mem context for protocol helper
38
39 unsigned int clientRemoteSize; // Remote clients
40 ProtocolHelperClient *clientRemote;
41
42 unsigned int clientLocalSize; // Local clients
43 ProtocolHelperClient *clientLocal;
44 } protocolHelper;
45
46 /***********************************************************************************************************************************
47 Init local mem context and data structure
48 ***********************************************************************************************************************************/
49 static void
protocolHelperInit(void)50 protocolHelperInit(void)
51 {
52 // In the protocol helper has not been initialized
53 if (protocolHelper.memContext == NULL)
54 {
55 // Create a mem context to store protocol objects
56 MEM_CONTEXT_BEGIN(memContextTop())
57 {
58 MEM_CONTEXT_NEW_BEGIN("ProtocolHelper")
59 {
60 protocolHelper.memContext = MEM_CONTEXT_NEW();
61 }
62 MEM_CONTEXT_NEW_END();
63 }
64 MEM_CONTEXT_END();
65 }
66 }
67
68 /**********************************************************************************************************************************/
69 bool
repoIsLocal(unsigned int repoIdx)70 repoIsLocal(unsigned int repoIdx)
71 {
72 FUNCTION_LOG_BEGIN(logLevelDebug);
73 FUNCTION_LOG_PARAM(UINT, repoIdx);
74 FUNCTION_LOG_END();
75
76 FUNCTION_LOG_RETURN(BOOL, !cfgOptionIdxTest(cfgOptRepoHost, repoIdx));
77 }
78
79 /**********************************************************************************************************************************/
80 void
repoIsLocalVerify(void)81 repoIsLocalVerify(void)
82 {
83 FUNCTION_TEST_VOID();
84
85 repoIsLocalVerifyIdx(cfgOptionGroupIdxDefault(cfgOptGrpRepo));
86
87 FUNCTION_TEST_RETURN_VOID();
88 }
89
90 /**********************************************************************************************************************************/
91 void
repoIsLocalVerifyIdx(unsigned int repoIdx)92 repoIsLocalVerifyIdx(unsigned int repoIdx)
93 {
94 FUNCTION_TEST_VOID();
95
96 if (!repoIsLocal(repoIdx))
97 THROW_FMT(HostInvalidError, "%s command must be run on the repository host", cfgCommandName());
98
99 FUNCTION_TEST_RETURN_VOID();
100 }
101
102 /**********************************************************************************************************************************/
103 bool
pgIsLocal(unsigned int pgIdx)104 pgIsLocal(unsigned int pgIdx)
105 {
106 FUNCTION_LOG_BEGIN(logLevelDebug);
107 FUNCTION_LOG_PARAM(UINT, pgIdx);
108 FUNCTION_LOG_END();
109
110 FUNCTION_LOG_RETURN(BOOL, !cfgOptionIdxTest(cfgOptPgHost, pgIdx));
111 }
112
113 /**********************************************************************************************************************************/
114 void
pgIsLocalVerify(void)115 pgIsLocalVerify(void)
116 {
117 FUNCTION_TEST_VOID();
118
119 if (!pgIsLocal(cfgOptionGroupIdxDefault(cfgOptGrpPg)))
120 THROW_FMT(HostInvalidError, "%s command must be run on the " PG_NAME " host", cfgCommandName());
121
122 FUNCTION_TEST_RETURN_VOID();
123 }
124
125 /***********************************************************************************************************************************
126 Get the command line required for local protocol execution
127 ***********************************************************************************************************************************/
128 static StringList *
protocolLocalParam(ProtocolStorageType protocolStorageType,unsigned int hostIdx,unsigned int processId)129 protocolLocalParam(ProtocolStorageType protocolStorageType, unsigned int hostIdx, unsigned int processId)
130 {
131 FUNCTION_LOG_BEGIN(logLevelDebug);
132 FUNCTION_LOG_PARAM(STRING_ID, protocolStorageType);
133 FUNCTION_LOG_PARAM(UINT, hostIdx);
134 FUNCTION_LOG_PARAM(UINT, processId);
135 FUNCTION_LOG_END();
136
137 StringList *result = NULL;
138
139 MEM_CONTEXT_TEMP_BEGIN()
140 {
141 // Option replacements
142 KeyValue *optionReplace = kvNew();
143
144 // Add the process id -- used when more than one process will be called
145 kvPut(optionReplace, VARSTRDEF(CFGOPT_PROCESS), VARUINT(processId));
146
147 // Add the pg default. Don't do this for repos because the repo default should come from the user or the local should
148 // handle all the repos equally. Repos don't get special handling like pg primaries or standbys.
149 if (protocolStorageType == protocolStorageTypePg)
150 kvPut(optionReplace, VARSTRDEF(CFGOPT_PG), VARUINT(cfgOptionGroupIdxToKey(cfgOptGrpPg, hostIdx)));
151
152 // Add the remote type
153 kvPut(optionReplace, VARSTRDEF(CFGOPT_REMOTE_TYPE), VARSTR(strIdToStr(protocolStorageType)));
154
155 // Only enable file logging on the local when requested
156 kvPut(
157 optionReplace, VARSTRDEF(CFGOPT_LOG_LEVEL_FILE),
158 cfgOptionBool(cfgOptLogSubprocess) ? cfgOption(cfgOptLogLevelFile) : VARSTRDEF("off"));
159
160 // Always output errors on stderr for debugging purposes
161 kvPut(optionReplace, VARSTRDEF(CFGOPT_LOG_LEVEL_STDERR), VARSTRDEF("error"));
162
163 // Disable output to stdout since it is used by the protocol
164 kvPut(optionReplace, VARSTRDEF(CFGOPT_LOG_LEVEL_CONSOLE), VARSTRDEF("off"));
165
166 result = strLstMove(cfgExecParam(cfgCommand(), cfgCmdRoleLocal, optionReplace, true, false), memContextPrior());
167 }
168 MEM_CONTEXT_TEMP_END();
169
170 FUNCTION_LOG_RETURN(STRING_LIST, result);
171 }
172
173 /**********************************************************************************************************************************/
174 // Helper to execute the local process. This is a separate function solely so that it can be shimmed during testing.
175 static void
protocolLocalExec(ProtocolHelperClient * helper,ProtocolStorageType protocolStorageType,unsigned int hostIdx,unsigned int processId)176 protocolLocalExec(
177 ProtocolHelperClient *helper, ProtocolStorageType protocolStorageType, unsigned int hostIdx, unsigned int processId)
178 {
179 FUNCTION_TEST_BEGIN();
180 FUNCTION_TEST_PARAM_P(VOID, helper);
181 FUNCTION_TEST_PARAM(ENUM, protocolStorageType);
182 FUNCTION_TEST_PARAM(UINT, hostIdx);
183 FUNCTION_TEST_PARAM(UINT, processId);
184 FUNCTION_TEST_END();
185
186 ASSERT(helper != NULL);
187
188 // Execute the protocol command
189 helper->exec = execNew(
190 cfgExe(), protocolLocalParam(protocolStorageType, hostIdx, processId),
191 strNewFmt(PROTOCOL_SERVICE_LOCAL "-%u process", processId), cfgOptionUInt64(cfgOptProtocolTimeout));
192 execOpen(helper->exec);
193
194 // Create protocol object
195 helper->client = protocolClientNew(
196 strNewFmt(PROTOCOL_SERVICE_LOCAL "-%u protocol", processId),
197 PROTOCOL_SERVICE_LOCAL_STR, execIoRead(helper->exec), execIoWrite(helper->exec));
198
199 // Move client to exec context so they are freed together
200 protocolClientMove(helper->client, execMemContext(helper->exec));
201
202 FUNCTION_TEST_RETURN_VOID();
203 }
204
205 ProtocolClient *
protocolLocalGet(ProtocolStorageType protocolStorageType,unsigned int hostIdx,unsigned int processId)206 protocolLocalGet(ProtocolStorageType protocolStorageType, unsigned int hostIdx, unsigned int processId)
207 {
208 FUNCTION_LOG_BEGIN(logLevelDebug);
209 FUNCTION_LOG_PARAM(STRING_ID, protocolStorageType);
210 FUNCTION_LOG_PARAM(UINT, hostIdx);
211 FUNCTION_LOG_PARAM(UINT, processId);
212 FUNCTION_LOG_END();
213
214 protocolHelperInit();
215
216 // Allocate the client cache
217 if (protocolHelper.clientLocalSize == 0)
218 {
219 MEM_CONTEXT_BEGIN(protocolHelper.memContext)
220 {
221 protocolHelper.clientLocalSize = cfgOptionUInt(cfgOptProcessMax) + 1;
222 protocolHelper.clientLocal = memNew(protocolHelper.clientLocalSize * sizeof(ProtocolHelperClient));
223
224 for (unsigned int clientIdx = 0; clientIdx < protocolHelper.clientLocalSize; clientIdx++)
225 protocolHelper.clientLocal[clientIdx] = (ProtocolHelperClient){.exec = NULL};
226 }
227 MEM_CONTEXT_END();
228 }
229
230 ASSERT(processId <= protocolHelper.clientLocalSize);
231
232 // Create protocol object
233 ProtocolHelperClient *protocolHelperClient = &protocolHelper.clientLocal[processId - 1];
234
235 if (protocolHelperClient->client == NULL)
236 {
237 MEM_CONTEXT_BEGIN(protocolHelper.memContext)
238 {
239 protocolLocalExec(protocolHelperClient, protocolStorageType, hostIdx, processId);
240 }
241 MEM_CONTEXT_END();
242
243 // Send noop to catch initialization errors
244 protocolClientNoOp(protocolHelperClient->client);
245 }
246
247 FUNCTION_LOG_RETURN(PROTOCOL_CLIENT, protocolHelperClient->client);
248 }
249
250 /***********************************************************************************************************************************
251 Free the protocol client and underlying exec'd process. Log any errors as warnings since it is not worth terminating the process
252 while closing a local/remote that has already completed its work. The warning will be an indication that something is not right.
253 ***********************************************************************************************************************************/
254 static void
protocolHelperClientFree(ProtocolHelperClient * protocolHelperClient)255 protocolHelperClientFree(ProtocolHelperClient *protocolHelperClient)
256 {
257 FUNCTION_LOG_BEGIN(logLevelTrace);
258 FUNCTION_LOG_PARAM_P(VOID, protocolHelperClient);
259 FUNCTION_LOG_END();
260
261 if (protocolHelperClient->client != NULL)
262 {
263 // Try to shutdown the protocol but only warn on error
264 TRY_BEGIN()
265 {
266 protocolClientFree(protocolHelperClient->client);
267 }
268 CATCH_ANY()
269 {
270 LOG_WARN(errorMessage());
271 }
272 TRY_END();
273
274 // Try to end the child process but only warn on error
275 TRY_BEGIN()
276 {
277 execFree(protocolHelperClient->exec);
278 }
279 CATCH_ANY()
280 {
281 LOG_WARN(errorMessage());
282 }
283 TRY_END();
284
285 protocolHelperClient->client = NULL;
286 protocolHelperClient->exec = NULL;
287 }
288
289 FUNCTION_LOG_RETURN_VOID();
290 }
291
292 /**********************************************************************************************************************************/
293 void
protocolLocalFree(unsigned int processId)294 protocolLocalFree(unsigned int processId)
295 {
296 FUNCTION_LOG_BEGIN(logLevelDebug);
297 FUNCTION_LOG_PARAM(UINT, processId);
298 FUNCTION_LOG_END();
299
300 if (protocolHelper.clientLocal != NULL)
301 {
302 ASSERT(processId <= protocolHelper.clientLocalSize);
303 protocolHelperClientFree(&protocolHelper.clientLocal[processId - 1]);
304 }
305
306 FUNCTION_LOG_RETURN_VOID();
307 }
308
309 /***********************************************************************************************************************************
310 Get the command line required for remote protocol execution
311 ***********************************************************************************************************************************/
312 static StringList *
protocolRemoteParam(ProtocolStorageType protocolStorageType,unsigned int hostIdx)313 protocolRemoteParam(ProtocolStorageType protocolStorageType, unsigned int hostIdx)
314 {
315 FUNCTION_LOG_BEGIN(logLevelDebug);
316 FUNCTION_LOG_PARAM(STRING_ID, protocolStorageType);
317 FUNCTION_LOG_PARAM(UINT, hostIdx);
318 FUNCTION_LOG_END();
319
320 // Is this a repo remote?
321 bool isRepo = protocolStorageType == protocolStorageTypeRepo;
322
323 // Option replacements
324 KeyValue *optionReplace = kvNew();
325
326 // Replace config options with the host versions
327 unsigned int optConfig = isRepo ? cfgOptRepoHostConfig : cfgOptPgHostConfig;
328
329 kvPut(
330 optionReplace, VARSTRDEF(CFGOPT_CONFIG),
331 cfgOptionIdxSource(optConfig, hostIdx) != cfgSourceDefault ? VARSTR(cfgOptionIdxStr(optConfig, hostIdx)) : NULL);
332
333 unsigned int optConfigIncludePath = isRepo ? cfgOptRepoHostConfigIncludePath : cfgOptPgHostConfigIncludePath;
334
335 kvPut(
336 optionReplace, VARSTRDEF(CFGOPT_CONFIG_INCLUDE_PATH),
337 cfgOptionIdxSource(optConfigIncludePath, hostIdx) != cfgSourceDefault ?
338 VARSTR(cfgOptionIdxStr(optConfigIncludePath, hostIdx)) : NULL);
339
340 unsigned int optConfigPath = isRepo ? cfgOptRepoHostConfigPath : cfgOptPgHostConfigPath;
341
342 kvPut(
343 optionReplace, VARSTRDEF(CFGOPT_CONFIG_PATH),
344 cfgOptionIdxSource(optConfigPath, hostIdx) != cfgSourceDefault ? VARSTR(cfgOptionIdxStr(optConfigPath, hostIdx)) : NULL);
345
346 // Update/remove repo/pg options that are sent to the remote
347 for (ConfigOption optionId = 0; optionId < CFG_OPTION_TOTAL; optionId++)
348 {
349 // Skip options that are not part of a group
350 if (!cfgOptionGroup(optionId))
351 continue;
352
353 bool remove = false;
354 bool skipHostZero = false;
355
356 // Remove repo options that are not needed on the remote
357 if (cfgOptionGroupId(optionId) == cfgOptGrpRepo)
358 {
359 // If remote type is pg then remove all repo options since they will not be used
360 if (protocolStorageType == protocolStorageTypePg)
361 {
362 remove = true;
363 }
364 // Else remove repo options for indexes that are not being passed to the repo. This prevents the remote from getting
365 // partial info about a repo, which could cause an error during validation.
366 else
367 {
368 for (unsigned int optionIdx = 0; optionIdx < cfgOptionIdxTotal(optionId); optionIdx++)
369 {
370 if (cfgOptionIdxTest(optionId, optionIdx) && optionIdx != hostIdx)
371 kvPut(optionReplace, VARSTRZ(cfgOptionIdxName(optionId, optionIdx)), NULL);
372 }
373 }
374 }
375 // Remove pg options that are not needed on the remote
376 else
377 {
378 ASSERT(cfgOptionGroupId(optionId) == cfgOptGrpPg);
379
380 // Remove unrequired/defaulted pg options when the remote type is repo since they won't be used
381 if (protocolStorageType == protocolStorageTypeRepo)
382 {
383 remove = !cfgParseOptionRequired(cfgCommand(), optionId) || cfgParseOptionDefault(cfgCommand(), optionId) != NULL;
384 }
385 // Move pg options to host index 0 (key 1) so they will be in the default index on the remote host
386 else
387 {
388 if (hostIdx != 0)
389 {
390 kvPut(
391 optionReplace, VARSTRZ(cfgOptionIdxName(optionId, 0)),
392 cfgOptionIdxSource(optionId, hostIdx) != cfgSourceDefault ? cfgOptionIdx(optionId, hostIdx) : NULL);
393 }
394
395 remove = true;
396 skipHostZero = true;
397 }
398 }
399
400 // Remove options that have been marked for removal if they are not already null or invalid. This is more efficient because
401 // cfgExecParam() won't have to search through as large a list looking for overrides.
402 if (remove)
403 {
404 // Loop through option indexes
405 for (unsigned int optionIdx = 0; optionIdx < cfgOptionIdxTotal(optionId); optionIdx++)
406 {
407 if (cfgOptionIdxTest(optionId, optionIdx) && !(skipHostZero && optionIdx == 0))
408 kvPut(optionReplace, VARSTRZ(cfgOptionIdxName(optionId, optionIdx)), NULL);
409 }
410 }
411 }
412
413 // Set repo default so the remote only operates on a single repo
414 if (protocolStorageType == protocolStorageTypeRepo)
415 kvPut(optionReplace, VARSTRDEF(CFGOPT_REPO), VARUINT(cfgOptionGroupIdxToKey(cfgOptGrpRepo, hostIdx)));
416
417 // Add the process id if not set. This means that the remote is being started from the main process and should always get a
418 // process id of 0.
419 if (!cfgOptionTest(cfgOptProcess))
420 kvPut(optionReplace, VARSTRDEF(CFGOPT_PROCESS), VARINT(0));
421
422 // Don't pass log-path or lock-path since these are host specific
423 kvPut(optionReplace, VARSTRDEF(CFGOPT_LOG_PATH), NULL);
424 kvPut(optionReplace, VARSTRDEF(CFGOPT_LOCK_PATH), NULL);
425
426 // Only enable file logging on the remote when requested
427 kvPut(
428 optionReplace, VARSTRDEF(CFGOPT_LOG_LEVEL_FILE),
429 cfgOptionBool(cfgOptLogSubprocess) ? cfgOption(cfgOptLogLevelFile) : VARSTRDEF("off"));
430
431 // Always output errors on stderr for debugging purposes
432 kvPut(optionReplace, VARSTRDEF(CFGOPT_LOG_LEVEL_STDERR), VARSTRDEF("error"));
433
434 // Disable output to stdout since it is used by the protocol
435 kvPut(optionReplace, VARSTRDEF(CFGOPT_LOG_LEVEL_CONSOLE), VARSTRDEF("off"));
436
437 // Add the remote type
438 kvPut(optionReplace, VARSTRDEF(CFGOPT_REMOTE_TYPE), VARSTR(strIdToStr(protocolStorageType)));
439
440 FUNCTION_LOG_RETURN(STRING_LIST, cfgExecParam(cfgCommand(), cfgCmdRoleRemote, optionReplace, false, true));
441 }
442
443 // Helper to add SSH parameters when executing the remote via SSH
444 static StringList *
protocolRemoteParamSsh(const ProtocolStorageType protocolStorageType,const unsigned int hostIdx)445 protocolRemoteParamSsh(const ProtocolStorageType protocolStorageType, const unsigned int hostIdx)
446 {
447 FUNCTION_LOG_BEGIN(logLevelDebug);
448 FUNCTION_LOG_PARAM(STRING_ID, protocolStorageType);
449 FUNCTION_LOG_PARAM(UINT, hostIdx);
450 FUNCTION_LOG_END();
451
452 StringList *result = NULL;
453
454 MEM_CONTEXT_TEMP_BEGIN()
455 {
456 // Is this a repo remote?
457 bool isRepo = protocolStorageType == protocolStorageTypeRepo;
458
459 // Fixed parameters for ssh command
460 result = strLstNew();
461 strLstAddZ(result, "-o");
462 strLstAddZ(result, "LogLevel=error");
463 strLstAddZ(result, "-o");
464 strLstAddZ(result, "Compression=no");
465 strLstAddZ(result, "-o");
466 strLstAddZ(result, "PasswordAuthentication=no");
467
468 // Append port if specified
469 ConfigOption optHostPort = isRepo ? cfgOptRepoHostPort : cfgOptPgHostPort;
470
471 if (cfgOptionIdxTest(optHostPort, hostIdx))
472 {
473 strLstAddZ(result, "-p");
474 strLstAdd(result, strNewFmt("%u", cfgOptionIdxUInt(optHostPort, hostIdx)));
475 }
476
477 // Append user/host
478 strLstAdd(
479 result,
480 strNewFmt(
481 "%s@%s", strZ(cfgOptionIdxStr(isRepo ? cfgOptRepoHostUser : cfgOptPgHostUser, hostIdx)),
482 strZ(cfgOptionIdxStr(isRepo ? cfgOptRepoHost : cfgOptPgHost, hostIdx))));
483
484 // Add remote command and parameters
485 StringList *paramList = protocolRemoteParam(protocolStorageType, hostIdx);
486
487 strLstInsert(paramList, 0, cfgOptionIdxStr(isRepo ? cfgOptRepoHostCmd : cfgOptPgHostCmd, hostIdx));
488 strLstAdd(result, strLstJoin(paramList, " "));
489
490 // Move to prior context
491 strLstMove(result, memContextPrior());
492 }
493 MEM_CONTEXT_TEMP_END();
494
495 FUNCTION_LOG_RETURN(STRING_LIST, result);
496 }
497
498 /**********************************************************************************************************************************/
499 // Helper to execute the local process. This is a separate function solely so that it can be shimmed during testing.
500 static void
protocolRemoteExec(ProtocolHelperClient * const helper,const ProtocolStorageType protocolStorageType,const unsigned int hostIdx,const unsigned int processId)501 protocolRemoteExec(
502 ProtocolHelperClient *const helper, const ProtocolStorageType protocolStorageType, const unsigned int hostIdx,
503 const unsigned int processId)
504 {
505 FUNCTION_TEST_BEGIN();
506 FUNCTION_TEST_PARAM_P(VOID, helper);
507 FUNCTION_TEST_PARAM(ENUM, protocolStorageType);
508 FUNCTION_TEST_PARAM(UINT, hostIdx);
509 FUNCTION_TEST_PARAM(UINT, processId);
510 FUNCTION_TEST_END();
511
512 ASSERT(helper != NULL);
513
514 // Execute the protocol command
515 const char *const host =
516 strZ(cfgOptionIdxStr(protocolStorageType == protocolStorageTypeRepo ? cfgOptRepoHost : cfgOptPgHost, hostIdx));
517
518 helper->exec = execNew(
519 cfgOptionStr(cfgOptCmdSsh), protocolRemoteParamSsh(protocolStorageType, hostIdx),
520 strNewFmt(PROTOCOL_SERVICE_REMOTE "-%u process on '%s'", processId, host), cfgOptionUInt64(cfgOptProtocolTimeout));
521 execOpen(helper->exec);
522
523 // Create protocol object
524 helper->client = protocolClientNew(
525 strNewFmt(PROTOCOL_SERVICE_REMOTE "-%u protocol on '%s'", processId, host), PROTOCOL_SERVICE_REMOTE_STR,
526 execIoRead(helper->exec), execIoWrite(helper->exec));
527
528 // Move client to exec context so they are freed together
529 protocolClientMove(helper->client, execMemContext(helper->exec));
530
531 FUNCTION_TEST_RETURN_VOID();
532 }
533
534 ProtocolClient *
protocolRemoteGet(ProtocolStorageType protocolStorageType,unsigned int hostIdx)535 protocolRemoteGet(ProtocolStorageType protocolStorageType, unsigned int hostIdx)
536 {
537 FUNCTION_LOG_BEGIN(logLevelDebug);
538 FUNCTION_LOG_PARAM(STRING_ID, protocolStorageType);
539 FUNCTION_LOG_PARAM(UINT, hostIdx);
540 FUNCTION_LOG_END();
541
542 // Is this a repo remote?
543 bool isRepo = protocolStorageType == protocolStorageTypeRepo;
544
545 protocolHelperInit();
546
547 // Allocate the client cache
548 if (protocolHelper.clientRemoteSize == 0)
549 {
550 MEM_CONTEXT_BEGIN(protocolHelper.memContext)
551 {
552 protocolHelper.clientRemoteSize = cfgOptionGroupIdxTotal(isRepo ? cfgOptGrpRepo : cfgOptGrpPg) + 1;
553 protocolHelper.clientRemote = memNew(protocolHelper.clientRemoteSize * sizeof(ProtocolHelperClient));
554
555 for (unsigned int clientIdx = 0; clientIdx < protocolHelper.clientRemoteSize; clientIdx++)
556 protocolHelper.clientRemote[clientIdx] = (ProtocolHelperClient){.exec = NULL};
557 }
558 MEM_CONTEXT_END();
559 }
560
561 // Determine protocol id for the remote. If the process option is set then use that since we want the remote protocol id to
562 // match the local protocol id. Otherwise set to 0 since the remote is being started from a main process and there should only
563 // be one remote per host.
564 unsigned int processId = 0;
565
566 if (cfgOptionTest(cfgOptProcess))
567 processId = cfgOptionUInt(cfgOptProcess);
568
569 CHECK(hostIdx < protocolHelper.clientRemoteSize);
570
571 // Create protocol object
572 ProtocolHelperClient *protocolHelperClient = &protocolHelper.clientRemote[hostIdx];
573
574 if (protocolHelperClient->client == NULL)
575 {
576 MEM_CONTEXT_BEGIN(protocolHelper.memContext)
577 {
578 protocolRemoteExec(protocolHelperClient, protocolStorageType, hostIdx, processId);
579
580 // Send noop to catch initialization errors
581 protocolClientNoOp(protocolHelperClient->client);
582
583 // Get cipher options from the remote if none are locally configured
584 if (isRepo && cfgOptionIdxStrId(cfgOptRepoCipherType, hostIdx) == cipherTypeNone)
585 {
586 // Options to query
587 VariantList *param = varLstNew();
588 varLstAdd(param, varNewStrZ(cfgOptionIdxName(cfgOptRepoCipherType, hostIdx)));
589 varLstAdd(param, varNewStrZ(cfgOptionIdxName(cfgOptRepoCipherPass, hostIdx)));
590
591 VariantList *optionList = configOptionRemote(protocolHelperClient->client, param);
592
593 if (!strEq(varStr(varLstGet(optionList, 0)), strIdToStr(cipherTypeNone)))
594 {
595 cfgOptionIdxSet(cfgOptRepoCipherType, hostIdx, cfgSourceConfig, varLstGet(optionList, 0));
596 cfgOptionIdxSet(cfgOptRepoCipherPass, hostIdx, cfgSourceConfig, varLstGet(optionList, 1));
597 }
598 }
599 }
600 MEM_CONTEXT_END();
601 }
602
603 FUNCTION_LOG_RETURN(PROTOCOL_CLIENT, protocolHelperClient->client);
604 }
605
606 /**********************************************************************************************************************************/
607 void
protocolRemoteFree(unsigned int hostIdx)608 protocolRemoteFree(unsigned int hostIdx)
609 {
610 FUNCTION_LOG_BEGIN(logLevelDebug);
611 FUNCTION_LOG_PARAM(UINT, hostIdx);
612 FUNCTION_LOG_END();
613
614 if (protocolHelper.clientRemote != NULL)
615 protocolHelperClientFree(&protocolHelper.clientRemote[hostIdx]);
616
617 FUNCTION_LOG_RETURN_VOID();
618 }
619
620 /**********************************************************************************************************************************/
621 void
protocolKeepAlive(void)622 protocolKeepAlive(void)
623 {
624 FUNCTION_LOG_VOID(logLevelTrace);
625
626 if (protocolHelper.memContext != NULL)
627 {
628 for (unsigned int clientIdx = 0; clientIdx < protocolHelper.clientRemoteSize; clientIdx++)
629 {
630 if (protocolHelper.clientRemote[clientIdx].client != NULL)
631 protocolClientNoOp(protocolHelper.clientRemote[clientIdx].client);
632 }
633 }
634
635 FUNCTION_LOG_RETURN_VOID();
636 }
637
638 /**********************************************************************************************************************************/
639 void
protocolFree(void)640 protocolFree(void)
641 {
642 FUNCTION_LOG_VOID(logLevelTrace);
643
644 if (protocolHelper.memContext != NULL)
645 {
646 // Free remotes
647 for (unsigned int clientIdx = 0; clientIdx < protocolHelper.clientRemoteSize; clientIdx++)
648 protocolRemoteFree(clientIdx);
649
650 // Free locals
651 for (unsigned int clientIdx = 1; clientIdx <= protocolHelper.clientLocalSize; clientIdx++)
652 protocolLocalFree(clientIdx);
653 }
654
655 FUNCTION_LOG_RETURN_VOID();
656 }
657