I've attached a chump little utility called pg_ping(1) and the
necessary update to pg_ctl(1) to see if a database is up and running
or not.  There are three things that I haven't completed with this
that I'll try and get to the next time I have a small chunk of time
available:

1) I'd hoped to add some basic profiling support so that admins could
   time the amount of time it takes to have a connection be
   established (I've found it to be strangely useful).

2) A -f switch that does a "full" test and actually executes "SELECT
   TRUE" from the database.

3) Poke around to see if I could add support for getopt_long()

Anyway, let me throw this out that way it can get kicked around as
appropriate.  Because it's so simple in its nature and because right
now pg_ctl(1) is kinda broken in its current state (setup any kind of
auth and pg_ctl hangs), it'd be slick 50 if this patch could sneak
into the 7.4 release.  :)

-sc


PS, it looks like there's a bug in the backend (not sure how old my
CVS copy is) if you comment out all of you pg_hba.conf entries, you
get a garbled message on the backend:

2003-07-18 15:53:40 [15386] FATAL:  No pg_hba.conf entry for host ^L, user pgsql, 
database template1
                                                                  ^^


-- 
Sean Chittenden
Index: src/bin/pg_ctl/pg_ctl.sh
===================================================================
RCS file: /home/ncvs/pgsql/pgsql-server/src/bin/pg_ctl/pg_ctl.sh,v
retrieving revision 1.32
diff -u -r1.32 pg_ctl.sh
--- src/bin/pg_ctl/pg_ctl.sh    20 Mar 2003 05:00:14 -0000      1.32
+++ src/bin/pg_ctl/pg_ctl.sh    18 Jul 2003 23:04:03 -0000
@@ -98,12 +98,12 @@
 fi
 
 # Check if needed programs actually exist in path
-if [ -x "$self_path/postmaster" ] && [ -x "$self_path/psql" ]; then
+if [ -x "$self_path/postmaster" ] && [ -x "$self_path/pg_ping" ]; then
     PGPATH="$self_path"
-elif [ -x "$bindir/postmaster" ] && [ -x "$bindir/psql" ]; then
+elif [ -x "$bindir/postmaster" ] && [ -x "$bindir/pg_ping" ]; then
     PGPATH="$bindir"
 else
-    echo "The programs 'postmaster' and 'psql' are needed by $CMDNAME but" 1>&2
+    echo "The programs 'postmaster' and 'pg_ping' are needed by $CMDNAME but" 1>&2
     echo "were not found in the directory '$bindir'." 1>&2
     echo "Check your installation." 1>&2
     exit 1
@@ -358,16 +358,12 @@
         fi
     fi
 
-# FIXME:  This is horribly misconceived.
-# 1) If password authentication is set up, the connection will fail.
-# 2) If a virtual host is set up, the connection may fail.
-# 3) If network traffic filters are set up tight enough, the connection
+# FIXME:  This is less misconceived, but not perfect.
+# 1) If a virtual host is set up, the connection may fail.
+# 2) If network traffic filters are set up tight enough, the connection
 #    may fail.
-# 4) When no Unix domain sockets are available, the connection will
-#    fail.  (Using TCP/IP by default ain't better.)
-# 5) If the dynamic loader is not set up correctly (for this user/at
-#    this time), psql will fail (to find libpq).
-# 6) If psql is misconfigured, this may fail.
+# 3) If the dynamic loader is not set up correctly (for this user/at
+#    this time), pg_ping will fail (to find libpq).
 
     # Attempt to use the right port
     # Use PGPORT if set, otherwise look in the configuration file
@@ -384,7 +380,13 @@
        $silence_echo $ECHO_N "waiting for postmaster to start..."$ECHO_C
        while :
        do
-           if "$PGPATH/psql" -p $PGPORT -l >/dev/null 2>&1
+           if [ -z "$PGPORT" ];then
+               PGPORT_OPT=""
+           else
+               PGPORT_OPT="-p $PGPORT"
+           fi
+
+           if "$PGPATH/pg_ping" -q $PGPORT_OPT
            then
                break;
            else
