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(0), 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 
648  d_ptr->serviceFlags = 0;
649  d_ptr->sysd = 0;
650  for (int i = 0; i < argc; ++i)
651  d_ptr->args.append(QString::fromLocal8Bit(argv[i]));
652 }
653 
665 {
666  delete d_ptr;
667  QtServiceBasePrivate::instance = 0;
668 }
669 
676 {
677  return d_ptr->controller.serviceName();
678 }
679 
686 {
687  return d_ptr->serviceDescription;
688 }
689 
695 void QtServiceBase::setServiceDescription(const QString &description)
696 {
697  d_ptr->serviceDescription = description;
698 }
699 
706 {
707  return d_ptr->startupType;
708 }
709 
716 {
717  d_ptr->startupType = type;
718 }
719 
726 QtServiceBase::ServiceFlags QtServiceBase::serviceFlags() const
727 {
728  return d_ptr->serviceFlags;
729 }
730 
756 {
757  if (d_ptr->args.size() > 1) {
758  QString a = d_ptr->args.at(1);
759  if (a == QLatin1String("-i") || a == QLatin1String("-install")) {
760  if (!d_ptr->controller.isInstalled()) {
761  QString account;
762  QString password;
763  if (d_ptr->args.size() > 2)
764  account = d_ptr->args.at(2);
765  if (d_ptr->args.size() > 3)
766  password = d_ptr->args.at(3);
767  if (!d_ptr->install(account, password)) {
768  fprintf(stderr, "The service %s could not be installed\n", serviceName().toLatin1().constData());
769  return -1;
770  } else {
771  printf("The service %s has been installed under: %s\n",
772  serviceName().toLatin1().constData(), d_ptr->filePath().toLatin1().constData());
773  }
774  } else {
775  fprintf(stderr, "The service %s is already installed\n", serviceName().toLatin1().constData());
776  }
777  return 0;
778  } else if (a == QLatin1String("-u") || a == QLatin1String("-uninstall")) {
779  if (d_ptr->controller.isInstalled()) {
780  if (!d_ptr->controller.uninstall()) {
781  fprintf(stderr, "The service %s could not be uninstalled\n", serviceName().toLatin1().constData());
782  return -1;
783  } else {
784  printf("The service %s has been uninstalled.\n",
785  serviceName().toLatin1().constData());
786  }
787  } else {
788  fprintf(stderr, "The service %s is not installed\n", serviceName().toLatin1().constData());
789  }
790  return 0;
791  } else if (a == QLatin1String("-v") || a == QLatin1String("-version")) {
792  printf("The service\n"
793  "\t%s\n\t%s\n\n", serviceName().toLatin1().constData(), d_ptr->args.at(0).toLatin1().constData());
794  printf("is %s", (d_ptr->controller.isInstalled() ? "installed" : "not installed"));
795  printf(" and %s\n\n", (d_ptr->controller.isRunning() ? "running" : "not running"));
796  return 0;
797  } else if (a == QLatin1String("-e") || a == QLatin1String("-exec")) {
798  d_ptr->args.removeAt(1);
799  int ec = d_ptr->run(false, d_ptr->args);
800  if (ec == -1)
801  qErrnoWarning("The service could not be executed.");
802  return ec;
803  } else if (a == QLatin1String("-t") || a == QLatin1String("-terminate")) {
804  if (!d_ptr->controller.stop())
805  qErrnoWarning("The service could not be stopped.");
806  return 0;
807  } else if (a == QLatin1String("-p") || a == QLatin1String("-pause")) {
808  d_ptr->controller.pause();
809  return 0;
810  } else if (a == QLatin1String("-r") || a == QLatin1String("-resume")) {
811  d_ptr->controller.resume();
812  return 0;
813  } else if (a == QLatin1String("-c") || a == QLatin1String("-command")) {
814  int code = 0;
815  if (d_ptr->args.size() > 2)
816  code = d_ptr->args.at(2).toInt();
817  d_ptr->controller.sendCommand(code);
818  return 0;
819  } else if (a == QLatin1String("-h") || a == QLatin1String("-help")) {
820  printf("\n%s -[i|u|e|s|v|h]\n"
821  "\t-i(nstall) [account] [password]\t: Install the service, optionally using given account and password\n"
822  "\t-u(ninstall)\t: Uninstall the service.\n"
823  "\t-e(xec)\t\t: Run as a regular application. Useful for debugging.\n"
824  "\t-t(erminate)\t: Stop the service.\n"
825  //"\t-c(ommand) num\t: Send command code num to the service.\n"
826  "\t-v(ersion)\t: Print version and status information.\n"
827  "\t-h(elp) \t: Show this help\n"
828  "\tNo arguments\t: Start the service.\n",
829  d_ptr->args.at(0).toLatin1().constData());
830  return 0;
831  }
832  }
833 #if defined(Q_OS_UNIX)
834  if (::getenv("QTSERVICE_RUN")) {
835  // Means we're the detached, real service process.
836  int ec = d_ptr->run(true, d_ptr->args);
837  if (ec == -1)
838  qErrnoWarning("The service failed to run.");
839  return ec;
840  }
841 #endif
842  if (!d_ptr->start()) {
843  fprintf(stderr, "The service %s could not start\n Run with argument -h for help.\n", serviceName().toLatin1().constData());
844  return -4;
845  }
846  return 0;
847 }
848 
871 {
872  return QtServiceBasePrivate::instance;
873 }
874 
903 {
904 }
905 
916 {
917 }
918 
929 {
930 }
931 
942 {
943 }
944 
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
QtServiceController(const QString &name)
Definition: qtservice.cpp:209
static QtServiceBase * instance()
Definition: qtservice.cpp:870
virtual ~QtServiceBase()
Definition: qtservice.cpp:664
The QtServiceController class allows you to control services from separate applications.
Definition: qtservice.h:66
StartupType startupType() const
void setStartupType(QtServiceController::StartupType startupType)
Definition: qtservice.cpp:715
QtServiceController::StartupType startupType() const
Definition: qtservice.cpp:705
QtServiceBase(int argc, char **argv, const QString &name)
Definition: qtservice.cpp:625
virtual ~QtServiceController()
Definition: qtservice.cpp:226
void setServiceDescription(const QString &description)
Definition: qtservice.cpp:695
QString serviceName() const
Definition: qtservice.cpp:675
static bool install(const QString &serviceFilePath, const QString &account=QString(), const QString &password=QString())
Definition: qtservice.cpp:304
virtual void processCommand(int code)
Definition: qtservice.cpp:941
virtual void resume()
Definition: qtservice.cpp:928
virtual void pause()
Definition: qtservice.cpp:915
QString serviceDescription() const
Definition: qtservice.cpp:685
ServiceFlags serviceFlags() const
Definition: qtservice.cpp:726