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

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 #include "commandlauncherjob.h"
0009 #include "kiogui_debug.h"
0010 #include "kprocessrunner_p.h"
0011 #include <KLocalizedString>
0012 #include <KShell>
0013 
0014 #include <QPointer>
0015 
0016 class KIO::CommandLauncherJobPrivate
0017 {
0018 public:
0019     QString m_command;
0020     QString m_desktopName;
0021     QString m_executable;
0022     QString m_workingDirectory;
0023     QStringList m_arguments;
0024     QByteArray m_startupId;
0025     QPointer<KProcessRunner> m_processRunner;
0026     QProcessEnvironment m_environment{QProcessEnvironment::InheritFromParent};
0027     qint64 m_pid = 0;
0028 };
0029 
0030 KIO::CommandLauncherJob::CommandLauncherJob(const QString &command, QObject *parent)
0031     : KJob(parent)
0032     , d(new CommandLauncherJobPrivate())
0033 {
0034     d->m_command = command;
0035 }
0036 
0037 KIO::CommandLauncherJob::CommandLauncherJob(const QString &executable, const QStringList &args, QObject *parent)
0038     : KJob(parent)
0039     , d(new CommandLauncherJobPrivate())
0040 {
0041     d->m_executable = executable;
0042     d->m_arguments = args;
0043 }
0044 
0045 KIO::CommandLauncherJob::~CommandLauncherJob()
0046 {
0047     // Do *NOT* delete the KProcessRunner instances here.
0048     // We need it to keep running so it can terminate startup notification on process exit.
0049 }
0050 
0051 void KIO::CommandLauncherJob::setCommand(const QString &command)
0052 {
0053     d->m_command = command;
0054 }
0055 
0056 QString KIO::CommandLauncherJob::command() const
0057 {
0058     if (d->m_command.isEmpty()) {
0059         return KShell::quoteArg(d->m_executable) + QLatin1Char(' ') + KShell::joinArgs(d->m_arguments);
0060     }
0061     return d->m_command;
0062 }
0063 
0064 void KIO::CommandLauncherJob::setExecutable(const QString &executable)
0065 {
0066     d->m_executable = executable;
0067 }
0068 
0069 void KIO::CommandLauncherJob::setDesktopName(const QString &desktopName)
0070 {
0071     d->m_desktopName = desktopName;
0072 }
0073 
0074 void KIO::CommandLauncherJob::setStartupId(const QByteArray &startupId)
0075 {
0076     d->m_startupId = startupId;
0077 }
0078 
0079 void KIO::CommandLauncherJob::setWorkingDirectory(const QString &workingDirectory)
0080 {
0081     d->m_workingDirectory = workingDirectory;
0082 }
0083 
0084 QString KIO::CommandLauncherJob::workingDirectory() const
0085 {
0086     return d->m_workingDirectory;
0087 }
0088 
0089 void KIO::CommandLauncherJob::setProcessEnvironment(const QProcessEnvironment &environment)
0090 {
0091     d->m_environment = environment;
0092 }
0093 
0094 void KIO::CommandLauncherJob::start()
0095 {
0096     // Some fallback for lazy callers, not 100% accurate though
0097     if (d->m_executable.isEmpty()) {
0098         const QStringList args = KShell::splitArgs(d->m_command);
0099         if (!args.isEmpty()) {
0100             d->m_executable = args.first();
0101         }
0102     }
0103 
0104     QString displayName = d->m_executable;
0105     KService::Ptr service = KService::serviceByDesktopName(d->m_desktopName);
0106     if (service) {
0107         displayName = service->name();
0108     }
0109     Q_EMIT description(this, i18nc("Launching application", "Launching %1", displayName), {}, {});
0110 
0111     if (d->m_command.isEmpty() && !d->m_executable.isEmpty()) {
0112         d->m_processRunner =
0113             KProcessRunner::fromExecutable(d->m_executable, d->m_arguments, d->m_desktopName, d->m_startupId, d->m_workingDirectory, d->m_environment);
0114     } else {
0115         d->m_processRunner =
0116             KProcessRunner::fromCommand(d->m_command, d->m_desktopName, d->m_executable, d->m_startupId, d->m_workingDirectory, d->m_environment);
0117     }
0118     connect(d->m_processRunner, &KProcessRunner::error, this, [this](const QString &errorText) {
0119         setError(KJob::UserDefinedError);
0120         setErrorText(errorText);
0121         emitResult();
0122     });
0123     connect(d->m_processRunner, &KProcessRunner::processStarted, this, [this](qint64 pid) {
0124         d->m_pid = pid;
0125         emitResult();
0126     });
0127 }
0128 
0129 bool KIO::CommandLauncherJob::waitForStarted()
0130 {
0131     if (d->m_processRunner.isNull()) {
0132         return false;
0133     }
0134     const bool ret = d->m_processRunner->waitForStarted();
0135     if (!d->m_processRunner.isNull()) {
0136         qApp->sendPostedEvents(d->m_processRunner); // so slotStarted gets called
0137     }
0138     return ret;
0139 }
0140 
0141 qint64 KIO::CommandLauncherJob::pid() const
0142 {
0143     return d->m_pid;
0144 }