I have written a couple of patches to improve the integration of the postgres daemon with systemd.
The setup that is shipped with Red Hat- and Debian-family packages at the moment is just an imitation of the old shell scripts, relying on polling by pg_ctl for readiness, with various custom pieces of complexity for handling custom port numbers and such. In the first patch, my proposal is to use sd_notify() calls from libsystemd to notify the systemd daemon directly when startup is completed. This is the recommended low-overhead solution that is now being adopted by many other server packages. It allows us to cut out pg_ctl completely from the startup configuration and makes the startup configuration manageable by non-wizards. An example is included in the patch. The second patch improves integration with the system journal managed by systemd. This is a facility that captures a daemon's standard output and error and records it in configurable places, including syslog. The patch adds a new log_destination that is like stderr but marks up the output so that systemd knows the severity. With that in place, users can choose to do away with the postgres log file management and let systemd do it. The third patch is technically unrelated but arose while I was working on this. It improves error reporting when the data directory is missing.
From c1725cbf51b79cfd78bc7f9358891599c4ffae6c Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <peter_e@gmx.net> Date: Tue, 17 Nov 2015 06:46:17 -0500 Subject: [PATCH 1/3] Add support for systemd service notifications Insert sd_notify() calls at server start and stop for integration with systemd. This allows the use of systemd service units of type "notify", which greatly simplifies the systemd configuration. --- configure | 38 +++++++++++++++++++++++++++++++++++++ configure.in | 9 +++++++++ doc/src/sgml/installation.sgml | 16 ++++++++++++++++ doc/src/sgml/runtime.sgml | 24 +++++++++++++++++++++++ src/Makefile.global.in | 1 + src/backend/Makefile | 4 ++++ src/backend/postmaster/postmaster.c | 21 ++++++++++++++++++++ src/include/pg_config.h.in | 3 +++ 8 files changed, 116 insertions(+) diff --git a/configure b/configure index b771a83..f9f93b3 100755 --- a/configure +++ b/configure @@ -709,6 +709,7 @@ with_libxml XML2_CONFIG UUID_EXTRA_OBJS with_uuid +with_systemd with_selinux with_openssl krb_srvtab @@ -830,6 +831,7 @@ with_ldap with_bonjour with_openssl with_selinux +with_systemd with_readline with_libedit_preferred with_uuid @@ -1518,6 +1520,7 @@ Optional Packages: --with-bonjour build with Bonjour support --with-openssl build with OpenSSL support --with-selinux build with SELinux support + --with-systemd build with systemd support --without-readline do not use GNU Readline nor BSD Libedit for editing --with-libedit-preferred prefer BSD Libedit over GNU Readline @@ -5695,6 +5698,41 @@ fi $as_echo "$with_selinux" >&6; } # +# Systemd +# +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build with systemd support" >&5 +$as_echo_n "checking whether to build with systemd support... " >&6; } + + + +# Check whether --with-systemd was given. +if test "${with_systemd+set}" = set; then : + withval=$with_systemd; + case $withval in + yes) + +$as_echo "#define USE_SYSTEMD 1" >>confdefs.h + + ;; + no) + : + ;; + *) + as_fn_error $? "no argument expected for --with-systemd option" "$LINENO" 5 + ;; + esac + +else + with_systemd=no + +fi + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_systemd" >&5 +$as_echo "$with_systemd" >&6; } + +# # Readline # diff --git a/configure.in b/configure.in index b5868b0..47c82cb 100644 --- a/configure.in +++ b/configure.in @@ -700,6 +700,15 @@ AC_SUBST(with_selinux) AC_MSG_RESULT([$with_selinux]) # +# Systemd +# +AC_MSG_CHECKING([whether to build with systemd support]) +PGAC_ARG_BOOL(with, systemd, no, [build with systemd support], + [AC_DEFINE([USE_SYSTEMD], 1, [Define to build with systemd support. (--with-systemd)])]) +AC_SUBST(with_systemd) +AC_MSG_RESULT([$with_systemd]) + +# # Readline # PGAC_ARG_BOOL(with, readline, yes, diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml index 8964834..7b6a389 100644 --- a/doc/src/sgml/installation.sgml +++ b/doc/src/sgml/installation.sgml @@ -813,6 +813,22 @@ <title>Configuration</title> </varlistentry> <varlistentry> + <term><option>--with-systemd</option></term> + <listitem> + <para> + Build with support + for <application>systemd</application><indexterm><primary>systemd</primary></indexterm> + service notifications. This improves integration if the server binary + is started under <application>systemd</application> but has no impact + otherwise; see <xref linkend="server-start"> for more + information. <application>libsystemd</application> and the + associated header files need to be installed to be able to use this + option. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><option>--without-readline</option></term> <listitem> <para> diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml index 6d5b108..5e95a3b 100644 --- a/doc/src/sgml/runtime.sgml +++ b/doc/src/sgml/runtime.sgml @@ -369,6 +369,30 @@ <title>Starting the Database Server</title> <filename>contrib/start-scripts/linux</filename> in the <productname>PostgreSQL</productname> source distribution. </para> + + <para> + When using <application>systemd</application>, you can use the following + service unit file (e.g., + at <filename>/etc/systemd/system/postgresql.service</filename>):<indexterm><primary>systemd</primary></indexterm> +<programlisting> +[Unit] +Description=PostgreSQL database server +Documentation=man:postgres(1) + +[Service] +Type=notify +User=postgres +ExecStart=/usr/local/pgsql/bin/postgres -D /usr/local/pgsql/data +ExecReload=/bin/kill -HUP $MAINPID +KillMode=mixed +KillSignal=SIGINT + +[Install] +WantedBy=multi-user.target +</programlisting> + Using <literal>Type=notify</literal> requires that the server binary was + built with <literal>configure --with-systemd</literal>. + </para> </listitem> <listitem> diff --git a/src/Makefile.global.in b/src/Makefile.global.in index 51f4797..e94d6a5 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in @@ -184,6 +184,7 @@ with_python = @with_python@ with_tcl = @with_tcl@ with_openssl = @with_openssl@ with_selinux = @with_selinux@ +with_systemd = @with_systemd@ with_libxml = @with_libxml@ with_libxslt = @with_libxslt@ with_system_tzdata = @with_system_tzdata@ diff --git a/src/backend/Makefile b/src/backend/Makefile index fb60420..455e50b 100644 --- a/src/backend/Makefile +++ b/src/backend/Makefile @@ -45,6 +45,10 @@ LIBS := $(filter-out -lpgport -lpgcommon, $(LIBS)) $(LDAP_LIBS_BE) # The backend doesn't need everything that's in LIBS, however LIBS := $(filter-out -lz -lreadline -ledit -ltermcap -lncurses -lcurses, $(LIBS)) +ifeq ($(with_systemd),yes) +LIBS += -lsystemd +endif + ########################################################################## all: submake-libpgport submake-schemapg postgres $(POSTGRES_IMP) diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 3cba0e5..b23e483 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -87,6 +87,10 @@ #include <dns_sd.h> #endif +#ifdef USE_SYSTEMD +#include <systemd/sd-daemon.h> +#endif + #ifdef HAVE_PTHREAD_IS_THREADED_NP #include <pthread.h> #endif @@ -2533,6 +2537,9 @@ pmdie(SIGNAL_ARGS) Shutdown = SmartShutdown; ereport(LOG, (errmsg("received smart shutdown request"))); +#ifdef USE_SYSTEMD + sd_notify(0, "STOPPING=1"); +#endif if (pmState == PM_RUN || pmState == PM_RECOVERY || pmState == PM_HOT_STANDBY || pmState == PM_STARTUP) @@ -2585,6 +2592,9 @@ pmdie(SIGNAL_ARGS) Shutdown = FastShutdown; ereport(LOG, (errmsg("received fast shutdown request"))); +#ifdef USE_SYSTEMD + sd_notify(0, "STOPPING=1"); +#endif if (StartupPID != 0) signal_child(StartupPID, SIGTERM); @@ -2645,6 +2655,9 @@ pmdie(SIGNAL_ARGS) Shutdown = ImmediateShutdown; ereport(LOG, (errmsg("received immediate shutdown request"))); +#ifdef USE_SYSTEMD + sd_notify(0, "STOPPING=1"); +#endif TerminateChildren(SIGQUIT); pmState = PM_WAIT_BACKENDS; @@ -2787,6 +2800,10 @@ reaper(SIGNAL_ARGS) ereport(LOG, (errmsg("database system is ready to accept connections"))); +#ifdef USE_SYSTEMD + sd_notify(0, "READY=1"); +#endif + continue; } @@ -4928,6 +4945,10 @@ sigusr1_handler(SIGNAL_ARGS) ereport(LOG, (errmsg("database system is ready to accept read only connections"))); +#ifdef USE_SYSTEMD + sd_notify(0, "READY=1"); +#endif + pmState = PM_HOT_STANDBY; /* Some workers may be scheduled to start now */ StartWorkerNeeded = true; diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index a20e0cd..110ecb1 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -830,6 +830,9 @@ /* Define to 1 to use Intel SSSE 4.2 CRC instructions with a runtime check. */ #undef USE_SSE42_CRC32C_WITH_RUNTIME_CHECK +/* Define to build with systemd support. (--with-systemd) */ +#undef USE_SYSTEMD + /* Define to select SysV-style semaphores. */ #undef USE_SYSV_SEMAPHORES -- 2.6.3
From ea46337f24df4d251243d4ea923c5ff1e2f65e96 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <peter_e@gmx.net> Date: Tue, 17 Nov 2015 06:46:44 -0500 Subject: [PATCH 2/3] Add log_destination "systemd" This allows systemd to associate severity levels with messages printed to standard output or standard error. --- doc/src/sgml/config.sgml | 14 +++++++++- doc/src/sgml/runtime.sgml | 2 +- src/backend/utils/error/elog.c | 37 +++++++++++++++++++++++++++ src/backend/utils/misc/guc.c | 4 ++- src/backend/utils/misc/postgresql.conf.sample | 7 ++--- src/include/utils/elog.h | 1 + 6 files changed, 59 insertions(+), 6 deletions(-) diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 6e14851..a61d179 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -3821,7 +3821,7 @@ <title>Where To Log</title> <para> <productname>PostgreSQL</productname> supports several methods for logging server messages, including - <systemitem>stderr</systemitem>, <systemitem>csvlog</systemitem> and + <systemitem>stderr</systemitem>, <systemitem>systemd</systemitem>, <systemitem>csvlog</systemitem> and <systemitem>syslog</systemitem>. On Windows, <systemitem>eventlog</systemitem> is also supported. Set this parameter to a list of desired log destinations separated by @@ -3831,6 +3831,18 @@ <title>Where To Log</title> file or on the server command line. </para> <para> + <systemitem>systemd</systemitem><indexterm><primary>systemd</primary></indexterm> + is like <systemitem>stderr</systemitem>, but log messages are + additionally prefixed so that <application>systemd</application> can + associate severity levels + (see <citerefentry><refentrytitle>sd-daemon</refentrytitle><manvolnum>3</manvolnum></citerefentry>). + This is recommended if the server is started and logs are captured + by <application>systemd</application>. It is normally not useful to + configure <systemitem>systemd</systemitem> together + with <systemitem>stderr</systemitem> + or <systemitem>syslog</systemitem>. + </para> + <para> If <systemitem>csvlog</> is included in <varname>log_destination</>, log entries are output in <quote>comma separated value</> (<acronym>CSV</>) format, which is convenient for diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml index 5e95a3b..0773610 100644 --- a/doc/src/sgml/runtime.sgml +++ b/doc/src/sgml/runtime.sgml @@ -382,7 +382,7 @@ <title>Starting the Database Server</title> [Service] Type=notify User=postgres -ExecStart=/usr/local/pgsql/bin/postgres -D /usr/local/pgsql/data +ExecStart=/usr/local/pgsql/bin/postgres -D /usr/local/pgsql/data -c log_destination=systemd ExecReload=/bin/kill -HUP $MAINPID KillMode=mixed KillSignal=SIGINT diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index 8b5b6c5..12e6cea 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -3004,6 +3004,43 @@ send_message_to_server_log(ErrorData *edata) write_console(buf.data, buf.len); } + if (Log_destination & LOG_DESTINATION_SYSTEMD) + { + const char *systemd_log_prefix; + + switch (edata->elevel) + { + case DEBUG5: + case DEBUG4: + case DEBUG3: + case DEBUG2: + case DEBUG1: + systemd_log_prefix = "<7>" /* SD_DEBUG */; + break; + case LOG: + case COMMERROR: + case INFO: + systemd_log_prefix = "<6>" /* SD_INFO */; + break; + case NOTICE: + case WARNING: + systemd_log_prefix = "<5>" /* SD_NOTICE */; + break; + case ERROR: + systemd_log_prefix = "<4>" /* SD_WARNING */; + break; + case FATAL: + systemd_log_prefix = "<3>" /* SD_ERR */; + break; + case PANIC: + default: + systemd_log_prefix = "<2>" /* LOG_CRIT */; + break; + } + write_console(systemd_log_prefix, strlen(systemd_log_prefix)); + write_console(buf.data, buf.len); + } + /* If in the syslogger process, try to write messages direct to file */ if (am_syslogger) write_syslogger_file(buf.data, buf.len, LOG_DESTINATION_STDERR); diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index a185749..c9a06bd 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -3123,7 +3123,7 @@ static struct config_string ConfigureNamesString[] = {"log_destination", PGC_SIGHUP, LOGGING_WHERE, gettext_noop("Sets the destination for server log output."), gettext_noop("Valid values are combinations of \"stderr\", " - "\"syslog\", \"csvlog\", and \"eventlog\", " + "\"syslog\", \"systemd\", \"csvlog\", and \"eventlog\", " "depending on the platform."), GUC_LIST_INPUT }, @@ -9694,6 +9694,8 @@ check_log_destination(char **newval, void **extra, GucSource source) if (pg_strcasecmp(tok, "stderr") == 0) newlogdest |= LOG_DESTINATION_STDERR; + else if (pg_strcasecmp(tok, "systemd") == 0) + newlogdest |= LOG_DESTINATION_SYSTEMD; else if (pg_strcasecmp(tok, "csvlog") == 0) newlogdest |= LOG_DESTINATION_CSVLOG; #ifdef HAVE_SYSLOG diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 029114f..2af7465 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -322,9 +322,10 @@ # - Where to Log - #log_destination = 'stderr' # Valid values are combinations of - # stderr, csvlog, syslog, and eventlog, - # depending on platform. csvlog - # requires logging_collector to be on. + # stderr, systemd, csvlog, syslog, and + # eventlog, depending on platform. + # csvlog requires logging_collector to + # be on. # This is used when logging to stderr: #logging_collector = off # Enable capturing of stderr and csvlog diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 7715719..b816e82 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -402,6 +402,7 @@ extern char *Log_destination_string; #define LOG_DESTINATION_SYSLOG 2 #define LOG_DESTINATION_EVENTLOG 4 #define LOG_DESTINATION_CSVLOG 8 +#define LOG_DESTINATION_SYSTEMD 16 /* Other exported functions */ extern void DebugFileOpen(void); -- 2.6.3
From 661989f7912a8d1df84bcb798fb4348a8114c291 Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <peter_e@gmx.net> Date: Tue, 17 Nov 2015 06:47:18 -0500 Subject: [PATCH 3/3] Improve error reporting when location specified by postgres -D does not exist Previously, the first error seen would be that postgresql.conf does not exist. But for the case where the whole directory does not exist, give an error message about that, together with a hint for how to create one. --- src/backend/utils/misc/guc.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index c9a06bd..d9de2da 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -4463,6 +4463,17 @@ SelectConfigFiles(const char *userDoption, const char *progname) else configdir = make_absolute_path(getenv("PGDATA")); + if (configdir && stat(configdir, &stat_buf) != 0) + { + write_stderr("%s: could not access \"%s\": %s\n", + progname, + configdir, + strerror(errno)); + if (errno == ENOENT) + write_stderr("Run initdb or pg_basebackup to initialize a PostgreSQL data directory.\n"); + return false; + } + /* * Find the configuration file: if config_file was specified on the * command line, use it, else use configdir/postgresql.conf. In any case @@ -4498,7 +4509,7 @@ SelectConfigFiles(const char *userDoption, const char *progname) */ if (stat(ConfigFileName, &stat_buf) != 0) { - write_stderr("%s cannot access the server configuration file \"%s\": %s\n", + write_stderr("%s: could not access the server configuration file \"%s\": %s\n", progname, ConfigFileName, strerror(errno)); free(configdir); return false; -- 2.6.3
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers