1.Daemon.Service - Light-Wizzard/WeBookServer GitHub Wiki

Daemon-Service

Qt is Cross-Platform, but to understand what a Service or Daemons is and what the difference is, we have the World of Windows, and it calls this a Service, but for the most part, we what them to do the same thing, which is, to run without a GUI or Terminal, so understand this is only running in memory, with no GUI, and no Terminal.

This only applies to systemd, there are older services, but most modern Linux OS uses systemd, normally the unit files, that store the information about the service, are stored in /lib/systemd/system, let us look at the Unit file, and example its content.

Table of Content

  1. Unit
  2. Service
  3. Install
  4. About-Tutorial
  5. About-systemd
  6. About-the-Example-for-this-Tutorial
  7. About-the-service-file
  8. About-the-qmake-pro-file
  9. About-the-socket-file
  10. About-Code-Style-and-Comments
  11. Progress
  12. Next

Unit

This relates to Daemon so its Unix based.

Section Directives for [Unit]

The [Unit] section is used for defining metadata for the unit and configuring the relationship of the unit to other units, and although section order does not matter to systemd when parsing the file, this section is often placed at the top because it provides an overview of the unit, the directives that we will use in the [Unit] section are:

Description=: This directive can be used to describe the name and basic functionality of the unit. It is returned by various systemd tools, so it is good to set this to something short, specific, and informative.

Documentation=: This directive provides a location for a list of URIs for documentation. These can be either internally available man pages or web-accessible URLs. The systemctl status command will expose this information, allowing for easy discoverability.

After=: The units listed in this directive will be started before starting the current unit. This does not imply a dependency relationship and one must be established through the above directives if this is required.

Service

This relates to Daemon so its Unix based.

The [Service] Section

The [Service] Section is used to provide the configuration that is only applicable for services, one of the basic things that should be specified within the [Service] section is the Type= of the service. This categorizes services by their process and daemonizing behavior. This is important because it tells systemd how to correctly manage the service and find out its state. The Type= directive can be one of the following:

simple: The main process of the service is specified in the start line. This is the default if the Type= and Busname= directives are not set, but the ExecStart= is set. Any communication should be handled outside of the unit through a second unit of the appropriate type (like through a .socket unit if this unit must communicate using sockets).

forking: This service type is used when the service forks a child process, exiting the parent process almost immediately. This tells systemd that the process is still running even though the parent exited.

oneshot: This type indicates that the process will be short-lived and that systemd should wait for the process to exit before continuing on with other units. This is the default Type= and ExecStart= are not set. It is used for one-off tasks.

dbus: This indicates that unit will take a name on the D-Bus bus. When this happens, systemd will continue to process the next unit.

notify: This indicates that the service will issue a notification when it has finished starting up. The systemd process will wait for this to happen before proceeding to other units.

We will use notify as the type.

NotifyAccess=: This specifies access to the socket that should be used to listen for notifications when the “notify” service type is selected This can be “none”, “main”, or “all. The default, "none”, ignores all status messages. The “main” option will listen to messages from the main process and the “all” option will cause all members of the service’s control group to be processed.

ExecStart=: This specifies the full path and the arguments of the command to be executed to start the process. This may only be specified once (except for “oneshot” services). If the path to the command is preceded by a dash “-” character, non-zero exit statuses will be accepted without marking the unit activation as failed.

ExecReload=: This optional directive indicates the command necessary to reload the configuration of the service if available.

ExecStop=: This indicates the command needed to stop the service. If this is not given, the process will be killed immediately when the service is stopped.

Restart=: This indicates the circumstances under which systemd will attempt to automatically restart the service. This can be set to values like “always”, “on-success”, “on-failure”, “on-abnormal”, “on-abort”, or “on-watchdog”. These will trigger a restart according to the way that the service was stopped.

Install

Section Directives for [Install]

This relates to Daemon so its Unix based.

