From f8dd00c3efcac3c245e5eb3989b2550f9894f574 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 24 Jun 2004 18:23:26 +0000 Subject: [PATCH] Extend pg_ctl to handle service management under WIN32. Lacks docs. Claudio Natoli and Magnus Hagander --- src/bin/pg_ctl/pg_ctl.c | 499 ++++++++++++++++++++++++++++++++++------ 1 file changed, 423 insertions(+), 76 deletions(-) diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c index 8eb7777774..4b06385c9a 100644 --- a/src/bin/pg_ctl/pg_ctl.c +++ b/src/bin/pg_ctl/pg_ctl.c @@ -4,7 +4,7 @@ * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/pg_ctl/pg_ctl.c,v 1.16 2004/06/11 16:36:31 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/pg_ctl/pg_ctl.c,v 1.17 2004/06/24 18:23:26 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -52,7 +52,10 @@ typedef enum RESTART_COMMAND, RELOAD_COMMAND, STATUS_COMMAND, - KILL_COMMAND + KILL_COMMAND, + REGISTER_COMMAND, + UNREGISTER_COMMAND, + RUN_AS_SERVICE_COMMAND } CtlCommand; @@ -63,14 +66,20 @@ static bool silence_echo = false; static ShutdownMode shutdown_mode = SMART_MODE; static int sig = SIGTERM; /* default */ static CtlCommand ctl_command = NO_COMMAND; -static char *pg_data_opts = NULL; static char *pg_data = NULL; static char *post_opts = NULL; static const char *progname; static char *log_file = NULL; static char *postgres_path = NULL; +static char *register_servicename = "PostgreSQL"; /* FIXME: + version ID? */ +static char *register_username = NULL; +static char *register_password = NULL; static char *argv0 = NULL; +static void write_stderr(const char *fmt,...) +/* This extension allows gcc to check the format string for consistency with + the supplied arguments. */ +__attribute__((format(printf, 1, 2))); static void *xmalloc(size_t size); static char *xstrdup(const char *s); static void do_advice(void); @@ -83,6 +92,16 @@ static void do_restart(void); static void do_reload(void); static void do_status(void); static void do_kill(pgpid_t pid); +#ifdef WIN32 +static bool pgwin32_IsInstalled(SC_HANDLE); +static char* pgwin32_CommandLine(bool); +static void pgwin32_doRegister(); +static void pgwin32_doUnregister(); +static void pgwin32_SetServiceStatus(DWORD); +static void WINAPI pgwin32_ServiceHandler(DWORD); +static void WINAPI pgwin32_ServiceMain(DWORD, LPTSTR*); +static void pgwin32_doRunAsService(); +#endif static pgpid_t get_pgpid(void); static char **readfile(char *path); static int start_postmaster(void); @@ -93,6 +112,63 @@ static char postopts_file[MAXPGPATH]; static char pid_file[MAXPGPATH]; static char conf_file[MAXPGPATH]; + +#ifdef WIN32 +static void +write_eventlog(int level, const char *line) +{ + static HANDLE evtHandle = INVALID_HANDLE_VALUE; + + if (evtHandle == INVALID_HANDLE_VALUE) { + evtHandle = RegisterEventSource(NULL,"PostgreSQL"); + if (evtHandle == NULL) { + evtHandle = INVALID_HANDLE_VALUE; + return; + } + } + + ReportEvent(evtHandle, + level, + 0, + 0, /* All events are Id 0 */ + NULL, + 1, + 0, + &line, + NULL); +} +#endif + +/* + * Write errors to stderr (or by equal means when stderr is + * not available). + */ +static void +write_stderr(const char *fmt,...) +{ + va_list ap; + + va_start(ap, fmt); +#ifndef WIN32 + /* On Unix, we just fprintf to stderr */ + vfprintf(stderr, fmt, ap); +#else + /* On Win32, we print to stderr if running on a console, or write to + * eventlog if running as a service */ + if (!isatty(fileno(stderr))) /* Running as a service */ + { + char errbuf[2048]; /* Arbitrary size? */ + + vsnprintf(errbuf, sizeof(errbuf), fmt, ap); + + write_eventlog(EVENTLOG_ERROR_TYPE, errbuf); + } + else /* Not running as service, write to stderr */ + vfprintf(stderr, fmt, ap); +#endif + va_end(ap); +} + /* * routines to check memory allocations and fail noisily. */ @@ -105,7 +181,7 @@ xmalloc(size_t size) result = malloc(size); if (!result) { - fprintf(stderr, _("%s: out of memory\n"), progname); + write_stderr(_("%s: out of memory\n"), progname); exit(1); } return result; @@ -121,7 +197,7 @@ xstrdup(const char *s) result = strdup(s); if (!result) { - fprintf(stderr, _("%s: out of memory\n"), progname); + write_stderr(_("%s: out of memory\n"), progname); exit(1); } return result; @@ -352,10 +428,9 @@ do_start(void) { old_pid = get_pgpid(); if (old_pid != 0) - fprintf(stderr, - _("%s: Another postmaster may be running. " - "Trying to start postmaster anyway.\n"), - progname); + write_stderr(_("%s: Another postmaster may be running. " + "Trying to start postmaster anyway.\n"), + progname); } if (post_opts == NULL) @@ -371,13 +446,13 @@ do_start(void) post_opts = ""; else { - fprintf(stderr, _("%s: cannot read %s\n"), progname, postopts_file); + write_stderr(_("%s: cannot read %s\n"), progname, postopts_file); exit(1); } } else if (optlines[0] == NULL || optlines[1] != NULL) { - fprintf(stderr, _("%s: option file %s must have exactly 1 line\n"), + write_stderr(_("%s: option file %s must have exactly 1 line\n"), progname, ctl_command == RESTART_COMMAND ? postopts_file : def_postopts_file); exit(1); @@ -419,18 +494,16 @@ do_start(void) postmaster_path)) < 0) { if (ret == -1) - fprintf(stderr, - _("The program \"postmaster\" is needed by %s " - "but was not found in the same directory as " - "\"%s\".\n" - "Check your installation.\n"), - progname, progname); + write_stderr(_("The program \"postmaster\" is needed by %s " + "but was not found in the same directory as " + "\"%s\".\n" + "Check your installation.\n"), + progname, progname); else - fprintf(stderr, - _("The program \"postmaster\" was found by %s " - "but was not the same version as \"%s\".\n" - "Check your installation.\n"), - progname, progname); + write_stderr(_("The program \"postmaster\" was found by %s " + "but was not the same version as \"%s\".\n" + "Check your installation.\n"), + progname, progname); exit(1); } postgres_path = postmaster_path; @@ -438,7 +511,7 @@ do_start(void) if (start_postmaster() != 0) { - fprintf(stderr, _("Unable to run the postmaster binary\n")); + write_stderr(_("Unable to run the postmaster binary\n")); exit(1); } @@ -448,10 +521,9 @@ do_start(void) pid = get_pgpid(); if (pid == old_pid) { - fprintf(stderr, - _("%s: cannot start postmaster\n" - "Examine the log output\n"), - progname); + write_stderr(_("%s: cannot start postmaster\n" + "Examine the log output\n"), + progname); exit(1); } } @@ -485,23 +557,22 @@ do_stop(void) if (pid == 0) /* no pid file */ { - fprintf(stderr, _("%s: could not find %s\n"), progname, pid_file); - fprintf(stderr, _("Is postmaster running?\n")); + write_stderr(_("%s: could not find %s\n"), progname, pid_file); + write_stderr(_("Is postmaster running?\n")); exit(1); } else if (pid < 0) /* standalone backend, not postmaster */ { pid = -pid; - fprintf(stderr, - _("%s: cannot stop postmaster; " - "postgres is running (PID: %ld)\n"), - progname, pid); + write_stderr(_("%s: cannot stop postmaster; " + "postgres is running (PID: %ld)\n"), + progname, pid); exit(1); } if (kill((pid_t) pid, sig) != 0) { - fprintf(stderr, _("stop signal failed (PID: %ld): %s\n"), pid, + write_stderr(_("stop signal failed (PID: %ld): %s\n"), pid, strerror(errno)); exit(1); } @@ -540,7 +611,7 @@ do_stop(void) if (!silence_echo) printf(_(" failed\n")); - fprintf(stderr, _("%s: postmaster does not shut down\n"), progname); + write_stderr(_("%s: postmaster does not shut down\n"), progname); exit(1); } if (!silence_echo) @@ -565,25 +636,24 @@ do_restart(void) if (pid == 0) /* no pid file */ { - fprintf(stderr, _("%s: could not find %s\n"), progname, pid_file); - fprintf(stderr, _("Is postmaster running?\nstarting postmaster anyway\n")); + write_stderr(_("%s: could not find %s\n"), progname, pid_file); + write_stderr(_("Is postmaster running?\nstarting postmaster anyway\n")); do_start(); return; } else if (pid < 0) /* standalone backend, not postmaster */ { pid = -pid; - fprintf(stderr, - _("%s: cannot restart postmaster; " - "postgres is running (PID: %ld)\n"), - progname, pid); - fprintf(stderr, _("Please terminate postgres and try again.\n")); + write_stderr(_("%s: cannot restart postmaster; " + "postgres is running (PID: %ld)\n"), + progname, pid); + write_stderr(_("Please terminate postgres and try again.\n")); exit(1); } if (kill((pid_t) pid, sig) != 0) { - fprintf(stderr, _("stop signal failed (PID: %ld): %s\n"), pid, + write_stderr(_("stop signal failed (PID: %ld): %s\n"), pid, strerror(errno)); exit(1); } @@ -616,7 +686,7 @@ do_restart(void) if (!silence_echo) printf(_(" failed\n")); - fprintf(stderr, _("%s: postmaster does not shut down\n"), progname); + write_stderr(_("%s: postmaster does not shut down\n"), progname); exit(1); } @@ -636,24 +706,23 @@ do_reload(void) pid = get_pgpid(); if (pid == 0) /* no pid file */ { - fprintf(stderr, _("%s: could not find %s\n"), progname, pid_file); - fprintf(stderr, _("Is postmaster running?\n")); + write_stderr(_("%s: could not find %s\n"), progname, pid_file); + write_stderr(_("Is postmaster running?\n")); exit(1); } else if (pid < 0) /* standalone backend, not postmaster */ { pid = -pid; - fprintf(stderr, - _("%s: cannot reload postmaster; " - "postgres is running (PID: %ld)\n"), - progname, pid); - fprintf(stderr, _("Please terminate postgres and try again.\n")); + write_stderr(_("%s: cannot reload postmaster; " + "postgres is running (PID: %ld)\n"), + progname, pid); + write_stderr(_("Please terminate postgres and try again.\n")); exit(1); } if (kill((pid_t) pid, sig) != 0) { - fprintf(stderr, _("reload signal failed (PID: %ld): %s\n"), pid, + write_stderr(_("reload signal failed (PID: %ld): %s\n"), pid, strerror(errno)); exit(1); } @@ -674,7 +743,7 @@ do_status(void) pid = get_pgpid(); if (pid == 0) /* no pid file */ { - fprintf(stderr, _("%s: postmaster or postgres not running\n"), progname); + write_stderr(_("%s: postmaster or postgres not running\n"), progname); exit(1); } else if (pid < 0) /* standalone backend */ @@ -702,18 +771,244 @@ do_kill(pgpid_t pid) { if (kill((pid_t) pid, sig) != 0) { - fprintf(stderr, _("signal %d failed (PID: %ld): %s\n"), sig, pid, + write_stderr(_("signal %d failed (PID: %ld): %s\n"), sig, pid, strerror(errno)); exit(1); } } +#ifdef WIN32 +static bool pgwin32_IsInstalled(SC_HANDLE hSCM) +{ + SC_HANDLE hService = OpenService(hSCM, register_servicename, SERVICE_QUERY_CONFIG); + bool bResult = (hService != NULL); + if (bResult) + CloseServiceHandle(hService); + return bResult; +} + +static char* pgwin32_CommandLine(bool registration) +{ + static char cmdLine[MAXPGPATH]; + int ret; + if (registration) + ret = find_my_exec(argv0, cmdLine); + else + ret = find_other_exec(argv0, "postmaster", PM_VERSIONSTR, cmdLine); + if (ret != 0) + { + write_stderr(_("Unable to find exe")); + exit(1); + } + + if (registration) + { + if (strcasecmp(cmdLine+strlen(cmdLine)-4,".exe")) + { + /* If commandline does not end in .exe, append it */ + strcat(cmdLine,".exe"); + } + strcat(cmdLine," runservice -N \""); + strcat(cmdLine,register_servicename); + strcat(cmdLine,"\""); + } + + if (pg_data) + { + strcat(cmdLine," -D \""); + strcat(cmdLine,pg_data); + strcat(cmdLine,"\""); + } + + if (post_opts) + { + strcat(cmdLine," "); + if (registration) + strcat(cmdLine," -o \""); + strcat(cmdLine,post_opts); + if (registration) + strcat(cmdLine,"\""); + } + + return cmdLine; +} + +static void +pgwin32_doRegister() +{ + SC_HANDLE hService; + SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (hSCM == NULL) + { + write_stderr(_("Unable to open service manager\n")); + exit(1); + } + if (pgwin32_IsInstalled(hSCM)) + { + CloseServiceHandle(hSCM); + write_stderr(_("Service \"%s\" already registered\n"),register_servicename); + exit(1); + } + + if ((hService = CreateService(hSCM, register_servicename, register_servicename, + SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, + SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, + pgwin32_CommandLine(true), + NULL, NULL, "RPCSS\0", register_username, register_password)) == NULL) + { + CloseServiceHandle(hSCM); + write_stderr(_("Unable to register service \"%s\" [%d]\n"), register_servicename, (int)GetLastError()); + exit(1); + } + CloseServiceHandle(hService); + CloseServiceHandle(hSCM); +} + +static void +pgwin32_doUnregister() +{ + SC_HANDLE hService; + SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); + if (hSCM == NULL) + { + write_stderr(_("Unable to open service manager\n")); + exit(1); + } + if (!pgwin32_IsInstalled(hSCM)) + { + CloseServiceHandle(hSCM); + write_stderr(_("Service \"%s\" not registered\n"),register_servicename); + exit(1); + } + + if ((hService = OpenService(hSCM, register_servicename, DELETE)) == NULL) + { + CloseServiceHandle(hSCM); + write_stderr(_("Unable to open service \"%s\" [%d]\n"), register_servicename, (int)GetLastError()); + exit(1); + } + if (!DeleteService(hService)) { + CloseServiceHandle(hService); + CloseServiceHandle(hSCM); + write_stderr(_("Unable to unregister service \"%s\" [%d]\n"), register_servicename, (int)GetLastError()); + exit(1); + } + CloseServiceHandle(hService); + CloseServiceHandle(hSCM); +} + + +static SERVICE_STATUS status; +static SERVICE_STATUS_HANDLE hStatus = (SERVICE_STATUS_HANDLE)0; +static HANDLE shutdownHandles[2]; +static pid_t postmasterPID = -1; +#define shutdownEvent shutdownHandles[0] +#define postmasterProcess shutdownHandles[1] + +static void pgwin32_SetServiceStatus(DWORD currentState) +{ + status.dwCurrentState = currentState; + SetServiceStatus(hStatus, (LPSERVICE_STATUS)&status); +} + +static void WINAPI pgwin32_ServiceHandler(DWORD request) +{ + switch (request) + { + case SERVICE_CONTROL_STOP: + case SERVICE_CONTROL_SHUTDOWN: + pgwin32_SetServiceStatus(SERVICE_STOP_PENDING); + SetEvent(shutdownEvent); + return; + + case SERVICE_CONTROL_PAUSE: + /* Win32 config reloading */ + kill(postmasterPID,SIGHUP); + return; + + /* FIXME: These could be used to replace other signals etc */ + case SERVICE_CONTROL_CONTINUE: + case SERVICE_CONTROL_INTERROGATE: + default: + break; + } +} + +static void WINAPI pgwin32_ServiceMain(DWORD argc, LPTSTR *argv) +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + DWORD ret; + + /* Initialize variables */ + status.dwWin32ExitCode = S_OK; + status.dwCheckPoint = 0; + status.dwWaitHint = 0; + status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + status.dwControlsAccepted = SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_PAUSE_CONTINUE; + status.dwServiceSpecificExitCode = 0; + status.dwCurrentState = SERVICE_START_PENDING; + + memset(&pi,0,sizeof(pi)); + memset(&si,0,sizeof(si)); + si.cb = sizeof(si); + + /* Register the control request handler */ + if ((hStatus = RegisterServiceCtrlHandler(register_servicename, pgwin32_ServiceHandler)) == (SERVICE_STATUS_HANDLE)0) + return; + + if ((shutdownEvent = CreateEvent(NULL,true,false,NULL)) == NULL) + return; + + /* Start the postmaster */ + pgwin32_SetServiceStatus(SERVICE_START_PENDING); + if (!CreateProcess(NULL,pgwin32_CommandLine(false),NULL,NULL,TRUE,0,NULL,NULL,&si,&pi)) + { + pgwin32_SetServiceStatus(SERVICE_STOPPED); + return; + } + postmasterPID = pi.dwProcessId; + postmasterProcess = pi.hProcess; + CloseHandle(pi.hThread); + pgwin32_SetServiceStatus(SERVICE_RUNNING); + + /* Wait for quit... */ + ret = WaitForMultipleObjects(2,shutdownHandles,FALSE,INFINITE); + pgwin32_SetServiceStatus(SERVICE_STOP_PENDING); + switch (ret) + { + case WAIT_OBJECT_0: /* shutdown event */ + kill(postmasterPID,SIGINT); + WaitForSingleObject(postmasterProcess,INFINITE); + break; + + case (WAIT_OBJECT_0+1): /* postmaster went down */ + break; + + default: + /* assert(false); */ + } + + CloseHandle(shutdownEvent); + CloseHandle(postmasterProcess); + + pgwin32_SetServiceStatus(SERVICE_STOPPED); +} + +static void pgwin32_doRunAsService() +{ + SERVICE_TABLE_ENTRY st[] = {{ register_servicename, pgwin32_ServiceMain }, + { NULL, NULL }}; + StartServiceCtrlDispatcher(st); +} + +#endif static void do_advice(void) { - fprintf(stderr, _("\nTry \"%s --help\" for more information.\n"), progname); + write_stderr(_("\nTry \"%s --help\" for more information.\n"), progname); } @@ -730,9 +1025,18 @@ do_help(void) printf(_(" %s reload [-D DATADIR] [-s]\n"), progname); printf(_(" %s status [-D DATADIR]\n"), progname); printf(_(" %s kill SIGNALNAME PROCESSID\n"), progname); +#ifdef WIN32 + printf(_(" %s register [-N servicename] [-U username] [-P password] [-D DATADIR] [-o \"OPTIONS\"]\n"), progname); + printf(_(" %s unregister [-N servicename]\n"), progname); +#endif printf(_("Common options:\n")); printf(_(" -D, --pgdata DATADIR location of the database storage area\n")); printf(_(" -s, --silent only print errors, no informational messages\n")); +#ifdef WIN32 + printf(_(" -N service name with which to register PostgreSQL server\n")); + printf(_(" -P user name of account to register PostgreSQL server\n")); + printf(_(" -U password of account to register PostgreSQL server\n")); +#endif printf(_(" -w wait until operation completes\n")); printf(_(" -W do not wait until operation completes\n")); printf(_(" --help show this help, then exit\n")); @@ -778,7 +1082,7 @@ set_mode(char *modeopt) } else { - fprintf(stderr, _("%s: invalid shutdown mode %s\n"), progname, modeopt); + write_stderr(_("%s: invalid shutdown mode %s\n"), progname, modeopt); do_advice(); exit(1); } @@ -811,7 +1115,7 @@ set_sig(char *signame) sig = SIGUSR2; else { - fprintf(stderr, _("%s: invalid signal \"%s\"\n"), progname, signame); + write_stderr(_("%s: invalid signal \"%s\"\n"), progname, signame); do_advice(); exit(1); } @@ -879,19 +1183,17 @@ main(int argc, char **argv) /* process command-line options */ while (optind < argc) { - while ((c = getopt_long(argc, argv, "D:l:m:o:p:swW", long_options, &option_index)) != -1) + while ((c = getopt_long(argc, argv, "D:l:m:N:o:p:P:sU:wW", long_options, &option_index)) != -1) { switch (c) { case 'D': { - int len = strlen(optarg) + 4; + int len = strlen(optarg); char *env_var; - pg_data_opts = xmalloc(len); - snprintf(pg_data_opts, len, "-D %s", optarg); - env_var = xmalloc(len + sizeof("PGDATA=")); - snprintf(env_var, len + sizeof("PGDATA="), "PGDATA=%s", optarg); + env_var = xmalloc(len + 8); + snprintf(env_var, len + 8, "PGDATA=%s", optarg); putenv(env_var); break; } @@ -901,15 +1203,36 @@ main(int argc, char **argv) case 'm': set_mode(optarg); break; + case 'N': + register_servicename = xstrdup(optarg); + break; case 'o': post_opts = xstrdup(optarg); break; case 'p': postgres_path = xstrdup(optarg); break; + case 'P': + register_password = xstrdup(optarg); + break; case 's': silence_echo = true; break; + case 'U': + if (strchr(optarg,'\\')) + register_username = xstrdup(optarg); + else /* Prepend .\ for local accounts */ + { + register_username = malloc(strlen(optarg)+3); + if (!register_username) + { + write_stderr(_("%s: out of memory\n"), progname); + exit(1); + } + strcpy(register_username,".\\"); + strcat(register_username,optarg); + } + break; case 'w': do_wait = true; wait_set = true; @@ -919,7 +1242,7 @@ main(int argc, char **argv) wait_set = true; break; default: - fprintf(stderr, _("%s: invalid option %s\n"), progname, optarg); + write_stderr(_("%s: invalid option %s\n"), progname, optarg); do_advice(); exit(1); } @@ -930,7 +1253,7 @@ main(int argc, char **argv) { if (ctl_command != NO_COMMAND) { - fprintf(stderr, _("%s: extra operation mode %s\n"), progname, argv[optind]); + write_stderr(_("%s: extra operation mode %s\n"), progname, argv[optind]); do_advice(); exit(1); } @@ -949,7 +1272,7 @@ main(int argc, char **argv) { if (argc - optind < 3) { - fprintf(stderr, _("%s: invalid kill syntax\n"), progname); + write_stderr(_("%s: invalid kill syntax\n"), progname); do_advice(); exit(1); } @@ -957,32 +1280,45 @@ main(int argc, char **argv) set_sig(argv[++optind]); killproc = atol(argv[++optind]); } +#ifdef WIN32 + else if (strcmp(argv[optind], "register") == 0) + ctl_command = REGISTER_COMMAND; + else if (strcmp(argv[optind], "unregister") == 0) + ctl_command = UNREGISTER_COMMAND; + else if (strcmp(argv[optind], "runservice") == 0) + ctl_command = RUN_AS_SERVICE_COMMAND; +#endif else { - fprintf(stderr, _("%s: invalid operation mode %s\n"), progname, argv[optind]); + write_stderr(_("%s: invalid operation mode %s\n"), progname, argv[optind]); do_advice(); exit(1); } optind++; } } - + if (ctl_command == NO_COMMAND) { - fprintf(stderr, _("%s: no operation specified\n"), progname); + write_stderr(_("%s: no operation specified\n"), progname); do_advice(); exit(1); } + /* Note we put any -D switch into the env var above */ pg_data = getenv("PGDATA"); - canonicalize_path(pg_data); - - if (pg_data == NULL && ctl_command != KILL_COMMAND) + if (pg_data) { - fprintf(stderr, - _("%s: no database directory specified " - "and environment variable PGDATA unset\n"), - progname); + /* XXX modifies environment var in-place ... ugly ... */ + canonicalize_path(pg_data); + } + + if (pg_data == NULL && + ctl_command != KILL_COMMAND && ctl_command != UNREGISTER_COMMAND) + { + write_stderr(_("%s: no database directory specified " + "and environment variable PGDATA unset\n"), + progname); do_advice(); exit(1); } @@ -1034,6 +1370,17 @@ main(int argc, char **argv) case KILL_COMMAND: do_kill(killproc); break; +#ifdef WIN32 + case REGISTER_COMMAND: + pgwin32_doRegister(); + break; + case UNREGISTER_COMMAND: + pgwin32_doUnregister(); + break; + case RUN_AS_SERVICE_COMMAND: + pgwin32_doRunAsService(); + break; +#endif default: break; }