Index: src/bin/Makefile
===================================================================
RCS file: /home/ncvs/pgsql/pgsql-server/src/bin/Makefile,v
retrieving revision 1.39
diff -u -r1.39 Makefile
--- src/bin/Makefile    3 Sep 2002 21:45:43 -0000       1.39
+++ src/bin/Makefile    18 Jul 2003 19:56:09 -0000
@@ -15,7 +15,7 @@
 
 DIRS := initdb initlocation ipcclean pg_ctl pg_dump pg_id \
        psql scripts pg_config pg_controldata pg_resetxlog \
-       pg_encoding
+       pg_encoding pg_ping
 
 ifeq ($(with_tcl), yes)
        DIRS += pgtclsh
Index: doc/src/sgml/ref/allfiles.sgml
===================================================================
RCS file: /home/ncvs/pgsql/pgsql-server/doc/src/sgml/ref/allfiles.sgml,v
retrieving revision 1.54
diff -u -r1.54 allfiles.sgml
--- doc/src/sgml/ref/allfiles.sgml      27 Jun 2003 14:45:25 -0000      1.54
+++ doc/src/sgml/ref/allfiles.sgml      18 Jul 2003 23:48:20 -0000
@@ -116,6 +116,7 @@
 <!entity pgCtl              system "pg_ctl-ref.sgml">
 <!entity pgDump             system "pg_dump.sgml">
 <!entity pgDumpall          system "pg_dumpall.sgml">
+<!entity pgPing                    system "pg_ping-ref.sgml">
 <!entity pgResetxlog        system "pg_resetxlog.sgml">
 <!entity pgRestore          system "pg_restore.sgml">
 <!entity pgTclSh            system "pgtclsh.sgml">
