Skip to content

Commit f1ad6f9

Browse files
committed
improve system/user detection for systemd
1 parent 0270b31 commit f1ad6f9

File tree

16 files changed

+203
-56
lines changed

16 files changed

+203
-56
lines changed

ProjectTemplate/service.service.in

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ After=network-online.target %{SvcSystemdSocketName}
88
[Service]
99
Type=notify
1010
NotifyAccess=exec
11-
ExecStart=$${target.path}/$$TARGET -platform minimal --backend systemd
12-
ExecReload=$${target.path}/$$TARGET -platform minimal --backend systemd reload $MAINPID
13-
ExecStop=$${target.path}/$$TARGET -platform minimal --backend systemd stop $MAINPID
11+
ExecStart=$${target.path}/$$TARGET --backend systemd
12+
ExecReload=$${target.path}/$$TARGET --backend systemd reload
13+
ExecStop=$${target.path}/$$TARGET --backend systemd stop
1414
#WatchdogSec=10
1515
Restart=on-abnormal
1616
RuntimeDirectory=$$TARGET

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ To start a terminal client, simply prepend `--terminal` as well as the backend t
128128
```.sh
129129
#!/bin/sh
130130
# use as "service-cli <arguments>" (assuming this script is named service-cli)
131-
exec /path/to/service -platform minimal --backend systemd --terminal "$@"
131+
exec /path/to/service --backend systemd --terminal "$@"
132132
```
133133

134134
(The platform argument is recommended to not depend on any windowing system)

doc/backends.dox

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ backend can be controlled properly
4242
- All QDebug is logged into journald
4343
- Uses QCoreApplication as application
4444
- Can be used for user and system services
45-
- should be used with `-platform minimal` to avoid any dependencies to the windowing system
4645
- supported systemd commands:
4746
- start
4847
- stop
@@ -77,6 +76,9 @@ backend can be controlled properly
7776
of that command. The first two parameters as well as the service name are automatically determined
7877
by the backend, depending on the control configuration. For example, to send the `SIGUSR1` signal to
7978
a running service, you would call `callCommand<int>("kill", QStringLiteral("--signal=SIGUSR1"));`
79+
- Custom Properties:
80+
- `runAsUser: bool [GSNR]`: Holds whether commands to systemd are issued as `--user` or `--system`.
81+
The default is determined by checking the current user id, but it can be overwritten.
8082
- Supports both blocking and nonblocking, the default is ServiceControl::BlockMode::Blocking
8183
- Has native restart command
8284

examples/service/EchoService/echoservice.service

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ After=network-online.target echoservice.socket
66
[Service]
77
Type=notify
88
NotifyAccess=exec
9-
ExecStart=/path/to/echoservice -platform minimal --backend systemd
10-
ExecReload=/path/to/echoservice -platform minimal --backend systemd reload $MAINPID
11-
ExecStop=/path/to/echoservice -platform minimal --backend systemd stop $MAINPID
9+
ExecStart=/path/to/echoservice --backend systemd
10+
ExecReload=/path/to/echoservice --backend systemd reload
11+
ExecStop=/path/to/echoservice --backend systemd stop
1212
WatchdogSec=10
1313
Restart=on-abnormal
1414
RuntimeDirectory=echoservice

