pg_ctl.c 46.9 KB
Newer Older
1 2 3 4
/*-------------------------------------------------------------------------
 *
 * pg_ctl --- start/stops/restarts the PostgreSQL server
 *
5
 * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
6
 *
7
 * $PostgreSQL: pgsql/src/bin/pg_ctl/pg_ctl.c,v 1.97 2008/04/23 13:44:59 mha Exp $
8 9 10 11
 *
 *-------------------------------------------------------------------------
 */

12 13 14 15 16 17 18 19
#ifdef WIN32
/*
 * 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
#endif

20 21 22
#include "postgres_fe.h"
#include "libpq-fe.h"

23
#include <locale.h>
24 25 26
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
27
#include <unistd.h>
28

29 30 31 32 33
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/time.h>
#include <sys/resource.h>
#endif

34 35
#include "libpq/pqsignal.h"
#include "getopt_long.h"
36
#include "miscadmin.h"
37

38
#if defined(__CYGWIN__)
39
#include <sys/cygwin.h>
40
#include <windows.h>
41 42
/* Cygwin defines WIN32 in windows.h, but we don't want it. */
#undef WIN32
43 44
#endif

45
#ifndef HAVE_INT_OPTRESET
46 47 48
int			optreset;
#endif

49 50
/* PID can be negative for standalone backend */
typedef long pgpid_t;
51 52 53 54 55 56 57


typedef enum
{
	SMART_MODE,
	FAST_MODE,
	IMMEDIATE_MODE
Bruce Momjian's avatar
Bruce Momjian committed
58
} ShutdownMode;
59 60 61 62 63 64 65 66 67 68


typedef enum
{
	NO_COMMAND = 0,
	START_COMMAND,
	STOP_COMMAND,
	RESTART_COMMAND,
	RELOAD_COMMAND,
	STATUS_COMMAND,
69 70 71 72
	KILL_COMMAND,
	REGISTER_COMMAND,
	UNREGISTER_COMMAND,
	RUN_AS_SERVICE_COMMAND
Bruce Momjian's avatar
Bruce Momjian committed
73
} CtlCommand;
74

75
#define DEFAULT_WAIT	60
76 77 78

static bool do_wait = false;
static bool wait_set = false;
79
static int	wait_seconds = DEFAULT_WAIT;
Neil Conway's avatar
Neil Conway committed
80
static bool silent_mode = false;
81
static ShutdownMode shutdown_mode = SMART_MODE;
Bruce Momjian's avatar
Bruce Momjian committed
82
static int	sig = SIGTERM;		/* default */
83 84
static CtlCommand ctl_command = NO_COMMAND;
static char *pg_data = NULL;
85
static char *pgdata_opt = NULL;
86 87 88 89
static char *post_opts = NULL;
static const char *progname;
static char *log_file = NULL;
static char *postgres_path = NULL;
90
static char *register_servicename = "PostgreSQL";		/* FIXME: + version ID? */
91 92
static char *register_username = NULL;
static char *register_password = NULL;
93
static char *argv0 = NULL;
94
static bool allow_core_files = false;
95

Bruce Momjian's avatar
Bruce Momjian committed
96 97
static void
write_stderr(const char *fmt,...)
98 99 100
/* This extension allows gcc to check the format string for consistency with
   the supplied arguments. */
__attribute__((format(printf, 1, 2)));
101
static void *pg_malloc(size_t size);
102 103 104 105 106
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);
Neil Conway's avatar
Neil Conway committed
107
static void do_start(void);
108 109 110 111
static void do_stop(void);
static void do_restart(void);
static void do_reload(void);
static void do_status(void);
112
static void do_kill(pgpid_t pid);
Neil Conway's avatar
Neil Conway committed
113
static void print_msg(const char *msg);
Bruce Momjian's avatar
Bruce Momjian committed
114

115
#if defined(WIN32) || defined(__CYGWIN__)
Bruce Momjian's avatar
Bruce Momjian committed
116 117
static bool pgwin32_IsInstalled(SC_HANDLE);
static char *pgwin32_CommandLine(bool);
Neil Conway's avatar
Neil Conway committed
118 119
static void pgwin32_doRegister(void);
static void pgwin32_doUnregister(void);
120 121
static void pgwin32_SetServiceStatus(DWORD);
static void WINAPI pgwin32_ServiceHandler(DWORD);
Bruce Momjian's avatar
Bruce Momjian committed
122
static void WINAPI pgwin32_ServiceMain(DWORD, LPTSTR *);
Neil Conway's avatar
Neil Conway committed
123
static void pgwin32_doRunAsService(void);
Bruce Momjian's avatar
Bruce Momjian committed
124
static int	CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION * processInfo);
125 126 127 128 129 130 131 132

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]
133
#endif
134

135
static pgpid_t get_pgpid(void);
Neil Conway's avatar
Neil Conway committed
136
static char **readfile(const char *path);
Bruce Momjian's avatar
Bruce Momjian committed
137
static int	start_postmaster(void);
138 139 140
static void read_post_opts(void);

static bool test_postmaster_connection(bool);
141
static bool postmaster_is_alive(pid_t pid);
142 143 144 145 146

static char def_postopts_file[MAXPGPATH];
static char postopts_file[MAXPGPATH];
static char pid_file[MAXPGPATH];
static char conf_file[MAXPGPATH];
147
static char backup_file[MAXPGPATH];
148

149 150 151 152
#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
static void unlimit_core_size(void);
#endif

153

154
#if defined(WIN32) || defined(__CYGWIN__)
155 156 157 158 159
static void
write_eventlog(int level, const char *line)
{
	static HANDLE evtHandle = INVALID_HANDLE_VALUE;

Bruce Momjian's avatar
Bruce Momjian committed
160 161 162 163 164
	if (evtHandle == INVALID_HANDLE_VALUE)
	{
		evtHandle = RegisterEventSource(NULL, "PostgreSQL");
		if (evtHandle == NULL)
		{
165 166 167 168 169 170 171 172
			evtHandle = INVALID_HANDLE_VALUE;
			return;
		}
	}

	ReportEvent(evtHandle,
				level,
				0,
Bruce Momjian's avatar
Bruce Momjian committed
173
				0,				/* All events are Id 0 */
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
				NULL,
				1,
				0,
				&line,
				NULL);
}
#endif

/*
 * Write errors to stderr (or by equal means when stderr is
 * not available).
 */
static void
write_stderr(const char *fmt,...)
{
Bruce Momjian's avatar
Bruce Momjian committed
189
	va_list		ap;
190 191

	va_start(ap, fmt);
192
#if !defined(WIN32) && !defined(__CYGWIN__)
193 194 195
	/* On Unix, we just fprintf to stderr */
	vfprintf(stderr, fmt, ap);
#else
Bruce Momjian's avatar
Bruce Momjian committed
196 197 198 199 200 201

	/*
	 * On Win32, we print to stderr if running on a console, or write to
	 * eventlog if running as a service
	 */
	if (!isatty(fileno(stderr)))	/* Running as a service */
202
	{
Bruce Momjian's avatar
Bruce Momjian committed
203
		char		errbuf[2048];		/* Arbitrary size? */
204 205 206 207 208

		vsnprintf(errbuf, sizeof(errbuf), fmt, ap);

		write_eventlog(EVENTLOG_ERROR_TYPE, errbuf);
	}
Bruce Momjian's avatar
Bruce Momjian committed
209
	else
210
		/* Not running as service, write to stderr */
211 212 213 214 215
		vfprintf(stderr, fmt, ap);
#endif
	va_end(ap);
}

216 217 218 219 220
/*
 * routines to check memory allocations and fail noisily.
 */

static void *
221
pg_malloc(size_t size)
222 223 224 225 226 227
{
	void	   *result;

	result = malloc(size);
	if (!result)
	{
228
		write_stderr(_("%s: out of memory\n"), progname);
229 230 231 232 233 234 235 236 237 238 239 240 241 242
		exit(1);
	}
	return result;
}


static char *
xstrdup(const char *s)
{
	char	   *result;

	result = strdup(s);
	if (!result)
	{
243
		write_stderr(_("%s: out of memory\n"), progname);
244 245 246 247 248
		exit(1);
	}
	return result;
}

Neil Conway's avatar
Neil Conway committed
249 250 251 252 253 254 255 256 257 258 259 260 261
/*
 * Given an already-localized string, print it to stdout unless the
 * user has specified that no messages should be printed.
 */
static void
print_msg(const char *msg)
{
	if (!silent_mode)
	{
		fputs(msg, stdout);
		fflush(stdout);
	}
}
262

