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"