src/plugins/servicebackends/launchd/launchdservicecontrol.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ bool LaunchdServiceControl::serviceExists() const
3333
bool LaunchdServiceControl::isEnabled() const
3434
{
3535
const auto target = QStringLiteral("user/%1/")
36-
.arg(::getuid());
36+
.arg(::geteuid());
3737
QByteArray outData;
3838
if (runLaunchctl("print-disabled", {target}, false, &outData) == EXIT_SUCCESS) {
3939
const QRegularExpression lineRegex{
@@ -71,7 +71,7 @@ bool LaunchdServiceControl::setEnabled(bool enabled)
7171
return true;
7272

7373
const auto target = QStringLiteral("user/%1/%2")
74-
.arg(::getuid())
74+
.arg(::geteuid())
7575
.arg(serviceId());
7676
return runLaunchctl(enabled ? "enable" : "disable", {target}, false) == EXIT_SUCCESS;
7777
}

src/plugins/servicebackends/systemd/systemdservicebackend.cpp

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#include "systemdservicebackend.h"
22
#include "systemdserviceplugin.h"
33

4-
#include <QtDBus/QDBusConnection>
4+
#include <QtCore/QCommandLineParser>
55

66
#include <chrono>
77
#include <csignal>
@@ -32,9 +32,37 @@ int SystemdServiceBackend::runService(int &argc, char **argv, int flags)
3232
{
3333
qInstallMessageHandler(SystemdServiceBackend::systemdMessageHandler);
3434
QCoreApplication app{argc, argv, flags};
35-
if(findArg(QStringLiteral("stop")))
35+
36+
QCommandLineParser parser;
37+
parser.addHelpOption();
38+
QCommandLineOption backendOpt{QStringLiteral("backend"), {}, QStringLiteral("backend")};
39+
backendOpt.setFlags(QCommandLineOption::HiddenFromHelp);
40+
parser.addOption(backendOpt);
41+
parser.addOption({
42+
QStringLiteral("system"),
43+
tr("Run the service as system service, independend of the current user id")
44+
});
45+
parser.addOption({
46+
QStringLiteral("user"),
47+
tr("Run the service as user service, independend of the current user id")
48+
});
49+
parser.addPositionalArgument(QStringLiteral("command"),
50+
tr("A command to execute on the primary service process. "
51+
"Can be either 'reload' to let the service reload its configuration "
52+
"or 'stop' to halt the service"),
53+
QStringLiteral("[stop|reload]"));
54+
55+
parser.process(app);
56+
if(parser.isSet(QStringLiteral("user")))
57+
_userService = true;
58+
else if(parser.isSet(QStringLiteral("system")))
59+
_userService = false;
60+
else
61+
_userService = ::geteuid() != 0;
62+
63+
if(parser.positionalArguments().startsWith(QStringLiteral("stop")))
3664
return stop();
37-
else if(findArg(QStringLiteral("reload")))
65+
else if(parser.positionalArguments().startsWith(QStringLiteral("reload")))
3866
return reload();
3967
else
4068
return run();
@@ -123,8 +151,7 @@ void SystemdServiceBackend::onStarted(bool success)
123151

124152
void SystemdServiceBackend::onReloaded(bool success)
125153
{
126-
if(!success) //TODO does not work! modify commands instead
127-
sd_notify(false, "ERRNO=1");
154+
Q_UNUSED(success)
128155
sd_notify(false, "READY=1");
129156
}
130157

@@ -159,7 +186,7 @@ int SystemdServiceBackend::run()
159186
registerForSignal(signal);
160187

161188
// register the D-Bus service
162-
auto connection = QDBusConnection::sessionBus();
189+
auto connection = dbusConnection();
163190
if(!connection.registerObject(DBusObjectPath, this)) {
164191
printDbusError(connection.lastError());
165192
return EXIT_FAILURE;
@@ -180,7 +207,7 @@ int SystemdServiceBackend::stop()
180207
using namespace de::skycoder42::QtService::ServicePlugin;
181208
sd_notify(false, "STOPPING=1");
182209

183-
auto connection = QDBusConnection::sessionBus();
210+
auto connection = dbusConnection();
184211
auto dbusInterface = new systemd{dbusId(), DBusObjectPath, connection, this};
185212
if (!dbusInterface->isValid()) {
186213
printDbusError(connection.lastError());
@@ -217,7 +244,7 @@ int SystemdServiceBackend::reload()
217244
using namespace de::skycoder42::QtService::ServicePlugin;
218245
sd_notify(false, "RELOADING=1");
219246

220-
auto connection = QDBusConnection::sessionBus();
247+
auto connection = dbusConnection();
221248
auto dbusInterface = new systemd{dbusId(), DBusObjectPath, connection, this};
222249
if (!dbusInterface->isValid()) {
223250
printDbusError(connection.lastError());
@@ -265,13 +292,12 @@ void SystemdServiceBackend::prepareWatchdog()
265292
}
266293
}
267294

268-
bool SystemdServiceBackend::findArg(const QString &command) const
295+
QDBusConnection SystemdServiceBackend::dbusConnection() const
269296
{
270-
for(const auto arg : QCoreApplication::arguments()) {
271-
if(arg == command)
272-
return true;
273-
}
274-
return false;
297+
if(_userService)
298+
return QDBusConnection::sessionBus();
299+
else
300+
return QDBusConnection::systemBus();
275301
}
276302

277303
QString SystemdServiceBackend::dbusId() const

src/plugins/servicebackends/systemd/systemdservicebackend.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
#define SYSTEMDSERVICEBACKEND_H
33

44
#include <QtCore/QTimer>
5+
6+
#include <QtDBus/QDBusConnection>
7+
58
#include <QtService/ServiceBackend>
69

710
#include "systemd_adaptor.h"
@@ -32,6 +35,7 @@ private Q_SLOTS:
3235
void onPaused(bool success);
3336

3437
private:
38+
bool _userService = true;
3539
QTimer *_watchdogTimer = nullptr;
3640
QMultiHash<QByteArray, int> _sockets;
3741

@@ -42,8 +46,8 @@ private Q_SLOTS:
4246
int reload();
4347

4448
void prepareWatchdog();
45-
bool findArg(const QString &command) const;
4649

50+
QDBusConnection dbusConnection() const;
4751
QString dbusId() const;
4852
void printDbusError(const QDBusError &error) const;
4953

src/plugins/servicebackends/systemd/systemdservicecontrol.cpp

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
using namespace QtService;
99

1010
SystemdServiceControl::SystemdServiceControl(QString &&serviceId, QObject *parent) :
11-
ServiceControl{std::move(serviceId), parent}
11+
ServiceControl{std::move(serviceId), parent},
12+
_runAsUser{::geteuid() != 0}
1213
{}
1314

1415
QString SystemdServiceControl::backend() const
@@ -140,6 +141,11 @@ ServiceControl::BlockMode SystemdServiceControl::blocking() const
140141
return _blocking ? BlockMode::Blocking : BlockMode::NonBlocking;
141142
}
142143

144+
bool SystemdServiceControl::isRunAsUser() const
145+
{
146+
return _runAsUser;
147+
}
148+
143149
QVariant SystemdServiceControl::callGenericCommand(const QByteArray &kind, const QVariantList &args)
144150
{
145151
QStringList sArgs;
@@ -189,6 +195,20 @@ bool SystemdServiceControl::setBlocking(bool blocking)
189195
return true;
190196
}
191197

198+
void SystemdServiceControl::setRunAsUser(bool runAsUser)
199+
{
200+
if (_runAsUser == runAsUser)
201+
return;
202+
203+
_runAsUser = runAsUser;
204+
emit runAsUserChanged(_runAsUser);
205+
}
206+
207+
void SystemdServiceControl::resetRunAsUser()
208+
{
209+
setRunAsUser(::geteuid() != 0);
210+
}
211+
192212
QString SystemdServiceControl::serviceName() const
193213
{
194214
const static QRegularExpression regex(QStringLiteral(R"__((.+)\.(?:service|socket|device|mount|automount|swap|target|path|timer|slice|scope))__"));
@@ -213,10 +233,10 @@ int SystemdServiceControl::runSystemctl(const QByteArray &command, const QString
213233

214234
QStringList args;
215235
args.reserve(extraArgs.size() + 4);
216-
if(::geteuid() == 0)
217-
args.append(QStringLiteral("--system"));
218-
else
236+
if(_runAsUser)
219237
args.append(QStringLiteral("--user"));
238+
else
239+
args.append(QStringLiteral("--system"));
220240
args.append(QString::fromUtf8(command));
221241
if(!noPrepare) {
222242
args.append(serviceId());

src/plugins/servicebackends/systemd/systemdservicecontrol.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ class SystemdServiceControl : public QtService::ServiceControl
77
{
88
Q_OBJECT
99

10+
Q_PROPERTY(bool runAsUser READ isRunAsUser WRITE setRunAsUser RESET resetRunAsUser NOTIFY runAsUserChanged)
11+
1012
public:
1113
explicit SystemdServiceControl(QString &&serviceId, QObject *parent = nullptr);
1214

@@ -16,6 +18,7 @@ class SystemdServiceControl : public QtService::ServiceControl
1618
Status status() const override;
1719
bool isAutostartEnabled() const override;
1820
BlockMode blocking() const override;
21+
bool isRunAsUser() const;
1922

2023
QVariant callGenericCommand(const QByteArray &kind, const QVariantList &args) override;
2124

@@ -27,6 +30,11 @@ public Q_SLOTS:
2730
bool enableAutostart() override;
2831
bool disableAutostart() override;
2932
bool setBlocking(bool blocking) override;
33+
void setRunAsUser(bool runAsUser);
34+
void resetRunAsUser();
35+
36+
Q_SIGNALS:
37+
void runAsUserChanged(bool runAsUser);
3038

3139
protected:
3240
QString serviceName() const override;
@@ -35,6 +43,7 @@ public Q_SLOTS:
3543
mutable bool _existsRefBase = false;
3644
mutable bool *_exists = nullptr;
3745
bool _blocking = true;
46+
bool _runAsUser;
3847

3948
int runSystemctl(const QByteArray &command,
4049
const QStringList &extraArgs = {},

src/service/service.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ public Q_SLOTS:
105105

106106
Q_SIGNALS:
107107
//! Must be emitted when starting was completed if onStart returned OperationPending
108-
void started(bool success); //TODO doc should be direct connected
108+
void started(bool success);
109109
//! Must be emitted when stopping was completed if onStop returned OperationPending
110110
void stopped(int exitCode = EXIT_SUCCESS);
111111
//! Must be emitted when reloading was completed if onReload returned OperationPending

0 commit comments

Comments
 (0)