263
static pgpid_t
264 265 266
get_pgpid(void)
{
	FILE	   *pidf;
267
	long		pid;
268 269 270 271 272 273 274 275 276

	pidf = fopen(pid_file, "r");
	if (pidf == NULL)
	{
		/* No pid file, not an error on startup */
		if (errno == ENOENT)
			return 0;
		else
		{
277
			write_stderr(_("%s: could not open PID file \"%s\": %s\n"),
Peter Eisentraut's avatar
Peter Eisentraut committed
278
						 progname, pid_file, strerror(errno));
279 280 281
			exit(1);
		}
	}
282 283 284 285 286 287
	if (fscanf(pidf, "%ld", &pid) != 1)
	{
		write_stderr(_("%s: invalid data in PID file \"%s\"\n"),
					 progname, pid_file);
		exit(1);
	}
288
	fclose(pidf);
289
	return (pgpid_t) pid;
290 291 292 293 294 295 296
}


/*
 * get the lines from a text file - return NULL if file can't be opened
 */
static char **
Neil Conway's avatar
Neil Conway committed
297
readfile(const char *path)
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
{
	FILE	   *infile;
	int			maxlength = 0,
				linelen = 0;
	int			nlines = 0;
	char	  **result;
	char	   *buffer;
	int			c;

	if ((infile = fopen(path, "r")) == NULL)
		return NULL;

	/* pass over the file twice - the first time to size the result */

	while ((c = fgetc(infile)) != EOF)
	{
		linelen++;
		if (c == '\n')
		{
			nlines++;
			if (linelen > maxlength)
				maxlength = linelen;
			linelen = 0;
		}
	}

	/* handle last line without a terminating newline (yuck) */
	if (linelen)
		nlines++;
	if (linelen > maxlength)
		maxlength = linelen;

	/* set up the result and the line buffer */
331 332
	result = (char **) pg_malloc((nlines + 1) * sizeof(char *));
	buffer = (char *) pg_malloc(maxlength + 1);
333 334 335 336 337 338 339 340

	/* now reprocess the file and store the lines */
	rewind(infile);
	nlines = 0;
	while (fgets(buffer, maxlength + 1, infile) != NULL)
		result[nlines++] = xstrdup(buffer);

	fclose(infile);
341
	free(buffer);
342 343 344 345 346 347 348 349 350 351 352 353 354 355
	result[nlines] = NULL;

	return result;
}



/*
 * start/test/stop routines
 */

static int
start_postmaster(void)
{
356
	char		cmd[MAXPGPATH];
Bruce Momjian's avatar
Bruce Momjian committed
357

358
#ifndef WIN32
Bruce Momjian's avatar
Bruce Momjian committed
359

360
	/*
361 362
	 * Since there might be quotes to handle here, it is easier simply to pass
	 * everything to a shell to process them.
363 364
	 */
	if (log_file != NULL)
365
		snprintf(cmd, MAXPGPATH, "%s\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1 &%s",
366 367
				 SYSTEMQUOTE, postgres_path, pgdata_opt, post_opts,
				 DEVNULL, log_file, SYSTEMQUOTE);
Bruce Momjian's avatar
Bruce Momjian committed
368
	else
369
		snprintf(cmd, MAXPGPATH, "%s\"%s\" %s%s < \"%s\" 2>&1 &%s",
370 371
				 SYSTEMQUOTE, postgres_path, pgdata_opt, post_opts,
				 DEVNULL, SYSTEMQUOTE);
372 373

	return system(cmd);
Bruce Momjian's avatar
Bruce Momjian committed
374 375 376 377 378 379 380 381 382 383 384
#else							/* WIN32 */

	/*
	 * On win32 we don't use system(). So we don't need to use & (which would
	 * be START /B on win32). However, we still call the shell (CMD.EXE) with
	 * it to handle redirection etc.
	 */
	PROCESS_INFORMATION pi;

	if (log_file != NULL)
		snprintf(cmd, MAXPGPATH, "CMD /C %s\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1%s",
385 386
				 SYSTEMQUOTE, postgres_path, pgdata_opt, post_opts,
				 DEVNULL, log_file, SYSTEMQUOTE);
Bruce Momjian's avatar
Bruce Momjian committed
387 388
	else
		snprintf(cmd, MAXPGPATH, "CMD /C %s\"%s\" %s%s < \"%s\" 2>&1%s",
Bruce Momjian's avatar
Bruce Momjian committed
389 390
				 SYSTEMQUOTE, postgres_path, pgdata_opt, post_opts,
				 DEVNULL, SYSTEMQUOTE);
391

Bruce Momjian's avatar
Bruce Momjian committed
392 393 394 395 396 397
	if (!CreateRestrictedProcess(cmd, &pi))
		return GetLastError();
	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);
	return 0;
#endif   /* WIN32 */
398 399 400 401
}



402 403 404 405 406
/*
 * 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!!
 */
407
static bool
408
test_postmaster_connection(bool do_checkpoint)
409 410 411 412 413
{
	PGconn	   *conn;
	bool		success = false;
	int			i;
	char		portstr[32];
Bruce Momjian's avatar
Bruce Momjian committed
414
	char	   *p;
415
	char	   *q;
Bruce Momjian's avatar
Bruce Momjian committed
416
	char		connstr[128];	/* Should be way more than enough! */
417 418 419

	*portstr = '\0';

420 421 422 423 424 425 426 427
	/*
	 * Look in post_opts for a -p switch.
	 *
	 * This parsing code is not amazingly bright; it could for instance
	 * get fooled if ' -p' occurs within a quoted argument value.  Given
	 * that few people pass complicated settings in post_opts, it's
	 * probably good enough.
	 */
428 429
	for (p = post_opts; *p;)
	{
430 431
		/* advance past whitespace */
		while (isspace((unsigned char) *p))
432
			p++;
Bruce Momjian's avatar
Bruce Momjian committed
433

434
		if (strncmp(p, "-p", 2) == 0)
435
		{
436 437
			p += 2;
			/* advance past any whitespace/quoting */
438
			while (isspace((unsigned char) *p) || *p == '\'' || *p == '"')
439
				p++;
440 441 442 443 444 445 446
			/* find end of value (not including any ending quote!) */
			q = p;
			while (*q &&
				   !(isspace((unsigned char) *q) || *q == '\'' || *q == '"'))
				q++;
			/* and save the argument value */
			strlcpy(portstr, p, Min((q - p) + 1, sizeof(portstr)));
447
			/* keep looking, maybe there is another -p */
448
			p = q;
449 450
		}
		/* Advance to next whitespace */
451
		while (*p && !isspace((unsigned char) *p))
452 453 454
			p++;
	}

455 456 457 458 459 460
	/*
	 * Search config file for a 'port' option.
	 *
	 * This parsing code isn't amazingly bright either, but it should be
	 * okay for valid port settings.
	 */
461 462 463 464 465 466 467
	if (!*portstr)
	{
		char	  **optlines;

		optlines = readfile(conf_file);
		if (optlines != NULL)
		{
Bruce Momjian's avatar
Bruce Momjian committed
468
			for (; *optlines != NULL; optlines++)
469 470 471
			{
				p = *optlines;

472
				while (isspace((unsigned char) *p))
473
					p++;
474
				if (strncmp(p, "port", 4) != 0)
475
					continue;
476
				p += 4;
477
				while (isspace((unsigned char) *p))
478 479 480 481
					p++;
				if (*p != '=')
					continue;
				p++;
482 483
				/* advance past any whitespace/quoting */
				while (isspace((unsigned char) *p) || *p == '\'' || *p == '"')
484
					p++;
485 486 487 488 489 490 491 492
				/* find end of value (not including any ending quote/comment!) */
				q = p;
				while (*q &&
					   !(isspace((unsigned char) *q) ||
						 *q == '\'' || *q == '"' || *q == '#'))
					q++;
				/* and save the argument value */
				strlcpy(portstr, p, Min((q - p) + 1, sizeof(portstr)));
493 494 495 496 497
				/* keep looking, maybe there is another */
			}
		}
	}

498
	/* Check environment */
499
	if (!*portstr && getenv("PGPORT") != NULL)
500
		strlcpy(portstr, getenv("PGPORT"), sizeof(portstr));
501

502
	/* Else use compiled-in default */
503 504 505
	if (!*portstr)
		snprintf(portstr, sizeof(portstr), "%d", DEF_PGPORT);

506 507 508 509 510 511
	/*
	 * We need to set a connect timeout otherwise on Windows the SCM will
	 * probably timeout first
	 */
	snprintf(connstr, sizeof(connstr),
			 "dbname=postgres port=%s connect_timeout=5", portstr);
512

513 514
	for (i = 0; i < wait_seconds; i++)
	{
515
		if ((conn = PQconnectdb(connstr)) != NULL &&
516
			(PQstatus(conn) == CONNECTION_OK ||
517
			 PQconnectionNeedsPassword(conn)))
518 519 520 521 522
		{
			PQfinish(conn);
			success = true;
			break;
		}
523 524
		else
		{
525
			PQfinish(conn);
526 527 528 529 530

#if defined(WIN32)
			if (do_checkpoint)
			{
				/*
Bruce Momjian's avatar
Bruce Momjian committed
531 532 533 534 535
				 * 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.
536 537 538
				 */
				status.dwWaitHint += 6000;
				status.dwCheckPoint++;
Bruce Momjian's avatar
Bruce Momjian committed
539
				SetServiceStatus(hStatus, (LPSERVICE_STATUS) & status);
540 541 542 543 544 545
			}

			else
#endif
				print_msg(".");

546 547
			pg_usleep(1000000); /* 1 sec */
		}
548 549 550 551 552 553
	}

	return success;
}


