char * get_repmgr_schema_quoted(PGconn *conn) { if (strcmp(repmgr_schema_quoted, "") == 0) { char *identifier = PQescapeIdentifier(conn, repmgr_schema, strlen(repmgr_schema)); maxlen_snprintf(repmgr_schema_quoted, "%s", identifier); PQfreemem(identifier); } return repmgr_schema_quoted; }
/* * get a connection to master by reading repl_nodes, creating a connection * to each node (one at a time) and finding if it is a master or a standby * * NB: If master_conninfo_out may be NULL. If it is non-null, it is assumed to * point to allocated memory of MAXCONNINFO in length, and the master server * connection string is placed there. */ PGconn * getMasterConnection(PGconn *standby_conn, char *schema, char *cluster, int *master_id, char *master_conninfo_out) { PGconn *master_conn = NULL; PGresult *res1; PGresult *res2; char sqlquery[QUERY_STR_LEN]; char master_conninfo_stack[MAXCONNINFO]; char *master_conninfo = &*master_conninfo_stack; char schema_quoted[MAXLEN]; int i; /* * If the caller wanted to get a copy of the connection info string, sub * out the local stack pointer for the pointer passed by the caller. */ if (master_conninfo_out != NULL) master_conninfo = master_conninfo_out; /* * XXX: This is copied in at least two other procedures * * Assemble the unquoted schema name */ { char *identifier = PQescapeIdentifier(standby_conn, schema, strlen(schema)); maxlen_snprintf(schema_quoted, "%s", identifier); PQfreemem(identifier); } /* find all nodes belonging to this cluster */ log_info(_("finding node list for cluster '%s'\n"), cluster); sqlquery_snprintf(sqlquery, "SELECT id, conninfo FROM %s.repl_nodes " " WHERE cluster = '%s' and not witness", schema_quoted, cluster); res1 = PQexec(standby_conn, sqlquery); if (PQresultStatus(res1) != PGRES_TUPLES_OK) { log_err(_("Can't get nodes info: %s\n"), PQerrorMessage(standby_conn)); PQclear(res1); PQfinish(standby_conn); exit(ERR_DB_QUERY); } for (i = 0; i < PQntuples(res1); i++) { /* initialize with the values of the current node being processed */ *master_id = atoi(PQgetvalue(res1, i, 0)); strncpy(master_conninfo, PQgetvalue(res1, i, 1), MAXCONNINFO); log_info(_("checking role of cluster node '%s'\n"), master_conninfo); master_conn = establishDBConnection(master_conninfo, false); if (PQstatus(master_conn) != CONNECTION_OK) continue; /* * Can't use the is_standby() function here because on error that * function closes the connection passed and exits. This still * needs to close master_conn first. */ res2 = PQexec(master_conn, "SELECT pg_is_in_recovery()"); if (PQresultStatus(res2) != PGRES_TUPLES_OK) { log_err(_("Can't get recovery state from this node: %s\n"), PQerrorMessage(master_conn)); PQclear(res2); PQfinish(master_conn); continue; } /* if false, this is the master */ if (strcmp(PQgetvalue(res2, 0, 0), "f") == 0) { PQclear(res2); PQclear(res1); return master_conn; } else { /* if it is a standby clear info */ PQclear(res2); PQfinish(master_conn); *master_id = -1; } } /* If we finish this loop without finding a master then * we doesn't have the info or the master has failed (or we * reached max_connections or superuser_reserved_connections, * anything else I'm missing?). * * Probably we will need to check the error to know if we need * to start failover procedure or just fix some situation on the * standby. */ PQclear(res1); return NULL; }
/* * create_node_record() * * Create an entry in the `repl_nodes` table. * * XXX we should pass the record parameters as a struct. */ bool create_node_record(PGconn *conn, char *action, int node, char *type, int upstream_node, char *cluster_name, char *node_name, char *conninfo, int priority, char *slot_name) { char sqlquery[QUERY_STR_LEN]; char upstream_node_id[MAXLEN]; char slot_name_buf[MAXLEN]; PGresult *res; if (upstream_node == NO_UPSTREAM_NODE) { /* * No explicit upstream node id provided for standby - attempt to * get primary node id */ if (strcmp(type, "standby") == 0) { int primary_node_id = get_master_node_id(conn, cluster_name); maxlen_snprintf(upstream_node_id, "%i", primary_node_id); } else { maxlen_snprintf(upstream_node_id, "%s", "NULL"); } } else { maxlen_snprintf(upstream_node_id, "%i", upstream_node); } if (slot_name != NULL && slot_name[0]) { maxlen_snprintf(slot_name_buf, "'%s'", slot_name); } else { maxlen_snprintf(slot_name_buf, "%s", "NULL"); } /* XXX convert to placeholder query */ sqlquery_snprintf(sqlquery, "INSERT INTO %s.repl_nodes " " (id, type, upstream_node_id, cluster, " " name, conninfo, slot_name, priority) " "VALUES (%i, '%s', %s, '%s', '%s', '%s', %s, %i) ", get_repmgr_schema_quoted(conn), node, type, upstream_node_id, cluster_name, node_name, conninfo, slot_name_buf, priority); if (action != NULL) { log_debug(_("%s: %s\n"), action, sqlquery); } res = PQexec(conn, sqlquery); if (!res || PQresultStatus(res) != PGRES_COMMAND_OK) { log_warning(_("Unable to create node record: %s\n"), PQerrorMessage(conn)); PQclear(res); return false; } PQclear(res); return true; }
/* * Execute a command via ssh on the remote host. * * TODO: implement SSH calls using libssh2. */ bool remote_command(const char *host, const char *user, const char *command, const char *ssh_options, PQExpBufferData *outputbuf) { FILE *fp; char ssh_command[MAXLEN] = ""; PQExpBufferData ssh_host; char output[MAXLEN] = ""; initPQExpBuffer(&ssh_host); if (*user != '\0') { appendPQExpBuffer(&ssh_host, "%s@", user); } appendPQExpBufferStr(&ssh_host, host); maxlen_snprintf(ssh_command, "ssh -o Batchmode=yes %s %s %s", ssh_options, ssh_host.data, command); termPQExpBuffer(&ssh_host); log_debug("remote_command():\n %s", ssh_command); fp = popen(ssh_command, "r"); if (fp == NULL) { log_error(_("unable to execute remote command:\n %s"), ssh_command); return false; } if (outputbuf != NULL) { /* TODO: better error handling */ while (fgets(output, MAXLEN, fp) != NULL) { appendPQExpBufferStr(outputbuf, output); } } else { while (fgets(output, MAXLEN, fp) != NULL) { if (!feof(fp)) { break; } } } pclose(fp); if (outputbuf != NULL) { if (outputbuf->data != NULL && outputbuf->data[0] != '\0') log_verbose(LOG_DEBUG, "remote_command(): output returned was:\n%s", outputbuf->data); else log_verbose(LOG_DEBUG, "remote_command(): no output returned"); } return true; }