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