554
#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
Bruce Momjian's avatar
Bruce Momjian committed
555
static void
556 557 558
unlimit_core_size(void)
{
	struct rlimit lim;
Bruce Momjian's avatar
Bruce Momjian committed
559 560

	getrlimit(RLIMIT_CORE, &lim);
561 562
	if (lim.rlim_max == 0)
	{
Bruce Momjian's avatar
Bruce Momjian committed
563 564 565
		write_stderr(_("%s: cannot set core file size limit; disallowed by hard limit\n"),
					 progname);
		return;
566 567 568 569
	}
	else if (lim.rlim_max == RLIM_INFINITY || lim.rlim_cur < lim.rlim_max)
	{
		lim.rlim_cur = lim.rlim_max;
Bruce Momjian's avatar
Bruce Momjian committed
570 571
		setrlimit(RLIMIT_CORE, &lim);
	}
572 573 574
}
#endif

575
static void
576
read_post_opts(void)
577 578 579 580 581 582 583 584 585 586 587 588
{
	char	   *optline = NULL;

	if (post_opts == NULL)
	{
		char	  **optlines;
		int			len;

		optlines = readfile(ctl_command == RESTART_COMMAND ?
							postopts_file : def_postopts_file);
		if (optlines == NULL)
		{
589
			if (ctl_command == START_COMMAND || ctl_command == RUN_AS_SERVICE_COMMAND)
590 591 592
				post_opts = "";
			else
			{
Peter Eisentraut's avatar
Peter Eisentraut committed
593
				write_stderr(_("%s: could not read file \"%s\"\n"), progname, postopts_file);
594 595 596 597 598
				exit(1);
			}
		}
		else if (optlines[0] == NULL || optlines[1] != NULL)
		{
599
			write_stderr(_("%s: option file \"%s\" must have exactly one line\n"),
Bruce Momjian's avatar
Bruce Momjian committed
600 601
						 progname, ctl_command == RESTART_COMMAND ?
						 postopts_file : def_postopts_file);
602 603 604 605 606 607 608
			exit(1);
		}
		else
		{
			optline = optlines[0];
			len = strcspn(optline, "\r\n");
			optline[len] = '\0';
Bruce Momjian's avatar
Bruce Momjian committed
609

610 611 612
			if (ctl_command == RESTART_COMMAND)
			{
				char	   *arg1;
Bruce Momjian's avatar
Bruce Momjian committed
613

614
				arg1 = strchr(optline, *SYSTEMQUOTE);
615 616 617 618
				if (arg1 == NULL || arg1 == optline)
					post_opts = "";
				else
				{
Bruce Momjian's avatar
Bruce Momjian committed
619
					*(arg1 - 1) = '\0'; /* this should be a space */
620 621 622 623 624 625 626 627 628
					post_opts = arg1;
				}
				if (postgres_path != NULL)
					postgres_path = optline;
			}
			else
				post_opts = optline;
		}
	}
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
}

static void
do_start(void)
{
	pgpid_t		pid;
	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();
648

649 650
	/* No -D or -D already added during server start */
	if (ctl_command == RESTART_COMMAND || pgdata_opt == NULL)
Bruce Momjian's avatar
Bruce Momjian committed
651 652
		pgdata_opt = "";

653 654 655 656 657
	if (postgres_path == NULL)
	{
		char	   *postmaster_path;
		int			ret;

658
		postmaster_path = pg_malloc(MAXPGPATH);
659

660
		if ((ret = find_other_exec(argv0, "postgres", PG_BACKEND_VERSIONSTR,
661 662
								   postmaster_path)) < 0)
		{
663 664
			char		full_path[MAXPGPATH];

665
			if (find_my_exec(argv0, full_path) < 0)
666
				strlcpy(full_path, progname, sizeof(full_path));
667

668
			if (ret == -1)
669
				write_stderr(_("The program \"postgres\" is needed by %s "
670 671
							   "but was not found in the\n"
							   "same directory as \"%s\".\n"
672
							   "Check your installation.\n"),
673
							 progname, full_path);
674
			else
675
				write_stderr(_("The program \"postgres\" was found by \"%s\"\n"
676
							   "but was not the same version as %s.\n"
677
							   "Check your installation.\n"),
678
							 full_path, progname);
679 680 681 682 683
			exit(1);
		}
		postgres_path = postmaster_path;
	}

684 685 686 687 688
#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
	if (allow_core_files)
		unlimit_core_size();
#endif

Peter Eisentraut's avatar
Peter Eisentraut committed
689 690
	exitcode = start_postmaster();
	if (exitcode != 0)
691
	{
692
		write_stderr(_("%s: could not start server: exit code was %d\n"),
Peter Eisentraut's avatar
Peter Eisentraut committed
693
					 progname, exitcode);
694 695 696 697 698 699 700 701 702
		exit(1);
	}

	if (old_pid != 0)
	{
		pg_usleep(1000000);
		pid = get_pgpid();
		if (pid == old_pid)
		{
703
			write_stderr(_("%s: could not start server\n"
Peter Eisentraut's avatar
Peter Eisentraut committed
704
						   "Examine the log output.\n"),
705
						 progname);
706 707 708 709 710 711
			exit(1);
		}
	}

	if (do_wait)
	{
712
		print_msg(_("waiting for server to start..."));
713

714
		if (test_postmaster_connection(false) == false)
715
		{
716
			printf(_("could not start server\n"));
717 718
			exit(1);
		}
Neil Conway's avatar
Neil Conway committed
719
		else
720 721
		{
			print_msg(_(" done\n"));
722
			print_msg(_("server started\n"));
723
		}
724
	}
Neil Conway's avatar
Neil Conway committed
725
	else
726
		print_msg(_("server starting\n"));
727 728 729 730 731 732 733
}


static void
do_stop(void)
{
	int			cnt;
734
	pgpid_t		pid;
735
	struct stat	statbuf;
736 737 738 739 740

	pid = get_pgpid();

	if (pid == 0)				/* no pid file */
	{
Peter Eisentraut's avatar
Peter Eisentraut committed
741
		write_stderr(_("%s: PID file \"%s\" does not exist\n"), progname, pid_file);
742
		write_stderr(_("Is server running?\n"));
743 744 745 746 747
		exit(1);
	}
	else if (pid < 0)			/* standalone backend, not postmaster */
	{
		pid = -pid;
748 749
		write_stderr(_("%s: cannot stop server; "
					   "single-user server is running (PID: %ld)\n"),
750
					 progname, pid);
751 752 753
		exit(1);
	}

754
	if (kill((pid_t) pid, sig) != 0)
755
	{
Peter Eisentraut's avatar
Peter Eisentraut committed
756
		write_stderr(_("%s: could not send stop signal (PID: %ld): %s\n"), progname, pid,
Bruce Momjian's avatar
Bruce Momjian committed
757
					 strerror(errno));
758 759 760 761 762
		exit(1);
	}

	if (!do_wait)
	{
763
		print_msg(_("server shutting down\n"));
764 765 766 767
		return;
	}
	else
	{
768 769 770 771 772 773
		if ((shutdown_mode == SMART_MODE) && (stat(backup_file, &statbuf) == 0))
		{
			print_msg(_("WARNING: online backup mode is active; must be ended\n"
						"   with pg_stop_backup() for shutdown to complete\n\n"));
		}

774
		print_msg(_("waiting for server to shut down..."));
Bruce Momjian's avatar
Bruce Momjian committed
775

776 777 778 779
		for (cnt = 0; cnt < wait_seconds; cnt++)
		{
			if ((pid = get_pgpid()) != 0)
			{
Neil Conway's avatar
Neil Conway committed
780
				print_msg(".");
Bruce Momjian's avatar
Bruce Momjian committed
781
				pg_usleep(1000000);		/* 1 sec */
782 783 784 785
			}
			else
				break;
		}
Bruce Momjian's avatar
Bruce Momjian committed
786 787

		if (pid != 0)			/* pid file still exists */
788
		{
Neil Conway's avatar
Neil Conway committed
789
			print_msg(_(" failed\n"));
Bruce Momjian's avatar
Bruce Momjian committed
790

791
			write_stderr(_("%s: server does not shut down\n"), progname);
792 793
			exit(1);
		}
Neil Conway's avatar
Neil Conway committed
794
		print_msg(_(" done\n"));
795

796
		printf(_("server stopped\n"));
797 798 799 800 801 802 803 804 805 806 807 808
	}
}


