1 /*
2     SPDX-FileCopyrightText: 2009 George Kiagiadakis <gkiagia@users.sourceforge.net>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include <config-drkonqi.h>
8 
9 #include "crashedapplication.h"
10 
11 #if HAVE_STRSIGNAL && defined(Q_OS_UNIX)
12 #include <clocale>
13 #include <cstdlib>
14 #include <cstring>
15 #else
16 #if defined(Q_OS_UNIX)
17 #include <signal.h>
18 #else
19 #include <windows.h>
20 #endif
21 #endif
22 
23 #include <KIO/CommandLauncherJob>
24 
CrashedApplication(int pid,int thread,int signalNumber,const QFileInfo & executable,const QString & version,const BugReportAddress & reportAddress,const QString & name,const QString & productName,const QDateTime & datetime,bool restarted,bool hasDeletedFiles,const QString & fakeBaseName,QObject * parent)25 CrashedApplication::CrashedApplication(int pid,
26                                        int thread,
27                                        int signalNumber,
28                                        const QFileInfo &executable,
29                                        const QString &version,
30                                        const BugReportAddress &reportAddress,
31                                        const QString &name,
32                                        const QString &productName,
33                                        const QDateTime &datetime,
34                                        bool restarted,
35                                        bool hasDeletedFiles,
36                                        const QString &fakeBaseName,
37                                        QObject *parent)
38 
39     : QObject(parent)
40     , m_pid(pid)
41     , m_signalNumber(signalNumber)
42     , m_name(name)
43     , m_executable(executable)
44     , m_fakeBaseName(fakeBaseName)
45     , m_version(version)
46     , m_reportAddress(reportAddress)
47     , m_productName(productName)
48     , m_restarted(restarted)
49     , m_thread(thread)
50     , m_datetime(datetime)
51     , m_hasDeletedFiles(hasDeletedFiles)
52 
53 {
54 }
55 
56 CrashedApplication::~CrashedApplication() = default;
57 
name() const58 QString CrashedApplication::name() const
59 {
60     return m_name.isEmpty() ? fakeExecutableBaseName() : m_name;
61 }
62 
executable() const63 QFileInfo CrashedApplication::executable() const
64 {
65     return m_executable;
66 }
67 
fakeExecutableBaseName() const68 QString CrashedApplication::fakeExecutableBaseName() const
69 {
70     if (!m_fakeBaseName.isEmpty()) {
71         return m_fakeBaseName;
72     } else {
73         return m_executable.baseName();
74     }
75 }
76 
version() const77 QString CrashedApplication::version() const
78 {
79     return m_version;
80 }
81 
bugReportAddress() const82 BugReportAddress CrashedApplication::bugReportAddress() const
83 {
84     return m_reportAddress;
85 }
86 
productName() const87 QString CrashedApplication::productName() const
88 {
89     return m_productName;
90 }
91 
pid() const92 int CrashedApplication::pid() const
93 {
94     return m_pid;
95 }
96 
signalNumber() const97 int CrashedApplication::signalNumber() const
98 {
99     return m_signalNumber;
100 }
101 
signalName() const102 QString CrashedApplication::signalName() const
103 {
104 #if HAVE_STRSIGNAL && defined(Q_OS_UNIX)
105     const QByteArray originalLocale(std::setlocale(LC_MESSAGES, nullptr));
106     std::setlocale(LC_MESSAGES, "C");
107     const char *name = strsignal(m_signalNumber);
108     std::setlocale(LC_MESSAGES, originalLocale.data() /* empty string if we got a nullptr */);
109     return QString::fromLocal8Bit(name ? name : "Unknown");
110 #else
111     switch (m_signalNumber) {
112 #if defined(Q_OS_UNIX)
113     case SIGILL:
114         return QLatin1String("SIGILL");
115     case SIGABRT:
116         return QLatin1String("SIGABRT");
117     case SIGFPE:
118         return QLatin1String("SIGFPE");
119     case SIGSEGV:
120         return QLatin1String("SIGSEGV");
121     case SIGBUS:
122         return QLatin1String("SIGBUS");
123 #else
124     case EXCEPTION_ACCESS_VIOLATION:
125         return QLatin1String("EXCEPTION_ACCESS_VIOLATION");
126     case EXCEPTION_DATATYPE_MISALIGNMENT:
127         return QLatin1String("EXCEPTION_DATATYPE_MISALIGNMENT");
128     case EXCEPTION_BREAKPOINT:
129         return QLatin1String("EXCEPTION_BREAKPOINT");
130     case EXCEPTION_SINGLE_STEP:
131         return QLatin1String("EXCEPTION_SINGLE_STEP");
132     case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
133         return QLatin1String("EXCEPTION_ARRAY_BOUNDS_EXCEEDED");
134     case EXCEPTION_FLT_DENORMAL_OPERAND:
135         return QLatin1String("EXCEPTION_FLT_DENORMAL_OPERAND");
136     case EXCEPTION_FLT_DIVIDE_BY_ZERO:
137         return QLatin1String("EXCEPTION_FLT_DIVIDE_BY_ZERO");
138     case EXCEPTION_FLT_INEXACT_RESULT:
139         return QLatin1String("EXCEPTION_FLT_INEXACT_RESULT");
140     case EXCEPTION_FLT_INVALID_OPERATION:
141         return QLatin1String("EXCEPTION_FLT_INVALID_OPERATION");
142     case EXCEPTION_FLT_OVERFLOW:
143         return QLatin1String("EXCEPTION_FLT_OVERFLOW");
144     case EXCEPTION_FLT_STACK_CHECK:
145         return QLatin1String("EXCEPTION_FLT_STACK_CHECK");
146     case EXCEPTION_FLT_UNDERFLOW:
147         return QLatin1String("EXCEPTION_FLT_UNDERFLOW");
148     case EXCEPTION_INT_DIVIDE_BY_ZERO:
149         return QLatin1String("EXCEPTION_INT_DIVIDE_BY_ZERO");
150     case EXCEPTION_INT_OVERFLOW:
151         return QLatin1String("EXCEPTION_INT_OVERFLOW");
152     case EXCEPTION_PRIV_INSTRUCTION:
153         return QLatin1String("EXCEPTION_PRIV_INSTRUCTION");
154     case EXCEPTION_IN_PAGE_ERROR:
155         return QLatin1String("EXCEPTION_IN_PAGE_ERROR");
156     case EXCEPTION_ILLEGAL_INSTRUCTION:
157         return QLatin1String("EXCEPTION_ILLEGAL_INSTRUCTION");
158     case EXCEPTION_NONCONTINUABLE_EXCEPTION:
159         return QLatin1String("EXCEPTION_NONCONTINUABLE_EXCEPTION");
160     case EXCEPTION_STACK_OVERFLOW:
161         return QLatin1String("EXCEPTION_STACK_OVERFLOW");
162     case EXCEPTION_INVALID_DISPOSITION:
163         return QLatin1String("EXCEPTION_INVALID_DISPOSITION");
164 #endif
165     default:
166         return QLatin1String("Unknown");
167     }
168 #endif
169 }
170 
hasBeenRestarted() const171 bool CrashedApplication::hasBeenRestarted() const
172 {
173     return m_restarted;
174 }
175 
thread() const176 int CrashedApplication::thread() const
177 {
178     return m_thread;
179 }
180 
datetime() const181 const QDateTime &CrashedApplication::datetime() const
182 {
183     return m_datetime;
184 }
185 
hasDeletedFiles() const186 bool CrashedApplication::hasDeletedFiles() const
187 {
188     return m_hasDeletedFiles;
189 }
190 
restart()191 void CrashedApplication::restart()
192 {
193     if (m_restarted) {
194         return;
195     }
196 
197     // start the application via CommandLauncherJob so it runs in a new cgroup if possible.
198     // if m_fakeBaseName is set, this means m_executable is the path to kdeinit4
199     // so we need to use the fakeBaseName to restart the app
200     auto job = new KIO::CommandLauncherJob(!m_fakeBaseName.isEmpty() ? m_fakeBaseName : m_executable.absoluteFilePath());
201     if (const QString &id = DrKonqi::startupId(); !id.isEmpty()) {
202         job->setStartupId(id.toUtf8());
203     }
204     connect(job, &KIO::CommandLauncherJob::result, this, [job, this] {
205         m_restarted = (job->error() == KJob::NoError);
206         Q_EMIT restarted(m_restarted);
207     });
208     job->start();
209 }
210 
getSuggestedKCrashFilename(const CrashedApplication * app)211 QString getSuggestedKCrashFilename(const CrashedApplication *app)
212 {
213     QString filename =
214         app->fakeExecutableBaseName() + QLatin1Char('-') + app->datetime().toString(QStringLiteral("yyyyMMdd-hhmmss")) + QStringLiteral(".kcrash");
215 
216     if (filename.contains(QLatin1Char('/'))) {
217         filename = filename.mid(filename.lastIndexOf(QLatin1Char('/')) + 1);
218     }
219 
220     return filename;
221 }
222