Index: src/bin/pg_ctl/pg_ctl.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/bin/pg_ctl/pg_ctl.c,v retrieving revision 1.16 diff -c -w -r1.16 pg_ctl.c *** src/bin/pg_ctl/pg_ctl.c 11 Jun 2004 16:36:31 -0000 1.16 --- src/bin/pg_ctl/pg_ctl.c 16 Jun 2004 13:41:38 -0000 *************** *** 52,58 **** RESTART_COMMAND, RELOAD_COMMAND, STATUS_COMMAND, ! KILL_COMMAND } CtlCommand; --- 52,61 ---- RESTART_COMMAND, RELOAD_COMMAND, STATUS_COMMAND, ! KILL_COMMAND, ! REGISTER_COMMAND, ! UNREGISTER_COMMAND, ! RUN_AS_SERVICE_COMMAND } CtlCommand; *************** *** 69,74 **** --- 72,80 ---- 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 *xmalloc(size_t size); *************** *** 83,88 **** --- 89,104 ---- 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); *************** *** 708,714 **** --- 724,951 ---- } } + #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) + { + fprintf(stderr, _("Unable to find exe")); + exit(1); + } + + if (registration) + { + 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) + { + fprintf(stderr,_("Unable to open service manager\n")); + exit(1); + } + if (pgwin32_IsInstalled(hSCM)) + { + CloseServiceHandle(hSCM); + fprintf(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); + fprintf(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) + { + fprintf(stderr,_("Unable to open service manager\n")); + exit(1); + } + if (!pgwin32_IsInstalled(hSCM)) + { + CloseServiceHandle(hSCM); + fprintf(stderr,_("Service \"%s\" not registered\n"),register_servicename); + exit(1); + } + + if ((hService = OpenService(hSCM, register_servicename, DELETE)) == NULL) + { + CloseServiceHandle(hSCM); + fprintf(stderr, _("Unable to open service \"%s\" [%d]\n"), register_servicename, (int)GetLastError()); + exit(1); + } + if (!DeleteService(hService)) { + CloseServiceHandle(hService); + CloseServiceHandle(hSCM); + fprintf(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; + 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) *************** *** 730,738 **** --- 967,984 ---- 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")); *************** *** 879,885 **** /* process command-line options */ while (optind < argc) { ! while ((c = getopt_long(argc, argv, "D:l:m:o:p:swW", long_options, &option_index)) != -1) { switch (c) { --- 1125,1131 ---- /* process command-line options */ while (optind < argc) { ! while ((c = getopt_long(argc, argv, "D:l:m:N:o:p:P:sU:wW", long_options, &option_index)) != -1) { switch (c) { *************** *** 901,915 **** --- 1147,1170 ---- 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': + register_username = xstrdup(optarg); + break; case 'w': do_wait = true; wait_set = true; *************** *** 957,962 **** --- 1212,1225 ---- 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]); *************** *** 975,983 **** } pg_data = getenv("PGDATA"); canonicalize_path(pg_data); ! if (pg_data == NULL && ctl_command != KILL_COMMAND) { fprintf(stderr, _("%s: no database directory specified " --- 1238,1247 ---- } pg_data = getenv("PGDATA"); + if (pg_data) canonicalize_path(pg_data); ! if (pg_data == NULL && ctl_command != KILL_COMMAND && ctl_command != UNREGISTER_COMMAND) { fprintf(stderr, _("%s: no database directory specified " *************** *** 1034,1039 **** --- 1298,1315 ---- 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; }