/*
 *	restart/reload routines
 */

static void
do_restart(void)
{
	int			cnt;
809
	pgpid_t		pid;
810
	struct stat	statbuf;
811 812 813 814 815

	pid = get_pgpid();

	if (pid == 0)				/* no pid file */
	{
816 817
		write_stderr(_("%s: PID file \"%s\" does not exist\n"),
					 progname, pid_file);
818 819
		write_stderr(_("Is server running?\n"));
		write_stderr(_("starting server anyway\n"));
820 821 822 823 824 825
		do_start();
		return;
	}
	else if (pid < 0)			/* standalone backend, not postmaster */
	{
		pid = -pid;
826 827
		if (postmaster_is_alive((pid_t) pid))
		{
828 829
			write_stderr(_("%s: cannot restart server; "
						   "single-user server is running (PID: %ld)\n"),
830
						 progname, pid);
831
			write_stderr(_("Please terminate the single-user server and try again.\n"));
832 833
			exit(1);
		}
834 835
	}

836
	if (postmaster_is_alive((pid_t) pid))
837
	{
838 839 840 841 842 843
		if (kill((pid_t) pid, sig) != 0)
		{
			write_stderr(_("%s: could not send stop signal (PID: %ld): %s\n"), progname, pid,
						 strerror(errno));
			exit(1);
		}
844

845 846 847 848 849 850
		if ((shutdown_mode == SMART_MODE) && (stat(backup_file, &statbuf) == 0))
		{
			print_msg(_("WARNING: online backup mode is active; must be ended\n"
						"   with pg_stop_backup() for shutdown to complete\n\n"));
		}

851
		print_msg(_("waiting for server to shut down..."));
852

853
		/* always wait for restart */
854

855
		for (cnt = 0; cnt < wait_seconds; cnt++)
856
		{
857 858 859
			if ((pid = get_pgpid()) != 0)
			{
				print_msg(".");
860
				pg_usleep(1000000);		/* 1 sec */
861 862 863
			}
			else
				break;
864 865
		}

866
		if (pid != 0)			/* pid file still exists */
867 868
		{
			print_msg(_(" failed\n"));
869

870
			write_stderr(_("%s: server does not shut down\n"), progname);
871 872 873 874
			exit(1);
		}

		print_msg(_(" done\n"));
875
		printf(_("server stopped\n"));
876 877 878
	}
	else
	{
879
		write_stderr(_("%s: old server process (PID: %ld) seems to be gone\n"),
880
					 progname, pid);
881
		write_stderr(_("starting server anyway\n"));
882 883 884 885 886 887 888 889 890
	}

	do_start();
}


static void
do_reload(void)
{
891
	pgpid_t		pid;
892 893 894 895

	pid = get_pgpid();
	if (pid == 0)				/* no pid file */
	{
Peter Eisentraut's avatar
Peter Eisentraut committed
896
		write_stderr(_("%s: PID file \"%s\" does not exist\n"), progname, pid_file);
897
		write_stderr(_("Is server running?\n"));
898 899 900 901 902
		exit(1);
	}
	else if (pid < 0)			/* standalone backend, not postmaster */
	{
		pid = -pid;
903 904
		write_stderr(_("%s: cannot reload server; "
					   "single-user server is running (PID: %ld)\n"),
905
					 progname, pid);
906
		write_stderr(_("Please terminate the single-user server and try again.\n"));
907 908 909
		exit(1);
	}

910
	if (kill((pid_t) pid, sig) != 0)
911
	{
912 913
		write_stderr(_("%s: could not send reload signal (PID: %ld): %s\n"),
					 progname, pid, strerror(errno));
914 915 916
		exit(1);
	}

917
	print_msg(_("server signaled\n"));
918 919 920 921 922 923
}

/*
 *	utility routines
 */

924 925 926 927 928 929
static bool
postmaster_is_alive(pid_t pid)
{
	/*
	 * Test to see if the process is still there.  Note that we do not
	 * consider an EPERM failure to mean that the process is still there;
930 931 932
	 * EPERM must mean that the given PID belongs to some other userid, and
	 * considering the permissions on $PGDATA, that means it's not the
	 * postmaster we are after.
933 934
	 *
	 * Don't believe that our own PID or parent shell's PID is the postmaster,
935
	 * either.	(Windows hasn't got getppid(), though.)
936 937 938 939 940 941 942 943 944 945 946 947
	 */
	if (pid == getpid())
		return false;
#ifndef WIN32
	if (pid == getppid())
		return false;
#endif
	if (kill(pid, 0) == 0)
		return true;
	return false;
}

948 949 950
static void
do_status(void)
{
951
	pgpid_t		pid;
952 953

	pid = get_pgpid();
954
	if (pid != 0)				/* 0 means no pid file */
955
	{
956 957 958 959 960
		if (pid < 0)			/* standalone backend */
		{
			pid = -pid;
			if (postmaster_is_alive((pid_t) pid))
			{
961
				printf(_("%s: single-user server is running (PID: %ld)\n"),
962 963 964 965
					   progname, pid);
				return;
			}
		}
966 967
		else
			/* postmaster */
968 969 970 971
		{
			if (postmaster_is_alive((pid_t) pid))
			{
				char	  **optlines;
972

973
				printf(_("%s: server is running (PID: %ld)\n"),
974
					   progname, pid);
975

976 977 978 979 980 981 982
				optlines = readfile(postopts_file);
				if (optlines != NULL)
					for (; *optlines != NULL; optlines++)
						fputs(*optlines, stdout);
				return;
			}
		}
983
	}
984
	printf(_("%s: no server running\n"), progname);
985
	exit(1);
986 987 988 989 990
}



static void
991
do_kill(pgpid_t pid)
992
{
993
	if (kill((pid_t) pid, sig) != 0)
994
	{
995 996
		write_stderr(_("%s: could not send signal %d (PID: %ld): %s\n"),
					 progname, sig, pid, strerror(errno));
997 998 999 1000
		exit(1);
	}
}

1001
#if defined(WIN32) || defined(__CYGWIN__)
1002

Bruce Momjian's avatar
Bruce Momjian committed
1003 1004
static bool
pgwin32_IsInstalled(SC_HANDLE hSCM)
1005
{
Bruce Momjian's avatar
Bruce Momjian committed
1006 1007 1008
	SC_HANDLE	hService = OpenService(hSCM, register_servicename, SERVICE_QUERY_CONFIG);
	bool		bResult = (hService != NULL);

1009 1010 1011 1012 1013
	if (bResult)
		CloseServiceHandle(hService);
	return bResult;
}

Bruce Momjian's avatar
Bruce Momjian committed
1014 1015
static char *
pgwin32_CommandLine(bool registration)
1016 1017
{
	static char cmdLine[MAXPGPATH];
Bruce Momjian's avatar
Bruce Momjian committed
1018
	int			ret;
1019

1020 1021 1022
#ifdef __CYGWIN__
	char		buf[MAXPGPATH];
#endif
Bruce Momjian's avatar
Bruce Momjian committed
1023

1024
	if (registration)
1025
	{
1026
		ret = find_my_exec(argv0, cmdLine);
1027 1028 1029 1030 1031 1032
		if (ret != 0)
		{
			write_stderr(_("%s: could not find own program executable\n"), progname);
			exit(1);
		}
	}
1033 1034
	else
	{
1035 1036
		ret = find_other_exec(argv0, "postgres", PG_BACKEND_VERSIONSTR,
							  cmdLine);
1037 1038
		if (ret != 0)
		{
1039
			write_stderr(_("%s: could not find postgres program executable\n"), progname);
1040 1041
			exit(1);
		}
1042 1043
	}

1044 1045 1046 1047 1048 1049
#ifdef __CYGWIN__
	/* need to convert to windows path */
	cygwin_conv_to_full_win32_path(cmdLine, buf);
	strcpy(cmdLine, buf);
#endif

1050 1051
	if (registration)
	{
1052
		if (pg_strcasecmp(cmdLine + strlen(cmdLine) - 4, ".exe"))
1053 1054
		{
			/* If commandline does not end in .exe, append it */
Bruce Momjian's avatar
Bruce Momjian committed
1055
			strcat(cmdLine, ".exe");
1056
		}
Bruce Momjian's avatar
Bruce Momjian committed
1057 1058 1059
		strcat(cmdLine, " runservice -N \"");
		strcat(cmdLine, register_servicename);
		strcat(cmdLine, "\"");
1060 1061 1062 1063
	}

	if (pg_data)
	{
Bruce Momjian's avatar
Bruce Momjian committed
1064 1065 1066
		strcat(cmdLine, " -D \"");
		strcat(cmdLine, pg_data);
		strcat(cmdLine, "\"");
1067 1068
	}

1069
	if (registration && do_wait)
1070
		strcat(cmdLine, " -w");
Bruce Momjian's avatar
Bruce Momjian committed
1071

1072 1073 1074 1075
	if (registration && wait_seconds != DEFAULT_WAIT)
		/* concatenate */
		sprintf(cmdLine + strlen(cmdLine), " -t %d", wait_seconds);

1076 1077
	if (post_opts)
	{
Bruce Momjian's avatar
Bruce Momjian committed
1078
		strcat(cmdLine, " ");
1079
		if (registration)
Bruce Momjian's avatar
Bruce Momjian committed
1080 1081
			strcat(cmdLine, " -o \"");
		strcat(cmdLine, post_opts);
1082
		if (registration)
Bruce Momjian's avatar
Bruce Momjian committed
1083
			strcat(cmdLine, "\"");
1084 1085 1086 1087 1088 1089
	}

	return cmdLine;
}

