blob: 06e952a099597193183d11f2e776ca5cf5949793 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* Utility to contact a segment and issue a primary/mirror mode transition
*/
#include "postmaster/primary_mirror_mode.h"
#include "postmaster/primary_mirror_transition_client.h"
#include <unistd.h>
#include "libpq/pqcomm.h"
#include "libpq/ip.h"
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif
/* buffer size for message to segment */
#define SEGMENT_MSG_BUF_SIZE 4096
/**
* gpmirrortransition builds a message from parameters and transmits it to the given server as
* mirror transition message.
*/
static inline bool
isEmpty(char *str)
{
return str == NULL || str[0] == '\0';
}
static bool
gpCheckForNeedToExitFn(void)
{
return false;
}
static void
gpMirrorErrorLogFunction(char *str)
{
fprintf(stderr, "%s\n", str);
}
static void
gpMirrorReceivedDataCallbackFunction(char *buf)
{
fprintf(stderr, "%s\n", buf);
}
/**
* *addrList will be filled in with the address(es) of the host/port when true is returned
*
* host/port may not be NULL
*/
static bool
determineTargetHost( struct addrinfo **addrList, char *host, char *port)
{
struct addrinfo hint;
int ret;
*addrList = NULL;
/* Initialize hint structure */
MemSet(&hint, 0, sizeof(hint));
hint.ai_socktype = SOCK_STREAM;
hint.ai_family = AF_UNSPEC;
/* Using pghost, so we have to look-up the hostname */
hint.ai_family = AF_UNSPEC;
/* Use pg_getaddrinfo_all() to resolve the address */
ret = pg_getaddrinfo_all(host, port, &hint, addrList);
if (ret || ! *addrList)
{
fprintf(stderr,"could not translate host name \"%s\" to address: %s\n", host, gai_strerror(ret));
return false;
}
return true;
}
static char*
readFully(FILE *f, int *msgLenOut)
{
int bufSize = 10;
char *buf = malloc(bufSize * sizeof(char));
int bufOffset = 0;
if ( buf == NULL )
{
fprintf(stderr, "Out of memory\n");
return NULL;
}
for ( ;; )
{
int numRead;
errno = 0;
numRead = fread(buf + bufOffset, sizeof(char), bufSize - bufOffset, f);
if ( errno != 0 )
{
if ( feof(f))
break;
fprintf( stderr, "Error reading input. Error code %d\n", errno);
return NULL;
}
else if ( numRead <= 0 && feof(f))
break;
bufOffset += numRead;
if ( bufOffset == bufSize )
{
// increase size!
bufSize *= 2;
buf = realloc(buf, bufSize * sizeof(char));
if ( buf == NULL )
{
fprintf(stderr, "Out of memory\n");
return NULL;
}
}
}
*msgLenOut = bufOffset;
return buf;
}
int
main(int argc, char **argv)
{
struct addrinfo *addrList = NULL;
char *host = NULL, *port = NULL, *inputFile = NULL;
char *mode = NULL;
char *status = NULL;
char *seg_addr = NULL;
char *seg_pm_port = NULL;
char *seg_rep_port = NULL;
char *peer_addr = NULL;
char *peer_pm_port = NULL;
char *peer_rep_port = NULL;
char *num_retries_str = NULL;
char *transition_timeout_str = NULL;
int num_retries = 20;
int transition_timeout = 3600; /* 1 hour */
char opt;
char msgBuffer[SEGMENT_MSG_BUF_SIZE];
char *msg = NULL;
int msgLen = 0;
while ((opt = getopt(argc, argv, "m:s:H:P:R:h:p:r:i:n:t:")) != -1)
{
switch (opt)
{
case 'i':
inputFile = optarg;
break;
case 'm':
mode = optarg;
break;
case 's':
status = optarg;
break;
case 'H':
seg_addr = optarg;
break;
case 'P':
seg_pm_port = optarg;
break;
case 'R':
seg_rep_port = optarg;
break;
case 'h':
host = peer_addr = optarg;
break;
case 'p':
port = peer_pm_port = optarg;
break;
case 'r':
peer_rep_port = optarg;
break;
case 'n':
num_retries_str = optarg;
break;
case 't':
transition_timeout_str = optarg;
break;
case '?':
fprintf(stderr, "Unrecognized option: -%c\n", optopt);
}
}
if (num_retries_str != NULL)
{
num_retries = (int) strtol(num_retries_str, NULL, 10);
if (num_retries == 0 || errno == ERANGE)
{
fprintf(stderr, "Invalid num_retries (-n) argument\n");
return TRANS_ERRCODE_ERROR_INVALID_ARGUMENT;
}
}
if (transition_timeout_str != NULL)
{
transition_timeout = (int) strtol (transition_timeout_str, NULL, 10);
if (transition_timeout == 0 || errno == ERANGE)
{
fprintf(stderr, "Invalid transition_timeout (-t) argument\n");
return TRANS_ERRCODE_ERROR_INVALID_ARGUMENT;
}
}
/* check if input file parameter is passed */
if (seg_addr == NULL)
{
if ( host == NULL)
{
fprintf(stderr, "Missing host (-h) argument\n");
return TRANS_ERRCODE_ERROR_INVALID_ARGUMENT;
}
if ( port == NULL )
{
fprintf(stderr, "Missing port (-p) argument\n");
return TRANS_ERRCODE_ERROR_INVALID_ARGUMENT;
}
/* find the target machine */
if ( ! determineTargetHost(&addrList, host, port))
{
return TRANS_ERRCODE_ERROR_HOST_LOOKUP_FAILED;
}
/* load the input message into memory */
if ( inputFile == NULL)
{
msg = readFully(stdin, &msgLen);
}
else
{
FILE *f = fopen(inputFile, "r");
if ( f == NULL)
{
fprintf(stderr, "Unable to open file %s\n", inputFile);
return TRANS_ERRCODE_ERROR_READING_INPUT;
}
msg = readFully(f, &msgLen);
fclose(f);
}
}
else
{
/* build message from passed parameters */
if (mode == NULL)
{
fprintf(stderr, "Missing mode (-m) argument\n");
return TRANS_ERRCODE_ERROR_INVALID_ARGUMENT;
}
if (status == NULL)
{
fprintf(stderr, "Missing status (-s) argument\n");
return TRANS_ERRCODE_ERROR_INVALID_ARGUMENT;
}
if (seg_addr == NULL)
{
fprintf(stderr, "Missing segment host (-H) argument\n");
return TRANS_ERRCODE_ERROR_INVALID_ARGUMENT;
}
if (seg_pm_port == NULL)
{
fprintf(stderr, "Missing segment postmaster port (-P) argument\n");
return TRANS_ERRCODE_ERROR_INVALID_ARGUMENT;
}
if (seg_rep_port == NULL)
{
fprintf(stderr, "Missing segment replication port (-R) argument\n");
return TRANS_ERRCODE_ERROR_INVALID_ARGUMENT;
}
if (peer_addr == NULL)
{
fprintf(stderr, "Missing peer host (-h) argument\n");
return TRANS_ERRCODE_ERROR_INVALID_ARGUMENT;
}
if (peer_pm_port == NULL)
{
fprintf(stderr, "Missing peer postmaster port (-p) argument\n");
return TRANS_ERRCODE_ERROR_INVALID_ARGUMENT;
}
if (peer_rep_port == NULL)
{
fprintf(stderr, "Missing peer replication port (-r) argument\n");
return TRANS_ERRCODE_ERROR_INVALID_ARGUMENT;
}
/* build message */
msgLen = snprintf(
msgBuffer, sizeof(msgBuffer),
"%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
mode,
status,
seg_addr,
seg_rep_port,
peer_addr,
peer_rep_port,
peer_pm_port
);
msg = msgBuffer;
/* find the target machine */
if (!determineTargetHost(&addrList, seg_addr, seg_pm_port))
{
return TRANS_ERRCODE_ERROR_HOST_LOOKUP_FAILED;
}
}
/* check for errors while building the message */
if ( msg == NULL )
{
return TRANS_ERRCODE_ERROR_READING_INPUT;
}
/* send the message */
PrimaryMirrorTransitionClientInfo client;
client.receivedDataCallbackFn = gpMirrorReceivedDataCallbackFunction;
client.errorLogFn = gpMirrorErrorLogFunction;
client.checkForNeedToExitFn = gpCheckForNeedToExitFn;
return sendTransitionMessage(&client, addrList, msg, msgLen, num_retries, transition_timeout);
}