The [Install] section, is optional and is used to define the behavior of a unit if it is enabled or disabled. Enabling a unit marks it to be automatically started at boot. In essence, this is accomplished by latching the unit in question onto another unit that is somewhere in the line of units to be started at boot, because of this, only units that can be enabled will have this section. The directives within dictate what should happen when the unit is enabled:

WantedBy=: The WantedBy= directive is the most common way to specify how a unit should be enabled. This directive allows you to specify a dependency relationship in a similar way to the Wants= directive does in the [Unit] section. The difference is that this directive is included in the ancillary unit allowing the primary unit listed to remain relatively clean. When a unit with this directive is enabled, a directory will be created within /etc/systemd/system named after the specified unit with .wants appended to the end. Within this, a symbolic link to the current unit will be created, creating the dependency. For instance, if the current unit has WantedBy=multi-user.target, a directory called multi-user.target.wants will be created within /etc/systemd/system (if not already available) and a symbolic link to the current unit will be placed within. Disabling this unit removes the link and removes the dependency relationship.

About Tutorial

This Tutorial is to help you understand what you need to know about using WeBookServer using systemd, and will only cover the basics, but I will include links to more detailed articles to help you.

About systemd

Unix only.

To understand systemd I will reference this GitHub project: https://github.com/systemd, luckily you only need to understand how to use it, so I will not get into a lot of the finer details of it, since that is covered at that link.

About the Example for this Tutorial

I will use WeBook Server for this Tutorial, you can follow along at https://github.com/Light-Wizzard/WeBookServer, for the first part of this tutorial.

When you write a system.d in Qt using WeBookServer, you will want to start from scratch, meaning start without your app being in it, this will make it easier for you to integrate it later, because you will need to have a very solid foundation to start with, so we will first look at how the server works.

About the service file

First, we need to talk about systemd unit, when you write an app, you need to have a service file, this tells systemd everything it needs to know about your service, and you can name it what you want, in this case, its WeBookServer, so we will name it WeBookServer.service, and we need to install it, meaning to copy it somewhere that root can execute it, so let us now look at this file.

[Unit]
Description=WeBookServer Service
Documentation=https://github.com/Light-Wizzard/WeBookServer
After=network-online.target WeBookServer.socket

[Service]
Type=notify
NotifyAccess=exec
ExecStart=$${target.path}/$$TARGET --backend systemd
ExecReload=$${target.path}/$$TARGET --backend systemd reload
ExecStop=$${target.path}/$$TARGET --backend systemd stop
#ExecEnable=$${target.path}/$$TARGET --backend systemd enable
#ExecDisable=$${target.path}/$$TARGET --backend systemd disable

WatchdogSec=10
Restart=on-abnormal
RuntimeDirectory=/lib/systemd/system/$$TARGET

[Install]
#WantedBy=multi-user.target
WantedBy=default.target

[Unit] is the section that systemd will use to identify this service, it gets its Type from here, the Type is "notify", and the NotifyAccess=exec, depending on if its Windows or Unix (Linux, Mac, Unix...), for example, in Windows, all exec has an "exe" extension, not so in Unix, so in Unix its WeBookServer, in Windows its WeBookServer.exe.

This is where you define what this service can do, ExecStart, ExecReload, and ExecStop, correspond with functions: onStart, onReload, and onStop in WeBookServer.cpp.

WatchdogSec is a Watch Dog Timer and is set in seconds, do not think your app with be more responsive by setting this to a lower number, like every second, check this out first: https://www.freedesktop.org/software/systemd/man/systemd.service.html#WatchdogSec=, this is a timeout, and that is not a good thing to set too low nor too high.

Restart=on-abnormal, you can look at the link above and find other options, and what these all mean, but that is beyond the scope of this tutorial to explain them all, only to point them out, and explain why they are the way they are, and what you might want to change for your own app.

About the qmake pro file

Notice how TARGET is defined in the qmake file WeBookServer.pro:

