File indexing completed on 2024-04-21 03:55:28

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2020 David Faure <faure@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 #ifndef KPROCESSRUNNER_P_H
0009 #define KPROCESSRUNNER_P_H
0010 
0011 #include "applicationlauncherjob.h"
0012 #include "config-kiogui.h"
0013 #include "kiogui_export.h"
0014 
0015 #include <KProcess>
0016 
0017 #if HAVE_X11
0018 #include <KStartupInfo>
0019 #endif
0020 #include <QObject>
0021 #include <memory>
0022 
0023 namespace KIOGuiPrivate
0024 {
0025 /**
0026  * @internal DO NOT USE
0027  */
0028 bool KIOGUI_EXPORT checkStartupNotify(const KService *service, bool *silent_arg, QByteArray *wmclass_arg);
0029 }
0030 
0031 /**
0032  * @internal  (exported for KIO GUI job unit tests)
0033  * This class runs a KService or a shell command, using QProcess internally.
0034  * It creates a startup notification and finishes it on success or on error (for the taskbar)
0035  * It also shows an error message if necessary (e.g. "program not found").
0036  */
0037 class KIOGUI_EXPORT KProcessRunner : public QObject
0038 {
0039     Q_OBJECT
0040 
0041 public:
0042     enum LaunchMode {
0043         Forking,
0044         SystemdAsScope,
0045         SystemdAsService,
0046     };
0047     Q_ENUM(LaunchMode)
0048 
0049     /**
0050      * Run a KService (application desktop file) to open @p urls.
0051      * @param service the service to run
0052      * @param urls the list of URLs, can be empty
0053      * @param flags various flags
0054      * @param suggestedFileName see KRun::setSuggestedFileName
0055      * @param asn Application startup notification id, if any (otherwise "")
0056      * @param serviceEntryPath the KService entryPath(), passed as an argument
0057      * because in some cases it could become an empty string, e.g. if an
0058      * ApplicationLauncherJob is created from a @c KServiceAction, the
0059      * ApplicationLauncherJob will call KService::setExec() which clears the
0060      * entryPath() of the KService
0061      */
0062     static KProcessRunner *fromApplication(const KService::Ptr &service,
0063                                            const QString &serviceEntryPath,
0064                                            const QList<QUrl> &urls,
0065                                            KIO::ApplicationLauncherJob::RunFlags flags = {},
0066                                            const QString &suggestedFileName = {},
0067                                            const QByteArray &asn = {});
0068 
0069     /**
0070      * Run a shell command
0071      * @param cmd must be a shell command. No need to append "&" to it.
0072      * @param desktopName name of the desktop file, if known.
0073      * @param execName the name of the executable, if known.
0074      * @param asn Application startup notification id, if any (otherwise "").
0075      * @param workingDirectory the working directory for the started process. The default
0076      *                         (if passing an empty string) is the user's document path.
0077      *                         This allows a command like "kwrite file.txt" to find file.txt from the right place.
0078      */
0079     static KProcessRunner *fromCommand(const QString &cmd,
0080                                        const QString &desktopName,
0081                                        const QString &execName,
0082                                        const QByteArray &asn,
0083                                        const QString &workingDirectory,
0084                                        const QProcessEnvironment &environment);
0085 
0086     /**
0087      * Run an executable with arguments (without invoking a shell, by starting a new process).
0088      *
0089      * @note: Starting from 5.92, if an actual executable named @p executable cannot be found
0090      * in PATH, this will return a nullptr.
0091      *
0092      * @param executable the name of (or full path to) the executable, mandatory
0093      * @param args the arguments to pass to the executable
0094      * @param desktopName name of the desktop file, if known.
0095      * @param asn Application startup notification id, if any (otherwise "").
0096      * @param workingDirectory the working directory for the started process. The default
0097      *                         (if passing an empty string) is the user's document path.
0098      *                         This allows a command like "kwrite file.txt" to find file.txt from the right place.
0099      */
0100     static KProcessRunner *fromExecutable(const QString &executable,
0101                                           const QStringList &args,
0102                                           const QString &desktopName,
0103                                           const QByteArray &asn,
0104                                           const QString &workingDirectory,
0105                                           const QProcessEnvironment &environment);
0106 
0107     /**
0108      * Blocks until the process has started. Only exists for KRun via Command/ApplicationLauncherJob, will disappear in KF6.
0109      */
0110     virtual bool waitForStarted(int timeout = 30000) = 0;
0111 
0112     ~KProcessRunner() override;
0113 
0114     static int instanceCount(); // for the unittest
0115 
0116 Q_SIGNALS:
0117     /**
0118      * @brief Emitted on error. In that case, finished() is not emitted.
0119      * @param errorString the error message
0120      */
0121     void error(const QString &errorString);
0122 
0123     /**
0124      * @brief emitted when the process was successfully started
0125      * @param pid PID of the process that was started
0126      */
0127     void processStarted(qint64 pid);
0128 
0129 protected:
0130     KProcessRunner();
0131     virtual void startProcess() = 0;
0132     void setPid(qint64 pid);
0133     void terminateStartupNotification();
0134     QString name() const;
0135     QString resolveServiceAlias() const;
0136     static QString escapeUnitName(const QString &input);
0137     void emitDelayedError(const QString &errorMsg);
0138 
0139     std::unique_ptr<KProcess> m_process;
0140     QString m_executable; // can be a full path
0141     QString m_desktopName;
0142     QString m_desktopFilePath;
0143     QString m_description;
0144     QString m_cmd;
0145     qint64 m_pid = 0;
0146     KService::Ptr m_service;
0147     QString m_serviceEntryPath;
0148     bool m_waitingForXdgToken = false;
0149     QList<QUrl> m_urls;
0150 #if HAVE_X11
0151     KStartupInfoId m_startupId;
0152 #endif
0153 
0154 private:
0155     void initFromDesktopName(const QString &desktopName,
0156                              const QString &execName,
0157                              const QByteArray &asn,
0158                              const QString &workingDirectory,
0159                              const QProcessEnvironment &environment);
0160     void init(const KService::Ptr &service, const QString &serviceEntryPath, const QString &userVisibleName, const QByteArray &asn);
0161 
0162     Q_DISABLE_COPY(KProcessRunner)
0163 };
0164 
0165 class ForkingProcessRunner : public KProcessRunner
0166 {
0167     Q_OBJECT
0168 
0169 public:
0170     explicit ForkingProcessRunner();
0171 
0172     void startProcess() override;
0173     bool waitForStarted(int timeout) override;
0174 
0175 protected Q_SLOTS:
0176     void slotProcessExited(int, QProcess::ExitStatus);
0177     void slotProcessError(QProcess::ProcessError error);
0178     virtual void slotProcessStarted();
0179 };
0180 
0181 #endif