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