blob: d054ce4d19afd65efba1e88f4850e070a45cac0a [file] [log] [blame]
Constantin Kaplinsky729598c2006-05-25 05:12:25 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
19// -=- Service.cxx
20
21#include <rfb_win32/Service.h>
22#include <rfb_win32/MsgWindow.h>
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000023#include <rfb_win32/ModuleFileName.h>
24#include <rfb_win32/Registry.h>
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000025#include <rfb/Threading.h>
26#include <logmessages/messages.h>
27#include <rdr/Exception.h>
28#include <rfb/LogWriter.h>
29
30
31using namespace rdr;
32using namespace rfb;
33using namespace win32;
34
35static LogWriter vlog("Service");
36
37
38// - Internal service implementation functions
39
40Service* service = 0;
Samuel Mannehed60c41932014-02-07 14:53:24 +000041bool runAsService = false;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000042
43VOID WINAPI serviceHandler(DWORD control) {
44 switch (control) {
45 case SERVICE_CONTROL_INTERROGATE:
46 vlog.info("cmd: report status");
47 service->setStatus();
48 return;
49 case SERVICE_CONTROL_PARAMCHANGE:
50 vlog.info("cmd: param change");
51 service->readParams();
52 return;
53 case SERVICE_CONTROL_SHUTDOWN:
54 vlog.info("cmd: OS shutdown");
55 service->osShuttingDown();
56 return;
57 case SERVICE_CONTROL_STOP:
58 vlog.info("cmd: stop");
59 service->setStatus(SERVICE_STOP_PENDING);
60 service->stop();
61 return;
62 };
63 vlog.debug("cmd: unknown %lu", control);
64}
65
66
Pierre Ossmanfc08bee2016-01-12 12:32:15 +010067// -=- Service main procedure
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000068
69VOID WINAPI serviceProc(DWORD dwArgc, LPTSTR* lpszArgv) {
70 vlog.debug("entering %s serviceProc", service->getName());
71 vlog.info("registering handler...");
72 service->status_handle = RegisterServiceCtrlHandler(service->getName(), serviceHandler);
73 if (!service->status_handle) {
74 DWORD err = GetLastError();
75 vlog.error("failed to register handler: %lu", err);
76 ExitProcess(err);
77 }
Pierre Ossmanfb450fb2015-03-03 16:34:56 +010078 vlog.debug("registered handler (%p)", service->status_handle);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000079 service->setStatus(SERVICE_START_PENDING);
80 vlog.debug("entering %s serviceMain", service->getName());
81 service->status.dwWin32ExitCode = service->serviceMain(dwArgc, lpszArgv);
82 vlog.debug("leaving %s serviceMain", service->getName());
83 service->setStatus(SERVICE_STOPPED);
84}
85
86
87// -=- Service
88
89Service::Service(const TCHAR* name_) : name(name_) {
90 vlog.debug("Service");
91 status_handle = 0;
92 status.dwControlsAccepted = SERVICE_CONTROL_INTERROGATE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
93 status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
94 status.dwWin32ExitCode = NO_ERROR;
95 status.dwServiceSpecificExitCode = 0;
96 status.dwCheckPoint = 0;
97 status.dwWaitHint = 30000;
98 status.dwCurrentState = SERVICE_STOPPED;
99}
100
101void
102Service::start() {
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100103 SERVICE_TABLE_ENTRY entry[2];
104 entry[0].lpServiceName = (TCHAR*)name;
105 entry[0].lpServiceProc = serviceProc;
106 entry[1].lpServiceName = NULL;
107 entry[1].lpServiceProc = NULL;
108 vlog.debug("entering dispatcher");
109 if (!SetProcessShutdownParameters(0x100, 0))
110 vlog.error("unable to set shutdown parameters: %lu", GetLastError());
111 service = this;
112 if (!StartServiceCtrlDispatcher(entry))
113 throw SystemException("unable to start service", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000114}
115
116void
117Service::setStatus() {
118 setStatus(status.dwCurrentState);
119}
120
121void
122Service::setStatus(DWORD state) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000123 if (status_handle == 0) {
124 vlog.debug("warning - cannot setStatus");
125 return;
126 }
127 status.dwCurrentState = state;
128 status.dwCheckPoint++;
129 if (!SetServiceStatus(status_handle, &status)) {
130 status.dwCurrentState = SERVICE_STOPPED;
131 status.dwWin32ExitCode = GetLastError();
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100132 vlog.error("unable to set service status:%lu", status.dwWin32ExitCode);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000133 }
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100134 vlog.debug("set status to %lu(%lu)", state, status.dwCheckPoint);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000135}
136
137Service::~Service() {
138 vlog.debug("~Service");
139 service = 0;
140}
141
142
143// Find out whether this process is running as the WinVNC service
144bool thisIsService() {
145 return service && (service->status.dwCurrentState != SERVICE_STOPPED);
146}
147
148
149// -=- Desktop handling code
150
151// Switch the current thread to the specified desktop
152static bool
153switchToDesktop(HDESK desktop) {
154 HDESK old_desktop = GetThreadDesktop(GetCurrentThreadId());
155 if (!SetThreadDesktop(desktop)) {
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100156 vlog.debug("switchToDesktop failed:%lu", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000157 return false;
158 }
159 if (!CloseDesktop(old_desktop))
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100160 vlog.debug("unable to close old desktop:%lu", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000161 return true;
162}
163
164// Determine whether the thread's current desktop is the input one
165static bool
166inputDesktopSelected() {
167 HDESK current = GetThreadDesktop(GetCurrentThreadId());
168 HDESK input = OpenInputDesktop(0, FALSE,
169 DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
170 DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
171 DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
172 DESKTOP_SWITCHDESKTOP | GENERIC_WRITE);
173 if (!input) {
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100174 vlog.debug("unable to OpenInputDesktop(1):%lu", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000175 return false;
176 }
177
178 DWORD size;
179 char currentname[256];
180 char inputname[256];
181
182 if (!GetUserObjectInformation(current, UOI_NAME, currentname, 256, &size)) {
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100183 vlog.debug("unable to GetUserObjectInformation(1):%lu", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000184 CloseDesktop(input);
185 return false;
186 }
187 if (!GetUserObjectInformation(input, UOI_NAME, inputname, 256, &size)) {
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100188 vlog.debug("unable to GetUserObjectInformation(2):%lu", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000189 CloseDesktop(input);
190 return false;
191 }
192 if (!CloseDesktop(input))
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100193 vlog.debug("unable to close input desktop:%lu", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000194
195 // *** vlog.debug("current=%s, input=%s", currentname, inputname);
196 bool result = strcmp(currentname, inputname) == 0;
197 return result;
198}
199
200// Switch the current thread into the input desktop
201static bool
202selectInputDesktop() {
203 // - Open the input desktop
204 HDESK desktop = OpenInputDesktop(0, FALSE,
205 DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
206 DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
207 DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
208 DESKTOP_SWITCHDESKTOP | GENERIC_WRITE);
209 if (!desktop) {
Pierre Ossmanfb450fb2015-03-03 16:34:56 +0100210 vlog.debug("unable to OpenInputDesktop(2):%lu", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000211 return false;
212 }
213
214 // - Switch into it
215 if (!switchToDesktop(desktop)) {
216 CloseDesktop(desktop);
217 return false;
218 }
219
220 // ***
221 DWORD size = 256;
222 char currentname[256];
223 if (GetUserObjectInformation(desktop, UOI_NAME, currentname, 256, &size)) {
224 vlog.debug("switched to %s", currentname);
225 }
226 // ***
227
228 vlog.debug("switched to input desktop");
229
230 return true;
231}
232
233
234// -=- Access points to desktop-switching routines
235
236bool
237rfb::win32::desktopChangeRequired() {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000238 return !inputDesktopSelected();
239}
240
241bool
242rfb::win32::changeDesktop() {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000243 return selectInputDesktop();
244}
245
246
247// -=- Ctrl-Alt-Del emulation
248
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000249bool
250rfb::win32::emulateCtrlAltDel() {
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100251 rfb::win32::Handle sessionEventCad =
252 CreateEvent(0, FALSE, FALSE, "Global\\SessionEventTigerVNCCad");
253 SetEvent(sessionEventCad);
254 return true;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000255}
256
257
258// -=- Application Event Log target Logger class
259
260class Logger_EventLog : public Logger {
261public:
262 Logger_EventLog(const TCHAR* srcname) : Logger("EventLog") {
263 eventlog = RegisterEventSource(NULL, srcname);
264 if (!eventlog)
265 printf("Unable to open event log:%ld\n", GetLastError());
266 }
267 ~Logger_EventLog() {
268 if (eventlog)
269 DeregisterEventSource(eventlog);
270 }
271
272 virtual void write(int level, const char *logname, const char *message) {
273 if (!eventlog) return;
274 TStr log(logname), msg(message);
275 const TCHAR* strings[] = {log, msg};
276 WORD type = EVENTLOG_INFORMATION_TYPE;
277 if (level == 0) type = EVENTLOG_ERROR_TYPE;
278 if (!ReportEvent(eventlog, type, 0, VNC4LogMessage, NULL, 2, 0, strings, NULL)) {
279 // *** It's not at all clear what is the correct behaviour if this fails...
280 printf("ReportEvent failed:%ld\n", GetLastError());
281 }
282 }
283
284protected:
285 HANDLE eventlog;
286};
287
288static Logger_EventLog* logger = 0;
289
290bool rfb::win32::initEventLogLogger(const TCHAR* srcname) {
291 if (logger)
292 return false;
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100293 logger = new Logger_EventLog(srcname);
294 logger->registerLogger();
295 return true;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000296}
297
298
299// -=- Registering and unregistering the service
300
Pierre Ossman018c67e2016-01-12 10:13:17 +0100301bool rfb::win32::registerService(const TCHAR* name,
302 const TCHAR* display,
303 const TCHAR* desc,
Adam Tkac8fb6ac02010-06-25 11:24:27 +0000304 int argc, char** argv) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000305
306 // - Initialise the default service parameters
307 const TCHAR* defaultcmdline;
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100308 defaultcmdline = _T("-service");
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000309
310 // - Get the full pathname of our executable
311 ModuleFileName buffer;
312
313 // - Calculate the command-line length
314 int cmdline_len = _tcslen(buffer.buf) + 4;
315 int i;
316 for (i=0; i<argc; i++) {
317 cmdline_len += strlen(argv[i]) + 3;
318 }
319
320 // - Add the supplied extra parameters to the command line
321 TCharArray cmdline(cmdline_len+_tcslen(defaultcmdline));
322 _stprintf(cmdline.buf, _T("\"%s\" %s"), buffer.buf, defaultcmdline);
323 for (i=0; i<argc; i++) {
324 _tcscat(cmdline.buf, _T(" \""));
325 _tcscat(cmdline.buf, TStr(argv[i]));
326 _tcscat(cmdline.buf, _T("\""));
327 }
328
329 // - Register the service
330
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100331 // - Open the SCM
332 ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
333 if (!scm)
334 throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000335
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100336 // - Add the service
337 ServiceHandle service = CreateService(scm,
338 name, display, SC_MANAGER_ALL_ACCESS,
339 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
340 SERVICE_AUTO_START, SERVICE_ERROR_IGNORE,
341 cmdline.buf, NULL, NULL, NULL, NULL, NULL);
342 if (!service)
343 throw rdr::SystemException("unable to create service", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000344
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100345 // - Set a description
346 SERVICE_DESCRIPTION sdesc = {(LPTSTR)desc};
347 ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &sdesc);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000348
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100349 // - Register the event log source
350 RegKey hk, hk2;
Pierre Ossman018c67e2016-01-12 10:13:17 +0100351
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100352 hk2.createKey(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application"));
353 hk.createKey(hk2, name);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000354
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100355 for (i=_tcslen(buffer.buf); i>0; i--) {
356 if (buffer.buf[i] == _T('\\')) {
357 buffer.buf[i+1] = 0;
358 break;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000359 }
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000360 }
361
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100362 const TCHAR* dllFilename = _T("logmessages.dll");
363 TCharArray dllPath(_tcslen(buffer.buf) + _tcslen(dllFilename) + 1);
364 _tcscpy(dllPath.buf, buffer.buf);
365 _tcscat(dllPath.buf, dllFilename);
366
367 hk.setExpandString(_T("EventMessageFile"), dllPath.buf);
368 hk.setInt(_T("TypesSupported"), EVENTLOG_ERROR_TYPE | EVENTLOG_INFORMATION_TYPE);
369
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000370 Sleep(500);
371
372 return true;
373}
374
375bool rfb::win32::unregisterService(const TCHAR* name) {
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100376 // - Open the SCM
377 ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
378 if (!scm)
379 throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000380
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100381 // - Create the service
382 ServiceHandle service = OpenService(scm, name, SC_MANAGER_ALL_ACCESS);
383 if (!service)
384 throw rdr::SystemException("unable to locate the service", GetLastError());
385 if (!DeleteService(service))
386 throw rdr::SystemException("unable to remove the service", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000387
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100388 // - Register the event log source
389 RegKey hk;
390 hk.openKey(HKEY_LOCAL_MACHINE, _T("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application"));
391 hk.deleteKey(name);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000392
393 Sleep(500);
394
395 return true;
396}
397
398
399// -=- Starting and stopping the service
400
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000401bool rfb::win32::startService(const TCHAR* name) {
402
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100403 // - Open the SCM
404 ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
405 if (!scm)
406 throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000407
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100408 // - Locate the service
409 ServiceHandle service = OpenService(scm, name, SERVICE_START);
410 if (!service)
411 throw rdr::SystemException("unable to open the service", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000412
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100413 // - Start the service
414 if (!StartService(service, 0, NULL))
415 throw rdr::SystemException("unable to start the service", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000416
417 Sleep(500);
418
419 return true;
420}
421
422bool rfb::win32::stopService(const TCHAR* name) {
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100423 // - Open the SCM
424 ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
425 if (!scm)
426 throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000427
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100428 // - Locate the service
429 ServiceHandle service = OpenService(scm, name, SERVICE_STOP);
430 if (!service)
431 throw rdr::SystemException("unable to open the service", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000432
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100433 // - Start the service
434 SERVICE_STATUS status;
435 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
436 throw rdr::SystemException("unable to stop the service", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000437
438 Sleep(500);
439
440 return true;
441}
442
443DWORD rfb::win32::getServiceState(const TCHAR* name) {
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100444 // - Open the SCM
445 ServiceHandle scm = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
446 if (!scm)
447 throw rdr::SystemException("unable to open Service Control Manager", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000448
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100449 // - Locate the service
450 ServiceHandle service = OpenService(scm, name, SERVICE_INTERROGATE);
451 if (!service)
452 throw rdr::SystemException("unable to open the service", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000453
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100454 // - Get the service status
455 SERVICE_STATUS status;
456 if (!ControlService(service, SERVICE_CONTROL_INTERROGATE, (SERVICE_STATUS*)&status))
457 throw rdr::SystemException("unable to query the service", GetLastError());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000458
Pierre Ossmanfc08bee2016-01-12 12:32:15 +0100459 return status.dwCurrentState;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000460}
461
462char* rfb::win32::serviceStateName(DWORD state) {
463 switch (state) {
464 case SERVICE_RUNNING: return strDup("Running");
465 case SERVICE_STOPPED: return strDup("Stopped");
466 case SERVICE_STOP_PENDING: return strDup("Stopping");
467 };
468 CharArray tmp(32);
469 sprintf(tmp.buf, "Unknown (%lu)", state);
470 return tmp.takeBuf();
471}
472
473
474bool rfb::win32::isServiceProcess() {
Samuel Mannehed60c41932014-02-07 14:53:24 +0000475 return runAsService;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000476}