# WeBookServer.pro
# Specifies the name of the template to use when generating the project.
# The allowed values are: app, lib, subdirs, aux, vcapp or vclib
TEMPLATE     = "app"
# Specifies the name of the target file. Contains the base name of the project file by default.
# Do not use Spaces in Target Name
# This pro(ject) file is based on a pattern
TARGET       = "WeBookServer"
#
INCLUDEPATH     += .
INCLUDEPATH     += src/
INCLUDEPATH     += "${QT_INSTALL_LIBS}/qt"
#
DEPENDPATH      += src/

#
QT              -= gui
QT              *= core
QT              *= core-private
!win32:QT       *= network

CONFIG          *= qt
CONFIG          *= console
CONFIG          *= cmdline
CONFIG          -= app_bundle
#CONFIG          *= "c++11"
#CONFIG          *= "c++14"
#CONFIG         *= "c++1z"
CONFIG          *= "c++17"
#CONFIG         *= "c++2a"
#CONFIG         *= "c++latest"
#
HEADERS         += src/WeBookServer.h
SOURCES         += src/WeBookServer.cpp src/main.cpp

unix:DISTFILES  += tools/haproxy.cfg tools/monit.conf tools/webookserver.ini tools/webookserver.sh tools/webooksetup.sh
DISTFILES       += README.md
DISTFILES       += WeBookServer.qdocconf
DISTFILES       += data/WeBook.toc
DISTFILES       += data/WeBook/ItemId1.1.html
DISTFILES       += data/WeBook/TitlePageID.html
DISTFILES       += data/WeBookClient.ini
DISTFILES       += data/WeBooks.cat
TRANSLATIONS    += src/WeBookServer_en_US.ts