Index: doc/src/sgml/ref/pg_ping-ref.sgml
===================================================================
RCS file: doc/src/sgml/ref/pg_ping-ref.sgml
diff -N doc/src/sgml/ref/pg_ping-ref.sgml
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ doc/src/sgml/ref/pg_ping-ref.sgml   18 Jul 2003 23:42:20 -0000
@@ -0,0 +1,198 @@
+<!--
+$Header$
+PostgreSQL documentation
+-->
+
+<refentry id="APP-PGPING">
+ <refmeta>
+  <refentrytitle 
id="APP-PGPING-TITLE"><application>pg_ping</application></refentrytitle>
+  <manvolnum>1</manvolnum>
+  <refmiscinfo>Application</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname id="pgping">pg_ping</refname>
+  <refpurpose>Test to see if a <productname>PostgreSQL</productname> database is up 
or down</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+  <cmdsynopsis>
+   <command>pg_ping</command>
+   <arg rep="repeat"><replaceable>connection-option</replaceable></arg>
+   <arg>-d <replaceable>dbname</replaceable></arg>
+   <arg>-h <replaceable>host</replaceable></arg>
+   <arg>-p <replaceable>port</replaceable></arg>
+   <arg>-q</arg>
+   <arg>-w <replaceable>connection timeout</replaceable></arg>
+   <arg>-v</arg>
+  </cmdsynopsis>
+ </refsynopsisdiv>
+
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <application>pg_ping</application> is a utility for determining
+   whether or not a specified <productname>PostgreSQL</productname>
+   database is up and running or down.  If up,
+   <application>pg_ping</application> exits with a 0 return status: if
+   down, it exists with an exit code of 1.
+  </para>
+
+  <para>
+   <application>pg_ping</application> uses
+   <application>libpq</application> and can be used to test whether
+   any local or remote <application>PostgreSQL</application> database
+   is up or down.  Any files, environment variables, or settings that
+   are applicable to <application>libpq</application> apply to
+   <application>pg_ping</application>.
+  </para>
+ </refsect1>
+
+
+ <refsect1>
+  <title>Options</title>
+
+   <para>
+    <application>pg_ping</application> accepts the following command-line arguments:
+
+    <variablelist>
+     <varlistentry>
+      <term><option>-d</option> <replaceable 
class="parameter">dbname</replaceable></term>
+      <listitem>
+       <para>
+       Specifies the database name to connect to.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>-h</option> <replaceable 
class="parameter">host</replaceable></term>
+      <listitem>
+       <para>
+       Specifies the host to connect to.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>-p</option> <replaceable 
class="parameter">port</replaceable></term>
+      <listitem>
+       <para>
+       Specifies the port to connect to.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>-q</option></term>
+      <listitem>
+       <para>
+       Decreases the verbosity of <application>pg_ping</application>.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>-w</option> <replaceable 
class="parameter">connection_timeout</replaceable></term>
+      <listitem>
+       <para>
+       Specifies the connection timeout.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><option>-v</option></term>
+      <listitem>
+       <para>
+       Increases the verbosity of <application>pg_ping</application>.
+       </para>
+      </listitem>
+     </varlistentry>
+
+    </variablelist>
+   </para>
+ </refsect1>
+
+
+ <refsect1>
+  <title>Diagnostics</title>
+
+   <para>
+    The output of <application>pg_ping</application> is dependent on
+    the verbosity, however, by default,
+    <application>pg_ping</application> will emmit the following
+    output:
+    <variablelist>
+     <varlistentry>
+      <term><computeroutput>UP</computeroutput></term>
+      <listitem>
+       <para>
+        The database is up and running and able to accept connections.
+       </para>
+      </listitem>
+     </varlistentry>
+
+     <varlistentry>
+      <term><computeroutput>DOWN</computeroutput></term>
+      <listitem>
+       <para>
+        The database is down and unavailable for one reason or another.
+       </para>
+      </listitem>
+     </varlistentry>
+
+    </variablelist>
+   </para>
+ </refsect1>
+
+
+ <refsect1>
+  <title>Examples</title>
+
+   <para>
+    To see if the database on <literal>db.example.com</literal> is up:
+<screen>
+<prompt>$ </prompt><userinput>pg_ping -h db.example.com</userinput>
+</screen>
+   </para>
+
+   <para>
+    To use <application>pg_ping</application> in a script and
+    silently, use the <literal>-q</literal> option to rely on the exit
+    code:
+<screen>
+<prompt>$ </prompt><userinput>pg_ping -q -h db.example.com</userinput>
+</screen>
+   </para>
+
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="app-pg-ctl" endterm="app-pg-ctl-title"></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
+
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:nil
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+sgml-parent-document:nil
+sgml-default-dtd-file:"../reference.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:"/usr/lib/sgml/catalog"
+sgml-local-ecat-files:nil
+End:
+-->
Index: doc/src/sgml/ref/pg_ctl-ref.sgml
===================================================================
RCS file: /home/ncvs/pgsql/pgsql-server/doc/src/sgml/ref/pg_ctl-ref.sgml,v
retrieving revision 1.22
diff -u -r1.22 pg_ctl-ref.sgml
--- doc/src/sgml/ref/pg_ctl-ref.sgml    25 Mar 2003 16:15:42 -0000      1.22
+++ doc/src/sgml/ref/pg_ctl-ref.sgml    18 Jul 2003 23:45:59 -0000
@@ -214,9 +214,9 @@
        Wait for the start or shutdown to complete.  Times out after
        60 seconds.  This is the default for shutdowns. A successful 
         shutdown is indicated by removal of the <acronym>PID</acronym> 
-        file. For starting up, a successful <command>psql -l</command> 
+        file. For starting up, a successful <application>pg_ping</application>
         indicates success. <command>pg_ctl</command> will attempt to 
-        use the proper port for psql. If the environment variable 
+        use the proper port for <application>pg_ping</application>. If the 
environment variable 
         PGPORT exists, that is used. Otherwise, it will see if a port 
         has been set in the <filename>postgresql.conf</filename> file. 
         If neither of those is used, it will use the default port that 
@@ -259,7 +259,7 @@
 
     <listitem>
      <para>
-      Default port for <xref linkend="app-psql"> (used by the -w option).
+      Default port for <xref linkend="app-pgping"> (used by the -w option).
      </para>
     </listitem>
    </varlistentry>
@@ -321,7 +321,7 @@
     <listitem>
      <para>
       This file, located in the data directory, is parsed to find the
-      proper port to use with <application>psql</application> when the
+      proper port to use with <application>pg_ping</application> when the
       <option>-w</option> is given in <option>start</option> mode.
      </para>
     </listitem>
Index: doc/src/sgml/release.sgml
===================================================================
RCS file: /home/ncvs/pgsql/pgsql-server/doc/src/sgml/release.sgml,v
retrieving revision 1.198
diff -u -r1.198 release.sgml
--- doc/src/sgml/release.sgml   17 Jul 2003 00:55:36 -0000      1.198
+++ doc/src/sgml/release.sgml   18 Jul 2003 23:50:49 -0000
@@ -55,6 +55,7 @@
 Long options for psql and pg_dump are now available on all platforms
 Read-only transactions
 Object owners can allow grantees to grant the privilege to others (grant option)
