*
* pg_ctl --- start/stops/restarts the PostgreSQL server
*
- * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/pg_ctl/pg_ctl.c,v 1.80 2007/05/31 15:13:04 petere Exp $
+ * src/bin/pg_ctl/pg_ctl.c
*
*-------------------------------------------------------------------------
*/
* Need this to get defines for restricted tokens and jobs. And it
* has to be set before any header from the Win32 API is loaded.
*/
-#define _WIN32_WINNT 0x0500
+#define _WIN32_WINNT 0x0501
#endif
#include "postgres_fe.h"
#include <locale.h>
#include <signal.h>
+#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "libpq/pqsignal.h"
#include "getopt_long.h"
+#include "miscadmin.h"
#if defined(__CYGWIN__)
#include <sys/cygwin.h>
#undef WIN32
#endif
-#ifndef HAVE_INT_OPTRESET
-int optreset;
-#endif
-
/* PID can be negative for standalone backend */
typedef long pgpid_t;
-#define WHITESPACE "\f\n\r\t\v" /* as defined by isspace() */
-
-/* postgres version ident string */
-#define PM_VERSIONSTR "postgres (PostgreSQL) " PG_VERSION "\n"
-
-
typedef enum
{
SMART_MODE,
typedef enum
{
NO_COMMAND = 0,
+ INIT_COMMAND,
START_COMMAND,
STOP_COMMAND,
RESTART_COMMAND,
RELOAD_COMMAND,
STATUS_COMMAND,
+ PROMOTE_COMMAND,
KILL_COMMAND,
REGISTER_COMMAND,
UNREGISTER_COMMAND,
RUN_AS_SERVICE_COMMAND
} CtlCommand;
+#define DEFAULT_WAIT 60
static bool do_wait = false;
static bool wait_set = false;
-static int wait_seconds = 60;
+static int wait_seconds = DEFAULT_WAIT;
static bool silent_mode = false;
static ShutdownMode shutdown_mode = SMART_MODE;
static int sig = SIGTERM; /* default */
static CtlCommand ctl_command = NO_COMMAND;
static char *pg_data = NULL;
+static char *pg_config = NULL;
static char *pgdata_opt = NULL;
static char *post_opts = NULL;
static const char *progname;
static char *log_file = NULL;
-static char *postgres_path = NULL;
-static const char *register_servicename = "PostgreSQL"; /* FIXME: + version ID? */
+static char *exec_path = NULL;
+static char *register_servicename = "PostgreSQL"; /* FIXME: + version ID? */
static char *register_username = NULL;
static char *register_password = NULL;
static char *argv0 = NULL;
static bool allow_core_files = false;
+static time_t start_time;
+
+static char postopts_file[MAXPGPATH];
+static char pid_file[MAXPGPATH];
+static char backup_file[MAXPGPATH];
+static char recovery_file[MAXPGPATH];
+static char promote_file[MAXPGPATH];
+
+#if defined(WIN32) || defined(__CYGWIN__)
+static DWORD pgctl_start_type = SERVICE_AUTO_START;
+static SERVICE_STATUS status;
+static SERVICE_STATUS_HANDLE hStatus = (SERVICE_STATUS_HANDLE) 0;
+static HANDLE shutdownHandles[2];
+static pid_t postmasterPID = -1;
+
+#define shutdownEvent shutdownHandles[0]
+#define postmasterProcess shutdownHandles[1]
+#endif
+
static void
write_stderr(const char *fmt,...)
/* This extension allows gcc to check the format string for consistency with
the supplied arguments. */
-__attribute__((format(printf, 1, 2)));
+__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
static void *pg_malloc(size_t size);
static char *xstrdup(const char *s);
static void do_advice(void);
static void do_help(void);
static void set_mode(char *modeopt);
static void set_sig(char *signame);
+static void do_init(void);
static void do_start(void);
static void do_stop(void);
static void do_restart(void);
static void do_reload(void);
static void do_status(void);
+static void do_promote(void);
static void do_kill(pgpid_t pid);
static void print_msg(const char *msg);
+static void adjust_data_dir(void);
#if defined(WIN32) || defined(__CYGWIN__)
static bool pgwin32_IsInstalled(SC_HANDLE);
static void WINAPI pgwin32_ServiceHandler(DWORD);
static void WINAPI pgwin32_ServiceMain(DWORD, LPTSTR *);
static void pgwin32_doRunAsService(void);
-static int CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION * processInfo);
+static int CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_service);
#endif
+
static pgpid_t get_pgpid(void);
static char **readfile(const char *path);
static int start_postmaster(void);
-static bool test_postmaster_connection(void);
-static bool postmaster_is_alive(pid_t pid);
+static void read_post_opts(void);
-static char def_postopts_file[MAXPGPATH];
-static char postopts_file[MAXPGPATH];
-static char pid_file[MAXPGPATH];
-static char conf_file[MAXPGPATH];
+static PGPing test_postmaster_connection(bool);
+static bool postmaster_is_alive(pid_t pid);
#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
static void unlimit_core_size(void);
{
static HANDLE evtHandle = INVALID_HANDLE_VALUE;
+ if (silent_mode && level == EVENTLOG_INFORMATION_TYPE)
+ return;
+
if (evtHandle == INVALID_HANDLE_VALUE)
{
evtHandle = RegisterEventSource(NULL, "PostgreSQL");
readfile(const char *path)
{
FILE *infile;
- int maxlength = 0,
+ int maxlength = 1,
linelen = 0;
int nlines = 0;
char **result;
/*
* Since there might be quotes to handle here, it is easier simply to pass
* everything to a shell to process them.
+ *
+ * XXX it would be better to fork and exec so that we would know the child
+ * postmaster's PID directly; then test_postmaster_connection could use
+ * the PID without having to rely on reading it back from the pidfile.
*/
if (log_file != NULL)
- snprintf(cmd, MAXPGPATH, "%s\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1 &%s",
- SYSTEMQUOTE, postgres_path, pgdata_opt, post_opts,
- DEVNULL, log_file, SYSTEMQUOTE);
+ snprintf(cmd, MAXPGPATH, SYSTEMQUOTE "\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1 &" SYSTEMQUOTE,
+ exec_path, pgdata_opt, post_opts,
+ DEVNULL, log_file);
else
- snprintf(cmd, MAXPGPATH, "%s\"%s\" %s%s < \"%s\" 2>&1 &%s",
- SYSTEMQUOTE, postgres_path, pgdata_opt, post_opts,
- DEVNULL, SYSTEMQUOTE);
+ snprintf(cmd, MAXPGPATH, SYSTEMQUOTE "\"%s\" %s%s < \"%s\" 2>&1 &" SYSTEMQUOTE,
+ exec_path, pgdata_opt, post_opts, DEVNULL);
return system(cmd);
#else /* WIN32 */
PROCESS_INFORMATION pi;
if (log_file != NULL)
- snprintf(cmd, MAXPGPATH, "CMD /C %s\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1%s",
- SYSTEMQUOTE, postgres_path, pgdata_opt, post_opts,
- DEVNULL, log_file, SYSTEMQUOTE);
+ snprintf(cmd, MAXPGPATH, "CMD /C " SYSTEMQUOTE "\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1" SYSTEMQUOTE,
+ exec_path, pgdata_opt, post_opts, DEVNULL, log_file);
else
- snprintf(cmd, MAXPGPATH, "CMD /C %s\"%s\" %s%s < \"%s\" 2>&1%s",
- SYSTEMQUOTE, postgres_path, pgdata_opt, post_opts,
- DEVNULL, SYSTEMQUOTE);
+ snprintf(cmd, MAXPGPATH, "CMD /C " SYSTEMQUOTE "\"%s\" %s%s < \"%s\" 2>&1" SYSTEMQUOTE,
+ exec_path, pgdata_opt, post_opts, DEVNULL);
- if (!CreateRestrictedProcess(cmd, &pi))
+ if (!CreateRestrictedProcess(cmd, &pi, false))
return GetLastError();
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
-/* Find the pgport and try a connection */
-static bool
-test_postmaster_connection(void)
+/*
+ * Find the pgport and try a connection
+ *
+ * Note that the checkpoint parameter enables a Windows service control
+ * manager checkpoint, it's got nothing to do with database checkpoints!!
+ */
+static PGPing
+test_postmaster_connection(bool do_checkpoint)
{
- PGconn *conn;
- bool success = false;
+ PGPing ret = PQPING_NO_RESPONSE;
+ bool found_stale_pidfile = false;
+ pgpid_t pm_pid = 0;
+ char connstr[MAXPGPATH * 2 + 256];
int i;
- char portstr[32];
- char *p;
- *portstr = '\0';
+ /* if requested wait time is zero, return "still starting up" code */
+ if (wait_seconds <= 0)
+ return PQPING_REJECT;
- /* post_opts */
- for (p = post_opts; *p;)
- {
- /* advance past whitespace/quoting */
- while (isspace((unsigned char) *p) || *p == '\'' || *p == '"')
- p++;
+ connstr[0] = '\0';
- if (strncmp(p, "-p", strlen("-p")) == 0)
+ for (i = 0; i < wait_seconds; i++)
+ {
+ /* Do we need a connection string? */
+ if (connstr[0] == '\0')
{
- p += strlen("-p");
- /* advance past whitespace/quoting */
- while (isspace((unsigned char) *p) || *p == '\'' || *p == '"')
- p++;
- strlcpy(portstr, p, Min(strcspn(p, "\"'" WHITESPACE) + 1,
- sizeof(portstr)));
- /* keep looking, maybe there is another -p */
+ /*----------
+ * The number of lines in postmaster.pid tells us several things:
+ *
+ * # of lines
+ * 0 lock file created but status not written
+ * 2 pre-9.1 server, shared memory not created
+ * 3 pre-9.1 server, shared memory created
+ * 5 9.1+ server, ports not opened
+ * 6 9.1+ server, shared memory not created
+ * 7 9.1+ server, shared memory created
+ *
+ * This code does not support pre-9.1 servers. On Unix machines
+ * we could consider extracting the port number from the shmem
+ * key, but that (a) is not robust, and (b) doesn't help with
+ * finding out the socket directory. And it wouldn't work anyway
+ * on Windows.
+ *
+ * If we see less than 6 lines in postmaster.pid, just keep
+ * waiting.
+ *----------
+ */
+ char **optlines;
+
+ /* Try to read the postmaster.pid file */
+ if ((optlines = readfile(pid_file)) != NULL &&
+ optlines[0] != NULL &&
+ optlines[1] != NULL &&
+ optlines[2] != NULL)
+ {
+ if (optlines[3] == NULL)
+ {
+ /* File is exactly three lines, must be pre-9.1 */
+ write_stderr(_("\n%s: -w option is not supported when starting a pre-9.1 server\n"),
+ progname);
+ return PQPING_NO_ATTEMPT;
+ }
+ else if (optlines[4] != NULL &&
+ optlines[5] != NULL)
+ {
+ /* File is complete enough for us, parse it */
+ long pmpid;
+ time_t pmstart;
+
+ /*
+ * Make sanity checks. If it's for a standalone backend
+ * (negative PID), or the recorded start time is before
+ * pg_ctl started, then either we are looking at the wrong
+ * data directory, or this is a pre-existing pidfile that
+ * hasn't (yet?) been overwritten by our child postmaster.
+ * Allow 2 seconds slop for possible cross-process clock
+ * skew.
+ */
+ pmpid = atol(optlines[LOCK_FILE_LINE_PID - 1]);
+ pmstart = atol(optlines[LOCK_FILE_LINE_START_TIME - 1]);
+ if (pmpid <= 0 || pmstart < start_time - 2)
+ {
+ /*
+ * Set flag to report stale pidfile if it doesn't get
+ * overwritten before we give up waiting.
+ */
+ found_stale_pidfile = true;
+ }
+ else
+ {
+ /*
+ * OK, seems to be a valid pidfile from our child.
+ */
+ int portnum;
+ char *sockdir;
+ char *hostaddr;
+ char host_str[MAXPGPATH];
+
+ found_stale_pidfile = false;
+ pm_pid = (pgpid_t) pmpid;
+
+ /*
+ * Extract port number and host string to use. Prefer
+ * using Unix socket if available.
+ */
+ portnum = atoi(optlines[LOCK_FILE_LINE_PORT - 1]);
+ sockdir = optlines[LOCK_FILE_LINE_SOCKET_DIR - 1];
+ hostaddr = optlines[LOCK_FILE_LINE_LISTEN_ADDR - 1];
+
+ /*
+ * While unix_socket_directory can accept relative
+ * directories, libpq's host parameter must have a
+ * leading slash to indicate a socket directory. So,
+ * ignore sockdir if it's relative, and try to use TCP
+ * instead.
+ */
+ if (sockdir[0] == '/')
+ strlcpy(host_str, sockdir, sizeof(host_str));
+ else
+ strlcpy(host_str, hostaddr, sizeof(host_str));
+
+ /* remove trailing newline */
+ if (strchr(host_str, '\n') != NULL)
+ *strchr(host_str, '\n') = '\0';
+
+ /* Fail if couldn't get either sockdir or host addr */
+ if (host_str[0] == '\0')
+ {
+ write_stderr(_("\n%s: -w option cannot use a relative socket directory specification\n"),
+ progname);
+ return PQPING_NO_ATTEMPT;
+ }
+
+ /* If postmaster is listening on "*", use localhost */
+ if (strcmp(host_str, "*") == 0)
+ strcpy(host_str, "localhost");
+
+ /*
+ * We need to set connect_timeout otherwise on Windows
+ * the Service Control Manager (SCM) will probably
+ * timeout first.
+ */
+ snprintf(connstr, sizeof(connstr),
+ "dbname=postgres port=%d host='%s' connect_timeout=5",
+ portnum, host_str);
+ }
+ }
+ }
}
- /* Advance to next whitespace */
- while (*p && !isspace((unsigned char) *p))
- p++;
- }
- /* config file */
- if (!*portstr)
- {
- char **optlines;
+ /* If we have a connection string, ping the server */
+ if (connstr[0] != '\0')
+ {
+ ret = PQping(connstr);
+ if (ret == PQPING_OK || ret == PQPING_NO_ATTEMPT)
+ break;
+ }
- optlines = readfile(conf_file);
- if (optlines != NULL)
+ /*
+ * The postmaster should create postmaster.pid very soon after being
+ * started. If it's not there after we've waited 5 or more seconds,
+ * assume startup failed and give up waiting. (Note this covers both
+ * cases where the pidfile was never created, and where it was created
+ * and then removed during postmaster exit.) Also, if there *is* a
+ * file there but it appears stale, issue a suitable warning and give
+ * up waiting.
+ */
+ if (i >= 5)
{
- for (; *optlines != NULL; optlines++)
+ struct stat statbuf;
+
+ if (stat(pid_file, &statbuf) != 0)
+ return PQPING_NO_RESPONSE;
+
+ if (found_stale_pidfile)
{
- p = *optlines;
-
- while (isspace((unsigned char) *p))
- p++;
- if (strncmp(p, "port", strlen("port")) != 0)
- continue;
- p += strlen("port");
- while (isspace((unsigned char) *p))
- p++;
- if (*p != '=')
- continue;
- p++;
- while (isspace((unsigned char) *p))
- p++;
- strlcpy(portstr, p, Min(strcspn(p, "#" WHITESPACE) + 1,
- sizeof(portstr)));
- /* keep looking, maybe there is another */
+ write_stderr(_("\n%s: this data directory appears to be running a pre-existing postmaster\n"),
+ progname);
+ return PQPING_NO_RESPONSE;
}
}
- }
- /* environment */
- if (!*portstr && getenv("PGPORT") != NULL)
- strlcpy(portstr, getenv("PGPORT"), sizeof(portstr));
-
- /* default */
- if (!*portstr)
- snprintf(portstr, sizeof(portstr), "%d", DEF_PGPORT);
+ /*
+ * If we've been able to identify the child postmaster's PID, check
+ * the process is still alive. This covers cases where the postmaster
+ * successfully created the pidfile but then crashed without removing
+ * it.
+ */
+ if (pm_pid > 0 && !postmaster_is_alive((pid_t) pm_pid))
+ return PQPING_NO_RESPONSE;
- for (i = 0; i < wait_seconds; i++)
- {
- if ((conn = PQsetdbLogin(NULL, portstr, NULL, NULL,
- "postgres", NULL, NULL)) != NULL &&
- (PQstatus(conn) == CONNECTION_OK ||
- (strcmp(PQerrorMessage(conn),
- PQnoPasswordSupplied) == 0)))
+ /* No response, or startup still in process; wait */
+#if defined(WIN32)
+ if (do_checkpoint)
{
- PQfinish(conn);
- success = true;
- break;
+ /*
+ * Increment the wait hint by 6 secs (connection timeout + sleep)
+ * We must do this to indicate to the SCM that our startup time is
+ * changing, otherwise it'll usually send a stop signal after 20
+ * seconds, despite incrementing the checkpoint counter.
+ */
+ status.dwWaitHint += 6000;
+ status.dwCheckPoint++;
+ SetServiceStatus(hStatus, (LPSERVICE_STATUS) &status);
}
else
- {
- PQfinish(conn);
+#endif
print_msg(".");
- pg_usleep(1000000); /* 1 sec */
- }
+
+ pg_usleep(1000000); /* 1 sec */
}
- return success;
+ /* return result of last call to PQping */
+ return ret;
}
#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
-static void
+static void
unlimit_core_size(void)
{
struct rlimit lim;
- getrlimit(RLIMIT_CORE,&lim);
+
+ getrlimit(RLIMIT_CORE, &lim);
if (lim.rlim_max == 0)
{
- write_stderr(_("%s: cannot set core size, disallowed by hard limit.\n"),
- progname);
- return;
+ write_stderr(_("%s: cannot set core file size limit; disallowed by hard limit\n"),
+ progname);
+ return;
}
else if (lim.rlim_max == RLIM_INFINITY || lim.rlim_cur < lim.rlim_max)
{
lim.rlim_cur = lim.rlim_max;
- setrlimit(RLIMIT_CORE,&lim);
- }
+ setrlimit(RLIMIT_CORE, &lim);
+ }
}
#endif
-
-
static void
-do_start(void)
+read_post_opts(void)
{
- pgpid_t pid;
- pgpid_t old_pid = 0;
- char *optline = NULL;
- int exitcode;
-
- if (ctl_command != RESTART_COMMAND)
- {
- old_pid = get_pgpid();
- if (old_pid != 0)
- write_stderr(_("%s: another server might be running; "
- "trying to start server anyway\n"),
- progname);
- }
-
if (post_opts == NULL)
{
- char **optlines;
- int len;
-
- optlines = readfile(ctl_command == RESTART_COMMAND ?
- postopts_file : def_postopts_file);
- if (optlines == NULL)
+ post_opts = ""; /* default */
+ if (ctl_command == RESTART_COMMAND)
{
- if (ctl_command == START_COMMAND)
- post_opts = "";
- else
+ char **optlines;
+
+ optlines = readfile(postopts_file);
+ if (optlines == NULL)
{
write_stderr(_("%s: could not read file \"%s\"\n"), progname, postopts_file);
exit(1);
}
- }
- else if (optlines[0] == NULL || optlines[1] != NULL)
- {
- write_stderr(_("%s: option file \"%s\" must have exactly one line\n"),
- progname, ctl_command == RESTART_COMMAND ?
- postopts_file : def_postopts_file);
- exit(1);
- }
- else
- {
- optline = optlines[0];
- len = strcspn(optline, "\r\n");
- optline[len] = '\0';
-
- if (ctl_command == RESTART_COMMAND)
+ else if (optlines[0] == NULL || optlines[1] != NULL)
+ {
+ write_stderr(_("%s: option file \"%s\" must have exactly one line\n"),
+ progname, postopts_file);
+ exit(1);
+ }
+ else
{
+ int len;
+ char *optline;
char *arg1;
- arg1 = strchr(optline, *SYSTEMQUOTE);
- if (arg1 == NULL || arg1 == optline)
- post_opts = "";
- else
+ optline = optlines[0];
+ /* trim off line endings */
+ len = strcspn(optline, "\r\n");
+ optline[len] = '\0';
+
+ /*
+ * Are we at the first option, as defined by space and
+ * double-quote?
+ */
+ if ((arg1 = strstr(optline, " \"")) != NULL)
{
- *(arg1 - 1) = '\0'; /* this should be a space */
- post_opts = arg1;
+ *arg1 = '\0'; /* terminate so we get only program
+ * name */
+ post_opts = arg1 + 1; /* point past whitespace */
}
- if (postgres_path != NULL)
- postgres_path = optline;
+ if (exec_path == NULL)
+ exec_path = optline;
}
- else
- post_opts = optline;
}
}
+}
- /* No -D or -D already added during server start */
- if (ctl_command == RESTART_COMMAND || pgdata_opt == NULL)
- pgdata_opt = "";
+static char *
+find_other_exec_or_die(const char *argv0, const char *target, const char *versionstr)
+{
+ int ret;
+ char *found_path;
- if (postgres_path == NULL)
+ found_path = pg_malloc(MAXPGPATH);
+
+ if ((ret = find_other_exec(argv0, target, versionstr, found_path)) < 0)
{
- char *postmaster_path;
- int ret;
+ char full_path[MAXPGPATH];
- postmaster_path = pg_malloc(MAXPGPATH);
+ if (find_my_exec(argv0, full_path) < 0)
+ strlcpy(full_path, progname, sizeof(full_path));
- if ((ret = find_other_exec(argv0, "postgres", PM_VERSIONSTR,
- postmaster_path)) < 0)
- {
- char full_path[MAXPGPATH];
+ if (ret == -1)
+ write_stderr(_("The program \"%s\" is needed by %s "
+ "but was not found in the\n"
+ "same directory as \"%s\".\n"
+ "Check your installation.\n"),
+ target, progname, full_path);
+ else
+ write_stderr(_("The program \"%s\" was found by \"%s\"\n"
+ "but was not the same version as %s.\n"
+ "Check your installation.\n"),
+ target, full_path, progname);
+ exit(1);
+ }
- if (find_my_exec(argv0, full_path) < 0)
- strlcpy(full_path, progname, sizeof(full_path));
+ return found_path;
+}
- if (ret == -1)
- write_stderr(_("The program \"postgres\" is needed by %s "
- "but was not found in the\n"
- "same directory as \"%s\".\n"
- "Check your installation.\n"),
- progname, full_path);
- else
- write_stderr(_("The program \"postgres\" was found by \"%s\"\n"
- "but was not the same version as %s.\n"
- "Check your installation.\n"),
- full_path, progname);
- exit(1);
- }
- postgres_path = postmaster_path;
+static void
+do_init(void)
+{
+ char cmd[MAXPGPATH];
+
+ if (exec_path == NULL)
+ exec_path = find_other_exec_or_die(argv0, "initdb", "initdb (PostgreSQL) " PG_VERSION "\n");
+
+ if (pgdata_opt == NULL)
+ pgdata_opt = "";
+
+ if (post_opts == NULL)
+ post_opts = "";
+
+ if (!silent_mode)
+ snprintf(cmd, MAXPGPATH, SYSTEMQUOTE "\"%s\" %s%s" SYSTEMQUOTE,
+ exec_path, pgdata_opt, post_opts);
+ else
+ snprintf(cmd, MAXPGPATH, SYSTEMQUOTE "\"%s\" %s%s > \"%s\"" SYSTEMQUOTE,
+ exec_path, pgdata_opt, post_opts, DEVNULL);
+
+ if (system(cmd) != 0)
+ {
+ write_stderr(_("%s: database system initialization failed\n"), progname);
+ exit(1);
+ }
+}
+
+static void
+do_start(void)
+{
+ pgpid_t old_pid = 0;
+ int exitcode;
+
+ if (ctl_command != RESTART_COMMAND)
+ {
+ old_pid = get_pgpid();
+ if (old_pid != 0)
+ write_stderr(_("%s: another server might be running; "
+ "trying to start server anyway\n"),
+ progname);
}
+ read_post_opts();
+
+ /* No -D or -D already added during server start */
+ if (ctl_command == RESTART_COMMAND || pgdata_opt == NULL)
+ pgdata_opt = "";
+
+ if (exec_path == NULL)
+ exec_path = find_other_exec_or_die(argv0, "postgres", PG_BACKEND_VERSIONSTR);
+
#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
if (allow_core_files)
unlimit_core_size();
#endif
+ /*
+ * If possible, tell the postmaster our parent shell's PID (see the
+ * comments in CreateLockFile() for motivation). Windows hasn't got
+ * getppid() unfortunately.
+ */
+#ifndef WIN32
+ {
+ static char env_var[32];
+
+ snprintf(env_var, sizeof(env_var), "PG_GRANDPARENT_PID=%d",
+ (int) getppid());
+ putenv(env_var);
+ }
+#endif
+
exitcode = start_postmaster();
if (exitcode != 0)
{
exit(1);
}
- if (old_pid != 0)
- {
- pg_usleep(1000000);
- pid = get_pgpid();
- if (pid == old_pid)
- {
- write_stderr(_("%s: could not start server\n"
- "Examine the log output.\n"),
- progname);
- exit(1);
- }
- }
-
if (do_wait)
{
print_msg(_("waiting for server to start..."));
- if (test_postmaster_connection() == false)
- {
- printf(_("could not start server\n"));
- exit(1);
- }
- else
+ switch (test_postmaster_connection(false))
{
- print_msg(_(" done\n"));
- print_msg(_("server started\n"));
+ case PQPING_OK:
+ print_msg(_(" done\n"));
+ print_msg(_("server started\n"));
+ break;
+ case PQPING_REJECT:
+ print_msg(_(" stopped waiting\n"));
+ print_msg(_("server is still starting up\n"));
+ break;
+ case PQPING_NO_RESPONSE:
+ print_msg(_(" stopped waiting\n"));
+ write_stderr(_("%s: could not start server\n"
+ "Examine the log output.\n"),
+ progname);
+ exit(1);
+ break;
+ case PQPING_NO_ATTEMPT:
+ print_msg(_(" failed\n"));
+ write_stderr(_("%s: could not wait for server because of misconfiguration\n"),
+ progname);
+ exit(1);
}
}
else
{
int cnt;
pgpid_t pid;
+ struct stat statbuf;
pid = get_pgpid();
}
else
{
+ /*
+ * If backup_label exists, an online backup is running. Warn the user
+ * that smart shutdown will wait for it to finish. However, if
+ * recovery.conf is also present, we're recovering from an online
+ * backup instead of performing one.
+ */
+ if (shutdown_mode == SMART_MODE &&
+ stat(backup_file, &statbuf) == 0 &&
+ stat(recovery_file, &statbuf) != 0)
+ {
+ print_msg(_("WARNING: online backup mode is active\n"
+ "Shutdown will not complete until pg_stop_backup() is called.\n\n"));
+ }
+
print_msg(_("waiting for server to shut down..."));
for (cnt = 0; cnt < wait_seconds; cnt++)
print_msg(_(" failed\n"));
write_stderr(_("%s: server does not shut down\n"), progname);
+ if (shutdown_mode == SMART_MODE)
+ write_stderr(_("HINT: The \"-m fast\" option immediately disconnects sessions rather than\n"
+ "waiting for session-initiated disconnection.\n"));
exit(1);
}
print_msg(_(" done\n"));
- printf(_("server stopped\n"));
+ print_msg(_("server stopped\n"));
}
}
{
int cnt;
pgpid_t pid;
+ struct stat statbuf;
pid = get_pgpid();
exit(1);
}
+ /*
+ * If backup_label exists, an online backup is running. Warn the user
+ * that smart shutdown will wait for it to finish. However, if
+ * recovery.conf is also present, we're recovering from an online
+ * backup instead of performing one.
+ */
+ if (shutdown_mode == SMART_MODE &&
+ stat(backup_file, &statbuf) == 0 &&
+ stat(recovery_file, &statbuf) != 0)
+ {
+ print_msg(_("WARNING: online backup mode is active\n"
+ "Shutdown will not complete until pg_stop_backup() is called.\n\n"));
+ }
+
print_msg(_("waiting for server to shut down..."));
/* always wait for restart */
print_msg(_(" failed\n"));
write_stderr(_("%s: server does not shut down\n"), progname);
+ if (shutdown_mode == SMART_MODE)
+ write_stderr(_("HINT: The \"-m fast\" option immediately disconnects sessions rather than\n"
+ "waiting for session-initiated disconnection.\n"));
exit(1);
}
print_msg(_(" done\n"));
- printf(_("server stopped\n"));
+ print_msg(_("server stopped\n"));
}
else
{
do_start();
}
-
static void
do_reload(void)
{
print_msg(_("server signaled\n"));
}
+
+/*
+ * promote
+ */
+
+static void
+do_promote(void)
+{
+ FILE *prmfile;
+ pgpid_t pid;
+ struct stat statbuf;
+
+ pid = get_pgpid();
+
+ if (pid == 0) /* no pid file */
+ {
+ write_stderr(_("%s: PID file \"%s\" does not exist\n"), progname, pid_file);
+ write_stderr(_("Is server running?\n"));
+ exit(1);
+ }
+ else if (pid < 0) /* standalone backend, not postmaster */
+ {
+ pid = -pid;
+ write_stderr(_("%s: cannot promote server; "
+ "single-user server is running (PID: %ld)\n"),
+ progname, pid);
+ exit(1);
+ }
+
+ /* If recovery.conf doesn't exist, the server is not in standby mode */
+ if (stat(recovery_file, &statbuf) != 0)
+ {
+ write_stderr(_("%s: cannot promote server; "
+ "server is not in standby mode\n"),
+ progname);
+ exit(1);
+ }
+
+ if ((prmfile = fopen(promote_file, "w")) == NULL)
+ {
+ write_stderr(_("%s: could not create promote signal file \"%s\": %s\n"),
+ progname, promote_file, strerror(errno));
+ exit(1);
+ }
+ if (fclose(prmfile))
+ {
+ write_stderr(_("%s: could not write promote signal file \"%s\": %s\n"),
+ progname, promote_file, strerror(errno));
+ exit(1);
+ }
+
+ sig = SIGUSR1;
+ if (kill((pid_t) pid, sig) != 0)
+ {
+ write_stderr(_("%s: could not send promote signal (PID: %ld): %s\n"),
+ progname, pid, strerror(errno));
+ if (unlink(promote_file) != 0)
+ write_stderr(_("%s: could not remove promote signal file \"%s\": %s\n"),
+ progname, promote_file, strerror(errno));
+ exit(1);
+ }
+
+ print_msg(_("server promoting\n"));
+}
+
+
/*
* utility routines
*/
pgpid_t pid;
pid = get_pgpid();
- if (pid != 0) /* 0 means no pid file */
+ /* Is there a pid file? */
+ if (pid != 0)
{
- if (pid < 0) /* standalone backend */
+ /* standalone backend? */
+ if (pid < 0)
{
pid = -pid;
if (postmaster_is_alive((pid_t) pid))
}
}
else
- /* postmaster */
+ /* must be a postmaster */
{
if (postmaster_is_alive((pid_t) pid))
{
}
}
printf(_("%s: no server running\n"), progname);
- exit(1);
+
+ /*
+ * The Linux Standard Base Core Specification 3.1 says this should return
+ * '3'
+ * http://refspecs.freestandards.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-ge
+ * neric/iniscrptact.html
+ */
+ exit(3);
}
}
else
{
- ret = find_other_exec(argv0, "postgres", PM_VERSIONSTR, cmdLine);
+ ret = find_other_exec(argv0, "postgres", PG_BACKEND_VERSIONSTR,
+ cmdLine);
if (ret != 0)
{
write_stderr(_("%s: could not find postgres program executable\n"), progname);
#ifdef __CYGWIN__
/* need to convert to windows path */
+#if CYGWIN_VERSION_DLL_MAJOR >= 1007
+ cygwin_conv_path(CCP_POSIX_TO_WIN_A, cmdLine, buf, sizeof(buf));
+#else
cygwin_conv_to_full_win32_path(cmdLine, buf);
+#endif
strcpy(cmdLine, buf);
#endif
if (registration)
{
- if (pg_strcasecmp(cmdLine + strlen(cmdLine) - 4, ".exe"))
+ if (pg_strcasecmp(cmdLine + strlen(cmdLine) - 4, ".exe") != 0)
{
/* If commandline does not end in .exe, append it */
strcat(cmdLine, ".exe");
strcat(cmdLine, "\"");
}
- if (pg_data)
+ if (pg_config)
{
strcat(cmdLine, " -D \"");
- strcat(cmdLine, pg_data);
+ strcat(cmdLine, pg_config);
strcat(cmdLine, "\"");
}
- if (do_wait)
+ if (registration && do_wait)
strcat(cmdLine, " -w");
+ if (registration && wait_seconds != DEFAULT_WAIT)
+ /* concatenate */
+ sprintf(cmdLine + strlen(cmdLine), " -t %d", wait_seconds);
+
+ if (registration && silent_mode)
+ strcat(cmdLine, " -s");
+
if (post_opts)
{
strcat(cmdLine, " ");
if ((hService = CreateService(hSCM, register_servicename, register_servicename,
SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
- SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
+ pgctl_start_type, SERVICE_ERROR_NORMAL,
pgwin32_CommandLine(true),
NULL, NULL, "RPCSS\0", register_username, register_password)) == NULL)
{
CloseServiceHandle(hSCM);
- write_stderr(_("%s: could not register service \"%s\": error code %d\n"), progname, register_servicename, (int) GetLastError());
+ write_stderr(_("%s: could not register service \"%s\": error code %lu\n"), progname, register_servicename, GetLastError());
exit(1);
}
CloseServiceHandle(hService);
if ((hService = OpenService(hSCM, register_servicename, DELETE)) == NULL)
{
CloseServiceHandle(hSCM);
- write_stderr(_("%s: could not open service \"%s\": error code %d\n"), progname, register_servicename, (int) GetLastError());
+ write_stderr(_("%s: could not open service \"%s\": error code %lu\n"), progname, register_servicename, GetLastError());
exit(1);
}
if (!DeleteService(hService))
{
CloseServiceHandle(hService);
CloseServiceHandle(hSCM);
- write_stderr(_("%s: could not unregister service \"%s\": error code %d\n"), progname, register_servicename, (int) GetLastError());
+ write_stderr(_("%s: could not unregister service \"%s\": error code %lu\n"), progname, register_servicename, GetLastError());
exit(1);
}
CloseServiceHandle(hService);
CloseServiceHandle(hSCM);
}
-
-static SERVICE_STATUS status;
-static SERVICE_STATUS_HANDLE hStatus = (SERVICE_STATUS_HANDLE) 0;
-static HANDLE shutdownHandles[2];
-static pid_t postmasterPID = -1;
-
-#define shutdownEvent shutdownHandles[0]
-#define postmasterProcess shutdownHandles[1]
-
static void
pgwin32_SetServiceStatus(DWORD currentState)
{
status.dwCurrentState = currentState;
- SetServiceStatus(hStatus, (LPSERVICE_STATUS) & status);
+ SetServiceStatus(hStatus, (LPSERVICE_STATUS) &status);
}
static void WINAPI
}
static void WINAPI
-pgwin32_ServiceMain(DWORD argc, LPTSTR * argv)
+pgwin32_ServiceMain(DWORD argc, LPTSTR *argv)
{
PROCESS_INFORMATION pi;
DWORD ret;
memset(&pi, 0, sizeof(pi));
+ read_post_opts();
+
/* Register the control request handler */
if ((hStatus = RegisterServiceCtrlHandler(register_servicename, pgwin32_ServiceHandler)) == (SERVICE_STATUS_HANDLE) 0)
return;
/* Start the postmaster */
pgwin32_SetServiceStatus(SERVICE_START_PENDING);
- if (!CreateRestrictedProcess(pgwin32_CommandLine(false), &pi))
+ if (!CreateRestrictedProcess(pgwin32_CommandLine(false), &pi, true))
{
pgwin32_SetServiceStatus(SERVICE_STOPPED);
return;
postmasterPID = pi.dwProcessId;
postmasterProcess = pi.hProcess;
CloseHandle(pi.hThread);
+
+ if (do_wait)
+ {
+ write_eventlog(EVENTLOG_INFORMATION_TYPE, _("Waiting for server startup...\n"));
+ if (test_postmaster_connection(true) != PQPING_OK)
+ {
+ write_eventlog(EVENTLOG_ERROR_TYPE, _("Timed out waiting for server startup\n"));
+ pgwin32_SetServiceStatus(SERVICE_STOPPED);
+ return;
+ }
+ write_eventlog(EVENTLOG_INFORMATION_TYPE, _("Server started and accepting connections\n"));
+ }
+
pgwin32_SetServiceStatus(SERVICE_RUNNING);
/* Wait for quit... */
ret = WaitForMultipleObjects(2, shutdownHandles, FALSE, INFINITE);
+
pgwin32_SetServiceStatus(SERVICE_STOP_PENDING);
switch (ret)
{
if (StartServiceCtrlDispatcher(st) == 0)
{
- write_stderr(_("%s: could not start service \"%s\": error code %d\n"), progname, register_servicename, (int) GetLastError());
+ write_stderr(_("%s: could not start service \"%s\": error code %lu\n"), progname, register_servicename, GetLastError());
exit(1);
}
}
* also load the couple of functions that *do* exist in minwg headers but not
* on NT4. That way, we don't break on NT4.
*/
-typedef BOOL(WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE);
-typedef BOOL(WINAPI * __IsProcessInJob) (HANDLE, HANDLE, PBOOL);
-typedef HANDLE(WINAPI * __CreateJobObject) (LPSECURITY_ATTRIBUTES, LPCTSTR);
-typedef BOOL(WINAPI * __SetInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, LPVOID, DWORD);
-typedef BOOL(WINAPI * __AssignProcessToJobObject) (HANDLE, HANDLE);
-typedef BOOL(WINAPI * __QueryInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, LPVOID, DWORD, LPDWORD);
-
-/* Windows API define missing from MingW headers */
+typedef BOOL (WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE);
+typedef BOOL (WINAPI * __IsProcessInJob) (HANDLE, HANDLE, PBOOL);
+typedef HANDLE (WINAPI * __CreateJobObject) (LPSECURITY_ATTRIBUTES, LPCTSTR);
+typedef BOOL (WINAPI * __SetInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, LPVOID, DWORD);
+typedef BOOL (WINAPI * __AssignProcessToJobObject) (HANDLE, HANDLE);
+typedef BOOL (WINAPI * __QueryInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, LPVOID, DWORD, LPDWORD);
+
+/* Windows API define missing from some versions of MingW headers */
+#ifndef DISABLE_MAX_PRIVILEGE
#define DISABLE_MAX_PRIVILEGE 0x1
+#endif
/*
* Create a restricted token, a job object sandbox, and execute the specified
* automatically destroyed when pg_ctl exits.
*/
static int
-CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION * processInfo)
+CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_service)
{
int r;
BOOL b;
* NT4 doesn't have CreateRestrictedToken, so just call ordinary
* CreateProcess
*/
- write_stderr("WARNING: cannot create restricted tokens on this platform\n");
+ write_stderr(_("%s: WARNING: cannot create restricted tokens on this platform\n"), progname);
if (Advapi32Handle != NULL)
FreeLibrary(Advapi32Handle);
return CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, processInfo);
/* Open the current token to use as a base for the restricted one */
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken))
{
- write_stderr("Failed to open process token: %lu\n", GetLastError());
+ write_stderr(_("%s: could not open process token: error code %lu\n"), progname, GetLastError());
return 0;
}
SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0,
0, &dropSids[1].Sid))
{
- write_stderr("Failed to allocate SIDs: %lu\n", GetLastError());
+ write_stderr(_("%s: could not allocate SIDs: error code %lu\n"), progname, GetLastError());
return 0;
}
if (!b)
{
- write_stderr("Failed to create restricted token: %lu\n", GetLastError());
+ write_stderr(_("%s: could not create restricted token: error code %lu\n"), progname, GetLastError());
return 0;
}
+#ifndef __CYGWIN__
+ AddUserToTokenDacl(restrictedToken);
+#endif
+
r = CreateProcessAsUser(restrictedToken, NULL, cmd, NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, NULL, &si, processInfo);
Kernel32Handle = LoadLibrary("KERNEL32.DLL");
* Log error if we can't get version, or if we're on WinXP/2003 or
* newer
*/
- write_stderr("WARNING: could not locate all job object functions in system API\n");
+ write_stderr(_("%s: WARNING: could not locate all job object functions in system API\n"), progname);
}
else
{
JOBOBJECT_BASIC_LIMIT_INFORMATION basicLimit;
JOBOBJECT_BASIC_UI_RESTRICTIONS uiRestrictions;
JOBOBJECT_SECURITY_LIMIT_INFORMATION securityLimit;
+ OSVERSIONINFO osv;
ZeroMemory(&basicLimit, sizeof(basicLimit));
ZeroMemory(&uiRestrictions, sizeof(uiRestrictions));
_SetInformationJobObject(job, JobObjectBasicLimitInformation, &basicLimit, sizeof(basicLimit));
uiRestrictions.UIRestrictionsClass = JOB_OBJECT_UILIMIT_DESKTOP | JOB_OBJECT_UILIMIT_DISPLAYSETTINGS |
- JOB_OBJECT_UILIMIT_EXITWINDOWS | JOB_OBJECT_UILIMIT_HANDLES | JOB_OBJECT_UILIMIT_READCLIPBOARD |
+ JOB_OBJECT_UILIMIT_EXITWINDOWS | JOB_OBJECT_UILIMIT_READCLIPBOARD |
JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS | JOB_OBJECT_UILIMIT_WRITECLIPBOARD;
+
+ if (as_service)
+ {
+ osv.dwOSVersionInfoSize = sizeof(osv);
+ if (!GetVersionEx(&osv) ||
+ osv.dwMajorVersion < 6 ||
+ (osv.dwMajorVersion == 6 && osv.dwMinorVersion == 0))
+ {
+ /*
+ * On Windows 7 (and presumably later),
+ * JOB_OBJECT_UILIMIT_HANDLES prevents us from
+ * starting as a service. So we only enable it on
+ * Vista and earlier (version <= 6.0)
+ */
+ uiRestrictions.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES;
+ }
+ }
_SetInformationJobObject(job, JobObjectBasicUIRestrictions, &uiRestrictions, sizeof(uiRestrictions));
securityLimit.SecurityLimitFlags = JOB_OBJECT_SECURITY_NO_ADMIN | JOB_OBJECT_SECURITY_ONLY_TOKEN;
}
}
+
CloseHandle(restrictedToken);
ResumeThread(processInfo->hThread);
static void
do_help(void)
{
- printf(_("%s is a utility to start, stop, restart, reload configuration files,\n"
- "report the status of a PostgreSQL server, or signal a PostgreSQL process.\n\n"), progname);
+ printf(_("%s is a utility to initialize, start, stop, or control a PostgreSQL server.\n\n"), progname);
printf(_("Usage:\n"));
- printf(_(" %s start [-w] [-D DATADIR] [-s] [-l FILENAME] [-o \"OPTIONS\"]\n"), progname);
- printf(_(" %s stop [-W] [-D DATADIR] [-s] [-m SHUTDOWN-MODE]\n"), progname);
- printf(_(" %s restart [-w] [-D DATADIR] [-s] [-m SHUTDOWN-MODE] [-o \"OPTIONS\"]\n"), progname);
+ printf(_(" %s init[db] [-D DATADIR] [-s] [-o \"OPTIONS\"]\n"), progname);
+ printf(_(" %s start [-w] [-t SECS] [-D DATADIR] [-s] [-l FILENAME] [-o \"OPTIONS\"]\n"), progname);
+ printf(_(" %s stop [-W] [-t SECS] [-D DATADIR] [-s] [-m SHUTDOWN-MODE]\n"), progname);
+ printf(_(" %s restart [-w] [-t SECS] [-D DATADIR] [-s] [-m SHUTDOWN-MODE]\n"
+ " [-o \"OPTIONS\"]\n"), progname);
printf(_(" %s reload [-D DATADIR] [-s]\n"), progname);
printf(_(" %s status [-D DATADIR]\n"), progname);
+ printf(_(" %s promote [-D DATADIR] [-s]\n"), progname);
printf(_(" %s kill SIGNALNAME PID\n"), progname);
#if defined(WIN32) || defined(__CYGWIN__)
printf(_(" %s register [-N SERVICENAME] [-U USERNAME] [-P PASSWORD] [-D DATADIR]\n"
- " [-w] [-o \"OPTIONS\"]\n"), progname);
+ " [-S START-TYPE] [-w] [-t SECS] [-o \"OPTIONS\"]\n"), progname);
printf(_(" %s unregister [-N SERVICENAME]\n"), progname);
#endif
printf(_("\nCommon options:\n"));
- printf(_(" -D, --pgdata DATADIR location of the database storage area\n"));
+ printf(_(" -D, --pgdata=DATADIR location of the database storage area\n"));
printf(_(" -s, --silent only print errors, no informational messages\n"));
+ printf(_(" -t, --timeout=SECS seconds to wait when using -w option\n"));
+ printf(_(" -V, --version output version information, then exit\n"));
printf(_(" -w wait until operation completes\n"));
printf(_(" -W do not wait until operation completes\n"));
- printf(_(" --help show this help, then exit\n"));
- printf(_(" --version output version information, then exit\n"));
+ printf(_(" -?, --help show this help, then exit\n"));
printf(_("(The default is to wait for shutdown, but not for start or restart.)\n\n"));
printf(_("If the -D option is omitted, the environment variable PGDATA is used.\n"));
printf(_("\nOptions for start or restart:\n"));
- printf(_(" -l, --log FILENAME write (or append) server log to FILENAME\n"));
- printf(_(" -o OPTIONS command line options to pass to postgres\n"
- " (PostgreSQL server executable)\n"));
- printf(_(" -p PATH-TO-POSTGRES normally not necessary\n"));
#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
printf(_(" -c, --core-files allow postgres to produce core files\n"));
#else
printf(_(" -c, --core-files not applicable on this platform\n"));
#endif
+ printf(_(" -l, --log=FILENAME write (or append) server log to FILENAME\n"));
+ printf(_(" -o OPTIONS command line options to pass to postgres\n"
+ " (PostgreSQL server executable) or initdb\n"));
+ printf(_(" -p PATH-TO-POSTGRES normally not necessary\n"));
printf(_("\nOptions for stop or restart:\n"));
- printf(_(" -m SHUTDOWN-MODE can be \"smart\", \"fast\", or \"immediate\"\n"));
+ printf(_(" -m, --mode=MODE MODE can be \"smart\", \"fast\", or \"immediate\"\n"));
printf(_("\nShutdown modes are:\n"));
printf(_(" smart quit after all clients have disconnected\n"));
printf(_(" immediate quit without complete shutdown; will lead to recovery on restart\n"));
printf(_("\nAllowed signal names for kill:\n"));
- printf(" HUP INT QUIT ABRT TERM USR1 USR2\n");
+ printf(" ABRT HUP INT QUIT TERM USR1 USR2\n");
#if defined(WIN32) || defined(__CYGWIN__)
printf(_("\nOptions for register and unregister:\n"));
printf(_(" -N SERVICENAME service name with which to register PostgreSQL server\n"));
printf(_(" -P PASSWORD password of account to register PostgreSQL server\n"));
printf(_(" -U USERNAME user name of account to register PostgreSQL server\n"));
+ printf(_(" -S START-TYPE service start type to register PostgreSQL server\n"));
+
+ printf(_("\nStart types are:\n"));
+ printf(_(" auto start service automatically during system startup (default)\n"));
+ printf(_(" demand start service on demand\n"));
#endif
printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
static void
set_sig(char *signame)
{
- if (!strcmp(signame, "HUP"))
+ if (strcmp(signame, "HUP") == 0)
sig = SIGHUP;
- else if (!strcmp(signame, "INT"))
+ else if (strcmp(signame, "INT") == 0)
sig = SIGINT;
- else if (!strcmp(signame, "QUIT"))
+ else if (strcmp(signame, "QUIT") == 0)
sig = SIGQUIT;
- else if (!strcmp(signame, "ABRT"))
+ else if (strcmp(signame, "ABRT") == 0)
sig = SIGABRT;
-
- /*
- * probably should NOT provide SIGKILL
- *
- * else if (!strcmp(signame,"KILL")) sig = SIGKILL;
- */
- else if (!strcmp(signame, "TERM"))
+#if 0
+ /* probably should NOT provide SIGKILL */
+ else if (strcmp(signame, "KILL") == 0)
+ sig = SIGKILL;
+#endif
+ else if (strcmp(signame, "TERM") == 0)
sig = SIGTERM;
- else if (!strcmp(signame, "USR1"))
+ else if (strcmp(signame, "USR1") == 0)
sig = SIGUSR1;
- else if (!strcmp(signame, "USR2"))
+ else if (strcmp(signame, "USR2") == 0)
sig = SIGUSR2;
else
{
do_advice();
exit(1);
}
+}
+
+#if defined(WIN32) || defined(__CYGWIN__)
+static void
+set_starttype(char *starttypeopt)
+{
+ if (strcmp(starttypeopt, "a") == 0 || strcmp(starttypeopt, "auto") == 0)
+ pgctl_start_type = SERVICE_AUTO_START;
+ else if (strcmp(starttypeopt, "d") == 0 || strcmp(starttypeopt, "demand") == 0)
+ pgctl_start_type = SERVICE_DEMAND_START;
+ else
+ {
+ write_stderr(_("%s: unrecognized start type \"%s\"\n"), progname, starttypeopt);
+ do_advice();
+ exit(1);
+ }
}
+#endif
+
+/*
+ * adjust_data_dir
+ *
+ * If a configuration-only directory was specified, find the real data dir.
+ */
+static void
+adjust_data_dir(void)
+{
+ char cmd[MAXPGPATH],
+ filename[MAXPGPATH],
+ *my_exec_path;
+ FILE *fd;
+
+ /* do nothing if we're working without knowledge of data dir */
+ if (pg_config == NULL)
+ return;
+
+ /* If there is no postgresql.conf, it can't be a config-only dir */
+ snprintf(filename, sizeof(filename), "%s/postgresql.conf", pg_config);
+ if ((fd = fopen(filename, "r")) == NULL)
+ return;
+ fclose(fd);
+
+ /* If PG_VERSION exists, it can't be a config-only dir */
+ snprintf(filename, sizeof(filename), "%s/PG_VERSION", pg_config);
+ if ((fd = fopen(filename, "r")) != NULL)
+ {
+ fclose(fd);
+ return;
+ }
+
+ /* Must be a configuration directory, so find the data directory */
+
+ /* we use a private my_exec_path to avoid interfering with later uses */
+ if (exec_path == NULL)
+ my_exec_path = find_other_exec_or_die(argv0, "postgres", PG_BACKEND_VERSIONSTR);
+ else
+ my_exec_path = xstrdup(exec_path);
+
+ snprintf(cmd, MAXPGPATH, SYSTEMQUOTE "\"%s\" %s%s -C data_directory" SYSTEMQUOTE,
+ my_exec_path, pgdata_opt ? pgdata_opt : "", post_opts ?
+ post_opts : "");
+
+ fd = popen(cmd, "r");
+ if (fd == NULL || fgets(filename, sizeof(filename), fd) == NULL)
+ {
+ write_stderr(_("%s: could not determine the data directory using command \"%s\"\n"), progname, cmd);
+ exit(1);
+ }
+ pclose(fd);
+ free(my_exec_path);
+
+ /* Remove trailing newline */
+ if (strchr(filename, '\n') != NULL)
+ *strchr(filename, '\n') = '\0';
+ free(pg_data);
+ pg_data = xstrdup(filename);
+ canonicalize_path(pg_data);
+}
int
{"mode", required_argument, NULL, 'm'},
{"pgdata", required_argument, NULL, 'D'},
{"silent", no_argument, NULL, 's'},
+ {"timeout", required_argument, NULL, 't'},
{"core-files", no_argument, NULL, 'c'},
{NULL, 0, NULL, 0}
};
#endif
progname = get_progname(argv[0]);
- set_pglocale_pgservice(argv[0], "pg_ctl");
+ set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_ctl"));
+ start_time = time(NULL);
/*
* save argv[0] so do_start() can look for the postmaster if necessary. we
*/
argv0 = argv[0];
- umask(077);
+ umask(S_IRWXG | S_IRWXO);
/* support --help and --version even if invoked as root */
if (argc > 1)
/* process command-line options */
while (optind < argc)
{
- while ((c = getopt_long(argc, argv, "cD:l:m:N:o:p:P:sU:wW", long_options, &option_index)) != -1)
+ while ((c = getopt_long(argc, argv, "cD:l:m:N:o:p:P:sS:t:U:wW", long_options, &option_index)) != -1)
{
switch (c)
{
post_opts = xstrdup(optarg);
break;
case 'p':
- postgres_path = xstrdup(optarg);
+ exec_path = xstrdup(optarg);
break;
case 'P':
register_password = xstrdup(optarg);
case 's':
silent_mode = true;
break;
+ case 'S':
+#if defined(WIN32) || defined(__CYGWIN__)
+ set_starttype(optarg);
+#else
+ write_stderr(_("%s: -S option not supported on this platform\n"),
+ progname);
+ exit(1);
+#endif
+ break;
+ case 't':
+ wait_seconds = atoi(optarg);
+ break;
case 'U':
if (strchr(optarg, '\\'))
register_username = xstrdup(optarg);
exit(1);
}
- if (strcmp(argv[optind], "start") == 0)
+ if (strcmp(argv[optind], "init") == 0
+ || strcmp(argv[optind], "initdb") == 0)
+ ctl_command = INIT_COMMAND;
+ else if (strcmp(argv[optind], "start") == 0)
ctl_command = START_COMMAND;
else if (strcmp(argv[optind], "stop") == 0)
ctl_command = STOP_COMMAND;
ctl_command = RELOAD_COMMAND;
else if (strcmp(argv[optind], "status") == 0)
ctl_command = STATUS_COMMAND;
+ else if (strcmp(argv[optind], "promote") == 0)
+ ctl_command = PROMOTE_COMMAND;
else if (strcmp(argv[optind], "kill") == 0)
{
if (argc - optind < 3)
}
/* Note we put any -D switch into the env var above */
- pg_data = getenv("PGDATA");
- if (pg_data)
+ pg_config = getenv("PGDATA");
+ if (pg_config)
{
- pg_data = xstrdup(pg_data);
- canonicalize_path(pg_data);
+ pg_config = xstrdup(pg_config);
+ canonicalize_path(pg_config);
+ pg_data = xstrdup(pg_config);
}
- if (pg_data == NULL &&
+ /* -D might point at config-only directory; if so find the real PGDATA */
+ adjust_data_dir();
+
+ /* Complain if -D needed and not provided */
+ if (pg_config == NULL &&
ctl_command != KILL_COMMAND && ctl_command != UNREGISTER_COMMAND)
{
- write_stderr(_("%s: no database directory specified "
- "and environment variable PGDATA unset\n"),
+ write_stderr(_("%s: no database directory specified and environment variable PGDATA unset\n"),
progname);
do_advice();
exit(1);
if (pg_data)
{
- snprintf(def_postopts_file, MAXPGPATH, "%s/postmaster.opts.default", pg_data);
snprintf(postopts_file, MAXPGPATH, "%s/postmaster.opts", pg_data);
snprintf(pid_file, MAXPGPATH, "%s/postmaster.pid", pg_data);
- snprintf(conf_file, MAXPGPATH, "%s/postgresql.conf", pg_data);
+ snprintf(backup_file, MAXPGPATH, "%s/backup_label", pg_data);
+ snprintf(recovery_file, MAXPGPATH, "%s/recovery.conf", pg_data);
+ snprintf(promote_file, MAXPGPATH, "%s/promote", pg_data);
}
switch (ctl_command)
{
+ case INIT_COMMAND:
+ do_init();
+ break;
case STATUS_COMMAND:
do_status();
break;
case RELOAD_COMMAND:
do_reload();
break;
+ case PROMOTE_COMMAND:
+ do_promote();
+ break;
case KILL_COMMAND:
do_kill(killproc);
break;