summaryrefslogtreecommitdiffstats
path: root/src/plugin-interfaces/containerinterface.cpp
blob: 6f3903217613f90176524f7ac17f226342d78d96 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
// Copyright (C) 2021 The Qt Company Ltd.
// Copyright (C) 2019 Luxoft Sweden AB
// Copyright (C) 2018 Pelagicore AG
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

#include "containerinterface.h"

ContainerInterface::ContainerInterface() { }
ContainerInterface::~ContainerInterface() { }

ContainerManagerInterface::ContainerManagerInterface() { }
ContainerManagerInterface::~ContainerManagerInterface() { }

bool ContainerManagerInterface::initialize(ContainerHelperFunctions *) { return true; }

/*! \class ContainerInterface
    \inmodule QtApplicationManager
    \brief An interface for custom container instances.

    A pointer to this interface should be returned from a successful call to
    ContainerManagerInterface::create()

    Each instance of this class corresponds to a single application or - if supported - a single
    quick-launcher instance.

    The interface is closely modelled after QProcess and even reuses many of QProcess' enums.
*/

/*! \enum ContainerInterface::ExitStatus
    This enum describes the different exit statuses of an application.

    \value NormalExit The application exited normally.
    \value CrashExit  The application crashed.
    \value ForcedExit The application was killed by the application manager, since it ignored the
                      quit request originating from a call to ApplicationManager::stopApplication.
    \value WatchdogExit The application was killed by the application manager's watchdog.
                        (since Qt 6.10)

    \sa ApplicationObject::lastExitStatus, QProcess::ExitStatus
*/

/*! \enum ContainerInterface::RunState
    This enum describes the different run states of an application.

    \value NotRunning   The application has not been started yet.
    \value StartingUp   The application has been started and is initializing.
    \value Running      The application is running.
    \value ShuttingDown The application has been stopped and is cleaning up (in multi-process mode
                        this state is only reached if the application is terminating gracefully).

    \sa ApplicationObject::runState, QProcess::ProcessState
*/

/*! \enum ContainerInterface::ProcessError
    This enum describes the different types of errors that are reported by an application.

    Its names and values are an exact copy of QProcess::ProcessError.

    \omitvalue FailedToStart
    \omitvalue Crashed
    \omitvalue Timedout
    \omitvalue WriteError
    \omitvalue ReadError
    \omitvalue UnknownError
*/

/*! \fn bool ContainerInterface::attachApplication(const QVariantMap &application)

    The application manager calls this function, when an \a application has to be attached to this
    container instance: the \a application map closely corresponds to the application's meta-data
    returned from the ApplicationManager::get() method:

    These fields are \b not available for this function:
    \table
    \header
      \li Name
    \row \li isRunning
    \row \li isStartingUp
    \row \li isShuttingDown
    \row \li isLocked
    \row \li isUpdating
    \row \li isRemovable
    \row \li updateProgress
    \row \li application
    \row \li lastExitCode
    \row \li lastExitStatus
    \endtable

    These fields are \b added on top of what ApplicationManager::get() provides:
    \table
    \header
      \li Name
      \li Description
    \row
      \li \c codeDir
      \li The directory where the application's code is located.
    \row
      \li \c manifestDir
      \li The directory where the application's manifest (\c info.yaml) is located.
    \row
      \li \c applicationProperties
      \li A map with all application properties as seen in the manifest.
    \endtable
*/

/*! \fn QString ContainerInterface::controlGroup() const

    This function needs to return which control group the application's container is in. The control
    group name returned by this function is not the low-level name used by the operating
    system, but an abstraction. See the \l{control group mapping}{container documentation} for more
    details on how to setup this mapping.

    \note If your custom container solution does not support cgroups, then simply return an empty
          QString here.
*/

/*! \fn bool ContainerInterface::setControlGroup(const QString &groupName)

    This function is expected to move the application's container into a control group. The
    \a groupName is not the low-level name used by the operating system, but an abstraction. See
    the \l{control group mapping}{container documentation} for more  details on how to setup this
    mapping.
    Returns \c true if the call was successful or \c false otherwise.

    \note If your custom container solution does not support cgroups, then simply return \c false
          here.
*/

/*! \fn bool ContainerInterface::setProgram(const QString &program)
    This function is called by the application manager in order to set the \a program to execute
    when starting the process. This function will be called before start().
    Needs to return \c true if \a program is valid within this container or \c false otherwise.

    \sa start()
*/

/*! \fn void ContainerInterface::setBaseDirectory(const QString &baseDirectory)
    Called by the application manager to set the initial working directory for the process within
    the container to \a baseDirectory.
*/

/*! \fn bool ContainerInterface::isReady() const
    Needs to return \c true if the container is ready for starting the application's program or
    \c false otherwise.

    \sa ready()
*/

