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 | |
17 | class KIO::CommandLauncherJobPrivate |
18 | { |
19 | public: |
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 | |
31 | KIO::CommandLauncherJob::CommandLauncherJob(const QString &command, QObject *parent) |
32 | : KJob(parent) |
33 | , d(new CommandLauncherJobPrivate()) |
34 | { |
35 | d->m_command = command; |
36 | } |
37 | |
38 | KIO::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 | |
46 | KIO::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 | |
52 | void KIO::CommandLauncherJob::setCommand(const QString &command) |
53 | { |
54 | d->m_command = command; |
55 | } |
56 | |
57 | QString 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 | |
65 | void KIO::CommandLauncherJob::setExecutable(const QString &executable) |
66 | { |
67 | d->m_executable = executable; |
68 | } |
69 | |
70 | void KIO::CommandLauncherJob::setDesktopName(const QString &desktopName) |
71 | { |
72 | d->m_desktopName = desktopName; |
73 | } |
74 | |
75 | void KIO::CommandLauncherJob::setStartupId(const QByteArray &startupId) |
76 | { |
77 | d->m_startupId = startupId; |
78 | } |
79 | |
80 | void KIO::CommandLauncherJob::setWorkingDirectory(const QString &workingDirectory) |
81 | { |
82 | d->m_workingDirectory = workingDirectory; |
83 | } |
84 | |
85 | QString KIO::CommandLauncherJob::workingDirectory() const |
86 | { |
87 | return d->m_workingDirectory; |
88 | } |
89 | |
90 | void KIO::CommandLauncherJob::setProcessEnvironment(const QProcessEnvironment &environment) |
91 | { |
92 | d->m_environment = environment; |
93 | } |
94 | |
95 | void 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 | |
144 | bool 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 | |
156 | qint64 KIO::CommandLauncherJob::pid() const |
157 | { |
158 | return d->m_pid; |
159 | } |
160 | |