diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -34,6 +34,7 @@ crashedapplication.cpp debugger.cpp debuggerlaunchers.cpp + ptracer.cpp debuggermanager.cpp applicationdetailsexamples.cpp gdbhighlighter.cpp diff --git a/src/debuggerlaunchers.h b/src/debuggerlaunchers.h --- a/src/debuggerlaunchers.h +++ b/src/debuggerlaunchers.h @@ -78,14 +78,15 @@ { Q_OBJECT public: - explicit DBusInterfaceLauncher(const QString &name, DBusInterfaceAdaptor *parent = nullptr); + explicit DBusInterfaceLauncher(const QString &name, qint64 pid, DBusInterfaceAdaptor *parent = nullptr); QString name() const override; public Q_SLOTS: void start() override; private: QString m_name; + qint64 m_pid; }; class DBusInterfaceAdaptor : public QDBusAbstractAdaptor @@ -97,7 +98,7 @@ public Q_SLOTS: int pid(); - Q_NOREPLY void registerDebuggingApplication(const QString &name); + Q_NOREPLY void registerDebuggingApplication(const QString &name, qint64 pid = 0); Q_NOREPLY void debuggingFinished(const QString &name); Q_NOREPLY void debuggerClosed(const QString &name); diff --git a/src/debuggerlaunchers.cpp b/src/debuggerlaunchers.cpp --- a/src/debuggerlaunchers.cpp +++ b/src/debuggerlaunchers.cpp @@ -16,6 +16,7 @@ */ #include "debuggerlaunchers.h" +#include #include #include @@ -26,6 +27,8 @@ #include "drkonqi.h" #include "crashedapplication.h" +#include "ptracer.h" + DefaultDebuggerLauncher::DefaultDebuggerLauncher(const Debugger & debugger, DebuggerManager *parent) : AbstractDebuggerLauncher(parent), m_debugger(debugger) { @@ -51,6 +54,7 @@ emit starting(); int pid = KProcess::startDetached(KShell::splitArgs(str)); if ( pid > 0 ) { + setPtracer(pid, DrKonqi::pid()); m_monitor->startMonitoring(pid); } else { qCWarning(DRKONQI_LOG) << "Could not start debugger:" << name(); @@ -60,6 +64,7 @@ void DefaultDebuggerLauncher::onProcessFinished() { + setPtracer(QCoreApplication::applicationPid(), DrKonqi::pid()); emit finished(); } @@ -76,8 +81,8 @@ #endif -DBusInterfaceLauncher::DBusInterfaceLauncher(const QString &name, DBusInterfaceAdaptor *parent) - : AbstractDebuggerLauncher(parent), m_name(name) +DBusInterfaceLauncher::DBusInterfaceLauncher(const QString &name, qint64 pid, DBusInterfaceAdaptor *parent) + : AbstractDebuggerLauncher(parent), m_name(name), m_pid(pid) { } @@ -89,6 +94,9 @@ void DBusInterfaceLauncher::start() { emit starting(); + + setPtracer(m_pid, DrKonqi::pid()); + emit static_cast(parent())->acceptDebuggingApplication(m_name); } @@ -108,10 +116,10 @@ return DrKonqi::crashedApplication()->pid(); } -void DBusInterfaceAdaptor::registerDebuggingApplication(const QString &name) +void DBusInterfaceAdaptor::registerDebuggingApplication(const QString &name, qint64 pid) { if (!name.isEmpty() && !m_launchers.contains(name)) { - auto launcher = new DBusInterfaceLauncher(name, this); + auto launcher = new DBusInterfaceLauncher(name, pid, this); m_launchers.insert(name, launcher); static_cast(parent())->addDebugger(launcher, true); } @@ -121,6 +129,7 @@ { auto it = m_launchers.find(name); if (it != m_launchers.end()) { + setPtracer(QCoreApplication::applicationPid(), DrKonqi::pid()); emit it.value()->finished(); } } diff --git a/src/ptracer.h b/src/ptracer.h new file mode 100644 --- /dev/null +++ b/src/ptracer.h @@ -0,0 +1,25 @@ +/* + Copyright (C) 2019 Christoph Roick + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef PTRACER_H +#define PTRACER_H + +#include + +/** On Linux, tell the process to allow the debugger to attach to it */ +void setPtracer(qint64 debuggerpid, qint64 debuggeepid); + +#endif diff --git a/src/ptracer.cpp b/src/ptracer.cpp new file mode 100644 --- /dev/null +++ b/src/ptracer.cpp @@ -0,0 +1,103 @@ +/* + Copyright (C) 2019 Christoph Roick + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include "ptracer.h" + +#ifdef Q_OS_LINUX + +#include "drkonqi_debug.h" + +#include +#include + +#include +#include +#include + +#include + +void setPtracer(qint64 debuggerpid, qint64 debuggeepid) +{ + + int sfd = socket(PF_UNIX, SOCK_STREAM, 0); + if (sfd < 0) { + qCWarning(DRKONQI_LOG) << "socket to set ptracer not accessible"; + return; + } + + static struct sockaddr_un server; + static socklen_t sl = sizeof(server); + server.sun_family = AF_UNIX; + const QString socketPath = + QStringLiteral("%1/kcrash_%2").arg(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation)) + .arg(debuggeepid); + + if (socketPath.size() >= static_cast(sizeof(server.sun_path))) { + qCWarning(DRKONQI_LOG) << "socket path is too long"; + close(sfd); + return; + } + strcpy(server.sun_path, QFile::encodeName(socketPath).constData()); + + if (::connect(sfd, (struct sockaddr *)&server, sl) == 0) { + static const int msize = 21; // most digits in a 64bit int (+sign +'\0') + char msg[msize]; + sprintf(msg, "%lld", debuggerpid); + + int r, bytes = 0; + while (bytes < msize) { + r = write(sfd, msg + bytes, msize - bytes); + if (r > 0) + bytes += r; + else if (r == -1 && errno != EINTR) + break; + } + if (bytes == msize) { + struct pollfd fd; + fd.fd = sfd; + fd.events = POLLIN; + while ((r = poll(&fd, 1, 1000)) == -1 && errno == EINTR) {} + if (r > 0 && (fd.revents & POLLIN)) { + char rmsg[msize]; + bytes = 0; + while (bytes < msize) { + r = read(sfd, rmsg + bytes, msize - bytes); + if (r > 0) + bytes += r; + else if (r == -1 && errno != EINTR) + break; + } + if (bytes == msize && memcmp(msg, rmsg, msize) == 0) + qCInfo(DRKONQI_LOG) << "ptracer set to" << debuggerpid << "by debugged process"; + else + qCWarning(DRKONQI_LOG) << "debugged process did not acknowledge setting ptracer to" << debuggerpid; + close(sfd); + return; + } + } + } + + qCWarning(DRKONQI_LOG) << "unable to set ptracer to" << debuggerpid; + close(sfd); +} + +#else + +void setPtracer(qint64, qint64) +{ +} + +#endif diff --git a/src/tests/crashtest/crashtest.cpp b/src/tests/crashtest/crashtest.cpp --- a/src/tests/crashtest/crashtest.cpp +++ b/src/tests/crashtest/crashtest.cpp @@ -130,13 +130,17 @@ QCommandLineParser parser; parser.addOption(QCommandLineOption(QStringLiteral("autorestart"), i18n("Automatically restart"))); + parser.addOption(QCommandLineOption(QStringLiteral("kdeinit"), i18n("Start DrKonqi using kdeinit"))); parser.addPositionalArgument(QStringLiteral("type"), i18n("Type of crash."), QStringLiteral("crash|malloc|div0|assert|threads")); aboutData.setupCommandLine(&parser); parser.process(app); aboutData.processCommandLine(&parser); - //start drkonqi directly so that drkonqi's output goes to the console + // Start drkonqi directly by default so that drkonqi's output goes to the console. KCrash::CrashFlags flags = KCrash::AlwaysDirectly; + // This can be disabled to be able to test kcrash's real default behavior. + if (parser.isSet(QStringLiteral("kdeinit"))) + flags &= ~KCrash::AlwaysDirectly; if (parser.isSet(QStringLiteral("autorestart"))) flags |= KCrash::AutoRestart; KCrash::setFlags(flags);