KCrash

metadata.cpp
1/*
2 SPDX-License-Identifier: LGPL-2.0-or-later
3 SPDX-FileCopyrightText: 2021 Harald Sitter <sitter@kde.org>
4*/
5
6#include "metadata_p.h"
7
8#include <QByteArray>
9#include <cerrno>
10
11#ifdef Q_OS_LINUX
12#include <cstring>
13#include <fcntl.h>
14#include <span>
15#include <sys/stat.h>
16#include <sys/types.h>
17#include <unistd.h>
18#endif
19
20namespace KCrash
21{
22#ifdef Q_OS_LINUX
23MetadataINIWriter::MetadataINIWriter(const QByteArray &path)
24{
25 if (path.isEmpty()) {
26 return;
27 }
28
29 fd = ::open(path.constData(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR);
30 if (fd == -1) {
31 fprintf(stderr, "Failed to open metadata file: %s\n", strerror(errno));
32 } else if (fd >= 0) {
33 writable = true;
34 } else {
35 fprintf(stderr, "MetadataINIWriter: Unexpected fd %d\n", fd);
36 Q_UNREACHABLE();
37 }
38}
39
40void MetadataINIWriter::startGroup(const char *group) const
41{
42 Q_ASSERT(group); // not null
43 Q_ASSERT(strlen(group) > 0); // not empty
44 Q_ASSERT(group[strlen(group) - 1] == '\n'); // end in newline
45
46 if (!writable) {
47 return;
48 }
49
50 write(fd, group, strlen(group));
51}
52
53void MetadataINIWriter::startTagsGroup() const
54{
55 startGroup("[KCrashTags]\n");
56}
57
58void MetadataINIWriter::startExtraGroup() const
59{
60 startGroup("[KCrashExtra]\n");
61}
62
63void MetadataINIWriter::startGPUGroup() const
64{
65 startGroup("[KCrashGPU]\n");
66}
67
68void MetadataINIWriter::startKCrashGroup() const
69{
70 startGroup("[KCrash]\n");
71}
72
73void MetadataINIWriter::close()
74{
75 if (fd >= 0 && ::close(fd) == -1) {
76 fprintf(stderr, "Failed to close metadata file: %s\n", strerror(errno));
77 }
78 writable = false;
79}
80
81void MetadataINIWriter::add(const char *key, const char *value, BoolValue boolValue)
82{
83 Q_ASSERT(key);
84 Q_ASSERT(value);
85 Q_ASSERT(key[0] == '-' && key[1] == '-'); // well-formed '--' prefix. This is important. MetadataWriter presume this
86 Q_UNUSED(boolValue); // value is a bool string but we don't care, we always write the value anyway
87
88 if (!writable) {
89 return;
90 }
91
92 const auto valueSpan = std::span{value, strlen(value)};
93
94 write(fd, key + 2, strlen(key + 2));
95 write(fd, "=", 1);
96 if (strstr(value, "\n")) { // if it contains \n then write literally \n (2 characters)
97 // Could appear in the exception what() string. KConfig knows what to do with this.
98 for (const auto &character : valueSpan) {
99 if (character == '\n') {
100 write(fd, "\\n", 2);
101 } else {
102 write(fd, &character, 1);
103 }
104 }
105 } else { // fast write entire string in one go since it contains no newlines
106 write(fd, valueSpan.data(), valueSpan.size());
107 }
108 write(fd, "\n", 1);
109}
110
111bool MetadataINIWriter::isWritable() const
112{
113 return writable;
114}
115#endif
116
117Metadata::Metadata(const char *cmd)
118{
119 // NB: cmd may be null! Just because we create metadata doesn't mean we'll execute drkonqi (we may only need the
120 // backing writers)
121 Q_ASSERT(argc == 0);
122 argv.at(argc++) = cmd;
123}
124
125void Metadata::setAdditionalWriter(MetadataWriter *writer)
126{
127 // Once set the writer oughtn't be reset as we have no use case for this and should we get one in the future
128 // it'll need at least review of the existing code to handle writer switching correctly.
129 Q_ASSERT(m_writer == nullptr);
130 Q_ASSERT(writer != nullptr);
131 m_writer = writer;
132}
133
134void Metadata::add(const char *key, const char *value)
135{
136 add(key, value, BoolValue::No);
137}
138
139void Metadata::addBool(const char *key)
140{
141 add(key, "true", BoolValue::Yes);
142}
143
144void Metadata::close()
145{
146 // NULL terminated list
147 argv.at(argc) = nullptr;
148
149 if (m_writer) {
150 m_writer->close();
151 }
152}
153
154void Metadata::add(const char *key, const char *value, BoolValue boolValue)
155{
156 Q_ASSERT(key);
157 Q_ASSERT(value);
158 Q_ASSERT(key[0] == '-' && key[1] == '-'); // well-formed '--' prefix. This is important. MetadataWriter presume this
159 const auto belowCap = argc < argv.max_size();
160 Q_ASSERT(belowCap); // argv has a static max size. guard against exhaustion
161 if (!belowCap) {
162 return;
163 }
164
165 argv.at(argc++) = key;
166 if (!boolValue) {
167 argv.at(argc++) = value;
168 }
169
170 if (m_writer) {
171 m_writer->add(key, value, boolValue);
172 }
173}
174
175} // namespace KCrash
This namespace contains functions to handle crashes.
QString path(const QString &relativePath)
KGuiItem add()
const QChar * constData() const const
bool isEmpty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Feb 21 2025 11:47:31 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.