File indexing completed on 2024-04-28 15:26:52

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