Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * pg_ctl --- start/stops/restarts the PostgreSQL server
4 : : *
5 : : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
6 : : *
7 : : * src/bin/pg_ctl/pg_ctl.c
8 : : *
9 : : *-------------------------------------------------------------------------
10 : : */
11 : :
12 : : #include "postgres_fe.h"
13 : :
14 : : #include <fcntl.h>
15 : : #include <signal.h>
16 : : #include <time.h>
17 : : #include <sys/resource.h>
18 : : #include <sys/stat.h>
19 : : #include <sys/time.h>
20 : : #include <sys/wait.h>
21 : : #include <unistd.h>
22 : :
23 : :
24 : : #include "catalog/pg_control.h"
25 : : #include "common/controldata_utils.h"
26 : : #include "common/file_perm.h"
27 : : #include "common/logging.h"
28 : : #include "common/string.h"
29 : : #include "datatype/timestamp.h"
30 : : #include "getopt_long.h"
31 : : #include "utils/pidfile.h"
32 : :
33 : : #ifdef WIN32 /* on Unix, we don't need libpq */
34 : : #include "pqexpbuffer.h"
35 : : #endif
36 : :
37 : :
38 : : typedef enum
39 : : {
40 : : SMART_MODE,
41 : : FAST_MODE,
42 : : IMMEDIATE_MODE,
43 : : } ShutdownMode;
44 : :
45 : : typedef enum
46 : : {
47 : : POSTMASTER_READY,
48 : : POSTMASTER_STILL_STARTING,
49 : : POSTMASTER_SHUTDOWN_IN_RECOVERY,
50 : : POSTMASTER_FAILED,
51 : : } WaitPMResult;
52 : :
53 : : typedef enum
54 : : {
55 : : NO_COMMAND = 0,
56 : : INIT_COMMAND,
57 : : START_COMMAND,
58 : : STOP_COMMAND,
59 : : RESTART_COMMAND,
60 : : RELOAD_COMMAND,
61 : : STATUS_COMMAND,
62 : : PROMOTE_COMMAND,
63 : : LOGROTATE_COMMAND,
64 : : KILL_COMMAND,
65 : : REGISTER_COMMAND,
66 : : UNREGISTER_COMMAND,
67 : : RUN_AS_SERVICE_COMMAND,
68 : : } CtlCommand;
69 : :
70 : : #define DEFAULT_WAIT 60
71 : :
72 : : #define WAITS_PER_SEC 10
73 : : StaticAssertDecl(USECS_PER_SEC % WAITS_PER_SEC == 0,
74 : : "WAITS_PER_SEC must divide USECS_PER_SEC evenly");
75 : :
76 : : static bool do_wait = true;
77 : : static int wait_seconds = DEFAULT_WAIT;
78 : : static bool wait_seconds_arg = false;
79 : : static bool silent_mode = false;
80 : : static ShutdownMode shutdown_mode = FAST_MODE;
81 : : static int sig = SIGINT; /* default */
82 : : static CtlCommand ctl_command = NO_COMMAND;
83 : : static char *pg_data = NULL;
84 : : static char *pg_config = NULL;
85 : : static char *pgdata_opt = NULL;
86 : : static char *post_opts = NULL;
87 : : static const char *progname;
88 : : static char *log_file = NULL;
89 : : static char *exec_path = NULL;
90 : : static char *event_source = NULL;
91 : : static char *register_servicename = "PostgreSQL"; /* FIXME: + version ID? */
92 : : static char *register_username = NULL;
93 : : static char *register_password = NULL;
94 : : static char *argv0 = NULL;
95 : : static bool allow_core_files = false;
96 : : static time_t start_time;
97 : :
98 : : static char postopts_file[MAXPGPATH];
99 : : static char version_file[MAXPGPATH];
100 : : static char pid_file[MAXPGPATH];
101 : : static char promote_file[MAXPGPATH];
102 : : static char logrotate_file[MAXPGPATH];
103 : :
104 : : static volatile pid_t postmasterPID = -1;
105 : :
106 : : #ifdef WIN32
107 : : static DWORD pgctl_start_type = SERVICE_AUTO_START;
108 : : static SERVICE_STATUS status;
109 : : static SERVICE_STATUS_HANDLE hStatus = (SERVICE_STATUS_HANDLE) 0;
110 : : static HANDLE shutdownHandles[2];
111 : :
112 : : #define shutdownEvent shutdownHandles[0]
113 : : #define postmasterProcess shutdownHandles[1]
114 : : #endif
115 : :
116 : :
117 : : static void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2);
118 : : static void do_advice(void);
119 : : static void do_help(void);
120 : : static void set_mode(char *modeopt);
121 : : static void set_sig(char *signame);
122 : : static void do_init(void);
123 : : static void do_start(void);
124 : : static void do_stop(void);
125 : : static void do_restart(void);
126 : : static void do_reload(void);
127 : : static void do_status(void);
128 : : static void do_promote(void);
129 : : static void do_logrotate(void);
130 : : static void do_kill(pid_t pid);
131 : : static void print_msg(const char *msg);
132 : : static void adjust_data_dir(void);
133 : :
134 : : #ifdef WIN32
135 : : #include <versionhelpers.h>
136 : : static bool pgwin32_IsInstalled(SC_HANDLE);
137 : : static char *pgwin32_CommandLine(bool);
138 : : static void pgwin32_doRegister(void);
139 : : static void pgwin32_doUnregister(void);
140 : : static void pgwin32_SetServiceStatus(DWORD);
141 : : static void WINAPI pgwin32_ServiceHandler(DWORD);
142 : : static void WINAPI pgwin32_ServiceMain(DWORD, LPTSTR *);
143 : : static void pgwin32_doRunAsService(void);
144 : : static int CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_service);
145 : : static PTOKEN_PRIVILEGES GetPrivilegesToDelete(HANDLE hToken);
146 : : #endif
147 : :
148 : : static pid_t get_pgpid(bool is_status_request);
149 : : static char **readfile(const char *path, int *numlines);
150 : : static void free_readfile(char **optlines);
151 : : static pid_t start_postmaster(void);
152 : : static void read_post_opts(void);
153 : :
154 : : static WaitPMResult wait_for_postmaster_start(pid_t pm_pid, bool do_checkpoint);
155 : : static bool wait_for_postmaster_stop(void);
156 : : static bool wait_for_postmaster_promote(void);
157 : : static bool postmaster_is_alive(pid_t pid);
158 : :
159 : : #if defined(HAVE_GETRLIMIT)
160 : : static void unlimit_core_size(void);
161 : : #endif
162 : :
163 : : static DBState get_control_dbstate(void);
164 : :
165 : :
166 : : #ifdef WIN32
167 : : static void
168 : : write_eventlog(int level, const char *line)
169 : : {
170 : : static HANDLE evtHandle = INVALID_HANDLE_VALUE;
171 : :
172 : : if (silent_mode && level == EVENTLOG_INFORMATION_TYPE)
173 : : return;
174 : :
175 : : if (evtHandle == INVALID_HANDLE_VALUE)
176 : : {
177 : : evtHandle = RegisterEventSource(NULL,
178 : : event_source ? event_source : DEFAULT_EVENT_SOURCE);
179 : : if (evtHandle == NULL)
180 : : {
181 : : evtHandle = INVALID_HANDLE_VALUE;
182 : : return;
183 : : }
184 : : }
185 : :
186 : : ReportEvent(evtHandle,
187 : : level,
188 : : 0,
189 : : 0, /* All events are Id 0 */
190 : : NULL,
191 : : 1,
192 : : 0,
193 : : &line,
194 : : NULL);
195 : : }
196 : : #endif
197 : :
198 : : /*
199 : : * Write errors to stderr (or by equal means when stderr is
200 : : * not available).
201 : : */
202 : : static void
7845 tgl@sss.pgh.pa.us 203 :CBC 43 : write_stderr(const char *fmt,...)
204 : : {
205 : : va_list ap;
206 : :
207 : 43 : va_start(ap, fmt);
208 : : #ifndef WIN32
209 : : /* On Unix, we just fprintf to stderr */
210 : 43 : vfprintf(stderr, fmt, ap);
211 : : #else
212 : :
213 : : /*
214 : : * On Win32, we print to stderr if running on a console, or write to
215 : : * eventlog if running as a service
216 : : */
217 : : if (pgwin32_is_service()) /* Running as a service */
218 : : {
219 : : char errbuf[2048]; /* Arbitrary size? */
220 : :
221 : : vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
222 : :
223 : : write_eventlog(EVENTLOG_ERROR_TYPE, errbuf);
224 : : }
225 : : else
226 : : /* Not running as service, write to stderr */
227 : : vfprintf(stderr, fmt, ap);
228 : : #endif
229 : 43 : va_end(ap);
230 : 43 : }
231 : :
232 : : /*
233 : : * Given an already-localized string, print it to stdout unless the
234 : : * user has specified that no messages should be printed.
235 : : */
236 : : static void
7732 neilc@samurai.com 237 : 6678 : print_msg(const char *msg)
238 : : {
239 [ + + ]: 6678 : if (!silent_mode)
240 : : {
241 : 6210 : fputs(msg, stdout);
242 : 6210 : fflush(stdout);
243 : : }
244 : 6678 : }
245 : :
246 : : static pid_t
4301 bruce@momjian.us 247 : 5674 : get_pgpid(bool is_status_request)
248 : : {
249 : : FILE *pidf;
250 : : int pid;
251 : : struct stat statbuf;
252 : :
253 [ + + ]: 5674 : if (stat(pg_data, &statbuf) != 0)
254 : : {
255 [ + - ]: 3 : if (errno == ENOENT)
4234 peter_e@gmx.net 256 : 3 : write_stderr(_("%s: directory \"%s\" does not exist\n"), progname,
257 : : pg_data);
258 : : else
644 michael@paquier.xyz 259 :UBC 0 : write_stderr(_("%s: could not access directory \"%s\": %m\n"), progname,
260 : : pg_data);
261 : :
262 : : /*
263 : : * The Linux Standard Base Core Specification 3.1 says this should
264 : : * return '4, program or service status is unknown'
265 : : * https://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
266 : : */
4301 bruce@momjian.us 267 [ + + ]:CBC 3 : exit(is_status_request ? 4 : 1);
268 : : }
269 : :
270 [ - + - - ]: 5671 : if (stat(version_file, &statbuf) != 0 && errno == ENOENT)
271 : : {
4234 peter_e@gmx.net 272 :UBC 0 : write_stderr(_("%s: directory \"%s\" is not a database cluster directory\n"),
273 : : progname, pg_data);
4301 bruce@momjian.us 274 [ # # ]: 0 : exit(is_status_request ? 4 : 1);
275 : : }
276 : :
7873 bruce@momjian.us 277 :CBC 5671 : pidf = fopen(pid_file, "r");
278 [ + + ]: 5671 : if (pidf == NULL)
279 : : {
280 : : /* No pid file, not an error on startup */
281 [ + - ]: 1508 : if (errno == ENOENT)
282 : 1508 : return 0;
283 : : else
284 : : {
644 michael@paquier.xyz 285 :UBC 0 : write_stderr(_("%s: could not open PID file \"%s\": %m\n"),
286 : : progname, pid_file);
7873 bruce@momjian.us 287 : 0 : exit(1);
288 : : }
289 : : }
1151 peter@eisentraut.org 290 [ - + ]:CBC 4163 : if (fscanf(pidf, "%d", &pid) != 1)
291 : : {
292 : : /* Is the file empty? */
4857 bruce@momjian.us 293 [ # # # # ]:UBC 0 : if (ftell(pidf) == 0 && feof(pidf))
294 : 0 : write_stderr(_("%s: the PID file \"%s\" is empty\n"),
295 : : progname, pid_file);
296 : : else
297 : 0 : write_stderr(_("%s: invalid data in PID file \"%s\"\n"),
298 : : progname, pid_file);
7545 tgl@sss.pgh.pa.us 299 : 0 : exit(1);
300 : : }
7873 bruce@momjian.us 301 :CBC 4163 : fclose(pidf);
1151 peter@eisentraut.org 302 : 4163 : return (pid_t) pid;
303 : : }
304 : :
305 : :
306 : : /*
307 : : * get the lines from a text file - return NULL if file can't be opened
308 : : *
309 : : * Trailing newlines are deleted from the lines (this is a change from pre-v10)
310 : : *
311 : : * *numlines is set to the number of line pointers returned; there is
312 : : * also an additional NULL pointer after the last real line.
313 : : */
314 : : static char **
3093 tgl@sss.pgh.pa.us 315 : 2415 : readfile(const char *path, int *numlines)
316 : : {
317 : : int fd;
318 : : int nlines;
319 : : char **result;
320 : : char *buffer;
321 : : char *linebegin;
322 : : int i;
323 : : int n;
324 : : int len;
325 : : struct stat statbuf;
326 : :
327 : 2415 : *numlines = 0; /* in case of failure or empty file */
328 : :
329 : : /*
330 : : * Slurp the file into memory.
331 : : *
332 : : * The file can change concurrently, so we read the whole file into memory
333 : : * with a single read() call. That's not guaranteed to get an atomic
334 : : * snapshot, but in practice, for a small file, it's close enough for the
335 : : * current use.
336 : : */
4812 heikki.linnakangas@i 337 : 2415 : fd = open(path, O_RDONLY | PG_BINARY, 0);
338 [ + + ]: 2415 : if (fd < 0)
339 : 783 : return NULL;
340 [ - + ]: 1632 : if (fstat(fd, &statbuf) < 0)
341 : : {
4807 heikki.linnakangas@i 342 :UBC 0 : close(fd);
7873 bruce@momjian.us 343 : 0 : return NULL;
344 : : }
4812 heikki.linnakangas@i 345 [ - + ]:CBC 1632 : if (statbuf.st_size == 0)
346 : : {
347 : : /* empty file */
4807 heikki.linnakangas@i 348 :UBC 0 : close(fd);
4812 349 : 0 : result = (char **) pg_malloc(sizeof(char *));
350 : 0 : *result = NULL;
351 : 0 : return result;
352 : : }
4812 heikki.linnakangas@i 353 :CBC 1632 : buffer = pg_malloc(statbuf.st_size + 1);
354 : :
355 : 1632 : len = read(fd, buffer, statbuf.st_size + 1);
356 : 1632 : close(fd);
357 [ - + ]: 1632 : if (len != statbuf.st_size)
358 : : {
359 : : /* oops, the file size changed between fstat and read */
4812 heikki.linnakangas@i 360 :UBC 0 : free(buffer);
361 : 0 : return NULL;
362 : : }
363 : :
364 : : /*
365 : : * Count newlines. We expect there to be a newline after each full line,
366 : : * including one at the end of file. If there isn't a newline at the end,
367 : : * any characters after the last newline will be ignored.
368 : : */
4812 heikki.linnakangas@i 369 :CBC 1632 : nlines = 0;
4807 370 [ + + ]: 395330 : for (i = 0; i < len; i++)
371 : : {
4812 372 [ + + ]: 393698 : if (buffer[i] == '\n')
7873 bruce@momjian.us 373 : 12307 : nlines++;
374 : : }
375 : :
376 : : /* set up the result buffer */
7464 377 : 1632 : result = (char **) pg_malloc((nlines + 1) * sizeof(char *));
3093 tgl@sss.pgh.pa.us 378 : 1632 : *numlines = nlines;
379 : :
380 : : /* now split the buffer into lines */
4812 heikki.linnakangas@i 381 : 1632 : linebegin = buffer;
382 : 1632 : n = 0;
383 [ + + ]: 395330 : for (i = 0; i < len; i++)
384 : : {
4807 385 [ + + ]: 393698 : if (buffer[i] == '\n')
386 : : {
3093 tgl@sss.pgh.pa.us 387 : 12307 : int slen = &buffer[i] - linebegin;
4584 bruce@momjian.us 388 : 12307 : char *linebuf = pg_malloc(slen + 1);
389 : :
4812 heikki.linnakangas@i 390 : 12307 : memcpy(linebuf, linebegin, slen);
391 : : /* we already dropped the \n, but get rid of any \r too */
3093 tgl@sss.pgh.pa.us 392 [ + + - + ]: 12307 : if (slen > 0 && linebuf[slen - 1] == '\r')
3093 tgl@sss.pgh.pa.us 393 :UBC 0 : slen--;
4812 heikki.linnakangas@i 394 :CBC 12307 : linebuf[slen] = '\0';
395 : 12307 : result[n++] = linebuf;
396 : 12307 : linebegin = &buffer[i + 1];
397 : : }
398 : : }
399 : 1632 : result[n] = NULL;
400 : :
7057 meskes@postgresql.or 401 : 1632 : free(buffer);
402 : :
7873 bruce@momjian.us 403 : 1632 : return result;
404 : : }
405 : :
406 : :
407 : : /*
408 : : * Free memory allocated for optlines through readfile()
409 : : */
410 : : static void
4308 sfrost@snowman.net 411 : 2415 : free_readfile(char **optlines)
412 : : {
4242 bruce@momjian.us 413 : 2415 : char *curr_line = NULL;
414 : 2415 : int i = 0;
415 : :
4308 sfrost@snowman.net 416 [ + + ]: 2415 : if (!optlines)
417 : 783 : return;
418 : :
4304 419 [ + + ]: 13939 : while ((curr_line = optlines[i++]))
420 : 12307 : free(curr_line);
421 : :
4308 422 : 1632 : free(optlines);
423 : : }
424 : :
425 : : /*
426 : : * start/test/stop routines
427 : : */
428 : :
429 : : /*
430 : : * Start the postmaster and return its PID.
431 : : *
432 : : * Currently, on Windows what we return is the PID of the shell process
433 : : * that launched the postmaster (and, we trust, is waiting for it to exit).
434 : : * So the PID is usable for "is the postmaster still running" checks,
435 : : * but cannot be compared directly to postmaster.pid.
436 : : *
437 : : * On Windows, we also save aside a handle to the shell process in
438 : : * "postmasterProcess", which the caller should close when done with it.
439 : : */
440 : : static pid_t
7873 bruce@momjian.us 441 : 765 : start_postmaster(void)
442 : : {
443 : : char *cmd;
444 : :
445 : : #ifndef WIN32
446 : : pid_t pm_pid;
447 : :
448 : : /* Flush stdio channels just before fork, to avoid double-output problems */
1205 tgl@sss.pgh.pa.us 449 : 765 : fflush(NULL);
450 : :
451 : : #ifdef EXEC_BACKEND
452 : : pg_disable_aslr();
453 : : #endif
454 : :
3718 455 : 765 : pm_pid = fork();
456 [ - + ]: 1530 : if (pm_pid < 0)
457 : : {
458 : : /* fork failed */
644 michael@paquier.xyz 459 :UBC 0 : write_stderr(_("%s: could not start server: %m\n"),
460 : : progname);
3718 tgl@sss.pgh.pa.us 461 : 0 : exit(1);
462 : : }
3718 tgl@sss.pgh.pa.us 463 [ + + ]:CBC 1530 : if (pm_pid > 0)
464 : : {
465 : : /* fork succeeded, in parent */
466 : 765 : return pm_pid;
467 : : }
468 : :
469 : : /* fork succeeded, in child */
470 : :
471 : : /*
472 : : * If possible, detach the postmaster process from the launching process
473 : : * group and make it a group leader, so that it doesn't get signaled along
474 : : * with the current group that launched it.
475 : : */
476 : : #ifdef HAVE_SETSID
2528 heikki.linnakangas@i 477 [ - + ]: 765 : if (setsid() < 0)
478 : : {
644 michael@paquier.xyz 479 :UBC 0 : write_stderr(_("%s: could not start server due to setsid() failure: %m\n"),
480 : : progname);
2528 heikki.linnakangas@i 481 : 0 : exit(1);
482 : : }
483 : : #endif
484 : :
485 : : /*
486 : : * Since there might be quotes to handle here, it is easier simply to pass
487 : : * everything to a shell to process them. Use exec so that the postmaster
488 : : * has the same PID as the current child process.
489 : : */
7873 bruce@momjian.us 490 [ + + ]:CBC 765 : if (log_file != NULL)
1565 tgl@sss.pgh.pa.us 491 : 750 : cmd = psprintf("exec \"%s\" %s%s < \"%s\" >> \"%s\" 2>&1",
492 : : exec_path, pgdata_opt, post_opts,
493 : : DEVNULL, log_file);
494 : : else
495 : 15 : cmd = psprintf("exec \"%s\" %s%s < \"%s\" 2>&1",
496 : : exec_path, pgdata_opt, post_opts, DEVNULL);
497 : :
3718 498 : 765 : (void) execl("/bin/sh", "/bin/sh", "-c", cmd, (char *) NULL);
499 : :
500 : : /* exec failed */
644 michael@paquier.xyz 501 : 765 : write_stderr(_("%s: could not start server: %m\n"),
502 : : progname);
3718 tgl@sss.pgh.pa.us 503 :UBC 0 : exit(1);
504 : :
505 : : return 0; /* keep dumb compilers quiet */
506 : :
507 : : #else /* WIN32 */
508 : :
509 : : /*
510 : : * As with the Unix case, it's easiest to use the shell (CMD.EXE) to
511 : : * handle redirection etc. Unfortunately CMD.EXE lacks any equivalent of
512 : : * "exec", so we don't get to find out the postmaster's PID immediately.
513 : : */
514 : : PROCESS_INFORMATION pi;
515 : : const char *comspec;
516 : :
517 : : /* Find CMD.EXE location using COMSPEC, if it's set */
518 : : comspec = getenv("COMSPEC");
519 : : if (comspec == NULL)
520 : : comspec = "CMD";
521 : :
522 : : if (log_file != NULL)
523 : : {
524 : : /*
525 : : * First, open the log file if it exists. The idea is that if the
526 : : * file is still locked by a previous postmaster run, we'll wait until
527 : : * it comes free, instead of failing with ERROR_SHARING_VIOLATION.
528 : : * (It'd be better to open the file in a sharing-friendly mode, but we
529 : : * can't use CMD.EXE to do that, so work around it. Note that the
530 : : * previous postmaster will still have the file open for a short time
531 : : * after removing postmaster.pid.)
532 : : *
533 : : * If the log file doesn't exist, we *must not* create it here. If we
534 : : * were launched with higher privileges than the restricted process
535 : : * will have, the log file might end up with permissions settings that
536 : : * prevent the postmaster from writing on it.
537 : : */
538 : : int fd = open(log_file, O_RDWR, 0);
539 : :
540 : : if (fd == -1)
541 : : {
542 : : /*
543 : : * ENOENT is expectable since we didn't use O_CREAT. Otherwise
544 : : * complain. We could just fall through and let CMD.EXE report
545 : : * the problem, but its error reporting is pretty miserable.
546 : : */
547 : : if (errno != ENOENT)
548 : : {
549 : : write_stderr(_("%s: could not open log file \"%s\": %m\n"),
550 : : progname, log_file);
551 : : exit(1);
552 : : }
553 : : }
554 : : else
555 : : close(fd);
556 : :
557 : : cmd = psprintf("\"%s\" /C \"\"%s\" %s%s < \"%s\" >> \"%s\" 2>&1\"",
558 : : comspec, exec_path, pgdata_opt, post_opts, DEVNULL, log_file);
559 : : }
560 : : else
561 : : cmd = psprintf("\"%s\" /C \"\"%s\" %s%s < \"%s\" 2>&1\"",
562 : : comspec, exec_path, pgdata_opt, post_opts, DEVNULL);
563 : :
564 : : if (!CreateRestrictedProcess(cmd, &pi, false))
565 : : {
566 : : write_stderr(_("%s: could not start server: error code %lu\n"),
567 : : progname, GetLastError());
568 : : exit(1);
569 : : }
570 : : /* Don't close command process handle here; caller must do so */
571 : : postmasterProcess = pi.hProcess;
572 : : CloseHandle(pi.hThread);
573 : : return pi.dwProcessId; /* Shell's PID, not postmaster's! */
574 : : #endif /* WIN32 */
575 : : }
576 : :
577 : :
578 : :
579 : : /*
580 : : * Wait for the postmaster to become ready.
581 : : *
582 : : * On Unix, pm_pid is the PID of the just-launched postmaster. On Windows,
583 : : * it may be the PID of an ancestor shell process, so we can't check the
584 : : * contents of postmaster.pid quite as carefully.
585 : : *
586 : : * On Windows, the static variable postmasterProcess is an implicit argument
587 : : * to this routine; it contains a handle to the postmaster process or an
588 : : * ancestor shell process thereof.
589 : : *
590 : : * Note that the checkpoint parameter enables a Windows service control
591 : : * manager checkpoint, it's got nothing to do with database checkpoints!!
592 : : */
593 : : static WaitPMResult
1151 peter@eisentraut.org 594 :CBC 765 : wait_for_postmaster_start(pid_t pm_pid, bool do_checkpoint)
595 : : {
596 : : int i;
597 : :
3095 tgl@sss.pgh.pa.us 598 [ + - ]: 2313 : for (i = 0; i < wait_seconds * WAITS_PER_SEC; i++)
599 : : {
600 : : char **optlines;
601 : : int numlines;
602 : :
603 : : /*
604 : : * Try to read the postmaster.pid file. If it's not valid, or if the
605 : : * status line isn't there yet, just keep waiting.
606 : : */
3093 607 [ + + ]: 2313 : if ((optlines = readfile(pid_file, &numlines)) != NULL &&
608 [ + + ]: 1530 : numlines >= LOCK_FILE_LINE_PM_STATUS)
609 : : {
610 : : /* File is complete enough for us, parse it */
611 : : pid_t pmpid;
612 : : time_t pmstart;
613 : :
614 : : /*
615 : : * Make sanity checks. If it's for the wrong PID, or the recorded
616 : : * start time is before pg_ctl started, then either we are looking
617 : : * at the wrong data directory, or this is a pre-existing pidfile
618 : : * that hasn't (yet?) been overwritten by our child postmaster.
619 : : * Allow 2 seconds slop for possible cross-process clock skew.
620 : : */
621 : 1495 : pmpid = atol(optlines[LOCK_FILE_LINE_PID - 1]);
435 nathan@postgresql.or 622 : 1495 : pmstart = atoll(optlines[LOCK_FILE_LINE_START_TIME - 1]);
3093 tgl@sss.pgh.pa.us 623 [ + - + + ]: 1495 : if (pmstart >= start_time - 2 &&
624 : : #ifndef WIN32
625 : : pmpid == pm_pid
626 : : #else
627 : : /* Windows can only reject standalone-backend PIDs */
628 : : pmpid > 0
629 : : #endif
630 : : )
631 : : {
632 : : /*
633 : : * OK, seems to be a valid pidfile from our child. Check the
634 : : * status line (this assumes a v10 or later server).
635 : : */
636 : 1491 : char *pmstatus = optlines[LOCK_FILE_LINE_PM_STATUS - 1];
637 : :
638 [ + + ]: 1491 : if (strcmp(pmstatus, PM_STATUS_READY) == 0 ||
639 [ + + ]: 741 : strcmp(pmstatus, PM_STATUS_STANDBY) == 0)
640 : : {
641 : : /* postmaster is done starting up */
642 : 752 : free_readfile(optlines);
643 : 765 : return POSTMASTER_READY;
644 : : }
645 : : }
646 : : }
647 : :
648 : : /*
649 : : * Free the results of readfile.
650 : : *
651 : : * This is safe to call even if optlines is NULL.
652 : : */
653 : 1561 : free_readfile(optlines);
654 : :
655 : : /*
656 : : * Check whether the child postmaster process is still alive. This
657 : : * lets us exit early if the postmaster fails during startup.
658 : : *
659 : : * On Windows, we may be checking the postmaster's parent shell, but
660 : : * that's fine for this purpose.
661 : : */
662 : : {
663 : : bool pm_died;
664 : : #ifndef WIN32
665 : : int exitstatus;
666 : :
516 667 : 1561 : pm_died = (waitpid(pm_pid, &exitstatus, WNOHANG) == pm_pid);
668 : : #else
669 : : pm_died = (WaitForSingleObject(postmasterProcess, 0) == WAIT_OBJECT_0);
670 : : #endif
671 [ + + ]: 1561 : if (pm_died)
672 : : {
673 : : /* See if postmaster terminated intentionally */
674 [ - + ]: 13 : if (get_control_dbstate() == DB_SHUTDOWNED_IN_RECOVERY)
675 : 13 : return POSTMASTER_SHUTDOWN_IN_RECOVERY;
676 : : else
677 : 13 : return POSTMASTER_FAILED;
678 : : }
679 : : }
680 : :
681 : : /* Startup still in process; wait, printing a dot once per second */
3095 682 [ + + ]: 1548 : if (i % WAITS_PER_SEC == 0)
683 : : {
684 : : #ifdef WIN32
685 : : if (do_checkpoint)
686 : : {
687 : : /*
688 : : * Increment the wait hint by 6 secs (connection timeout +
689 : : * sleep). We must do this to indicate to the SCM that our
690 : : * startup time is changing, otherwise it'll usually send a
691 : : * stop signal after 20 seconds, despite incrementing the
692 : : * checkpoint counter.
693 : : */
694 : : status.dwWaitHint += 6000;
695 : : status.dwCheckPoint++;
696 : : SetServiceStatus(hStatus, (LPSERVICE_STATUS) &status);
697 : : }
698 : : else
699 : : #endif
1815 bruce@momjian.us 700 : 766 : print_msg(".");
701 : : }
702 : :
42 alvherre@kurilemu.de 703 :GNC 1548 : pg_usleep(USECS_PER_SEC / WAITS_PER_SEC);
704 : : }
705 : :
706 : : /* out of patience; report that postmaster is still starting up */
3093 tgl@sss.pgh.pa.us 707 :UBC 0 : return POSTMASTER_STILL_STARTING;
708 : : }
709 : :
710 : :
711 : : /*
712 : : * Wait for the postmaster to stop.
713 : : *
714 : : * Returns true if the postmaster stopped cleanly (i.e., removed its pidfile).
715 : : * Returns false if the postmaster dies uncleanly, or if we time out.
716 : : */
717 : : static bool
1405 tgl@sss.pgh.pa.us 718 :CBC 838 : wait_for_postmaster_stop(void)
719 : : {
720 : : int cnt;
721 : :
722 [ + - ]: 3904 : for (cnt = 0; cnt < wait_seconds * WAITS_PER_SEC; cnt++)
723 : : {
724 : : pid_t pid;
725 : :
726 [ + + ]: 3904 : if ((pid = get_pgpid(false)) == 0)
727 : 838 : return true; /* pid file is gone */
728 : :
1151 peter@eisentraut.org 729 [ - + ]: 3066 : if (kill(pid, 0) != 0)
730 : : {
731 : : /*
732 : : * Postmaster seems to have died. Check the pid file once more to
733 : : * avoid a race condition, but give up waiting.
734 : : */
1405 tgl@sss.pgh.pa.us 735 [ # # ]:UBC 0 : if (get_pgpid(false) == 0)
736 : 0 : return true; /* pid file is gone */
737 : 0 : return false; /* postmaster died untimely */
738 : : }
739 : :
1405 tgl@sss.pgh.pa.us 740 [ + + ]:CBC 3066 : if (cnt % WAITS_PER_SEC == 0)
741 : 846 : print_msg(".");
42 alvherre@kurilemu.de 742 :GNC 3066 : pg_usleep(USECS_PER_SEC / WAITS_PER_SEC);
743 : : }
1405 tgl@sss.pgh.pa.us 744 :UBC 0 : return false; /* timeout reached */
745 : : }
746 : :
747 : :
748 : : /*
749 : : * Wait for the postmaster to promote.
750 : : *
751 : : * Returns true on success, else false.
752 : : * To avoid waiting uselessly, we check for postmaster death here too.
753 : : */
754 : : static bool
1405 tgl@sss.pgh.pa.us 755 :CBC 39 : wait_for_postmaster_promote(void)
756 : : {
757 : : int cnt;
758 : :
759 [ + - ]: 101 : for (cnt = 0; cnt < wait_seconds * WAITS_PER_SEC; cnt++)
760 : : {
761 : : pid_t pid;
762 : : DBState state;
763 : :
764 [ - + ]: 101 : if ((pid = get_pgpid(false)) == 0)
1405 tgl@sss.pgh.pa.us 765 :UBC 0 : return false; /* pid file is gone */
1151 peter@eisentraut.org 766 [ - + ]:CBC 101 : if (kill(pid, 0) != 0)
1405 tgl@sss.pgh.pa.us 767 :UBC 0 : return false; /* postmaster died */
768 : :
1405 tgl@sss.pgh.pa.us 769 :CBC 101 : state = get_control_dbstate();
770 [ + + ]: 101 : if (state == DB_IN_PRODUCTION)
771 : 39 : return true; /* successful promotion */
772 : :
773 [ + + ]: 62 : if (cnt % WAITS_PER_SEC == 0)
774 : 39 : print_msg(".");
42 alvherre@kurilemu.de 775 :GNC 62 : pg_usleep(USECS_PER_SEC / WAITS_PER_SEC);
776 : : }
1405 tgl@sss.pgh.pa.us 777 :UBC 0 : return false; /* timeout reached */
778 : : }
779 : :
780 : :
781 : : #if defined(HAVE_GETRLIMIT)
782 : : static void
6920 andrew@dunslane.net 783 : 0 : unlimit_core_size(void)
784 : : {
785 : : struct rlimit lim;
786 : :
6606 bruce@momjian.us 787 : 0 : getrlimit(RLIMIT_CORE, &lim);
6920 andrew@dunslane.net 788 [ # # ]: 0 : if (lim.rlim_max == 0)
789 : : {
6606 bruce@momjian.us 790 : 0 : write_stderr(_("%s: cannot set core file size limit; disallowed by hard limit\n"),
791 : : progname);
792 : 0 : return;
793 : : }
6920 andrew@dunslane.net 794 [ # # # # ]: 0 : else if (lim.rlim_max == RLIM_INFINITY || lim.rlim_cur < lim.rlim_max)
795 : : {
796 : 0 : lim.rlim_cur = lim.rlim_max;
6606 bruce@momjian.us 797 : 0 : setrlimit(RLIMIT_CORE, &lim);
798 : : }
799 : : }
800 : : #endif
801 : :
802 : : static void
6742 magnus@hagander.net 803 :CBC 765 : read_post_opts(void)
804 : : {
7873 bruce@momjian.us 805 [ + + ]: 765 : if (post_opts == NULL)
806 : : {
6032 807 : 113 : post_opts = ""; /* default */
6382 808 [ + + ]: 113 : if (ctl_command == RESTART_COMMAND)
809 : : {
810 : : char **optlines;
811 : : int numlines;
812 : :
3093 tgl@sss.pgh.pa.us 813 : 101 : optlines = readfile(postopts_file, &numlines);
6382 bruce@momjian.us 814 [ - + ]: 101 : if (optlines == NULL)
815 : : {
7735 peter_e@gmx.net 816 :UBC 0 : write_stderr(_("%s: could not read file \"%s\"\n"), progname, postopts_file);
7873 bruce@momjian.us 817 : 0 : exit(1);
818 : : }
3093 tgl@sss.pgh.pa.us 819 [ - + ]:CBC 101 : else if (numlines != 1)
820 : : {
6382 bruce@momjian.us 821 :UBC 0 : write_stderr(_("%s: option file \"%s\" must have exactly one line\n"),
822 : : progname, postopts_file);
823 : 0 : exit(1);
824 : : }
825 : : else
826 : : {
827 : : char *optline;
828 : : char *arg1;
829 : :
6382 bruce@momjian.us 830 :CBC 101 : optline = optlines[0];
831 : :
832 : : /*
833 : : * Are we at the first option, as defined by space and
834 : : * double-quote?
835 : : */
836 [ + - ]: 101 : if ((arg1 = strstr(optline, " \"")) != NULL)
837 : : {
3100 tgl@sss.pgh.pa.us 838 : 101 : *arg1 = '\0'; /* terminate so we get only program name */
4242 bruce@momjian.us 839 : 101 : post_opts = pg_strdup(arg1 + 1); /* point past whitespace */
840 : : }
5850 peter_e@gmx.net 841 [ + - ]: 101 : if (exec_path == NULL)
4304 sfrost@snowman.net 842 : 101 : exec_path = pg_strdup(optline);
843 : : }
844 : :
845 : : /* Free the results of readfile. */
4308 846 : 101 : free_readfile(optlines);
847 : : }
848 : : }
6742 magnus@hagander.net 849 : 765 : }
850 : :
851 : : /*
852 : : * SIGINT signal handler used while waiting for postmaster to start up.
853 : : * Forwards the SIGINT to the postmaster process, asking it to shut down,
854 : : * before terminating pg_ctl itself. This way, if the user hits CTRL-C while
855 : : * waiting for the server to start up, the server launch is aborted.
856 : : */
857 : : static void
1189 tgl@sss.pgh.pa.us 858 :UBC 0 : trap_sigint_during_startup(SIGNAL_ARGS)
859 : : {
2528 heikki.linnakangas@i 860 [ # # ]: 0 : if (postmasterPID != -1)
861 : : {
862 [ # # ]: 0 : if (kill(postmasterPID, SIGINT) != 0)
644 michael@paquier.xyz 863 : 0 : write_stderr(_("%s: could not send stop signal (PID: %d): %m\n"),
864 : : progname, (int) postmasterPID);
865 : : }
866 : :
867 : : /*
868 : : * Clear the signal handler, and send the signal again, to terminate the
869 : : * process as normal.
870 : : */
1189 tgl@sss.pgh.pa.us 871 : 0 : pqsignal(postgres_signal_arg, SIG_DFL);
872 : 0 : raise(postgres_signal_arg);
2528 heikki.linnakangas@i 873 : 0 : }
874 : :
875 : : static char *
5850 peter_e@gmx.net 876 :CBC 665 : find_other_exec_or_die(const char *argv0, const char *target, const char *versionstr)
877 : : {
878 : : int ret;
879 : : char *found_path;
880 : :
881 : 665 : found_path = pg_malloc(MAXPGPATH);
882 : :
883 [ - + ]: 665 : if ((ret = find_other_exec(argv0, target, versionstr, found_path)) < 0)
884 : : {
885 : : char full_path[MAXPGPATH];
886 : :
5850 peter_e@gmx.net 887 [ # # ]:UBC 0 : if (find_my_exec(argv0, full_path) < 0)
888 : 0 : strlcpy(full_path, progname, sizeof(full_path));
889 : :
890 [ # # ]: 0 : if (ret == -1)
1348 tgl@sss.pgh.pa.us 891 : 0 : write_stderr(_("program \"%s\" is needed by %s but was not found in the same directory as \"%s\"\n"),
892 : : target, progname, full_path);
893 : : else
894 : 0 : write_stderr(_("program \"%s\" was found by \"%s\" but was not the same version as %s\n"),
895 : : target, full_path, progname);
5850 peter_e@gmx.net 896 : 0 : exit(1);
897 : : }
898 : :
5850 peter_e@gmx.net 899 :CBC 665 : return found_path;
900 : : }
901 : :
902 : : static void
903 : 1 : do_init(void)
904 : : {
905 : : char *cmd;
906 : :
907 [ + - ]: 1 : if (exec_path == NULL)
908 : 1 : exec_path = find_other_exec_or_die(argv0, "initdb", "initdb (PostgreSQL) " PG_VERSION "\n");
909 : :
5845 itagaki.takahiro@gma 910 [ - + ]: 1 : if (pgdata_opt == NULL)
5845 itagaki.takahiro@gma 911 :UBC 0 : pgdata_opt = "";
912 : :
5850 peter_e@gmx.net 913 [ - + ]:CBC 1 : if (post_opts == NULL)
5850 peter_e@gmx.net 914 :UBC 0 : post_opts = "";
915 : :
5850 peter_e@gmx.net 916 [ + - ]:CBC 1 : if (!silent_mode)
1565 tgl@sss.pgh.pa.us 917 : 1 : cmd = psprintf("\"%s\" %s%s",
918 : : exec_path, pgdata_opt, post_opts);
919 : : else
1565 tgl@sss.pgh.pa.us 920 :UBC 0 : cmd = psprintf("\"%s\" %s%s > \"%s\"",
921 : : exec_path, pgdata_opt, post_opts, DEVNULL);
922 : :
1205 tgl@sss.pgh.pa.us 923 :CBC 1 : fflush(NULL);
5850 peter_e@gmx.net 924 [ - + ]: 1 : if (system(cmd) != 0)
925 : : {
5850 peter_e@gmx.net 926 :UBC 0 : write_stderr(_("%s: database system initialization failed\n"), progname);
927 : 0 : exit(1);
928 : : }
5850 peter_e@gmx.net 929 :CBC 1 : }
930 : :
931 : : static void
6742 magnus@hagander.net 932 : 766 : do_start(void)
933 : : {
1151 peter@eisentraut.org 934 : 766 : pid_t old_pid = 0;
935 : : pid_t pm_pid;
936 : :
6742 magnus@hagander.net 937 [ + + ]: 766 : if (ctl_command != RESTART_COMMAND)
938 : : {
2256 tgl@sss.pgh.pa.us 939 : 665 : old_pid = get_pgpid(false);
940 [ + + ]: 664 : if (old_pid != 0)
4614 peter_e@gmx.net 941 : 3 : write_stderr(_("%s: another server might be running; "
942 : : "trying to start server anyway\n"),
943 : : progname);
944 : : }
945 : :
6742 magnus@hagander.net 946 : 765 : read_post_opts();
947 : :
948 : : /* No -D or -D already added during server start */
7780 bruce@momjian.us 949 [ + + - + ]: 765 : if (ctl_command == RESTART_COMMAND || pgdata_opt == NULL)
7779 950 : 101 : pgdata_opt = "";
951 : :
5850 peter_e@gmx.net 952 [ + + ]: 765 : if (exec_path == NULL)
953 : 664 : exec_path = find_other_exec_or_die(argv0, "postgres", PG_BACKEND_VERSIONSTR);
954 : :
955 : : #if defined(HAVE_GETRLIMIT)
6920 andrew@dunslane.net 956 [ - + ]: 765 : if (allow_core_files)
6920 andrew@dunslane.net 957 :UBC 0 : unlimit_core_size();
958 : : #endif
959 : :
960 : : /*
961 : : * If possible, tell the postmaster our parent shell's PID (see the
962 : : * comments in CreateLockFile() for motivation). Windows hasn't got
963 : : * getppid() unfortunately.
964 : : */
965 : : #ifndef WIN32
966 : : {
967 : : char env_var[32];
968 : :
1812 tgl@sss.pgh.pa.us 969 :CBC 765 : snprintf(env_var, sizeof(env_var), "%d", (int) getppid());
970 : 765 : setenv("PG_GRANDPARENT_PID", env_var, 1);
971 : : }
972 : : #endif
973 : :
3718 974 : 765 : pm_pid = start_postmaster();
975 : :
7873 bruce@momjian.us 976 [ + - ]: 765 : if (do_wait)
977 : : {
978 : : /*
979 : : * If the user interrupts the startup (e.g. with CTRL-C), we'd like to
980 : : * abort the server launch. Install a signal handler that will
981 : : * forward SIGINT to the postmaster process, while we wait.
982 : : *
983 : : * (We don't bother to reset the signal handler after the launch, as
984 : : * we're about to exit, anyway.)
985 : : */
2528 heikki.linnakangas@i 986 : 765 : postmasterPID = pm_pid;
987 : 765 : pqsignal(SIGINT, trap_sigint_during_startup);
988 : :
7121 peter_e@gmx.net 989 : 765 : print_msg(_("waiting for server to start..."));
990 : :
1405 tgl@sss.pgh.pa.us 991 [ + - - + : 765 : switch (wait_for_postmaster_start(pm_pid, false))
- ]
992 : : {
3093 993 : 752 : case POSTMASTER_READY:
5498 994 : 752 : print_msg(_(" done\n"));
995 : 752 : print_msg(_("server started\n"));
996 : 752 : break;
3093 tgl@sss.pgh.pa.us 997 :UBC 0 : case POSTMASTER_STILL_STARTING:
5498 998 : 0 : print_msg(_(" stopped waiting\n"));
3151 peter_e@gmx.net 999 : 0 : write_stderr(_("%s: server did not start in time\n"),
1000 : : progname);
1001 : 0 : exit(1);
1002 : : break;
516 tgl@sss.pgh.pa.us 1003 : 0 : case POSTMASTER_SHUTDOWN_IN_RECOVERY:
1004 : 0 : print_msg(_(" done\n"));
1005 : 0 : print_msg(_("server shut down because of recovery target settings\n"));
1006 : 0 : break;
3093 tgl@sss.pgh.pa.us 1007 :CBC 13 : case POSTMASTER_FAILED:
5498 1008 : 13 : print_msg(_(" stopped waiting\n"));
1009 : 13 : write_stderr(_("%s: could not start server\n"
1010 : : "Examine the log output.\n"),
1011 : : progname);
1012 : 13 : exit(1);
1013 : : break;
1014 : : }
1015 : : }
1016 : : else
7121 peter_e@gmx.net 1017 :UBC 0 : print_msg(_("server starting\n"));
1018 : :
1019 : : #ifdef WIN32
1020 : : /* Now we don't need the handle to the shell process anymore */
1021 : : CloseHandle(postmasterProcess);
1022 : : postmasterProcess = INVALID_HANDLE_VALUE;
1023 : : #endif
7873 bruce@momjian.us 1024 :CBC 752 : }
1025 : :
1026 : :
1027 : : static void
1028 : 744 : do_stop(void)
1029 : : {
1030 : : pid_t pid;
1031 : :
4301 1032 : 744 : pid = get_pgpid(false);
1033 : :
7873 1034 [ + + ]: 744 : if (pid == 0) /* no pid file */
1035 : : {
7735 peter_e@gmx.net 1036 : 1 : write_stderr(_("%s: PID file \"%s\" does not exist\n"), progname, pid_file);
7121 1037 : 1 : write_stderr(_("Is server running?\n"));
7873 bruce@momjian.us 1038 : 1 : exit(1);
1039 : : }
1040 [ - + ]: 743 : else if (pid < 0) /* standalone backend, not postmaster */
1041 : : {
7873 bruce@momjian.us 1042 :UBC 0 : pid = -pid;
7121 peter_e@gmx.net 1043 : 0 : write_stderr(_("%s: cannot stop server; "
1044 : : "single-user server is running (PID: %d)\n"),
1045 : : progname, (int) pid);
7873 bruce@momjian.us 1046 : 0 : exit(1);
1047 : : }
1048 : :
1151 peter@eisentraut.org 1049 [ - + ]:CBC 743 : if (kill(pid, sig) != 0)
1050 : : {
644 michael@paquier.xyz 1051 :UBC 0 : write_stderr(_("%s: could not send stop signal (PID: %d): %m\n"), progname, (int) pid);
7873 bruce@momjian.us 1052 : 0 : exit(1);
1053 : : }
1054 : :
7873 bruce@momjian.us 1055 [ - + ]:CBC 743 : if (!do_wait)
1056 : : {
7121 peter_e@gmx.net 1057 :UBC 0 : print_msg(_("server shutting down\n"));
7873 bruce@momjian.us 1058 : 0 : return;
1059 : : }
1060 : : else
1061 : : {
3597 tgl@sss.pgh.pa.us 1062 :CBC 743 : print_msg(_("waiting for server to shut down..."));
1063 : :
1405 1064 [ - + ]: 743 : if (!wait_for_postmaster_stop())
1065 : : {
7732 neilc@samurai.com 1066 :UBC 0 : print_msg(_(" failed\n"));
1067 : :
3597 tgl@sss.pgh.pa.us 1068 : 0 : write_stderr(_("%s: server does not shut down\n"), progname);
5395 bruce@momjian.us 1069 [ # # ]: 0 : if (shutdown_mode == SMART_MODE)
5394 1070 : 0 : write_stderr(_("HINT: The \"-m fast\" option immediately disconnects sessions rather than\n"
1071 : : "waiting for session-initiated disconnection.\n"));
7873 1072 : 0 : exit(1);
1073 : : }
7732 neilc@samurai.com 1074 :CBC 743 : print_msg(_(" done\n"));
1075 : :
5779 peter_e@gmx.net 1076 : 743 : print_msg(_("server stopped\n"));
1077 : : }
1078 : : }
1079 : :
1080 : :
1081 : : /*
1082 : : * restart/reload routines
1083 : : */
1084 : :
1085 : : static void
7873 bruce@momjian.us 1086 : 101 : do_restart(void)
1087 : : {
1088 : : pid_t pid;
1089 : :
4301 1090 : 101 : pid = get_pgpid(false);
1091 : :
7873 1092 [ + + ]: 101 : if (pid == 0) /* no pid file */
1093 : : {
7531 tgl@sss.pgh.pa.us 1094 : 6 : write_stderr(_("%s: PID file \"%s\" does not exist\n"),
1095 : : progname, pid_file);
7121 peter_e@gmx.net 1096 : 6 : write_stderr(_("Is server running?\n"));
2966 1097 : 6 : write_stderr(_("trying to start server anyway\n"));
7873 bruce@momjian.us 1098 : 6 : do_start();
1099 : 5 : return;
1100 : : }
1101 [ - + ]: 95 : else if (pid < 0) /* standalone backend, not postmaster */
1102 : : {
7873 bruce@momjian.us 1103 :UBC 0 : pid = -pid;
1151 peter@eisentraut.org 1104 [ # # ]: 0 : if (postmaster_is_alive(pid))
1105 : : {
7121 peter_e@gmx.net 1106 : 0 : write_stderr(_("%s: cannot restart server; "
1107 : : "single-user server is running (PID: %d)\n"),
1108 : : progname, (int) pid);
1109 : 0 : write_stderr(_("Please terminate the single-user server and try again.\n"));
7531 tgl@sss.pgh.pa.us 1110 : 0 : exit(1);
1111 : : }
1112 : : }
1113 : :
1151 peter@eisentraut.org 1114 [ + - ]:CBC 95 : if (postmaster_is_alive(pid))
1115 : : {
1116 [ - + ]: 95 : if (kill(pid, sig) != 0)
1117 : : {
644 michael@paquier.xyz 1118 :UBC 0 : write_stderr(_("%s: could not send stop signal (PID: %d): %m\n"), progname, (int) pid);
7531 tgl@sss.pgh.pa.us 1119 : 0 : exit(1);
1120 : : }
1121 : :
7121 peter_e@gmx.net 1122 :CBC 95 : print_msg(_("waiting for server to shut down..."));
1123 : :
1124 : : /* always wait for restart */
1405 tgl@sss.pgh.pa.us 1125 [ - + ]: 95 : if (!wait_for_postmaster_stop())
1126 : : {
7531 tgl@sss.pgh.pa.us 1127 :UBC 0 : print_msg(_(" failed\n"));
1128 : :
7121 peter_e@gmx.net 1129 : 0 : write_stderr(_("%s: server does not shut down\n"), progname);
5395 bruce@momjian.us 1130 [ # # ]: 0 : if (shutdown_mode == SMART_MODE)
5394 1131 : 0 : write_stderr(_("HINT: The \"-m fast\" option immediately disconnects sessions rather than\n"
1132 : : "waiting for session-initiated disconnection.\n"));
7531 tgl@sss.pgh.pa.us 1133 : 0 : exit(1);
1134 : : }
1135 : :
7531 tgl@sss.pgh.pa.us 1136 :CBC 95 : print_msg(_(" done\n"));
5779 peter_e@gmx.net 1137 : 95 : print_msg(_("server stopped\n"));
1138 : : }
1139 : : else
1140 : : {
1151 peter@eisentraut.org 1141 :UBC 0 : write_stderr(_("%s: old server process (PID: %d) seems to be gone\n"),
1142 : : progname, (int) pid);
7121 peter_e@gmx.net 1143 : 0 : write_stderr(_("starting server anyway\n"));
1144 : : }
1145 : :
7873 bruce@momjian.us 1146 :CBC 95 : do_start();
1147 : : }
1148 : :
1149 : : static void
5305 peter_e@gmx.net 1150 : 112 : do_reload(void)
1151 : : {
1152 : : pid_t pid;
1153 : :
4301 bruce@momjian.us 1154 : 112 : pid = get_pgpid(false);
5305 peter_e@gmx.net 1155 [ - + ]: 112 : if (pid == 0) /* no pid file */
1156 : : {
5305 peter_e@gmx.net 1157 :UBC 0 : write_stderr(_("%s: PID file \"%s\" does not exist\n"), progname, pid_file);
1158 : 0 : write_stderr(_("Is server running?\n"));
1159 : 0 : exit(1);
1160 : : }
5305 peter_e@gmx.net 1161 [ - + ]:CBC 112 : else if (pid < 0) /* standalone backend, not postmaster */
1162 : : {
5305 peter_e@gmx.net 1163 :UBC 0 : pid = -pid;
1164 : 0 : write_stderr(_("%s: cannot reload server; "
1165 : : "single-user server is running (PID: %d)\n"),
1166 : : progname, (int) pid);
1167 : 0 : write_stderr(_("Please terminate the single-user server and try again.\n"));
1168 : 0 : exit(1);
1169 : : }
1170 : :
1151 peter@eisentraut.org 1171 [ - + ]:CBC 112 : if (kill(pid, sig) != 0)
1172 : : {
644 michael@paquier.xyz 1173 :UBC 0 : write_stderr(_("%s: could not send reload signal (PID: %d): %m\n"),
1174 : : progname, (int) pid);
5305 peter_e@gmx.net 1175 : 0 : exit(1);
1176 : : }
1177 : :
5305 peter_e@gmx.net 1178 :CBC 112 : print_msg(_("server signaled\n"));
1179 : 112 : }
1180 : :
1181 : :
1182 : : /*
1183 : : * promote
1184 : : */
1185 : :
1186 : : static void
5418 rhaas@postgresql.org 1187 : 43 : do_promote(void)
1188 : : {
1189 : : FILE *prmfile;
1190 : : pid_t pid;
1191 : :
4301 bruce@momjian.us 1192 : 43 : pid = get_pgpid(false);
1193 : :
5418 rhaas@postgresql.org 1194 [ + + ]: 42 : if (pid == 0) /* no pid file */
1195 : : {
1196 : 1 : write_stderr(_("%s: PID file \"%s\" does not exist\n"), progname, pid_file);
1197 : 1 : write_stderr(_("Is server running?\n"));
1198 : 1 : exit(1);
1199 : : }
1200 [ - + ]: 41 : else if (pid < 0) /* standalone backend, not postmaster */
1201 : : {
5418 rhaas@postgresql.org 1202 :UBC 0 : pid = -pid;
1203 : 0 : write_stderr(_("%s: cannot promote server; "
1204 : : "single-user server is running (PID: %d)\n"),
1205 : : progname, (int) pid);
1206 : 0 : exit(1);
1207 : : }
1208 : :
3430 peter_e@gmx.net 1209 [ + + ]:CBC 41 : if (get_control_dbstate() != DB_IN_ARCHIVE_RECOVERY)
1210 : : {
5418 rhaas@postgresql.org 1211 : 1 : write_stderr(_("%s: cannot promote server; "
1212 : : "server is not in standby mode\n"),
1213 : : progname);
1214 : 1 : exit(1);
1215 : : }
1216 : :
4502 heikki.linnakangas@i 1217 : 40 : snprintf(promote_file, MAXPGPATH, "%s/promote", pg_data);
1218 : :
5418 rhaas@postgresql.org 1219 [ - + ]: 40 : if ((prmfile = fopen(promote_file, "w")) == NULL)
1220 : : {
644 michael@paquier.xyz 1221 :UBC 0 : write_stderr(_("%s: could not create promote signal file \"%s\": %m\n"),
1222 : : progname, promote_file);
5418 rhaas@postgresql.org 1223 : 0 : exit(1);
1224 : : }
5418 rhaas@postgresql.org 1225 [ - + ]:CBC 40 : if (fclose(prmfile))
1226 : : {
644 michael@paquier.xyz 1227 :UBC 0 : write_stderr(_("%s: could not write promote signal file \"%s\": %m\n"),
1228 : : progname, promote_file);
5418 rhaas@postgresql.org 1229 : 0 : exit(1);
1230 : : }
1231 : :
5418 rhaas@postgresql.org 1232 :CBC 40 : sig = SIGUSR1;
1151 peter@eisentraut.org 1233 [ - + ]: 40 : if (kill(pid, sig) != 0)
1234 : : {
644 michael@paquier.xyz 1235 :UBC 0 : write_stderr(_("%s: could not send promote signal (PID: %d): %m\n"),
1236 : : progname, (int) pid);
5418 rhaas@postgresql.org 1237 [ # # ]: 0 : if (unlink(promote_file) != 0)
644 michael@paquier.xyz 1238 : 0 : write_stderr(_("%s: could not remove promote signal file \"%s\": %m\n"),
1239 : : progname, promote_file);
5418 rhaas@postgresql.org 1240 : 0 : exit(1);
1241 : : }
1242 : :
3420 peter_e@gmx.net 1243 [ + + ]:CBC 40 : if (do_wait)
1244 : : {
1245 : 39 : print_msg(_("waiting for server to promote..."));
1405 tgl@sss.pgh.pa.us 1246 [ + - ]: 39 : if (wait_for_postmaster_promote())
1247 : : {
3420 peter_e@gmx.net 1248 : 39 : print_msg(_(" done\n"));
1249 : 39 : print_msg(_("server promoted\n"));
1250 : : }
1251 : : else
1252 : : {
3420 peter_e@gmx.net 1253 :UBC 0 : print_msg(_(" stopped waiting\n"));
3151 1254 : 0 : write_stderr(_("%s: server did not promote in time\n"),
1255 : : progname);
1256 : 0 : exit(1);
1257 : : }
1258 : : }
1259 : : else
3420 peter_e@gmx.net 1260 :CBC 1 : print_msg(_("server promoting\n"));
5418 rhaas@postgresql.org 1261 : 40 : }
1262 : :
1263 : : /*
1264 : : * log rotate
1265 : : */
1266 : :
1267 : : static void
2663 akorotkov@postgresql 1268 : 1 : do_logrotate(void)
1269 : : {
1270 : : FILE *logrotatefile;
1271 : : pid_t pid;
1272 : :
1273 : 1 : pid = get_pgpid(false);
1274 : :
1275 [ - + ]: 1 : if (pid == 0) /* no pid file */
1276 : : {
2663 akorotkov@postgresql 1277 :UBC 0 : write_stderr(_("%s: PID file \"%s\" does not exist\n"), progname, pid_file);
1278 : 0 : write_stderr(_("Is server running?\n"));
1279 : 0 : exit(1);
1280 : : }
2663 akorotkov@postgresql 1281 [ - + ]:CBC 1 : else if (pid < 0) /* standalone backend, not postmaster */
1282 : : {
2663 akorotkov@postgresql 1283 :UBC 0 : pid = -pid;
1284 : 0 : write_stderr(_("%s: cannot rotate log file; "
1285 : : "single-user server is running (PID: %d)\n"),
1286 : : progname, (int) pid);
1287 : 0 : exit(1);
1288 : : }
1289 : :
2663 akorotkov@postgresql 1290 :CBC 1 : snprintf(logrotate_file, MAXPGPATH, "%s/logrotate", pg_data);
1291 : :
1292 [ - + ]: 1 : if ((logrotatefile = fopen(logrotate_file, "w")) == NULL)
1293 : : {
644 michael@paquier.xyz 1294 :UBC 0 : write_stderr(_("%s: could not create log rotation signal file \"%s\": %m\n"),
1295 : : progname, logrotate_file);
2663 akorotkov@postgresql 1296 : 0 : exit(1);
1297 : : }
2663 akorotkov@postgresql 1298 [ - + ]:CBC 1 : if (fclose(logrotatefile))
1299 : : {
644 michael@paquier.xyz 1300 :UBC 0 : write_stderr(_("%s: could not write log rotation signal file \"%s\": %m\n"),
1301 : : progname, logrotate_file);
2663 akorotkov@postgresql 1302 : 0 : exit(1);
1303 : : }
1304 : :
2663 akorotkov@postgresql 1305 :CBC 1 : sig = SIGUSR1;
1151 peter@eisentraut.org 1306 [ - + ]: 1 : if (kill(pid, sig) != 0)
1307 : : {
644 michael@paquier.xyz 1308 :UBC 0 : write_stderr(_("%s: could not send log rotation signal (PID: %d): %m\n"),
1309 : : progname, (int) pid);
2663 akorotkov@postgresql 1310 [ # # ]: 0 : if (unlink(logrotate_file) != 0)
644 michael@paquier.xyz 1311 : 0 : write_stderr(_("%s: could not remove log rotation signal file \"%s\": %m\n"),
1312 : : progname, logrotate_file);
2663 akorotkov@postgresql 1313 : 0 : exit(1);
1314 : : }
1315 : :
2663 akorotkov@postgresql 1316 :CBC 1 : print_msg(_("server signaled to rotate log file\n"));
1317 : 1 : }
1318 : :
1319 : :
1320 : : /*
1321 : : * utility routines
1322 : : */
1323 : :
1324 : : static bool
7545 tgl@sss.pgh.pa.us 1325 : 96 : postmaster_is_alive(pid_t pid)
1326 : : {
1327 : : /*
1328 : : * Test to see if the process is still there. Note that we do not
1329 : : * consider an EPERM failure to mean that the process is still there;
1330 : : * EPERM must mean that the given PID belongs to some other userid, and
1331 : : * considering the permissions on $PGDATA, that means it's not the
1332 : : * postmaster we are after.
1333 : : *
1334 : : * Don't believe that our own PID or parent shell's PID is the postmaster,
1335 : : * either. (Windows hasn't got getppid(), though.)
1336 : : */
1337 [ - + ]: 96 : if (pid == getpid())
7545 tgl@sss.pgh.pa.us 1338 :UBC 0 : return false;
1339 : : #ifndef WIN32
7545 tgl@sss.pgh.pa.us 1340 [ - + ]:CBC 96 : if (pid == getppid())
7545 tgl@sss.pgh.pa.us 1341 :UBC 0 : return false;
1342 : : #endif
7545 tgl@sss.pgh.pa.us 1343 [ + - ]:CBC 96 : if (kill(pid, 0) == 0)
1344 : 96 : return true;
7545 tgl@sss.pgh.pa.us 1345 :UBC 0 : return false;
1346 : : }
1347 : :
1348 : : static void
7873 bruce@momjian.us 1349 :CBC 3 : do_status(void)
1350 : : {
1351 : : pid_t pid;
1352 : :
4301 1353 : 3 : pid = get_pgpid(true);
1354 : : /* Is there a pid file? */
5178 1355 [ + + ]: 2 : if (pid != 0)
1356 : : {
1357 : : /* standalone backend? */
1358 [ - + ]: 1 : if (pid < 0)
1359 : : {
7545 tgl@sss.pgh.pa.us 1360 :UBC 0 : pid = -pid;
1151 peter@eisentraut.org 1361 [ # # ]: 0 : if (postmaster_is_alive(pid))
1362 : : {
1363 : 0 : printf(_("%s: single-user server is running (PID: %d)\n"),
1364 : : progname, (int) pid);
7545 tgl@sss.pgh.pa.us 1365 : 0 : return;
1366 : : }
1367 : : }
1368 : : else
1369 : : /* must be a postmaster */
1370 : : {
1151 peter@eisentraut.org 1371 [ + - ]:CBC 1 : if (postmaster_is_alive(pid))
1372 : : {
1373 : : char **optlines;
1374 : : char **curr_line;
1375 : : int numlines;
1376 : :
1377 : 1 : printf(_("%s: server is running (PID: %d)\n"),
1378 : : progname, (int) pid);
1379 : :
3093 tgl@sss.pgh.pa.us 1380 : 1 : optlines = readfile(postopts_file, &numlines);
7545 1381 [ + - ]: 1 : if (optlines != NULL)
1382 : : {
4304 sfrost@snowman.net 1383 [ + + ]: 2 : for (curr_line = optlines; *curr_line != NULL; curr_line++)
3093 tgl@sss.pgh.pa.us 1384 : 1 : puts(*curr_line);
1385 : :
1386 : : /* Free the results of readfile */
4308 sfrost@snowman.net 1387 : 1 : free_readfile(optlines);
1388 : : }
7545 tgl@sss.pgh.pa.us 1389 : 1 : return;
1390 : : }
1391 : : }
1392 : : }
7121 peter_e@gmx.net 1393 : 1 : printf(_("%s: no server running\n"), progname);
1394 : :
1395 : : /*
1396 : : * The Linux Standard Base Core Specification 3.1 says this should return
1397 : : * '3, program is not running'
1398 : : * https://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
1399 : : */
5178 bruce@momjian.us 1400 : 1 : exit(3);
1401 : : }
1402 : :
1403 : :
1404 : :
1405 : : static void
1151 peter@eisentraut.org 1406 : 9 : do_kill(pid_t pid)
1407 : : {
1408 [ - + ]: 9 : if (kill(pid, sig) != 0)
1409 : : {
644 michael@paquier.xyz 1410 :UBC 0 : write_stderr(_("%s: could not send signal %d (PID: %d): %m\n"),
1411 : : progname, sig, (int) pid);
7873 bruce@momjian.us 1412 : 0 : exit(1);
1413 : : }
7873 bruce@momjian.us 1414 :CBC 9 : }
1415 : :
1416 : : #ifdef WIN32
1417 : :
1418 : : static bool
1419 : : pgwin32_IsInstalled(SC_HANDLE hSCM)
1420 : : {
1421 : : SC_HANDLE hService = OpenService(hSCM, register_servicename, SERVICE_QUERY_CONFIG);
1422 : : bool bResult = (hService != NULL);
1423 : :
1424 : : if (bResult)
1425 : : CloseServiceHandle(hService);
1426 : : return bResult;
1427 : : }
1428 : :
1429 : : static char *
1430 : : pgwin32_CommandLine(bool registration)
1431 : : {
1432 : : PQExpBuffer cmdLine = createPQExpBuffer();
1433 : : char cmdPath[MAXPGPATH];
1434 : : int ret;
1435 : :
1436 : : if (registration)
1437 : : {
1438 : : ret = find_my_exec(argv0, cmdPath);
1439 : : if (ret != 0)
1440 : : {
1441 : : write_stderr(_("%s: could not find own program executable\n"), progname);
1442 : : exit(1);
1443 : : }
1444 : : }
1445 : : else
1446 : : {
1447 : : ret = find_other_exec(argv0, "postgres", PG_BACKEND_VERSIONSTR,
1448 : : cmdPath);
1449 : : if (ret != 0)
1450 : : {
1451 : : write_stderr(_("%s: could not find postgres program executable\n"), progname);
1452 : : exit(1);
1453 : : }
1454 : : }
1455 : :
1456 : : /* if path does not end in .exe, append it */
1457 : : if (strlen(cmdPath) < 4 ||
1458 : : pg_strcasecmp(cmdPath + strlen(cmdPath) - 4, ".exe") != 0)
1459 : : snprintf(cmdPath + strlen(cmdPath), sizeof(cmdPath) - strlen(cmdPath),
1460 : : ".exe");
1461 : :
1462 : : /* use backslashes in path to avoid problems with some third-party tools */
1463 : : make_native_path(cmdPath);
1464 : :
1465 : : /* be sure to double-quote the executable's name in the command */
1466 : : appendPQExpBuffer(cmdLine, "\"%s\"", cmdPath);
1467 : :
1468 : : /* append assorted switches to the command line, as needed */
1469 : :
1470 : : if (registration)
1471 : : appendPQExpBuffer(cmdLine, " runservice -N \"%s\"",
1472 : : register_servicename);
1473 : :
1474 : : if (pg_config)
1475 : : {
1476 : : /* We need the -D path to be absolute */
1477 : : char *dataDir;
1478 : :
1479 : : if ((dataDir = make_absolute_path(pg_config)) == NULL)
1480 : : {
1481 : : /* make_absolute_path already reported the error */
1482 : : exit(1);
1483 : : }
1484 : : make_native_path(dataDir);
1485 : : appendPQExpBuffer(cmdLine, " -D \"%s\"", dataDir);
1486 : : free(dataDir);
1487 : : }
1488 : :
1489 : : if (registration && event_source != NULL)
1490 : : appendPQExpBuffer(cmdLine, " -e \"%s\"", event_source);
1491 : :
1492 : : if (registration && do_wait)
1493 : : appendPQExpBufferStr(cmdLine, " -w");
1494 : :
1495 : : /* Don't propagate a value from an environment variable. */
1496 : : if (registration && wait_seconds_arg && wait_seconds != DEFAULT_WAIT)
1497 : : appendPQExpBuffer(cmdLine, " -t %d", wait_seconds);
1498 : :
1499 : : if (registration && silent_mode)
1500 : : appendPQExpBufferStr(cmdLine, " -s");
1501 : :
1502 : : if (post_opts)
1503 : : {
1504 : : if (registration)
1505 : : appendPQExpBuffer(cmdLine, " -o \"%s\"", post_opts);
1506 : : else
1507 : : appendPQExpBuffer(cmdLine, " %s", post_opts);
1508 : : }
1509 : :
1510 : : return cmdLine->data;
1511 : : }
1512 : :
1513 : : static void
1514 : : pgwin32_doRegister(void)
1515 : : {
1516 : : SC_HANDLE hService;
1517 : : SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1518 : :
1519 : : if (hSCM == NULL)
1520 : : {
1521 : : write_stderr(_("%s: could not open service manager\n"), progname);
1522 : : exit(1);
1523 : : }
1524 : : if (pgwin32_IsInstalled(hSCM))
1525 : : {
1526 : : CloseServiceHandle(hSCM);
1527 : : write_stderr(_("%s: service \"%s\" already registered\n"), progname, register_servicename);
1528 : : exit(1);
1529 : : }
1530 : :
1531 : : if ((hService = CreateService(hSCM, register_servicename, register_servicename,
1532 : : SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
1533 : : pgctl_start_type, SERVICE_ERROR_NORMAL,
1534 : : pgwin32_CommandLine(true),
1535 : : NULL, NULL, "RPCSS\0", register_username, register_password)) == NULL)
1536 : : {
1537 : : CloseServiceHandle(hSCM);
1538 : : write_stderr(_("%s: could not register service \"%s\": error code %lu\n"),
1539 : : progname, register_servicename,
1540 : : GetLastError());
1541 : : exit(1);
1542 : : }
1543 : : CloseServiceHandle(hService);
1544 : : CloseServiceHandle(hSCM);
1545 : : }
1546 : :
1547 : : static void
1548 : : pgwin32_doUnregister(void)
1549 : : {
1550 : : SC_HANDLE hService;
1551 : : SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1552 : :
1553 : : if (hSCM == NULL)
1554 : : {
1555 : : write_stderr(_("%s: could not open service manager\n"), progname);
1556 : : exit(1);
1557 : : }
1558 : : if (!pgwin32_IsInstalled(hSCM))
1559 : : {
1560 : : CloseServiceHandle(hSCM);
1561 : : write_stderr(_("%s: service \"%s\" not registered\n"), progname, register_servicename);
1562 : : exit(1);
1563 : : }
1564 : :
1565 : : if ((hService = OpenService(hSCM, register_servicename, DELETE)) == NULL)
1566 : : {
1567 : : CloseServiceHandle(hSCM);
1568 : : write_stderr(_("%s: could not open service \"%s\": error code %lu\n"),
1569 : : progname, register_servicename,
1570 : : GetLastError());
1571 : : exit(1);
1572 : : }
1573 : : if (!DeleteService(hService))
1574 : : {
1575 : : CloseServiceHandle(hService);
1576 : : CloseServiceHandle(hSCM);
1577 : : write_stderr(_("%s: could not unregister service \"%s\": error code %lu\n"),
1578 : : progname, register_servicename,
1579 : : GetLastError());
1580 : : exit(1);
1581 : : }
1582 : : CloseServiceHandle(hService);
1583 : : CloseServiceHandle(hSCM);
1584 : : }
1585 : :
1586 : : static void
1587 : : pgwin32_SetServiceStatus(DWORD currentState)
1588 : : {
1589 : : status.dwCurrentState = currentState;
1590 : : SetServiceStatus(hStatus, (LPSERVICE_STATUS) &status);
1591 : : }
1592 : :
1593 : : static void WINAPI
1594 : : pgwin32_ServiceHandler(DWORD request)
1595 : : {
1596 : : switch (request)
1597 : : {
1598 : : case SERVICE_CONTROL_STOP:
1599 : : case SERVICE_CONTROL_SHUTDOWN:
1600 : :
1601 : : /*
1602 : : * We only need a short wait hint here as it just needs to wait
1603 : : * for the next checkpoint. They occur every 5 seconds during
1604 : : * shutdown
1605 : : */
1606 : : status.dwWaitHint = 10000;
1607 : : pgwin32_SetServiceStatus(SERVICE_STOP_PENDING);
1608 : : SetEvent(shutdownEvent);
1609 : : return;
1610 : :
1611 : : case SERVICE_CONTROL_PAUSE:
1612 : : /* Win32 config reloading */
1613 : : status.dwWaitHint = 5000;
1614 : : kill(postmasterPID, SIGHUP);
1615 : : return;
1616 : :
1617 : : /* FIXME: These could be used to replace other signals etc */
1618 : : case SERVICE_CONTROL_CONTINUE:
1619 : : case SERVICE_CONTROL_INTERROGATE:
1620 : : default:
1621 : : break;
1622 : : }
1623 : : }
1624 : :
1625 : : static void WINAPI
1626 : : pgwin32_ServiceMain(DWORD argc, LPTSTR *argv)
1627 : : {
1628 : : PROCESS_INFORMATION pi;
1629 : : DWORD ret;
1630 : :
1631 : : /* Initialize variables */
1632 : : status.dwWin32ExitCode = S_OK;
1633 : : status.dwCheckPoint = 0;
1634 : : status.dwWaitHint = 60000;
1635 : : status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
1636 : : status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_PAUSE_CONTINUE;
1637 : : status.dwServiceSpecificExitCode = 0;
1638 : : status.dwCurrentState = SERVICE_START_PENDING;
1639 : :
1640 : : memset(&pi, 0, sizeof(pi));
1641 : :
1642 : : read_post_opts();
1643 : :
1644 : : /* Register the control request handler */
1645 : : if ((hStatus = RegisterServiceCtrlHandler(register_servicename, pgwin32_ServiceHandler)) == (SERVICE_STATUS_HANDLE) 0)
1646 : : return;
1647 : :
1648 : : if ((shutdownEvent = CreateEvent(NULL, true, false, NULL)) == NULL)
1649 : : return;
1650 : :
1651 : : /* Start the postmaster */
1652 : : pgwin32_SetServiceStatus(SERVICE_START_PENDING);
1653 : : if (!CreateRestrictedProcess(pgwin32_CommandLine(false), &pi, true))
1654 : : {
1655 : : pgwin32_SetServiceStatus(SERVICE_STOPPED);
1656 : : return;
1657 : : }
1658 : : postmasterPID = pi.dwProcessId;
1659 : : postmasterProcess = pi.hProcess;
1660 : : CloseHandle(pi.hThread);
1661 : :
1662 : : if (do_wait)
1663 : : {
1664 : : write_eventlog(EVENTLOG_INFORMATION_TYPE, _("Waiting for server startup...\n"));
1665 : : if (wait_for_postmaster_start(postmasterPID, true) != POSTMASTER_READY)
1666 : : {
1667 : : write_eventlog(EVENTLOG_ERROR_TYPE, _("Timed out waiting for server startup\n"));
1668 : : pgwin32_SetServiceStatus(SERVICE_STOPPED);
1669 : : return;
1670 : : }
1671 : : write_eventlog(EVENTLOG_INFORMATION_TYPE, _("Server started and accepting connections\n"));
1672 : : }
1673 : :
1674 : : pgwin32_SetServiceStatus(SERVICE_RUNNING);
1675 : :
1676 : : /* Wait for quit... */
1677 : : ret = WaitForMultipleObjects(2, shutdownHandles, FALSE, INFINITE);
1678 : :
1679 : : pgwin32_SetServiceStatus(SERVICE_STOP_PENDING);
1680 : : switch (ret)
1681 : : {
1682 : : case WAIT_OBJECT_0: /* shutdown event */
1683 : : {
1684 : : /*
1685 : : * status.dwCheckPoint can be incremented by
1686 : : * wait_for_postmaster_start(), so it might not start from 0.
1687 : : */
1688 : : int maxShutdownCheckPoint = status.dwCheckPoint + 12;
1689 : :
1690 : : kill(postmasterPID, SIGINT);
1691 : :
1692 : : /*
1693 : : * Increment the checkpoint and try again. Abort after 12
1694 : : * checkpoints as the postmaster has probably hung.
1695 : : */
1696 : : while (WaitForSingleObject(postmasterProcess, 5000) == WAIT_TIMEOUT && status.dwCheckPoint < maxShutdownCheckPoint)
1697 : : {
1698 : : status.dwCheckPoint++;
1699 : : SetServiceStatus(hStatus, (LPSERVICE_STATUS) &status);
1700 : : }
1701 : : break;
1702 : : }
1703 : :
1704 : : case (WAIT_OBJECT_0 + 1): /* postmaster went down */
1705 : : break;
1706 : :
1707 : : default:
1708 : : /* shouldn't get here? */
1709 : : break;
1710 : : }
1711 : :
1712 : : CloseHandle(shutdownEvent);
1713 : : CloseHandle(postmasterProcess);
1714 : :
1715 : : pgwin32_SetServiceStatus(SERVICE_STOPPED);
1716 : : }
1717 : :
1718 : : static void
1719 : : pgwin32_doRunAsService(void)
1720 : : {
1721 : : SERVICE_TABLE_ENTRY st[] = {{register_servicename, pgwin32_ServiceMain},
1722 : : {NULL, NULL}};
1723 : :
1724 : : if (StartServiceCtrlDispatcher(st) == 0)
1725 : : {
1726 : : write_stderr(_("%s: could not start service \"%s\": error code %lu\n"),
1727 : : progname, register_servicename,
1728 : : GetLastError());
1729 : : exit(1);
1730 : : }
1731 : : }
1732 : :
1733 : :
1734 : : /*
1735 : : * Set up STARTUPINFO for the new process to inherit this process' handles.
1736 : : *
1737 : : * Process started as services appear to have "empty" handles (GetStdHandle()
1738 : : * returns NULL) rather than invalid ones. But passing down NULL ourselves
1739 : : * doesn't work, it's interpreted as STARTUPINFO->hStd* not being set. But we
1740 : : * can pass down INVALID_HANDLE_VALUE - which makes GetStdHandle() in the new
1741 : : * process (and its child processes!) return INVALID_HANDLE_VALUE. Which
1742 : : * achieves the goal of postmaster running in a similar environment as pg_ctl.
1743 : : */
1744 : : static void
1745 : : InheritStdHandles(STARTUPINFO *si)
1746 : : {
1747 : : si->dwFlags |= STARTF_USESTDHANDLES;
1748 : : si->hStdInput = GetStdHandle(STD_INPUT_HANDLE);
1749 : : if (si->hStdInput == NULL)
1750 : : si->hStdInput = INVALID_HANDLE_VALUE;
1751 : : si->hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
1752 : : if (si->hStdOutput == NULL)
1753 : : si->hStdOutput = INVALID_HANDLE_VALUE;
1754 : : si->hStdError = GetStdHandle(STD_ERROR_HANDLE);
1755 : : if (si->hStdError == NULL)
1756 : : si->hStdError = INVALID_HANDLE_VALUE;
1757 : : }
1758 : :
1759 : : /*
1760 : : * Create a restricted token, a job object sandbox, and execute the specified
1761 : : * process with it.
1762 : : *
1763 : : * Returns 0 on success, non-zero on failure, same as CreateProcess().
1764 : : *
1765 : : * NOTE! Job object will only work when running as a service, because it's
1766 : : * automatically destroyed when pg_ctl exits.
1767 : : */
1768 : : static int
1769 : : CreateRestrictedProcess(char *cmd, PROCESS_INFORMATION *processInfo, bool as_service)
1770 : : {
1771 : : int r;
1772 : : BOOL b;
1773 : : STARTUPINFO si;
1774 : : HANDLE origToken;
1775 : : HANDLE restrictedToken;
1776 : : BOOL inJob;
1777 : : SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
1778 : : SID_AND_ATTRIBUTES dropSids[2];
1779 : : PTOKEN_PRIVILEGES delPrivs;
1780 : :
1781 : : ZeroMemory(&si, sizeof(si));
1782 : : si.cb = sizeof(si);
1783 : :
1784 : : /*
1785 : : * Set stdin/stdout/stderr handles to be inherited in the child process.
1786 : : * That allows postmaster and the processes it starts to perform
1787 : : * additional checks to see if running in a service (otherwise they get
1788 : : * the default console handles - which point to "somewhere").
1789 : : */
1790 : : InheritStdHandles(&si);
1791 : :
1792 : : /* Open the current token to use as a base for the restricted one */
1793 : : if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken))
1794 : : {
1795 : : /*
1796 : : * Most Windows targets make DWORD a 32-bit unsigned long, but in case
1797 : : * it doesn't cast DWORD before printing.
1798 : : */
1799 : : write_stderr(_("%s: could not open process token: error code %lu\n"),
1800 : : progname, GetLastError());
1801 : : return 0;
1802 : : }
1803 : :
1804 : : /* Allocate list of SIDs to remove */
1805 : : ZeroMemory(&dropSids, sizeof(dropSids));
1806 : : if (!AllocateAndInitializeSid(&NtAuthority, 2,
1807 : : SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0,
1808 : : 0, &dropSids[0].Sid) ||
1809 : : !AllocateAndInitializeSid(&NtAuthority, 2,
1810 : : SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0,
1811 : : 0, &dropSids[1].Sid))
1812 : : {
1813 : : write_stderr(_("%s: could not allocate SIDs: error code %lu\n"),
1814 : : progname, GetLastError());
1815 : : return 0;
1816 : : }
1817 : :
1818 : : /* Get list of privileges to remove */
1819 : : delPrivs = GetPrivilegesToDelete(origToken);
1820 : : if (delPrivs == NULL)
1821 : : /* Error message already printed */
1822 : : return 0;
1823 : :
1824 : : b = CreateRestrictedToken(origToken,
1825 : : 0,
1826 : : sizeof(dropSids) / sizeof(dropSids[0]),
1827 : : dropSids,
1828 : : delPrivs->PrivilegeCount, delPrivs->Privileges,
1829 : : 0, NULL,
1830 : : &restrictedToken);
1831 : :
1832 : : free(delPrivs);
1833 : : FreeSid(dropSids[1].Sid);
1834 : : FreeSid(dropSids[0].Sid);
1835 : : CloseHandle(origToken);
1836 : :
1837 : : if (!b)
1838 : : {
1839 : : write_stderr(_("%s: could not create restricted token: error code %lu\n"),
1840 : : progname, GetLastError());
1841 : : return 0;
1842 : : }
1843 : :
1844 : : AddUserToTokenDacl(restrictedToken);
1845 : : r = CreateProcessAsUser(restrictedToken, NULL, cmd, NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, NULL, &si, processInfo);
1846 : :
1847 : : if (IsProcessInJob(processInfo->hProcess, NULL, &inJob))
1848 : : {
1849 : : if (!inJob)
1850 : : {
1851 : : /*
1852 : : * Job objects are working, and the new process isn't in one, so
1853 : : * we can create one safely. If any problems show up when setting
1854 : : * it, we're going to ignore them.
1855 : : */
1856 : : HANDLE job;
1857 : : char jobname[128];
1858 : :
1859 : : sprintf(jobname, "PostgreSQL_%lu", processInfo->dwProcessId);
1860 : :
1861 : : job = CreateJobObject(NULL, jobname);
1862 : : if (job)
1863 : : {
1864 : : JOBOBJECT_BASIC_LIMIT_INFORMATION basicLimit;
1865 : : JOBOBJECT_BASIC_UI_RESTRICTIONS uiRestrictions;
1866 : : JOBOBJECT_SECURITY_LIMIT_INFORMATION securityLimit;
1867 : :
1868 : : ZeroMemory(&basicLimit, sizeof(basicLimit));
1869 : : ZeroMemory(&uiRestrictions, sizeof(uiRestrictions));
1870 : : ZeroMemory(&securityLimit, sizeof(securityLimit));
1871 : :
1872 : : basicLimit.LimitFlags = JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION | JOB_OBJECT_LIMIT_PRIORITY_CLASS;
1873 : : basicLimit.PriorityClass = NORMAL_PRIORITY_CLASS;
1874 : : SetInformationJobObject(job, JobObjectBasicLimitInformation, &basicLimit, sizeof(basicLimit));
1875 : :
1876 : : uiRestrictions.UIRestrictionsClass = JOB_OBJECT_UILIMIT_DESKTOP | JOB_OBJECT_UILIMIT_DISPLAYSETTINGS |
1877 : : JOB_OBJECT_UILIMIT_EXITWINDOWS | JOB_OBJECT_UILIMIT_READCLIPBOARD |
1878 : : JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS | JOB_OBJECT_UILIMIT_WRITECLIPBOARD;
1879 : :
1880 : : SetInformationJobObject(job, JobObjectBasicUIRestrictions, &uiRestrictions, sizeof(uiRestrictions));
1881 : :
1882 : : securityLimit.SecurityLimitFlags = JOB_OBJECT_SECURITY_NO_ADMIN | JOB_OBJECT_SECURITY_ONLY_TOKEN;
1883 : : securityLimit.JobToken = restrictedToken;
1884 : : SetInformationJobObject(job, JobObjectSecurityLimitInformation, &securityLimit, sizeof(securityLimit));
1885 : :
1886 : : AssignProcessToJobObject(job, processInfo->hProcess);
1887 : : }
1888 : : }
1889 : : }
1890 : :
1891 : : CloseHandle(restrictedToken);
1892 : :
1893 : : ResumeThread(processInfo->hThread);
1894 : :
1895 : : /*
1896 : : * We intentionally don't close the job object handle, because we want the
1897 : : * object to live on until pg_ctl shuts down.
1898 : : */
1899 : : return r;
1900 : : }
1901 : :
1902 : : /*
1903 : : * Get a list of privileges to delete from the access token. We delete all privileges
1904 : : * except SeLockMemoryPrivilege which is needed to use large pages, and
1905 : : * SeChangeNotifyPrivilege which is enabled by default in DISABLE_MAX_PRIVILEGE.
1906 : : */
1907 : : static PTOKEN_PRIVILEGES
1908 : : GetPrivilegesToDelete(HANDLE hToken)
1909 : : {
1910 : : int i,
1911 : : j;
1912 : : DWORD length;
1913 : : PTOKEN_PRIVILEGES tokenPrivs;
1914 : : LUID luidLockPages;
1915 : : LUID luidChangeNotify;
1916 : :
1917 : : if (!LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &luidLockPages) ||
1918 : : !LookupPrivilegeValue(NULL, SE_CHANGE_NOTIFY_NAME, &luidChangeNotify))
1919 : : {
1920 : : write_stderr(_("%s: could not get LUIDs for privileges: error code %lu\n"),
1921 : : progname, GetLastError());
1922 : : return NULL;
1923 : : }
1924 : :
1925 : : if (!GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &length) &&
1926 : : GetLastError() != ERROR_INSUFFICIENT_BUFFER)
1927 : : {
1928 : : write_stderr(_("%s: could not get token information: error code %lu\n"),
1929 : : progname, GetLastError());
1930 : : return NULL;
1931 : : }
1932 : :
1933 : : tokenPrivs = (PTOKEN_PRIVILEGES) pg_malloc_extended(length,
1934 : : MCXT_ALLOC_NO_OOM);
1935 : : if (tokenPrivs == NULL)
1936 : : {
1937 : : write_stderr(_("%s: out of memory\n"), progname);
1938 : : return NULL;
1939 : : }
1940 : :
1941 : : if (!GetTokenInformation(hToken, TokenPrivileges, tokenPrivs, length, &length))
1942 : : {
1943 : : write_stderr(_("%s: could not get token information: error code %lu\n"),
1944 : : progname, GetLastError());
1945 : : free(tokenPrivs);
1946 : : return NULL;
1947 : : }
1948 : :
1949 : : for (i = 0; i < tokenPrivs->PrivilegeCount; i++)
1950 : : {
1951 : : if (memcmp(&tokenPrivs->Privileges[i].Luid, &luidLockPages, sizeof(LUID)) == 0 ||
1952 : : memcmp(&tokenPrivs->Privileges[i].Luid, &luidChangeNotify, sizeof(LUID)) == 0)
1953 : : {
1954 : : for (j = i; j < tokenPrivs->PrivilegeCount - 1; j++)
1955 : : tokenPrivs->Privileges[j] = tokenPrivs->Privileges[j + 1];
1956 : : tokenPrivs->PrivilegeCount--;
1957 : : }
1958 : : }
1959 : :
1960 : : return tokenPrivs;
1961 : : }
1962 : : #endif /* WIN32 */
1963 : :
1964 : : static void
1965 : 1 : do_advice(void)
1966 : : {
7728 peter_e@gmx.net 1967 : 1 : write_stderr(_("Try \"%s --help\" for more information.\n"), progname);
7873 bruce@momjian.us 1968 : 1 : }
1969 : :
1970 : :
1971 : :
1972 : : static void
1973 : 1 : do_help(void)
1974 : : {
5305 peter_e@gmx.net 1975 : 1 : printf(_("%s is a utility to initialize, start, stop, or control a PostgreSQL server.\n\n"), progname);
7873 bruce@momjian.us 1976 : 1 : printf(_("Usage:\n"));
2663 akorotkov@postgresql 1977 : 1 : printf(_(" %s init[db] [-D DATADIR] [-s] [-o OPTIONS]\n"), progname);
1978 : 1 : printf(_(" %s start [-D DATADIR] [-l FILENAME] [-W] [-t SECS] [-s]\n"
1979 : : " [-o OPTIONS] [-p PATH] [-c]\n"), progname);
1980 : 1 : printf(_(" %s stop [-D DATADIR] [-m SHUTDOWN-MODE] [-W] [-t SECS] [-s]\n"), progname);
1981 : 1 : printf(_(" %s restart [-D DATADIR] [-m SHUTDOWN-MODE] [-W] [-t SECS] [-s]\n"
1982 : : " [-o OPTIONS] [-c]\n"), progname);
1983 : 1 : printf(_(" %s reload [-D DATADIR] [-s]\n"), progname);
1984 : 1 : printf(_(" %s status [-D DATADIR]\n"), progname);
1985 : 1 : printf(_(" %s promote [-D DATADIR] [-W] [-t SECS] [-s]\n"), progname);
1986 : 1 : printf(_(" %s logrotate [-D DATADIR] [-s]\n"), progname);
1987 : 1 : printf(_(" %s kill SIGNALNAME PID\n"), progname);
1988 : : #ifdef WIN32
1989 : : printf(_(" %s register [-D DATADIR] [-N SERVICENAME] [-U USERNAME] [-P PASSWORD]\n"
1990 : : " [-S START-TYPE] [-e SOURCE] [-W] [-t SECS] [-s] [-o OPTIONS]\n"), progname);
1991 : : printf(_(" %s unregister [-N SERVICENAME]\n"), progname);
1992 : : #endif
1993 : :
7728 peter_e@gmx.net 1994 : 1 : printf(_("\nCommon options:\n"));
4964 1995 : 1 : printf(_(" -D, --pgdata=DATADIR location of the database storage area\n"));
1996 : : #ifdef WIN32
1997 : : printf(_(" -e SOURCE event source for logging when running as a service\n"));
1998 : : #endif
3734 1999 : 1 : printf(_(" -s, --silent only print errors, no informational messages\n"));
4964 2000 : 1 : printf(_(" -t, --timeout=SECS seconds to wait when using -w option\n"));
4929 2001 : 1 : printf(_(" -V, --version output version information, then exit\n"));
3259 2002 : 1 : printf(_(" -w, --wait wait until operation completes (default)\n"));
3345 2003 : 1 : printf(_(" -W, --no-wait do not wait until operation completes\n"));
4929 2004 : 1 : printf(_(" -?, --help show this help, then exit\n"));
7728 2005 : 1 : printf(_("If the -D option is omitted, the environment variable PGDATA is used.\n"));
2006 : :
2007 : 1 : printf(_("\nOptions for start or restart:\n"));
2008 : : #if defined(HAVE_GETRLIMIT)
6920 andrew@dunslane.net 2009 : 1 : printf(_(" -c, --core-files allow postgres to produce core files\n"));
2010 : : #else
2011 : : printf(_(" -c, --core-files not applicable on this platform\n"));
2012 : : #endif
4964 peter_e@gmx.net 2013 : 1 : printf(_(" -l, --log=FILENAME write (or append) server log to FILENAME\n"));
3345 2014 : 1 : printf(_(" -o, --options=OPTIONS command line options to pass to postgres\n"
2015 : : " (PostgreSQL server executable) or initdb\n"));
6138 2016 : 1 : printf(_(" -p PATH-TO-POSTGRES normally not necessary\n"));
4142 fujii@postgresql.org 2017 : 1 : printf(_("\nOptions for stop or restart:\n"));
4964 peter_e@gmx.net 2018 : 1 : printf(_(" -m, --mode=MODE MODE can be \"smart\", \"fast\", or \"immediate\"\n"));
2019 : :
7728 2020 : 1 : printf(_("\nShutdown modes are:\n"));
7873 bruce@momjian.us 2021 : 1 : printf(_(" smart quit after all clients have disconnected\n"));
3162 tgl@sss.pgh.pa.us 2022 : 1 : printf(_(" fast quit directly, with proper shutdown (default)\n"));
7728 peter_e@gmx.net 2023 : 1 : printf(_(" immediate quit without complete shutdown; will lead to recovery on restart\n"));
2024 : :
2025 : 1 : printf(_("\nAllowed signal names for kill:\n"));
2998 andres@anarazel.de 2026 : 1 : printf(" ABRT HUP INT KILL QUIT TERM USR1 USR2\n");
2027 : :
2028 : : #ifdef WIN32
2029 : : printf(_("\nOptions for register and unregister:\n"));
2030 : : printf(_(" -N SERVICENAME service name with which to register PostgreSQL server\n"));
2031 : : printf(_(" -P PASSWORD password of account to register PostgreSQL server\n"));
2032 : : printf(_(" -U USERNAME user name of account to register PostgreSQL server\n"));
2033 : : printf(_(" -S START-TYPE service start type to register PostgreSQL server\n"));
2034 : :
2035 : : printf(_("\nStart types are:\n"));
2036 : : printf(_(" auto start service automatically during system startup (default)\n"));
2037 : : printf(_(" demand start service on demand\n"));
2038 : : #endif
2039 : :
2118 peter@eisentraut.org 2040 : 1 : printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
2041 : 1 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
7873 bruce@momjian.us 2042 : 1 : }
2043 : :
2044 : :
2045 : :
2046 : : static void
2047 : 635 : set_mode(char *modeopt)
2048 : : {
2049 [ + - + + ]: 635 : if (strcmp(modeopt, "s") == 0 || strcmp(modeopt, "smart") == 0)
2050 : : {
2051 : 44 : shutdown_mode = SMART_MODE;
2052 : 44 : sig = SIGTERM;
2053 : : }
2054 [ + - + + ]: 591 : else if (strcmp(modeopt, "f") == 0 || strcmp(modeopt, "fast") == 0)
2055 : : {
2056 : 271 : shutdown_mode = FAST_MODE;
2057 : 271 : sig = SIGINT;
2058 : : }
2059 [ + - + - ]: 320 : else if (strcmp(modeopt, "i") == 0 || strcmp(modeopt, "immediate") == 0)
2060 : : {
2061 : 320 : shutdown_mode = IMMEDIATE_MODE;
2062 : 320 : sig = SIGQUIT;
2063 : : }
2064 : : else
2065 : : {
7735 peter_e@gmx.net 2066 :UBC 0 : write_stderr(_("%s: unrecognized shutdown mode \"%s\"\n"), progname, modeopt);
7873 bruce@momjian.us 2067 : 0 : do_advice();
2068 : 0 : exit(1);
2069 : : }
7873 bruce@momjian.us 2070 :CBC 635 : }
2071 : :
2072 : :
2073 : :
2074 : : static void
2075 : 9 : set_sig(char *signame)
2076 : : {
5103 peter_e@gmx.net 2077 [ - + ]: 9 : if (strcmp(signame, "HUP") == 0)
7873 bruce@momjian.us 2078 :UBC 0 : sig = SIGHUP;
5103 peter_e@gmx.net 2079 [ + + ]:CBC 9 : else if (strcmp(signame, "INT") == 0)
7873 bruce@momjian.us 2080 : 4 : sig = SIGINT;
5103 peter_e@gmx.net 2081 [ + + ]: 5 : else if (strcmp(signame, "QUIT") == 0)
7873 bruce@momjian.us 2082 : 2 : sig = SIGQUIT;
5103 peter_e@gmx.net 2083 [ - + ]: 3 : else if (strcmp(signame, "ABRT") == 0)
7873 bruce@momjian.us 2084 :UBC 0 : sig = SIGABRT;
4937 bruce@momjian.us 2085 [ + - ]:CBC 3 : else if (strcmp(signame, "KILL") == 0)
5103 peter_e@gmx.net 2086 : 3 : sig = SIGKILL;
5103 peter_e@gmx.net 2087 [ # # ]:UBC 0 : else if (strcmp(signame, "TERM") == 0)
7873 bruce@momjian.us 2088 : 0 : sig = SIGTERM;
5103 peter_e@gmx.net 2089 [ # # ]: 0 : else if (strcmp(signame, "USR1") == 0)
7873 bruce@momjian.us 2090 : 0 : sig = SIGUSR1;
5103 peter_e@gmx.net 2091 [ # # ]: 0 : else if (strcmp(signame, "USR2") == 0)
7873 bruce@momjian.us 2092 : 0 : sig = SIGUSR2;
2093 : : else
2094 : : {
7735 peter_e@gmx.net 2095 : 0 : write_stderr(_("%s: unrecognized signal name \"%s\"\n"), progname, signame);
7873 bruce@momjian.us 2096 : 0 : do_advice();
2097 : 0 : exit(1);
2098 : : }
7873 bruce@momjian.us 2099 :CBC 9 : }
2100 : :
2101 : :
2102 : : #ifdef WIN32
2103 : : static void
2104 : : set_starttype(char *starttypeopt)
2105 : : {
2106 : : if (strcmp(starttypeopt, "a") == 0 || strcmp(starttypeopt, "auto") == 0)
2107 : : pgctl_start_type = SERVICE_AUTO_START;
2108 : : else if (strcmp(starttypeopt, "d") == 0 || strcmp(starttypeopt, "demand") == 0)
2109 : : pgctl_start_type = SERVICE_DEMAND_START;
2110 : : else
2111 : : {
2112 : : write_stderr(_("%s: unrecognized start type \"%s\"\n"), progname, starttypeopt);
2113 : : do_advice();
2114 : : exit(1);
2115 : : }
2116 : : }
2117 : : #endif
2118 : :
2119 : : /*
2120 : : * adjust_data_dir
2121 : : *
2122 : : * If a configuration-only directory was specified, find the real data dir.
2123 : : */
2124 : : static void
5185 2125 : 1679 : adjust_data_dir(void)
2126 : : {
2127 : : char filename[MAXPGPATH];
2128 : : char *my_exec_path,
2129 : : *cmd;
2130 : : FILE *fd;
2131 : :
2132 : : /* do nothing if we're working without knowledge of data dir */
4936 tgl@sss.pgh.pa.us 2133 [ + + ]: 1679 : if (pg_config == NULL)
2134 : 1679 : return;
2135 : :
2136 : : /* If there is no postgresql.conf, it can't be a config-only dir */
5185 bruce@momjian.us 2137 : 1670 : snprintf(filename, sizeof(filename), "%s/postgresql.conf", pg_config);
2138 [ + + ]: 1670 : if ((fd = fopen(filename, "r")) == NULL)
2139 : 4 : return;
2140 : 1666 : fclose(fd);
2141 : :
2142 : : /* If PG_VERSION exists, it can't be a config-only dir */
2143 : 1666 : snprintf(filename, sizeof(filename), "%s/PG_VERSION", pg_config);
2144 [ + - ]: 1666 : if ((fd = fopen(filename, "r")) != NULL)
2145 : : {
2146 : 1666 : fclose(fd);
2147 : 1666 : return;
2148 : : }
2149 : :
2150 : : /* Must be a configuration directory, so find the data directory */
2151 : :
2152 : : /* we use a private my_exec_path to avoid interfering with later uses */
5185 bruce@momjian.us 2153 [ # # ]:UBC 0 : if (exec_path == NULL)
2154 : 0 : my_exec_path = find_other_exec_or_die(argv0, "postgres", PG_BACKEND_VERSIONSTR);
2155 : : else
4823 tgl@sss.pgh.pa.us 2156 : 0 : my_exec_path = pg_strdup(exec_path);
2157 : :
2158 : : /* it's important for -C to be the first option, see main.c */
1565 2159 : 0 : cmd = psprintf("\"%s\" -C data_directory %s%s",
2160 : : my_exec_path,
2161 [ # # ]: 0 : pgdata_opt ? pgdata_opt : "",
2162 [ # # ]: 0 : post_opts ? post_opts : "");
1205 2163 : 0 : fflush(NULL);
2164 : :
5185 bruce@momjian.us 2165 : 0 : fd = popen(cmd, "r");
1127 peter@eisentraut.org 2166 [ # # # # : 0 : if (fd == NULL || fgets(filename, sizeof(filename), fd) == NULL || pclose(fd) != 0)
# # ]
2167 : : {
4915 peter_e@gmx.net 2168 : 0 : write_stderr(_("%s: could not determine the data directory using command \"%s\"\n"), progname, cmd);
5185 bruce@momjian.us 2169 : 0 : exit(1);
2170 : : }
2171 : 0 : free(my_exec_path);
2172 : :
2173 : : /* strip trailing newline and carriage return */
2321 michael@paquier.xyz 2174 : 0 : (void) pg_strip_crlf(filename);
2175 : :
5185 bruce@momjian.us 2176 : 0 : free(pg_data);
4823 tgl@sss.pgh.pa.us 2177 : 0 : pg_data = pg_strdup(filename);
5185 bruce@momjian.us 2178 : 0 : canonicalize_path(pg_data);
2179 : : }
2180 : :
2181 : :
2182 : : static DBState
3430 peter_e@gmx.net 2183 :CBC 155 : get_control_dbstate(void)
2184 : : {
2185 : : DBState ret;
2186 : : bool crc_ok;
2451 peter@eisentraut.org 2187 : 155 : ControlFileData *control_file_data = get_controlfile(pg_data, &crc_ok);
2188 : :
3366 peter_e@gmx.net 2189 [ - + ]: 155 : if (!crc_ok)
2190 : : {
3430 peter_e@gmx.net 2191 :UBC 0 : write_stderr(_("%s: control file appears to be corrupt\n"), progname);
2192 : 0 : exit(1);
2193 : : }
2194 : :
3366 peter_e@gmx.net 2195 :CBC 155 : ret = control_file_data->state;
2196 : 155 : pfree(control_file_data);
2197 : 155 : return ret;
2198 : : }
2199 : :
2200 : :
2201 : : int
7873 bruce@momjian.us 2202 : 1764 : main(int argc, char **argv)
2203 : : {
2204 : : static struct option long_options[] = {
2205 : : {"help", no_argument, NULL, '?'},
2206 : : {"version", no_argument, NULL, 'V'},
2207 : : {"log", required_argument, NULL, 'l'},
2208 : : {"mode", required_argument, NULL, 'm'},
2209 : : {"pgdata", required_argument, NULL, 'D'},
2210 : : {"options", required_argument, NULL, 'o'},
2211 : : {"silent", no_argument, NULL, 's'},
2212 : : {"timeout", required_argument, NULL, 't'},
2213 : : {"core-files", no_argument, NULL, 'c'},
2214 : : {"wait", no_argument, NULL, 'w'},
2215 : : {"no-wait", no_argument, NULL, 'W'},
2216 : : {NULL, 0, NULL, 0}
2217 : : };
2218 : :
2219 : : char *env_wait;
2220 : : int option_index;
2221 : : int c;
1151 peter@eisentraut.org 2222 : 1764 : pid_t killproc = 0;
2223 : :
2451 2224 : 1764 : pg_logging_init(argv[0]);
7873 bruce@momjian.us 2225 : 1764 : progname = get_progname(argv[0]);
6214 peter_e@gmx.net 2226 : 1764 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_ctl"));
5464 bruce@momjian.us 2227 : 1764 : start_time = time(NULL);
2228 : :
2229 : : /*
2230 : : * save argv[0] so do_start() can look for the postmaster if necessary. we
2231 : : * don't look for postmaster here because in many cases we won't need it.
2232 : : */
7873 2233 : 1764 : argv0 = argv[0];
2234 : :
2235 : : /* Set restrictive mode mask until PGDATA permissions are checked */
2810 sfrost@snowman.net 2236 : 1764 : umask(PG_MODE_MASK_OWNER);
2237 : :
2238 : : /* support --help and --version even if invoked as root */
7779 bruce@momjian.us 2239 [ + - ]: 1764 : if (argc > 1)
2240 : : {
4551 2241 [ + + - + ]: 1764 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
2242 : : {
7873 2243 : 1 : do_help();
2244 : 1 : exit(0);
2245 : : }
4551 2246 [ + + + + ]: 1763 : else if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
2247 : : {
7252 peter_e@gmx.net 2248 : 83 : puts("pg_ctl (PostgreSQL) " PG_VERSION);
7873 bruce@momjian.us 2249 : 83 : exit(0);
2250 : : }
2251 : : }
2252 : :
2253 : : /*
2254 : : * Disallow running as root, to forestall any possible security holes.
2255 : : */
2256 : : #ifndef WIN32
7725 tgl@sss.pgh.pa.us 2257 [ - + ]: 1680 : if (geteuid() == 0)
2258 : : {
7725 tgl@sss.pgh.pa.us 2259 :UBC 0 : write_stderr(_("%s: cannot be run as root\n"
2260 : : "Please log in (using, e.g., \"su\") as the "
2261 : : "(unprivileged) user that will\n"
2262 : : "own the server process.\n"),
2263 : : progname);
2264 : 0 : exit(1);
2265 : : }
2266 : : #endif
2267 : :
3597 noah@leadboat.com 2268 :CBC 1680 : env_wait = getenv("PGCTLTIMEOUT");
2269 [ - + ]: 1680 : if (env_wait != NULL)
3597 noah@leadboat.com 2270 :UBC 0 : wait_seconds = atoi(env_wait);
2271 : :
2272 : : /* process command-line options */
886 nathan@postgresql.or 2273 :CBC 6426 : while ((c = getopt_long(argc, argv, "cD:e:l:m:N:o:p:P:sS:t:U:wW",
2274 [ + + ]: 6426 : long_options, &option_index)) != -1)
2275 : : {
2276 [ + - + + : 4747 : switch (c)
- + - - +
- + - + +
- + ]
2277 : : {
2278 : 1670 : case 'D':
2279 : : {
2280 : : char *pgdata_D;
2281 : :
2282 : 1670 : pgdata_D = pg_strdup(optarg);
2283 : 1670 : canonicalize_path(pgdata_D);
2284 : 1670 : setenv("PGDATA", pgdata_D, 1);
2285 : :
2286 : : /*
2287 : : * We could pass PGDATA just in an environment variable
2288 : : * but we do -D too for clearer postmaster 'ps' display
2289 : : */
2290 : 1670 : pgdata_opt = psprintf("-D \"%s\" ", pgdata_D);
2291 : 1670 : free(pgdata_D);
7873 bruce@momjian.us 2292 : 1670 : break;
2293 : : }
886 nathan@postgresql.or 2294 :UBC 0 : case 'e':
2295 : 0 : event_source = pg_strdup(optarg);
2296 : 0 : break;
886 nathan@postgresql.or 2297 :CBC 788 : case 'l':
2298 : 788 : log_file = pg_strdup(optarg);
2299 : 788 : break;
2300 : 635 : case 'm':
2301 : 635 : set_mode(optarg);
2302 : 635 : break;
886 nathan@postgresql.or 2303 :UBC 0 : case 'N':
2304 : 0 : register_servicename = pg_strdup(optarg);
2305 : 0 : break;
886 nathan@postgresql.or 2306 :CBC 744 : case 'o':
2307 : : /* append option? */
2308 [ + + ]: 744 : if (!post_opts)
2309 : 704 : post_opts = pg_strdup(optarg);
2310 : : else
2311 : : {
2312 : 40 : char *old_post_opts = post_opts;
2313 : :
2314 : 40 : post_opts = psprintf("%s %s", old_post_opts, optarg);
2315 : 40 : free(old_post_opts);
2316 : : }
2317 : 744 : break;
886 nathan@postgresql.or 2318 :UBC 0 : case 'p':
2319 : 0 : exec_path = pg_strdup(optarg);
2320 : 0 : break;
2321 : 0 : case 'P':
2322 : 0 : register_password = pg_strdup(optarg);
2323 : 0 : break;
886 nathan@postgresql.or 2324 :CBC 117 : case 's':
2325 : 117 : silent_mode = true;
2326 : 117 : break;
886 nathan@postgresql.or 2327 :UBC 0 : case 'S':
2328 : : #ifdef WIN32
2329 : : set_starttype(optarg);
2330 : : #else
2331 : 0 : write_stderr(_("%s: -S option not supported on this platform\n"),
2332 : : progname);
2333 : 0 : exit(1);
2334 : : #endif
2335 : : break;
886 nathan@postgresql.or 2336 :CBC 1 : case 't':
2337 : 1 : wait_seconds = atoi(optarg);
2338 : 1 : wait_seconds_arg = true;
2339 : 1 : break;
886 nathan@postgresql.or 2340 :UBC 0 : case 'U':
2341 [ # # ]: 0 : if (strchr(optarg, '\\'))
2342 : 0 : register_username = pg_strdup(optarg);
2343 : : else
2344 : : /* Prepend .\ for local accounts */
2345 : 0 : register_username = psprintf(".\\%s", optarg);
2346 : 0 : break;
886 nathan@postgresql.or 2347 :CBC 790 : case 'w':
2348 : 790 : do_wait = true;
2349 : 790 : break;
2350 : 1 : case 'W':
2351 : 1 : do_wait = false;
2352 : 1 : break;
886 nathan@postgresql.or 2353 :UBC 0 : case 'c':
2354 : 0 : allow_core_files = true;
2355 : 0 : break;
886 nathan@postgresql.or 2356 :CBC 1 : default:
2357 : : /* getopt_long already issued a suitable error message */
2358 : 1 : do_advice();
2359 : 1 : exit(1);
2360 : : }
2361 : : }
2362 : :
2363 : : /* Process an action */
2364 [ + - ]: 1679 : if (optind < argc)
2365 : : {
2366 [ + - ]: 1679 : if (strcmp(argv[optind], "init") == 0
2367 [ + + ]: 1679 : || strcmp(argv[optind], "initdb") == 0)
2368 : 1 : ctl_command = INIT_COMMAND;
2369 [ + + ]: 1678 : else if (strcmp(argv[optind], "start") == 0)
2370 : 665 : ctl_command = START_COMMAND;
2371 [ + + ]: 1013 : else if (strcmp(argv[optind], "stop") == 0)
2372 : 744 : ctl_command = STOP_COMMAND;
2373 [ + + ]: 269 : else if (strcmp(argv[optind], "restart") == 0)
2374 : 101 : ctl_command = RESTART_COMMAND;
2375 [ + + ]: 168 : else if (strcmp(argv[optind], "reload") == 0)
2376 : 112 : ctl_command = RELOAD_COMMAND;
2377 [ + + ]: 56 : else if (strcmp(argv[optind], "status") == 0)
2378 : 3 : ctl_command = STATUS_COMMAND;
2379 [ + + ]: 53 : else if (strcmp(argv[optind], "promote") == 0)
2380 : 43 : ctl_command = PROMOTE_COMMAND;
2381 [ + + ]: 10 : else if (strcmp(argv[optind], "logrotate") == 0)
2382 : 1 : ctl_command = LOGROTATE_COMMAND;
2383 [ + - ]: 9 : else if (strcmp(argv[optind], "kill") == 0)
2384 : : {
2385 [ - + ]: 9 : if (argc - optind < 3)
2386 : : {
886 nathan@postgresql.or 2387 :UBC 0 : write_stderr(_("%s: missing arguments for kill mode\n"), progname);
7873 bruce@momjian.us 2388 : 0 : do_advice();
2389 : 0 : exit(1);
2390 : : }
886 nathan@postgresql.or 2391 :CBC 9 : ctl_command = KILL_COMMAND;
2392 : 9 : set_sig(argv[++optind]);
2393 : 9 : killproc = atol(argv[++optind]);
2394 : : }
2395 : : #ifdef WIN32
2396 : : else if (strcmp(argv[optind], "register") == 0)
2397 : : ctl_command = REGISTER_COMMAND;
2398 : : else if (strcmp(argv[optind], "unregister") == 0)
2399 : : ctl_command = UNREGISTER_COMMAND;
2400 : : else if (strcmp(argv[optind], "runservice") == 0)
2401 : : ctl_command = RUN_AS_SERVICE_COMMAND;
2402 : : #endif
2403 : : else
2404 : : {
886 nathan@postgresql.or 2405 :UBC 0 : write_stderr(_("%s: unrecognized operation mode \"%s\"\n"), progname, argv[optind]);
2406 : 0 : do_advice();
2407 : 0 : exit(1);
2408 : : }
886 nathan@postgresql.or 2409 :CBC 1679 : optind++;
2410 : : }
2411 : :
2412 [ - + ]: 1679 : if (optind < argc)
2413 : : {
886 nathan@postgresql.or 2414 :UBC 0 : write_stderr(_("%s: too many command-line arguments (first is \"%s\")\n"), progname, argv[optind]);
2415 : 0 : do_advice();
2416 : 0 : exit(1);
2417 : : }
2418 : :
7873 bruce@momjian.us 2419 [ - + ]:CBC 1679 : if (ctl_command == NO_COMMAND)
2420 : : {
7845 tgl@sss.pgh.pa.us 2421 :UBC 0 : write_stderr(_("%s: no operation specified\n"), progname);
7873 bruce@momjian.us 2422 : 0 : do_advice();
2423 : 0 : exit(1);
2424 : : }
2425 : :
2426 : : /* Note we put any -D switch into the env var above */
5185 bruce@momjian.us 2427 :CBC 1679 : pg_config = getenv("PGDATA");
2428 [ + + ]: 1679 : if (pg_config)
2429 : : {
4823 tgl@sss.pgh.pa.us 2430 : 1670 : pg_config = pg_strdup(pg_config);
5185 bruce@momjian.us 2431 : 1670 : canonicalize_path(pg_config);
4823 tgl@sss.pgh.pa.us 2432 : 1670 : pg_data = pg_strdup(pg_config);
2433 : : }
2434 : :
2435 : : /* -D might point at config-only directory; if so find the real PGDATA */
5185 bruce@momjian.us 2436 : 1679 : adjust_data_dir();
2437 : :
2438 : : /* Complain if -D needed and not provided */
2439 [ + + ]: 1679 : if (pg_config == NULL &&
7845 tgl@sss.pgh.pa.us 2440 [ - + - - ]: 9 : ctl_command != KILL_COMMAND && ctl_command != UNREGISTER_COMMAND)
2441 : : {
5541 alvherre@alvh.no-ip. 2442 :UBC 0 : write_stderr(_("%s: no database directory specified and environment variable PGDATA unset\n"),
2443 : : progname);
7873 bruce@momjian.us 2444 : 0 : do_advice();
2445 : 0 : exit(1);
2446 : : }
2447 : :
7873 bruce@momjian.us 2448 [ + + ]:CBC 1679 : if (ctl_command == RELOAD_COMMAND)
2449 : : {
2450 : 112 : sig = SIGHUP;
2451 : 112 : do_wait = false;
2452 : : }
2453 : :
7013 2454 [ + + ]: 1679 : if (pg_data)
2455 : : {
2456 : 1670 : snprintf(postopts_file, MAXPGPATH, "%s/postmaster.opts", pg_data);
4301 2457 : 1670 : snprintf(version_file, MAXPGPATH, "%s/PG_VERSION", pg_data);
7013 2458 : 1670 : snprintf(pid_file, MAXPGPATH, "%s/postmaster.pid", pg_data);
2459 : :
2460 : : /*
2461 : : * Set mask based on PGDATA permissions,
2462 : : *
2463 : : * Don't error here if the data directory cannot be stat'd. This is
2464 : : * handled differently based on the command and we don't want to
2465 : : * interfere with that logic.
2466 : : */
2810 sfrost@snowman.net 2467 [ + + ]: 1670 : if (GetDataDirectoryCreatePerm(pg_data))
2468 : 1666 : umask(pg_mode_mask);
2469 : : }
2470 : :
7873 bruce@momjian.us 2471 [ + + + + : 1679 : switch (ctl_command)
+ + + + +
- ]
2472 : : {
5850 peter_e@gmx.net 2473 : 1 : case INIT_COMMAND:
2474 : 1 : do_init();
2475 : 1 : break;
7873 bruce@momjian.us 2476 : 3 : case STATUS_COMMAND:
2477 : 3 : do_status();
2478 : 1 : break;
2479 : 665 : case START_COMMAND:
2480 : 665 : do_start();
2481 : 656 : break;
2482 : 744 : case STOP_COMMAND:
2483 : 744 : do_stop();
2484 : 743 : break;
2485 : 101 : case RESTART_COMMAND:
2486 : 101 : do_restart();
2487 : 96 : break;
2488 : 112 : case RELOAD_COMMAND:
2489 : 112 : do_reload();
2490 : 112 : break;
5305 peter_e@gmx.net 2491 : 43 : case PROMOTE_COMMAND:
2492 : 43 : do_promote();
2493 : 40 : break;
2663 akorotkov@postgresql 2494 : 1 : case LOGROTATE_COMMAND:
2495 : 1 : do_logrotate();
2496 : 1 : break;
7873 bruce@momjian.us 2497 : 9 : case KILL_COMMAND:
7869 2498 : 9 : do_kill(killproc);
7873 2499 : 9 : break;
2500 : : #ifdef WIN32
2501 : : case REGISTER_COMMAND:
2502 : : pgwin32_doRegister();
2503 : : break;
2504 : : case UNREGISTER_COMMAND:
2505 : : pgwin32_doUnregister();
2506 : : break;
2507 : : case RUN_AS_SERVICE_COMMAND:
2508 : : pgwin32_doRunAsService();
2509 : : break;
2510 : : #endif
7873 bruce@momjian.us 2511 :UBC 0 : default:
2512 : 0 : break;
2513 : : }
2514 : :
7873 bruce@momjian.us 2515 :CBC 1659 : exit(0);
2516 : : }
|