diff options
author | Koichi Suzuki | 2011-11-29 04:16:16 +0000 |
---|---|---|
committer | Koichi Suzuki | 2011-11-29 04:16:16 +0000 |
commit | a43534d7519176e6dcdaf4517b329c2fed935c8b (patch) | |
tree | d38427ba59524e1bde9dc997df8d94873d89c1f1 | |
parent | 66a9181ee348aa2149b42ef79fa0ef6da95e2390 (diff) |
This commit includes configuration file support for GTM and GTM-Proxy,
together with a couple of bug fix of GTM Standby and GTM-Proxy reconnect.
Now GTM and GTM-Proxy configuration files are mandatory. They should
be placed at the working directory, as specified by -D command line
option, as "gtm.conf" and "gtm_proxy.conf", respectively.
Their format is the same as Details will be found in the documentation.
Similar to coordinator/datanode, you can override some of the parameters
in configuration file by command line options.
Modified/affected files are as follows:
modified: doc-xc/src/sgml/ref/gtm.sgmlin
modified: doc-xc/src/sgml/ref/gtm_proxy.sgmlin
modified: src/gtm/Makefile
modified: src/gtm/README
modified: src/gtm/common/Makefile
new file: src/gtm/common/gtm_opt_handler.c
new file: src/gtm/common/gtm_opt_scanner.l
modified: src/gtm/gtm_ctl/gtm_ctl.c
modified: src/gtm/main/Makefile
new file: src/gtm/main/gtm.conf.sample
new file: src/gtm/main/gtm_opt.c
modified: src/gtm/main/gtm_standby.c
modified: src/gtm/main/main.c
modified: src/gtm/path/path.c
modified: src/gtm/proxy/Makefile
new file: src/gtm/proxy/gtm_proxy.conf.sample
new file: src/gtm/proxy/gtm_proxy_opt.c
modified: src/gtm/proxy/proxy_main.c
modified: src/gtm/recovery/register.c
modified: src/gtm/recovery/standby_utils.c
modified: src/include/gtm/gtm.h
new file: src/include/gtm/gtm_opt.h
new file: src/include/gtm/gtm_opt_tables.h
modified: src/include/gtm/gtm_proxy.h
modified: src/include/gtm/gtm_standby.h
modified: src/include/gtm/path.h
26 files changed, 6025 insertions, 123 deletions
diff --git a/doc-xc/src/sgml/ref/gtm.sgmlin b/doc-xc/src/sgml/ref/gtm.sgmlin index 24a2e56696..c27703ef81 100644 --- a/doc-xc/src/sgml/ref/gtm.sgmlin +++ b/doc-xc/src/sgml/ref/gtm.sgmlin @@ -39,8 +39,232 @@ PostgreSQL documentation using gtm_ctl(8). </para> + <para> + You must provide gtm configuration + file <filename>gtm.conf</filename> placed at gtm working directory + as specified by <literal>-D</literal> command line option. The + configuration file specifies gtm running envinment and resources. + </para> + + <para> + Some of the parameters specified in the control file can be overriden by + command line options. + </para> + + </refsect1> + + <refsect1> + <title>Configuration File</title> + +&xconly + <para> + <literal>GTM</literal> configuration parameters are specified in the configuration file + <filename>gtm.conf</filename> placed in the working directory + specified as <literal>-D</literal> option + of <application>gtm</application> command line option as described + in the next section. + </para> + <para> + Format of the configuration file is the same as <filename>postgresql.conf</filename>. + Options are as follows. + </para> + +<!-- Add description of each gtm.conf entry --> +<!-- Notice + The following options are found in the source code (gtm_opt.c) but is only for + internal use and will not be presented in the following list. + + 1. data_di + 2. config_file + + Also the following options are for high-availability hook. This will be + documented later. + + 1. error_reporter + 2. status_reader +--> + <variablelist> + + <varlistentry id="gtm-opt-active-host" xreflabel="gtm_opt_active_host"> + <term><varname>active_host</varname> (<type>string</type>)</term> + <indexterm> + <primary><varname>active_host</varname> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies listen addresses (host name or IP address) of active <application>gtm</application>. This + parameter is effective only when this <application>gtm</application> + is specified as a standby. There is no default value for this parameter. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-opt-active-port" xreflabel="gtm_opt_active_port"> + <term><varname>active_port</varname> (<type>integer</type>)</term> + <indexterm> + <primary><varname>active_port</varname> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies the port number of active <application>gtm</application>. This + parameter is effective only when this <application>gtm</application> + is specified as a standby. + There is no default value for this parameter. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-opt-keepalives-count" xreflabel="gtm_opt_keepalives_count"> + <term><varname>keepalives_count</varname> (<type>integer</type>)</term> + <indexterm> + <primary><varname>keepalives_count</varname> (<type>integer</type>)</primary> + </indexterm> + <listitem> + <para> + Specifies <literal>keepalives_count</literal> option for the + connection to <application>gtm</application>. This option is + effective only when it runs as GTM-Standby. + Default value is zero and keepalives feature is disabled. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-opt-keepalives-idle" xreflabel="gtm_opt_keepalives_idle"> + <term><varname>keepalives_idle</varname> (<type>integer</type>)</term> + <indexterm> + <primary><varname>keepalives_idle</varname> (<type>integer</type>)</primary> + </indexterm> + <listitem> + <para> + Specifies <literal>keepalives_idle</literal> option for the + connection to <application>gtm</application>. This option is + effective only when it runs as GTM-Standby. + Default value is zero and keepalives feature is disabled. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-opt-keepalives-interval" xreflabel="gtm_opt_keepalives_interval"> + <term><varname>keepalives_interval</varname> (<type>integer</type>)</term> + <indexterm> + <primary><varname>keepalives_interval</varname> (<type>integer</type>)</primary> + </indexterm> + <listitem> + <para> + Specifies <literal>keepalives_interval</literal> option for the + connection to <application>gtm</application>. This option is + effective only when it runs as GTM-Standby. + Default value is zero and keepalives feature is disabled. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-opt-listen-addresses" xreflabel="gtm_opt_listen_addresses"> + <term><varname>listen_addresses</varname> (<type>string</type>)</term> + <indexterm> + <primary><varname>listen_addresses</varname> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies listen addresses (host name or IP address) of + this <application>gtm</application> + or <application>gtm</application> standby. + Default value is '*'. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-opt-log-file" xreflabel="gtm_opt_log_file"> + <term><varname>log_file</varname> (<type>string</type>)</term> + <indexterm> + <primary><varname>log_file</varname> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies <filename>log</filename> file name. This file will be + created at the working directory of + this <application>gtm</application> as specified + by <literal>-D</literal> command line option. + The default is <filename>gtm.log</filename>. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-opt-log-min-messages" xreflabel="gtm_opt_log_min_messages"> + <term><varname>log_min_messages</varname> (<type>enum</type>)</term> + <indexterm> + <primary><varname>log_min_messages</varname> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Controls which message levels are written to the log. + Valid values are <literal>DEBUG</literal>, <literal>DEBUG5</literal>, + <literal>DEBUG4</literal>, <literal>DEBUG3</literal>, <literal>DEBUG2</literal>, + <literal>DEBUG1</literal>, <literal>INFO</literal>, <literal>NOTICE</literal>, + <literal>WARNING</literal>, <literal>ERROR</literal>, <literal>LOG</literal>, + <literal>FATAL</literal> and <literal>PANIC</literal>. + Each level includes all the levels that follow it. + The later the level, the fewer messages are sent. + The default is <literal>WARNING</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-opt-nodename" xreflabel="gtm_opt_nodename"> + <term><varname>nodename</varname> (<type>string</type>)</term> + <indexterm> + <primary><varname>nodename</varname> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies the name of this <application>gtm</application> or <application>gtm</application> standby. + There is no default value for this parameter. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-opt-port" xreflabel="gtm_opt_port"> + <term><varname>port</varname> (<type>integer</type>)</term> + <indexterm> + <primary><varname>port</varname> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies the port number of <application>gtm</application> or <application>gtm</application> standby. + Default value is 6666. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-opt-startup" xreflabel="gtm_opt_startup"> + <term><varname>startup</varname> (<type>enum</type>)</term> + <indexterm> + <primary><varname>startup</varname> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies the startup mode of this <application>gtm</application>. + Valied values are <literal>act</literal> or <literal>standby</literal>. + <literal>act</literal> means to start up + this <application>gtm</application> as usual so + that <application>gtm</application> clients (coordinators, data + nodes or gtm-proxies) can connect for transaction + management. <literal>standby</literal> means + this <application>gtm</application> starts up as a backup + of <literal>act</literal> + gtm. <literal>standby</literal> <literal>gtm</literal> can be + promoted to <literal>act</literal> when <literal>act</literal> + fails. + </para> + </listitem> + </varlistentry> + + + </variablelist> + </refsect1> + <refsect1> <title>Options</title> @@ -51,6 +275,11 @@ PostgreSQL documentation </para> <para> + Parameters specified as command line options override these specified in the configuration file described + in the previous section. + </para> + + <para> Options are as follows: </para> diff --git a/doc-xc/src/sgml/ref/gtm_proxy.sgmlin b/doc-xc/src/sgml/ref/gtm_proxy.sgmlin index fc434a7bd1..d8ee7f6327 100644 --- a/doc-xc/src/sgml/ref/gtm_proxy.sgmlin +++ b/doc-xc/src/sgml/ref/gtm_proxy.sgmlin @@ -44,6 +44,214 @@ PostgreSQL documentation It is highly advised to start and stop gtm_proxy with gtm_ctl(8). </para> + <para> + You must provide gtm configuration + file <filename>gtm_proxy.conf</filename> placed at gtm working directory + as specified by <literal>-D</literal> command line option. The + configuration file specifies gtm running envinment and resources. + </para> + + <para> + Some of the parameters specified in the control file can be overriden by + command line options. + </para> + + </refsect1> + +<refsect1> + <title>Configuration File</title> + +&xconly + <para> + <literal>GTM-Proxy</literal> configuration parameters are specified in the configuration file + <filename>gtm_proxy.conf</filename> placed in the working directory + specified as <literal>-D</literal> option + of <application>gtm_proxy</application> command line option as described + in the next section. + </para> + + <para> + Format of the configuration file is the same as <filename>postgresql.conf</filename>. + Options are as follows. + </para> + +<!-- Add description of each gtm.conf entry --> +<!-- Notice + The following options are found in the source code (gtm_opt.c) but is only for + internal use and will not be presented in the following list. + + 1. data_dir + 2. config_file + + Also the following options are for high-availability hook. This will be + documented later. + 1. error_reporter + 2. status_reader + 3. err_wait_opt + 4. err_wait_count + 5. err_wait_interval +--> + <variablelist> + + <varlistentry id="gtm-proxy-opt-gtm-host" xreflabel="gtm_proxy_opt_gtm_host"> + <term><varname>gtm_host</varname> (<type>string</type>)</term> + <indexterm> + <primary><varname>gtm_host</varname> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies listen addresses (host name or IP address) of <application>gtm</application>. + There is no default value for this parameter. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-proxy-opt-gtm-port" xreflabel="gtm_proxy_opt_gtm_port"> + <term><varname>gtm_port</varname> (<type>integer</type>)</term> + <indexterm> + <primary><varname>gtm_port</varname> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies the port number of <application>gtm</application>. + There is no default value for this parameter. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-proxy-opt-keepalives-count" xreflabel="gtm_proxy_opt_keepalives_count"> + <term><varname>keepalives_count</varname> (<type>integer</type>)</term> + <indexterm> + <primary><varname>keepalives_count</varname> (<type>integer</type>)</primary> + </indexterm> + <listitem> + <para> + Specifies <literal>keepalives_count</literal> option for the + connection to <application>gtm</application>. + Default value is zero and keepalives feature is disabled. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-proxy-opt-keepalives-idle" xreflabel="gtm_proxy_opt_keepalives_idle"> + <term><varname>keepalives_idle</varname> (<type>integer</type>)</term> + <indexterm> + <primary><varname>keepalives_idle</varname> (<type>integer</type>)</primary> + </indexterm> + <listitem> + <para> + Specifies <literal>keepalives_idle</literal> option for the + connection to <application>gtm</application>. + Default value is zero and keepalives feature is disabled. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-proxy-opt-keepalives-interval" xreflabel="gtm_proxy_opt_keepalives_interval"> + <term><varname>keepalives_interval</varname> (<type>integer</type>)</term> + <indexterm> + <primary><varname>keepalives_interval</varname> (<type>integer</type>)</primary> + </indexterm> + <listitem> + <para> + Specifies <literal>keepalives_interval</literal> option for the + connection to <application>gtm</application>. + Default value is zero and keepalives feature is disabled. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-proxy-opt-log-file" xreflabel="gtm_proxy_opt_log_file"> + <term><varname>log_file</varname> (<type>string</type>)</term> + <indexterm> + <primary><varname>log_file</varname> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies <filename>log</filename> file name. This file will be + created at the working directory of + this <application>gtm_proxy</application> as specified + by <literal>-D</literal> command line option. + The default is <filename>gtm_proxy.log</filename>. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-proxy-opt-log-min-messages" xreflabel="gtm_proxy_opt_log_min_messages"> + <term><varname>log_min_messages</varname> (<type>enum</type>)</term> + <indexterm> + <primary><varname>log_min_messages</varname> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Controls which message levels are written to the log. + Valid values are <literal>DEBUG</literal>, <literal>DEBUG5</literal>, + <literal>DEBUG4</literal>, <literal>DEBUG3</literal>, <literal>DEBUG2</literal>, + <literal>DEBUG1</literal>, <literal>INFO</literal>, <literal>NOTICE</literal>, + <literal>WARNING</literal>, <literal>ERROR</literal>, <literal>LOG</literal>, + <literal>FATAL</literal> and <literal>PANIC</literal>. + Each level includes all the levels that follow it. + The later the level, the fewer messages are sent. + The default is <literal>WARNING</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-proxy-opt-listen-addresses" xreflabel="gtm_proxy_opt_listen_addresses"> + <term><varname>listen_addresses</varname> (<type>string</type>)</term> + <indexterm> + <primary><varname>listen_addresses</varname> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies listen addresses (host name or IP address) of + this <application>gtm_proxy</application>. + Default value is '*'. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-proxy-opt-nodename" xreflabel="gtm_proxy_opt_nodename"> + <term><varname>nodename</varname> (<type>string</type>)</term> + <indexterm> + <primary><varname>nodename</varname> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies the name of this <application>gtm_proxy</application>. + There is no default value for this parameter. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-proxy-opt-port" xreflabel="gtm_proxy_opt_port"> + <term><varname>port</varname> (<type>integer</type>)</term> + <indexterm> + <primary><varname>port</varname> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies the port number of this <application>gtm_proxy</application>. + Default port value is 6666. + </para> + </listitem> + </varlistentry> + + <varlistentry id="gtm-proxy-opt-worker-threads" xreflabel="gtm_proxy_opt_worker_threads"> + <term><varname>worker_threads</varname> (<type>integer</type>)</term> + <indexterm> + <primary><varname>worker_threads</varname> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies the number of worker threads for this <literal>gtm_proxy</literal>. + Default value is 1. + </para> + </listitem> + </varlistentry> + + </variablelist> + </refsect1> <refsect1> diff --git a/src/gtm/Makefile b/src/gtm/Makefile index 2616cec901..ee176c3b8a 100644 --- a/src/gtm/Makefile +++ b/src/gtm/Makefile @@ -36,8 +36,12 @@ install: all $(INSTALL_PROGRAM) ./main/gtm$(X) '$(DESTDIR)$(bindir)/gtm$(X)' $(INSTALL_PROGRAM) ./gtm_ctl/gtm_ctl$(X) '$(DESTDIR)$(bindir)/gtm_ctl$(X)' $(INSTALL_PROGRAM) ./proxy/gtm_proxy$(X) '$(DESTDIR)$(bindir)/gtm_proxy$(X)' + $(INSTALL_PROGRAM) ./main/gtm.conf.sample '$(DESTDIR)$(datadir)/gtm.conf.sample' + $(INSTALL_PROGRAM) ./proxy/gtm_proxy.conf.sample '$(DESTDIR)$(datadir)/gtm_proxy.conf.sample' uninstall: rm -f $(DESTDIR)$(bindir)/gtm$(X) rm -f $(DESTDIR)$(bindir)/gtm_ctl$(X) rm -f $(DESTDIR)$(bindir)/gtm_proxy$(X) + rm -f $(DESTDIR)$(datadir)/gtm.conf.sample + rm -f $(DESTDIR)$(datadir)/gtm_proxy.conf.sample diff --git a/src/gtm/README b/src/gtm/README index 77cff3695b..cb1f17804b 100644 --- a/src/gtm/README +++ b/src/gtm/README @@ -59,3 +59,43 @@ This would build various test clients, statically linking to the libgtmclient.a library in the client directory. You may need to change the connect string appropriately connect to the GTM server. +============================================================ +Additional Information for NODE_NAME, GTM_CONFIGURATION and GTM-Standby + +Nov. 25, 2011 +============================================================ + +1. NODE_NAME project + +Now XC changed its naming convention of each component from number to string +name. According to this, all the "id" is now changed to "name". + +2. GTM_CONFIGURATION + +Because GTM and GTM-Proxy has more options, it is not convenient to specify all +of these in command line options. Instaead, as in PostgreSQL, GTM/GTM-Proxy +now need their configuration file placed on their working directries. + +Details will be available in the documentation, gtm.conf.sample and gtm_proxy.conf.sample. +Sample files will be installed in the "share" directory at the installation directory. + +3. GTM-Standby + +To prevent GTM from SPOF, GTM-Standby feature is now under the development. +At present, only a basic feature is available. GTM-Standby can backup GTM. +When GTM fails, GTM-Standby can fail-over. GTM-Proxy can reconnect to the +new GTM without sacrificing running transactions. However, please note that +GTM-Standby has to connect to GTM before any GTM-Proxy connects to it. + +Peple who modify GTM code should be careful to test this feature works. +The test will be: + +1) Start GTM in ACT mode, and the another GTM as Standby mode. +2) Start more than one GTM-Proxies. +3) Start datanode/coordinator +4) Run psql +5) While psql is in a transaction block, promote GTM-Standby and then + reconnect GTM-Proxy to the promoted GTM. +6) See if transaction keeps running. + +The test sequence will be expanded as GTM-Standby feature improves. diff --git a/src/gtm/common/Makefile b/src/gtm/common/Makefile index c295fb43b1..769ed16876 100644 --- a/src/gtm/common/Makefile +++ b/src/gtm/common/Makefile @@ -13,9 +13,16 @@ SO_MINOR_VERSION= 0 LDFLAGS=-L$(top_builddir)/common -L$(top_builddir)/libpq LIBS=-lpthread -OBJS=aset.o mcxt.o gtm_utils.o elog.o assert.o stringinfo.o gtm_lock.o gtm_list.o gtm_serialize.o gtm_serialize_debug.o +OBJS=gtm_opt_scanner.o aset.o mcxt.o gtm_utils.o elog.o assert.o stringinfo.o gtm_lock.o gtm_list.o gtm_serialize.o gtm_serialize_debug.o gtm_opt_handler.o -all:all-lib +all:gtm_opt_scanner.c all-lib + +gtm_opt_hander.o:gtm_opt_scanner.h gtm_opt_handler.c + +gtm_opt_scanner.h:gtm_opt_scanner.c + +gtm_opt_scanner.c:gtm_opt_scanner.l + $(FLEX) $(FLEXFLAGS) --header-file=gtm_opt_scanner.h -o'$@' $< # Shared library stuff include $(top_srcdir)/src/Makefile.shlib @@ -23,6 +30,8 @@ include $(top_srcdir)/src/Makefile.shlib clean: rm -f $(OBJS) rm -f libgtm.so libgtm.so.1 libgtm.so.1.0 + rm -f gtm_opt_scanner.c + rm -f gtm_opt_scanner.h distclean: clean diff --git a/src/gtm/common/gtm_opt_handler.c b/src/gtm/common/gtm_opt_handler.c new file mode 100644 index 0000000000..63490386b0 --- /dev/null +++ b/src/gtm/common/gtm_opt_handler.c @@ -0,0 +1,3522 @@ +/* -*-pgsql-c-*- */ +/* + * Scanner for the configuration file + * + * Copyright (c) 2000-2011, PostgreSQL Global Development Group + * + * src/backend/utils/misc/guc-file.l + */ + +#include "gtm/gtm.h" + +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdlib.h> + +#include "mb/pg_wchar.h" +#include "gtm/path.h" +#include "gtm/assert.h" +#include "gtm/gtm_opt.h" +#include "gtm/gtm_opt_tables.h" +#include "gtm/elog.h" +#include "./gtm_opt_scanner.h" + + +/* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */ +#undef fprintf +#define fprintf(file, fmt, msg) ereport(ERROR, (errmsg_internal("%s", msg))) + +enum { + GTMOPT_ID = 1, + GTMOPT_STRING = 2, + GTMOPT_INTEGER = 3, + GTMOPT_REAL = 4, + GTMOPT_EQUALS = 5, + GTMOPT_UNQUOTED_STRING = 6, + GTMOPT_QUALIFIED_ID = 7, + GTMOPT_EOL = 99, + GTMOPT_ERROR = 100 +}; + +static unsigned int ConfigFileLineno; + +/* flex fails to supply a prototype for GTMOPT_yylex, so provide one */ +int GTMOPT_GTMOPT_yylex(void); + +/* Functions defeined in this file */ +static char *GTMOPT_scanstr(const char *s); +static struct config_generic *find_option(const char *name, bool create_placeholders, int elevel); +static char *gtm_opt_strdup(int elevel, const char *src); +static int gtm_opt_name_compare(const char *namea, const char *nameb); +struct config_generic **get_gtm_opt_variables(void); +void build_gtm_opt_variables(void); +static bool gtm_opt_parse_bool(const char *value, bool *result); +static bool gtm_opt_parse_bool_with_len(const char *value, size_t len, bool *result); +static void set_config_sourcefile(const char *name, char *sourcefile, int sourceline); +static int gtm_opt_var_compare(const void *a, const void *b); +static void InitializeOneGTMOption(struct config_generic * gconf); +static void ReportGTMOption(struct config_generic * record); +static char *_ShowOption(struct config_generic * record, bool use_units); + +/* + * Variables to bel fed by specific option definition: gtm_opt.c and gtm_proxy_opt.c + */ +extern char *GTMConfigFileName; +extern char *data_directory; +extern struct config_generic **gtm_opt_variables; +extern int num_gtm_opt_variables; +extern int size_gtm_opt_variables; +extern bool reporting_enabled; /* TRUE to enable GTMOPT_REPORT */ +extern char *config_filename; /* Default configuration file name */ +extern int GTMOptUpdateCount; /* Indicates when specific option is updated */ +extern bool isStartUp; + +/* + * Tables of options: to be defined in gtm_opt.c and gtm_proxy_opt.c + */ +extern struct config_bool ConfigureNamesBool[]; +extern struct config_int ConfigureNamesInt[]; +extern struct config_real ConfigureNamesReal[]; +extern struct config_string ConfigureNamesString[]; +extern struct config_enum ConfigureNamesEnum[]; + +/* + * Note: MAX_BACKENDS is limited to 2^23-1 because inval.c stores the + * backend ID as a 3-byte signed integer. Even if that limitation were + * removed, we still could not exceed INT_MAX/4 because some places compute + * 4*MaxBackends without any overflow check. This is rechecked in + * check_maxconnections, since MaxBackends is computed as MaxConnections + * plus autovacuum_max_workers plus one (for the autovacuum launcher). + */ +#define MAX_BACKENDS 0x7fffff + +#define KB_PER_MB (1024) +#define KB_PER_GB (1024*1024) + +#define MS_PER_S 1000 +#define S_PER_MIN 60 +#define MS_PER_MIN (1000 * 60) +#define MIN_PER_H 60 +#define S_PER_H (60 * 60) +#define MS_PER_H (1000 * 60 * 60) +#define MIN_PER_D (60 * 24) +#define S_PER_D (60 * 60 * 24) +#define MS_PER_D (1000 * 60 * 60 * 24) + +/* + * Exported function to read and process the configuration file. The + * parameter indicates in what context the file is being read --- either + * postmaster startup (including standalone-backend startup) or SIGHUP. + * All options mentioned in the configuration file are set to new values. + * If an error occurs, no values will be changed. + */ +void +ProcessConfigFile(GtmOptContext context) +{ + int elevel; + ConfigVariable *item, + *head, + *tail; + char *cvc = NULL; + int i; + + Assert((context == GTMC_STARTUP || context == GTMC_SIGHUP)); + + if (context == GTMC_SIGHUP) + elevel = DEBUG2; + else + elevel = ERROR; + + /* Parse the file into a list of option names and values */ + head = tail = NULL; + + if (!ParseConfigFile(GTMConfigFileName, NULL, 0, elevel, &head, &tail)) + goto cleanup_list; + +#if 0 + /* No custom_variable_classes now */ + /* + * This part of the code remained the same as original guc.c because + * we might want to have custom variable class for gtm.conf. + */ + /* + * We need the proposed new value of custom_variable_classes to check + * custom variables with. ParseConfigFile ensured that if it's in + * the file, it's first in the list. But first check to see if we + * have an active value from the command line, which should override + * the file in any case. (Since there's no relevant env var, the + * only possible nondefault sources are the file and ARGV.) + */ + cvc_struct = (struct config_string *) + find_option("custom_variable_classes", false, elevel); + Assert(cvc_struct); + if (cvc_struct->gen.reset_source > GTMC_S_FILE) + { + cvc = gtm_opt_strdup(elevel, cvc_struct->reset_val); + if (cvc == NULL) + goto cleanup_list; + } + else if (head != NULL && + gtm_opt_name_compare(head->name, "custom_variable_classes") == 0) + { + /* + * Need to canonicalize the value by calling the check hook. + */ + void *extra = NULL; + + cvc = gtm_opt_strdup(elevel, head->value); + if (cvc == NULL) + goto cleanup_list; + if (extra) + free(extra); + } +#endif + + /* + * Mark all extant GUC variables as not present in the config file. + * We need this so that we can tell below which ones have been removed + * from the file since we last processed it. + */ + for (i = 0; i < num_gtm_opt_variables; i++) + { + struct config_generic *gconf = gtm_opt_variables[i]; + + gconf->status &= ~GTMOPT_IS_IN_FILE; + } + + /* + * Check if all options are valid. As a side-effect, the GTMOPT_IS_IN_FILE + * flag is set on each GUC variable mentioned in the list. + */ + for (item = head; item; item = item->next) + { + char *sep = strchr(item->name, GTMOPT_QUALIFIER_SEPARATOR); + + if (sep) + { + /* + * There is no GUC entry. If we called set_config_option then + * it would make a placeholder, which we don't want to do yet, + * since we could still fail further down the list. Do nothing + * (assuming that making the placeholder will succeed later). + */ + if (find_option(item->name, false, elevel) == NULL) + continue; + /* + * 3. There is already a GUC entry (either real or placeholder) for + * the variable. In this case we should let set_config_option + * check it, since the assignment could well fail if it's a real + * entry. + */ + } + + if (!set_config_option(item->name, item->value, context, + GTMC_S_FILE, false)) + goto cleanup_list; + } + + /* + * Check for variables having been removed from the config file, and + * revert their reset values (and perhaps also effective values) to the + * boot-time defaults. If such a variable can't be changed after startup, + * just throw a warning and continue. (This is analogous to the fact that + * set_config_option only throws a warning for a new but different value. + * If we wanted to make it a hard error, we'd need an extra pass over the + * list so that we could throw the error before starting to apply + * changes.) + */ + for (i = 0; i < num_gtm_opt_variables; i++) + { + struct config_generic *gconf = gtm_opt_variables[i]; + GtmOptStack *stack; + + if (gconf->reset_source != GTMC_S_FILE || + (gconf->status & GTMOPT_IS_IN_FILE)) + continue; + if (gconf->context < GTMC_SIGHUP) + { + /* + * In the original code, errcode() stores specified error code to sqlerrcode, which does not + * exist in GTM. + */ + if (isStartUp) + { + write_stderr("parameter \"%s\" cannot be changed without restarting the server", + gconf->name); + } + else + { + ereport(elevel, + (0, + errmsg("parameter \"%s\" cannot be changed without restarting the server", + gconf->name))); + } + continue; + } + + /* + * Reset any "file" sources to "default", else set_config_option + * will not override those settings. + */ + if (gconf->reset_source == GTMC_S_FILE) + gconf->reset_source = GTMC_S_DEFAULT; + if (gconf->source == GTMC_S_FILE) + gconf->source = GTMC_S_DEFAULT; + for (stack = gconf->stack; stack; stack = stack->prev) + { + if (stack->source == GTMC_S_FILE) + stack->source = GTMC_S_DEFAULT; + } + + /* Now we can re-apply the wired-in default (i.e., the boot_val) */ + set_config_option(gconf->name, NULL, context, GTMC_S_DEFAULT, + true); + if (context == GTMC_SIGHUP) + { + if (isStartUp) + { + write_stderr("parameter \"%s\" removed from configuration file, reset to default\n", + gconf->name); + } + else + { + ereport(elevel, + (errmsg("parameter \"%s\" removed from configuration file, reset to default", + gconf->name))); + } + } + } + + /* + * Restore any variables determined by environment variables or + * dynamically-computed defaults. This is a no-op except in the case + * where one of these had been in the config file and is now removed. + * + * In particular, we *must not* do this during the postmaster's + * initial loading of the file, since the timezone functions in + * particular should be run only after initialization is complete. + * + * XXX this is an unmaintainable crock, because we have to know how + * to set (or at least what to call to set) every variable that could + * potentially have GTMC_S_DYNAMIC_DEFAULT or GTMC_S_ENV_VAR source. + * However, there's no time to redesign it for 9.1. + */ + + /* If we got here all the options checked out okay, so apply them. */ + for (item = head; item; item = item->next) + { + char *pre_value = NULL; + + if (set_config_option(item->name, item->value, context, + GTMC_S_FILE, true)) + { + set_config_sourcefile(item->name, item->filename, + item->sourceline); + + if (pre_value) + { + const char *post_value = GetConfigOption(item->name, false); + + if (!post_value) + post_value = ""; + if (strcmp(pre_value, post_value) != 0) + { + if (isStartUp) + { + write_stderr("parameter \"%s\" changed to \"%s\"\n", + item->name, item->value); + } + else + { + ereport(elevel, + (errmsg("parameter \"%s\" changed to \"%s\"", + item->name, item->value))); + } + } + } + } + + if (pre_value) + free(pre_value); + } + + /* PGXCTODO: configuration file reload time update */ + +cleanup_list: + FreeConfigVariables(head); + if (cvc) + free(cvc); +} + +/* + * See next function for details. This one will just work with a config_file + * name rather than an already opened File Descriptor + */ +bool +ParseConfigFile(const char *config_file, const char *calling_file, + int depth, int elevel, + ConfigVariable **head_p, + ConfigVariable **tail_p) +{ + bool OK = true; + FILE *fp; + char abs_path[MAXPGPATH]; + + /* + * Reject too-deep include nesting depth. This is just a safety check + * to avoid dumping core due to stack overflow if an include file loops + * back to itself. The maximum nesting depth is pretty arbitrary. + */ + if (depth > 10) + { + if (isStartUp) + { + write_stderr("could not open configuration file \"%s\": maximum nesting depth exceeded\n", + config_file); + } + else + { + ereport(elevel, + (0, + errmsg("could not open configuration file \"%s\": maximum nesting depth exceeded", + config_file))); + } + return false; + } + + /* + * If config_file is a relative path, convert to absolute. We consider + * it to be relative to the directory holding the calling file. + */ + if (!is_absolute_path(config_file)) + { + if (calling_file != NULL) + { + strlcpy(abs_path, calling_file, sizeof(abs_path)); + get_parent_directory(abs_path); + join_path_components(abs_path, abs_path, config_file); + canonicalize_path(abs_path); + config_file = abs_path; + } + else + { + /* + * calling_file is NULL, we make an absolute path from $PGDATA + */ + join_path_components(abs_path, data_directory, config_file); + canonicalize_path(abs_path); + config_file = abs_path; + } + } + + fp = fopen(config_file, "r"); + if (!fp) + { + if (isStartUp) + { + write_stderr("could not open configuration file \"%s\": %m\n", + config_file); + } + else + { + ereport(elevel, + (0, + errmsg("could not open configuration file \"%s\": %m", + config_file))); + } + return false; + } + + OK = ParseConfigFp(fp, config_file, depth, elevel, head_p, tail_p); + + fclose(fp); + + return OK; +} + +/* + * Read and parse a single configuration file. This function recurses + * to handle "include" directives. + * + * Input parameters: + * fp: file pointer from AllocateFile for the configuration file to parse + * config_file: absolute or relative path of file to read + * depth: recursion depth (used only to prevent infinite recursion) + * elevel: error logging level determined by ProcessConfigFile() + * Output parameters: + * head_p, tail_p: head and tail of linked list of name/value pairs + * + * *head_p and *tail_p must be initialized to NULL before calling the outer + * recursion level. On exit, they contain a list of name-value pairs read + * from the input file(s). + * + * Returns TRUE if successful, FALSE if an error occurred. The error has + * already been ereport'd, it is only necessary for the caller to clean up + * its own state and release the name/value pairs list. + * + * Note: if elevel >= ERROR then an error will not return control to the + * caller, and internal state such as open files will not be cleaned up. + * This case occurs only during postmaster or standalone-backend startup, + * where an error will lead to immediate process exit anyway; so there is + * no point in contorting the code so it can clean up nicely. + */ +bool +ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel, + ConfigVariable **head_p, ConfigVariable **tail_p) +{ + bool OK = true; + YY_BUFFER_STATE lex_buffer; + int token; + + /* + * Parse + */ + lex_buffer = GTMOPT_yy_create_buffer(fp, YY_BUF_SIZE); + GTMOPT_yy_switch_to_buffer(lex_buffer); + + ConfigFileLineno = 1; + + /* This loop iterates once per logical line */ + while ((token = GTMOPT_yylex())) + { + char *opt_name, *opt_value; + ConfigVariable *item; + + if (token == GTMOPT_EOL) /* empty or comment line */ + continue; + + /* first token on line is option name */ + if (token != GTMOPT_ID && token != GTMOPT_QUALIFIED_ID) + goto parse_error; + opt_name = strdup(GTMOPT_yytext); + + /* next we have an optional equal sign; discard if present */ + token = GTMOPT_yylex(); + if (token == GTMOPT_EQUALS) + token = GTMOPT_yylex(); + + /* now we must have the option value */ + if (token != GTMOPT_ID && + token != GTMOPT_STRING && + token != GTMOPT_INTEGER && + token != GTMOPT_REAL && + token != GTMOPT_UNQUOTED_STRING) + goto parse_error; + if (token == GTMOPT_STRING) /* strip quotes and escapes */ + opt_value = GTMOPT_scanstr(GTMOPT_yytext); + else + opt_value = strdup(GTMOPT_yytext); + + /* now we'd like an end of line, or possibly EOF */ + token = GTMOPT_yylex(); + if (token != GTMOPT_EOL) + { + if (token != 0) + goto parse_error; + /* treat EOF like \n for line numbering purposes, cf bug 4752 */ + ConfigFileLineno++; + } + + /* OK, process the option name and value */ + if (gtm_opt_name_compare(opt_name, "include") == 0) + { + /* + * An include directive isn't a variable and should be processed + * immediately. + */ + unsigned int save_ConfigFileLineno = ConfigFileLineno; + + if (!ParseConfigFile(opt_value, config_file, + depth + 1, elevel, + head_p, tail_p)) + { + free(opt_name); + free(opt_value); + OK = false; + goto cleanup_exit; + } + GTMOPT_yy_switch_to_buffer(lex_buffer); + ConfigFileLineno = save_ConfigFileLineno; + free(opt_name); + free(opt_value); + } + else if (gtm_opt_name_compare(opt_name, "custom_variable_classes") == 0) + { + /* + * This variable must be processed first as it controls + * the validity of other variables; so it goes at the head + * of the result list. If we already found a value for it, + * replace with this one. + */ + item = *head_p; + if (item != NULL && + gtm_opt_name_compare(item->name, "custom_variable_classes") == 0) + { + /* replace existing head item */ + free(item->name); + free(item->value); + item->name = opt_name; + item->value = opt_value; + item->filename = strdup(config_file); + item->sourceline = ConfigFileLineno-1; + } + else + { + /* prepend to list */ + item = malloc(sizeof *item); + item->name = opt_name; + item->value = opt_value; + item->filename = strdup(config_file); + item->sourceline = ConfigFileLineno-1; + item->next = *head_p; + *head_p = item; + if (*tail_p == NULL) + *tail_p = item; + } + } + else + { + /* ordinary variable, append to list */ + item = malloc(sizeof *item); + item->name = opt_name; + item->value = opt_value; + item->filename = strdup(config_file); + item->sourceline = ConfigFileLineno-1; + item->next = NULL; + if (*head_p == NULL) + *head_p = item; + else + (*tail_p)->next = item; + *tail_p = item; + } + + /* break out of loop if read EOF, else loop for next line */ + if (token == 0) + break; + } + + /* successful completion of parsing */ + goto cleanup_exit; + + parse_error: + if (token == GTMOPT_EOL || token == 0) + { + if (isStartUp) + { + write_stderr("syntax error in file \"%s\" line %u, near end of line\n", + config_file, ConfigFileLineno - 1); + } + else + { + ereport(elevel, + (0, + errmsg("syntax error in file \"%s\" line %u, near end of line", + config_file, ConfigFileLineno - 1))); + } + } + else + { + if (isStartUp) + { + write_stderr("syntax error in file \"%s\" line %u, near token \"%s\"\n", + config_file, ConfigFileLineno, GTMOPT_yytext); + } + else + { + ereport(elevel, + (0, + errmsg("syntax error in file \"%s\" line %u, near token \"%s\"", + config_file, ConfigFileLineno, GTMOPT_yytext))); + } + } + OK = false; + +cleanup_exit: + GTMOPT_yy_delete_buffer(lex_buffer); + return OK; +} + + +/* + * Free a list of ConfigVariables, including the names and the values + */ +void +FreeConfigVariables(ConfigVariable *list) +{ + ConfigVariable *item; + + item = list; + while (item) + { + ConfigVariable *next = item->next; + + free(item->name); + free(item->value); + free(item->filename); + free(item); + item = next; + } +} + + +/* + * scanstr + * + * Strip the quotes surrounding the given string, and collapse any embedded + * '' sequences and backslash escapes. + * + * the string returned is malloc'd and should eventually be free'd by the + * caller. + */ +static char * +GTMOPT_scanstr(const char *s) +{ + char *newStr; + int len, + i, + j; + + Assert(s != NULL && s[0] == '\''); + len = strlen(s); + Assert(len >= 2); + Assert(s[len-1] == '\''); + + /* Skip the leading quote; we'll handle the trailing quote below */ + s++, len--; + + /* Since len still includes trailing quote, this is enough space */ + newStr = malloc(len); + + for (i = 0, j = 0; i < len; i++) + { + if (s[i] == '\\') + { + i++; + switch (s[i]) + { + case 'b': + newStr[j] = '\b'; + break; + case 'f': + newStr[j] = '\f'; + break; + case 'n': + newStr[j] = '\n'; + break; + case 'r': + newStr[j] = '\r'; + break; + case 't': + newStr[j] = '\t'; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + int k; + long octVal = 0; + + for (k = 0; + s[i + k] >= '0' && s[i + k] <= '7' && k < 3; + k++) + octVal = (octVal << 3) + (s[i + k] - '0'); + i += k - 1; + newStr[j] = ((char) octVal); + } + break; + default: + newStr[j] = s[i]; + break; + } /* switch */ + } + else if (s[i] == '\'' && s[i+1] == '\'') + { + /* doubled quote becomes just one quote */ + newStr[j] = s[++i]; + } + else + newStr[j] = s[i]; + j++; + } + + /* We copied the ending quote to newStr, so replace with \0 */ + Assert(j > 0 && j <= len); + newStr[--j] = '\0'; + + return newStr; +} + +/* + * The following code includes most of the code ported from guc.c. + * Because they should be shared by gtm_opt.c and gtm_proxy_opt.c, they are placed here. + */ + +/* + * Some infrastructure for checking malloc/strdup/realloc calls + */ +static void * +gtm_opt_malloc(int elevel, size_t size) +{ + void *data; + + data = malloc(size); + if (data == NULL) + { + if (isStartUp) + { + write_stderr("out of memory\n"); + } + else + { + ereport(elevel, + (0, + errmsg("out of memory"))); + } + } + return data; +} + +#if 0 +/* PGXCTODO: this will be used for future extensions */ +static void * +gtm_opt_realloc(int elevel, void *old, size_t size) +{ + void *data; + + data = realloc(old, size); + if (data == NULL) + { + if (isStartUp) + { + write_stderr("out of memory\n"); + } + else + { + ereport(elevel, + (0, + errmsg("out of memory"))); + } + } + return data; +} +#endif + +static char * +gtm_opt_strdup(int elevel, const char *src) +{ + char *data; + + data = strdup(src); + if (data == NULL) + { + if (isStartUp) + { + write_stderr("out of memory\n"); + } + else + { + ereport(elevel, + (0, + errmsg("out of memory"))); + } + } + return data; +} + +/* + * Detect whether strval is referenced anywhere in a GTM string item + */ +static bool +string_field_used(struct config_string * conf, char *strval) +{ + GtmOptStack *stack; + + if (strval == *(conf->variable) || + strval == conf->reset_val || + strval == conf->boot_val) + return true; + for (stack = conf->gen.stack; stack; stack = stack->prev) + { + if (strval == stack->prior.val.stringval || + strval == stack->masked.val.stringval) + return true; + } + return false; +} + + +/* + * Support for assigning to a field of a string GTM item. Free the prior + * value if it's not referenced anywhere else in the item (including stacked + * states). + */ +static void +set_string_field(struct config_string * conf, char **field, char *newval) +{ + char *oldval = *field; + + /* Do the assignment */ + *field = newval; + + /* Free old value if it's not NULL and isn't referenced anymore */ + if (oldval && !string_field_used(conf, oldval)) + free(oldval); +} + + +/* + * Detect whether an "extra" struct is referenced anywhere in a GTM item + */ +static bool +extra_field_used(struct config_generic * gconf, void *extra) +{ + GtmOptStack *stack; + + if (extra == gconf->extra) + return true; + switch (gconf->vartype) + { + case GTMC_BOOL: + if (extra == ((struct config_bool *) gconf)->reset_extra) + return true; + break; + case GTMC_INT: + if (extra == ((struct config_int *) gconf)->reset_extra) + return true; + break; + case GTMC_REAL: + if (extra == ((struct config_real *) gconf)->reset_extra) + return true; + break; + case GTMC_STRING: + if (extra == ((struct config_string *) gconf)->reset_extra) + return true; + break; + case GTMC_ENUM: + if (extra == ((struct config_enum *) gconf)->reset_extra) + return true; + break; + } + for (stack = gconf->stack; stack; stack = stack->prev) + { + if (extra == stack->prior.extra || + extra == stack->masked.extra) + return true; + } + + return false; +} + + +/* + * Support for assigning to an "extra" field of a GTM item. Free the prior + * value if it's not referenced anywhere else in the item (including stacked + * states). + */ +static void +set_extra_field(struct config_generic * gconf, void **field, void *newval) +{ + void *oldval = *field; + + /* Do the assignment */ + *field = newval; + + /* Free old value if it's not NULL and isn't referenced anymore */ + if (oldval && !extra_field_used(gconf, oldval)) + free(oldval); +} + + +/* + * Support for copying a variable's active value into a stack entry. + * The "extra" field associated with the active value is copied, too. + * + * NB: be sure stringval and extra fields of a new stack entry are + * initialized to NULL before this is used, else we'll try to free() them. + */ +static void +set_stack_value(struct config_generic * gconf, config_var_value *val) +{ + switch (gconf->vartype) + { + case GTMC_BOOL: + val->val.boolval = + *((struct config_bool *) gconf)->variable; + break; + case GTMC_INT: + val->val.intval = + *((struct config_int *) gconf)->variable; + break; + case GTMC_REAL: + val->val.realval = + *((struct config_real *) gconf)->variable; + break; + case GTMC_STRING: + set_string_field((struct config_string *) gconf, + &(val->val.stringval), + *((struct config_string *) gconf)->variable); + break; + case GTMC_ENUM: + val->val.enumval = + *((struct config_enum *) gconf)->variable; + break; + } + set_extra_field(gconf, &(val->extra), gconf->extra); +} + +#if 0 +/* PGXCTODO: This is let for future extension support */ +/* + * Support for discarding a no-longer-needed value in a stack entry. + * The "extra" field associated with the stack entry is cleared, too. + */ +static void +discard_stack_value(struct config_generic * gconf, config_var_value *val) +{ + switch (gconf->vartype) + { + case GTMC_BOOL: + case GTMC_INT: + case GTMC_REAL: + case GTMC_ENUM: + /* no need to do anything */ + break; + case GTMC_STRING: + set_string_field((struct config_string *) gconf, + &(val->val.stringval), + NULL); + break; + } + set_extra_field(gconf, &(val->extra), NULL); +} +#endif + +/* + * Fetch the sorted array pointer (exported for help_config.c's use ONLY) + */ +struct config_generic ** +get_gtm_opt_variables(void) +{ + return gtm_opt_variables; +} + +/* + * Build the sorted array. This is split out so that it could be + * re-executed after startup (eg, we could allow loadable modules to + * add vars, and then we'd need to re-sort). + */ +void +build_gtm_opt_variables(void) +{ + int size_vars; + int num_vars = 0; + struct config_generic **gtm_opt_vars; + int i; + + for (i = 0; ConfigureNamesBool[i].gen.name; i++) + { + struct config_bool *conf = &ConfigureNamesBool[i]; + + /* Rather than requiring vartype to be filled in by hand, do this: */ + conf->gen.vartype = GTMC_BOOL; + num_vars++; + } + + for (i = 0; ConfigureNamesInt[i].gen.name; i++) + { + struct config_int *conf = &ConfigureNamesInt[i]; + + conf->gen.vartype = GTMC_INT; + num_vars++; + } + + for (i = 0; ConfigureNamesReal[i].gen.name; i++) + { + struct config_real *conf = &ConfigureNamesReal[i]; + + conf->gen.vartype = GTMC_REAL; + num_vars++; + } + + for (i = 0; ConfigureNamesString[i].gen.name; i++) + { + struct config_string *conf = &ConfigureNamesString[i]; + + conf->gen.vartype = GTMC_STRING; + num_vars++; + } + + for (i = 0; ConfigureNamesEnum[i].gen.name; i++) + { + struct config_enum *conf = &ConfigureNamesEnum[i]; + + conf->gen.vartype = GTMC_ENUM; + num_vars++; + } + + /* + * Create table with 20% slack + */ + size_vars = num_vars + num_vars / 4; + + gtm_opt_vars = (struct config_generic **) + gtm_opt_malloc(FATAL, size_vars * sizeof(struct config_generic *)); + + num_vars = 0; + + for (i = 0; ConfigureNamesBool[i].gen.name; i++) + gtm_opt_vars[num_vars++] = &ConfigureNamesBool[i].gen; + + for (i = 0; ConfigureNamesInt[i].gen.name; i++) + gtm_opt_vars[num_vars++] = &ConfigureNamesInt[i].gen; + + for (i = 0; ConfigureNamesReal[i].gen.name; i++) + gtm_opt_vars[num_vars++] = &ConfigureNamesReal[i].gen; + + for (i = 0; ConfigureNamesString[i].gen.name; i++) + gtm_opt_vars[num_vars++] = &ConfigureNamesString[i].gen; + + for (i = 0; ConfigureNamesEnum[i].gen.name; i++) + gtm_opt_vars[num_vars++] = &ConfigureNamesEnum[i].gen; + + if (gtm_opt_variables) + free(gtm_opt_variables); + gtm_opt_variables = gtm_opt_vars; + num_gtm_opt_variables = num_vars; + size_gtm_opt_variables = size_vars; + qsort((void *) gtm_opt_variables, num_gtm_opt_variables, + sizeof(struct config_generic *), gtm_opt_var_compare); +} + + +#if 0 +/* PGXCTODO: This is let for future extension support */ +/* + * Add a new GTM variable to the list of known variables. The + * list is expanded if needed. + */ +static bool +add_gtm_opt_variable(struct config_generic * var, int elevel) +{ + if (num_gtm_opt_variables + 1 >= size_gtm_opt_variables) + { + /* + * Increase the vector by 25% + */ + int size_vars = size_gtm_opt_variables + size_gtm_opt_variables / 4; + struct config_generic **gtm_opt_vars; + + if (size_vars == 0) + { + size_vars = 100; + gtm_opt_vars = (struct config_generic **) + gtm_opt_malloc(elevel, size_vars * sizeof(struct config_generic *)); + } + else + { + gtm_opt_vars = (struct config_generic **) + gtm_opt_realloc(elevel, gtm_opt_variables, size_vars * sizeof(struct config_generic *)); + } + + if (gtm_opt_vars == NULL) + return false; /* out of memory */ + + gtm_opt_variables = gtm_opt_vars; + size_gtm_opt_variables = size_vars; + } + gtm_opt_variables[num_gtm_opt_variables++] = var; + qsort((void *) gtm_opt_variables, num_gtm_opt_variables, + sizeof(struct config_generic *), gtm_opt_var_compare); + return true; +} + + +/* + * Create and add a placeholder variable. It's presumed to belong + * to a valid custom variable class at this point. + */ +static struct config_generic * +add_placeholder_variable(const char *name, int elevel) +{ + size_t sz = sizeof(struct config_string) + sizeof(char *); + struct config_string *var; + struct config_generic *gen; + + var = (struct config_string *) gtm_opt_malloc(elevel, sz); + if (var == NULL) + return NULL; + memset(var, 0, sz); + gen = &var->gen; + + gen->name = gtm_opt_strdup(elevel, name); + if (gen->name == NULL) + { + free(var); + return NULL; + } + + gen->context = GTMC_USERSET; + gen->short_desc = "GTM placeholder variable"; + gen->flags = GTMOPT_NO_SHOW_ALL | GTMOPT_NOT_IN_SAMPLE | GTMOPT_CUSTOM_PLACEHOLDER; + gen->vartype = GTMC_STRING; + + /* + * The char* is allocated at the end of the struct since we have no + * 'static' place to point to. Note that the current value, as well as + * the boot and reset values, start out NULL. + */ + var->variable = (char **) (var + 1); + + if (!add_gtm_opt_variable((struct config_generic *) var, elevel)) + { + free((void *) gen->name); + free(var); + return NULL; + } + + return gen; +} +#endif + +/* + * Look up option NAME. If it exists, return a pointer to its record, + * else return NULL. If create_placeholders is TRUE, we'll create a + * placeholder record for a valid-looking custom variable name. + */ +static struct config_generic * +find_option(const char *name, bool create_placeholders, int elevel) +{ + const char **key = &name; + struct config_generic **res; + + Assert(name); + + /* + * By equating const char ** with struct config_generic *, we are assuming + * the name field is first in config_generic. + */ + res = (struct config_generic **) bsearch((void *) &key, + (void *) gtm_opt_variables, + num_gtm_opt_variables, + sizeof(struct config_generic *), + gtm_opt_var_compare); + if (res) + return *res; + + /* Unknown name */ + return NULL; +} + + +/* + * comparator for qsorting and bsearching gtm_opt_variables array + */ +static int +gtm_opt_var_compare(const void *a, const void *b) +{ + struct config_generic *confa = *(struct config_generic **) a; + struct config_generic *confb = *(struct config_generic **) b; + + return gtm_opt_name_compare(confa->name, confb->name); +} + + +/* + * the bare comparison function for GTM names + */ +static int +gtm_opt_name_compare(const char *namea, const char *nameb) +{ + /* + * The temptation to use strcasecmp() here must be resisted, because the + * array ordering has to remain stable across setlocale() calls. So, build + * our own with a simple ASCII-only downcasing. + */ + while (*namea && *nameb) + { + char cha = *namea++; + char chb = *nameb++; + + if (cha >= 'A' && cha <= 'Z') + cha += 'a' - 'A'; + if (chb >= 'A' && chb <= 'Z') + chb += 'a' - 'A'; + if (cha != chb) + return cha - chb; + } + if (*namea) + return 1; /* a is longer */ + if (*nameb) + return -1; /* b is longer */ + return 0; +} + + +/* + * Initialize GTM options during program startup. + * + * Note that we cannot read the config file yet, since we have not yet + * processed command-line switches. + */ +void +InitializeGTMOptions(void) +{ + int i; + + /* + * Build sorted array of all GTM variables. + */ + build_gtm_opt_variables(); + + /* + * Load all variables with their compiled-in defaults, and initialize + * status fields as needed. + */ + for (i = 0; i < num_gtm_opt_variables; i++) + { + InitializeOneGTMOption(gtm_opt_variables[i]); + } + + reporting_enabled = false; + +} + + +/* + * Initialize one GTM option variable to its compiled-in default. + * + * Note: the reason for calling check_hooks is not that we think the boot_val + * might fail, but that the hooks might wish to compute an "extra" struct. + */ +static void +InitializeOneGTMOption(struct config_generic * gconf) +{ + gconf->status = 0; + gconf->reset_source = GTMC_S_DEFAULT; + gconf->source = GTMC_S_DEFAULT; + gconf->stack = NULL; + gconf->extra = NULL; + gconf->sourcefile = NULL; + gconf->sourceline = 0; + gconf->context = GTMC_DEFAULT; + + switch (gconf->vartype) + { + case GTMC_BOOL: + { + struct config_bool *conf = (struct config_bool *) gconf; + bool newval = conf->boot_val; + void *extra = NULL; + + *conf->variable = conf->reset_val = newval; + conf->gen.extra = conf->reset_extra = extra; + break; + } + case GTMC_INT: + { + struct config_int *conf = (struct config_int *) gconf; + int newval = conf->boot_val; + void *extra = NULL; + + Assert(newval >= conf->min); + Assert(newval <= conf->max); + *conf->variable = conf->reset_val = newval; + conf->gen.extra = conf->reset_extra = extra; + break; + } + case GTMC_REAL: + { + struct config_real *conf = (struct config_real *) gconf; + double newval = conf->boot_val; + void *extra = NULL; + + Assert(newval >= conf->min); + Assert(newval <= conf->max); + *conf->variable = conf->reset_val = newval; + conf->gen.extra = conf->reset_extra = extra; + break; + } + case GTMC_STRING: + { + struct config_string *conf = (struct config_string *) gconf; + char *newval; + void *extra = NULL; + + /* non-NULL boot_val must always get strdup'd */ + if (conf->boot_val != NULL) + newval = gtm_opt_strdup(FATAL, conf->boot_val); + else + newval = NULL; + + *conf->variable = conf->reset_val = newval; + conf->gen.extra = conf->reset_extra = extra; + break; + } + case GTMC_ENUM: + { + struct config_enum *conf = (struct config_enum *) gconf; + int newval = conf->boot_val; + void *extra = NULL; + + *conf->variable = conf->reset_val = newval; + conf->gen.extra = conf->reset_extra = extra; + break; + } + } +} + + +/* + * Select the configuration files and data directory to be used, and + * do the initial read of postgresql.conf. + * + * This is called after processing command-line switches. + * userDoption is the -D switch value if any (NULL if unspecified). + * progname is just for use in error messages. + * + * Returns true on success; on failure, prints a suitable error message + * to stderr and returns false. + */ +bool +SelectConfigFiles(const char *userDoption, const char *progname) +{ + char *configdir; + char *fname; + struct stat stat_buf; + + /* configdir is -D option, or $PGDATA if no -D */ + if (userDoption) + configdir = make_absolute_path(userDoption); + else + configdir = NULL; + + /* + * Find the configuration file: if config_file was specified on the + * command line, use it, else use configdir/postgresql.conf. In any case + * ensure the result is an absolute path, so that it will be interpreted + * the same way by future backends. + */ + if (GTMConfigFileName) + { + if (GTMConfigFileName[0] == '/') + fname = make_absolute_path(GTMConfigFileName); + else + { + if (configdir) + { + fname = gtm_opt_malloc(FATAL, + strlen(configdir) + strlen(GTMConfigFileName) + 2); + sprintf(fname, "%s/%s", configdir, GTMConfigFileName); + } + else + fname = make_absolute_path(GTMConfigFileName); + } + } + else if (configdir) + { + fname = gtm_opt_malloc(FATAL, + strlen(configdir) + strlen(config_filename) + 2); + sprintf(fname, "%s/%s", configdir, config_filename); + } + else + { + write_stderr("%s does not know where to find the server configuration file.\n" + "You must specify the --config-file or -D invocation " + "option or set the PGDATA environment variable.\n", + progname); + return false; + } + + /* + * Set the GTMConfigFileName GTM variable to its final value, ensuring that + * it can't be overridden later. + */ + SetConfigOption("config_file", fname, GTMC_STARTUP, GTMC_S_OVERRIDE); + free(fname); + + /* + * Now read the config file for the first time. + */ + if (stat(GTMConfigFileName, &stat_buf) != 0) + { + write_stderr("%s cannot access the server configuration file \"%s\": %s\n", + progname, GTMConfigFileName, strerror(errno)); + return false; + } + + ProcessConfigFile(GTMC_STARTUP); + + free(configdir); + + return true; +} + +/* + * Reset all options to their saved default values (implements RESET ALL) + */ +void +ResetAllOptions(void) +{ + int i; + + for (i = 0; i < num_gtm_opt_variables; i++) + { + struct config_generic *gconf = gtm_opt_variables[i]; + + /* Don't reset if special exclusion from RESET ALL */ + if (gconf->flags & GTMOPT_NO_RESET_ALL) + continue; + /* No need to reset if wasn't SET */ + if (gconf->source <= GTMC_S_OVERRIDE) + continue; + + switch (gconf->vartype) + { + case GTMC_BOOL: + { + struct config_bool *conf = (struct config_bool *) gconf; + + *conf->variable = conf->reset_val; + set_extra_field(&conf->gen, &conf->gen.extra, + conf->reset_extra); + break; + } + case GTMC_INT: + { + struct config_int *conf = (struct config_int *) gconf; + + *conf->variable = conf->reset_val; + set_extra_field(&conf->gen, &conf->gen.extra, + conf->reset_extra); + break; + } + case GTMC_REAL: + { + struct config_real *conf = (struct config_real *) gconf; + + *conf->variable = conf->reset_val; + set_extra_field(&conf->gen, &conf->gen.extra, + conf->reset_extra); + break; + } + case GTMC_STRING: + { + struct config_string *conf = (struct config_string *) gconf; + + set_string_field(conf, conf->variable, conf->reset_val); + set_extra_field(&conf->gen, &conf->gen.extra, + conf->reset_extra); + break; + } + case GTMC_ENUM: + { + struct config_enum *conf = (struct config_enum *) gconf; + + *conf->variable = conf->reset_val; + set_extra_field(&conf->gen, &conf->gen.extra, + conf->reset_extra); + break; + } + } + + gconf->source = gconf->reset_source; + + if (gconf->flags & GTMOPT_REPORT) + ReportGTMOption(gconf); + } +} + + + +/* + * push_old_value + * Push previous state during transactional assignment to a GTM variable. + */ +static void +push_old_value(struct config_generic * gconf) +{ + GtmOptStack *stack; + + /* If we're not inside a nest level, do nothing */ + if (GTMOptUpdateCount == 0) + return; + + /* Do we already have a stack entry of the current nest level? */ + stack = gconf->stack; + if (stack && stack->nest_level >= GTMOptUpdateCount) + return; + + /* + * Push a new stack entry + * + * We keep all the stack entries in TopTransactionContext for simplicity. + */ + stack = (GtmOptStack *) MemoryContextAllocZero(TopMemoryContext, + sizeof(GtmOptStack)); + + stack->prev = gconf->stack; + stack->nest_level = GTMOptUpdateCount; + stack->source = gconf->source; + set_stack_value(gconf, &stack->prior); + + gconf->stack = stack; +} + + + +/* + * Enter a new nesting level for GTM values. This is called at subtransaction + * start and when entering a function that has proconfig settings. NOTE that + * we must not risk error here, else subtransaction start will be unhappy. + */ +int +NewGTMNestLevel(void) +{ + return ++GTMOptUpdateCount; +} + +/* + * Try to parse value as an integer. The accepted formats are the + * usual decimal, octal, or hexadecimal formats, optionally followed by + * a unit name if "flags" indicates a unit is allowed. + * + * If the string parses okay, return true, else false. + * If okay and result is not NULL, return the value in *result. + * If not okay and hintmsg is not NULL, *hintmsg is set to a suitable + * HINT message, or NULL if no hint provided. + */ +bool +parse_int(const char *value, int *result, int flags, const char **hintmsg) +{ + int64 val; + char *endptr; + + /* To suppress compiler warnings, always set output params */ + if (result) + *result = 0; + if (hintmsg) + *hintmsg = NULL; + + /* We assume here that int64 is at least as wide as long */ + errno = 0; + val = strtol(value, &endptr, 0); + + if (endptr == value) + return false; /* no HINT for integer syntax error */ + + if (errno == ERANGE || val != (int64) ((int32) val)) + { + if (hintmsg) + *hintmsg = gettext_noop("Value exceeds integer range."); + return false; + } + + /* allow whitespace between integer and unit */ + while (isspace((unsigned char) *endptr)) + endptr++; + + /* Handle possible unit */ + if (*endptr != '\0') + { + /* + * Note: the multiple-switch coding technique here is a bit tedious, + * but seems necessary to avoid intermediate-value overflows. + */ + if (flags & GTMOPT_UNIT_MEMORY) + { + /* Set hint for use if no match or trailing garbage */ + if (hintmsg) + *hintmsg = gettext_noop("Valid units for this parameter are \"kB\", \"MB\", and \"GB\"."); + +#if BLCKSZ < 1024 || BLCKSZ > (1024*1024) +#error BLCKSZ must be between 1KB and 1MB +#endif +#if XLOG_BLCKSZ < 1024 || XLOG_BLCKSZ > (1024*1024) +#error XLOG_BLCKSZ must be between 1KB and 1MB +#endif + + if (strncmp(endptr, "kB", 2) == 0) + { + endptr += 2; + switch (flags & GTMOPT_UNIT_MEMORY) + { + case GTMOPT_UNIT_BLOCKS: + val /= (BLCKSZ / 1024); + break; + case GTMOPT_UNIT_XBLOCKS: + val /= (XLOG_BLCKSZ / 1024); + break; + } + } + else if (strncmp(endptr, "MB", 2) == 0) + { + endptr += 2; + switch (flags & GTMOPT_UNIT_MEMORY) + { + case GTMOPT_UNIT_KB: + val *= KB_PER_MB; + break; + case GTMOPT_UNIT_BLOCKS: + val *= KB_PER_MB / (BLCKSZ / 1024); + break; + case GTMOPT_UNIT_XBLOCKS: + val *= KB_PER_MB / (XLOG_BLCKSZ / 1024); + break; + } + } + else if (strncmp(endptr, "GB", 2) == 0) + { + endptr += 2; + switch (flags & GTMOPT_UNIT_MEMORY) + { + case GTMOPT_UNIT_KB: + val *= KB_PER_GB; + break; + case GTMOPT_UNIT_BLOCKS: + val *= KB_PER_GB / (BLCKSZ / 1024); + break; + case GTMOPT_UNIT_XBLOCKS: + val *= KB_PER_GB / (XLOG_BLCKSZ / 1024); + break; + } + } + } + else if (flags & GTMOPT_UNIT_TIME) + { + /* Set hint for use if no match or trailing garbage */ + if (hintmsg) + *hintmsg = gettext_noop("Valid units for this parameter are \"ms\", \"s\", \"min\", \"h\", and \"d\"."); + + if (strncmp(endptr, "ms", 2) == 0) + { + endptr += 2; + switch (flags & GTMOPT_UNIT_TIME) + { + case GTMOPT_UNIT_S: + val /= MS_PER_S; + break; + case GTMOPT_UNIT_MIN: + val /= MS_PER_MIN; + break; + } + } + else if (strncmp(endptr, "s", 1) == 0) + { + endptr += 1; + switch (flags & GTMOPT_UNIT_TIME) + { + case GTMOPT_UNIT_MS: + val *= MS_PER_S; + break; + case GTMOPT_UNIT_MIN: + val /= S_PER_MIN; + break; + } + } + else if (strncmp(endptr, "min", 3) == 0) + { + endptr += 3; + switch (flags & GTMOPT_UNIT_TIME) + { + case GTMOPT_UNIT_MS: + val *= MS_PER_MIN; + break; + case GTMOPT_UNIT_S: + val *= S_PER_MIN; + break; + } + } + else if (strncmp(endptr, "h", 1) == 0) + { + endptr += 1; + switch (flags & GTMOPT_UNIT_TIME) + { + case GTMOPT_UNIT_MS: + val *= MS_PER_H; + break; + case GTMOPT_UNIT_S: + val *= S_PER_H; + break; + case GTMOPT_UNIT_MIN: + val *= MIN_PER_H; + break; + } + } + else if (strncmp(endptr, "d", 1) == 0) + { + endptr += 1; + switch (flags & GTMOPT_UNIT_TIME) + { + case GTMOPT_UNIT_MS: + val *= MS_PER_D; + break; + case GTMOPT_UNIT_S: + val *= S_PER_D; + break; + case GTMOPT_UNIT_MIN: + val *= MIN_PER_D; + break; + } + } + } + + /* allow whitespace after unit */ + while (isspace((unsigned char) *endptr)) + endptr++; + + if (*endptr != '\0') + return false; /* appropriate hint, if any, already set */ + + /* Check for overflow due to units conversion */ + if (val != (int64) ((int32) val)) + { + if (hintmsg) + *hintmsg = gettext_noop("Value exceeds integer range."); + return false; + } + } + + if (result) + *result = (int) val; + return true; +} + + + +/* + * Try to parse value as a floating point number in the usual format. + * If the string parses okay, return true, else false. + * If okay and result is not NULL, return the value in *result. + */ +bool +parse_real(const char *value, double *result) +{ + double val; + char *endptr; + + if (result) + *result = 0; /* suppress compiler warning */ + + errno = 0; + val = strtod(value, &endptr); + if (endptr == value || errno == ERANGE) + return false; + + /* allow whitespace after number */ + while (isspace((unsigned char) *endptr)) + endptr++; + if (*endptr != '\0') + return false; + + if (result) + *result = val; + return true; +} + + + +/* + * Lookup the value for an enum option with the selected name + * (case-insensitive). + * If the enum option is found, sets the retval value and returns + * true. If it's not found, return FALSE and retval is set to 0. + */ +bool +config_enum_lookup_by_name(struct config_enum * record, const char *value, + int *retval) +{ + const struct config_enum_entry *entry; + + for (entry = record->options; entry && entry->name; entry++) + { + if (pg_strcasecmp(value, entry->name) == 0) + { + *retval = entry->val; + return TRUE; + } + } + + *retval = 0; + return FALSE; +} + + + +/* + * Return a list of all available options for an enum, excluding + * hidden ones, separated by the given separator. + * If prefix is non-NULL, it is added before the first enum value. + * If suffix is non-NULL, it is added to the end of the string. + */ +static char * +config_enum_get_options(struct config_enum * record, const char *prefix, + const char *suffix, const char *separator) +{ + const struct config_enum_entry *entry; + StringInfoData retstr; + int seplen; + + initStringInfo(&retstr); + appendStringInfoString(&retstr, prefix); + + seplen = strlen(separator); + for (entry = record->options; entry && entry->name; entry++) + { + if (!entry->hidden) + { + appendStringInfoString(&retstr, entry->name); + appendBinaryStringInfo(&retstr, separator, seplen); + } + } + + /* + * All the entries may have been hidden, leaving the string empty if no + * prefix was given. This indicates a broken GTM setup, since there is no + * use for an enum without any values, so we just check to make sure we + * don't write to invalid memory instead of actually trying to do + * something smart with it. + */ + if (retstr.len >= seplen) + { + /* Replace final separator */ + retstr.data[retstr.len - seplen] = '\0'; + retstr.len -= seplen; + } + + appendStringInfoString(&retstr, suffix); + + return retstr.data; +} + + +/* + * Sets option `name' to given value. The value should be a string + * which is going to be parsed and converted to the appropriate data + * type. The context and source parameters indicate in which context this + * function is being called so it can apply the access restrictions + * properly. + * + * If value is NULL, set the option to its default value (normally the + * reset_val, but if source == GTMC_S_DEFAULT we instead use the boot_val). + * + * action indicates whether to set the value globally in the session, locally + * to the current top transaction, or just for the duration of a function call. + * + * If changeVal is false then don't really set the option but do all + * the checks to see if it would work. + * + * If there is an error (non-existing option, invalid value) then an + * ereport(ERROR) is thrown *unless* this is called in a context where we + * don't want to ereport (currently, startup or SIGHUP config file reread). + * In that case we write a suitable error message via ereport(LOG) and + * return false. This is working around the deficiencies in the ereport + * mechanism, so don't blame me. In all other cases, the function + * returns true, including cases where the input is valid but we chose + * not to apply it because of context or source-priority considerations. + * + * See also SetConfigOption for an external interface. + */ +bool +set_config_option(const char *name, const char *value, + GtmOptContext context, GtmOptSource source, + bool changeVal) +{ + struct config_generic *record; + int elevel; + bool prohibitValueChange = false; + bool makeDefault; + + if (context == GTMC_SIGHUP || source == GTMC_S_DEFAULT) + { + /* + * To avoid cluttering the log, only the postmaster bleats loudly + * about problems with the config file. + */ + elevel = DEBUG3; + } + else if (source == GTMC_S_DATABASE || source == GTMC_S_USER || + source == GTMC_S_DATABASE_USER) + elevel = WARNING; + else + elevel = ERROR; + + record = find_option(name, true, elevel); + if (record == NULL) + { + if (isStartUp) + { + write_stderr("unrecognized configuration parameter \"%s\"\n", name); + } + else + { + ereport(elevel, + (0, + errmsg("unrecognized configuration parameter \"%s\"", name))); + } + return false; + } + + /* + * If source is postgresql.conf, mark the found record with + * GTMOPT_IS_IN_FILE. This is for the convenience of ProcessConfigFile. Note + * that we do it even if changeVal is false, since ProcessConfigFile wants + * the marking to occur during its testing pass. + */ + if (source == GTMC_S_FILE) + record->status |= GTMOPT_IS_IN_FILE; + + /* + * Check if the option can be set at this time. See guc.h for the precise + * rules. + */ + switch (record->context) + { + case GTMC_DEFAULT: + case GTMC_STARTUP: + if (context == GTMC_SIGHUP) + { + /* + * We are re-reading a GTMC_POSTMASTER variable from + * postgresql.conf. We can't change the setting, so we should + * give a warning if the DBA tries to change it. However, + * because of variant formats, canonicalization by check + * hooks, etc, we can't just compare the given string directly + * to what's stored. Set a flag to check below after we have + * the final storable value. + * + * During the "checking" pass we just do nothing, to avoid + * printing the warning twice. + */ + if (!changeVal) + return true; + + prohibitValueChange = true; + } + else if (context != GTMC_STARTUP) + { + if (isStartUp) + { + write_stderr("parameter \"%s\" cannot be changed without restarting the server\n", + name); + } + else + { + ereport(elevel, + (0, + errmsg("parameter \"%s\" cannot be changed without restarting the server", + name))); + } + return false; + } + break; + case GTMC_SIGHUP: + if (context != GTMC_SIGHUP && context != GTMC_STARTUP) + { + if (isStartUp) + { + write_stderr("parameter \"%s\" cannot be changed now\n", + name); + } + else + { + ereport(elevel, + (0, + errmsg("parameter \"%s\" cannot be changed now", + name))); + } + return false; + } + + /* + * Hmm, the idea of the SIGHUP context is "ought to be global, but + * can be changed after postmaster start". But there's nothing + * that prevents a crafty administrator from sending SIGHUP + * signals to individual backends only. + */ + break; + default: + if (isStartUp) + { + write_stderr("GtmOptContext invalid (%d)\n", + context); + } + else + { + ereport(elevel, + (0, + errmsg("GtmOptContext invalid (%d)", + context))); + } + return false; + } + + /* + * Should we set reset/stacked values? (If so, the behavior is not + * transactional.) This is done either when we get a default value from + * the database's/user's/client's default settings or when we reset a + * value to its default. + */ + makeDefault = changeVal && (source <= GTMC_S_OVERRIDE) && + ((value != NULL) || source == GTMC_S_DEFAULT); + + /* + * Ignore attempted set if overridden by previously processed setting. + * However, if changeVal is false then plow ahead anyway since we are + * trying to find out if the value is potentially good, not actually use + * it. Also keep going if makeDefault is true, since we may want to set + * the reset/stacked values even if we can't set the variable itself. + */ + if (record->source > source) + { + if (changeVal && !makeDefault) + { + if (isStartUp) + { + write_stderr("\"%s\": setting ignored because previous source is higher priority\n", + name); + } + else + { + elog(DEBUG3, "\"%s\": setting ignored because previous source is higher priority", + name); + } + return true; + } + changeVal = false; + } + + /* + * Evaluate value and set variable. + */ + switch (record->vartype) + { + case GTMC_BOOL: + { + struct config_bool *conf = (struct config_bool *) record; + bool newval; + void *newextra = NULL; + + if (value) + { + if (!gtm_opt_parse_bool(value, &newval)) + { + if (isStartUp) + { + write_stderr("parameter \"%s\" requires a Boolean value\n", + name); + } + else + { + ereport(elevel, + (0, + errmsg("parameter \"%s\" requires a Boolean value", + name))); + } + return false; + } + } + else if (source == GTMC_S_DEFAULT) + { + newval = conf->boot_val; + } + else + { + newval = conf->reset_val; + newextra = conf->reset_extra; + source = conf->gen.reset_source; + } + + if (prohibitValueChange) + { + if (*conf->variable != newval) + { + if (isStartUp) + { + write_stderr("parameter \"%s\" cannot be changed without restarting the server\n", + name); + } + else + { + ereport(elevel, + (0, + errmsg("parameter \"%s\" cannot be changed without restarting the server", + name))); + } + } + return false; + } + + if (changeVal) + { + /* Save old value to support transaction abort */ + if (!makeDefault) + push_old_value(&conf->gen); + + *conf->variable = newval; + set_extra_field(&conf->gen, &conf->gen.extra, + newextra); + conf->gen.source = source; + } + if (makeDefault) + { + GtmOptStack *stack; + + if (conf->gen.reset_source <= source) + { + conf->reset_val = newval; + set_extra_field(&conf->gen, &conf->reset_extra, + newextra); + conf->gen.reset_source = source; + } + for (stack = conf->gen.stack; stack; stack = stack->prev) + { + if (stack->source <= source) + { + stack->prior.val.boolval = newval; + set_extra_field(&conf->gen, &stack->prior.extra, + newextra); + stack->source = source; + } + } + } + + /* Perhaps we didn't install newextra anywhere */ + if (newextra && !extra_field_used(&conf->gen, newextra)) + free(newextra); + break; + } + + case GTMC_INT: + { + struct config_int *conf = (struct config_int *) record; + int newval; + void *newextra = NULL; + + if (value) + { + const char *hintmsg; + + if (!parse_int(value, &newval, conf->gen.flags, &hintmsg)) + { + if (isStartUp) + { + write_stderr("invalid value for parameter \"%s\": \"%s\"\n", + name, value); + } + else + { + ereport(elevel, + (0, + errmsg("invalid value for parameter \"%s\": \"%s\"", + name, value), + hintmsg ? errhint("%s", _(hintmsg)) : 0)); + } + return false; + } + if (newval < conf->min || newval > conf->max) + { + if (isStartUp) + { + write_stderr("%d is outside the valid range for parameter \"%s\" (%d .. %d)\n", + newval, name, conf->min, conf->max); + } + else + { + ereport(elevel, + (0, + errmsg("%d is outside the valid range for parameter \"%s\" (%d .. %d)", + newval, name, conf->min, conf->max))); + } + return false; + } + } + else if (source == GTMC_S_DEFAULT) + { + newval = conf->boot_val; + } + else + { + newval = conf->reset_val; + newextra = conf->reset_extra; + source = conf->gen.reset_source; + } + + if (prohibitValueChange) + { + if (*conf->variable != newval) + { + if (isStartUp) + { + write_stderr("parameter \"%s\" cannot be changed without restarting the server\n", + name); + } + else + { + ereport(elevel, + (0, + errmsg("parameter \"%s\" cannot be changed without restarting the server", + name))); + } + } + return false; + } + + if (changeVal) + { + /* Save old value to support transaction abort */ + if (!makeDefault) + push_old_value(&conf->gen); + + *conf->variable = newval; + set_extra_field(&conf->gen, &conf->gen.extra, + newextra); + conf->gen.source = source; + } + if (makeDefault) + { + GtmOptStack *stack; + + if (conf->gen.reset_source <= source) + { + conf->reset_val = newval; + set_extra_field(&conf->gen, &conf->reset_extra, + newextra); + conf->gen.reset_source = source; + } + for (stack = conf->gen.stack; stack; stack = stack->prev) + { + if (stack->source <= source) + { + stack->prior.val.intval = newval; + set_extra_field(&conf->gen, &stack->prior.extra, + newextra); + stack->source = source; + } + } + } + + /* Perhaps we didn't install newextra anywhere */ + if (newextra && !extra_field_used(&conf->gen, newextra)) + free(newextra); + break; + } + + case GTMC_REAL: + { + struct config_real *conf = (struct config_real *) record; + double newval; + void *newextra = NULL; + + if (value) + { + if (!parse_real(value, &newval)) + { + if (isStartUp) + { + write_stderr("parameter \"%s\" requires a numeric value\n", + name); + } + else + { + ereport(elevel, + (0, + errmsg("parameter \"%s\" requires a numeric value", + name))); + } + return false; + } + if (newval < conf->min || newval > conf->max) + { + if (isStartUp) + { + write_stderr("%g is outside the valid range for parameter \"%s\" (%g .. %g)\n", + newval, name, conf->min, conf->max); + } + else + { + ereport(elevel, + (0, + errmsg("%g is outside the valid range for parameter \"%s\" (%g .. %g)", + newval, name, conf->min, conf->max))); + } + return false; + } + } + else if (source == GTMC_S_DEFAULT) + { + newval = conf->boot_val; + } + else + { + newval = conf->reset_val; + newextra = conf->reset_extra; + source = conf->gen.reset_source; + } + + if (prohibitValueChange) + { + if (*conf->variable != newval) + { + if (isStartUp) + { + write_stderr("parameter \"%s\" cannot be changed without restarting the server\n", + name); + } + else + { + ereport(elevel, + (0, + errmsg("parameter \"%s\" cannot be changed without restarting the server", + name))); + } + } + return false; + } + + if (changeVal) + { + /* Save old value to support transaction abort */ + if (!makeDefault) + push_old_value(&conf->gen); + + *conf->variable = newval; + set_extra_field(&conf->gen, &conf->gen.extra, + newextra); + conf->gen.source = source; + } + if (makeDefault) + { + GtmOptStack *stack; + + if (conf->gen.reset_source <= source) + { + conf->reset_val = newval; + set_extra_field(&conf->gen, &conf->reset_extra, + newextra); + conf->gen.reset_source = source; + } + for (stack = conf->gen.stack; stack; stack = stack->prev) + { + if (stack->source <= source) + { + stack->prior.val.realval = newval; + set_extra_field(&conf->gen, &stack->prior.extra, + newextra); + stack->source = source; + } + } + } + + /* Perhaps we didn't install newextra anywhere */ + if (newextra && !extra_field_used(&conf->gen, newextra)) + free(newextra); + break; + } + + case GTMC_STRING: + { + struct config_string *conf = (struct config_string *) record; + char *newval; + void *newextra = NULL; + + if (value) + { + /* + * The value passed by the caller could be transient, so + * we always strdup it. + */ + newval = gtm_opt_strdup(elevel, value); + if (newval == NULL) + return false; + } + else if (source == GTMC_S_DEFAULT) + { + /* non-NULL boot_val must always get strdup'd */ + if (conf->boot_val != NULL) + { + newval = gtm_opt_strdup(elevel, conf->boot_val); + if (newval == NULL) + return false; + } + else + newval = NULL; + + } + else + { + /* + * strdup not needed, since reset_val is already under + * guc.c's control + */ + newval = conf->reset_val; + newextra = conf->reset_extra; + source = conf->gen.reset_source; + } + + if (prohibitValueChange) + { + /* newval shouldn't be NULL, so we're a bit sloppy here */ + if (*conf->variable == NULL || newval == NULL || + strcmp(*conf->variable, newval) != 0) + { + if (isStartUp) + { + write_stderr("parameter \"%s\" cannot be changed without restarting the server\n", + name); + } + else + { + ereport(elevel, + (0, + errmsg("parameter \"%s\" cannot be changed without restarting the server", + name))); + } + } + return false; + } + + if (changeVal) + { + /* Save old value to support transaction abort */ + if (!makeDefault) + push_old_value(&conf->gen); + + set_string_field(conf, conf->variable, newval); + set_extra_field(&conf->gen, &conf->gen.extra, + newextra); + conf->gen.source = source; + } + + if (makeDefault) + { + GtmOptStack *stack; + + if (conf->gen.reset_source <= source) + { + set_string_field(conf, &conf->reset_val, newval); + set_extra_field(&conf->gen, &conf->reset_extra, + newextra); + conf->gen.reset_source = source; + } + for (stack = conf->gen.stack; stack; stack = stack->prev) + { + if (stack->source <= source) + { + set_string_field(conf, &stack->prior.val.stringval, + newval); + set_extra_field(&conf->gen, &stack->prior.extra, + newextra); + stack->source = source; + } + } + } + + /* Perhaps we didn't install newval anywhere */ + if (newval && !string_field_used(conf, newval)) + free(newval); + /* Perhaps we didn't install newextra anywhere */ + if (newextra && !extra_field_used(&conf->gen, newextra)) + free(newextra); + break; + } + + case GTMC_ENUM: + { + struct config_enum *conf = (struct config_enum *) record; + int newval; + void *newextra = NULL; + + if (value) + { + if (!config_enum_lookup_by_name(conf, value, &newval)) + { + char *hintmsg; + + hintmsg = config_enum_get_options(conf, + "Available values: ", + ".", ", "); + + if (isStartUp) + { + write_stderr("invalid value for parameter \"%s\": \"%s\". %s\n", + name, value, hintmsg); + } + else + { + ereport(elevel, + (0, + errmsg("invalid value for parameter \"%s\": \"%s\"", + name, value), + hintmsg ? errhint("%s", _(hintmsg)) : 0)); + } + + if (hintmsg) + free(hintmsg); + return false; + } + } + else if (source == GTMC_S_DEFAULT) + { + newval = conf->boot_val; + } + else + { + newval = conf->reset_val; + newextra = conf->reset_extra; + source = conf->gen.reset_source; + } + + if (prohibitValueChange) + { + if (*conf->variable != newval) + { + if (isStartUp) + { + write_stderr("parameter \"%s\" cannot be changed without restarting the server\n", + name); + } + else + { + ereport(elevel, + (0, + errmsg("parameter \"%s\" cannot be changed without restarting the server", + name))); + } + } + return false; + } + + if (changeVal) + { + /* Save old value to support transaction abort */ + if (!makeDefault) + push_old_value(&conf->gen); + + *conf->variable = newval; + set_extra_field(&conf->gen, &conf->gen.extra, + newextra); + conf->gen.source = source; + } + if (makeDefault) + { + GtmOptStack *stack; + + if (conf->gen.reset_source <= source) + { + conf->reset_val = newval; + set_extra_field(&conf->gen, &conf->reset_extra, + newextra); + conf->gen.reset_source = source; + } + for (stack = conf->gen.stack; stack; stack = stack->prev) + { + if (stack->source <= source) + { + stack->prior.val.enumval = newval; + set_extra_field(&conf->gen, &stack->prior.extra, + newextra); + stack->source = source; + } + } + } + + /* Perhaps we didn't install newextra anywhere */ + if (newextra && !extra_field_used(&conf->gen, newextra)) + free(newextra); + break; + } + } + + if (changeVal && (record->flags & GTMOPT_REPORT)) + ReportGTMOption(record); + + return true; +} + + + + +/* + * Set the fields for source file and line number the setting came from. + */ +static void +set_config_sourcefile(const char *name, char *sourcefile, int sourceline) +{ + struct config_generic *record; + int elevel; + + /* + * To avoid cluttering the log, only the postmaster bleats loudly about + * problems with the config file. + */ + elevel = DEBUG3; + + record = find_option(name, true, elevel); + /* should not happen */ + if (record == NULL) + { + if (isStartUp) + write_stderr("unrecognized configuration parameter \"%s\"\n", name); + else + elog(ERROR, "unrecognized configuration parameter \"%s\"", name); + } + + sourcefile = gtm_opt_strdup(elevel, sourcefile); + if (record->sourcefile) + free(record->sourcefile); + record->sourcefile = sourcefile; + record->sourceline = sourceline; +} + + +/* + * Set a config option to the given value. See also set_config_option, + * this is just the wrapper to be called from outside GTM. NB: this + * is used only for non-transactional operations. + * + * Note: there is no support here for setting source file/line, as it + * is currently not needed. + */ +void +SetConfigOption(const char *name, const char *value, + GtmOptContext context, GtmOptSource source) +{ + (void) set_config_option(name, value, context, source, + true); +} + + + + +/* + * Fetch the current value of the option `name'. If the option doesn't exist, + * throw an ereport and don't return. + * + * If restrict_superuser is true, we also enforce that only superusers can + * see GTMOPT_SUPERUSER_ONLY variables. This should only be passed as true + * in user-driven calls. + * + * The string is *not* allocated for modification and is really only + * valid until the next call to configuration related functions. + */ +const char * +GetConfigOption(const char *name, bool restrict_superuser) +{ + struct config_generic *record; + static char buffer[256]; + + record = find_option(name, false, ERROR); + if (record == NULL) + { + if (isStartUp) + write_stderr("unrecognized configuration parameter \"%s\"\n", name); + else + ereport(ERROR, + (0, + errmsg("unrecognized configuration parameter \"%s\"", name))); + } + switch (record->vartype) + { + case GTMC_BOOL: + return *((struct config_bool *) record)->variable ? "on" : "off"; + + case GTMC_INT: + snprintf(buffer, sizeof(buffer), "%d", + *((struct config_int *) record)->variable); + return buffer; + + case GTMC_REAL: + snprintf(buffer, sizeof(buffer), "%g", + *((struct config_real *) record)->variable); + return buffer; + + case GTMC_STRING: + return *((struct config_string *) record)->variable; + + case GTMC_ENUM: + return config_enum_lookup_by_value((struct config_enum *) record, + *((struct config_enum *) record)->variable); + } + return NULL; +} + + +/* + * Get the RESET value associated with the given option. + * + * Note: this is not re-entrant, due to use of static result buffer; + * not to mention that a string variable could have its reset_val changed. + * Beware of assuming the result value is good for very long. + */ +const char * +GetConfigOptionResetString(const char *name) +{ + struct config_generic *record; + static char buffer[256]; + + record = find_option(name, false, ERROR); + if (record == NULL) + { + if (isStartUp) + write_stderr("unrecognized configuration parameter \"%s\"\n", name); + else + ereport(ERROR, + (0, + errmsg("unrecognized configuration parameter \"%s\"", name))); + } + + switch (record->vartype) + { + case GTMC_BOOL: + return ((struct config_bool *) record)->reset_val ? "on" : "off"; + + case GTMC_INT: + snprintf(buffer, sizeof(buffer), "%d", + ((struct config_int *) record)->reset_val); + return buffer; + + case GTMC_REAL: + snprintf(buffer, sizeof(buffer), "%g", + ((struct config_real *) record)->reset_val); + return buffer; + + case GTMC_STRING: + return ((struct config_string *) record)->reset_val; + + case GTMC_ENUM: + return config_enum_lookup_by_value((struct config_enum *) record, + ((struct config_enum *) record)->reset_val); + } + return NULL; +} + + +void +EmitWarningsOnPlaceholders(const char *className) +{ + int classLen = strlen(className); + int i; + + for (i = 0; i < num_gtm_opt_variables; i++) + { + struct config_generic *var = gtm_opt_variables[i]; + + if ((var->flags & GTMOPT_CUSTOM_PLACEHOLDER) != 0 && + strncmp(className, var->name, classLen) == 0 && + var->name[classLen] == GTMOPT_QUALIFIER_SEPARATOR) + { + if (isStartUp) + write_stderr("unrecognized configuration parameter \"%s\"\n", + var->name); + else + ereport(WARNING, + (0, + errmsg("unrecognized configuration parameter \"%s\"", + var->name))); + } + } +} + + +/* + * Return GTM variable value by name; optionally return canonical + * form of name. Return value is malloc'd. + */ +char * +GetConfigOptionByName(const char *name, const char **varname) +{ + struct config_generic *record; + + record = find_option(name, false, ERROR); + if (record == NULL) + { + if (isStartUp) + write_stderr("unrecognized configuration parameter \"%s\"\n", name); + else + ereport(ERROR, + (0, + errmsg("unrecognized configuration parameter \"%s\"", name))); + } + if (varname) + *varname = record->name; + + return _ShowOption(record, true); +} + +/* + * Return GTM variable value by variable number; optionally return canonical + * form of name. Return value is malloc'd. + */ +void +GetConfigOptionByNum(int varnum, const char **values, bool *noshow) +{ + char buffer[256]; + struct config_generic *conf; + + /* check requested variable number valid */ + Assert((varnum >= 0) && (varnum < num_gtm_opt_variables)); + + conf = gtm_opt_variables[varnum]; + + if (noshow) + { + if (conf->flags & GTMOPT_NO_SHOW_ALL) + *noshow = true; + else + *noshow = false; + } + + /* first get the generic attributes */ + + /* name */ + values[0] = conf->name; + + /* setting : use _ShowOption in order to avoid duplicating the logic */ + values[1] = _ShowOption(conf, false); + + /* unit */ + if (conf->vartype == GTMC_INT) + { + static char buf[8]; + + switch (conf->flags & (GTMOPT_UNIT_MEMORY | GTMOPT_UNIT_TIME)) + { + case GTMOPT_UNIT_KB: + values[2] = "kB"; + break; + case GTMOPT_UNIT_BLOCKS: + snprintf(buf, sizeof(buf), "%dkB", BLCKSZ / 1024); + values[2] = buf; + break; + case GTMOPT_UNIT_XBLOCKS: + snprintf(buf, sizeof(buf), "%dkB", XLOG_BLCKSZ / 1024); + values[2] = buf; + break; + case GTMOPT_UNIT_MS: + values[2] = "ms"; + break; + case GTMOPT_UNIT_S: + values[2] = "s"; + break; + case GTMOPT_UNIT_MIN: + values[2] = "min"; + break; + default: + values[2] = ""; + break; + } + } + else + values[2] = NULL; + +#if 0 + /* PGXCTODO: Group parameters are not used yet */ + /* group */ + values[3] = config_group_names[conf->group]; +#endif + + /* short_desc */ + values[4] = conf->short_desc; + + /* extra_desc */ + values[5] = conf->long_desc; + + /* context */ + values[6] = GtmOptContext_Names[conf->context]; + + /* vartype */ + values[7] = config_type_names[conf->vartype]; + + /* source */ + values[8] = GtmOptSource_Names[conf->source]; + + /* now get the type specifc attributes */ + switch (conf->vartype) + { + case GTMC_BOOL: + { + struct config_bool *lconf = (struct config_bool *) conf; + + /* min_val */ + values[9] = NULL; + + /* max_val */ + values[10] = NULL; + + /* enumvals */ + values[11] = NULL; + + /* boot_val */ + values[12] = strdup(lconf->boot_val ? "on" : "off"); + + /* reset_val */ + values[13] = strdup(lconf->reset_val ? "on" : "off"); + } + break; + + case GTMC_INT: + { + struct config_int *lconf = (struct config_int *) conf; + + /* min_val */ + snprintf(buffer, sizeof(buffer), "%d", lconf->min); + values[9] = strdup(buffer); + + /* max_val */ + snprintf(buffer, sizeof(buffer), "%d", lconf->max); + values[10] = strdup(buffer); + + /* enumvals */ + values[11] = NULL; + + /* boot_val */ + snprintf(buffer, sizeof(buffer), "%d", lconf->boot_val); + values[12] = strdup(buffer); + + /* reset_val */ + snprintf(buffer, sizeof(buffer), "%d", lconf->reset_val); + values[13] = strdup(buffer); + } + break; + + case GTMC_REAL: + { + struct config_real *lconf = (struct config_real *) conf; + + /* min_val */ + snprintf(buffer, sizeof(buffer), "%g", lconf->min); + values[9] = strdup(buffer); + + /* max_val */ + snprintf(buffer, sizeof(buffer), "%g", lconf->max); + values[10] = strdup(buffer); + + /* enumvals */ + values[11] = NULL; + + /* boot_val */ + snprintf(buffer, sizeof(buffer), "%g", lconf->boot_val); + values[12] = strdup(buffer); + + /* reset_val */ + snprintf(buffer, sizeof(buffer), "%g", lconf->reset_val); + values[13] = strdup(buffer); + } + break; + + case GTMC_STRING: + { + struct config_string *lconf = (struct config_string *) conf; + + /* min_val */ + values[9] = NULL; + + /* max_val */ + values[10] = NULL; + + /* enumvals */ + values[11] = NULL; + + /* boot_val */ + if (lconf->boot_val == NULL) + values[12] = NULL; + else + values[12] = strdup(lconf->boot_val); + + /* reset_val */ + if (lconf->reset_val == NULL) + values[13] = NULL; + else + values[13] = strdup(lconf->reset_val); + } + break; + + case GTMC_ENUM: + { + struct config_enum *lconf = (struct config_enum *) conf; + + /* min_val */ + values[9] = NULL; + + /* max_val */ + values[10] = NULL; + + /* enumvals */ + + /* + * NOTE! enumvals with double quotes in them are not + * supported! + */ + values[11] = config_enum_get_options((struct config_enum *) conf, + "{\"", "\"}", "\",\""); + + /* boot_val */ + values[12] = strdup(config_enum_lookup_by_value(lconf, + lconf->boot_val)); + + /* reset_val */ + values[13] = strdup(config_enum_lookup_by_value(lconf, + lconf->reset_val)); + } + break; + + default: + { + /* + * should never get here, but in case we do, set 'em to NULL + */ + + /* min_val */ + values[9] = NULL; + + /* max_val */ + values[10] = NULL; + + /* enumvals */ + values[11] = NULL; + + /* boot_val */ + values[12] = NULL; + + /* reset_val */ + values[13] = NULL; + } + break; + } + + /* + * If the setting came from a config file, set the source location. For + * security reasons, we don't show source file/line number for + * non-superusers. + */ + if (conf->source == GTMC_S_FILE) + { + values[14] = conf->sourcefile; + snprintf(buffer, sizeof(buffer), "%d", conf->sourceline); + values[15] = strdup(buffer); + } + else + { + values[14] = NULL; + values[15] = NULL; + } +} + +/* + * Return the total number of GTM variables + */ +int +GetNumConfigOptions(void) +{ + return num_gtm_opt_variables; +} + + +static char * +_ShowOption(struct config_generic * record, bool use_units) +{ + char buffer[256]; + const char *val; + + switch (record->vartype) + { + case GTMC_BOOL: + { + struct config_bool *conf = (struct config_bool *) record; + + val = *conf->variable ? "on" : "off"; + } + break; + + case GTMC_INT: + { + struct config_int *conf = (struct config_int *) record; + + /* + * Use int64 arithmetic to avoid overflows in units + * conversion. + */ + int64 result = *conf->variable; + const char *unit; + + if (use_units && result > 0 && + (record->flags & GTMOPT_UNIT_MEMORY)) + { + switch (record->flags & GTMOPT_UNIT_MEMORY) + { + case GTMOPT_UNIT_BLOCKS: + result *= BLCKSZ / 1024; + break; + case GTMOPT_UNIT_XBLOCKS: + result *= XLOG_BLCKSZ / 1024; + break; + } + + if (result % KB_PER_GB == 0) + { + result /= KB_PER_GB; + unit = "GB"; + } + else if (result % KB_PER_MB == 0) + { + result /= KB_PER_MB; + unit = "MB"; + } + else + { + unit = "kB"; + } + } + else if (use_units && result > 0 && + (record->flags & GTMOPT_UNIT_TIME)) + { + switch (record->flags & GTMOPT_UNIT_TIME) + { + case GTMOPT_UNIT_S: + result *= MS_PER_S; + break; + case GTMOPT_UNIT_MIN: + result *= MS_PER_MIN; + break; + } + + if (result % MS_PER_D == 0) + { + result /= MS_PER_D; + unit = "d"; + } + else if (result % MS_PER_H == 0) + { + result /= MS_PER_H; + unit = "h"; + } + else if (result % MS_PER_MIN == 0) + { + result /= MS_PER_MIN; + unit = "min"; + } + else if (result % MS_PER_S == 0) + { + result /= MS_PER_S; + unit = "s"; + } + else + { + unit = "ms"; + } + } + else + unit = ""; + + snprintf(buffer, sizeof(buffer), INT64_FORMAT "%s", + result, unit); + val = buffer; + + } + break; + + case GTMC_REAL: + { + struct config_real *conf = (struct config_real *) record; + + snprintf(buffer, sizeof(buffer), "%g", + *conf->variable); + val = buffer; + } + break; + + case GTMC_STRING: + { + struct config_string *conf = (struct config_string *) record; + + if (*conf->variable && **conf->variable) + val = *conf->variable; + else + val = ""; + } + break; + + case GTMC_ENUM: + { + struct config_enum *conf = (struct config_enum *) record; + + val = config_enum_lookup_by_value(conf, *conf->variable); + } + break; + + default: + /* just to keep compiler quiet */ + val = "???"; + break; + } + + return strdup(val); +} + + + +/* + * A little "long argument" simulation, although not quite GNU + * compliant. Takes a string of the form "some-option=some value" and + * returns name = "some_option" and value = "some value" in malloc'ed + * storage. Note that '-' is converted to '_' in the option name. If + * there is no '=' in the input string then value will be NULL. + */ +void +ParseLongOption(const char *string, char **name, char **value) +{ + size_t equal_pos; + char *cp; + + AssertArg(string); + AssertArg(name); + AssertArg(value); + + equal_pos = strcspn(string, "="); + + if (string[equal_pos] == '=') + { + *name = gtm_opt_malloc(FATAL, equal_pos + 1); + strlcpy(*name, string, equal_pos + 1); + + *value = gtm_opt_strdup(FATAL, &string[equal_pos + 1]); + } + else + { + /* no equal sign in string */ + *name = gtm_opt_strdup(FATAL, string); + *value = NULL; + } + + for (cp = *name; *cp; cp++) + if (*cp == '-') + *cp = '_'; +} + +#if 0 +/* + * keep-alive related APIs will be used in future extensions + */ +void +gtm_assign_tcp_keepalives_idle(int newval, void *extra) +{ + /* + * The kernel API provides no way to test a value without setting it; and + * once we set it we might fail to unset it. So there seems little point + * in fully implementing the check-then-assign GTM API for these + * variables. Instead we just do the assignment on demand. pqcomm.c + * reports any problems via elog(LOG). + * + * This approach means that the GTM value might have little to do with the + * actual kernel value, so we use a show_hook that retrieves the kernel + * value rather than trusting GTM's copy. + */ +#if 0 + (void) pq_setkeepalivesidle(newval, MyProcPort); +#else + (void) pq_setkeepalivesidle_all(newval); +#endif +} + +const char * +gtm_show_tcp_keepalives_idle(void) +{ + /* See comments in assign_tcp_keepalives_idle */ + static char nbuf[16]; + +#if 0 + snprintf(nbuf, sizeof(nbuf), "%d", pq_getkeepalivesidle(MyProcPort)); +#else + snprintf(nbuf, sizeof(nbuf), "%d", pq_getkeepalivesidle_all()); +#endif + return nbuf; +} + +void +gtm_assign_tcp_keepalives_interval(int newval, void *extra) +{ + /* See comments in assign_tcp_keepalives_idle */ +#if 0 + (void) pq_setkeepalivesinterval(newval, MyProcPort); +#else + (void) pq_setkeepalivesinterval_all(newval); +#endif +} + +const char * +gtm_show_tcp_keepalives_interval(void) +{ + /* See comments in assign_tcp_keepalives_idle */ + static char nbuf[16]; + +#if 0 + snprintf(nbuf, sizeof(nbuf), "%d", pq_getkeepalivesinterval(MyProcPort)); +#else + snprintf(nbuf, sizeof(nbuf), "%d", pq_getkeepalivesinterval_all()); +#endif + return nbuf; +} + +void +gtm_assign_tcp_keepalives_count(int newval, void *extra) +{ + /* See comments in assign_tcp_keepalives_idle */ +#if 0 + (void) pq_setkeepalivescount(newval, MyProcPort); +#else + (void) pq_setkeepalivescount_all(newval); +#endif +} + +const char * +gtm_show_tcp_keepalives_count(void) +{ + /* See comments in assign_tcp_keepalives_idle */ + static char nbuf[16]; + +#if 0 + snprintf(nbuf, sizeof(nbuf), "%d", pq_getkeepalivescount(MyProcPort)); +#else + snprintf(nbuf, sizeof(nbuf), "%d", pq_getkeepalivescount_all()); +#endif + return nbuf; +} +#endif + +/* + * Try to interpret value as boolean value. Valid values are: true, + * false, yes, no, on, off, 1, 0; as well as unique prefixes thereof. + * If the string parses okay, return true, else false. + * If okay and result is not NULL, return the value in *result. + */ +static bool +gtm_opt_parse_bool(const char *value, bool *result) +{ + return gtm_opt_parse_bool_with_len(value, strlen(value), result); +} + +static bool +gtm_opt_parse_bool_with_len(const char *value, size_t len, bool *result) +{ + switch (*value) + { + case 't': + case 'T': + if (pg_strncasecmp(value, "true", len) == 0) + { + if (result) + *result = true; + return true; + } + break; + case 'f': + case 'F': + if (pg_strncasecmp(value, "false", len) == 0) + { + if (result) + *result = false; + return true; + } + break; + case 'y': + case 'Y': + if (pg_strncasecmp(value, "yes", len) == 0) + { + if (result) + *result = true; + return true; + } + break; + case 'n': + case 'N': + if (pg_strncasecmp(value, "no", len) == 0) + { + if (result) + *result = false; + return true; + } + break; + case 'o': + case 'O': + /* 'o' is not unique enough */ + if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0) + { + if (result) + *result = true; + return true; + } + else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0) + { + if (result) + *result = false; + return true; + } + break; + case '1': + if (len == 1) + { + if (result) + *result = true; + return true; + } + break; + case '0': + if (len == 1) + { + if (result) + *result = false; + return true; + } + break; + default: + break; + } + + if (result) + *result = false; /* suppress compiler warning */ + return false; +} + +/* + * ReportGUCOption: if appropriate, transmit option value to frontend + */ +static void +ReportGTMOption(struct config_generic * record) +{ + /* So far, it is empty. */ +} + +/* + * Lookup the name for an enum option with the selected value. + * Should only ever be called with known-valid values, so throws + * an elog(ERROR) if the enum option is not found. + * + * The returned string is a pointer to static data and not + * allocated for modification. + */ +const char * +config_enum_lookup_by_value(struct config_enum * record, int val) +{ + const struct config_enum_entry *entry; + + for (entry = record->options; entry && entry->name; entry++) + { + if (entry->val == val) + return entry->name; + } + + if (isStartUp) + write_stderr("could not find enum option %d for %s\n", + val, record->gen.name); + else + elog(ERROR, "could not find enum option %d for %s", + val, record->gen.name); + return NULL; /* silence compiler */ +} diff --git a/src/gtm/common/gtm_opt_scanner.l b/src/gtm/common/gtm_opt_scanner.l new file mode 100644 index 0000000000..f9be2cbfbe --- /dev/null +++ b/src/gtm/common/gtm_opt_scanner.l @@ -0,0 +1,92 @@ +/* -*-pgsql-c-*- */ +/* + * Scanner for the configuration file + * + * Copyright (c) 2000-2011, PostgreSQL Global Development Group + * + * src/backend/utils/misc/guc-file.l + */ + +%{ + +#include "gtm/gtm.h" + +#include <ctype.h> +#include <unistd.h> +#include <stdlib.h> + +#include "mb/pg_wchar.h" +#include "gtm/assert.h" +#include "gtm/gtm_opt.h" +#include "gtm/elog.h" + + +/* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */ +#undef fprintf +#define fprintf(file, fmt, msg) ereport(ERROR, (errmsg_internal("%s", msg))) + +enum { + GTMOPT_ID = 1, + GTMOPT_STRING = 2, + GTMOPT_INTEGER = 3, + GTMOPT_REAL = 4, + GTMOPT_EQUALS = 5, + GTMOPT_UNQUOTED_STRING = 6, + GTMOPT_QUALIFIED_ID = 7, + GTMOPT_EOL = 99, + GTMOPT_ERROR = 100 +}; + +static unsigned int ConfigFileLineno; + +/* flex fails to supply a prototype for yylex, so provide one */ +int GTMOPT_yylex(void); + +%} + +%option 8bit +%option never-interactive +%option nodefault +%option noinput +%option nounput +%option noyywrap +%option prefix="GTMOPT_yy" + + +SIGN ("-"|"+") +DIGIT [0-9] +HEXDIGIT [0-9a-fA-F] + +UNIT_LETTER [a-zA-Z] + +INTEGER {SIGN}?({DIGIT}+|0x{HEXDIGIT}+){UNIT_LETTER}* + +EXPONENT [Ee]{SIGN}?{DIGIT}+ +REAL {SIGN}?{DIGIT}*"."{DIGIT}*{EXPONENT}? + +LETTER [A-Za-z_\200-\377] +LETTER_OR_DIGIT [A-Za-z_0-9\200-\377] + +ID {LETTER}{LETTER_OR_DIGIT}* +QUALIFIED_ID {ID}"."{ID} + +UNQUOTED_STRING {LETTER}({LETTER_OR_DIGIT}|[-._:/])* +STRING \'([^'\\\n]|\\.|\'\')*\' + +%% + +\n ConfigFileLineno++; return GTMOPT_EOL; +[ \t\r]+ /* eat whitespace */ +#.* /* eat comment (.* matches anything until newline) */ + +{ID} return GTMOPT_ID; +{QUALIFIED_ID} return GTMOPT_QUALIFIED_ID; +{STRING} return GTMOPT_STRING; +{UNQUOTED_STRING} return GTMOPT_UNQUOTED_STRING; +{INTEGER} return GTMOPT_INTEGER; +{REAL} return GTMOPT_REAL; += return GTMOPT_EQUALS; + +. return GTMOPT_ERROR; + +%% diff --git a/src/gtm/gtm_ctl/gtm_ctl.c b/src/gtm/gtm_ctl/gtm_ctl.c index dcb586206c..4ced20d1f7 100644 --- a/src/gtm/gtm_ctl/gtm_ctl.c +++ b/src/gtm/gtm_ctl/gtm_ctl.c @@ -646,11 +646,7 @@ do_reconnect(void) fprintf(reconnect_point_file, "%s\n", gtm_opts); fclose(reconnect_point_file); free(reconnect_point_file_nam); -#if 0 /* GTM_SBY_DEBUG */ - write_stderr("Now about to send SIGUSR1 to pid %ld.\n", pid); - write_stderr("Returning. This is the debug. Don't send signal actually.\n"); - return; -#endif + if (kill((pid_t) pid, SIGUSR1) != 0) { write_stderr(_("%s: could not send promote signal (PID: %ld): %s\n"), progname, pid, @@ -882,7 +878,7 @@ do_help(void) printf(_("\nCommon options:\n")); printf(_(" -D DATADIR location of the database storage area\n")); - printf(_(" -i NODE_ID set gtm_proxy ID registered on GTM\n")); + printf(_(" -i nodename set gtm_proxy nodename registered on GTM\n")); printf(_(" (option ignored if used with GTM)\n")); printf(_(" -t SECS seconds to wait when using -w option\n")); printf(_(" -w wait until operation completes\n")); @@ -940,7 +936,7 @@ int main(int argc, char **argv) { int c; - int node_id = 0; /* GTM Proxy ID */ + char *nodename = NULL; /* GTM Proxy nodename */ progname = "gtm_ctl"; @@ -1014,7 +1010,7 @@ main(int argc, char **argv) break; } case 'i': - node_id = atoi(optarg); + nodename = strdup(optarg); break; case 'l': log_file = xstrdup(optarg); @@ -1128,6 +1124,7 @@ main(int argc, char **argv) } if (strcmp(gtm_app,"gtm_proxy") != 0 && + strcmp(gtm_app, "gtm_standby") != 0 && strcmp(gtm_app,"gtm") != 0) { write_stderr(_("%s: launch option incorrect\n"), @@ -1140,18 +1137,14 @@ main(int argc, char **argv) if (ctl_command == START_COMMAND || ctl_command == RESTART_COMMAND) { - if (node_id == 0 && strcmp(gtm_app, "gtm_proxy") == 0) - { - write_stderr("%s: GTM Proxy ID not specified\n", - progname); - do_advice(); - exit(1); - } /* Rebuild option string to include Proxy ID */ if (strcmp(gtm_app, "gtm_proxy") == 0) { gtmdata_opt = (char *) pg_realloc(gtmdata_opt, strlen(gtmdata_opt) + 9); - sprintf(gtmdata_opt, "%s -i %d ", gtmdata_opt, node_id); + if (nodename) + sprintf(gtmdata_opt, "%s -i %s ", gtmdata_opt, nodename); + else + sprintf(gtmdata_opt, "%s ", gtmdata_opt); } } @@ -1173,26 +1166,6 @@ main(int argc, char **argv) } } -#if 0 - if (gtm_data) - { - if (strcmp(gtm_app,"gtm_proxy") == 0) - { - snprintf(pid_file, MAXPGPATH, "%s/gtm_proxy.pid", gtm_data); - snprintf(gtmopts_file, MAXPGPATH, "%s/gtm_proxy.opts", gtm_data); - } - else if (strcmp(gtm_app,"gtm") == 0) - { - snprintf(pid_file, MAXPGPATH, "%s/gtm.pid", gtm_data); - snprintf(gtmopts_file, MAXPGPATH, "%s/gtm.opts", gtm_data); - } - else if (strcmp(gtm_app,"gtm_standby") == 0) - { - snprintf(pid_file, MAXPGPATH, "%s/gtm.pid", gtm_data); - snprintf(gtmopts_file, MAXPGPATH, "%s/gtm.opts", gtm_data); - } - } -#else /* Build strings for pid file and option file */ if (strcmp(gtm_app,"gtm_proxy") == 0) { @@ -1209,7 +1182,6 @@ main(int argc, char **argv) snprintf(pid_file, MAXPGPATH, "%s/gtm.pid", gtm_data); snprintf(gtmopts_file, MAXPGPATH, "%s/gtm.opts", gtm_data); } -#endif if (ctl_command==STATUS_COMMAND) gtm_opts = xstrdup("-c"); diff --git a/src/gtm/main/Makefile b/src/gtm/main/Makefile index 5874143311..f5d1684d37 100644 --- a/src/gtm/main/Makefile +++ b/src/gtm/main/Makefile @@ -3,13 +3,13 @@ top_builddir=../../.. include $(top_builddir)/src/Makefile.global -OBJS=main.o gtm_thread.o gtm_txn.o gtm_seq.o gtm_snap.o gtm_time.o gtm_standby.o ../libpq/libpqcomm.a ../path/libgtmpath.a ../recovery/libgtmrecovery.a ../client/libgtmclient.a ../common/libgtm.a +OBJS=main.o gtm_thread.o gtm_txn.o gtm_seq.o gtm_snap.o gtm_time.o gtm_standby.o gtm_opt.o ../libpq/libpqcomm.a ../path/libgtmpath.a ../recovery/libgtmrecovery.a ../client/libgtmclient.a ../common/libgtm.a LDFLAGS=-L$(top_builddir)/common -L$(top_builddir)/libpq LIBS=-lpthread gtm:$(OBJS) - $(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) $^ -o gtm + $(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) $^ ../../port/libpgport.a -o gtm all:gtm diff --git a/src/gtm/main/gtm.conf.sample b/src/gtm/main/gtm.conf.sample new file mode 100644 index 0000000000..4121849eef --- /dev/null +++ b/src/gtm/main/gtm.conf.sample @@ -0,0 +1,58 @@ +# ---------------------- +# GTM configuration file +# ---------------------- +# +# This file must be placed on gtm working directory +# specified by -D command line option of gtm or gtm_ctl. The +# configuration file name must be "gtm.conf" +# +# +# This file consists of lines of the form +# +# name = value +# +# (The "=" is optional.) Whitespace may be used. Comments are +# introduced with "#" anywhere on a line. The complete list of +# parameter names and allowed values can be found in the +# Postgres-XC documentation. +# +# The commented-out settings shown in this file represent the default +# values. +# +# Re-commenting a setting is NOT sufficient to revert it to the default +# value. +# +# You need to restart the server. + +#------------------------------------------------------------------------------ +# GENERAL PARAMETERS +#------------------------------------------------------------------------------ +#nodename = '' # Specifies the node name. + # (changes requires restart) +#listen_addresses = '*' # Listen addresses of this GTM. + # (changes requires restart) +#port = 6666 # Port number of this GTM. + # (changes requires restart) + +#startup = ACT # Start mode. ACT/STANDBY. + +#------------------------------------------------------------------------------ +# GTM STANDBY PARAMETERS +#------------------------------------------------------------------------------ +#Those parameters are effective when GTM is activated as a standby server +#active_host = '' # Listen address of active GTM. + # (changes requires restart) +#active_port = # Port number of active GTM. + # (changes requires restart) + +#--------------------------------------- +# OTHER OPTIONS +#--------------------------------------- +#keepalives_idle = 0 # Keepalives_idle parameter. +#keepalives_interval = 0 # Keepalives_interval parameter. +#keepalives_count = 0 # Keepalives_count internal parameter. +#log_file = 'gtm.log' # Log file name +#log_min_messages = WARNING # log_min_messages. Default WARNING. + # Valid value: DEBUG, DEBUG5, DEBUG4, DEBU3, + # DEBUG2, DEBUG1, INFO, NOTICE, WARNING, + # ERROR, LOG, FATAL, PANIC diff --git a/src/gtm/main/gtm_opt.c b/src/gtm/main/gtm_opt.c new file mode 100644 index 0000000000..580a4646cf --- /dev/null +++ b/src/gtm/main/gtm_opt.c @@ -0,0 +1,342 @@ +/*-------------------------------------------------------------------- + * guc.c + * + * Support for grand unified configuration scheme, including SET + * command, configuration file, and + command line options. + * See src/backend/utils/misc/README for more information. + * + * + * Copyright (c) 2000-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + * Written by Peter Eisentraut <[email protected]>. + * + * IDENTIFICATION + * src/backend/utils/misc/guc.c + * + *-------------------------------------------------------------------- + */ +#include "gtm/gtm_c.h" + +#include <ctype.h> +#include <float.h> +#include <math.h> +#include <limits.h> +#include <unistd.h> +#include <sys/stat.h> + +#include "gtm/gtm.h" +#include "gtm/path.h" +#include "gtm/gtm_opt_tables.h" +#include "gtm/gtm_opt.h" +#include "gtm/gtm_standby.h" + + +#define CONFIG_FILENAME "gtm.conf" +const char *config_filename = CONFIG_FILENAME; + +/* + * Variables declared elsewhere for gtm, mainly option variables. + */ + +extern char *NodeName; +extern char *ListenAddresses; +extern int GTMPortNumber; +extern char *active_addr; +extern int active_port; +extern int GTM_StandbyMode; +extern char *error_reporter; +extern char *status_reader; +extern int log_min_messages; +extern int keepalives_idle; +extern int keepalives_count; +extern int keepalives_interval; +extern char *GTMDataDir; + + + +/* + * We have different sets for client and server message level options because + * they sort slightly different (see "log" level) + */ + +Server_Message_Level_Options(); +Gtm_Startup_Mode_Options(); + +/* + * GTM option variables that are exported from this module + */ +char *data_directory; +char *GTMConfigFileName; + +/* + * Displayable names for context types (enum GtmContext) + */ +gtmOptContext_Names(); + +/* + * Displayable names for source types (enum GtmSource) + */ +gtmOptSource_Names(); + + +/* + * Displayable names for GTM variable types (enum config_type) + */ +Config_Type_Names(); + +/* + * Contents of GTM tables + * + * See src/backend/utils/misc/README for design notes. + * + * TO ADD AN OPTION: + * + * 1. Declare a global variable of type bool, int, double, or char* + * and make use of it. + * + * 2. Decide at what times it's safe to set the option. See guc.h for + * details. + * + * 3. Decide on a name, a default value, upper and lower bounds (if + * applicable), etc. + * + * 4. Add a record below. + * + * 5. Add it to src/backend/utils/misc/postgresql.conf.sample, if + * appropriate. + * + * 6. Don't forget to document the option (at least in config.sgml). + * + * 7. If it's a new GTMOPT_LIST option you must edit pg_dumpall.c to ensure + * it is not single quoted at dump time. + */ + + +/******** option records follow ********/ + +struct config_bool ConfigureNamesBool[] = +{ + /* End-of-list marker */ + { + {NULL, 0, NULL, NULL, 0}, NULL, false, false, NULL + } +}; + + +struct config_int ConfigureNamesInt[] = +{ + { + {"port", GTMC_STARTUP, + gettext_noop("Listen Port of GTM or GTM standby server."), + NULL, + 0 + }, + >MPortNumber, + 0, 0, INT_MAX, + 0, NULL + }, + { + {"active_port", GTMC_SIGHUP, + gettext_noop("GTM server port number when it works as GTM-Standby."), + NULL, + 0 + }, + &active_port, + 0, 0, INT_MAX, + 0, NULL + }, + { + {"keepalives_idle", GTMC_STARTUP, + gettext_noop("Sets \"keepalives_idle\" option for the connection to GTM."), + gettext_noop("This option is effective only when it runs as GTM-Standby."), + GTMOPT_UNIT_TIME + }, + &keepalives_idle, + 0, 0, INT_MAX, + 0, NULL + }, + { + {"keepalives_interval", GTMC_STARTUP, + gettext_noop("Sets \"keepalives_interval\" option fo the connetion to GTM."), + gettext_noop("This option is effective only when it runs as GTM-Standby."), + GTMOPT_UNIT_TIME + }, + &keepalives_interval, + 0, 0, INT_MAX, + 0, NULL + }, + { + {"keepalives_count", GTMC_STARTUP, + gettext_noop("Sets \"keepalives_count\" option to the connection to GTM."), + gettext_noop("This option is effective only when it runs as GTM-Standby."), + 0 + }, + &keepalives_count, + 0, 0, INT_MAX, + 0, NULL + }, + /* End-of-list marker */ + { + {NULL, 0, NULL, NULL, 0}, NULL, 0, 0, 0, 0, NULL + } +}; + + +struct config_real ConfigureNamesReal[] = +{ + /* End-of-list marker */ + { + {NULL, 0, NULL, NULL, 0}, NULL, 0.0, 0.0, 0.0, 0.0, NULL + } +}; + +struct config_string ConfigureNamesString[] = +{ + { + {"data_dir", GTMC_STARTUP, + gettext_noop("Work directory."), + NULL, + 0 + }, + >MDataDir, + NULL, + NULL, + NULL + }, + + { + {"config_file", GTMC_SIGHUP, + gettext_noop("Configuration file name."), + NULL, + 0 + }, + >MConfigFileName, + CONFIG_FILENAME, + NULL, + NULL + }, + + { + {"nodename", GTMC_STARTUP, + gettext_noop("Name of this GTM/GTM-Standby."), + NULL, + 0 + }, + &NodeName, + "", + NULL, + NULL + }, + + { + {"listen_addresses", GTMC_STARTUP, + gettext_noop("Listen address."), + NULL, + 0 + }, + &ListenAddresses, + NULL, + NULL, NULL + }, + + { + {"active_host", GTMC_SIGHUP, + gettext_noop("Address of target GTM ACT."), + gettext_noop("This parameter is effective only when it runs as GTM-Standby"), + 0 + }, + &active_addr, + NULL, + NULL, NULL + }, + + { + {"log_file", GTMC_SIGHUP, + gettext_noop("Log file name."), + NULL, + 0 + }, + >MLogFile, + "gtm.log", + NULL, NULL + }, + + { + {"error_reporter", GTMC_SIGHUP, + gettext_noop("Command to report various errors."), + NULL, + 0 + }, + &error_reporter, + NULL, + NULL, NULL + }, + + { + {"status_reader", GTMC_SIGHUP, + gettext_noop("Command to get status of global XC node status."), + gettext_noop("Runs when configuration file is read by SIGHUP"), + 0 + }, + &status_reader, + NULL, + NULL, NULL + }, + + /* End-of-list marker */ + { + {NULL, 0, NULL, NULL}, NULL, NULL, NULL, NULL + } +}; + + +struct config_enum ConfigureNamesEnum[] = +{ + { + {"log_min_messages", GTMC_SIGHUP, + gettext_noop("Minimum message level to write to the log file."), + NULL, + 0 + }, + &log_min_messages, + WARNING, + server_message_level_options, + WARNING, NULL + }, + + { + {"startup", GTMC_SIGHUP, + gettext_noop("Specifies startup mode, act or standby."), + NULL, + 0 + }, + >M_StandbyMode, + GTM_ACT_MODE, + gtm_startup_mode_options, + GTM_ACT_MODE, NULL + }, + + /* End-of-list marker */ + { + {NULL, 0, NULL, NULL, 0}, NULL, 0, NULL, 0, NULL + } +}; + +/******** end of options list ********/ + +/* + * Actual lookup of variables is done through this single, sorted array. + */ +struct config_generic **gtm_opt_variables; + +/* Current number of variables contained in the vector */ +int num_gtm_opt_variables; + +/* Vector capacity */ +int size_gtm_opt_variables; + + +bool reporting_enabled; /* TRUE to enable GTMOPT_REPORT */ + +int GTMOptUpdateCount = 0; /* Indicates when specific option is updated */ diff --git a/src/gtm/main/gtm_standby.c b/src/gtm/main/gtm_standby.c index 8d9bad0c97..6e5a31d32d 100644 --- a/src/gtm/main/gtm_standby.c +++ b/src/gtm/main/gtm_standby.c @@ -33,6 +33,8 @@ static char *standbyDataDir; static GTM_Conn *gtm_standby_connect_to_standby_int(int *); +extern char *NodeName; /* Defined in main.c */ + int gtm_standby_start_startup(void) { @@ -42,8 +44,8 @@ gtm_standby_start_startup(void) elog(LOG, "Connecting the GTM active on %s:%d...", active_address, active_port); - sprintf(connect_string, "host=%s port=%d node_name=one remote_type=%d", - active_address, active_port, PGXC_NODE_GTM); + sprintf(connect_string, "host=%s port=%d node_name=%s remote_type=%d", + active_address, active_port, NodeName, PGXC_NODE_GTM); GTM_ActiveConn = PQconnectGTM(connect_string); if (GTM_ActiveConn == NULL) @@ -336,7 +338,7 @@ find_standby_node_info(void) node[i]->port, node[i]->status); - if ( (strcmp(standbyNodeName, node[i]->nodename) == 0) && + if ( (strcmp(standbyNodeName, node[i]->nodename) != 0) && node[i]->status == NODE_CONNECTED) return node[i]; } @@ -396,8 +398,8 @@ gtm_standby_connect_to_standby_int(int *report_needed) *report_needed = 1; snprintf(conn_string, sizeof(conn_string), - "host=%s port=%d node_name=one remote_type=4", - n->ipaddress, n->port); + "host=%s port=%d node_name=%s remote_type=4", + n->ipaddress, n->port, NodeName); standby = PQconnectGTM(conn_string); diff --git a/src/gtm/main/main.c b/src/gtm/main/main.c index 4648e9c3ac..44d40bae4b 100644 --- a/src/gtm/main/main.c +++ b/src/gtm/main/main.c @@ -44,6 +44,7 @@ #include "gtm/gtm_txn.h" #include "gtm/gtm_seq.h" #include "gtm/gtm_msg.h" +#include "gtm/gtm_opt.h" extern int optind; extern char *optarg; @@ -61,6 +62,14 @@ int GTMPortNumber; char GTMControlFile[GTM_MAX_PATH]; char *GTMDataDir; char *NodeName; +char *active_addr; +int active_port; +int keepalives_idle; +int keepalives_interval; +int keepalives_count; +char *error_reporter; +char *status_reader; +bool isStartUp; GTM_ThreadID TopMostThreadID; @@ -117,6 +126,7 @@ MainThreadInit() * use malloc */ thrinfo = (GTM_ThreadInfo *)malloc(sizeof (GTM_ThreadInfo)); + memset(thrinfo, 0, sizeof(GTM_ThreadInfo)); if (thrinfo == NULL) { @@ -261,10 +271,47 @@ main(int argc, char *argv[]) int i; GlobalTransactionId next_gxid = InvalidGlobalTransactionId; int ctlfd; - char *active_addr; - int active_port; /* + * Local variable to hold command line options. + * + * if -c option is specified, then only -D option will be taken to locate + * GTM data directory. All the other options are ignored. GTM status + * will be printed out based on the specified data directory and GTM will + * simply exit. If -D option is not specified in this case, current directory + * will be used. + * + * In other case, all the command line options are analyzed. + * + * They're first analyzed and then -D option are used to locate configuration file. + * If -D option is not specified, then the default value will be used. + * + * Please note that configuration specified in the configuration file (gtm.conf) + * will be analyzed first and then will be overridden by the value specified + * in command line options. -D and -C options are handled separately and used + * to determine configuration file location. + * + * Please also note that -x option (startup GXID) will be handled in this section. + * It has no corresponding configuration from the configuration file. + */ + + bool is_gtm_status = false; /* "false" means no -c option was found */ + char *listen_addresses = NULL; + char *node_name = NULL; + char *port_number = NULL; + char *data_dir = NULL; + char *log_file = NULL; + char *is_standby_mode = NULL; + char *dest_addr = NULL; + char *dest_port = NULL; + + isStartUp = true; + + /* + * At first, initialize options. Also moved something from BaseInit() here. + */ + InitializeGTMOptions(); + /* * Catch standard options before doing much else */ if (argc > 1) @@ -276,7 +323,7 @@ main(int argc, char *argv[]) } } - ListenAddresses = GTM_DEFAULT_HOSTNAME; + ListenAddresses = strdup(GTM_DEFAULT_HOSTNAME); GTMPortNumber = GTM_DEFAULT_PORT; /* @@ -291,15 +338,21 @@ main(int argc, char *argv[]) break; /* never reach here. */ case 'h': - ListenAddresses = strdup(optarg); + if (listen_addresses) + free(listen_addresses); + listen_addresses = strdup(optarg); break; case 'n': + if (NodeName) + free(NodeName); NodeName = strdup(optarg); break; case 'p': - GTMPortNumber = atoi(optarg); + if (port_number) + free(port_number); + port_number = strdup(optarg); break; case 'x': @@ -307,24 +360,34 @@ main(int argc, char *argv[]) break; case 'D': - GTMDataDir = strdup(optarg); - canonicalize_path(GTMDataDir); + if (data_dir) + free(data_dir); + data_dir = strdup(optarg); + canonicalize_path(data_dir); break; case 'l': - GTMLogFile = strdup(optarg); + if (log_file) + free(log_file); + log_file = strdup(optarg); break; case 's': - Recovery_StandbySetStandby(true); + if (is_standby_mode) + free(is_standby_mode); + is_standby_mode = strdup("standby"); break; case 'i': - active_addr = strdup(optarg); + if (dest_addr) + free(dest_addr); + dest_addr = strdup(optarg); break; case 'q': - active_port = atoi(optarg); + if (dest_port) + free(dest_port); + dest_port = strdup(optarg); break; default: @@ -334,6 +397,74 @@ main(int argc, char *argv[]) } /* + * Handle status, if any + */ + if (is_gtm_status) + gtm_status(); + /* Never reach beyond here */ + + /* + * Setup work directory + */ + if (data_dir) + SetConfigOption("data_dir", data_dir, GTMC_STARTUP, GTMC_S_OVERRIDE); + + /* + * Setup configuration file + */ + if (!SelectConfigFiles(data_dir, progname)) + exit(1); + + /* + * Parse config file + */ + ProcessConfigFile(GTMC_STARTUP); + /* + * Override with command line options + */ + if (log_file) + { + SetConfigOption("log_file", log_file, GTMC_STARTUP, GTMC_S_OVERRIDE); + free(log_file); + log_file = NULL; + } + if (listen_addresses) + { + SetConfigOption("listen_addreses", listen_addresses, GTMC_STARTUP, GTMC_S_OVERRIDE); + free(listen_addresses); + listen_addresses = NULL; + } + if (node_name) + { + SetConfigOption("nodename", node_name, GTMC_STARTUP, GTMC_S_OVERRIDE); + free(node_name); + node_name = NULL; + } + if (port_number) + { + SetConfigOption("port", port_number, GTMC_STARTUP, GTMC_S_OVERRIDE); + free(port_number); + port_number = NULL; + } + if (is_standby_mode) + { + SetConfigOption("startup", is_standby_mode, GTMC_STARTUP, GTMC_S_OVERRIDE); + free(is_standby_mode); + is_standby_mode = NULL; + } + if (dest_addr) + { + SetConfigOption("active_host", dest_addr, GTMC_STARTUP, GTMC_S_OVERRIDE); + free(dest_addr); + dest_addr = NULL; + } + if (dest_port) + { + SetConfigOption("active_port", dest_port, GTMC_STARTUP, GTMC_S_OVERRIDE); + free(dest_port); + dest_port = NULL; + } + /* * Check options for the standby mode. */ if (Recovery_IsStandby()) diff --git a/src/gtm/path/path.c b/src/gtm/path/path.c index 32009a3bcf..ccf19c7af6 100644 --- a/src/gtm/path/path.c +++ b/src/gtm/path/path.c @@ -129,6 +129,18 @@ canonicalize_path(char *path) } /* + * get_parent_directory + * + * Modify the given string in-place to name the parent directory of the + * named file. + */ +void +get_parent_directory(char *path) +{ + trim_directory(path); +} + +/* * trim_directory * * Trim trailing directory from path, that is, remove any trailing slashes, @@ -238,3 +250,53 @@ make_absolute_path(const char *path) return new; } + + +/* + * join_path_components - join two path components, inserting a slash + * + * ret_path is the output area (must be of size MAXPGPATH) + * + * ret_path can be the same as head, but not the same as tail. + */ +void +join_path_components(char *ret_path, + const char *head, const char *tail) +{ + if (ret_path != head) + strlcpy(ret_path, head, MAXPGPATH); + + /* + * Remove any leading "." and ".." in the tail component, adjusting head + * as needed. + */ + for (;;) + { + if (tail[0] == '.' && IS_DIR_SEP(tail[1])) + { + tail += 2; + } + else if (tail[0] == '.' && tail[1] == '\0') + { + tail += 1; + break; + } + else if (tail[0] == '.' && tail[1] == '.' && IS_DIR_SEP(tail[2])) + { + trim_directory(ret_path); + tail += 3; + } + else if (tail[0] == '.' && tail[1] == '.' && tail[2] == '\0') + { + trim_directory(ret_path); + tail += 2; + break; + } + else + break; + } + if (*tail) + snprintf(ret_path + strlen(ret_path), MAXPGPATH - strlen(ret_path), + "/%s", tail); +} + diff --git a/src/gtm/proxy/Makefile b/src/gtm/proxy/Makefile index baac5309c7..4eeb482425 100644 --- a/src/gtm/proxy/Makefile +++ b/src/gtm/proxy/Makefile @@ -3,14 +3,14 @@ top_builddir=../../.. include $(top_builddir)/src/Makefile.global -OBJS=proxy_main.o proxy_thread.o proxy_utils.o ../libpq/libpqcomm.a ../path/libgtmpath.a ../recovery/libgtmrecovery.a ../client/libgtmclient.a ../common/libgtm.a +OBJS=proxy_main.o proxy_thread.o proxy_utils.o gtm_proxy_opt.o ../libpq/libpqcomm.a ../path/libgtmpath.a ../recovery/libgtmrecovery.a ../client/libgtmclient.a ../common/libgtm.a LDFLAGS=-L$(top_builddir)/common -L$(top_builddir)/libpq LIBS=-lpthread gtm_proxy:$(OBJS) - $(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) $^ -o gtm_proxy + $(CC) $(CFLAGS) $(LDFLAGS) $(LIBS) $^ ../../port/libpgport_srv.a -o gtm_proxy all:gtm_proxy diff --git a/src/gtm/proxy/gtm_proxy.conf.sample b/src/gtm/proxy/gtm_proxy.conf.sample new file mode 100644 index 0000000000..97eefd0c3d --- /dev/null +++ b/src/gtm/proxy/gtm_proxy.conf.sample @@ -0,0 +1,64 @@ +#----------------------------- +# GTM Proxy configuration file +#----------------------------- +# +# This file must be placed on gtm working directory +# specified by -D command line option of gtm_proxy or gtm_ctl. +# The configuration file name must be "gtm_proxy.conf" +# +# +# This file consists of lines of the form +# +# name = value +# +# (The "=" is optional.) Whitespace may be used. Comments are +# introduced with "#" anywhere on a line. The complete list of +# parameter names and allowed values can be found in the +# Postgres-XC documentation. +# +# The commented-out settings shown in this file represent the default +# values. +# +# Re-commenting a setting is NOT sufficient to revert it to the default +# value. +# +# You need to restart the server. + +#------------------------------------------------------------------------------ +# GENERAL PARAMETERS +#------------------------------------------------------------------------------ +#nodename = '' # Specifies the node name. + # (changes requires restart) +#listen_addresses = '*' # Listen addresses of this GTM. + # (changes requires restart) +#port = 6666 # Port number of this GTM. + # (changes requires restart) + +#------------------------------------------------------------------------------ +# GTM PROXY PARAMETERS +#------------------------------------------------------------------------------ +#worker_threads = 1 # Number of the worker thread of this + # GTM proxy + # (changes requires restart) + +#------------------------------------------------------------------------------ +# GTM CONNECTION PARAMETERS +#------------------------------------------------------------------------------ +# Those parameters are used to connect to a GTM server +#gtm_host = '' # Listen address of the active GTM. + # (changes requires restart) +#gtm_port = # Port number of the active GTM. + # (changes requires restart) + +#------------------------------------------------------------------------------ +# Other options +#------------------------------------------------------------------------------ +#keepalives_idle = 0 # Keepalives_idle parameter. +#keepalives_interval = 0 # Keepalives_interval parameter. +#keepalives_count = 0 # Keepalives_count internal parameter. +#log_file = 'gtm_proxy.log' # Log file name +#log_min_messages = WARNING # log_min_messages. Default WARNING. + # Valid value: DEBUG, DEBUG5, DEBUG4, DEBU3, + # DEBUG2, DEBUG1, INFO, NOTICE, WARNING, + # ERROR, LOG, FATAL, PANIC. + diff --git a/src/gtm/proxy/gtm_proxy_opt.c b/src/gtm/proxy/gtm_proxy_opt.c new file mode 100644 index 0000000000..0492aaece1 --- /dev/null +++ b/src/gtm/proxy/gtm_proxy_opt.c @@ -0,0 +1,392 @@ +/*-------------------------------------------------------------------- + * guc.c + * + * Support for grand unified configuration scheme, including SET + * command, configuration file, and + command line options. + * See src/backend/utils/misc/README for more information. + * + * + * Copyright (c) 2000-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 2010-2011 Nippon Telegraph and Telephone Corporation + * Written by Peter Eisentraut <[email protected]>. + * + * IDENTIFICATION + * src/backend/utils/misc/guc.c + * + *-------------------------------------------------------------------- + */ +#include "gtm/gtm_c.h" + +#include <ctype.h> +#include <float.h> +#include <math.h> +#include <limits.h> +#include <unistd.h> +#include <sys/stat.h> + +#include "gtm/gtm.h" +#include "gtm/path.h" +#include "gtm/gtm_opt_tables.h" +#include "gtm/gtm_opt.h" +#include "gtm/gtm_standby.h" + +#define CONFIG_FILENAME "gtm_proxy.conf" +const char *config_filename = CONFIG_FILENAME; + +/* + * Variables declared elsewhere for gtm, mainly option variables. + */ + +extern char *GTMProxyNodeName; +extern char *ListenAddresses; +extern int GTMPortNumber; +extern char *error_reporter; +extern char *status_reader; +extern int log_min_messages; +extern int keepalives_idle; +extern int keepalives_count; +extern int keepalives_interval; +extern bool GTMErrorWaitOpt; +extern char *GTMServerHost; +extern int GTMProxyPortNumber; +extern int GTMServerPortNumber; +extern int GTMServerKeepalivesIdle; +extern int GTMServerKeepalivesInterval; +extern int GTMServerKeepalivesCount; +extern int GTMErrorWaitSecs; +extern int GTMErrorWaitCount; +extern int GTMProxyWorkerThreads; +extern char *GTMProxyDataDir; +extern char *GTMProxyConfigFileName; +extern char *GTMConfigFileName; + + +/* + * Macros for values + * + * Some of them are declared also in proxy_main.c. + */ +#define GTM_PROXY_DEFAULT_WORKERS 2 + +/* + * We have different sets for client and server message level options because + * they sort slightly different (see "log" level) + */ + +Server_Message_Level_Options(); + +static const struct config_enum_entry gtm_startup_mode_options[] = { + {"act", GTM_ACT_MODE, false}, + {"standby", GTM_STANDBY_MODE, false}, + {NULL, 0, false} +}; + + +/* + * GTM option variables that are exported from this module + */ +char *data_directory; +char *GTMConfigFileName; + + +/* + * Displayable names for context types (enum GtmContext) + */ +gtmOptContext_Names(); + +/* + * Displayable names for source types (enum GtmSource) + * + */ +gtmOptSource_Names(); + +/* + * Displayable names for GTM variable types (enum config_type) + * + * Note: these strings are deliberately not localized. + */ +Config_Type_Names(); + + +/* + * Contents of GTM tables + * + * See src/backend/utils/misc/README for design notes. + * + * TO ADD AN OPTION: + * + * 1. Declare a global variable of type bool, int, double, or char* + * and make use of it. + * + * 2. Decide at what times it's safe to set the option. See guc.h for + * details. + * + * 3. Decide on a name, a default value, upper and lower bounds (if + * applicable), etc. + * + * 4. Add a record below. + * + * 5. Add it to src/backend/utils/misc/postgresql.conf.sample, if + * appropriate. + * + * 6. Don't forget to document the option (at least in config.sgml). + * + * 7. If it's a new GTMOPT_LIST option you must edit pg_dumpall.c to ensure + * it is not single quoted at dump time. + */ + + +/******** option records follow ********/ + +struct config_bool ConfigureNamesBool[] = +{ + { + {"err_wait_opt", GTMC_SIGHUP, + gettext_noop("If GTM_Proxy waits for reconnect when GTM communication error is encountered."), + NULL, + 0 + }, + >MErrorWaitOpt, + false, false, NULL + }, + /* End-of-list marker */ + { + {NULL, 0, NULL, NULL, 0}, NULL, false, false, NULL + } +}; + + +struct config_int ConfigureNamesInt[] = +{ + { + {"port", GTMC_STARTUP, + gettext_noop("Listen Port of GTM_Proxy server."), + NULL, + 0 + }, + >MProxyPortNumber, + 0, 0, INT_MAX, + 0, NULL + }, + { + {"gtm_port", GTMC_SIGHUP, + gettext_noop("GTM server port number."), + NULL, + 0 + }, + >MServerPortNumber, + 0, 0, INT_MAX, + 0, NULL + }, + { + {"keepalives_idle", GTMC_STARTUP, + gettext_noop("Sets \"keepalives_idle\" option for the connection to GTM."), + NULL, + GTMOPT_UNIT_TIME + }, + >MServerKeepalivesIdle, + 0, 0, INT_MAX, + 0, NULL + }, + { + {"keepalives_interval", GTMC_STARTUP, + gettext_noop("Sets \"keepalives_interval\" option fo the connetion to GTM."), + NULL, + GTMOPT_UNIT_TIME + }, + >MServerKeepalivesInterval, + 0, 0, INT_MAX, + 0, NULL + }, + { + {"keepalives_count", GTMC_STARTUP, + gettext_noop("Sets \"keepalives_count\" option to the connection to GTM."), + NULL, + 0 + }, + >MServerKeepalivesCount, + 0, 0, INT_MAX, + 0, NULL + }, + { + {"err_wait_interval", GTMC_SIGHUP, + gettext_noop("Wait interval to wait for reconnect."), + gettext_noop("This parameter determines GTM Proxy behavior when GTM communication error is encountered."), + 0 + }, + >MErrorWaitSecs, + 0, 0, INT_MAX, + 0, NULL + }, + { + {"err_wait_count", GTMC_SIGHUP, + gettext_noop("Number of err_wait_interval to wait for reconnect."), + gettext_noop("This parameter determines GTM Prox behavior when GTM communication error is encountered."), + 0 + }, + >MErrorWaitCount, + 0, 0, INT_MAX, + 0, NULL + }, + { + {"worker_threads", GTMC_STARTUP, + gettext_noop("Number of worker thread."), + NULL, + 0 + }, + >MProxyWorkerThreads, + GTM_PROXY_DEFAULT_WORKERS, 1, INT_MAX, + 0, NULL + }, + /* End-of-list marker */ + { + {NULL, 0, NULL, NULL, 0}, NULL, 0, 0, 0, 0, NULL + } +}; + + +struct config_real ConfigureNamesReal[] = +{ + /* End-of-list marker */ + { + {NULL, 0, NULL, NULL, 0}, NULL, 0.0, 0.0, 0.0, 0.0, NULL + } +}; + +struct config_string ConfigureNamesString[] = +{ + { + {"data_dir", GTMC_STARTUP, + gettext_noop("Work directory."), + NULL, + 0 + }, + >MProxyDataDir, + NULL, + NULL, + NULL + }, + + { + {"config_file", GTMC_SIGHUP, + gettext_noop("Configuration file name."), + NULL, + 0 + }, + >MConfigFileName, + CONFIG_FILENAME, + NULL, + NULL + }, + + { + {"listen_addresses", GTMC_STARTUP, + gettext_noop("Listen address."), + NULL, + 0 + }, + &ListenAddresses, + NULL, + NULL, NULL + }, + + { + {"nodename", GTMC_STARTUP, + gettext_noop("My node name."), + NULL, + 0, + }, + >MProxyNodeName, + NULL, + NULL, NULL + }, + + { + {"gtm_host", GTMC_SIGHUP, + gettext_noop("Address of target GTM ACT."), + NULL, + 0 + }, + >MServerHost, + NULL, + NULL, NULL + }, + + { + {"log_file", GTMC_SIGHUP, + gettext_noop("Log file name."), + NULL, + 0 + }, + >MLogFile, + "gtm_proxy.log", + NULL, NULL + }, + + { + {"error_reporter", GTMC_SIGHUP, + gettext_noop("Command to report various errors."), + NULL, + 0 + }, + &error_reporter, + NULL, + NULL, NULL + }, + + { + {"status_reader", GTMC_SIGHUP, + gettext_noop("Command to get status of global XC node status."), + gettext_noop("Runs when configuration file is read by SIGHUP"), + 0 + }, + &status_reader, + NULL, + NULL, NULL + }, + + /* End-of-list marker */ + { + {NULL, 0, NULL, NULL}, NULL, NULL, NULL, NULL + } +}; + + +struct config_enum ConfigureNamesEnum[] = +{ + { + {"log_min_messages", GTMC_SIGHUP, + gettext_noop("Minimum message level to write to the log file."), + NULL, + 0 + }, + &log_min_messages, + WARNING, + server_message_level_options, + WARNING, NULL + }, + + /* End-of-list marker */ + { + {NULL, 0, NULL, NULL, 0}, NULL, 0, NULL, 0, NULL + } +}; + +/******** end of options list ********/ + +/* + * Actual lookup of variables is done through this single, sorted array. + */ +struct config_generic **gtm_opt_variables; + +/* Current number of variables contained in the vector */ +int num_gtm_opt_variables; + +/* Vector capacity */ +int size_gtm_opt_variables; + + +bool reporting_enabled; /* TRUE to enable GTMOPT_REPORT */ + +int GTMOptUpdateCount = 0; /* Indicates when specific option is updated */ diff --git a/src/gtm/proxy/proxy_main.c b/src/gtm/proxy/proxy_main.c index f3d80272b0..928d80dc3f 100644 --- a/src/gtm/proxy/proxy_main.c +++ b/src/gtm/proxy/proxy_main.c @@ -44,6 +44,7 @@ #include "gtm/gtm_standby.h" /* For reconnect control lock */ #include "gtm/gtm_lock.h" +#include "gtm/gtm_opt.h" extern int optind; extern char *optarg; @@ -61,15 +62,24 @@ char *ListenAddresses; int GTMProxyPortNumber; int GTMProxyWorkerThreads; char *GTMProxyDataDir; +char *GTMProxyConfigFileName; +char *GTMConfigFileName; /* GTM communication error handling options */ -int GTMErrorWaitOpt = FALSE; /* Wait and assume XCM if TRUE */ +bool GTMErrorWaitOpt = FALSE; /* Wait and assume XCM if TRUE */ int GTMErrorWaitSecs = 0; /* Duration of each wait */ int GTMErrorWaitCount = 0; /* How many durations to wait */ char *GTMServerHost; int GTMServerPortNumber; +/* + * Keepalives setup for the connection with GTM server + */ +int GTMServerKeepalivesIdle = 0; +int GTMServerKeepalivesInterval = 0; +int GTMServerKeepalivesCount = 0; + char *GTMProxyNodeName = NULL; GTM_ThreadID TopMostThreadID; @@ -79,6 +89,13 @@ short ReadyToReconnect = FALSE; char *NewGTMServerHost; int NewGTMServerPortNumber; +/* Status reader/reporter */ +char *error_reporter; +char *status_reader; + +/* Mode */ +bool isStartUp = false; + /* Reconnect Control Lock */ GTM_RWLock ReconnectControlLock; jmp_buf mainThreadSIGUSR1_buf; @@ -92,6 +109,16 @@ pthread_key_t threadinfo_key; static bool GTMProxyAbortPending = false; static GTM_Conn *master_conn; + +/* + * External Routines + */ +extern void InitializeGTMOptions(void); + + +/* + * Internal Routines + */ static Port *ConnCreate(int serverFd); static void ConnFree(Port *conn); static int ServerLoop(void); @@ -502,7 +529,7 @@ help(const char *progname) printf(_(" -p port GTM proxy port number\n")); printf(_(" -s hostname GTM server hostname/IP \n")); printf(_(" -t port GTM server port number\n")); - printf(_(" -i ID number GTM proxy ID number\n")); + printf(_(" -i nodename GTM proxy nodename\n")); printf(_(" -n count Number of worker threads\n")); printf(_(" -D directory GTM proxy working directory\n")); printf(_(" -l filename GTM proxy log file name \n")); @@ -518,6 +545,27 @@ main(int argc, char *argv[]) int i; /* + * Variable to store option parameters + */ + char *listen_addresses = NULL; + char *node_name = NULL; + char *proxy_port_number = NULL; + char *proxy_worker_threads = NULL; + char *data_dir = NULL; + char *log_file = NULL; + char *gtm_host = NULL; + char *gtm_port = NULL; + char *gtm_err_wait_secs = NULL; + char *gtm_err_wait_count = NULL; + + isStartUp = true; + + /* + * At first, initialize options. Also moved something from BaseInit() here. + */ + InitializeGTMOptions(); + + /* * Catch standard options before doing much else */ if (argc > 1) @@ -529,7 +577,9 @@ main(int argc, char *argv[]) } } - ListenAddresses = GTM_PROXY_DEFAULT_HOSTNAME; +/* + ListenAddresses = strdup(GTM_PROXY_DEFAULT_HOSTNAME); +*/ GTMProxyPortNumber = GTM_PROXY_DEFAULT_PORT; GTMProxyWorkerThreads = GTM_PROXY_DEFAULT_WORKERS; @@ -544,52 +594,72 @@ main(int argc, char *argv[]) { case 'h': /* Listen address of the proxy */ - ListenAddresses = strdup(optarg); + if (listen_addresses) + free(listen_addresses); + listen_addresses = strdup(optarg); break; case 'i': /* GTM Proxy identification name */ - GTMProxyNodeName = strdup(optarg); + if (node_name) + free(node_name); + node_name = strdup(optarg); break; case 'p': /* Port number for the proxy to listen on */ - GTMProxyPortNumber = atoi(optarg); + if (proxy_port_number) + free(proxy_port_number); + proxy_port_number = strdup(optarg); break; case 'n': /* Number of worker threads */ - GTMProxyWorkerThreads = atoi(optarg); + if (proxy_worker_threads) + free(proxy_worker_threads); + proxy_worker_threads = strdup(optarg); break; case 'D': - GTMProxyDataDir = strdup(optarg); - canonicalize_path(GTMProxyDataDir); + if (data_dir) + free(data_dir); + data_dir = strdup(optarg); + canonicalize_path(data_dir); break; case 'l': /* The log file */ - GTMLogFile = strdup(optarg); + if (log_file) + free(log_file); + log_file = strdup(optarg); break; case 's': /* GTM server host name */ - GTMServerHost = strdup(optarg); + if (gtm_host) + free(gtm_host); + gtm_host = strdup(optarg); break; case 't': /* GTM server port number */ - GTMServerPortNumber = atoi(optarg); + if (gtm_port) + free(gtm_port); + gtm_port = strdup(optarg); break; case 'w': /* Duration to wait at GTM communication error */ - GTMErrorWaitSecs = atoi(optarg); + if (gtm_err_wait_secs) + free(gtm_err_wait_secs); + gtm_err_wait_secs = strdup(optarg); break; case 'z': /* How many durations to wait */ - GTMErrorWaitCount = atoi(optarg); + if (gtm_err_wait_count) + free(gtm_err_wait_count); + gtm_err_wait_count = strdup(optarg); break; default: @@ -598,6 +668,85 @@ main(int argc, char *argv[]) } } + /* + * Setup working directory + */ + if (data_dir) + SetConfigOption("data_dir", data_dir, GTMC_STARTUP, GTMC_S_OVERRIDE); + + /* + * Setup configuration file + */ + if (!SelectConfigFiles(data_dir, progname)) + exit(1); + + /* + * Parse config file + */ + ProcessConfigFile(GTMC_STARTUP); + + /* + * Override with command line options. "data_dir" was handled in the privious line. + */ + if (listen_addresses) + { + SetConfigOption("listen_addresses", listen_addresses, GTMC_STARTUP, GTMC_S_OVERRIDE); + free(listen_addresses); + listen_addresses = NULL; + } + if (node_name) + { + SetConfigOption("nodename", node_name, GTMC_STARTUP, GTMC_S_OVERRIDE); + free(node_name); + node_name = NULL; + } + if (proxy_port_number) + { + SetConfigOption("port", proxy_port_number, GTMC_STARTUP, GTMC_S_OVERRIDE); + free(proxy_port_number); + proxy_port_number = NULL; + } + if (proxy_worker_threads) + { + SetConfigOption("worker_threads", proxy_worker_threads, GTMC_STARTUP, GTMC_S_OVERRIDE); + free(proxy_worker_threads); + proxy_worker_threads = NULL; + } + if (log_file) + { + SetConfigOption("log_file", log_file, GTMC_STARTUP, GTMC_S_OVERRIDE); + free(log_file); + log_file = NULL; + } + if (gtm_host) + { + SetConfigOption("gtm_host", gtm_host, GTMC_STARTUP, GTMC_S_OVERRIDE); + free(gtm_host); + gtm_host = NULL; + } + if (gtm_port) + { + SetConfigOption("gtm_port", gtm_port, GTMC_STARTUP, GTMC_S_OVERRIDE); + free(gtm_port); + gtm_port = NULL; + } + if (gtm_err_wait_secs) + { + SetConfigOption("err_wait_interval", gtm_err_wait_secs, GTMC_STARTUP, GTMC_S_OVERRIDE); + free(gtm_err_wait_secs); + gtm_err_wait_secs = NULL; + } + if (gtm_err_wait_count) + { + SetConfigOption("err_wait_count", gtm_err_wait_count, GTMC_STARTUP, GTMC_S_OVERRIDE); + free(gtm_err_wait_count); + gtm_err_wait_count = NULL; + } + + + /* + * Check Options + */ if (GTMProxyDataDir == NULL) { write_stderr("GTM Proxy data directory must be specified\n"); @@ -618,11 +767,15 @@ main(int argc, char *argv[]) */ if (GTMErrorWaitSecs > 0 && GTMErrorWaitCount > 0) { - GTMErrorWaitOpt = TRUE; + if (GTMErrorWaitOpt == false) + { + GTMErrorWaitSecs = 0; + GTMErrorWaitCount = 0; + } } else { - GTMErrorWaitOpt = FALSE; + GTMErrorWaitOpt = false; GTMErrorWaitSecs = 0; GTMErrorWaitCount = 0; } @@ -796,7 +949,9 @@ ServerLoop(void) * the resource but this may not happen so many times. */ + elog(LOG, "Main Thread reconnecting to new GTM."); RegisterProxy(TRUE); + elog(LOG, "Reconnected."); /* If it is done, then release the lock for worker threads. */ GTM_RWLockRelease(&ReconnectControlLock); @@ -1808,14 +1963,18 @@ ProcessPGXCNodeCommand(GTMProxy_ConnectionInfo *conninfo, GTM_Conn *gtm_conn, /* Then obtain the node name */ len = pq_getmsgint(message, sizeof(GTM_StrLen)); - cmd_data.cd_reg.nodename = (char *)pq_getmsgbytes(message, len); + cmd_data.cd_reg.nodename = palloc(len + 1); + memcpy(cmd_data.cd_reg.nodename, (char *)pq_getmsgbytes(message, len), len); + cmd_data.cd_reg.nodename[len] = '\0'; /* * Now we have to waste the following host information. It is taken from * the address field in the conn. */ len = pq_getmsgint(message, sizeof(GTM_StrLen)); - cmd_data.cd_reg.ipaddress = (char *)pq_getmsgbytes(message, len); + cmd_data.cd_reg.ipaddress = palloc(len + 1); + memcpy(cmd_data.cd_reg.ipaddress, (char *)pq_getmsgbytes(message, len), len); + cmd_data.cd_reg.ipaddress[len] = '\0'; /* Then the next is the port number */ memcpy(&cmd_data.cd_reg.port, @@ -1825,19 +1984,24 @@ ProcessPGXCNodeCommand(GTMProxy_ConnectionInfo *conninfo, GTM_Conn *gtm_conn, /* Proxy name */ len = pq_getmsgint(message, sizeof(GTM_StrLen)); - cmd_data.cd_reg.gtm_proxy_nodename = (char *)pq_getmsgbytes(message, len); + cmd_data.cd_reg.gtm_proxy_nodename = palloc(len + 1); + memcpy(cmd_data.cd_reg.gtm_proxy_nodename, (char *)pq_getmsgbytes(message, len), len); + cmd_data.cd_reg.gtm_proxy_nodename[len] = '\0'; /* get data folder data */ len = pq_getmsgint(message, sizeof (int)); - cmd_data.cd_reg.datafolder = (char *)pq_getmsgbytes(message, len); + cmd_data.cd_reg.datafolder = palloc(len + 1); + memcpy(cmd_data.cd_reg.datafolder, (char *)pq_getmsgbytes(message, len), len); + cmd_data.cd_reg.datafolder[len] = '\0'; /* Now we have one more data to waste, "status" */ cmd_data.cd_reg.status = pq_getmsgint(message, sizeof(GTM_PGXCNodeStatus)); pq_getmsgend(message); /* Copy also remote host address in data to be proxied */ - cmd_data.cd_reg.ipaddress = (char *) palloc(strlen(remote_host)); + cmd_data.cd_reg.ipaddress = (char *) palloc(strlen(remote_host) + 1); memcpy(cmd_data.cd_reg.ipaddress, remote_host, strlen(remote_host)); + cmd_data.cd_reg.ipaddress[strlen(remote_host)] = '\0'; /* Registering has to be saved where it can be seen by all the threads */ oldContext = MemoryContextSwitchTo(TopMostMemoryContext); @@ -1867,10 +2031,14 @@ ProcessPGXCNodeCommand(GTMProxy_ConnectionInfo *conninfo, GTM_Conn *gtm_conn, { int len; MemoryContext oldContext; + char *nodename; memcpy(&cmd_data.cd_reg.type, pq_getmsgbytes(message, sizeof (GTM_PGXCNodeType)), sizeof (GTM_PGXCNodeType)); len = pq_getmsgint(message, sizeof(GTM_StrLen)); - memcpy(&cmd_data.cd_reg.nodename, pq_getmsgbytes(message, len), len); + nodename = palloc(len + 1); + memcpy(nodename, pq_getmsgbytes(message, len), len); + nodename[len] = '\0'; /* Need null-terminate */ + cmd_data.cd_reg.nodename = nodename; pq_getmsgend(message); /* Unregistering has to be saved in a place where it can be seen by all the threads */ @@ -3002,12 +3170,14 @@ workerThreadReconnectToGTMstandby(void) /* Disconnect the current connection and re-connect to the new GTM */ GTMPQfinish(GetMyThreadInfo->thr_gtm_conn); - sprintf(gtm_connect_string, "host=%s port=%d node name=%s remote_type=%d", + sprintf(gtm_connect_string, "host=%s port=%d node_name=%s remote_type=%d", NewGTMServerHost, NewGTMServerPortNumber, GTMProxyNodeName, PGXC_NODE_GTM_PROXY); + elog(LOG, "Worker thread connecting to %s", gtm_connect_string); GetMyThreadInfo->thr_gtm_conn = PQconnectGTM(gtm_connect_string); if (GetMyThreadInfo->thr_gtm_conn == NULL) - elog(FATAL, "GTM connection failed."); + elog(FATAL, "Worker thread GTM connection failed."); + elog(LOG, "Worker thread connection done."); /* Set GTM communication error handling option */ GetMyThreadInfo->thr_gtm_conn->gtmErrorWaitOpt = GTMErrorWaitOpt; diff --git a/src/gtm/recovery/register.c b/src/gtm/recovery/register.c index b081326287..21d416d5e5 100644 --- a/src/gtm/recovery/register.c +++ b/src/gtm/recovery/register.c @@ -381,11 +381,15 @@ Recovery_PGXCNodeRegister(GTM_PGXCNodeType type, /* Fill in structure */ nodeinfo->type = type; - nodeinfo->nodename = pgxcnode_copy_char(nodename); + if (nodename) + nodeinfo->nodename = pgxcnode_copy_char(nodename); nodeinfo->port = port; - nodeinfo->proxyname = pgxcnode_copy_char(proxyname); - nodeinfo->datafolder = pgxcnode_copy_char(datafolder); - nodeinfo->ipaddress = pgxcnode_copy_char(ipaddress); + if (proxyname) + nodeinfo->proxyname = pgxcnode_copy_char(proxyname); + if (datafolder) + nodeinfo->datafolder = pgxcnode_copy_char(datafolder); + if (ipaddress) + nodeinfo->ipaddress = pgxcnode_copy_char(ipaddress); nodeinfo->status = status; nodeinfo->socket = socket; @@ -462,8 +466,6 @@ ProcessPGXCNodeRegister(Port *myport, StringInfo message) memcpy(proxyname, (char *)pq_getmsgbytes(message, len), len); proxyname[len] = '\0'; - elog(LOG, "ProcessPGXCNodeRegister: ipaddress = %s", ipaddress); - /* * Finish by reading Data Folder (length and then string) */ @@ -472,6 +474,11 @@ ProcessPGXCNodeRegister(Port *myport, StringInfo message) memcpy(datafolder, (char *)pq_getmsgbytes(message, len), len); datafolder[len] = '\0'; + elog(LOG, + "ProcessPGXCNodeRegister: ipaddress = \"%s\", node name = \"%s\", proxy name = \"%s\", " + "datafolder \"%s\"", + ipaddress, node_name, proxyname, datafolder); + status = pq_getmsgint(message, sizeof (GTM_PGXCNodeStatus)); if ((type!=PGXC_NODE_GTM_PROXY) && @@ -483,6 +490,7 @@ ProcessPGXCNodeRegister(Port *myport, StringInfo message) ereport(ERROR, (EINVAL, errmsg("Unknown node type."))); + elog(LOG, "Node type = %d", type); /* * We must use the TopMostMemoryContext because the Node ID information is @@ -729,10 +737,10 @@ ProcessPGXCNodeList(Port *myport, StringInfo message) void Recovery_SaveRegisterInfo(void) { -GTM_PGXCNodeInfoHashBucket *bucket; -gtm_ListCell *elem; -GTM_PGXCNodeInfo *nodeinfo = NULL; -int hash, ctlfd; + GTM_PGXCNodeInfoHashBucket *bucket; + gtm_ListCell *elem; + GTM_PGXCNodeInfo *nodeinfo = NULL; + int hash, ctlfd; char filebkp[GTM_NODE_FILE_MAX_PATH]; GTM_RWLockAcquire(&RegisterFileLock, GTM_LOCKMODE_WRITE); @@ -749,7 +757,7 @@ int hash, ctlfd; return; } -for (hash = 0; hash < NODE_HASH_TABLE_SIZE; hash++) + for (hash = 0; hash < NODE_HASH_TABLE_SIZE; hash++) { bucket = >M_PGXCNodes[hash]; @@ -769,24 +777,57 @@ for (hash = 0; hash < NODE_HASH_TABLE_SIZE; hash++) write(ctlfd, &NodeRegisterMagic, sizeof (NodeRegisterMagic)); write(ctlfd, &nodeinfo->type, sizeof (GTM_PGXCNodeType)); - len = strlen(nodeinfo->nodename); - write(ctlfd, &len, sizeof(uint32)); - write(ctlfd, nodeinfo->nodename, len); + if (nodeinfo->nodename) + { + len = strlen(nodeinfo->nodename); + write(ctlfd, &len, sizeof(uint32)); + write(ctlfd, nodeinfo->nodename, len); + } + else + { + len = 0; + write(ctlfd, &len, sizeof(uint32)); + } + write(ctlfd, &nodeinfo->port, sizeof (GTM_PGXCNodePort)); - len = strlen(nodeinfo->proxyname); - write(ctlfd, &len, sizeof(uint32)); - write(ctlfd, nodeinfo->proxyname, len); + if (nodeinfo->proxyname) + { + len = strlen(nodeinfo->proxyname); + write(ctlfd, &len, sizeof(uint32)); + write(ctlfd, nodeinfo->proxyname, len); + } + else + { + len = 0; + write(ctlfd, &len, sizeof(uint32)); + } write(ctlfd, &nodeinfo->status, sizeof (GTM_PGXCNodeStatus)); - len = strlen(nodeinfo->ipaddress); - write(ctlfd, &len, sizeof(uint32)); - write(ctlfd, nodeinfo->ipaddress, len); + if (nodeinfo->ipaddress) + { + len = strlen(nodeinfo->ipaddress); + write(ctlfd, &len, sizeof(uint32)); + write(ctlfd, nodeinfo->ipaddress, len); + } + else + { + len = 0; + write(ctlfd, &len, sizeof(uint32)); + } - len = strlen(nodeinfo->datafolder); - write(ctlfd, &len, sizeof(uint32)); - write(ctlfd, nodeinfo->datafolder, len); + if (nodeinfo->datafolder) + { + len = strlen(nodeinfo->datafolder); + write(ctlfd, &len, sizeof(uint32)); + write(ctlfd, nodeinfo->datafolder, len); + } + else + { + len = 0; + write(ctlfd, &len, sizeof(uint32)); + } write(ctlfd, &NodeEndMagic, sizeof(NodeEndMagic)); @@ -835,9 +876,18 @@ Recovery_RecordRegisterInfo(GTM_PGXCNodeInfo *nodeinfo, bool is_register) write(ctlfd, &NodeUnregisterMagic, sizeof (NodeUnregisterMagic)); write(ctlfd, &nodeinfo->type, sizeof (GTM_PGXCNodeType)); - len = strlen(nodeinfo->nodename); - write(ctlfd, &len, sizeof(uint32)); - write(ctlfd, nodeinfo->nodename, len); + + if (nodeinfo->nodename) + { + len = strlen(nodeinfo->nodename); + write(ctlfd, &len, sizeof(uint32)); + write(ctlfd, nodeinfo->nodename, len); + } + else + { + len = 0; + write(ctlfd, &len, sizeof(uint32)); + } if (is_register) { @@ -845,19 +895,43 @@ Recovery_RecordRegisterInfo(GTM_PGXCNodeInfo *nodeinfo, bool is_register) write(ctlfd, &nodeinfo->port, sizeof (GTM_PGXCNodePort)); - len = strlen(nodeinfo->proxyname); - write(ctlfd, &len, sizeof(uint32)); - write(ctlfd, nodeinfo->proxyname, len); + if (nodeinfo->proxyname) + { + len = strlen(nodeinfo->proxyname); + write(ctlfd, &len, sizeof(uint32)); + write(ctlfd, nodeinfo->proxyname, len); + } + else + { + len = 0; + write(ctlfd, &len, sizeof(uint32)); + } write(ctlfd, &nodeinfo->status, sizeof (GTM_PGXCNodeStatus)); - len = strlen(nodeinfo->ipaddress); - write(ctlfd, &len, sizeof(uint32)); - write(ctlfd, nodeinfo->ipaddress, len); + if (nodeinfo->ipaddress) + { + len = strlen(nodeinfo->ipaddress); + write(ctlfd, &len, sizeof(uint32)); + write(ctlfd, nodeinfo->ipaddress, len); + } + else + { + len = 0; + write(ctlfd, &len, sizeof(uint32)); + } - len = strlen(nodeinfo->datafolder); - write(ctlfd, &len, sizeof(uint32)); - write(ctlfd, nodeinfo->datafolder, len); + if (nodeinfo->datafolder) + { + len = strlen(nodeinfo->datafolder); + write(ctlfd, &len, sizeof(uint32)); + write(ctlfd, nodeinfo->datafolder, len); + } + else + { + len = 0; + write(ctlfd, &len, sizeof(uint32)); + } } write(ctlfd, &NodeEndMagic, sizeof(NodeEndMagic)); diff --git a/src/gtm/recovery/standby_utils.c b/src/gtm/recovery/standby_utils.c index 4acf81c2b3..a28c0f933a 100644 --- a/src/gtm/recovery/standby_utils.c +++ b/src/gtm/recovery/standby_utils.c @@ -21,9 +21,9 @@ /* * Variables to interact with GTM active under GTM standby mode. */ -static bool GTM_StandbyMode = false; -static char *GTM_ActiveAddress; -static int GTM_ActivePort; +bool GTM_StandbyMode = false; +char *GTM_ActiveAddress; +int GTM_ActivePort; /* For thread safety, values above are protected by a lock */ static GTM_RWLock StandbyLock; diff --git a/src/include/gtm/gtm.h b/src/include/gtm/gtm.h index 9643c6b840..ab970fb925 100644 --- a/src/include/gtm/gtm.h +++ b/src/include/gtm/gtm.h @@ -36,7 +36,7 @@ typedef enum GTM_ThreadStatus struct GTM_ConnectionInfo; -#define ERRORDATA_STACK_SIZE 5 +#define ERRORDATA_STACK_SIZE 20 typedef struct GTM_ThreadInfo { diff --git a/src/include/gtm/gtm_opt.h b/src/include/gtm/gtm_opt.h new file mode 100644 index 0000000000..7d0de94fd6 --- /dev/null +++ b/src/include/gtm/gtm_opt.h @@ -0,0 +1,328 @@ +/*-------------------------------------------------------------------- + * gtm_opt.h + * + * External declarations pertaining to gtm/main/gtm_opt.c, gtm/proxy/gtm_proxy_opt.c and + * gtm/common/gtm_opt_file.l + * + * Portions Copyright (c) 2011, Nippon Telegraph and Telephone Corporation + * Portions Copyright (c) 2000-2011, PostgreSQL Global Development Group + * Written by Peter Eisentraut <[email protected]>. + * Modified by Koichi Suzuki <[email protected]> + * + * src/include/gtm/gtm_opt.h + *-------------------------------------------------------------------- + */ +#ifndef GTM_OPT_H +#define GTM_OPT_H + +/* + * Certain options can only be set at certain times. The rules are + * like this: + * + * INTERNAL options cannot be set by the user at all, but only through + * internal processes ("server_version" is an example). These are GUC + * variables only so they can be shown by SHOW, etc. + * + * POSTMASTER options can only be set when the postmaster starts, + * either from the configuration file or the command line. + * + * SIGHUP options can only be set at postmaster startup or by changing + * the configuration file and sending the HUP signal to the postmaster + * or a backend process. (Notice that the signal receipt will not be + * evaluated immediately. The postmaster and the backend check it at a + * certain point in their main loop. It's safer to wait than to read a + * file asynchronously.) + * + * BACKEND options can only be set at postmaster startup, from the + * configuration file, or by client request in the connection startup + * packet (e.g., from libpq's PGOPTIONS variable). Furthermore, an + * already-started backend will ignore changes to such an option in the + * configuration file. The idea is that these options are fixed for a + * given backend once it's started, but they can vary across backends. + * + * SUSET options can be set at postmaster startup, with the SIGHUP + * mechanism, or from SQL if you're a superuser. + * + * USERSET options can be set by anyone any time. + */ +typedef enum +{ + GTMC_DEFAULT, + GTMC_STARTUP, + GTMC_SIGHUP, + GTMC_USERSET +} GtmOptContext; + +/* + * The following type records the source of the current setting. A + * new setting can only take effect if the previous setting had the + * same or lower level. (E.g, changing the config file doesn't + * override the postmaster command line.) Tracking the source allows us + * to process sources in any convenient order without affecting results. + * Sources <= GTMC_S_OVERRIDE will set the default used by RESET, as well + * as the current value. Note that source == GTMC_S_OVERRIDE should be + * used when setting a GTMC_INTERNAL option. + * + * GTMC_S_INTERACTIVE isn't actually a source value, but is the + * dividing line between "interactive" and "non-interactive" sources for + * error reporting purposes. + * + * GTMC_S_TEST is used when testing values to be stored as per-database or + * per-user defaults ("doit" will always be false, so this never gets stored + * as the actual source of any value). This is an interactive case, but + * it needs its own source value because some assign hooks need to make + * different validity checks in this case. + * + * NB: see GtmOptSource_Names in gtm_opt.c and gtm_proxy_opt.c if you change this. + */ +typedef enum +{ + GTMC_S_DEFAULT, /* hard-wired default ("boot_val") */ + GTMC_S_DYNAMIC_DEFAULT, /* default computed during initialization */ + GTMC_S_ENV_VAR, /* postmaster environment variable *//* Not used in GTM */ + GTMC_S_FILE, /* gtm.conf or gtm_proxy.conf */ + GTMC_S_ARGV, /* postmaster command line */ + GTMC_S_DATABASE, /* per-database setting *//* Not used in GTM */ + GTMC_S_USER, /* per-user setting *//* Not used in GTM */ + GTMC_S_DATABASE_USER, /* per-user-and-database setting *//* Not used in GTM */ + GTMC_S_CLIENT, /* from client connection request *//* Not used in GTM */ + GTMC_S_OVERRIDE, /* special case to forcibly set default *//* Not used in GTM */ + GTMC_S_INTERACTIVE, /* dividing line for error reporting *//* Not used in GTM */ + GTMC_S_TEST, /* test per-database or per-user setting *//* Not used in GTM */ + GTMC_S_SESSION /* SET command *//* Not used in GTM */ +} GtmOptSource; + +/* + * Parsing the configuration file will return a list of name-value pairs + * with source location info. + */ +typedef struct ConfigVariable +{ + char *name; + char *value; + char *filename; + int sourceline; + struct ConfigVariable *next; +} ConfigVariable; + +extern bool ParseConfigFile(const char *config_file, const char *calling_file, + int depth, int elevel, + ConfigVariable **head_p, ConfigVariable **tail_p); +extern bool ParseConfigFp(FILE *fp, const char *config_file, + int depth, int elevel, + ConfigVariable **head_p, ConfigVariable **tail_p); +extern void FreeConfigVariables(ConfigVariable *list); + +/* + * The possible values of an enum variable are specified by an array of + * name-value pairs. The "hidden" flag means the value is accepted but + * won't be displayed when guc.c is asked for a list of acceptable values. + */ +struct config_enum_entry +{ + const char *name; + int val; + bool hidden; +}; + +/* + * Signatures for per-variable check/assign/show hook functions + */ +/* No hook in GTM */ +#if 0 +typedef bool (*GtmOptBoolCheckHook) (bool *newval, void **extra, GtmOptSource source); +typedef bool (*GtmOptIntCheckHook) (int *newval, void **extra, GtmOptSource source); +typedef bool (*GtmOptRealCheckHook) (double *newval, void **extra, GtmOptSource source); +typedef bool (*GtmOptStringCheckHook) (char **newval, void **extra, GtmOptSource source); +typedef bool (*GtmOptEnumCheckHook) (int *newval, void **extra, GtmOptSource source); + +typedef void (*GtmOptBoolAssignHook) (bool newval, void *extra); +typedef void (*GtmOptIntAssignHook) (int newval, void *extra); +typedef void (*GtmOptRealAssignHook) (double newval, void *extra); +typedef void (*GtmOptStringAssignHook) (const char *newval, void *extra); +typedef void (*GtmOptEnumAssignHook) (int newval, void *extra); + +typedef const char *(*GtmOptShowHook) (void); +#endif + +/* + * Miscellaneous + */ +/* + * GTM does not have SET command so it is not used in GTM. It's a dummy. + */ +typedef enum +{ + /* Types of set_config_option actions */ + GTMOPT_ACTION_SET, /* regular SET command */ + GTMOPT_ACTION_LOCAL, /* SET LOCAL command */ + GTMOPT_ACTION_SAVE /* function SET option */ +} GtmOptAction; + +#define GTMOPT_QUALIFIER_SEPARATOR '.' + +/* + * bit values in "flags" of a GUC variable + */ +#define GTMOPT_LIST_INPUT 0x0001 /* input can be list format */ +#define GTMOPT_LIST_QUOTE 0x0002 /* double-quote list elements */ +#define GTMOPT_NO_SHOW_ALL 0x0004 /* exclude from SHOW ALL */ +#define GTMOPT_NO_RESET_ALL 0x0008 /* exclude from RESET ALL */ +#define GTMOPT_REPORT 0x0010 /* auto-report changes to client */ +#define GTMOPT_NOT_IN_SAMPLE 0x0020 /* not in postgresql.conf.sample */ +#define GTMOPT_DISALLOW_IN_FILE 0x0040 /* can't set in postgresql.conf */ +#define GTMOPT_CUSTOM_PLACEHOLDER 0x0080 /* placeholder for custom variable */ +#define GTMOPT_SUPERUSER_ONLY 0x0100 /* show only to superusers */ +#define GTMOPT_IS_NAME 0x0200 /* limit string to NAMEDATALEN-1 */ + +#define GTMOPT_UNIT_KB 0x0400 /* value is in kilobytes */ +#define GTMOPT_UNIT_BLOCKS 0x0800 /* value is in blocks */ +#define GTMOPT_UNIT_XBLOCKS 0x0C00 /* value is in xlog blocks */ +#define GTMOPT_UNIT_MEMORY 0x0C00 /* mask for KB, BLOCKS, XBLOCKS */ + +#define GTMOPT_UNIT_MS 0x1000 /* value is in milliseconds */ +#define GTMOPT_UNIT_S 0x2000 /* value is in seconds */ +#define GTMOPT_UNIT_MIN 0x4000 /* value is in minutes */ +#define GTMOPT_UNIT_TIME 0x7000 /* mask for MS, S, MIN */ + +#define GTMOPT_NOT_WHILE_SEC_REST 0x8000 /* can't set if security restricted */ + +/* + * Functions exported by gtm_opt.c + */ +extern void SetConfigOption(const char *name, const char *value, + GtmOptContext context, GtmOptSource source); + +extern void EmitWarningsOnPlaceholders(const char *className); + +extern const char *GetConfigOption(const char *name, bool restrict_superuser); +extern const char *GetConfigOptionResetString(const char *name); +extern void ProcessConfigFile(GtmOptContext context); +extern void InitializeGTMOptions(void); +extern bool SelectConfigFiles(const char *userDoption, const char *progname); +extern void ResetAllOptions(void); +extern int NewGTMNestLevel(void); +extern bool parse_int(const char *value, int *result, int flags, + const char **hintmsg); +extern bool parse_real(const char *value, double *result); +extern bool set_config_option(const char *name, const char *value, + GtmOptContext context, GtmOptSource source, + bool changeVal); + +extern char *GetConfigOptionByName(const char *name, const char **varname); +extern void GetConfigOptionByNum(int varnum, const char **values, bool *noshow); +extern int GetNumConfigOptions(void); +extern void ParseLongOption(const char *string, char **name, char **value); + +#ifndef PG_KRB_SRVTAB +#define PG_KRB_SRVTAB "" +#endif +#ifndef PG_KRB_SRVNAM +#define PG_KRB_SRVNAM "" +#endif + +/* upper limit for GUC variables measured in kilobytes of memory */ +/* note that various places assume the byte size fits in a "long" variable */ +#if SIZEOF_SIZE_T > 4 && SIZEOF_LONG > 4 +#define MAX_KILOBYTES INT_MAX +#else +#define MAX_KILOBYTES (INT_MAX / 1024) +#endif + +#ifdef TRACE_SORT +extern bool trace_sort; +#endif +#ifdef TRACE_SYNCSCAN +extern bool trace_syncscan; +#endif +#ifdef DEBUG_BOUNDED_SORT +extern bool optimize_bounded_sort; +#endif + + +/* + * Log_min_messages ENUM strings + */ +#define Server_Message_Level_Options()\ +static const struct config_enum_entry server_message_level_options[] = {\ + {"debug", DEBUG2, true},\ + {"debug5", DEBUG5, false},\ + {"debug4", DEBUG4, false},\ + {"debug3", DEBUG3, false},\ + {"debug2", DEBUG2, false},\ + {"debug1", DEBUG1, false},\ + {"info", INFO, false},\ + {"notice", NOTICE, false},\ + {"warning", WARNING, false},\ + {"error", ERROR, false},\ + {"log", LOG, false},\ + {"fatal", FATAL, false},\ + {"panic", PANIC, false},\ + {NULL, 0, false}\ +} + +/* + * Server Startup Option ENUM strings + */ +#define Gtm_Startup_Mode_Options()\ +static const struct config_enum_entry gtm_startup_mode_options[] = {\ + {"act", GTM_ACT_MODE, false},\ + {"standby", GTM_STANDBY_MODE, false},\ + {NULL, 0, false}\ +} + +/* + * Displayable names for context types (enum GtmContext) + * + * Note: these strings are deliberately not localized. + */ +#define gtmOptContext_Names()\ +const char *const GtmOptContext_Names[] =\ +{\ + /* GTMC_STGARTUP */ "startup",\ + /* GTMC_SIGHUP */ "sighup"\ +} + +/* + * Displayable names for source types (enum GtmSource) + * + * Note: these strings are deliberately not localized. + */ +#define gtmOptSource_Names()\ +const char *const GtmOptSource_Names[] =\ +{\ + /* GTMC_S_DEFAULT */ "default",\ + /* GTMC_S_DYNAMIC_DEFAULT */ "default",\ + /* GTMC_S_ENV_VAR */ "environment variable",\ + /* GTMC_S_FILE */ "configuration file",\ + /* GTMC_S_ARGV */ "command line",\ + /* GTMC_S_DATABASE */ "database",\ + /* GTMC_S_USER */ "user",\ + /* GTMC_S_DATABASE_USER */ "database user",\ + /* GTMC_S_CLIENT */ "client",\ + /* GTMC_S_OVERRIDE */ "override",\ + /* GTMC_S_INTERACTIVE */ "interactive",\ + /* GTMC_S_TEST */ "test",\ + /* GTMC_S_SESSION */ "session"\ +} + +/* + * Displayable names for GTM variable types (enum config_type) + * + * Note: these strings are deliberately not localized. + */ +#define Config_Type_Names()\ +const char *const config_type_names[] =\ +{\ + /* GTMC_BOOL */ "bool",\ + /* GTMC_INT */ "integer",\ + /* GTMC_REAL */ "real",\ + /* GTMC_STRING */ "string",\ + /* GTMC_ENUM */ "enum"\ +} + + + + + +#endif /* GTM_OPT_H */ diff --git a/src/include/gtm/gtm_opt_tables.h b/src/include/gtm/gtm_opt_tables.h new file mode 100644 index 0000000000..a0035e6631 --- /dev/null +++ b/src/include/gtm/gtm_opt_tables.h @@ -0,0 +1,194 @@ +/*------------------------------------------------------------------------- + * + * gtm_opt_tables.h + * Declarations of tables used by GTM configuration file. + * + * Portions Copyright (c) 2011, Nippon Telegraph and Telephone Corporation + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * + * src/include/gtm/gtm_opt_tables.h + * + *------------------------------------------------------------------------- + */ +#ifndef GTM_OPT_TABLES_H +#define GTM_OPT_TABLES_H 1 + +#include "gtm/gtm_opt.h" + +/* + * GUC supports these types of variables: + */ +enum config_type +{ + GTMC_BOOL, + GTMC_INT, + GTMC_REAL, + GTMC_STRING, + GTMC_ENUM +}; + +union config_var_val +{ + bool boolval; + int intval; + double realval; + char *stringval; + int enumval; +}; + +/* + * The actual value of a GUC variable can include a malloc'd opaque struct + * "extra", which is created by its check_hook and used by its assign_hook. + */ +typedef struct config_var_value +{ + union config_var_val val; + void *extra; +} config_var_value; + +/* + * Stack entry for saving the state a variable had prior to an uncommitted + * transactional change. In GTM, only chance to update options is SIGINT. + */ +typedef enum +{ + /* This is almost GtmOptAction, but we need a fourth state for SET+LOCAL */ + GTMOPT_SAVE, /* entry caused by function SET option */ + GTMOPT_SET, /* entry caused by plain SET command */ + GTMOPT_LOCAL, /* entry caused by SET LOCAL command */ + GTMOPT_SET_LOCAL /* entry caused by SET then SET LOCAL */ +} GtmOptStackState; + +typedef struct guc_stack +{ + struct guc_stack *prev; /* previous stack item, if any */ + int nest_level; /* nesting depth at which we made entry */ + GtmOptStackState state; /* see enum above */ + GtmOptSource source; /* source of the prior value */ + config_var_value prior; /* previous value of variable */ + config_var_value masked; /* SET value in a GTMOPT_SET_LOCAL entry */ + /* masked value's source must be GTMC_S_SESSION, so no need to store it */ +} GtmOptStack; + +/* + * Generic fields applicable to all types of variables + * + * The short description should be less than 80 chars in length. Some + * applications may use the long description as well, and will append + * it to the short description. (separated by a newline or '. ') + * + * Note that sourcefile/sourceline are kept here, and not pushed into stacked + * values, although in principle they belong with some stacked value if the + * active value is session- or transaction-local. This is to avoid bloating + * stack entries. We know they are only relevant when source == GTMC_S_FILE. + */ +struct config_generic +{ + /* constant fields, must be set correctly in initial value: */ + const char *name; /* name of variable - MUST BE FIRST */ + GtmOptContext context; /* context required to set the variable */ + const char *short_desc; /* short desc. of this variable's purpose */ + const char *long_desc; /* long desc. of this variable's purpose */ + int flags; /* flag bits, see below */ + /* variable fields, initialized at runtime: */ + enum config_type vartype; /* type of variable (set only at startup) */ + int status; /* status bits, see below */ + GtmOptSource reset_source; /* source of the reset_value */ + GtmOptSource source; /* source of the current actual value */ + GtmOptStack *stack; /* stacked prior values */ + void *extra; /* "extra" pointer for current actual value */ + char *sourcefile; /* file current setting is from (NULL if not + * file) */ + int sourceline; /* line in source file */ +}; + +/* bit values in flags field are defined in guc.h */ + +/* bit values in status field */ +#define GTMOPT_IS_IN_FILE 0x0001 /* found it in config file */ +/* + * Caution: the GTMOPT_IS_IN_FILE bit is transient state for ProcessConfigFile. + * Do not assume that its value represents useful information elsewhere. + */ + + +/* GUC records for specific variable types */ + +struct config_bool +{ + struct config_generic gen; + /* constant fields, must be set correctly in initial value: */ + bool *variable; + bool boot_val; + /* variable fields, initialized at runtime: */ + bool reset_val; + void *reset_extra; +}; + +struct config_int +{ + struct config_generic gen; + /* constant fields, must be set correctly in initial value: */ + int *variable; + int boot_val; + int min; + int max; + /* variable fields, initialized at runtime: */ + int reset_val; + void *reset_extra; +}; + +struct config_real +{ + struct config_generic gen; + /* constant fields, must be set correctly in initial value: */ + double *variable; + double boot_val; + double min; + double max; + /* variable fields, initialized at runtime: */ + double reset_val; + void *reset_extra; +}; + +struct config_string +{ + struct config_generic gen; + /* constant fields, must be set correctly in initial value: */ + char **variable; + const char *boot_val; + /* variable fields, initialized at runtime: */ + char *reset_val; + void *reset_extra; +}; + +struct config_enum +{ + struct config_generic gen; + /* constant fields, must be set correctly in initial value: */ + int *variable; + int boot_val; + const struct config_enum_entry *options; + /* variable fields, initialized at runtime: */ + int reset_val; + void *reset_extra; +}; + +/* constant tables corresponding to enums above and in guc.h */ +extern const char *const config_group_names[]; +extern const char *const config_type_names[]; +extern const char *const GtmOptContext_Names[]; +extern const char *const GtmOptSource_Names[]; + +/* get the current set of variables */ +extern struct config_generic **get_guc_variables(void); + +extern void build_guc_variables(void); + +/* search in enum options */ +extern const char *config_enum_lookup_by_value(struct config_enum * record, int val); +extern bool config_enum_lookup_by_name(struct config_enum * record, + const char *value, int *retval); + + +#endif /* GTM_OPT_TABLES_H */ diff --git a/src/include/gtm/gtm_proxy.h b/src/include/gtm/gtm_proxy.h index 7e77220366..b0d30c03b6 100644 --- a/src/include/gtm/gtm_proxy.h +++ b/src/include/gtm/gtm_proxy.h @@ -59,7 +59,7 @@ typedef struct GTMProxy_Connections GTM_RWLock gc_lock; } GTMProxy_Connections; -#define ERRORDATA_STACK_SIZE 5 +#define ERRORDATA_STACK_SIZE 20 #define GTM_PROXY_MAX_CONNECTIONS 1024 typedef struct GTMProxy_ThreadInfo diff --git a/src/include/gtm/gtm_standby.h b/src/include/gtm/gtm_standby.h index 2072599a51..97cce3e33a 100644 --- a/src/include/gtm/gtm_standby.h +++ b/src/include/gtm/gtm_standby.h @@ -42,4 +42,10 @@ void gtm_standby_disconnect_from_standby(GTM_Conn *conn); GTM_Conn *gtm_standby_reconnect_to_standby(GTM_Conn *old_conn, int retry_max); bool gtm_standby_check_communication_error(int *retry_count, GTM_Conn *oldconn); +/* + * Startup mode + */ +#define GTM_ACT_MODE 0 +#define GTM_STANDBY_MODE 1 + #endif /* GTM_STANDBY_H */ diff --git a/src/include/gtm/path.h b/src/include/gtm/path.h index cc07813ace..e8d98fcbf0 100644 --- a/src/include/gtm/path.h +++ b/src/include/gtm/path.h @@ -18,4 +18,7 @@ extern void canonicalize_path(char *path); extern char *make_absolute_path(const char *path); +extern void get_parent_directory(char *path); +extern void join_path_components(char *ret_path, const char *head, const char *tail); + #endif |