win32:VERSION    = 0.1.0.0 # major.minor.patch.build
else:VERSION     = 0.1.0   # major.minor.patch
DEFINES          = APP_VERSION=\\\"$${VERSION}\\\"
win32:LIBS      += -luser32
###############################################################################
# https://github.com/qt-labs/qthttpserver
###############################################################################
INCLUDEPATH     *= src/QHttpServer src/QHttpServer/httpserver src/QHttpServer/sslserver
DEPENDSPATH     *= src/QHttpServer src/QHttpServer/httpserver src/QHttpServer/sslserver
# This is for Travis
INCLUDEPATH     += "${QT_INSTALL_LIBS}/qt/QtHttpServer"
INCLUDEPATH     += "${QT_INSTALL_LIBS}/qt/QtSslServer"
#
INCLUDEPATH     *= include/QtHttpServer/5.12.0/QtHttpServer
INCLUDEPATH     *= include/QtHttpServer/5.12.0/QtHttpServer/private
INCLUDEPATH     *= include/QtSslServer/5.12.0/QtSslServer
INCLUDEPATH     *= include/QtSslServer/5.12.0/QtSslServer/private
#
HEADERS         += src/QHttpServer/httpserver/qabstracthttpserver.h
HEADERS         += src/QHttpServer/httpserver/qabstracthttpserver_p.h
HEADERS         += src/QHttpServer/httpserver/qhttpserver.h
HEADERS         += src/QHttpServer/httpserver/qhttpserver_p.h
HEADERS         += src/QHttpServer/httpserver/qhttpserverliterals_p.h
HEADERS         += src/QHttpServer/httpserver/qhttpserverrequest.h
HEADERS         += src/QHttpServer/httpserver/qhttpserverrequest_p.h
HEADERS         += src/QHttpServer/httpserver/qhttpserverresponder.h
HEADERS         += src/QHttpServer/httpserver/qhttpserverresponder_p.h
HEADERS         += src/QHttpServer/httpserver/qhttpserverresponse.h
HEADERS         += src/QHttpServer/httpserver/qhttpserverresponse_p.h
HEADERS         += src/QHttpServer/httpserver/qhttpserverrouter.h
HEADERS         += src/QHttpServer/httpserver/qhttpserverrouter_p.h
HEADERS         += src/QHttpServer/httpserver/qhttpserverrouterrule.h
HEADERS         += src/QHttpServer/httpserver/qhttpserverrouterrule_p.h
HEADERS         += src/QHttpServer/httpserver/qhttpserverrouterviewtraits.h
HEADERS         += src/QHttpServer/httpserver/qhttpserverviewtraits.h
HEADERS         += src/QHttpServer/httpserver/qhttpserverviewtraits_impl.h
HEADERS         += src/QHttpServer/httpserver/qthttpserverglobal.h
HEADERS         += src/QHttpServer/sslserver/qsslserver.h
HEADERS         += src/QHttpServer/sslserver/qsslserver_p.h
#
SOURCES         += src/QtService/qtservice.cpp
SOURCES         += src/QHttpServer/httpserver/qabstracthttpserver.cpp
SOURCES         += src/QHttpServer/httpserver/qhttpserver.cpp
SOURCES         += src/QHttpServer/httpserver/qhttpserverliterals.cpp
SOURCES         += src/QHttpServer/httpserver/qhttpserverrequest.cpp
SOURCES         += src/QHttpServer/httpserver/qhttpserverresponder.cpp
SOURCES         += src/QHttpServer/httpserver/qhttpserverresponse.cpp
SOURCES         += src/QHttpServer/httpserver/qhttpserverrouter.cpp
SOURCES         += src/QHttpServer/httpserver/qhttpserverrouterrule.cpp
SOURCES         += src/QHttpServer/sslserver/qsslserver.cpp
include(src/QHttpServer/3rdparty/http-parser.pri)
include(include/QtHttpServer/headers.pri)
include(include/QtSslServer/headers.pri)
###############################################################################
# Forked: https://github.com/qtproject/qt-solutions                           #
# https://github.com/Light-Wizzard/qt-solutions/tree/master/qtservice         #
###############################################################################
INCLUDEPATH     += src/QtService
DEPENDPATH      += src/QtService
HEADERS         += src/QtService/qtservice.h
HEADERS         += src/QtService/qtservice_p.h
# Source Code
win32:SOURCES   += src/QtService/qtservice_win.cpp
unix:HEADERS    += src/QtService/qtunixsocket.h   src/QtService/qtunixserversocket.h
unix:SOURCES    += src/QtService/qtunixsocket.cpp src/QtService/qtunixserversocket.cpp
unix:SOURCES    += src/QtService/qtservice_unix.cpp

win32 {
    qtservice-buildlib:shared:DEFINES += QT_QTSERVICE_EXPORT
    else:qtservice-uselib:DEFINES     += QT_QTSERVICE_IMPORT
}
###############################################################################
# Fork: https://github.com/francescmm/QLogger                                 #
# https://github.com/Light-Wizzard/QLogger                                    #
###############################################################################
HEADERS     *= src/QLogger/QLoggerLevel.h
HEADERS     *= src/QLogger/QLoggerConstants.h
# QLoggerManager
HEADERS     *= src/QLogger/QLoggerManager.h
SOURCES     *= src/QLogger/QLoggerManager.cpp
# QLoggerWriter
HEADERS     *= src/QLogger/QLoggerWriter.h
SOURCES     *= src/QLogger/QLoggerWriter.cpp
# QLoggerCommon
HEADERS     *= src/QLogger/QLoggerCommon.h
SOURCES     *= src/QLogger/QLoggerCommon.cpp
# QLoggerCrypto
HEADERS     *= src/QLogger/QLoggerCrypto.h
SOURCES     *= src/QLogger/QLoggerCrypto.cpp
###############################################################################
# Fork: https://github.com/bricke/Qt-AES                                      #
# https://github.com/Light-Wizzard/Qt-AES                                     #
###############################################################################
INCLUDEPATH *= src/QtAES
DEPENDSPATH *= src/QtAES
#
SOURCES     *= src/QtAES/QAESEncryption.cpp
HEADERS     *= src/QtAES/QAESEncryption.h
# Test
#QT          += testlib
#HEADERS     *= src/QtAES/unit_test/aestest.h
#SOURCES     *= src/QtAES/maintest.cpp
#SOURCES     *= src/QtAES/unit_test/aestest.cpp
##
#DISTFILES   *= src/QtAES/unit_test/longText.txt
#RESOURCES   *= src/QtAES/res.qrc
###############################################################################
#
macos:QMAKE_INFO_PLIST = macos/Info.plist
ios:QMAKE_INFO_PLIST = ios/Info.plist
#