/*! \fn QString ContainerInterface::mapContainerPathToHost(const QString &containerPath) const
    The application manager will call this function whenever paths have to be mapped between the
    filesystem namespace of the application's container and the application manager.

    You can simply return \a containerPath, if both are running in the same namespace.
*/

/*! \fn QString ContainerInterface::mapHostPathToContainer(const QString &hostPath) const
    The application manager will call this function whenever paths have to be mapped between the
    filesystem namespace of the application manager and the application's container.

    You can simply return \a hostPath, if both are running in the same namespace.
*/

/*! \fn bool ContainerInterface::start(const QStringList &arguments,
                                       const QMap<QString, QString> &runtimeEnvironment,
                                       const QVariantMap &amConfig)
    This function will be called to asynchronously start the application's program (as set by
    setProgram()), with the additional command line \a arguments and with the additional environment
    variables from \a runtimeEnvironment.

    The \a runtimeEnvironment is a string map for environment variables and their values. An empty
    value in this map means, that the environment variable denoted by its key shall be unset. The
    native runtime will define these variables by default:

    \table
    \header
      \li Name
      \li Description
    \row
      \li \c QT_QPA_PLATFORM
      \li Set to \c wayland.
    \row
      \li \c QT_IM_MODULE
      \li Empty (unset), which results in applications using the default wayland text input method.
    \row
      \li \c QT_SCALE_FACTOR
      \li Empty (unset), to prevent scaling of wayland clients relative to the compositor. Otherwise
          running the application manager on a 4K desktop with scaling would result in double-scaled
          applications within the application manager.
    \row
      \li \c QT_WAYLAND_SHELL_INTEGRATION
      \li Set to \c xdg-shell. This is the preferred wayland shell integration.
    \row
      \li \c AM_CONFIG
      \li A YAML, UTF-8 string encoded version of the \a amConfig map (see below).
    \row
      \li \c AM_NO_DLT_LOGGING
      \li Only set to \c 1, if DLT logging is to be switched off (otherwise not set at all).
          The same information is available via \a amConfig (see below), but some applications and
          launchers may need this information as early as possible.
    \endtable

    The \a amConfig map is a collection of settings that are communicated to the program from the
    application manager. The same information is already string encoded in the \c AM_CONFIG
    environment variable within \a runtimeEnvironment, but it would be tedious to reparse that YAML
    fragment in the container plugin.

    \target amConfigDetails
    These are the currently defined fields in amConfig:

    \table
    \header
      \li Name
      \li Type
      \li Description
    \row
      \li \c baseDir
      \li string
      \li The base directory from where the application manager was started. Needed for relative
          import paths.
    \row
      \li \c dbus/p2p
      \li string
      \li The D-Bus address for the peer-to-peer bus between the application manager and this
          application.
    \row
      \li \c dbus/org.freedesktop.Notifications
      \li string
      \li The D-Bus address for the notification interface. Can either be \c session (default),
          \c system or an actual D-Bus bus address.
    \row
      \li \c logging/dlt
      \li bool
      \li Logging should be done via DLT, if this is set to \c true.
    \row
      \li \c logging/rules
      \li list<string>
      \li The Qt logging rules as set in the application manager's main config.
    \row
      \li \c systemProperties
      \li object
      \li The project specific \l{system properties} that were set via the application manager's
          main config file.
    \row
      \li \c runtimeConfiguration
      \li object
      \li The \l {Runtime configuration} used for this application.
    \row
      \li \c ui/slowAnimations
      \li bool
      \li Set to \c true, if animations should be slowed down.
    \row
      \li \c ui/iconThemeName
      \li string
      \li If set in the application manager, this will forward the icon theme name to the application
          (see QIcon::setThemeName()).
    \row
      \li \c ui/iconThemeSearchPaths
      \li string
      \li If set in the application manager, this will forward the icon theme search paths to the
          application (see QIcon::setThemeSearchPaths()).
    \row
      \li \c ui/opengl
      \li object
      \li If a specific OpenGL configuration is requested either globally for the application manager
          or just for this application (via its \c info.yaml manifest), this field will contain the
          \l{OpenGL Specification}{required OpenGL configuration}.
    \endtable

    The application manager will only ever call this function once for any given instance.

    This function should return \c true in case it succeeded or \c false otherwise. In case it
    returns \c true, the implementation needs to either emit the started() or
    \l{QProcess::errorOccurred()}{errorOccurred()} signal (can be delayed) in response to this
    call.

    \sa QProcess::start()
*/

