QtWebApp
qtservice.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 <stdio.h>
45 #include <QTimer>
46 #include <QVector>
47 #include <QProcess>
48 
49 #if defined(QTSERVICE_DEBUG)
50 #include <QDebug>
51 #include <QFile>
52 #include <QTime>
53 #include <QMutex>
54 #if defined(Q_OS_WIN32)
55 #include <qt_windows.h>
56 #else
57 #include <unistd.h>
58 #include <stdlib.h>
59 #endif
60 
61 static QFile* f = 0;
62 
63 static void qtServiceCloseDebugLog()
64 {
65  if (!f)
66  return;
67  QString ps(QTime::currentTime().toString("HH:mm:ss.zzz ") + QLatin1String("--- DEBUG LOG CLOSED ---\n\n"));
68  f->write(ps.toLatin1());
69  f->flush();
70  f->close();
71  delete f;
72  f = 0;
73 }
74 
75 void qtServiceLogDebug(QtMsgType type, const char* msg)
76 {
77  static QMutex mutex;
78  QMutexLocker locker(&mutex);
79  QString s(QTime::currentTime().toString("HH:mm:ss.zzz "));
80  s += QString("[%1] ").arg(
81 #if defined(Q_OS_WIN32)
82  GetCurrentProcessId());
83 #else
84  getpid());
85 #endif
86 
87  if (!f) {
88 #if defined(Q_OS_WIN32)
89  f = new QFile("c:/service-debuglog.txt");
90 #else
91  f = new QFile("/tmp/service-debuglog.txt");
92 #endif
93  if (!f->open(QIODevice::WriteOnly | QIODevice::Append)) {
94  delete f;
95  f = 0;
96  return;
97  }
98  QString ps(QLatin1String("\n") + s + QLatin1String("--- DEBUG LOG OPENED ---\n"));
99  f->write(ps.toLatin1());
100  }
101 
102  switch (type) {
103  case QtWarningMsg:
104  s += QLatin1String("WARNING: ");
105  break;
106  case QtCriticalMsg:
107  s += QLatin1String("CRITICAL: ");
108  break;
109  case QtFatalMsg:
110  s+= QLatin1String("FATAL: ");
111  break;
112  case QtDebugMsg:
113  s += QLatin1String("DEBUG: ");
114  break;
115  default:
116  // Nothing
117  break;
118  }
119 
120  s += msg;
121  s += QLatin1String("\n");
122 
123  f->write(s.toLatin1());
124  f->flush();
125 
126  if (type == QtFatalMsg) {
127  qtServiceCloseDebugLog();
128  exit(1);
129  }
130 }
131 
132 #endif
133 
210  : d_ptr(new QtServiceControllerPrivate())
211 {
212  Q_D(QtServiceController);
213  d->q_ptr = this;
214  d->serviceName = name;
215 }
227 {
228  delete d_ptr;
229 }
258 {
259  Q_D(const QtServiceController);
260  return d->serviceName;
261 }
304 bool QtServiceController::install(const QString &serviceFilePath, const QString &account,
305  const QString &password)
306 {
307  QStringList arguments;
308  arguments << QLatin1String("-i");
309  arguments << account;
310  arguments << password;
311  return (QProcess::execute(serviceFilePath, arguments) == 0);
312 }
313 
314 
347 {
348  return start(QStringList());
349 }
350 
406 class QtServiceStarter : public QObject
407 {
408  Q_OBJECT
409 public:
411  : QObject(), d_ptr(service) {}
412 public slots:
413  void slotStart()
414  {
415  d_ptr->startService();
416  }
417 private:
418  QtServiceBasePrivate *d_ptr;
419 };
420 #include "qtservice.moc"
421 
422 QtServiceBase *QtServiceBasePrivate::instance = 0;
423 
424 QtServiceBasePrivate::QtServiceBasePrivate(const QString &name)
425  : startupType(QtServiceController::ManualStartup), serviceFlags(), controller(name)
426 {
427 
428 }
429 
430 QtServiceBasePrivate::~QtServiceBasePrivate()
431 {
432 
433 }
434 
435 void QtServiceBasePrivate::startService()
436 {
437  q_ptr->start();
438 }
439 
440 int QtServiceBasePrivate::run(bool asService, const QStringList &argList)
441 {
442  int argc = argList.size();
443  QVector<char *> argv(argc);
444  QList<QByteArray> argvData;
445  for (int i = 0; i < argc; ++i)
446  argvData.append(argList.at(i).toLocal8Bit());
447  for (int i = 0; i < argc; ++i)
448  argv[i] = argvData[i].data();
449 
450  if (asService && !sysInit())
451  return -1;
452 
453  q_ptr->createApplication(argc, argv.data());
454  QCoreApplication *app = QCoreApplication::instance();
455  if (!app)
456  return -1;
457 
458  if (asService)
459  sysSetPath();
460 
461  QtServiceStarter starter(this);
462  QTimer::singleShot(0, &starter, SLOT(slotStart()));
463  int res = q_ptr->executeApplication();
464  delete app;
465 
466  if (asService)
467  sysCleanup();
468  return res;
469 }
470 
471 
625 QtServiceBase::QtServiceBase(int argc, char **argv, const QString &name)
626 {
627 #if defined(QTSERVICE_DEBUG)
628  qInstallMsgHandler(qtServiceLogDebug);
629  qAddPostRoutine(qtServiceCloseDebugLog);
630 #endif
631 
632  Q_ASSERT(!QtServiceBasePrivate::instance);
633  QtServiceBasePrivate::instance = this;
634 
635  QString nm(name);
636  if (nm.length() > 255) {
637  qWarning("QtService: 'name' is longer than 255 characters.");
638  nm.truncate(255);
639  }
640  if (nm.contains('\\')) {
641  qWarning("QtService: 'name' contains backslashes '\\'.");
642  nm.replace((QChar)'\\', (QChar)'\0');
643  }
644 
645  d_ptr = new QtServiceBasePrivate(nm);
646  d_ptr->q_ptr = this;
647  #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
648  d_ptr->serviceFlags = ServiceFlags();
649  #else
650  d_ptr->serviceFlags = 0;
651  #endif
652  d_ptr->sysd = 0;
653  for (int i = 0; i < argc; ++i)
654  d_ptr->args.append(QString::fromLocal8Bit(argv[i]));
655 }
656 
668 {
669  delete d_ptr;
670  QtServiceBasePrivate::instance = 0;
671 }
672 
679 {
680  return d_ptr->controller.serviceName();
681 }
682 
689 {
690  return d_ptr->serviceDescription;
691 }
692 
698 void QtServiceBase::setServiceDescription(const QString &description)
699 {
700  d_ptr->serviceDescription = description;
701 }
702 
709 {
710  return d_ptr->startupType;
711 }
712 
719 {
720  d_ptr->startupType = type;
721 }
722 
729 QtServiceBase::ServiceFlags QtServiceBase::serviceFlags() const
730 {
731  return d_ptr->serviceFlags;
732 }
733 
759 {
760  if (d_ptr->args.size() > 1) {
761  QString a = d_ptr->args.at(1);
762  if (a == QLatin1String("-i") || a == QLatin1String("-install")) {
763  if (!d_ptr->controller.isInstalled()) {
764  QString account;
765  QString password;
766  if (d_ptr->args.size() > 2)
767  account = d_ptr->args.at(2);
768  if (d_ptr->args.size() > 3)
769  password = d_ptr->args.at(3);
770  if (!d_ptr->install(account, password)) {
771  fprintf(stderr, "The service %s could not be installed\n", serviceName().toLatin1().constData());
772  return -1;
773  } else {
774  printf("The service %s has been installed under: %s\n",
775  serviceName().toLatin1().constData(), d_ptr->filePath().toLatin1().constData());
776  }
777  } else {
778  fprintf(stderr, "The service %s is already installed\n", serviceName().toLatin1().constData());
779  }
780  return 0;
781  } else if (a == QLatin1String("-u") || a == QLatin1String("-uninstall")) {
782  if (d_ptr->controller.isInstalled()) {
783  if (!d_ptr->controller.uninstall()) {
784  fprintf(stderr, "The service %s could not be uninstalled\n", serviceName().toLatin1().constData());
785  return -1;
786  } else {
787  printf("The service %s has been uninstalled.\n",
788  serviceName().toLatin1().constData());
789  }
790  } else {
791  fprintf(stderr, "The service %s is not installed\n", serviceName().toLatin1().constData());
792  }
793  return 0;
794  } else if (a == QLatin1String("-v") || a == QLatin1String("-version")) {
795  printf("The service\n"
796  "\t%s\n\t%s\n\n", serviceName().toLatin1().constData(), d_ptr->args.at(0).toLatin1().constData());
797  printf("is %s", (d_ptr->controller.isInstalled() ? "installed" : "not installed"));
798  printf(" and %s\n\n", (d_ptr->controller.isRunning() ? "running" : "not running"));
799  return 0;
800  } else if (a == QLatin1String("-e") || a == QLatin1String("-exec")) {
801  d_ptr->args.removeAt(1);
802  int ec = d_ptr->run(false, d_ptr->args);
803  if (ec == -1)
804  qErrnoWarning("The service could not be executed.");
805  return ec;
806  } else if (a == QLatin1String("-t") || a == QLatin1String("-terminate")) {
807  if (!d_ptr->controller.stop())
808  qErrnoWarning("The service could not be stopped.");
809  return 0;
810  } else if (a == QLatin1String("-p") || a == QLatin1String("-pause")) {
811  d_ptr->controller.pause();
812  return 0;
813  } else if (a == QLatin1String("-r") || a == QLatin1String("-resume")) {
814  d_ptr->controller.resume();
815  return 0;
816  } else if (a == QLatin1String("-c") || a == QLatin1String("-command")) {
817  int code = 0;
818  if (d_ptr->args.size() > 2)
819  code = d_ptr->args.at(2).toInt();
820  d_ptr->controller.sendCommand(code);
821  return 0;
822  } else if (a == QLatin1String("-h") || a == QLatin1String("-help")) {
823  printf("\n%s -[i|u|e|s|v|h]\n"
824  "\t-i(nstall) [account] [password]\t: Install the service, optionally using given account and password\n"
825  "\t-u(ninstall)\t: Uninstall the service.\n"
826  "\t-e(xec)\t\t: Run as a regular application. Useful for debugging.\n"
827  "\t-t(erminate)\t: Stop the service.\n"
828  //"\t-c(ommand) num\t: Send command code num to the service.\n"
829  "\t-v(ersion)\t: Print version and status information.\n"
830  "\t-h(elp) \t: Show this help\n"
831  "\tNo arguments\t: Start the service.\n",
832  d_ptr->args.at(0).toLatin1().constData());
833  return 0;
834  }
835  }
836 #if defined(Q_OS_UNIX)
837  if (::getenv("QTSERVICE_RUN")) {
838  // Means we're the detached, real service process.
839  int ec = d_ptr->run(true, d_ptr->args);
840  if (ec == -1)
841  qErrnoWarning("The service failed to run.");
842  return ec;
843  }
844 #endif
845  if (!d_ptr->start()) {
846  fprintf(stderr, "The service %s could not start\n Run with argument -h for help.\n", serviceName().toLatin1().constData());
847  return -4;
848  }
849  return 0;
850 }
851 
874 {
875  return QtServiceBasePrivate::instance;
876 }
877 
906 {
907 }
908 
919 {
920 }
921 
932 {
933 }
934 
945 {
946 }
947 
The QtServiceBase class provides an API for implementing Windows services and Unix daemons.
Definition: qtservice.h:104
void setServiceDescription(const QString &description)
Definition: qtservice.cpp:698
virtual void pause()
Definition: qtservice.cpp:918
virtual void processCommand(int code)
Definition: qtservice.cpp:944
QString serviceName() const
Definition: qtservice.cpp:678
void setStartupType(QtServiceController::StartupType startupType)
Definition: qtservice.cpp:718
QString serviceDescription() const
Definition: qtservice.cpp:688
QtServiceBase(int argc, char **argv, const QString &name)
Definition: qtservice.cpp:625
virtual ~QtServiceBase()
Definition: qtservice.cpp:667
virtual void stop()
Definition: qtservice.cpp:905
static QtServiceBase * instance()
Definition: qtservice.cpp:873
QtServiceController::StartupType startupType() const
Definition: qtservice.cpp:708
virtual void resume()
Definition: qtservice.cpp:931
ServiceFlags serviceFlags() const
Definition: qtservice.cpp:729
virtual int executeApplication()=0
virtual void createApplication(int &argc, char **argv)=0
virtual void start()=0
The QtServiceController class allows you to control services from separate applications.
Definition: qtservice.h:67
bool sendCommand(int code)
virtual ~QtServiceController()
Definition: qtservice.cpp:226
QString serviceName() const
Definition: qtservice.cpp:257
QString serviceFilePath() const
static bool install(const QString &serviceFilePath, const QString &account=QString(), const QString &password=QString())
Definition: qtservice.cpp:304
bool isInstalled() const
QtServiceController(const QString &name)
Definition: qtservice.cpp:209