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