File indexing completed on 2024-05-12 04:51:18
0001 /* 0002 This file is part of the KDE libraries 0003 0004 SPDX-FileCopyrightText: 2007 Oswald Buddenhagen <ossi@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #include "k3bkprocess_p.h" 0010 0011 #include <QStandardPaths> 0012 #include <kshell.h> 0013 #ifdef Q_OS_WIN 0014 # include <kshell_p.h> 0015 #endif 0016 0017 #include <QFile> 0018 0019 #ifdef Q_OS_WIN 0020 # include <windows.h> 0021 #else 0022 # include <unistd.h> 0023 # include <errno.h> 0024 #endif 0025 0026 #ifndef Q_OS_WIN 0027 # define STD_OUTPUT_HANDLE 1 0028 # define STD_ERROR_HANDLE 2 0029 #endif 0030 0031 void K3bKProcessPrivate::writeAll(const QByteArray &buf, int fd) 0032 { 0033 #ifdef Q_OS_WIN 0034 HANDLE h = GetStdHandle(fd); 0035 if (h) { 0036 DWORD wr; 0037 WriteFile(h, buf.data(), buf.size(), &wr, 0); 0038 } 0039 #else 0040 int off = 0; 0041 do { 0042 int ret = ::write(fd, buf.data() + off, buf.size() - off); 0043 if (ret < 0) { 0044 if (errno != EINTR) 0045 return; 0046 } else { 0047 off += ret; 0048 } 0049 } while (off < buf.size()); 0050 #endif 0051 } 0052 0053 void K3bKProcessPrivate::forwardStd(::QProcess::ProcessChannel good, int fd) 0054 { 0055 Q_Q(K3bKProcess); 0056 0057 QProcess::ProcessChannel oc = q->readChannel(); 0058 q->setReadChannel(good); 0059 writeAll(q->readAll(), fd); 0060 q->setReadChannel(oc); 0061 } 0062 0063 void K3bKProcessPrivate::_k_forwardStdout() 0064 { 0065 forwardStd(QProcess::StandardOutput, STD_OUTPUT_HANDLE); 0066 } 0067 0068 void K3bKProcessPrivate::_k_forwardStderr() 0069 { 0070 forwardStd(QProcess::StandardError, STD_ERROR_HANDLE); 0071 } 0072 0073 ///////////////////////////// 0074 // public member functions // 0075 ///////////////////////////// 0076 0077 K3bKProcess::K3bKProcess(QObject *parent) : 0078 K3bQProcess(parent), 0079 d_ptr(new K3bKProcessPrivate) 0080 { 0081 d_ptr->q_ptr = this; 0082 setOutputChannelMode(KProcess::ForwardedChannels); 0083 } 0084 0085 K3bKProcess::K3bKProcess(K3bKProcessPrivate *d, QObject *parent) : 0086 K3bQProcess(parent), 0087 d_ptr(d) 0088 { 0089 d_ptr->q_ptr = this; 0090 setOutputChannelMode(KProcess::ForwardedChannels); 0091 } 0092 0093 K3bKProcess::~K3bKProcess() 0094 { 0095 delete d_ptr; 0096 } 0097 0098 void K3bKProcess::setOutputChannelMode(KProcess::OutputChannelMode mode) 0099 { 0100 Q_D(K3bKProcess); 0101 0102 d->outputChannelMode = mode; 0103 disconnect(this, SIGNAL(readyReadStandardOutput())); 0104 disconnect(this, SIGNAL(readyReadStandardError())); 0105 switch (mode) { 0106 case KProcess::OnlyStdoutChannel: 0107 connect(this, SIGNAL(readyReadStandardError()), SLOT(_k_forwardStderr())); 0108 break; 0109 case KProcess::OnlyStderrChannel: 0110 connect(this, SIGNAL(readyReadStandardOutput()), SLOT(_k_forwardStdout())); 0111 break; 0112 default: 0113 K3bQProcess::setProcessChannelMode((QProcess::ProcessChannelMode)mode); 0114 return; 0115 } 0116 K3bQProcess::setProcessChannelMode(QProcess::SeparateChannels); 0117 } 0118 0119 KProcess::OutputChannelMode K3bKProcess::outputChannelMode() const 0120 { 0121 Q_D(const K3bKProcess); 0122 0123 return d->outputChannelMode; 0124 } 0125 0126 void K3bKProcess::setNextOpenMode(QIODevice::OpenMode mode) 0127 { 0128 Q_D(K3bKProcess); 0129 0130 d->openMode = mode; 0131 } 0132 0133 #define DUMMYENV "_KPROCESS_DUMMY_=" 0134 0135 void K3bKProcess::clearEnvironment() 0136 { 0137 setEnvironment(QStringList() << QString::fromLatin1(DUMMYENV)); 0138 } 0139 0140 void K3bKProcess::setEnv(const QString &name, const QString &value, bool overwrite) 0141 { 0142 QStringList env = environment(); 0143 if (env.isEmpty()) { 0144 env = systemEnvironment(); 0145 env.removeAll(QString::fromLatin1(DUMMYENV)); 0146 } 0147 QString fname(name); 0148 fname.append('='); 0149 for (QStringList::Iterator it = env.begin(); it != env.end(); ++it) 0150 if ((*it).startsWith(fname)) { 0151 if (overwrite) { 0152 *it = fname.append(value); 0153 setEnvironment(env); 0154 } 0155 return; 0156 } 0157 env.append(fname.append(value)); 0158 setEnvironment(env); 0159 } 0160 0161 void K3bKProcess::unsetEnv(const QString &name) 0162 { 0163 QStringList env = environment(); 0164 if (env.isEmpty()) { 0165 env = systemEnvironment(); 0166 env.removeAll(QString::fromLatin1(DUMMYENV)); 0167 } 0168 QString fname(name); 0169 fname.append('='); 0170 for (QStringList::Iterator it = env.begin(); it != env.end(); ++it) 0171 if ((*it).startsWith(fname)) { 0172 env.erase(it); 0173 if (env.isEmpty()) 0174 env.append(DUMMYENV); 0175 setEnvironment(env); 0176 return; 0177 } 0178 } 0179 0180 void K3bKProcess::setProgram(const QString &exe, const QStringList &args) 0181 { 0182 Q_D(K3bKProcess); 0183 0184 d->prog = exe; 0185 d->args = args; 0186 } 0187 0188 void K3bKProcess::setProgram(const QStringList &argv) 0189 { 0190 Q_D(K3bKProcess); 0191 0192 Q_ASSERT( !argv.isEmpty() ); 0193 d->args = argv; 0194 d->prog = d->args.takeFirst(); 0195 } 0196 0197 K3bKProcess &K3bKProcess::operator<<(const QString &arg) 0198 { 0199 Q_D(K3bKProcess); 0200 0201 if (d->prog.isEmpty()) 0202 d->prog = arg; 0203 else 0204 d->args << arg; 0205 return *this; 0206 } 0207 0208 K3bKProcess &K3bKProcess::operator<<(const QStringList &args) 0209 { 0210 Q_D(K3bKProcess); 0211 0212 if (d->prog.isEmpty()) 0213 setProgram(args); 0214 else 0215 d->args << args; 0216 return *this; 0217 } 0218 0219 void K3bKProcess::clearProgram() 0220 { 0221 Q_D(K3bKProcess); 0222 0223 d->prog.clear(); 0224 d->args.clear(); 0225 } 0226 0227 void K3bKProcess::setShellCommand(const QString &cmd) 0228 { 0229 Q_D(K3bKProcess); 0230 0231 KShell::Errors err; 0232 d->args = KShell::splitArgs( 0233 cmd, KShell::AbortOnMeta | KShell::TildeExpand, &err); 0234 if (err == KShell::NoError && !d->args.isEmpty()) { 0235 d->prog = QStandardPaths::findExecutable(d->args[0]); 0236 if (!d->prog.isEmpty()) { 0237 d->args.removeFirst(); 0238 return; 0239 } 0240 } 0241 0242 d->args.clear(); 0243 0244 #ifdef Q_OS_UNIX 0245 // #ifdef NON_FREE // ... as they ship non-POSIX /bin/sh 0246 # if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) && !defined(__GNU__) 0247 // If /bin/sh is a symlink, we can be pretty sure that it points to a 0248 // POSIX shell - the original bourne shell is about the only non-POSIX 0249 // shell still in use and it is always installed natively as /bin/sh. 0250 d->prog = QFile::symLinkTarget(QString::fromLatin1("/bin/sh")); 0251 if (d->prog.isEmpty()) { 0252 // Try some known POSIX shells. 0253 d->prog = QStandardPaths::findExecutable("ksh"); 0254 if (d->prog.isEmpty()) { 0255 d->prog = QStandardPaths::findExecutable("ash"); 0256 if (d->prog.isEmpty()) { 0257 d->prog = QStandardPaths::findExecutable("bash"); 0258 if (d->prog.isEmpty()) { 0259 d->prog = QStandardPaths::findExecutable("zsh"); 0260 if (d->prog.isEmpty()) 0261 // We're pretty much screwed, to be honest ... 0262 d->prog = QString::fromLatin1("/bin/sh"); 0263 } 0264 } 0265 } 0266 } 0267 # else 0268 d->prog = QString::fromLatin1("/bin/sh"); 0269 # endif 0270 0271 d->args << "-c" << cmd; 0272 #else // Q_OS_UNIX 0273 // KMacroExpander::expandMacrosShellQuote(), KShell::quoteArg() and 0274 // KShell::joinArgs() may generate these for security reasons. 0275 setEnv(PERCENT_VARIABLE, "%"); 0276 0277 //see also TrollTechTaskTracker entry 88373. 0278 d->prog = QStandardPaths::findExecutable("kcmdwrapper"); 0279 0280 UINT size; 0281 WCHAR sysdir[MAX_PATH + 1]; 0282 size = GetSystemDirectoryW(sysdir, MAX_PATH + 1); 0283 QString cmdexe = QString::fromUtf16((const ushort *) sysdir, size); 0284 cmdexe.append("\\cmd.exe"); 0285 0286 d->args << cmdexe << cmd; 0287 #endif 0288 } 0289 0290 QStringList K3bKProcess::program() const 0291 { 0292 Q_D(const K3bKProcess); 0293 0294 QStringList argv = d->args; 0295 argv.prepend(d->prog); 0296 return argv; 0297 } 0298 0299 void K3bKProcess::start() 0300 { 0301 Q_D(K3bKProcess); 0302 0303 K3bQProcess::start(d->prog, d->args, d->openMode); 0304 } 0305 0306 int K3bKProcess::execute(int msecs) 0307 { 0308 start(); 0309 if (!waitForFinished(msecs)) { 0310 kill(); 0311 waitForFinished(-1); 0312 return -2; 0313 } 0314 return (exitStatus() == QProcess::NormalExit) ? exitCode() : -1; 0315 } 0316 0317 // static 0318 int K3bKProcess::execute(const QString &exe, const QStringList &args, int msecs) 0319 { 0320 K3bKProcess p; 0321 p.setProgram(exe, args); 0322 return p.execute(msecs); 0323 } 0324 0325 // static 0326 int K3bKProcess::execute(const QStringList &argv, int msecs) 0327 { 0328 K3bKProcess p; 0329 p.setProgram(argv); 0330 return p.execute(msecs); 0331 } 0332 0333 int K3bKProcess::startDetached() 0334 { 0335 Q_D(K3bKProcess); 0336 0337 qint64 pid; 0338 if (!K3bQProcess::startDetached(d->prog, d->args, workingDirectory(), &pid)) 0339 return 0; 0340 return (int) pid; 0341 } 0342 0343 // static 0344 int K3bKProcess::startDetached(const QString &exe, const QStringList &args) 0345 { 0346 qint64 pid; 0347 if (!K3bQProcess::startDetached(exe, args, QString(), &pid)) 0348 return 0; 0349 return (int) pid; 0350 } 0351 0352 // static 0353 int K3bKProcess::startDetached(const QStringList &argv) 0354 { 0355 QStringList args = argv; 0356 QString prog = args.takeFirst(); 0357 return startDetached(prog, args); 0358 } 0359 0360 int K3bKProcess::pid() const 0361 { 0362 #ifdef Q_OS_UNIX 0363 return (int) K3bQProcess::pid(); 0364 #else 0365 return K3bQProcess::pid() ? K3bQProcess::pid()->dwProcessId : 0; 0366 #endif 0367 } 0368 0369 #include "moc_k3bkprocess.cpp"