1 /*-------------------------------------------------------------------------
4 * Windows service integration and eventlog
6 * Copyright (c) 2005, PostgreSQL Global Development Group
7 * Authors: Magnus Hagander, Hiroshi Saito, Marko Kreen
8 *-------------------------------------------------------------------------
11 #define WIN32_LEAN_AND_MEAN
19 #if defined(UNICODE) || defined(_UNICODE)
20 #error This code does not support wide characters.
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
,
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"
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"
58 static void usage(int err
, char *exe
)
60 printf(usage_str
, basename(exe
));
64 static int exec_real_main(int argc
, char *argv
[])
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
++) {
77 for (j
= 1; p
[j
]; j
++) {
78 if (!strchr("qvhV", p
[j
]))
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
;
94 case SERVICE_START_PENDING
:
95 case SERVICE_STOP_PENDING
:
96 svcStatus
.dwControlsAccepted
= 0;
97 svcStatus
.dwWaitHint
= 5000;
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
)
116 case SERVICE_CONTROL_STOP
:
117 case SERVICE_CONTROL_SHUTDOWN
:
118 win32_setservicestatus(SERVICE_STOP_PENDING
);
121 case SERVICE_CONTROL_INTERROGATE
:
122 SetServiceStatus(hStatus
, &svcStatus
);
127 /* notify control thread about stop */
128 static void win32_service_cleanup(void)
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
)
143 char *new_argv
[] = { servicename
, cf_config_file
, NULL
};
145 /* register control request handler */
146 hStatus
= RegisterServiceCtrlHandler(servicename
, win32_servicehandler
);
148 fatal("could not connect to service control handler: %s", strerror(GetLastError()));
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()));
176 /* Open Service Control Manager */
177 static SC_HANDLE
openSCM(void)
179 SC_HANDLE manager
= OpenSCManager(NULL
, NULL
, SC_MANAGER_ALL_ACCESS
);
181 fprintf(stderr
, "Failed to open service control manager: %s\n", strerror(GetLastError()));
187 /* Full path to current config file. */
188 static const char *get_config_fullpath(void)
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()));
202 /* Register a service with the specified name with the local service control manager */
203 static void RegisterService(void)
207 const char *config_fn
= get_config_fullpath();
210 SERVICE_DESCRIPTION sd
;
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()));
218 snprintf(cmdline
, sizeof(cmdline
), "%s -service \"%s\"", self
, config_fn
);
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
);
225 fprintf(stderr
, "Failed to create service: %s\n", strerror(GetLastError()));
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)
251 service
= OpenService(manager
, cf_jobname
, SC_MANAGER_ALL_ACCESS
);
253 fprintf(stderr
, "Failed to open service: %s\n", strerror(GetLastError()));
257 if (!DeleteService(service
)) {
258 fprintf(stderr
, "Failed to delete service: %s\n", strerror(GetLastError()));
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
;
278 const char *strlist
[1] = { buf
};
282 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
288 elevel
= EVENTLOG_ERROR_TYPE
;
291 elevel
= EVENTLOG_WARNING_TYPE
;
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
;
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
);
320 /* config loader for service register/unregister */
321 static void win32_load_config(char *conf
)
323 cf_config_file
= conf
;
329 * Wrapper around actual main() that handles win32 hacks.
333 int main(int argc
, char *argv
[])
337 /* initialize socket subsystem */
338 if (WSAStartup(MAKEWORD(2,0), &wsaData
))
339 fatal("Cannot start the network subsystem");
341 /* service cmdline */
343 if (!strcmp(argv
[1], "-service")) {
345 cf_config_file
= argv
[2];
346 win32_servicestart();
350 if (!strcmp(argv
[1], "-regservice")) {
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
];
359 printf("unknown arg: %s\n", argv
[i
]);
367 if (!strcmp(argv
[1], "-unregservice")) {
368 win32_load_config(argv
[2]);
374 return exec_real_main(argc
, argv
);