1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2020 David Faure <faure@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "commandlauncherjob.h"
9#include "../core/global.h"
10#include "kiogui_debug.h"
11#include "kprocessrunner_p.h"
12#include <KLocalizedString>
13#include <KShell>
14
15#include <QPointer>
16
17class KIO::CommandLauncherJobPrivate
18{
19public:
20 QString m_command;
21 QString m_desktopName;
22 QString m_executable;
23 QString m_workingDirectory;
24 QStringList m_arguments;
25 QByteArray m_startupId;
26 QPointer<KProcessRunner> m_processRunner;
27 QProcessEnvironment m_environment{QProcessEnvironment::InheritFromParent};
28 qint64 m_pid = 0;
29};
30
31KIO::CommandLauncherJob::CommandLauncherJob(const QString &command, QObject *parent)
32 : KJob(parent)
33 , d(new CommandLauncherJobPrivate())
34{
35 d->m_command = command;
36}
37
38KIO::CommandLauncherJob::CommandLauncherJob(const QString &executable, const QStringList &args, QObject *parent)
39 : KJob(parent)
40 , d(new CommandLauncherJobPrivate())
41{
42 d->m_executable = executable;
43 d->m_arguments = args;
44}
45
46KIO::CommandLauncherJob::~CommandLauncherJob()
47{
48 // Do *NOT* delete the KProcessRunner instances here.
49 // We need it to keep running so it can terminate startup notification on process exit.
50}
51
52void KIO::CommandLauncherJob::setCommand(const QString &command)
53{
54 d->m_command = command;
55}
56
57QString KIO::CommandLauncherJob::command() const
58{
59 if (d->m_command.isEmpty()) {
60 return KShell::quoteArg(arg: d->m_executable) + QLatin1Char(' ') + KShell::joinArgs(args: d->m_arguments);
61 }
62 return d->m_command;
63}
64
65void KIO::CommandLauncherJob::setExecutable(const QString &executable)
66{
67 d->m_executable = executable;
68}
69
70void KIO::CommandLauncherJob::setDesktopName(const QString &desktopName)
71{
72 d->m_desktopName = desktopName;
73}
74
75void KIO::CommandLauncherJob::setStartupId(const QByteArray &startupId)
76{
77 d->m_startupId = startupId;
78}
79
80void KIO::CommandLauncherJob::setWorkingDirectory(const QString &workingDirectory)
81{
82 d->m_workingDirectory = workingDirectory;
83}
84
85QString KIO::CommandLauncherJob::workingDirectory() const
86{
87 return d->m_workingDirectory;
88}
89
90void KIO::CommandLauncherJob::setProcessEnvironment(const QProcessEnvironment &environment)
91{
92 d->m_environment = environment;
93}
94
95void KIO::CommandLauncherJob::start()
96{
97 // Some fallback for lazy callers, not 100% accurate though
98 if (d->m_executable.isEmpty()) {
99 const QStringList args = KShell::splitArgs(cmd: d->m_command);
100 if (!args.isEmpty()) {
101 d->m_executable = args.first();
102 }
103 }
104
105 if (d->m_executable.isEmpty()) {
106 setError(KJob::UserDefinedError);
107 setErrorText(i18nc("An error message", "Empty command provided"));
108 emitResult();
109 return;
110 }
111
112 QString displayName = d->m_executable;
113 KService::Ptr service = KService::serviceByDesktopName(name: d->m_desktopName);
114 if (service) {
115 displayName = service->name();
116 }
117 Q_EMIT description(job: this, i18nc("Launching application", "Launching %1", displayName), field1: {}, field2: {});
118
119 if (d->m_command.isEmpty() && !d->m_executable.isEmpty()) {
120 d->m_processRunner =
121 KProcessRunner::fromExecutable(executable: d->m_executable, args: d->m_arguments, desktopName: d->m_desktopName, asn: d->m_startupId, workingDirectory: d->m_workingDirectory, environment: d->m_environment);
122
123 if (!d->m_processRunner) {
124 setError(KIO::ERR_DOES_NOT_EXIST);
125 setErrorText(d->m_executable);
126 emitResult();
127 return;
128 }
129 } else {
130 d->m_processRunner =
131 KProcessRunner::fromCommand(cmd: d->m_command, desktopName: d->m_desktopName, execName: d->m_executable, asn: d->m_startupId, workingDirectory: d->m_workingDirectory, environment: d->m_environment);
132 }
133 connect(sender: d->m_processRunner, signal: &KProcessRunner::error, context: this, slot: [this](const QString &errorText) {
134 setError(KJob::UserDefinedError);
135 setErrorText(errorText);
136 emitResult();
137 });
138 connect(sender: d->m_processRunner, signal: &KProcessRunner::processStarted, context: this, slot: [this](qint64 pid) {
139 d->m_pid = pid;
140 emitResult();
141 });
142}
143
144bool KIO::CommandLauncherJob::waitForStarted()
145{
146 if (d->m_processRunner.isNull()) {
147 return false;
148 }
149 const bool ret = d->m_processRunner->waitForStarted();
150 if (!d->m_processRunner.isNull()) {
151 qApp->sendPostedEvents(receiver: d->m_processRunner); // so slotStarted gets called
152 }
153 return ret;
154}
155
156qint64 KIO::CommandLauncherJob::pid() const
157{
158 return d->m_pid;
159}
160

source code of kio/src/gui/commandlauncherjob.cpp