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;
  	}