/*! \fn qint64 ContainerInterface::processId() const

    Should return the native process identifier for the running process, if available. If no process
    is currently running, \c 0 is returned.

    \note The process identifier needs to be translated into the application manager's PID
          namespace, if the container solution is managing its own, private namespace.
    \note This identifier is needed to support security features within the application manager:
          Having a valid PID for every container is necessary (a) in order for Wayland windows to
          only be accepted when coming from known PIDs and (b) for system services to retrieve
          meta-data on applications via the D-Bus call ApplicationManager::identifyApplication().
          If you need a quick workaround during development for Wayland to accept windows from any
          PID, then run the application manager with \c{--no-security}.
*/

/*! \fn QProcess::ProcessState ContainerInterface::state() const

    This function needs to return the current state of the process within the container.

    \sa stateChanged(), errorOccured(), QProcess::state()
*/

/*! \fn void ContainerInterface::stop(ExitStatus exitStatus)
    \since 6.10

    Called by the application manager, if it wants to stop the current process within the
    container.

    The \a exitStatus argument tells the container implementation how to stop the process:
    \table
    \header
      \li Exit status
      \li Description
    \row
      \li NormalExit
      \li On Unix, the equivalent would be sending a \c SIGTERM signal. The process may not exit
          as a result of calling this function.
    \row
      \li ForcedExit
      \li On Unix, the equivalent would be sending a \c SIGKILL signal. The process should exit
          immediately.
    \row
      \li WatchdogExit
      \li On Unix, the equivalent would be sending the signal returned by
          ContainerHelperFunctions::watchdogSignal(). The process should exit immediately.
    \row
      \li CrashExit
      \li Not used in this context.
    \endtable
*/

/*! \fn void ContainerInterface::kill()
    \deprecated [6.10] Implement stop() and handle ForcedExit instead.
*/

/*! \fn void ContainerInterface::terminate()
    \deprecated [6.10] Implement stop() and handle NormalExit instead.
*/

/*! \fn void ContainerInterface::ready()

    If the container implementation needs to do potentially expensive initialization work in the
    background, it can tell the application manager to delay calling start() until it has emitted
    this signal.

    In case this is not needed, then you never have to emit this signal, but you are expected to
    always return \c true from isReady().

    \sa isReady()
*/

/*! \fn void ContainerInterface::started()

    This signal has to be emitted when the process within the container has started and state()
    returns QProcess::Running.

    \sa QProcess::started()
*/

/*! \fn void ContainerInterface::errorOccured(ProcessError processError)

    This signal needs to be emitted when an error occurs with the process within the container. The
    specified \a processError describes the type of error that occurred.

    \sa QProcess::errorOccurred()
*/

/*! \fn void ContainerInterface::finished(int exitCode, ExitStatus exitStatus)

    This signal has to be emitted when the process within the container finishes. The \a exitStatus
    parameter gives an indication why the process exited - in case of a normal exit, the \a exitCode
    will be the return value of the process' \c main() function.

    \sa QProcess::finished()
*/

/*! \fn void ContainerInterface::stateChanged(RunState state)

    This signal needs to be emitted whenever the \a state of the process within the container changes.
*/


/*! \class ContainerManagerInterface
    \inmodule QtApplicationManager
    \brief A plugin interface for custom container solutions.

    This is the interface that you have to implement, if you want to get your own custom container
    solution into the application manager.

    For an example, please see \c{src/plugins/bubblewrap-container-plugin/}. This is a plugin, that
    integrates with \c bubblewrap containers.
*/

/*! \fn void ContainerManagerInterface::initialize(ContainerHelperFunctions *helpers)
    This function is called by the application manager right after your plugin has been loaded,
    your interface has been instantiated and the configuration was set via setConfiguration.

    The \a helpers interface gives you access to common functionality that can be shared between
    different container plugin implementations. The pointer is owned by the application manager and
    is valid during the lifetime of the interface.

    Return \c true if your plugin is usable, given its configuration and system state. If not,
    then return \c false and the application manager will disable this container plugin.

    \sa ContainerHelperFunctions
*/

/*! \fn QString ContainerManagerInterface::identifier() const
    Should return the unique identifier string for this container plugin.
*/

/*! \fn bool ContainerManagerInterface::supportsQuickLaunch() const
    Expected to return \c true if the interface has support for quick-launching or \c false otherwise.
*/

/*! \fn void ContainerManagerInterface::setConfiguration(const QVariantMap &configuration)
    Called by the application manager after parsing its configuration files. The \a configuration
    map corresponds to the (optional) container specific configuration as described in the
    \l{Containers}{container documentation}.
*/

