client_login_timeout: check wait_for_welcome
[pgbouncer.git] / win32 / win32support.c
blobbae81d985db650b4669b02467a5abe6f81e10a21
1 /*-------------------------------------------------------------------------
2 * win32service.c
4 * Windows service integration and eventlog
6 * Copyright (c) 2005, PostgreSQL Global Development Group
7 * Authors: Magnus Hagander, Hiroshi Saito, Marko Kreen
8 *-------------------------------------------------------------------------
9 */
11 #define WIN32_LEAN_AND_MEAN
12 #include <windows.h>
13 #include <winsock2.h>
14 #include <stdio.h>
15 #include <stdlib.h>
17 #include "bouncer.h"
19 #if defined(UNICODE) || defined(_UNICODE)
20 #error This code does not support wide characters.
21 #endif
23 /* Globals for service control */
24 static SERVICE_STATUS_HANDLE hStatus = 0;
25 static SERVICE_STATUS svcStatus = {
26 .dwServiceType = SERVICE_WIN32_OWN_PROCESS,
27 .dwControlsAccepted = 0,
28 .dwWin32ExitCode = NO_ERROR,
29 .dwCheckPoint = 0,
30 .dwWaitHint = 0,
31 .dwCurrentState = SERVICE_START_PENDING,
34 /* Event source name for ReportEvent.
36 * Also used as placeholder for service handling API's, but it is ignored
37 * because our service is defined as WIN32_OWN_PROCESS.
39 static char *servicename = "pgbouncer";
41 static char *service_username = NULL;
42 static char *service_password = NULL;
44 static char *serviceDescription = "Lightweight connection pooler for PostgreSQL.";
46 /* custom help string for win32 exe */
47 static const char *usage_str =
48 "Usage: %s [OPTION]... config.ini\n"
49 " -q No console messages\n"
50 " -v Increase verbosity\n"
51 " -V Show version\n"
52 " -h Show this help screen and exit\n"
53 "Windows service registration:\n"
54 " -regservice config.ini [-U username [-P password]]\n"
55 " -unregservice config.ini\n"
56 "";
58 static void usage(int err, char *exe)
60 printf(usage_str, basename(exe));
61 exit(err);
64 static int exec_real_main(int argc, char *argv[])
66 int i, j;
68 /* win32 stdio seems to be fully buffered by default */
69 setvbuf(stdout, NULL, _IONBF, 0);
70 setvbuf(stderr, NULL, _IONBF, 0);
72 /* check if regular arguments are in allowed list */
73 for (i = 1; i < argc; i++) {
74 char *p = argv[i];
75 if (p[0] != '-')
76 continue;
77 for (j = 1; p[j]; j++) {
78 if (!strchr("qvhV", p[j]))
79 usage(1, argv[0]);
80 if (p[j] == 'h')
81 usage(0, argv[0]);
85 /* call actual main() */
86 return real_main(argc, argv);
89 /* Set the current service status */
90 static void win32_setservicestatus(DWORD state)
92 svcStatus.dwCurrentState = state;
93 switch (state) {
94 case SERVICE_START_PENDING:
95 case SERVICE_STOP_PENDING:
96 svcStatus.dwControlsAccepted = 0;
97 svcStatus.dwWaitHint = 5000;
98 break;
99 default:
100 svcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
101 svcStatus.dwWaitHint = 0;
104 SetServiceStatus(hStatus, &svcStatus);
108 * Handle any events sent by the service control manager
109 * NOTE! Events are sent on a different thread! And it's
110 * not a pthreads thread, so avoid calling anything that
111 * may use pthreads - like pgbouncer_log()
113 static void WINAPI win32_servicehandler(DWORD request)
115 switch (request) {
116 case SERVICE_CONTROL_STOP:
117 case SERVICE_CONTROL_SHUTDOWN:
118 win32_setservicestatus(SERVICE_STOP_PENDING);
119 cf_shutdown = 2;
120 break;
121 case SERVICE_CONTROL_INTERROGATE:
122 SetServiceStatus(hStatus, &svcStatus);
123 break;
127 /* notify control thread about stop */
128 static void win32_service_cleanup(void)
130 if (hStatus)
131 win32_setservicestatus(SERVICE_STOPPED);
132 hStatus = 0; /* may get called twice from atexit */
136 * Entrypoint for actual service work.
138 * Service is set-up and then actual main() is called.
140 static void WINAPI win32_servicemain(DWORD argc, LPSTR *argv)
142 int new_argc = 2;
143 char *new_argv[] = { servicename, cf_config_file, NULL };
145 /* register control request handler */
146 hStatus = RegisterServiceCtrlHandler(servicename, win32_servicehandler);
147 if (hStatus == 0) {
148 fatal("could not connect to service control handler: %s", strerror(GetLastError()));
149 exit(1);
152 /* Tell SCM we are running before we make any API calls */
153 win32_setservicestatus(SERVICE_RUNNING);
155 /* register with system atexit(), in case somebody calls exit() */
156 atexit(win32_service_cleanup);
158 /* Execute actual main() */
159 exec_real_main(new_argc, new_argv);
161 win32_service_cleanup();
164 /* Start running as a service */
165 static void win32_servicestart(void)
167 SERVICE_TABLE_ENTRY st[] = { {servicename, win32_servicemain}, {NULL, NULL} };
169 if (StartServiceCtrlDispatcher(st) == 0) {
170 fprintf(stderr, "could not start service control dispatcher: %s\n",
171 strerror(GetLastError()));
172 exit(1);
176 /* Open Service Control Manager */
177 static SC_HANDLE openSCM(void)
179 SC_HANDLE manager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
180 if (!manager) {
181 fprintf(stderr, "Failed to open service control manager: %s\n", strerror(GetLastError()));
182 exit(1);
184 return manager;
187 /* Full path to current config file. */
188 static const char *get_config_fullpath(void)
190 DWORD r;
191 static char buf[PATH_MAX];
193 r = GetFullPathName(cf_config_file, sizeof(buf), buf, NULL);
194 if (r == 0 || r >= sizeof(buf)) {
195 fprintf(stderr, "Failed to get full pathname for '%s': %s\n",
196 cf_config_file, strerror(GetLastError()));
197 exit(1);
199 return buf;
202 /* Register a service with the specified name with the local service control manager */
203 static void RegisterService(void)
205 char self[1024];
206 char cmdline[2048];
207 const char *config_fn = get_config_fullpath();
208 SC_HANDLE manager;
209 SC_HANDLE service;
210 SERVICE_DESCRIPTION sd;
211 DWORD r;
213 r = GetModuleFileName(NULL, self, sizeof(self));
214 if (!r || r >= sizeof(self)) {
215 fprintf(stderr, "Failed to determine path name: %s\n", strerror(GetLastError()));
216 exit(1);
218 snprintf(cmdline, sizeof(cmdline), "%s -service \"%s\"", self, config_fn);
220 manager = openSCM();
221 service = CreateService(manager, cf_jobname, cf_jobname, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
222 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, cmdline, NULL, NULL, "RPCSS\0",
223 service_username, service_password);
224 if (!service) {
225 fprintf(stderr, "Failed to create service: %s\n", strerror(GetLastError()));
226 exit(1);
229 /* explain the service purpose */
230 sd.lpDescription = serviceDescription;
231 ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &sd);
233 CloseServiceHandle(service);
234 CloseServiceHandle(manager);
236 printf("Service registered.\n");
237 if (service_username == NULL) {
238 printf("\nWARNING! Service is registered to run as Local System. You are\n");
239 printf("encouraged to change this to a low privilege account to increase\n");
240 printf("system security. (Eg. NT AUTHORITY\\Local Service)\n");
244 /* Remove a service with the specified name from the local service control manager */
245 static void UnRegisterService(void)
247 SC_HANDLE manager;
248 SC_HANDLE service;
250 manager = openSCM();
251 service = OpenService(manager, cf_jobname, SC_MANAGER_ALL_ACCESS);
252 if (!service) {
253 fprintf(stderr, "Failed to open service: %s\n", strerror(GetLastError()));
254 exit(1);
257 if (!DeleteService(service)) {
258 fprintf(stderr, "Failed to delete service: %s\n", strerror(GetLastError()));
259 exit(1);
262 CloseServiceHandle(service);
263 CloseServiceHandle(manager);
265 printf("Service removed.\n");
270 * syslog() interface to event log.
273 void win32_eventlog(int level, const char *fmt, ...)
275 static HANDLE evtHandle = INVALID_HANDLE_VALUE;
276 int elevel;
277 char buf[1024];
278 const char *strlist[1] = { buf };
279 va_list ap;
281 va_start(ap, fmt);
282 vsnprintf(buf, sizeof(buf), fmt, ap);
283 va_end(ap);
285 switch (level) {
286 case LOG_CRIT:
287 case LOG_ERR:
288 elevel = EVENTLOG_ERROR_TYPE;
289 break;
290 case LOG_WARNING:
291 elevel = EVENTLOG_WARNING_TYPE;
292 break;
293 default:
294 elevel = EVENTLOG_INFORMATION_TYPE;
297 if (evtHandle == INVALID_HANDLE_VALUE) {
298 evtHandle = RegisterEventSource(NULL, servicename);
299 if (evtHandle == NULL || evtHandle == INVALID_HANDLE_VALUE) {
300 evtHandle = INVALID_HANDLE_VALUE;
301 return;
304 ReportEvent(evtHandle, elevel, 0, 0, NULL, 1, 0, strlist, NULL);
308 * Error strings for win32 errors.
311 const char *win32_strerror(int e)
313 static char buf[1024];
314 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, e,
315 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
316 buf, sizeof(buf), NULL);
317 return buf;
320 /* config loader for service register/unregister */
321 static void win32_load_config(char *conf)
323 cf_config_file = conf;
324 init_objects();
325 load_config(false);
329 * Wrapper around actual main() that handles win32 hacks.
332 #undef main
333 int main(int argc, char *argv[])
335 WSADATA wsaData;
337 /* initialize socket subsystem */
338 if (WSAStartup(MAKEWORD(2,0), &wsaData))
339 fatal("Cannot start the network subsystem");
341 /* service cmdline */
342 if (argc >= 3) {
343 if (!strcmp(argv[1], "-service")) {
344 cf_quiet = 1;
345 cf_config_file = argv[2];
346 win32_servicestart();
347 return 0;
350 if (!strcmp(argv[1], "-regservice")) {
351 int i;
352 win32_load_config(argv[2]);
353 for (i = 3; i < argc; i++) {
354 if (!strcmp(argv[i], "-U") && i + 1 < argc) {
355 service_username = argv[++i];
356 } else if (!strcmp(argv[i], "-P") && i + 1 < argc) {
357 service_password = argv[++i];
358 } else {
359 printf("unknown arg: %s\n", argv[i]);
360 usage(1, argv[0]);
363 RegisterService();
364 return 0;
367 if (!strcmp(argv[1], "-unregservice")) {
368 win32_load_config(argv[2]);
369 UnRegisterService();
370 return 0;
374 return exec_real_main(argc, argv);