1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the Qt Solutions component.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** You may use this file under the terms of the BSD license as follows:
10 **
11 ** "Redistribution and use in source and binary forms, with or without
12 ** modification, are permitted provided that the following conditions are
13 ** met:
14 ** * Redistributions of source code must retain the above copyright
15 ** notice, this list of conditions and the following disclaimer.
16 ** * Redistributions in binary form must reproduce the above copyright
17 ** notice, this list of conditions and the following disclaimer in
18 ** the documentation and/or other materials provided with the
19 ** distribution.
20 ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
21 ** of its contributors may be used to endorse or promote products derived
22 ** from this software without specific prior written permission.
23 **
24 **
25 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40
41 #include "qtservice.h"
42 #include "qtservice_p.h"
43 #include <QCoreApplication>
44 #include <QDateTime>
45 #include <QFile>
46 #include <QLibrary>
47 #include <QMutex>
48 #include <QSemaphore>
49 #include <QProcess>
50 #include <QSettings>
51 #include <QTextStream>
52 #include <qt_windows.h>
53 #include <QWaitCondition>
54 #include <QAbstractEventDispatcher>
55 #include <QVector>
56 #include <QThread>
57 #if QT_VERSION >= 0x050000
58 # include <QAbstractNativeEventFilter>
59 #endif
60 #include <stdio.h>
61 #if defined(QTSERVICE_DEBUG)
62 #include <QDebug>
63 #endif
64
65 typedef SERVICE_STATUS_HANDLE(WINAPI*PRegisterServiceCtrlHandler)(const wchar_t*,LPHANDLER_FUNCTION);
66 static PRegisterServiceCtrlHandler pRegisterServiceCtrlHandler = 0;
67 typedef BOOL(WINAPI*PSetServiceStatus)(SERVICE_STATUS_HANDLE,LPSERVICE_STATUS);
68 static PSetServiceStatus pSetServiceStatus = 0;
69 typedef BOOL(WINAPI*PChangeServiceConfig2)(SC_HANDLE,DWORD,LPVOID);
70 static PChangeServiceConfig2 pChangeServiceConfig2 = 0;
71 typedef BOOL(WINAPI*PCloseServiceHandle)(SC_HANDLE);
72 static PCloseServiceHandle pCloseServiceHandle = 0;
73 typedef SC_HANDLE(WINAPI*PCreateService)(SC_HANDLE,LPCTSTR,LPCTSTR,DWORD,DWORD,DWORD,DWORD,LPCTSTR,LPCTSTR,LPDWORD,LPCTSTR,LPCTSTR,LPCTSTR);
74 static PCreateService pCreateService = 0;
75 typedef SC_HANDLE(WINAPI*POpenSCManager)(LPCTSTR,LPCTSTR,DWORD);
76 static POpenSCManager pOpenSCManager = 0;
77 typedef BOOL(WINAPI*PDeleteService)(SC_HANDLE);
78 static PDeleteService pDeleteService = 0;
79 typedef SC_HANDLE(WINAPI*POpenService)(SC_HANDLE,LPCTSTR,DWORD);
80 static POpenService pOpenService = 0;
81 typedef BOOL(WINAPI*PQueryServiceStatus)(SC_HANDLE,LPSERVICE_STATUS);
82 static PQueryServiceStatus pQueryServiceStatus = 0;
83 typedef BOOL(WINAPI*PStartServiceCtrlDispatcher)(CONST SERVICE_TABLE_ENTRY*);
84 static PStartServiceCtrlDispatcher pStartServiceCtrlDispatcher = 0;
85 typedef BOOL(WINAPI*PStartService)(SC_HANDLE,DWORD,const wchar_t**);
86 static PStartService pStartService = 0;
87 typedef BOOL(WINAPI*PControlService)(SC_HANDLE,DWORD,LPSERVICE_STATUS);
88 static PControlService pControlService = 0;
89 typedef HANDLE(WINAPI*PDeregisterEventSource)(HANDLE);
90 static PDeregisterEventSource pDeregisterEventSource = 0;
91 typedef BOOL(WINAPI*PReportEvent)(HANDLE,WORD,WORD,DWORD,PSID,WORD,DWORD,LPCTSTR*,LPVOID);
92 static PReportEvent pReportEvent = 0;
93 typedef HANDLE(WINAPI*PRegisterEventSource)(LPCTSTR,LPCTSTR);
94 static PRegisterEventSource pRegisterEventSource = 0;
95 typedef DWORD(WINAPI*PRegisterServiceProcess)(DWORD,DWORD);
96 static PRegisterServiceProcess pRegisterServiceProcess = 0;
97 typedef BOOL(WINAPI*PQueryServiceConfig)(SC_HANDLE,LPQUERY_SERVICE_CONFIG,DWORD,LPDWORD);
98 static PQueryServiceConfig pQueryServiceConfig = 0;
99 typedef BOOL(WINAPI*PQueryServiceConfig2)(SC_HANDLE,DWORD,LPBYTE,DWORD,LPDWORD);
100 static PQueryServiceConfig2 pQueryServiceConfig2 = 0;
101
102
103 #define RESOLVE(name) p##name = (P##name)lib.resolve(#name);
104 #define RESOLVEA(name) p##name = (P##name)lib.resolve(#name"A");
105 #define RESOLVEW(name) p##name = (P##name)lib.resolve(#name"W");
106
winServiceInit()107 static bool winServiceInit()
108 {
109 if (!pOpenSCManager) {
110 QLibrary lib("advapi32");
111
112 // only resolve unicode versions
113 RESOLVEW(RegisterServiceCtrlHandler);
114 RESOLVE(SetServiceStatus);
115 RESOLVEW(ChangeServiceConfig2);
116 RESOLVE(CloseServiceHandle);
117 RESOLVEW(CreateService);
118 RESOLVEW(OpenSCManager);
119 RESOLVE(DeleteService);
120 RESOLVEW(OpenService);
121 RESOLVE(QueryServiceStatus);
122 RESOLVEW(StartServiceCtrlDispatcher);
123 RESOLVEW(StartService); // need only Ansi version
124 RESOLVE(ControlService);
125 RESOLVE(DeregisterEventSource);
126 RESOLVEW(ReportEvent);
127 RESOLVEW(RegisterEventSource);
128 RESOLVEW(QueryServiceConfig);
129 RESOLVEW(QueryServiceConfig2);
130 }
131 return pOpenSCManager != 0;
132 }
133
isInstalled() const134 bool QtServiceController::isInstalled() const
135 {
136 Q_D(const QtServiceController);
137 bool result = false;
138 if (!winServiceInit())
139 return result;
140
141 // Open the Service Control Manager
142 SC_HANDLE hSCM = pOpenSCManager(0, 0, 0);
143 if (hSCM) {
144 // Try to open the service
145 SC_HANDLE hService = pOpenService(hSCM, (wchar_t*)d->serviceName.utf16(),
146 SERVICE_QUERY_CONFIG);
147
148 if (hService) {
149 result = true;
150 pCloseServiceHandle(hService);
151 }
152 pCloseServiceHandle(hSCM);
153 }
154 return result;
155 }
156
isRunning() const157 bool QtServiceController::isRunning() const
158 {
159 Q_D(const QtServiceController);
160 bool result = false;
161 if (!winServiceInit())
162 return result;
163
164 // Open the Service Control Manager
165 SC_HANDLE hSCM = pOpenSCManager(0, 0, 0);
166 if (hSCM) {
167 // Try to open the service
168 SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(),
169 SERVICE_QUERY_STATUS);
170 if (hService) {
171 SERVICE_STATUS info;
172 int res = pQueryServiceStatus(hService, &info);
173 if (res)
174 result = info.dwCurrentState != SERVICE_STOPPED;
175 pCloseServiceHandle(hService);
176 }
177 pCloseServiceHandle(hSCM);
178 }
179 return result;
180 }
181
182
serviceFilePath() const183 QString QtServiceController::serviceFilePath() const
184 {
185 Q_D(const QtServiceController);
186 QString result;
187 if (!winServiceInit())
188 return result;
189
190 // Open the Service Control Manager
191 SC_HANDLE hSCM = pOpenSCManager(0, 0, 0);
192 if (hSCM) {
193 // Try to open the service
194 SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(),
195 SERVICE_QUERY_CONFIG);
196 if (hService) {
197 DWORD sizeNeeded = 0;
198 char data[8 * 1024];
199 if (pQueryServiceConfig(hService, (LPQUERY_SERVICE_CONFIG)data, 8 * 1024, &sizeNeeded)) {
200 LPQUERY_SERVICE_CONFIG config = (LPQUERY_SERVICE_CONFIG)data;
201 result = QString::fromUtf16((const ushort*)config->lpBinaryPathName);
202 }
203 pCloseServiceHandle(hService);
204 }
205 pCloseServiceHandle(hSCM);
206 }
207 return result;
208 }
209
serviceDescription() const210 QString QtServiceController::serviceDescription() const
211 {
212 Q_D(const QtServiceController);
213 QString result;
214 if (!winServiceInit())
215 return result;
216
217 // Open the Service Control Manager
218 SC_HANDLE hSCM = pOpenSCManager(0, 0, 0);
219 if (hSCM) {
220 // Try to open the service
221 SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(),
222 SERVICE_QUERY_CONFIG);
223 if (hService) {
224 DWORD dwBytesNeeded;
225 char data[8 * 1024];
226 if (pQueryServiceConfig2(
227 hService,
228 SERVICE_CONFIG_DESCRIPTION,
229 (unsigned char *)data,
230 8096,
231 &dwBytesNeeded)) {
232 LPSERVICE_DESCRIPTION desc = (LPSERVICE_DESCRIPTION)data;
233 if (desc->lpDescription)
234 result = QString::fromUtf16((const ushort*)desc->lpDescription);
235 }
236 pCloseServiceHandle(hService);
237 }
238 pCloseServiceHandle(hSCM);
239 }
240 return result;
241 }
242
startupType() const243 QtServiceController::StartupType QtServiceController::startupType() const
244 {
245 Q_D(const QtServiceController);
246 StartupType result = ManualStartup;
247 if (!winServiceInit())
248 return result;
249
250 // Open the Service Control Manager
251 SC_HANDLE hSCM = pOpenSCManager(0, 0, 0);
252 if (hSCM) {
253 // Try to open the service
254 SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(),
255 SERVICE_QUERY_CONFIG);
256 if (hService) {
257 DWORD sizeNeeded = 0;
258 char data[8 * 1024];
259 if (pQueryServiceConfig(hService, (QUERY_SERVICE_CONFIG *)data, 8 * 1024, &sizeNeeded)) {
260 QUERY_SERVICE_CONFIG *config = (QUERY_SERVICE_CONFIG *)data;
261 result = config->dwStartType == SERVICE_DEMAND_START ? ManualStartup : AutoStartup;
262 }
263 pCloseServiceHandle(hService);
264 }
265 pCloseServiceHandle(hSCM);
266 }
267 return result;
268 }
269
uninstall()270 bool QtServiceController::uninstall()
271 {
272 Q_D(QtServiceController);
273 bool result = false;
274 if (!winServiceInit())
275 return result;
276
277 // Open the Service Control Manager
278 SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
279 if (hSCM) {
280 // Try to open the service
281 SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(), DELETE);
282 if (hService) {
283 if (pDeleteService(hService))
284 result = true;
285 pCloseServiceHandle(hService);
286 }
287 pCloseServiceHandle(hSCM);
288 }
289 return result;
290 }
291
start(const QStringList & args)292 bool QtServiceController::start(const QStringList &args)
293 {
294 Q_D(QtServiceController);
295 bool result = false;
296 if (!winServiceInit())
297 return result;
298
299 // Open the Service Control Manager
300 SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_CONNECT);
301 if (hSCM) {
302 // Try to open the service
303 SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(), SERVICE_START);
304 if (hService) {
305 QVector<const wchar_t *> argv(args.size());
306 for (int i = 0; i < args.size(); ++i)
307 argv[i] = (const wchar_t*)args.at(i).utf16();
308
309 if (pStartService(hService, args.size(), argv.data()))
310 result = true;
311 pCloseServiceHandle(hService);
312 }
313 pCloseServiceHandle(hSCM);
314 }
315 return result;
316 }
317
stop()318 bool QtServiceController::stop()
319 {
320 Q_D(QtServiceController);
321 bool result = false;
322 if (!winServiceInit())
323 return result;
324
325 SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_CONNECT);
326 if (hSCM) {
327 SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(), SERVICE_STOP|SERVICE_QUERY_STATUS);
328 if (hService) {
329 SERVICE_STATUS status;
330 if (pControlService(hService, SERVICE_CONTROL_STOP, &status)) {
331 bool stopped = status.dwCurrentState == SERVICE_STOPPED;
332 int i = 0;
333 while(!stopped && i < 10) {
334 Sleep(200);
335 if (!pQueryServiceStatus(hService, &status))
336 break;
337 stopped = status.dwCurrentState == SERVICE_STOPPED;
338 ++i;
339 }
340 result = stopped;
341 } else {
342 qErrnoWarning(GetLastError(), "stopping");
343 }
344 pCloseServiceHandle(hService);
345 }
346 pCloseServiceHandle(hSCM);
347 }
348 return result;
349 }
350
pause()351 bool QtServiceController::pause()
352 {
353 Q_D(QtServiceController);
354 bool result = false;
355 if (!winServiceInit())
356 return result;
357
358 SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_CONNECT);
359 if (hSCM) {
360 SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(),
361 SERVICE_PAUSE_CONTINUE);
362 if (hService) {
363 SERVICE_STATUS status;
364 if (pControlService(hService, SERVICE_CONTROL_PAUSE, &status))
365 result = true;
366 pCloseServiceHandle(hService);
367 }
368 pCloseServiceHandle(hSCM);
369 }
370 return result;
371 }
372
resume()373 bool QtServiceController::resume()
374 {
375 Q_D(QtServiceController);
376 bool result = false;
377 if (!winServiceInit())
378 return result;
379
380 SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_CONNECT);
381 if (hSCM) {
382 SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(),
383 SERVICE_PAUSE_CONTINUE);
384 if (hService) {
385 SERVICE_STATUS status;
386 if (pControlService(hService, SERVICE_CONTROL_CONTINUE, &status))
387 result = true;
388 pCloseServiceHandle(hService);
389 }
390 pCloseServiceHandle(hSCM);
391 }
392 return result;
393 }
394
sendCommand(int code)395 bool QtServiceController::sendCommand(int code)
396 {
397 Q_D(QtServiceController);
398 bool result = false;
399 if (!winServiceInit())
400 return result;
401
402 if (code < 0 || code > 127 || !isRunning())
403 return result;
404
405 SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_CONNECT);
406 if (hSCM) {
407 SC_HANDLE hService = pOpenService(hSCM, (wchar_t *)d->serviceName.utf16(),
408 SERVICE_USER_DEFINED_CONTROL);
409 if (hService) {
410 SERVICE_STATUS status;
411 if (pControlService(hService, 128 + code, &status))
412 result = true;
413 pCloseServiceHandle(hService);
414 }
415 pCloseServiceHandle(hSCM);
416 }
417 return result;
418 }
419
420 #if defined(QTSERVICE_DEBUG)
421 # if QT_VERSION >= 0x050000
422 extern void qtServiceLogDebug(QtMsgType type, const QMessageLogContext &context, const QString &msg);
423 # else
424 extern void qtServiceLogDebug(QtMsgType type, const char* msg);
425 # endif
426 #endif
427
logMessage(const QString & message,MessageType type,int id,uint category,const QByteArray & data)428 void QtServiceBase::logMessage(const QString &message, MessageType type,
429 int id, uint category, const QByteArray &data)
430 {
431 #if defined(QTSERVICE_DEBUG)
432 QByteArray dbgMsg("[LOGGED ");
433 switch (type) {
434 case Error: dbgMsg += "Error] " ; break;
435 case Warning: dbgMsg += "Warning] "; break;
436 case Success: dbgMsg += "Success] "; break;
437 case Information: //fall through
438 default: dbgMsg += "Information] "; break;
439 }
440 # if QT_VERSION >= 0x050000
441 qtServiceLogDebug((QtMsgType)-1, QMessageLogContext(), QLatin1String(dbgMsg) + message);
442 # else
443 qtServiceLogDebug((QtMsgType)-1, (dbgMsg + message.toAscii()).constData());
444 # endif
445 #endif
446
447 Q_D(QtServiceBase);
448 if (!winServiceInit())
449 return;
450 WORD wType;
451 switch (type) {
452 case Error: wType = EVENTLOG_ERROR_TYPE; break;
453 case Warning: wType = EVENTLOG_WARNING_TYPE; break;
454 case Information: wType = EVENTLOG_INFORMATION_TYPE; break;
455 default: wType = EVENTLOG_SUCCESS; break;
456 }
457 HANDLE h = pRegisterEventSource(0, (wchar_t *)d->controller.serviceName().utf16());
458 if (h) {
459 const wchar_t *msg = (wchar_t*)message.utf16();
460 const char *bindata = data.size() ? data.constData() : 0;
461 pReportEvent(h, wType, category, id, 0, 1, data.size(),(const wchar_t **)&msg,
462 const_cast<char *>(bindata));
463 pDeregisterEventSource(h);
464 }
465 }
466
467 class QtServiceControllerHandler : public QObject
468 {
469 Q_OBJECT
470 public:
471 QtServiceControllerHandler(QtServiceSysPrivate *sys);
472
473 protected:
474 void customEvent(QEvent *e);
475
476 private:
477 QtServiceSysPrivate *d_sys;
478 };
479
480 class QtServiceSysPrivate
481 {
482 public:
483 enum {
484 QTSERVICE_STARTUP = 256
485 };
486 QtServiceSysPrivate();
487
488 void setStatus( DWORD dwState );
489 void setServiceFlags(QtServiceBase::ServiceFlags flags);
490 DWORD serviceFlags(QtServiceBase::ServiceFlags flags) const;
491 inline bool available() const;
492 static void WINAPI serviceMain( DWORD dwArgc, wchar_t** lpszArgv );
493 static void WINAPI handler( DWORD dwOpcode );
494
495 SERVICE_STATUS status;
496 SERVICE_STATUS_HANDLE serviceStatus;
497 QStringList serviceArgs;
498
499 static QtServiceSysPrivate *instance;
500 #if QT_VERSION < 0x050000
501 static QCoreApplication::EventFilter nextFilter;
502 #endif
503
504 QWaitCondition condition;
505 QMutex mutex;
506 QSemaphore startSemaphore;
507 QSemaphore startSemaphore2;
508
509 QtServiceControllerHandler *controllerHandler;
510
511 void handleCustomEvent(QEvent *e);
512 };
513
QtServiceControllerHandler(QtServiceSysPrivate * sys)514 QtServiceControllerHandler::QtServiceControllerHandler(QtServiceSysPrivate *sys)
515 : QObject(), d_sys(sys)
516 {
517
518 }
519
customEvent(QEvent * e)520 void QtServiceControllerHandler::customEvent(QEvent *e)
521 {
522 d_sys->handleCustomEvent(e);
523 }
524
525
526 QtServiceSysPrivate *QtServiceSysPrivate::instance = 0;
527 #if QT_VERSION < 0x050000
528 QCoreApplication::EventFilter QtServiceSysPrivate::nextFilter = 0;
529 #endif
530
QtServiceSysPrivate()531 QtServiceSysPrivate::QtServiceSysPrivate()
532 {
533 instance = this;
534 }
535
available() const536 inline bool QtServiceSysPrivate::available() const
537 {
538 return 0 != pOpenSCManager;
539 }
540
serviceMain(DWORD dwArgc,wchar_t ** lpszArgv)541 void WINAPI QtServiceSysPrivate::serviceMain(DWORD dwArgc, wchar_t** lpszArgv)
542 {
543 if (!instance || !QtServiceBase::instance())
544 return;
545
546 // Windows spins off a random thread to call this function on
547 // startup, so here we just signal to the QApplication event loop
548 // in the main thread to go ahead with start()'ing the service.
549
550 for (DWORD i = 0; i < dwArgc; i++)
551 instance->serviceArgs.append(QString::fromUtf16((unsigned short*)lpszArgv[i]));
552
553 instance->startSemaphore.release(); // let the qapp creation start
554 instance->startSemaphore2.acquire(); // wait until its done
555 // Register the control request handler
556 instance->serviceStatus = pRegisterServiceCtrlHandler((TCHAR*)QtServiceBase::instance()->serviceName().utf16(), handler);
557
558 if (!instance->serviceStatus) // cannot happen - something is utterly wrong
559 return;
560
561 handler(QTSERVICE_STARTUP); // Signal startup to the application -
562 // causes QtServiceBase::start() to be called in the main thread
563
564 // The MSDN doc says that this thread should just exit - the service is
565 // running in the main thread (here, via callbacks in the handler thread).
566 }
567
568
569 // The handler() is called from the thread that called
570 // StartServiceCtrlDispatcher, i.e. our HandlerThread, and
571 // not from the main thread that runs the event loop, so we
572 // have to post an event to ourselves, and use a QWaitCondition
573 // and a QMutex to synchronize.
handleCustomEvent(QEvent * e)574 void QtServiceSysPrivate::handleCustomEvent(QEvent *e)
575 {
576 int code = e->type() - QEvent::User;
577
578 switch(code) {
579 case QTSERVICE_STARTUP: // Startup
580 QtServiceBase::instance()->start();
581 break;
582 case SERVICE_CONTROL_STOP:
583 QtServiceBase::instance()->stop();
584 QCoreApplication::instance()->quit();
585 break;
586 case SERVICE_CONTROL_PAUSE:
587 QtServiceBase::instance()->pause();
588 break;
589 case SERVICE_CONTROL_CONTINUE:
590 QtServiceBase::instance()->resume();
591 break;
592 default:
593 if (code >= 128 && code <= 255)
594 QtServiceBase::instance()->processCommand(code - 128);
595 break;
596 }
597
598 mutex.lock();
599 condition.wakeAll();
600 mutex.unlock();
601 }
602
handler(DWORD code)603 void WINAPI QtServiceSysPrivate::handler( DWORD code )
604 {
605 if (!instance)
606 return;
607
608 instance->mutex.lock();
609 switch (code) {
610 case QTSERVICE_STARTUP: // QtService startup (called from WinMain when started)
611 instance->setStatus(SERVICE_START_PENDING);
612 QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code)));
613 instance->condition.wait(&instance->mutex);
614 instance->setStatus(SERVICE_RUNNING);
615 break;
616 case SERVICE_CONTROL_STOP: // 1
617 instance->setStatus(SERVICE_STOP_PENDING);
618 QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code)));
619 instance->condition.wait(&instance->mutex);
620 // status will be reported as stopped by start() when qapp::exec returns
621 break;
622
623 case SERVICE_CONTROL_PAUSE: // 2
624 instance->setStatus(SERVICE_PAUSE_PENDING);
625 QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code)));
626 instance->condition.wait(&instance->mutex);
627 instance->setStatus(SERVICE_PAUSED);
628 break;
629
630 case SERVICE_CONTROL_CONTINUE: // 3
631 instance->setStatus(SERVICE_CONTINUE_PENDING);
632 QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code)));
633 instance->condition.wait(&instance->mutex);
634 instance->setStatus(SERVICE_RUNNING);
635 break;
636
637 case SERVICE_CONTROL_INTERROGATE: // 4
638 break;
639
640 case SERVICE_CONTROL_SHUTDOWN: // 5
641 // Don't waste time with reporting stop pending, just do it
642 QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + SERVICE_CONTROL_STOP)));
643 instance->condition.wait(&instance->mutex);
644 // status will be reported as stopped by start() when qapp::exec returns
645 break;
646
647 default:
648 if ( code >= 128 && code <= 255 ) {
649 QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code)));
650 instance->condition.wait(&instance->mutex);
651 }
652 break;
653 }
654
655 instance->mutex.unlock();
656
657 // Report current status
658 if (instance->available() && instance->status.dwCurrentState != SERVICE_STOPPED)
659 pSetServiceStatus(instance->serviceStatus, &instance->status);
660 }
661
setStatus(DWORD state)662 void QtServiceSysPrivate::setStatus(DWORD state)
663 {
664 if (!available())
665 return;
666 status.dwCurrentState = state;
667 pSetServiceStatus(serviceStatus, &status);
668 }
669
setServiceFlags(QtServiceBase::ServiceFlags flags)670 void QtServiceSysPrivate::setServiceFlags(QtServiceBase::ServiceFlags flags)
671 {
672 if (!available())
673 return;
674 status.dwControlsAccepted = serviceFlags(flags);
675 pSetServiceStatus(serviceStatus, &status);
676 }
677
serviceFlags(QtServiceBase::ServiceFlags flags) const678 DWORD QtServiceSysPrivate::serviceFlags(QtServiceBase::ServiceFlags flags) const
679 {
680 DWORD control = 0;
681 if (flags & QtServiceBase::CanBeSuspended)
682 control |= SERVICE_ACCEPT_PAUSE_CONTINUE;
683 if (!(flags & QtServiceBase::CannotBeStopped))
684 control |= SERVICE_ACCEPT_STOP;
685 if (flags & QtServiceBase::NeedsStopOnShutdown)
686 control |= SERVICE_ACCEPT_SHUTDOWN;
687
688 return control;
689 }
690
691 #include "qtservice_win.moc"
692
693
694 class HandlerThread : public QThread
695 {
696 public:
HandlerThread()697 HandlerThread()
698 : success(true), console(false), QThread()
699 {}
700
calledOk()701 bool calledOk() { return success; }
runningAsConsole()702 bool runningAsConsole() { return console; }
703
704 protected:
705 bool success, console;
run()706 void run()
707 {
708 SERVICE_TABLE_ENTRYW st [2];
709 st[0].lpServiceName = (wchar_t*)QtServiceBase::instance()->serviceName().utf16();
710 st[0].lpServiceProc = QtServiceSysPrivate::serviceMain;
711 st[1].lpServiceName = 0;
712 st[1].lpServiceProc = 0;
713
714 success = (pStartServiceCtrlDispatcher(st) != 0); // should block
715
716 if (!success) {
717 if (GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
718 // Means we're started from console, not from service mgr
719 // start() will ask the mgr to start another instance of us as a service instead
720 console = true;
721 }
722 else {
723 QtServiceBase::instance()->logMessage(QString("The Service failed to start [%1]").arg(qt_error_string(GetLastError())), QtServiceBase::Error);
724 }
725 QtServiceSysPrivate::instance->startSemaphore.release(); // let start() continue, since serviceMain won't be doing it
726 }
727 }
728 };
729
730 /*
731 Ignore WM_ENDSESSION system events, since they make the Qt kernel quit
732 */
733
734 #if QT_VERSION >= 0x050000
735
736 class QtServiceAppEventFilter : public QAbstractNativeEventFilter
737 {
738 public:
QtServiceAppEventFilter()739 QtServiceAppEventFilter() {}
740 bool nativeEventFilter(const QByteArray &eventType, void *message, long *result);
741 };
742
nativeEventFilter(const QByteArray &,void * message,long * result)743 bool QtServiceAppEventFilter::nativeEventFilter(const QByteArray &, void *message, long *result)
744 {
745 MSG *winMessage = (MSG*)message;
746 if (winMessage->message == WM_ENDSESSION && (winMessage->lParam & ENDSESSION_LOGOFF)) {
747 *result = TRUE;
748 return true;
749 }
750 return false;
751 }
752
Q_GLOBAL_STATIC(QtServiceAppEventFilter,qtServiceAppEventFilter)753 Q_GLOBAL_STATIC(QtServiceAppEventFilter, qtServiceAppEventFilter)
754
755 #else
756
757 bool myEventFilter(void* message, long* result)
758 {
759 MSG* msg = reinterpret_cast<MSG*>(message);
760 if (!msg || (msg->message != WM_ENDSESSION) || !(msg->lParam & ENDSESSION_LOGOFF))
761 return QtServiceSysPrivate::nextFilter ? QtServiceSysPrivate::nextFilter(message, result) : false;
762
763 if (QtServiceSysPrivate::nextFilter)
764 QtServiceSysPrivate::nextFilter(message, result);
765 if (result)
766 *result = TRUE;
767 return true;
768 }
769
770 #endif
771
772 /* There are three ways we can be started:
773
774 - By a service controller (e.g. the Services control panel), with
775 no (service-specific) arguments. ServiceBase::exec() will then call
776 start() below, and the service will start.
777
778 - From the console, but with no (service-specific) arguments. This
779 means we should ask a controller to start the service (i.e. another
780 instance of this executable), and then just terminate. We discover
781 this case (as different from the above) by the fact that
782 StartServiceCtrlDispatcher will return an error, instead of blocking.
783
784 - From the console, with -e(xec) argument. ServiceBase::exec() will
785 then call ServiceBasePrivate::exec(), which calls
786 ServiceBasePrivate::run(), which runs the application as a normal
787 program.
788 */
789
790 bool QtServiceBasePrivate::start()
791 {
792 sysInit();
793 if (!winServiceInit())
794 return false;
795
796 // Since StartServiceCtrlDispatcher() blocks waiting for service
797 // control events, we need to call it in another thread, so that
798 // the main thread can run the QApplication event loop.
799 HandlerThread* ht = new HandlerThread();
800 ht->start();
801
802 QtServiceSysPrivate* sys = QtServiceSysPrivate::instance;
803
804 // Wait until service args have been received by serviceMain.
805 // If Windows doesn't call serviceMain (or
806 // StartServiceControlDispatcher doesn't return an error) within
807 // a timeout of 20 secs, something is very wrong; give up
808 if (!sys->startSemaphore.tryAcquire(1, 20000))
809 return false;
810
811 if (!ht->calledOk()) {
812 if (ht->runningAsConsole())
813 return controller.start(args.mid(1));
814 else
815 return false;
816 }
817
818 int argc = sys->serviceArgs.size();
819 QVector<char *> argv(argc);
820 QList<QByteArray> argvData;
821 for (int i = 0; i < argc; ++i)
822 argvData.append(sys->serviceArgs.at(i).toLocal8Bit());
823 for (int i = 0; i < argc; ++i)
824 argv[i] = argvData[i].data();
825
826 q_ptr->createApplication(argc, argv.data());
827 QCoreApplication *app = QCoreApplication::instance();
828 if (!app)
829 return false;
830
831 #if QT_VERSION >= 0x050000
832 QAbstractEventDispatcher::instance()->installNativeEventFilter(qtServiceAppEventFilter());
833 #else
834 QtServiceSysPrivate::nextFilter = app->setEventFilter(myEventFilter);
835 #endif
836
837 sys->controllerHandler = new QtServiceControllerHandler(sys);
838
839 sys->startSemaphore2.release(); // let serviceMain continue (and end)
840
841 sys->status.dwWin32ExitCode = q_ptr->executeApplication();
842 sys->setStatus(SERVICE_STOPPED);
843
844 if (ht->isRunning())
845 ht->wait(1000); // let the handler thread finish
846 delete sys->controllerHandler;
847 sys->controllerHandler = 0;
848 if (ht->isFinished())
849 delete ht;
850 delete app;
851 sysCleanup();
852 return true;
853 }
854
install(const QString & account,const QString & password)855 bool QtServiceBasePrivate::install(const QString &account, const QString &password)
856 {
857 bool result = false;
858 if (!winServiceInit())
859 return result;
860
861 // Open the Service Control Manager
862 SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
863 if (hSCM) {
864 QString acc = account;
865 DWORD dwStartType = startupType == QtServiceController::AutoStartup ? SERVICE_AUTO_START : SERVICE_DEMAND_START;
866 DWORD dwServiceType = SERVICE_WIN32_OWN_PROCESS;
867 wchar_t *act = 0;
868 wchar_t *pwd = 0;
869 if (!acc.isEmpty()) {
870 // The act string must contain a string of the format "Domain\UserName",
871 // so if only a username was specified without a domain, default to the local machine domain.
872 if (!acc.contains(QChar('\\'))) {
873 acc.prepend(QLatin1String(".\\"));
874 }
875 if (!acc.endsWith(QLatin1String("\\LocalSystem")))
876 act = (wchar_t*)acc.utf16();
877 }
878 if (!password.isEmpty() && act) {
879 pwd = (wchar_t*)password.utf16();
880 }
881
882 // Only set INTERACTIVE if act is LocalSystem. (and act should be 0 if it is LocalSystem).
883 if (!act) dwServiceType |= SERVICE_INTERACTIVE_PROCESS;
884
885 // Create the service
886 SC_HANDLE hService = pCreateService(hSCM, (wchar_t *)controller.serviceName().utf16(),
887 (wchar_t *)controller.serviceName().utf16(),
888 SERVICE_ALL_ACCESS,
889 dwServiceType, // QObject::inherits ( const char * className ) for no inter active ????
890 dwStartType, SERVICE_ERROR_NORMAL, (wchar_t *)filePath().utf16(),
891 0, 0, 0,
892 act, pwd);
893 if (hService) {
894 result = true;
895 if (!serviceDescription.isEmpty()) {
896 SERVICE_DESCRIPTION sdesc;
897 sdesc.lpDescription = (wchar_t *)serviceDescription.utf16();
898 pChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &sdesc);
899 }
900 pCloseServiceHandle(hService);
901 }
902 pCloseServiceHandle(hSCM);
903 }
904 return result;
905 }
906
filePath() const907 QString QtServiceBasePrivate::filePath() const
908 {
909 wchar_t path[_MAX_PATH];
910 ::GetModuleFileNameW( 0, path, sizeof(path) );
911 return QString::fromUtf16((unsigned short*)path);
912 }
913
sysInit()914 bool QtServiceBasePrivate::sysInit()
915 {
916 sysd = new QtServiceSysPrivate();
917
918 sysd->serviceStatus = 0;
919 sysd->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS;
920 sysd->status.dwCurrentState = SERVICE_STOPPED;
921 sysd->status.dwControlsAccepted = sysd->serviceFlags(serviceFlags);
922 sysd->status.dwWin32ExitCode = NO_ERROR;
923 sysd->status.dwServiceSpecificExitCode = 0;
924 sysd->status.dwCheckPoint = 0;
925 sysd->status.dwWaitHint = 0;
926
927 return true;
928 }
929
sysSetPath()930 void QtServiceBasePrivate::sysSetPath()
931 {
932
933 }
934
sysCleanup()935 void QtServiceBasePrivate::sysCleanup()
936 {
937 if (sysd) {
938 delete sysd;
939 sysd = 0;
940 }
941 }
942
setServiceFlags(QtServiceBase::ServiceFlags flags)943 void QtServiceBase::setServiceFlags(QtServiceBase::ServiceFlags flags)
944 {
945 if (d_ptr->serviceFlags == flags)
946 return;
947 d_ptr->serviceFlags = flags;
948 if (d_ptr->sysd)
949 d_ptr->sysd->setServiceFlags(flags);
950 }
951
952
953