+Add "pg_ping" utility to fix pg_ctl -w when an authentication scheme is in use
 ]]></literallayout>
 
  </sect1>
Index: src/bin/pg_ping/Makefile
===================================================================
RCS file: src/bin/pg_ping/Makefile
diff -N src/bin/pg_ping/Makefile
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ src/bin/pg_ping/Makefile    18 Jul 2003 22:43:52 -0000
@@ -0,0 +1,38 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for src/bin/pg_ping
+#
+# Copyright (C) 2000 by PostgreSQL Global Development Team
+#
+# $Header$
+#
+#-------------------------------------------------------------------------
+
+subdir = src/bin/pg_ping
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+CFLAGS += -I../../../src/interfaces/libpq
+
+all: pg_ping
+
+pg_ping: pg_ping.o
+       $(CC) $(CFLAGS) -I../../../src/interfaces/libpq $(LDFLAGS) $(libpq) $^ $(LIBS) 
-o $@
+
+install: all installdirs
+       $(INSTALL_PROGRAM) pg_ping$(X) $(DESTDIR)$(bindir)/pg_ping$(X)
+
+installdirs:
+       $(mkinstalldirs) $(DESTDIR)$(bindir)
+
+uninstall:
+       rm -f $(DESTDIR)$(bindir)/pg_ping$(X)
+
+depend dep:
+       $(CC) -MM $(CFLAGS) *.c >depend
+
+clean distclean maintainer-clean:
+       rm -f pg_ping$(X) pg_ping.o
+
+ifeq (depend,$(wildcard depend))
+include depend
+endif
Index: src/bin/pg_ping/pg_ping.c
===================================================================
RCS file: src/bin/pg_ping/pg_ping.c
diff -N src/bin/pg_ping/pg_ping.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ src/bin/pg_ping/pg_ping.c   18 Jul 2003 23:32:08 -0000
@@ -0,0 +1,221 @@
+/*
+ * pg_ping.c
+ *
+ * A very basic "ping" utility to see if PostgreSQL is alive and responding to
+ * requests.  This doesn't do anything as fancy as perform an SQL query, but it
+ * is intended to provide basic testing functionality to see if a database is
+ * up or down (primarily in startup/shutdown scripts).
+ *
+ * Copyright (C) 2003 by PostgreSQL Global Development Group
+ *
+ * $Header$
+ */
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "libpq-fe.h"
+#include "postgres_fe.h"
+
+void   usage(char *);
+
+
+int
+main(int argc, char *argv[])
+{
+       int      c,
+                ret = 0,
+                connstate = -1,
+                verbosity = 1;
+       char    *conninfo = NULL,
+               *dbname = NULL,
+               *host = NULL,
+               *port = NULL,
+               *statestr = NULL,
+               *timeout = NULL;
+       PGconn  *conn;
+
+       extern int       optind;
+       extern char     *optarg;
+
+       while ((c = getopt(argc, argv, "d:h:qp:vw:")) != -1)
+       {
+               switch (c)
+               {
+                       case 'd':
+                               asprintf(&dbname, "dbname='%s'", optarg);
+                               break;
+                       case 'h':
+                               asprintf(&host, "host='%s'", optarg);
+                               break;
+                       case 'q':
+                               verbosity--;
+                               break;
+                       case 'p':
+                               asprintf(&port, "port='%s'", optarg);
+                               break;
+                       case 'u':
+                               /* It'd be slick if there was a global
+                                * PGSQL_WWW_DOCS global that way
+                                * developers could embed URLs in help
+                                * messages and the URLs would be
+                                * updated every release, such as:
+                                *
+                                * printf("%s/%s", PGSQL_WWW_DOCS, "libpq-files.html");
+                                */
+                               fprintf(stderr, "Username not supported by %s.  Use a 
~/.pgpass file instead\n", argv[0]);
+                               exit(1);
+                       case 'v':
+                               verbosity++;
+                               break;
+                       case 'w':
+                               asprintf(&timeout, "connect_timeout='%s'", optarg);
+                               break;
+                       default:
+                               usage(argv[0]);
+               }
+       }
+
+       if (argc - optind != 0)
+               usage(argv[0]);
+
+       /* Not having a PQconnect struct and having to work with
+        * strings is the sucks. */
+       if (dbname != NULL)
+               asprintf(&conninfo, "%s", dbname);
+
+       if (host != NULL) {
+               if (conninfo == NULL)
+                       asprintf(&conninfo, "%s", host);
+               else
+                       asprintf(&conninfo, "%s;%s", strdup(conninfo),host);
+       }
+
+       if (port != NULL) {
+               if (conninfo == NULL)
+                       asprintf(&conninfo, "%s", port);
+               else
+                       asprintf(&conninfo, "%s;%s", strdup(conninfo),port);
+       }
+
+       if (timeout != NULL) {
+               if (conninfo == NULL)
+                       asprintf(&conninfo, "%s", timeout);
+               else
+                       asprintf(&conninfo, "%s;%s", strdup(conninfo),timeout);
+       }
+
+       if (conninfo == NULL)
+               conninfo = "";
+
+       if (verbosity >= 3)
+               printf("Connect string: %s\n", conninfo);
+
+       conn = PQconnectStart(conninfo);
+       do {
+               statestr = NULL;
+               switch(PQstatus(conn)) {
+                       case CONNECTION_OK:
+                               if (connstate != 0) {
+                                       statestr = "Database up and running.";
+                                       connstate = 0;
+                               }
+                               ret = 0;
+                               break;
+                       case CONNECTION_BAD:
+                               if (connstate != 1) {
+                                       statestr = "Unable to make connection.";
+                                       connstate = 1;
+                               }
+                               ret = 1;
+                               break;
+                       case CONNECTION_STARTED:
+                               if (connstate != 2) {
+                                       statestr = "Waiting for connection to be 
made.";
+                                       connstate = 2;
+                               }
+                               ret = -1;
+                               break;
+                       case CONNECTION_MADE:
+                               if (connstate != 3) {
+                                       statestr = "Connection OK; waiting to send.";
+                                       connstate = 3;
+                               }
+                               ret = 0;
+                               break;
+                       case CONNECTION_AWAITING_RESPONSE:
+                               if (connstate != 4) {
+                                       statestr = "Waiting for a response from the 
postmaster.";
+                                       connstate = 4;
+                               }
+                               ret = -1;
+                               break;
+                       case CONNECTION_AUTH_OK:
+                               if (connstate != 5) {
+                                       statestr = "Received authentication; waiting 
for backend startup.";
+                                       connstate = 5;
+                               }
+                               ret = -1;
+                               break;
+                       case CONNECTION_SETENV:
+                               if (connstate != 6) {
+                                       statestr = "Negotiating environment.";
+                                       connstate = 6;
+                               }
+                               ret = -1;
+                               break;
+                       case CONNECTION_SSL_STARTUP:
+                               if (connstate != 7) {
+                                       statestr = "Negotiating SSL.";
+                                       connstate = 7;
+                               }
+                               ret = -1;
+                               break;
+                       case CONNECTION_NEEDED:
+                               if (connstate != 8) {
+                                       statestr = "Internal state: connect() needed.";
+                                       connstate = 8;
+                               }
+                               ret = -1;
+                               break;
+                       default:
+                               fprintf(stderr, "Unknown status code, aborting\n");
+                               abort();
+               }
+
+               if (verbosity >= 3)
+                       printf("%s\n", statestr);
+       } while (ret == -1);
+
+       PQfinish(conn);
+
+       if (verbosity >= 1) {
+               if (verbosity >= 2)
+                       printf("Database ");
+
+               if (ret == 0)
+                       printf("UP");
+               else
+                       printf("DOWN");
+
+               if (verbosity >= 2)
+                       printf(": %s\n", statestr);
+               else
+                       printf("\n");
+       }
+
+       return(ret);
+}
+
+
+void
+usage(char *progname)
+{
+       fprintf(stderr, "Usage: %s [-q] [-v] [-d dbname] [-h hostname] [-p port] [-w 
timeout]\n", progname);
+       exit(1);
+}

Attachment: pgp00000.pgp
Description: PGP signature

Reply via email to