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(file: path.constData(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR);
30 if (fd == -1) {
31 fprintf(stderr, format: "Failed to open metadata file: %s\n", strerror(errno));
32 } else if (fd >= 0) {
33 writable = true;
34 const char *header = "[KCrash]\n";
35 write(fd: fd, buf: header, n: strlen(s: header));
36 } else {
37 fprintf(stderr, format: "MetadataINIWriter: Unexpected fd %d\n", fd);
38 Q_UNREACHABLE();
39 }
40}
41
42void MetadataINIWriter::close()
43{
44 if (fd >= 0 && ::close(fd: fd) == -1) {
45 fprintf(stderr, format: "Failed to close metadata file: %s\n", strerror(errno));
46 }
47 writable = false;
48}
49
50void MetadataINIWriter::add(const char *key, const char *value, BoolValue boolValue)
51{
52 Q_ASSERT(key);
53 Q_ASSERT(value);
54 Q_ASSERT(key[0] == '-' && key[1] == '-'); // well-formed '--' prefix. This is important. MetadataWriter presume this
55 Q_UNUSED(boolValue); // value is a bool string but we don't care, we always write the value anyway
56
57 if (fd < 0) {
58 return;
59 }
60
61 const auto valueSpan = std::span{value, strlen(s: value)};
62
63 write(fd: fd, buf: key + 2, n: strlen(s: key + 2));
64 write(fd: fd, buf: "=", n: 1);
65 if (strstr(haystack: value, needle: "\n")) { // if it contains \n then write literally \n (2 characters)
66 // Could appear in the exception what() string. KConfig knows what to do with this.
67 for (const auto &character : valueSpan) {
68 if (character == '\n') {
69 write(fd: fd, buf: "\\n", n: 2);
70 } else {
71 write(fd: fd, buf: &character, n: 1);
72 }
73 }
74 } else { // fast write entire string in one go since it contains no newlines
75 write(fd: fd, buf: valueSpan.data(), n: valueSpan.size());
76 }
77 write(fd: fd, buf: "\n", n: 1);
78}
79
80bool MetadataINIWriter::isWritable() const
81{
82 return writable;
83}
84#endif
85
86Metadata::Metadata(const char *cmd)
87{
88 // NB: cmd may be null! Just because we create metadata doesn't mean we'll execute drkonqi (we may only need the
89 // backing writers)
90 Q_ASSERT(argc == 0);
91 argv.at(n: argc++) = cmd;
92}
93
94void Metadata::setAdditionalWriter(MetadataWriter *writer)
95{
96 // Once set the writer oughtn't be reset as we have no use case for this and should we get one in the future
97 // it'll need at least review of the existing code to handle writer switching correctly.
98 Q_ASSERT(m_writer == nullptr);
99 Q_ASSERT(writer != nullptr);
100 m_writer = writer;
101}
102
103void Metadata::add(const char *key, const char *value)
104{
105 add(key, value, boolValue: BoolValue::No);
106}
107
108void Metadata::addBool(const char *key)
109{
110 add(key, value: "true", boolValue: BoolValue::Yes);
111}
112
113void Metadata::close()
114{
115 // NULL terminated list
116 argv.at(n: argc) = nullptr;
117
118 if (m_writer) {
119 m_writer->close();
120 }
121}
122
123void Metadata::add(const char *key, const char *value, BoolValue boolValue)
124{
125 Q_ASSERT(key);
126 Q_ASSERT(value);
127 Q_ASSERT(key[0] == '-' && key[1] == '-'); // well-formed '--' prefix. This is important. MetadataWriter presume this
128 Q_ASSERT(argc < argv.max_size()); // argv has a static max size. guard against exhaustion
129
130 argv.at(n: argc++) = key;
131 if (!boolValue) {
132 argv.at(n: argc++) = value;
133 }
134
135 if (m_writer) {
136 m_writer->add(key, value, boolValue);
137 }
138}
139
140} // namespace KCrash
141

source code of kcrash/src/metadata.cpp