| /* |
| * 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); |
| } |