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

Reply via email to