static void
Neil Conway's avatar
Neil Conway committed
1090
pgwin32_doRegister(void)
1091
{
Bruce Momjian's avatar
Bruce Momjian committed
1092 1093 1094
	SC_HANDLE	hService;
	SC_HANDLE	hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

1095 1096
	if (hSCM == NULL)
	{
Peter Eisentraut's avatar
Peter Eisentraut committed
1097
		write_stderr(_("%s: could not open service manager\n"), progname);
1098 1099 1100 1101 1102
		exit(1);
	}
	if (pgwin32_IsInstalled(hSCM))
	{
		CloseServiceHandle(hSCM);
Peter Eisentraut's avatar
Peter Eisentraut committed
1103
		write_stderr(_("%s: service \"%s\" already registered\n"), progname, register_servicename);
1104 1105 1106 1107
		exit(1);
	}

	if ((hService = CreateService(hSCM, register_servicename, register_servicename,
1108 1109
							   SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
								  SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
1110
								  pgwin32_CommandLine(true),
1111
	   NULL, NULL, "RPCSS\0", register_username, register_password)) == NULL)
1112 1113
	{
		CloseServiceHandle(hSCM);
1114
		write_stderr(_("%s: could not register service \"%s\": error code %d\n"), progname, register_servicename, (int) GetLastError());
1115 1116 1117 1118 1119 1120 1121
		exit(1);
	}
	CloseServiceHandle(hService);
	CloseServiceHandle(hSCM);
}

static void
Neil Conway's avatar
Neil Conway committed
1122
pgwin32_doUnregister(void)
1123
{
Bruce Momjian's avatar
Bruce Momjian committed
1124 1125 1126
	SC_HANDLE	hService;
	SC_HANDLE	hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

1127 1128
	if (hSCM == NULL)
	{
Peter Eisentraut's avatar
Peter Eisentraut committed
1129
		write_stderr(_("%s: could not open service manager\n"), progname);
1130 1131 1132 1133 1134
		exit(1);
	}
	if (!pgwin32_IsInstalled(hSCM))
	{
		CloseServiceHandle(hSCM);
Peter Eisentraut's avatar
Peter Eisentraut committed
1135
		write_stderr(_("%s: service \"%s\" not registered\n"), progname, register_servicename);
1136 1137 1138 1139 1140 1141
		exit(1);
	}

	if ((hService = OpenService(hSCM, register_servicename, DELETE)) == NULL)
	{
		CloseServiceHandle(hSCM);
1142
		write_stderr(_("%s: could not open service \"%s\": error code %d\n"), progname, register_servicename, (int) GetLastError());
1143 1144
		exit(1);
	}
Bruce Momjian's avatar
Bruce Momjian committed
1145 1146
	if (!DeleteService(hService))
	{
1147 1148
		CloseServiceHandle(hService);
		CloseServiceHandle(hSCM);
1149
		write_stderr(_("%s: could not unregister service \"%s\": error code %d\n"), progname, register_servicename, (int) GetLastError());
1150 1151 1152 1153 1154
		exit(1);
	}
	CloseServiceHandle(hService);
	CloseServiceHandle(hSCM);
}
1155

Bruce Momjian's avatar
Bruce Momjian committed
1156 1157
static void
pgwin32_SetServiceStatus(DWORD currentState)
1158 1159
{
	status.dwCurrentState = currentState;
Bruce Momjian's avatar
Bruce Momjian committed
1160
	SetServiceStatus(hStatus, (LPSERVICE_STATUS) & status);
1161 1162
}

Bruce Momjian's avatar
Bruce Momjian committed
1163 1164
static void WINAPI
pgwin32_ServiceHandler(DWORD request)
1165 1166 1167 1168 1169
{
	switch (request)
	{
		case SERVICE_CONTROL_STOP:
		case SERVICE_CONTROL_SHUTDOWN:
Bruce Momjian's avatar
Bruce Momjian committed
1170 1171

			/*
1172 1173 1174
			 * We only need a short wait hint here as it just needs to wait
			 * for the next checkpoint. They occur every 5 seconds during
			 * shutdown
1175
			 */
Bruce Momjian's avatar
Bruce Momjian committed
1176
			status.dwWaitHint = 10000;
1177 1178 1179 1180 1181 1182
			pgwin32_SetServiceStatus(SERVICE_STOP_PENDING);
			SetEvent(shutdownEvent);
			return;

		case SERVICE_CONTROL_PAUSE:
			/* Win32 config reloading */
1183
			status.dwWaitHint = 5000;
Bruce Momjian's avatar
Bruce Momjian committed
1184
			kill(postmasterPID, SIGHUP);
1185 1186
			return;

Bruce Momjian's avatar
Bruce Momjian committed
1187
			/* FIXME: These could be used to replace other signals etc */
1188 1189 1190 1191 1192 1193 1194
		case SERVICE_CONTROL_CONTINUE:
		case SERVICE_CONTROL_INTERROGATE:
		default:
			break;
	}
}

Bruce Momjian's avatar
Bruce Momjian committed
1195 1196
static void WINAPI
pgwin32_ServiceMain(DWORD argc, LPTSTR * argv)
1197 1198
{
	PROCESS_INFORMATION pi;
Bruce Momjian's avatar
Bruce Momjian committed
1199
	DWORD		ret;
1200
	DWORD		check_point_start;
1201 1202

	/* Initialize variables */
Bruce Momjian's avatar
Bruce Momjian committed
1203 1204 1205 1206 1207 1208
	status.dwWin32ExitCode = S_OK;
	status.dwCheckPoint = 0;
	status.dwWaitHint = 60000;
	status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
	status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PAUSE_CONTINUE;
	status.dwServiceSpecificExitCode = 0;
1209 1210
	status.dwCurrentState = SERVICE_START_PENDING;

Bruce Momjian's avatar
Bruce Momjian committed
1211
	memset(&pi, 0, sizeof(pi));
1212

Bruce Momjian's avatar
Bruce Momjian committed
1213
	read_post_opts();
1214

1215
	/* Register the control request handler */
Bruce Momjian's avatar
Bruce Momjian committed
1216
	if ((hStatus = RegisterServiceCtrlHandler(register_servicename, pgwin32_ServiceHandler)) == (SERVICE_STATUS_HANDLE) 0)
1217 1218
		return;

Bruce Momjian's avatar
Bruce Momjian committed
1219
	if ((shutdownEvent = CreateEvent(NULL, true, false, NULL)) == NULL)
1220 1221 1222 1223
		return;

	/* Start the postmaster */
	pgwin32_SetServiceStatus(SERVICE_START_PENDING);
1224
	if (!CreateRestrictedProcess(pgwin32_CommandLine(false), &pi))
1225 1226 1227 1228
	{
		pgwin32_SetServiceStatus(SERVICE_STOPPED);
		return;
	}
Bruce Momjian's avatar
Bruce Momjian committed
1229 1230
	postmasterPID = pi.dwProcessId;
	postmasterProcess = pi.hProcess;
1231
	CloseHandle(pi.hThread);
1232 1233 1234 1235 1236 1237

	if (do_wait)
	{
		write_eventlog(EVENTLOG_INFORMATION_TYPE, _("Waiting for server startup...\n"));
		if (test_postmaster_connection(true) == false)
		{
Bruce Momjian's avatar
Bruce Momjian committed
1238 1239
			write_eventlog(EVENTLOG_INFORMATION_TYPE, _("Timed out waiting for server startup\n"));
			pgwin32_SetServiceStatus(SERVICE_STOPPED);
1240 1241 1242 1243 1244
			return;
		}
		write_eventlog(EVENTLOG_INFORMATION_TYPE, _("Server started and accepting connections\n"));
	}

Bruce Momjian's avatar
Bruce Momjian committed
1245 1246 1247 1248 1249
	/*
	 * Save the checkpoint value as it might have been incremented in
	 * test_postmaster_connection
	 */
	check_point_start = status.dwCheckPoint;
1250

1251 1252 1253
	pgwin32_SetServiceStatus(SERVICE_RUNNING);

	/* Wait for quit... */
Bruce Momjian's avatar
Bruce Momjian committed
1254
	ret = WaitForMultipleObjects(2, shutdownHandles, FALSE, INFINITE);
1255

1256 1257 1258
	pgwin32_SetServiceStatus(SERVICE_STOP_PENDING);
	switch (ret)
	{
Bruce Momjian's avatar
Bruce Momjian committed
1259 1260 1261 1262 1263 1264
		case WAIT_OBJECT_0:		/* shutdown event */
			kill(postmasterPID, SIGINT);

			/*
			 * Increment the checkpoint and try again Abort after 12
			 * checkpoints as the postmaster has probably hung
1265
			 */
Bruce Momjian's avatar
Bruce Momjian committed
1266
			while (WaitForSingleObject(postmasterProcess, 5000) == WAIT_TIMEOUT && status.dwCheckPoint < 12)
1267
				status.dwCheckPoint++;
1268 1269
			break;

Bruce Momjian's avatar
Bruce Momjian committed
1270
		case (WAIT_OBJECT_0 + 1):		/* postmaster went down */
1271 1272 1273
			break;

		default:
1274 1275
			/* shouldn't get here? */
			break;
1276 1277 1278 1279 1280 1281 1282 1283
	}

	CloseHandle(shutdownEvent);
	CloseHandle(postmasterProcess);

	pgwin32_SetServiceStatus(SERVICE_STOPPED);
}