release: DESTDIR = "$${OUT_PWD}/build/release"
debug:   DESTDIR = "$${OUT_PWD}/build/debug"

OBJECTS_DIR = "$${DESTDIR}/obj"
MOC_DIR     = "$${DESTDIR}/moc"
RCC_DIR     = "$${DESTDIR}/qrc"
UI_DIR      = "$${DESTDIR}/ui"

unix {
    isEmpty(PREFIX) {
        PREFIX = /usr
    }
    target.path         = $${PREFIX}/bin
    shortcutfiles.files = usr/share/applications/$${TARGET}.desktop
    shortcutfiles.path  = usr/share/applications
    data.files         += usr/share/icons/hicolor/48x48/apps/$${TARGET}.png
    data.path           = usr/share/pixmaps
    INSTALLS           += shortcutfiles
    INSTALLS           += data
    INSTALLS           += target
    macx {
        RC_FILE = macos/Icon.icns
    }
}
################################ End of File ##################################

You will want to change this for your own app TARGET = WeBookServer, and any reference to is, you will note in the service file that RuntimeDirectory=$$TARGET, the $$ is qmakes way of defining variables, like bash $, you can use $${TARGET}, and you can access any of these in code, more on that later.

About the socket file

[Unit]
Description=WeBookServer Service Socket
Documentation=https://github.com/Light-Wizzard/WeBookServer
After=network.target
PartOf=WeBookServer.service

[Socket]
ListenStream=6633

[Install]
WantedBy=sockets.target

Note that you do not create a normal Qt App, you can set the QCoreApplication setApplicationName and setApplicationVersion, that is used for Settings, and other metadata, but note how it sets the name to WeBookServer and sets the version.

About Code Style and Comments

I added code comments to help break the sections up into parts that are easier to read, these are not Doxygen, and that is because they are harder for most people to read, and I used the c standard, and not the JavaScript Hack format:

// C Standard
if (true)
{
   doThis();
}
// JavaScript Standard
if (true) {
   doThis();
}

Most people, even programmers, do not know, that the reason for the JavaScript having the bracket on the same line, was due to some versions of the JavaScript Engine, that do not have inline functions only option, which forces you to put all your code on one line or in-line aka inline, function {}, meaning all on one line, but in JavaScript, you can have multiple lines, and why this hack works, why people use it in C, is beyond me, but if you do not like it, remove this from the Tutorial, or just understand why I formatted it this way, I have been programming for 51 years, so I am old school, sorry about the Personal remark, remove these if you copy this tutorial, for my site its fine, but this is not written for just my site, but actually for WeBookServer and QtHttpServer by QtLabs, as a lack of knowing if they will have this built-in, my site is more geared to finding how to do it, regardless, I will need my Qt Service app, and if you are reading this, so do you, so let us get started.

Progress

This is a work in progress, feel free to file any issues, and you can copy and edit this to your needs, this is a Copyleft, meaning you copy and paste it into your site without having to reference where you got, or given any credit to the original author, making it easy to assimilate and its futile to resist because you do not have to assume anything, this means can do what you want with it, so it has no Copyright or License, you can use whatever License you require.

Next

Getting Started https://github.com/Light-Wizzard/WeBookServer/wiki/2.Getting.Started

End of File end of Tutorial