QtWebApp
qtservice_win.cpp
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 
97 // Do not remove, even if the compiler warns that this variable is unused!
98 static PRegisterServiceProcess pRegisterServiceProcess = 0;
99 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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:
468 
469 protected:
470  void customEvent(QEvent *e);
471 
472 private:
473  QtServiceSysPrivate *d_sys;
474 };
475 
477 {
478 public:
479  enum {
480  QTSERVICE_STARTUP = 256
481  };
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 #if QT_VERSION < 0x050000
497  static QCoreApplication::EventFilter nextFilter;
498 #endif
499 
500  QWaitCondition condition;
501  QMutex mutex;
502  QSemaphore startSemaphore;
503  QSemaphore startSemaphore2;
504 
505  QtServiceControllerHandler *controllerHandler;
506 
507  void handleCustomEvent(QEvent *e);
508 };
509 
510 QtServiceControllerHandler::QtServiceControllerHandler(QtServiceSysPrivate *sys)
511  : QObject(), d_sys(sys)
512 {
513 
514 }
515 
516 void QtServiceControllerHandler::customEvent(QEvent *e)
517 {
518  d_sys->handleCustomEvent(e);
519 }
520 
521 
522 QtServiceSysPrivate *QtServiceSysPrivate::instance = 0;
523 #if QT_VERSION < 0x050000
524 QCoreApplication::EventFilter QtServiceSysPrivate::nextFilter = 0;
525 #endif
526 
527 QtServiceSysPrivate::QtServiceSysPrivate()
528 {
529  instance = this;
530 }
531 
532 inline bool QtServiceSysPrivate::available() const
533 {
534  return 0 != pOpenSCManager;
535 }
536 
537 void WINAPI QtServiceSysPrivate::serviceMain(DWORD dwArgc, wchar_t** lpszArgv)
538 {
539  if (!instance || !QtServiceBase::instance())
540  return;
541 
542  // Windows spins off a random thread to call this function on
543  // startup, so here we just signal to the QApplication event loop
544  // in the main thread to go ahead with start()'ing the service.
545 
546  for (DWORD i = 0; i < dwArgc; i++)
547  instance->serviceArgs.append(QString::fromUtf16((unsigned short*)lpszArgv[i]));
548 
549  instance->startSemaphore.release(); // let the qapp creation start
550  instance->startSemaphore2.acquire(); // wait until its done
551  // Register the control request handler
552  instance->serviceStatus = pRegisterServiceCtrlHandler((TCHAR*)QtServiceBase::instance()->serviceName().utf16(), handler);
553 
554  if (!instance->serviceStatus) // cannot happen - something is utterly wrong
555  return;
556 
557  handler(QTSERVICE_STARTUP); // Signal startup to the application -
558  // causes QtServiceBase::start() to be called in the main thread
559 
560  // The MSDN doc says that this thread should just exit - the service is
561  // running in the main thread (here, via callbacks in the handler thread).
562 }
563 
564 
565 // The handler() is called from the thread that called
566 // StartServiceCtrlDispatcher, i.e. our HandlerThread, and
567 // not from the main thread that runs the event loop, so we
568 // have to post an event to ourselves, and use a QWaitCondition
569 // and a QMutex to synchronize.
570 void QtServiceSysPrivate::handleCustomEvent(QEvent *e)
571 {
572  int code = e->type() - QEvent::User;
573 
574  switch(code) {
575  case QTSERVICE_STARTUP: // Startup
577  break;
578  case SERVICE_CONTROL_STOP:
580  QCoreApplication::instance()->quit();
581  break;
582  case SERVICE_CONTROL_PAUSE:
584  break;
585  case SERVICE_CONTROL_CONTINUE:
587  break;
588  default:
589  if (code >= 128 && code <= 255)
591  break;
592  }
593 
594  mutex.lock();
595  condition.wakeAll();
596  mutex.unlock();
597 }
598 
599 void WINAPI QtServiceSysPrivate::handler( DWORD code )
600 {
601  if (!instance)
602  return;
603 
604  instance->mutex.lock();
605  switch (code) {
606  case QTSERVICE_STARTUP: // QtService startup (called from WinMain when started)
607  instance->setStatus(SERVICE_START_PENDING);
608  QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code)));
609  instance->condition.wait(&instance->mutex);
610  instance->setStatus(SERVICE_RUNNING);
611  break;
612  case SERVICE_CONTROL_STOP: // 1
613  instance->setStatus(SERVICE_STOP_PENDING);
614  QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code)));
615  instance->condition.wait(&instance->mutex);
616  // status will be reported as stopped by start() when qapp::exec returns
617  break;
618 
619  case SERVICE_CONTROL_PAUSE: // 2
620  instance->setStatus(SERVICE_PAUSE_PENDING);
621  QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code)));
622  instance->condition.wait(&instance->mutex);
623  instance->setStatus(SERVICE_PAUSED);
624  break;
625 
626  case SERVICE_CONTROL_CONTINUE: // 3
627  instance->setStatus(SERVICE_CONTINUE_PENDING);
628  QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code)));
629  instance->condition.wait(&instance->mutex);
630  instance->setStatus(SERVICE_RUNNING);
631  break;
632 
633  case SERVICE_CONTROL_INTERROGATE: // 4
634  break;
635 
636  case SERVICE_CONTROL_SHUTDOWN: // 5
637  // Don't waste time with reporting stop pending, just do it
638  QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + SERVICE_CONTROL_STOP)));
639  instance->condition.wait(&instance->mutex);
640  // status will be reported as stopped by start() when qapp::exec returns
641  break;
642 
643  default:
644  if ( code >= 128 && code <= 255 ) {
645  QCoreApplication::postEvent(instance->controllerHandler, new QEvent(QEvent::Type(QEvent::User + code)));
646  instance->condition.wait(&instance->mutex);
647  }
648  break;
649  }
650 
651  instance->mutex.unlock();
652 
653  // Report current status
654  if (instance->available() && instance->status.dwCurrentState != SERVICE_STOPPED)
655  pSetServiceStatus(instance->serviceStatus, &instance->status);
656 }
657 
658 void QtServiceSysPrivate::setStatus(DWORD state)
659 {
660  if (!available())
661  return;
662  status.dwCurrentState = state;
663  pSetServiceStatus(serviceStatus, &status);
664 }
665 
666 void QtServiceSysPrivate::setServiceFlags(QtServiceBase::ServiceFlags flags)
667 {
668  if (!available())
669  return;
670  status.dwControlsAccepted = serviceFlags(flags);
671  pSetServiceStatus(serviceStatus, &status);
672 }
673 
674 DWORD QtServiceSysPrivate::serviceFlags(QtServiceBase::ServiceFlags flags) const
675 {
676  DWORD control = 0;
677  if (flags & QtServiceBase::CanBeSuspended)
678  control |= SERVICE_ACCEPT_PAUSE_CONTINUE;
679  if (!(flags & QtServiceBase::CannotBeStopped))
680  control |= SERVICE_ACCEPT_STOP;
681  if (flags & QtServiceBase::NeedsStopOnShutdown)
682  control |= SERVICE_ACCEPT_SHUTDOWN;
683 
684  return control;
685 }
686 
687 #include "qtservice_win.moc"
688 
689 
690 class HandlerThread : public QThread
691 {
692 public:
693  HandlerThread()
694  : QThread(), success(true), console(false)
695  {}
696 
697  bool calledOk() { return success; }
698  bool runningAsConsole() { return console; }
699 
700 protected:
701  bool success;
702  bool console;
703  void run()
704  {
705  SERVICE_TABLE_ENTRYW st [2];
706  st[0].lpServiceName = (wchar_t*)QtServiceBase::instance()->serviceName().utf16();
707  st[0].lpServiceProc = QtServiceSysPrivate::serviceMain;
708  st[1].lpServiceName = 0;
709  st[1].lpServiceProc = 0;
710 
711  success = (pStartServiceCtrlDispatcher(st) != 0); // should block
712 
713  if (!success) {
714  if (GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) {
715  // Means we're started from console, not from service mgr
716  // start() will ask the mgr to start another instance of us as a service instead
717  console = true;
718  }
719  else {
720  QtServiceBase::instance()->logMessage(QString("The Service failed to start [%1]").arg(qt_error_string(GetLastError())), QtServiceBase::Error);
721  }
722  QtServiceSysPrivate::instance->startSemaphore.release(); // let start() continue, since serviceMain won't be doing it
723  }
724  }
725 };
726 
727 /*
728  Ignore WM_ENDSESSION system events, since they make the Qt kernel quit
729 */
730 
731 #if QT_VERSION >= 0x050000
732 
733 class QtServiceAppEventFilter : public QAbstractNativeEventFilter
734 {
735 public:
736  QtServiceAppEventFilter() {}
737  bool nativeEventFilter(const QByteArray &eventType, void *message, long *result);
738 };
739 
740 bool QtServiceAppEventFilter::nativeEventFilter(const QByteArray &, void *message, long *result)
741 {
742  MSG *winMessage = (MSG*)message;
743  if (winMessage->message == WM_ENDSESSION && (winMessage->lParam & ENDSESSION_LOGOFF)) {
744  *result = TRUE;
745  return true;
746  }
747  return false;
748 }
749 
750 Q_GLOBAL_STATIC(QtServiceAppEventFilter, qtServiceAppEventFilter)
751 
752 #else
753 
754 bool myEventFilter(void* message, long* result)
755 {
756  MSG* msg = reinterpret_cast<MSG*>(message);
757  if (!msg || (msg->message != WM_ENDSESSION) || !(msg->lParam & ENDSESSION_LOGOFF))
758  return QtServiceSysPrivate::nextFilter ? QtServiceSysPrivate::nextFilter(message, result) : false;
759 
760  if (QtServiceSysPrivate::nextFilter)
761  QtServiceSysPrivate::nextFilter(message, result);
762  if (result)
763  *result = TRUE;
764  return true;
765 }
766 
767 #endif
768 
769 /* There are three ways we can be started:
770 
771  - By a service controller (e.g. the Services control panel), with
772  no (service-specific) arguments. ServiceBase::exec() will then call
773  start() below, and the service will start.
774 
775  - From the console, but with no (service-specific) arguments. This
776  means we should ask a controller to start the service (i.e. another
777  instance of this executable), and then just terminate. We discover
778  this case (as different from the above) by the fact that
779  StartServiceCtrlDispatcher will return an error, instead of blocking.
780 
781  - From the console, with -e(xec) argument. ServiceBase::exec() will
782  then call ServiceBasePrivate::exec(), which calls
783  ServiceBasePrivate::run(), which runs the application as a normal
784  program.
785 */
786 
787 bool QtServiceBasePrivate::start()
788 {
789  sysInit();
790  if (!winServiceInit())
791  return false;
792 
793  // Since StartServiceCtrlDispatcher() blocks waiting for service
794  // control events, we need to call it in another thread, so that
795  // the main thread can run the QApplication event loop.
796  HandlerThread* ht = new HandlerThread();
797  ht->start();
798 
799  QtServiceSysPrivate* sys = QtServiceSysPrivate::instance;
800 
801  // Wait until service args have been received by serviceMain.
802  // If Windows doesn't call serviceMain (or
803  // StartServiceControlDispatcher doesn't return an error) within
804  // a timeout of 20 secs, something is very wrong; give up
805  if (!sys->startSemaphore.tryAcquire(1, 20000))
806  return false;
807 
808  if (!ht->calledOk()) {
809  if (ht->runningAsConsole())
810  return controller.start(args.mid(1));
811  else
812  return false;
813  }
814 
815  int argc = sys->serviceArgs.size();
816  QVector<char *> argv(argc);
817  QList<QByteArray> argvData;
818  for (int i = 0; i < argc; ++i)
819  argvData.append(sys->serviceArgs.at(i).toLocal8Bit());
820  for (int i = 0; i < argc; ++i)
821  argv[i] = argvData[i].data();
822 
823  q_ptr->createApplication(argc, argv.data());
824  QCoreApplication *app = QCoreApplication::instance();
825  if (!app)
826  return false;
827 
828 #if QT_VERSION >= 0x050000
829  QAbstractEventDispatcher::instance()->installNativeEventFilter(qtServiceAppEventFilter());
830 #else
831  QtServiceSysPrivate::nextFilter = app->setEventFilter(myEventFilter);
832 #endif
833 
834  sys->controllerHandler = new QtServiceControllerHandler(sys);
835 
836  sys->startSemaphore2.release(); // let serviceMain continue (and end)
837 
838  sys->status.dwWin32ExitCode = q_ptr->executeApplication();
839  sys->setStatus(SERVICE_STOPPED);
840 
841  if (ht->isRunning())
842  ht->wait(1000); // let the handler thread finish
843  delete sys->controllerHandler;
844  sys->controllerHandler = 0;
845  if (ht->isFinished())
846  delete ht;
847  delete app;
848  sysCleanup();
849  return true;
850 }
851 
852 bool QtServiceBasePrivate::install(const QString &account, const QString &password)
853 {
854  bool result = false;
855  if (!winServiceInit())
856  return result;
857 
858  // Open the Service Control Manager
859  SC_HANDLE hSCM = pOpenSCManager(0, 0, SC_MANAGER_ALL_ACCESS);
860  if (hSCM) {
861  QString acc = account;
862  DWORD dwStartType = startupType == QtServiceController::AutoStartup ? SERVICE_AUTO_START : SERVICE_DEMAND_START;
863  DWORD dwServiceType = SERVICE_WIN32_OWN_PROCESS;
864  wchar_t *act = 0;
865  wchar_t *pwd = 0;
866  if (!acc.isEmpty()) {
867  // The act string must contain a string of the format "Domain\UserName",
868  // so if only a username was specified without a domain, default to the local machine domain.
869  if (!acc.contains(QChar('\\'))) {
870  acc.prepend(QLatin1String(".\\"));
871  }
872  if (!acc.endsWith(QLatin1String("\\LocalSystem")))
873  act = (wchar_t*)acc.utf16();
874  }
875  if (!password.isEmpty() && act) {
876  pwd = (wchar_t*)password.utf16();
877  }
878 
879  // Only set INTERACTIVE if act is LocalSystem. (and act should be 0 if it is LocalSystem).
880  if (!act) dwServiceType |= SERVICE_INTERACTIVE_PROCESS;
881 
882  // Create the service
883  SC_HANDLE hService = pCreateService(hSCM, (wchar_t *)controller.serviceName().utf16(),
884  (wchar_t *)controller.serviceName().utf16(),
885  SERVICE_ALL_ACCESS,
886  dwServiceType, // QObject::inherits ( const char * className ) for no inter active ????
887  dwStartType, SERVICE_ERROR_NORMAL, (wchar_t *)filePath().utf16(),
888  0, 0, 0,
889  act, pwd);
890  if (hService) {
891  result = true;
892  if (!serviceDescription.isEmpty()) {
893  SERVICE_DESCRIPTION sdesc;
894  sdesc.lpDescription = (wchar_t *)serviceDescription.utf16();
895  pChangeServiceConfig2(hService, SERVICE_CONFIG_DESCRIPTION, &sdesc);
896  }
897  pCloseServiceHandle(hService);
898  }
899  pCloseServiceHandle(hSCM);
900  }
901  return result;
902 }
903 
904 QString QtServiceBasePrivate::filePath() const
905 {
906  wchar_t path[_MAX_PATH];
907  ::GetModuleFileNameW( 0, path, sizeof(path) );
908  return QString::fromUtf16((unsigned short*)path);
909 }
910 
911 bool QtServiceBasePrivate::sysInit()
912 {
913  sysd = new QtServiceSysPrivate();
914 
915  sysd->serviceStatus = 0;
916  sysd->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS;
917  sysd->status.dwCurrentState = SERVICE_STOPPED;
918  sysd->status.dwControlsAccepted = sysd->serviceFlags(serviceFlags);
919  sysd->status.dwWin32ExitCode = NO_ERROR;
920  sysd->status.dwServiceSpecificExitCode = 0;
921  sysd->status.dwCheckPoint = 0;
922  sysd->status.dwWaitHint = 0;
923 
924  return true;
925 }
926 
927 void QtServiceBasePrivate::sysSetPath()
928 {
929 
930 }
931 
932 void QtServiceBasePrivate::sysCleanup()
933 {
934  if (sysd) {
935  delete sysd;
936  sysd = 0;
937  }
938 }
939 
940 void QtServiceBase::setServiceFlags(QtServiceBase::ServiceFlags flags)
941 {
942  if (d_ptr->serviceFlags == flags)
943  return;
944  d_ptr->serviceFlags = flags;
945  if (d_ptr->sysd)
946  d_ptr->sysd->setServiceFlags(flags);
947 }
948 
949 
QString serviceDescription() const
QString serviceFilePath() const
virtual void stop()
Definition: qtservice.cpp:902
QString serviceName() const
Definition: qtservice.cpp:257
The QtServiceBase class provides an API for implementing Windows services and Unix daemons...
Definition: qtservice.h:103
static QtServiceBase * instance()
Definition: qtservice.cpp:870
The QtServiceController class allows you to control services from separate applications.
Definition: qtservice.h:66
StartupType startupType() const
virtual void start()=0
bool sendCommand(int code)
void logMessage(const QString &message, MessageType type=Success, int id=0, uint category=0, const QByteArray &data=QByteArray())
QString serviceName() const
Definition: qtservice.cpp:675
void setServiceFlags(ServiceFlags flags)
virtual void processCommand(int code)
Definition: qtservice.cpp:941
virtual void resume()
Definition: qtservice.cpp:928
virtual void pause()
Definition: qtservice.cpp:915
bool isInstalled() const