Bruce Momjian's avatar
Bruce Momjian committed
1284
static void
Neil Conway's avatar
Neil Conway committed
1285
pgwin32_doRunAsService(void)
1286
{
Bruce Momjian's avatar
Bruce Momjian committed
1287 1288 1289
	SERVICE_TABLE_ENTRY st[] = {{register_servicename, pgwin32_ServiceMain},
	{NULL, NULL}};

Tom Lane's avatar
Tom Lane committed
1290
	if (StartServiceCtrlDispatcher(st) == 0)
1291 1292 1293 1294
	{
		write_stderr(_("%s: could not start service \"%s\": error code %d\n"), progname, register_servicename, (int) GetLastError());
		exit(1);
	}
1295
}
1296 1297 1298 1299 1300 1301 1302 1303


/*
 * Mingw headers are incomplete, and so are the libraries. So we have to load
 * a whole lot of API functions dynamically. Since we have to do this anyway,
 * also load the couple of functions that *do* exist in minwg headers but not
 * on NT4. That way, we don't break on NT4.
 */
Bruce Momjian's avatar
Bruce Momjian committed
1304 1305 1306 1307 1308 1309
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);
1310 1311

/* Windows API define missing from MingW headers */
Bruce Momjian's avatar
Bruce Momjian committed
1312
#define DISABLE_MAX_PRIVILEGE	0x1
1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326

/*
 * Create a restricted token, a job object sandbox, and execute the specified
 * process with it.
 *
 * Returns 0 on success, non-zero on failure, same as CreateProcess().
 *
 * On NT4, or any other system not containing the required functions, will
 * launch the process under the current token without doing any modifications.
 *
 * NOTE! Job object will only work when running as a service, because it's
 * automatically destroyed when pg_ctl exits.
 */
static int
Bruce Momjian's avatar
Bruce Momjian committed
1327
CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION * processInfo)
1328
{
Bruce Momjian's avatar
Bruce Momjian committed
1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361
	int			r;
	BOOL		b;
	STARTUPINFO si;
	HANDLE		origToken;
	HANDLE		restrictedToken;
	SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
	SID_AND_ATTRIBUTES dropSids[2];

	/* Functions loaded dynamically */
	__CreateRestrictedToken _CreateRestrictedToken = NULL;
	__IsProcessInJob _IsProcessInJob = NULL;
	__CreateJobObject _CreateJobObject = NULL;
	__SetInformationJobObject _SetInformationJobObject = NULL;
	__AssignProcessToJobObject _AssignProcessToJobObject = NULL;
	__QueryInformationJobObject _QueryInformationJobObject = NULL;
	HANDLE		Kernel32Handle;
	HANDLE		Advapi32Handle;

	ZeroMemory(&si, sizeof(si));
	si.cb = sizeof(si);

	Advapi32Handle = LoadLibrary("ADVAPI32.DLL");
	if (Advapi32Handle != NULL)
	{
		_CreateRestrictedToken = (__CreateRestrictedToken) GetProcAddress(Advapi32Handle, "CreateRestrictedToken");
	}

	if (_CreateRestrictedToken == NULL)
	{
		/*
		 * NT4 doesn't have CreateRestrictedToken, so just call ordinary
		 * CreateProcess
		 */
1362
		write_stderr("WARNING: cannot create restricted tokens on this platform\n");
Bruce Momjian's avatar
Bruce Momjian committed
1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425
		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());
		return 0;
	}

	/* Allocate list of SIDs to remove */
	ZeroMemory(&dropSids, sizeof(dropSids));
	if (!AllocateAndInitializeSid(&NtAuthority, 2,
		 SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0,
								  0, &dropSids[0].Sid) ||
		!AllocateAndInitializeSid(&NtAuthority, 2,
	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());
		return 0;
	}

	b = _CreateRestrictedToken(origToken,
							   DISABLE_MAX_PRIVILEGE,
							   sizeof(dropSids) / sizeof(dropSids[0]),
							   dropSids,
							   0, NULL,
							   0, NULL,
							   &restrictedToken);

	FreeSid(dropSids[1].Sid);
	FreeSid(dropSids[0].Sid);
	CloseHandle(origToken);
	FreeLibrary(Advapi32Handle);

	if (!b)
	{
		write_stderr("Failed to create restricted token: %lu\n", GetLastError());
		return 0;
	}

	r = CreateProcessAsUser(restrictedToken, NULL, cmd, NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, NULL, &si, processInfo);

	Kernel32Handle = LoadLibrary("KERNEL32.DLL");
	if (Kernel32Handle != NULL)
	{
		_IsProcessInJob = (__IsProcessInJob) GetProcAddress(Kernel32Handle, "IsProcessInJob");
		_CreateJobObject = (__CreateJobObject) GetProcAddress(Kernel32Handle, "CreateJobObjectA");
		_SetInformationJobObject = (__SetInformationJobObject) GetProcAddress(Kernel32Handle, "SetInformationJobObject");
		_AssignProcessToJobObject = (__AssignProcessToJobObject) GetProcAddress(Kernel32Handle, "AssignProcessToJobObject");
		_QueryInformationJobObject = (__QueryInformationJobObject) GetProcAddress(Kernel32Handle, "QueryInformationJobObject");
	}

	/* Verify that we found all functions */
	if (_IsProcessInJob == NULL || _CreateJobObject == NULL || _SetInformationJobObject == NULL || _AssignProcessToJobObject == NULL || _QueryInformationJobObject == NULL)
	{
		/*
		 * IsProcessInJob() is not available on < WinXP, so there is no need
		 * to log the error every time in that case
		 */
1426 1427 1428
		OSVERSIONINFO osv;

		osv.dwOSVersionInfoSize = sizeof(osv);
Bruce Momjian's avatar
Bruce Momjian committed
1429 1430 1431 1432 1433 1434 1435 1436
		if (!GetVersionEx(&osv) ||		/* could not get version */
			(osv.dwMajorVersion == 5 && osv.dwMinorVersion > 0) ||		/* 5.1=xp, 5.2=2003, etc */
			osv.dwMajorVersion > 5)		/* anything newer should have the API */

			/*
			 * Log error if we can't get version, or if we're on WinXP/2003 or
			 * newer
			 */
1437
			write_stderr("WARNING: could not locate all job object functions in system API\n");
Bruce Momjian's avatar
Bruce Momjian committed
1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486
	}
	else
	{
		BOOL		inJob;

		if (_IsProcessInJob(processInfo->hProcess, NULL, &inJob))
		{
			if (!inJob)
			{
				/*
				 * Job objects are working, and the new process isn't in one,
				 * so we can create one safely. If any problems show up when
				 * setting it, we're going to ignore them.
				 */
				HANDLE		job;
				char		jobname[128];

				sprintf(jobname, "PostgreSQL_%lu", processInfo->dwProcessId);

				job = _CreateJobObject(NULL, jobname);
				if (job)
				{
					JOBOBJECT_BASIC_LIMIT_INFORMATION basicLimit;
					JOBOBJECT_BASIC_UI_RESTRICTIONS uiRestrictions;
					JOBOBJECT_SECURITY_LIMIT_INFORMATION securityLimit;

					ZeroMemory(&basicLimit, sizeof(basicLimit));
					ZeroMemory(&uiRestrictions, sizeof(uiRestrictions));
					ZeroMemory(&securityLimit, sizeof(securityLimit));

					basicLimit.LimitFlags = JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION | JOB_OBJECT_LIMIT_PRIORITY_CLASS;
					basicLimit.PriorityClass = NORMAL_PRIORITY_CLASS;
					_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_SYSTEMPARAMETERS | JOB_OBJECT_UILIMIT_WRITECLIPBOARD;
					_SetInformationJobObject(job, JobObjectBasicUIRestrictions, &uiRestrictions, sizeof(uiRestrictions));

					securityLimit.SecurityLimitFlags = JOB_OBJECT_SECURITY_NO_ADMIN | JOB_OBJECT_SECURITY_ONLY_TOKEN;
					securityLimit.JobToken = restrictedToken;
					_SetInformationJobObject(job, JobObjectSecurityLimitInformation, &securityLimit, sizeof(securityLimit));

					_AssignProcessToJobObject(job, processInfo->hProcess);
				}
			}
		}
	}