/*! \fn ContainerInterface *ContainerManagerInterface::create(bool isQuickLaunch, const QVector<int> &stdioRedirections, const QMap<QString, QString> &debugWrapperEnvironment, const QStringList &debugWrapperCommand)
    The application manager will call this function every time it needs to create a specific
    container for a direct application launch, or a runtime quick-launcher (depending on the value
    of the \a isQuickLaunch parameter).

    If the \a stdioRedirections vector is not empty, the plugin should - if possible - redirect
    standard-io streams: the vector can have 3 entries at most, with the index corresponding to the
    Unix standard-io file number (0: \c stdin, 1: \c stdout and 2: \c stderr). The values in this
    vector are either open OS file descriptors for redirections or \c -1.
    \c{[-1, 5, 5]} would mean: ignore \c stdin and redirect both \c stdout and \c stderr to fd \c 5.

    The ownership of these file descriptors is transferred if, and only if, a new ContainerInterface
    is successfully instantiated (i.e. the return value is not nullptr). They then have to be closed
    either immediately, in case this plugin is not able to use them, or latest, when the started
    application has finished.

    The \a debugWrapperEnvironment is an optional string map for environment variables and their
    values, if a debug-wrapper is to be used (the \a debugWrapperCommand is not empty - see below).
    An empty value in this map means, that the environment variable denoted by its key shall be
    unset.

    In case the \a debugWrapperCommand is not empty, the plugin is requested to execute the binary
    set by ContainterInterface::setProgram using this debug-wrapper. The plugin is responsible for
    combining both and for handling the replacement of \c{%program%} and \c{%arguments%}. See the
    \l{DebugWrappers} {debug-wrapper documentation} for more information.
*/


/*! \class ContainerHelperFunctions
    \inmodule QtApplicationManager
    \brief A helper class for custom container solutions.

    Your custom container implementation can use these functions to avoid code duplication.
    Keep in mind that your plugin cannot link to the static application manager libraries, as
    this would result in duplicate symbols.

    A pointer to this interface is given to the plugin via ContainerManagerInterface::initialize().
    The plugin does not own this pointer, it is owned by the application manager and valid
    during the lifetime of the plugin.
*/

/*! \fn void ContainerHelperFunctions::closeAndClearFileDescriptors(QVector<int> &fdList)

    Convenience function that closes all the file descriptors in the \a fdList vector and also
    clears the vector afterwards.
    Used mainly when dealing with stdio redirections.
*/

/*! \fn QStringList ContainerHelperFunctions::substituteCommand(const QStringList &debugWrapperCommand, const QString &program, const QStringList &arguments)

    This function substitutes the \c{%program%} and \c{%arguments%} placeholders in the
    \a debugWrapperCommand with the actual values of \a program and \a arguments.
    The result is a QStringList that can be used to start the program with the debug-wrapper.
    If the \a debugWrapperCommand is empty, an empty QStringList is returned.
*/

/*! \fn bool ContainerHelperFunctions::hasRootPrivileges()

    Returns \c true if the application manager process was started via \c sudo or is
    \c setuid-root and \c false otherwise.
    Root privileges are required for certain operations, such as bindMountFileSystem.
*/

/*! \fn void ContainerHelperFunctions::bindMountFileSystem(const QString &from, const QString &to, bool readOnly, quint64 namespacePid)

    This function bind mounts the file system at \a from to the mountpoint \a to.
    If \a readOnly is \c true, the bind mount will be read-only.

    If \a namespacePid is non-zero, the bind mount will be done in the kernel mount namespace of
    the process with the given PID. This is useful to bind-mount the application directory into
    already started containers in case of quick-launching.
    If \a namespacePid is zero, the bind mount will be done in the application manager's mount
    namespace.

    \note This function needs root privileges.
    \note This functions will throw a \c std::exception on error and will simply return on
          successful completion.
*/

/*! \fn int ContainerHelperFunctions::watchdogSignal()
    \since 6.10

    Returns the Unix signal that is registered as "killed by watchdog" in the application
    manager. If applicable, use this when the application manager requests
    \l{ContainerInterface::stop}{stop} with \l{ContainerInterface::WatchdogExit}{WatchdogExit}
    via ContainerInterface.
*/

/*! \fn QString ContainerHelperFunctions::checkDBusSocketPath(const QString &dbusAddress, const QByteArray &typeHint)
    \since 6.10

    This function parses the given \a dbusAddress to check if the referenced D-Bus socket exists.
    If it does, this function returns the absolute path to that socket.
    If the socket does not exist, it throws a \c std::exception.

    The \a typeHint is used to provide context for the exception's error message (e.g.
    \c "session" or \c "p2p").
*/

/*! \fn QString ContainerHelperFunctions::checkWaylandSocketPath(const QString &xdgRuntimeDir, const QString &waylandDisplay)
    \since 6.10

    Checks if a Wayland socket exists in the given \a xdgRuntimeDir named \a waylandDisplay.
    If it does, this function returns the absolute path to that socket.
    If the socket does not exist, it throws a \c std::exception.
*/