/* * check_pghost_envvar() * * Tests that PGHOST does not point to a non-local server */ void check_pghost_envvar(void) { PQconninfoOption *option; PQconninfoOption *start; /* Get valid libpq env vars from the PQconndefaults function */ start = PQconndefaults(); if (!start) pg_fatal("out of memory\n"); for (option = start; option->keyword != NULL; option++) { if (option->envvar && (strcmp(option->envvar, "PGHOST") == 0 || strcmp(option->envvar, "PGHOSTADDR") == 0)) { const char *value = getenv(option->envvar); if (value && strlen(value) > 0 && /* check for 'local' host values */ (strcmp(value, "localhost") != 0 && strcmp(value, "127.0.0.1") != 0 && strcmp(value, "::1") != 0 && value[0] != '/')) pg_fatal("libpq environment variable %s has a non-local server value: %s\n", option->envvar, value); } } /* Free the memory that libpq allocated on our behalf */ PQconninfoFree(start); }
static PyObject * psyco_conn_get_dsn_parameters(connectionObject *self) { #if PG_VERSION_NUM >= 90300 PyObject *res = NULL; PQconninfoOption *options = NULL; EXC_IF_CONN_CLOSED(self); if (!(options = PQconninfo(self->pgconn))) { PyErr_NoMemory(); goto exit; } res = psycopg_dict_from_conninfo_options(options, /* include_password = */ 0); exit: PQconninfoFree(options); return res; #else PyErr_SetString(NotSupportedError, "PQconninfo not available in libpq < 9.3"); return NULL; #endif }
/* * check_for_libpq_envvars() * * tests whether any libpq environment variables are set. * Since pg_upgrade connects to both the old and the new server, * it is potentially dangerous to have any of these set. * * If any are found, will log them and cancel. */ void check_for_libpq_envvars(void) { PQconninfoOption *option; PQconninfoOption *start; bool found = false; /* Get valid libpq env vars from the PQconndefaults function */ start = option = PQconndefaults(); while (option->keyword != NULL) { const char *value; if (option->envvar && (value = getenv(option->envvar)) && strlen(value) > 0) { found = true; pg_log(PG_WARNING, "libpq env var %-20s is currently set to: %s\n", option->envvar, value); } option++; } /* Free the memory that libpq allocated on our behalf */ PQconninfoFree(start); if (found) pg_log(PG_FATAL, "libpq env vars have been found and listed above, please unset them for pg_upgrade\n"); }
int Pg_conndefaults(ClientData cData, Tcl_Interp *interp, int argc, CONST84 char *argv[]) { PQconninfoOption *options = PQconndefaults(); PQconninfoOption *option; Tcl_DString result; char ibuf[32]; if (options) { Tcl_DStringInit(&result); for (option = options; option->keyword != NULL; option++) { char *val = option->val ? option->val : ""; sprintf(ibuf, "%d", option->dispsize); Tcl_DStringStartSublist(&result); Tcl_DStringAppendElement(&result, option->keyword); Tcl_DStringAppendElement(&result, option->label); Tcl_DStringAppendElement(&result, option->dispchar); Tcl_DStringAppendElement(&result, ibuf); Tcl_DStringAppendElement(&result, val); Tcl_DStringEndSublist(&result); } Tcl_DStringResult(interp, &result); PQconninfoFree(options); } return TCL_OK; }
void pgConn::ExamineLibpqVersion() { libpqVersion = 7.3; PQconninfoOption *cio = PQconndefaults(); if (cio) { PQconninfoOption *co = cio; while (co->keyword) { if (!strcmp(co->keyword, "sslmode")) { if (libpqVersion < 7.4) libpqVersion = 7.4; } if (!strcmp(co->keyword, "sslrootcert")) { if (libpqVersion < 8.4) libpqVersion = 8.4; } if (!strcmp(co->keyword, "sslcompression")) { if (libpqVersion < 9.2) libpqVersion = 9.2; } co++; } PQconninfoFree(cio); } }
static PyObject * psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs) { char *err = NULL; PQconninfoOption *options = NULL; PyObject *res = NULL, *dsn; static char *kwlist[] = {"dsn", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &dsn)) { return NULL; } Py_INCREF(dsn); /* for ensure_bytes */ if (!(dsn = psycopg_ensure_bytes(dsn))) { goto exit; } options = PQconninfoParse(Bytes_AS_STRING(dsn), &err); if (options == NULL) { if (err != NULL) { PyErr_Format(ProgrammingError, "invalid dsn: %s", err); PQfreemem(err); } else { PyErr_SetString(OperationalError, "PQconninfoParse() failed"); } goto exit; } res = psycopg_dict_from_conninfo_options(options, /* include_password = */ 1); exit: PQconninfoFree(options); /* safe on null */ Py_XDECREF(dsn); return res; }
/* Establishes two network connections to a Postgres server: one for SQL, and one * for replication. context->conninfo contains the connection string or URL to connect * to, and context->app_name is the client name (which appears, for example, in * pg_stat_activity). Returns 0 on success. */ int client_connect(client_context_t context) { if (!context->conninfo || context->conninfo[0] == '\0') { client_error(context, "conninfo must be set in client context"); return EINVAL; } if (!context->app_name || context->app_name[0] == '\0') { client_error(context, "app_name must be set in client context"); return EINVAL; } context->sql_conn = PQconnectdb(context->conninfo); if (PQstatus(context->sql_conn) != CONNECTION_OK) { client_error(context, "Connection to database failed: %s", PQerrorMessage(context->sql_conn)); return EIO; } /* Parse the connection string into key-value pairs */ char *error = NULL; PQconninfoOption *parsed_opts = PQconninfoParse(context->conninfo, &error); if (!parsed_opts) { client_error(context, "Replication connection info: %s", error); PQfreemem(error); return EIO; } /* Copy the key-value pairs into a new structure with added replication options */ PQconninfoOption *option; int optcount = 2; /* replication, fallback_application_name */ for (option = parsed_opts; option->keyword != NULL; option++) { if (option->val != NULL && option->val[0] != '\0') optcount++; } const char **keys = malloc((optcount + 1) * sizeof(char *)); const char **values = malloc((optcount + 1) * sizeof(char *)); int i = 0; for (option = parsed_opts; option->keyword != NULL; option++) { if (option->val != NULL && option->val[0] != '\0') { keys[i] = option->keyword; values[i] = option->val; i++; } } keys[i] = "replication"; values[i] = "database"; i++; keys[i] = "fallback_application_name"; values[i] = context->app_name; i++; keys[i] = NULL; values[i] = NULL; int err = 0; context->repl.conn = PQconnectdbParams(keys, values, true); if (PQstatus(context->repl.conn) != CONNECTION_OK) { client_error(context, "Replication connection failed: %s", PQerrorMessage(context->repl.conn)); err = EIO; } free(keys); free(values); PQconninfoFree(parsed_opts); return err; }
static PyObject * psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs) { char *err = NULL; PQconninfoOption *options = NULL, *o; PyObject *dict = NULL, *res = NULL, *dsn; static char *kwlist[] = {"dsn", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &dsn)) { return NULL; } Py_INCREF(dsn); /* for ensure_bytes */ if (!(dsn = psycopg_ensure_bytes(dsn))) { goto exit; } options = PQconninfoParse(Bytes_AS_STRING(dsn), &err); if (options == NULL) { if (err != NULL) { PyErr_Format(ProgrammingError, "error parsing the dsn: %s", err); PQfreemem(err); } else { PyErr_SetString(OperationalError, "PQconninfoParse() failed"); } goto exit; } if (!(dict = PyDict_New())) { goto exit; } for (o = options; o->keyword != NULL; o++) { if (o->val != NULL) { PyObject *value; if (!(value = Text_FromUTF8(o->val))) { goto exit; } if (PyDict_SetItemString(dict, o->keyword, value) != 0) { Py_DECREF(value); goto exit; } Py_DECREF(value); } } /* success */ res = dict; dict = NULL; exit: PQconninfoFree(options); /* safe on null */ Py_XDECREF(dict); Py_XDECREF(dsn); return res; }
static PGconn *get_pqdb_connection(void) { persistent_users_db_t *pud = get_persistent_users_db(); PGconn *pqdbconnection = (PGconn*)pthread_getspecific(connection_key); if(pqdbconnection) { ConnStatusType status = PQstatus(pqdbconnection); if(status != CONNECTION_OK) { PQfinish(pqdbconnection); pqdbconnection = NULL; (void) pthread_setspecific(connection_key, pqdbconnection); } } if(!pqdbconnection) { char *errmsg=NULL; PQconninfoOption *co = PQconninfoParse(pud->userdb, &errmsg); if(!co) { if(errmsg) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open PostgreSQL DB connection <%s>, connection string format error: %s\n",pud->userdb,errmsg); turn_free(errmsg,strlen(errmsg)+1); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open PostgreSQL DB connection: <%s>, unknown connection string format error\n",pud->userdb); } } else { PQconninfoFree(co); if(errmsg) turn_free(errmsg,strlen(errmsg)+1); pqdbconnection = PQconnectdb(pud->userdb); if(!pqdbconnection) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open PostgreSQL DB connection: <%s>, runtime error\n",pud->userdb); } else { ConnStatusType status = PQstatus(pqdbconnection); if(status != CONNECTION_OK) { PQfinish(pqdbconnection); pqdbconnection = NULL; TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open PostgreSQL DB connection: <%s>, runtime error\n",pud->userdb); } else if(!donot_print_connection_success){ TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "PostgreSQL DB connection success: %s\n",pud->userdb); donot_print_connection_success = 1; } } } if(pqdbconnection) { (void) pthread_setspecific(connection_key, pqdbconnection); } } return pqdbconnection; }
RAISES_NEG static int obscure_password(connectionObject *conn) { PQconninfoOption *options; PyObject *d = NULL, *v = NULL, *dsn = NULL; char *tmp; int rv = -1; if (!conn || !conn->dsn) { return 0; } if (!(options = PQconninfoParse(conn->dsn, NULL))) { /* unlikely: the dsn was already tested valid */ return 0; } if (!(d = psycopg_dict_from_conninfo_options( options, /* include_password = */ 1))) { goto exit; } if (NULL == PyDict_GetItemString(d, "password")) { /* the dsn doesn't have a password */ rv = 0; goto exit; } /* scrub the password and put back the connection string together */ if (!(v = Text_FromUTF8("xxx"))) { goto exit; } if (0 > PyDict_SetItemString(d, "password", v)) { goto exit; } if (!(dsn = psycopg_make_dsn(Py_None, d))) { goto exit; } if (!(dsn = psycopg_ensure_bytes(dsn))) { goto exit; } /* Replace the connection string on the connection object */ tmp = conn->dsn; psycopg_strdup(&conn->dsn, Bytes_AS_STRING(dsn), -1); PyMem_Free(tmp); rv = 0; exit: PQconninfoFree(options); Py_XDECREF(v); Py_XDECREF(d); Py_XDECREF(dsn); return rv; }
/* Return 1 if the "replication" keyword is set in the DSN, 0 otherwise */ static int dsn_has_replication(char *pgdsn) { int ret = 0; PQconninfoOption *connopts, *ptr; connopts = PQconninfoParse(pgdsn, NULL); for(ptr = connopts; ptr->keyword != NULL; ptr++) { if(strcmp(ptr->keyword, "replication") == 0 && ptr->val != NULL) ret = 1; } PQconninfoFree(connopts); return ret; }
/* * ConnectionGetOptionValue inspects the provided connection for an option with * a given keyword and returns a new palloc'd string with that options's value. * The function returns NULL if the connection has no setting for an option with * the provided keyword. */ static char * ConnectionGetOptionValue(PGconn *connection, char *optionKeyword) { char *optionValue = NULL; PQconninfoOption *conninfoOptions = PQconninfo(connection); for (PQconninfoOption *option = conninfoOptions; option->keyword != NULL; option++) { if (strncmp(option->keyword, optionKeyword, NAMEDATALEN) == 0) { optionValue = pstrdup(option->val); } } PQconninfoFree(conninfoOptions); return optionValue; }
CAMLprim value PQconndefaults_stub(value __unused v_unit) { CAMLparam0(); CAMLlocal2(v_res, v_el); PQconninfoOption *cios = PQconndefaults(), *p = cios; int i, j, n; while (p->keyword != NULL) p++; n = p - cios; p = cios; v_res = caml_alloc_tuple(n); for (i = 0; i < n; i++, p++) { value v_field; v_el = caml_alloc_small(7, 0); for (j = 0; j < 7; j++) Field(v_el, j) = v_None; Store_field(v_res, i, v_el); v_field = caml_copy_string(p->keyword); Field(v_el, 0) = v_field; if (p->envvar) { v_field = make_some(caml_copy_string(p->envvar)); caml_modify(&Field(v_el, 1), v_field); } if (p->compiled) { v_field = make_some(caml_copy_string(p->compiled)); caml_modify(&Field(v_el, 2), v_field); }; if (p->val) { v_field = make_some(caml_copy_string(p->val)); caml_modify(&Field(v_el, 3), v_field); }; v_field = caml_copy_string(p->label); caml_modify(&Field(v_el, 4), v_field); v_field = caml_copy_string(p->dispchar); caml_modify(&Field(v_el, 5), v_field); caml_modify(&Field(v_el, 6), Val_int(p->dispsize)); }; PQconninfoFree(cios); CAMLreturn(v_res); }
/* * Parse configuration file; if any errors are encountered, * list them and exit. * * Ensure any default values set here are synced with repmgr.conf.sample * and any other documentation. */ bool parse_config(t_configuration_options *options) { FILE *fp; char *s, buff[MAXLINELENGTH]; char name[MAXLEN]; char value[MAXLEN]; /* For sanity-checking provided conninfo string */ PQconninfoOption *conninfo_options; char *conninfo_errmsg = NULL; /* Collate configuration file errors here for friendlier reporting */ static ErrorList config_errors = { NULL, NULL }; fp = fopen(config_file_path, "r"); /* * Since some commands don't require a config file at all, not having one * isn't necessarily a problem. * * If the user explictly provided a configuration file and we can't * read it we'll raise an error. * * If no configuration file was provided, we'll try and read the default\ * file if it exists and is readable, but won't worry if it's not. */ if (fp == NULL) { if (config_file_provided) { log_err(_("unable to open provided configuration file '%s'; terminating\n"), config_file_path); exit(ERR_BAD_CONFIG); } log_notice(_("no configuration file provided and default file '%s' not found - " "continuing with default values\n"), DEFAULT_CONFIG_FILE); return false; } /* Initialize configuration options with sensible defaults * note: the default log level is set in log.c and does not need * to be initialised here */ memset(options->cluster_name, 0, sizeof(options->cluster_name)); options->node = -1; options->upstream_node = NO_UPSTREAM_NODE; options->use_replication_slots = 0; memset(options->conninfo, 0, sizeof(options->conninfo)); options->failover = MANUAL_FAILOVER; options->priority = DEFAULT_PRIORITY; memset(options->node_name, 0, sizeof(options->node_name)); memset(options->promote_command, 0, sizeof(options->promote_command)); memset(options->follow_command, 0, sizeof(options->follow_command)); memset(options->rsync_options, 0, sizeof(options->rsync_options)); memset(options->ssh_options, 0, sizeof(options->ssh_options)); memset(options->pg_bindir, 0, sizeof(options->pg_bindir)); memset(options->pg_ctl_options, 0, sizeof(options->pg_ctl_options)); memset(options->pg_basebackup_options, 0, sizeof(options->pg_basebackup_options)); /* default master_response_timeout is 60 seconds */ options->master_response_timeout = 60; /* default to 6 reconnection attempts at intervals of 10 seconds */ options->reconnect_attempts = 6; options->reconnect_interval = 10; options->monitor_interval_secs = 2; options->retry_promote_interval_secs = 300; memset(options->event_notification_command, 0, sizeof(options->event_notification_command)); options->tablespace_mapping.head = NULL; options->tablespace_mapping.tail = NULL; /* Read next line */ while ((s = fgets(buff, sizeof buff, fp)) != NULL) { bool known_parameter = true; /* Parse name/value pair from line */ parse_line(buff, name, value); /* Skip blank lines */ if (!strlen(name)) continue; /* Skip comments */ if (name[0] == '#') continue; /* Copy into correct entry in parameters struct */ if (strcmp(name, "cluster") == 0) strncpy(options->cluster_name, value, MAXLEN); else if (strcmp(name, "node") == 0) options->node = repmgr_atoi(value, "node", &config_errors); else if (strcmp(name, "upstream_node") == 0) options->upstream_node = repmgr_atoi(value, "upstream_node", &config_errors); else if (strcmp(name, "conninfo") == 0) strncpy(options->conninfo, value, MAXLEN); else if (strcmp(name, "rsync_options") == 0) strncpy(options->rsync_options, value, QUERY_STR_LEN); else if (strcmp(name, "ssh_options") == 0) strncpy(options->ssh_options, value, QUERY_STR_LEN); else if (strcmp(name, "loglevel") == 0) strncpy(options->loglevel, value, MAXLEN); else if (strcmp(name, "logfacility") == 0) strncpy(options->logfacility, value, MAXLEN); else if (strcmp(name, "failover") == 0) { char failoverstr[MAXLEN]; strncpy(failoverstr, value, MAXLEN); if (strcmp(failoverstr, "manual") == 0) { options->failover = MANUAL_FAILOVER; } else if (strcmp(failoverstr, "automatic") == 0) { options->failover = AUTOMATIC_FAILOVER; } else { log_err(_("value for 'failover' must be 'automatic' or 'manual'\n")); exit(ERR_BAD_CONFIG); } } else if (strcmp(name, "priority") == 0) options->priority = repmgr_atoi(value, "priority", &config_errors); else if (strcmp(name, "node_name") == 0) strncpy(options->node_name, value, MAXLEN); else if (strcmp(name, "promote_command") == 0) strncpy(options->promote_command, value, MAXLEN); else if (strcmp(name, "follow_command") == 0) strncpy(options->follow_command, value, MAXLEN); else if (strcmp(name, "master_response_timeout") == 0) options->master_response_timeout = repmgr_atoi(value, "master_response_timeout", &config_errors); /* 'primary_response_timeout' as synonym for 'master_response_timeout' - * we'll switch terminology in a future release (3.1?) */ else if (strcmp(name, "primary_response_timeout") == 0) options->master_response_timeout = repmgr_atoi(value, "primary_response_timeout", &config_errors); else if (strcmp(name, "reconnect_attempts") == 0) options->reconnect_attempts = repmgr_atoi(value, "reconnect_attempts", &config_errors); else if (strcmp(name, "reconnect_interval") == 0) options->reconnect_interval = repmgr_atoi(value, "reconnect_interval", &config_errors); else if (strcmp(name, "pg_bindir") == 0) strncpy(options->pg_bindir, value, MAXLEN); else if (strcmp(name, "pg_ctl_options") == 0) strncpy(options->pg_ctl_options, value, MAXLEN); else if (strcmp(name, "pg_basebackup_options") == 0) strncpy(options->pg_basebackup_options, value, MAXLEN); else if (strcmp(name, "logfile") == 0) strncpy(options->logfile, value, MAXLEN); else if (strcmp(name, "monitor_interval_secs") == 0) options->monitor_interval_secs = repmgr_atoi(value, "monitor_interval_secs", &config_errors); else if (strcmp(name, "retry_promote_interval_secs") == 0) options->retry_promote_interval_secs = repmgr_atoi(value, "retry_promote_interval_secs", &config_errors); else if (strcmp(name, "use_replication_slots") == 0) /* XXX we should have a dedicated boolean argument format */ options->use_replication_slots = repmgr_atoi(value, "use_replication_slots", &config_errors); else if (strcmp(name, "event_notification_command") == 0) strncpy(options->event_notification_command, value, MAXLEN); else if (strcmp(name, "event_notifications") == 0) parse_event_notifications_list(options, value); else if (strcmp(name, "tablespace_mapping") == 0) tablespace_list_append(options, value); else { known_parameter = false; log_warning(_("%s/%s: unknown name/value pair provided; ignoring\n"), name, value); } /* * Raise an error if a known parameter is provided with an empty value. * Currently there's no reason why empty parameters are needed; if * we want to accept those, we'd need to add stricter default checking, * as currently e.g. an empty `node` value will be converted to '0'. */ if (known_parameter == true && !strlen(value)) { char error_message_buf[MAXLEN] = ""; snprintf(error_message_buf, MAXLEN, _("no value provided for parameter \"%s\""), name); error_list_append(&config_errors, error_message_buf); } } fclose(fp); /* Check config settings */ /* The following checks are for the presence of the parameter */ if (*options->cluster_name == '\0') { error_list_append(&config_errors, _("\"cluster\": parameter was not found\n")); } if (options->node == -1) { error_list_append(&config_errors, _("\"node\": parameter was not found\n")); } if (*options->node_name == '\0') { error_list_append(&config_errors, _("\"node_name\": parameter was not found\n")); } if (*options->conninfo == '\0') { error_list_append(&config_errors, _("\"conninfo\": parameter was not found\n")); } else { /* Sanity check the provided conninfo string * * NOTE: this verifies the string format and checks for valid options * but does not sanity check values */ conninfo_options = PQconninfoParse(options->conninfo, &conninfo_errmsg); if (conninfo_options == NULL) { char error_message_buf[MAXLEN] = ""; snprintf(error_message_buf, MAXLEN, _("\"conninfo\": %s"), conninfo_errmsg); error_list_append(&config_errors, error_message_buf); } PQconninfoFree(conninfo_options); } // exit_with_errors here if (config_errors.head != NULL) { exit_with_errors(&config_errors); } return true; }
/* * Connect to the server. Returns a valid PGconn pointer if connected, * or NULL on non-permanent error. On permanent error, the function will * call exit(1) directly. */ PGconn * GetConnection(void) { PGconn *tmpconn; int argcount = 7; /* dbname, replication, fallback_app_name, * host, user, port, password */ int i; const char **keywords; const char **values; const char *tmpparam; bool need_password; PQconninfoOption *conn_opts = NULL; PQconninfoOption *conn_opt; char *err_msg = NULL; /* * Merge the connection info inputs given in form of connection string, * options and default values (dbname=replication, replication=true, etc.) */ i = 0; if (connection_string) { conn_opts = PQconninfoParse(connection_string, &err_msg); if (conn_opts == NULL) { fprintf(stderr, "%s: %s", progname, err_msg); exit(1); } for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++) { if (conn_opt->val != NULL && conn_opt->val[0] != '\0') argcount++; } keywords = pg_malloc0((argcount + 1) * sizeof(*keywords)); values = pg_malloc0((argcount + 1) * sizeof(*values)); for (conn_opt = conn_opts; conn_opt->keyword != NULL; conn_opt++) { if (conn_opt->val != NULL && conn_opt->val[0] != '\0') { keywords[i] = conn_opt->keyword; values[i] = conn_opt->val; i++; } } } else { keywords = pg_malloc0((argcount + 1) * sizeof(*keywords)); values = pg_malloc0((argcount + 1) * sizeof(*values)); } keywords[i] = "dbname"; values[i] = "replication"; i++; keywords[i] = "replication"; values[i] = "true"; i++; keywords[i] = "fallback_application_name"; values[i] = progname; i++; if (dbhost) { keywords[i] = "host"; values[i] = dbhost; i++; } if (dbuser) { keywords[i] = "user"; values[i] = dbuser; i++; } if (dbport) { keywords[i] = "port"; values[i] = dbport; i++; } /* If -W was given, force prompt for password, but only the first time */ need_password = (dbgetpassword == 1 && dbpassword == NULL); while (true) { /* Get a new password if appropriate */ if (need_password) { if (dbpassword) free(dbpassword); dbpassword = simple_prompt(_("Password: "******"password"; values[i] = dbpassword; } else { keywords[i] = NULL; values[i] = NULL; } tmpconn = PQconnectdbParams(keywords, values, true); /* * If there is too little memory even to allocate the PGconn object * and PQconnectdbParams returns NULL, we call exit(1) directly. */ if (!tmpconn) { fprintf(stderr, _("%s: could not connect to server\n"), progname); exit(1); } /* If we need a password and -w wasn't given, loop back and get one */ if (PQstatus(tmpconn) == CONNECTION_BAD && PQconnectionNeedsPassword(tmpconn) && dbgetpassword != -1) { PQfinish(tmpconn); need_password = true; } else break; } if (PQstatus(tmpconn) != CONNECTION_OK) { fprintf(stderr, _("%s: could not connect to server: %s\n"), progname, PQerrorMessage(tmpconn)); PQfinish(tmpconn); free(values); free(keywords); if (conn_opts) PQconninfoFree(conn_opts); return NULL; } /* Connection ok! */ free(values); free(keywords); if (conn_opts) PQconninfoFree(conn_opts); /* * Ensure we have the same value of integer timestamps as the server we * are connecting to. */ tmpparam = PQparameterStatus(tmpconn, "integer_datetimes"); if (!tmpparam) { fprintf(stderr, _("%s: could not determine server setting for integer_datetimes\n"), progname); PQfinish(tmpconn); exit(1); } #ifdef HAVE_INT64_TIMESTAMP if (strcmp(tmpparam, "on") != 0) #else if (strcmp(tmpparam, "off") != 0) #endif { fprintf(stderr, _("%s: integer_datetimes compile flag does not match server\n"), progname); PQfinish(tmpconn); exit(1); } return tmpconn; }
/* * Create a recovery.conf file in memory using a PQExpBuffer */ static void GenerateRecoveryConf(PGconn *conn) { PQconninfoOption *connOptions; PQconninfoOption *option; PQExpBufferData conninfo_buf; char *escaped; recoveryconfcontents = createPQExpBuffer(); if (!recoveryconfcontents) { fprintf(stderr, _("%s: out of memory\n"), progname); disconnect_and_exit(1); } connOptions = PQconninfo(conn); if (connOptions == NULL) { fprintf(stderr, _("%s: out of memory\n"), progname); disconnect_and_exit(1); } appendPQExpBufferStr(recoveryconfcontents, "standby_mode = 'on'\n"); initPQExpBuffer(&conninfo_buf); for (option = connOptions; option && option->keyword; option++) { /* * Do not emit this setting if: - the setting is "replication", * "dbname" or "fallback_application_name", since these would be * overridden by the libpqwalreceiver module anyway. - not set or * empty. */ if (strcmp(option->keyword, "replication") == 0 || strcmp(option->keyword, "dbname") == 0 || strcmp(option->keyword, "fallback_application_name") == 0 || (option->val == NULL) || (option->val != NULL && option->val[0] == '\0')) continue; /* Separate key-value pairs with spaces */ if (conninfo_buf.len != 0) appendPQExpBufferStr(&conninfo_buf, " "); /* * Write "keyword=value" pieces, the value string is escaped and/or * quoted if necessary. */ escaped = escapeConnectionParameter(option->val); appendPQExpBuffer(&conninfo_buf, "%s=%s", option->keyword, escaped); free(escaped); } /* * Escape the connection string, so that it can be put in the config file. * Note that this is different from the escaping of individual connection * options above! */ escaped = escape_quotes(conninfo_buf.data); appendPQExpBuffer(recoveryconfcontents, "primary_conninfo = '%s'\n", escaped); free(escaped); if (PQExpBufferBroken(recoveryconfcontents) || PQExpBufferDataBroken(conninfo_buf)) { fprintf(stderr, _("%s: out of memory\n"), progname); disconnect_and_exit(1); } termPQExpBuffer(&conninfo_buf); PQconninfoFree(connOptions); }
/* * Parse configuration file; if any errors are encountered, * list them and exit. * * Ensure any default values set here are synced with repmgr.conf.sample * and any other documentation. */ void _parse_config(t_configuration_options *options, ItemList *error_list) { FILE *fp; char *s, buf[MAXLINELENGTH]; char name[MAXLEN]; char value[MAXLEN]; /* For sanity-checking provided conninfo string */ PQconninfoOption *conninfo_options; char *conninfo_errmsg = NULL; bool node_found = false; /* Initialize configuration options with sensible defaults * note: the default log level is set in log.c and does not need * to be initialised here */ memset(options->cluster_name, 0, sizeof(options->cluster_name)); options->node = UNKNOWN_NODE_ID; options->upstream_node = NO_UPSTREAM_NODE; options->use_replication_slots = 0; memset(options->conninfo, 0, sizeof(options->conninfo)); memset(options->barman_server, 0, sizeof(options->barman_server)); memset(options->barman_config, 0, sizeof(options->barman_config)); options->failover = MANUAL_FAILOVER; options->priority = DEFAULT_PRIORITY; memset(options->node_name, 0, sizeof(options->node_name)); memset(options->promote_command, 0, sizeof(options->promote_command)); memset(options->follow_command, 0, sizeof(options->follow_command)); memset(options->service_stop_command, 0, sizeof(options->service_stop_command)); memset(options->service_start_command, 0, sizeof(options->service_start_command)); memset(options->service_restart_command, 0, sizeof(options->service_restart_command)); memset(options->service_reload_command, 0, sizeof(options->service_reload_command)); memset(options->service_promote_command, 0, sizeof(options->service_promote_command)); memset(options->rsync_options, 0, sizeof(options->rsync_options)); memset(options->ssh_options, 0, sizeof(options->ssh_options)); memset(options->pg_bindir, 0, sizeof(options->pg_bindir)); memset(options->pg_ctl_options, 0, sizeof(options->pg_ctl_options)); memset(options->pg_basebackup_options, 0, sizeof(options->pg_basebackup_options)); memset(options->restore_command, 0, sizeof(options->restore_command)); /* default master_response_timeout is 60 seconds */ options->master_response_timeout = 60; /* default to 6 reconnection attempts at intervals of 10 seconds */ options->reconnect_attempts = 6; options->reconnect_interval = 10; options->monitor_interval_secs = 2; options->retry_promote_interval_secs = 300; /* default to resyncing repl_nodes table every 30 seconds on the witness server */ options->witness_repl_nodes_sync_interval_secs = 30; memset(options->event_notification_command, 0, sizeof(options->event_notification_command)); options->event_notifications.head = NULL; options->event_notifications.tail = NULL; options->tablespace_mapping.head = NULL; options->tablespace_mapping.tail = NULL; /* * If no configuration file available (user didn't specify and none found * in the default locations), return with default values */ if (config_file_found == false) { log_verbose(LOG_NOTICE, _("no configuration file provided and no default file found - " "continuing with default values\n")); return; } fp = fopen(config_file_path, "r"); /* * A configuration file has been found, either provided by the user * or found in one of the default locations. If we can't open it, * fail with an error. */ if (fp == NULL) { if (config_file_provided) { log_err(_("unable to open provided configuration file \"%s\"; terminating\n"), config_file_path); } else { log_err(_("unable to open default configuration file \"%s\"; terminating\n"), config_file_path); } exit(ERR_BAD_CONFIG); } /* Read file */ while ((s = fgets(buf, sizeof buf, fp)) != NULL) { bool known_parameter = true; /* Parse name/value pair from line */ parse_line(buf, name, value); /* Skip blank lines */ if (!strlen(name)) continue; /* Skip comments */ if (name[0] == '#') continue; /* Copy into correct entry in parameters struct */ if (strcmp(name, "cluster") == 0) strncpy(options->cluster_name, value, MAXLEN); else if (strcmp(name, "node") == 0) { options->node = repmgr_atoi(value, "node", error_list, false); node_found = true; } else if (strcmp(name, "upstream_node") == 0) options->upstream_node = repmgr_atoi(value, "upstream_node", error_list, false); else if (strcmp(name, "conninfo") == 0) strncpy(options->conninfo, value, MAXLEN); else if (strcmp(name, "barman_server") == 0) strncpy(options->barman_server, value, MAXLEN); else if (strcmp(name, "barman_config") == 0) strncpy(options->barman_config, value, MAXLEN); else if (strcmp(name, "rsync_options") == 0) strncpy(options->rsync_options, value, QUERY_STR_LEN); else if (strcmp(name, "ssh_options") == 0) strncpy(options->ssh_options, value, QUERY_STR_LEN); else if (strcmp(name, "loglevel") == 0) strncpy(options->loglevel, value, MAXLEN); else if (strcmp(name, "logfacility") == 0) strncpy(options->logfacility, value, MAXLEN); else if (strcmp(name, "failover") == 0) { char failoverstr[MAXLEN]; strncpy(failoverstr, value, MAXLEN); if (strcmp(failoverstr, "manual") == 0) { options->failover = MANUAL_FAILOVER; } else if (strcmp(failoverstr, "automatic") == 0) { options->failover = AUTOMATIC_FAILOVER; } else { item_list_append(error_list, _("value for 'failover' must be 'automatic' or 'manual'\n")); } } else if (strcmp(name, "priority") == 0) options->priority = repmgr_atoi(value, "priority", error_list, true); else if (strcmp(name, "node_name") == 0) strncpy(options->node_name, value, MAXLEN); else if (strcmp(name, "promote_command") == 0) strncpy(options->promote_command, value, MAXLEN); else if (strcmp(name, "follow_command") == 0) strncpy(options->follow_command, value, MAXLEN); else if (strcmp(name, "service_stop_command") == 0) strncpy(options->service_stop_command, value, MAXLEN); else if (strcmp(name, "service_start_command") == 0) strncpy(options->service_start_command, value, MAXLEN); else if (strcmp(name, "service_restart_command") == 0) strncpy(options->service_restart_command, value, MAXLEN); else if (strcmp(name, "service_reload_command") == 0) strncpy(options->service_reload_command, value, MAXLEN); else if (strcmp(name, "service_promote_command") == 0) strncpy(options->service_promote_command, value, MAXLEN); else if (strcmp(name, "master_response_timeout") == 0) options->master_response_timeout = repmgr_atoi(value, "master_response_timeout", error_list, false); /* * 'primary_response_timeout' as synonym for 'master_response_timeout' - * we'll switch terminology in a future release (3.1?) */ else if (strcmp(name, "primary_response_timeout") == 0) options->master_response_timeout = repmgr_atoi(value, "primary_response_timeout", error_list, false); else if (strcmp(name, "reconnect_attempts") == 0) options->reconnect_attempts = repmgr_atoi(value, "reconnect_attempts", error_list, false); else if (strcmp(name, "reconnect_interval") == 0) options->reconnect_interval = repmgr_atoi(value, "reconnect_interval", error_list, false); else if (strcmp(name, "pg_bindir") == 0) strncpy(options->pg_bindir, value, MAXLEN); else if (strcmp(name, "pg_ctl_options") == 0) strncpy(options->pg_ctl_options, value, MAXLEN); else if (strcmp(name, "pg_basebackup_options") == 0) strncpy(options->pg_basebackup_options, value, MAXLEN); else if (strcmp(name, "logfile") == 0) strncpy(options->logfile, value, MAXLEN); else if (strcmp(name, "monitor_interval_secs") == 0) options->monitor_interval_secs = repmgr_atoi(value, "monitor_interval_secs", error_list, false); else if (strcmp(name, "retry_promote_interval_secs") == 0) options->retry_promote_interval_secs = repmgr_atoi(value, "retry_promote_interval_secs", error_list, false); else if (strcmp(name, "witness_repl_nodes_sync_interval_secs") == 0) options->witness_repl_nodes_sync_interval_secs = repmgr_atoi(value, "witness_repl_nodes_sync_interval_secs", error_list, false); else if (strcmp(name, "use_replication_slots") == 0) /* XXX we should have a dedicated boolean argument format */ options->use_replication_slots = repmgr_atoi(value, "use_replication_slots", error_list, false); else if (strcmp(name, "event_notification_command") == 0) strncpy(options->event_notification_command, value, MAXLEN); else if (strcmp(name, "event_notifications") == 0) parse_event_notifications_list(options, value); else if (strcmp(name, "tablespace_mapping") == 0) tablespace_list_append(options, value); else if (strcmp(name, "restore_command") == 0) strncpy(options->restore_command, value, MAXLEN); else { known_parameter = false; log_warning(_("%s/%s: unknown name/value pair provided; ignoring\n"), name, value); } /* * Raise an error if a known parameter is provided with an empty value. * Currently there's no reason why empty parameters are needed; if * we want to accept those, we'd need to add stricter default checking, * as currently e.g. an empty `node` value will be converted to '0'. */ if (known_parameter == true && !strlen(value)) { char error_message_buf[MAXLEN] = ""; snprintf(error_message_buf, MAXLEN, _("no value provided for parameter \"%s\""), name); item_list_append(error_list, error_message_buf); } } fclose(fp); if (node_found == false) { item_list_append(error_list, _("\"node\": parameter was not found")); } else if (options->node == 0) { item_list_append(error_list, _("\"node\": must be greater than zero")); } if (strlen(options->conninfo)) { /* Sanity check the provided conninfo string * * NOTE: PQconninfoParse() verifies the string format and checks for valid options * but does not sanity check values */ conninfo_options = PQconninfoParse(options->conninfo, &conninfo_errmsg); if (conninfo_options == NULL) { char error_message_buf[MAXLEN] = ""; snprintf(error_message_buf, MAXLEN, _("\"conninfo\": %s"), conninfo_errmsg); item_list_append(error_list, error_message_buf); } PQconninfoFree(conninfo_options); } }