1487
#ifndef __CYGWIN__
1488
    AddUserToDacl(processInfo->hProcess);
1489
#endif
1490
    
Bruce Momjian's avatar
Bruce Momjian committed
1491 1492 1493 1494 1495 1496 1497
	CloseHandle(restrictedToken);

	ResumeThread(processInfo->hThread);

	FreeLibrary(Kernel32Handle);

	/*
1498 1499 1500
	 * We intentionally don't close the job object handle, because we want the
	 * object to live on until pg_ctl shuts down.
	 */
Bruce Momjian's avatar
Bruce Momjian committed
1501
	return r;
1502
}
1503 1504
#endif

1505 1506 1507
static void
do_advice(void)
{
1508
	write_stderr(_("Try \"%s --help\" for more information.\n"), progname);
1509 1510 1511 1512 1513 1514 1515
}



static void
do_help(void)
{
1516
	printf(_("%s is a utility to start, stop, restart, reload configuration files,\n"
1517
			 "report the status of a PostgreSQL server, or signal a PostgreSQL process.\n\n"), progname);
1518
	printf(_("Usage:\n"));
1519
	printf(_("  %s start   [-w] [-t SECS] [-D DATADIR] [-s] [-l FILENAME] [-o \"OPTIONS\"]\n"), progname);
1520
	printf(_("  %s stop    [-W] [-t SECS] [-D DATADIR] [-s] [-m SHUTDOWN-MODE]\n"), progname);
1521 1522
	printf(_("  %s restart [-w] [-t SECS] [-D DATADIR] [-s] [-m SHUTDOWN-MODE]\n"
		 "                 [-o \"OPTIONS\"]\n"), progname);
1523 1524
	printf(_("  %s reload  [-D DATADIR] [-s]\n"), progname);
	printf(_("  %s status  [-D DATADIR]\n"), progname);
1525
	printf(_("  %s kill    SIGNALNAME PID\n"), progname);
1526
#if defined(WIN32) || defined(__CYGWIN__)
1527
	printf(_("  %s register   [-N SERVICENAME] [-U USERNAME] [-P PASSWORD] [-D DATADIR]\n"
Bruce Momjian's avatar
Bruce Momjian committed
1528
		 "                    [-w] [-t SECS] [-o \"OPTIONS\"]\n"), progname);
Peter Eisentraut's avatar
Peter Eisentraut committed
1529
	printf(_("  %s unregister [-N SERVICENAME]\n"), progname);
1530
#endif
1531 1532

	printf(_("\nCommon options:\n"));
1533
	printf(_("  -D, --pgdata DATADIR   location of the database storage area\n"));
1534
	printf(_("  -s, --silent           only print errors, no informational messages\n"));
1535
	printf(_("  -t SECS                seconds to wait when using -w option\n"));
1536 1537 1538 1539
	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"));
1540
	printf(_("(The default is to wait for shutdown, but not for start or restart.)\n\n"));
1541 1542 1543 1544
	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"));
1545
	printf(_("  -o OPTIONS             command line options to pass to postgres\n"
1546
			 "                         (PostgreSQL server executable)\n"));
1547
	printf(_("  -p PATH-TO-POSTGRES    normally not necessary\n"));
1548 1549 1550 1551 1552
#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
1553
	printf(_("\nOptions for stop or restart:\n"));
1554
	printf(_("  -m SHUTDOWN-MODE   can be \"smart\", \"fast\", or \"immediate\"\n"));
1555 1556

	printf(_("\nShutdown modes are:\n"));
1557 1558
	printf(_("  smart       quit after all clients have disconnected\n"));
	printf(_("  fast        quit directly, with proper shutdown\n"));
1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571
	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");

#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"));
#endif

	printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595
}



static void
set_mode(char *modeopt)
{
	if (strcmp(modeopt, "s") == 0 || strcmp(modeopt, "smart") == 0)
	{
		shutdown_mode = SMART_MODE;
		sig = SIGTERM;
	}
	else if (strcmp(modeopt, "f") == 0 || strcmp(modeopt, "fast") == 0)
	{
		shutdown_mode = FAST_MODE;
		sig = SIGINT;
	}
	else if (strcmp(modeopt, "i") == 0 || strcmp(modeopt, "immediate") == 0)
	{
		shutdown_mode = IMMEDIATE_MODE;
		sig = SIGQUIT;
	}
	else
	{
Peter Eisentraut's avatar
Peter Eisentraut committed
1596
		write_stderr(_("%s: unrecognized shutdown mode \"%s\"\n"), progname, modeopt);
1597 1598 1599 1600 1601 1602 1603 1604 1605 1606
		do_advice();
		exit(1);
	}
}



static void
set_sig(char *signame)
{
1607
	if (!strcmp(signame, "HUP"))
1608
		sig = SIGHUP;
1609
	else if (!strcmp(signame, "INT"))
1610
		sig = SIGINT;
1611
	else if (!strcmp(signame, "QUIT"))
1612
		sig = SIGQUIT;
1613
	else if (!strcmp(signame, "ABRT"))
1614 1615 1616 1617 1618
		sig = SIGABRT;

	/*
	 * probably should NOT provide SIGKILL
	 *
1619
	 * else if (!strcmp(signame,"KILL")) sig = SIGKILL;
1620
	 */
1621
	else if (!strcmp(signame, "TERM"))
1622
		sig = SIGTERM;
1623
	else if (!strcmp(signame, "USR1"))
1624
		sig = SIGUSR1;
1625
	else if (!strcmp(signame, "USR2"))
1626 1627 1628
		sig = SIGUSR2;
	else
	{
Peter Eisentraut's avatar
Peter Eisentraut committed
1629
		write_stderr(_("%s: unrecognized signal name \"%s\"\n"), progname, signame);
1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647
		do_advice();
		exit(1);
	}

}



int
main(int argc, char **argv)
{
	static struct option long_options[] = {
		{"help", no_argument, NULL, '?'},
		{"version", no_argument, NULL, 'V'},
		{"log", required_argument, NULL, 'l'},
		{"mode", required_argument, NULL, 'm'},
		{"pgdata", required_argument, NULL, 'D'},
		{"silent", no_argument, NULL, 's'},
1648
		{"timeout", required_argument, NULL, 't'},
1649
		{"core-files", no_argument, NULL, 'c'},
1650
		{NULL, 0, NULL, 0}
1651 1652 1653 1654
	};

	int			option_index;
	int			c;
1655
	pgpid_t		killproc = 0;
Bruce Momjian's avatar
Bruce Momjian committed
1656

1657
#if defined(WIN32) || defined(__CYGWIN__)
1658 1659 1660 1661
	setvbuf(stderr, NULL, _IONBF, 0);
#endif

	progname = get_progname(argv[0]);
1662
	set_pglocale_pgservice(argv[0], "pg_ctl");
1663 1664

	/*
1665 1666
	 * save argv[0] so do_start() can look for the postmaster if necessary. we
	 * don't look for postmaster here because in many cases we won't need it.
1667 1668 1669 1670 1671
	 */
	argv0 = argv[0];

	umask(077);

1672
	/* support --help and --version even if invoked as root */
Bruce Momjian's avatar
Bruce Momjian committed
1673 1674
	if (argc > 1)
	{
1675 1676 1677 1678 1679 1680 1681 1682
		if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 ||
			strcmp(argv[1], "-?") == 0)
		{
			do_help();
			exit(0);
		}
		else if (strcmp(argv[1], "-V") == 0 || strcmp(argv[1], "--version") == 0)
		{
1683
			puts("pg_ctl (PostgreSQL) " PG_VERSION);
1684 1685 1686 1687
			exit(0);
		}
	}

1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702
	/*
	 * Disallow running as root, to forestall any possible security holes.
	 */
#ifndef WIN32
	if (geteuid() == 0)
	{
		write_stderr(_("%s: cannot be run as root\n"
					   "Please log in (using, e.g., \"su\") as the "
					   "(unprivileged) user that will\n"
					   "own the server process.\n"),
					 progname);
		exit(1);
	}
#endif

1703
	/*
Bruce Momjian's avatar
Bruce Momjian committed
1704
	 * 'Action' can be before or after args so loop over both. Some
1705 1706 1707
	 * getopt_long() implementations will reorder argv[] to place all flags
	 * first (GNU?), but we don't rely on it. Our /port version doesn't do
	 * that.
1708 1709
	 */
	optind = 1;
Bruce Momjian's avatar
Bruce Momjian committed
1710

1711 1712 1713
	/* process command-line options */
	while (optind < argc)
	{
1714
		while ((c = getopt_long(argc, argv, "cD:l:m:N:o:p:P:st:U:wW", long_options, &option_index)) != -1)
1715 1716 1717 1718
		{
			switch (c)
			{
				case 'D':
Bruce Momjian's avatar
Bruce Momjian committed
1719
					{
1720
						char	   *pgdata_D;
1721
						char	   *env_var = pg_malloc(strlen(optarg) + 8);
Bruce Momjian's avatar
Bruce Momjian committed
1722

1723
						pgdata_D = xstrdup(optarg);
1724
						canonicalize_path(pgdata_D);
1725
						snprintf(env_var, strlen(optarg) + 8, "PGDATA=%s",
1726
								 pgdata_D);
Bruce Momjian's avatar
Bruce Momjian committed
1727 1728 1729
						putenv(env_var);

						/*
1730 1731 1732
						 * We could pass PGDATA just in an environment
						 * variable but we do -D too for clearer postmaster
						 * 'ps' display
Bruce Momjian's avatar
Bruce Momjian committed
1733
						 */
1734
						pgdata_opt = pg_malloc(strlen(pgdata_D) + 7);
1735 1736
						snprintf(pgdata_opt, strlen(pgdata_D) + 7,
								 "-D \"%s\" ",
1737
								 pgdata_D);
Bruce Momjian's avatar
Bruce Momjian committed
1738 1739
						break;
					}
1740 1741 1742 1743 1744 1745
				case 'l':
					log_file = xstrdup(optarg);
					break;
				case 'm':
					set_mode(optarg);
					break;
1746 1747 1748
				case 'N':
					register_servicename = xstrdup(optarg);
					break;
1749 1750 1751 1752 1753 1754
				case 'o':
					post_opts = xstrdup(optarg);
					break;
				case 'p':
					postgres_path = xstrdup(optarg);
					break;
1755
				case 'P':
Bruce Momjian's avatar
Bruce Momjian committed
1756
					register_password = xstrdup(optarg);
1757
					break;
1758
				case 's':
Neil Conway's avatar
Neil Conway committed
1759
					silent_mode = true;
1760
					break;
1761 1762 1763
				case 't':
					wait_seconds = atoi(optarg);
					break;
1764
				case 'U':
Bruce Momjian's avatar
Bruce Momjian committed
1765 1766 1767
					if (strchr(optarg, '\\'))
						register_username = xstrdup(optarg);
					else
1768
						/* Prepend .\ for local accounts */
1769
					{
Bruce Momjian's avatar
Bruce Momjian committed
1770
						register_username = malloc(strlen(optarg) + 3);
1771 1772 1773 1774 1775
						if (!register_username)
						{
							write_stderr(_("%s: out of memory\n"), progname);
							exit(1);
						}
Bruce Momjian's avatar
Bruce Momjian committed
1776 1777
						strcpy(register_username, ".\\");
						strcat(register_username, optarg);
1778 1779
					}
					break;
1780 1781 1782 1783 1784 1785 1786 1787
				case 'w':
					do_wait = true;
					wait_set = true;
					break;
				case 'W':
					do_wait = false;
					wait_set = true;
					break;
1788 1789 1790
				case 'c':
					allow_core_files = true;
					break;
1791
				default:
1792
					/* getopt_long already issued a suitable error message */
1793 1794 1795 1796
					do_advice();
					exit(1);
			}
		}
Bruce Momjian's avatar
Bruce Momjian committed
1797

1798 1799 1800 1801 1802
		/* Process an action */
		if (optind < argc)
		{
			if (ctl_command != NO_COMMAND)
			{
1803
				write_stderr(_("%s: too many command-line arguments (first is \"%s\")\n"), progname, argv[optind]);
1804 1805 1806
				do_advice();
				exit(1);
			}
Bruce Momjian's avatar
Bruce Momjian committed
1807

1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821
			if (strcmp(argv[optind], "start") == 0)
				ctl_command = START_COMMAND;
			else if (strcmp(argv[optind], "stop") == 0)
				ctl_command = STOP_COMMAND;
			else if (strcmp(argv[optind], "restart") == 0)
				ctl_command = RESTART_COMMAND;
			else if (strcmp(argv[optind], "reload") == 0)
				ctl_command = RELOAD_COMMAND;
			else if (strcmp(argv[optind], "status") == 0)
				ctl_command = STATUS_COMMAND;
			else if (strcmp(argv[optind], "kill") == 0)
			{
				if (argc - optind < 3)
				{
1822
					write_stderr(_("%s: missing arguments for kill mode\n"), progname);
1823 1824 1825 1826
					do_advice();
					exit(1);
				}
				ctl_command = KILL_COMMAND;
1827 1828
				set_sig(argv[++optind]);
				killproc = atol(argv[++optind]);
1829
			}
1830
#if defined(WIN32) || defined(__CYGWIN__)
1831 1832 1833 1834 1835 1836 1837
			else if (strcmp(argv[optind], "register") == 0)
				ctl_command = REGISTER_COMMAND;
			else if (strcmp(argv[optind], "unregister") == 0)
				ctl_command = UNREGISTER_COMMAND;
			else if (strcmp(argv[optind], "runservice") == 0)
				ctl_command = RUN_AS_SERVICE_COMMAND;
#endif
1838 1839
			else
			{
Peter Eisentraut's avatar
Peter Eisentraut committed
1840
				write_stderr(_("%s: unrecognized operation mode \"%s\"\n"), progname, argv[optind]);
1841 1842 1843 1844 1845 1846
				do_advice();
				exit(1);
			}
			optind++;
		}
	}
1847

1848 1849
	if (ctl_command == NO_COMMAND)
	{
1850
		write_stderr(_("%s: no operation specified\n"), progname);
1851 1852 1853 1854
		do_advice();
		exit(1);
	}

1855
	/* Note we put any -D switch into the env var above */
1856
	pg_data = getenv("PGDATA");
1857
	if (pg_data)
1858
	{
1859
		pg_data = xstrdup(pg_data);
1860 1861
		canonicalize_path(pg_data);
	}
1862

1863 1864
	if (pg_data == NULL &&
		ctl_command != KILL_COMMAND && ctl_command != UNREGISTER_COMMAND)
1865
	{
1866 1867 1868
		write_stderr(_("%s: no database directory specified "
					   "and environment variable PGDATA unset\n"),
					 progname);
1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894
		do_advice();
		exit(1);
	}

	if (!wait_set)
	{
		switch (ctl_command)
		{
			case RESTART_COMMAND:
			case START_COMMAND:
				do_wait = false;
				break;
			case STOP_COMMAND:
				do_wait = true;
				break;
			default:
				break;
		}
	}

	if (ctl_command == RELOAD_COMMAND)
	{
		sig = SIGHUP;
		do_wait = false;
	}

Bruce Momjian's avatar
Bruce Momjian committed
1895 1896 1897 1898 1899 1900
	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);
1901
		snprintf(backup_file, MAXPGPATH, "%s/backup_label", pg_data);
Bruce Momjian's avatar
Bruce Momjian committed
1902
	}
1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921

	switch (ctl_command)
	{
		case STATUS_COMMAND:
			do_status();
			break;
		case START_COMMAND:
			do_start();
			break;
		case STOP_COMMAND:
			do_stop();
			break;
		case RESTART_COMMAND:
			do_restart();
			break;
		case RELOAD_COMMAND:
			do_reload();
			break;
		case KILL_COMMAND:
1922
			do_kill(killproc);
1923
			break;
1924
#if defined(WIN32) || defined(__CYGWIN__)
Bruce Momjian's avatar
Bruce Momjian committed
1925 1926 1927 1928 1929 1930 1931 1932 1933
		case REGISTER_COMMAND:
			pgwin32_doRegister();
			break;
		case UNREGISTER_COMMAND:
			pgwin32_doUnregister();
			break;
		case RUN_AS_SERVICE_COMMAND:
			pgwin32_doRunAsService();
			break;
1934
#endif
1935 1936 1937 1938 1939 1940
		default:
			break;
	}

	exit(0);
}