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 | |
20 | namespace KCrash |
21 | { |
22 | #ifdef Q_OS_LINUX |
23 | MetadataINIWriter::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 * = "[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 | |
42 | void 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 | |
50 | void 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 | |
80 | bool MetadataINIWriter::isWritable() const |
81 | { |
82 | return writable; |
83 | } |
84 | #endif |
85 | |
86 | Metadata::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 | |
94 | void 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 | |
103 | void Metadata::add(const char *key, const char *value) |
104 | { |
105 | add(key, value, boolValue: BoolValue::No); |
106 | } |
107 | |
108 | void Metadata::addBool(const char *key) |
109 | { |
110 | add(key, value: "true" , boolValue: BoolValue::Yes); |
111 | } |
112 | |
113 | void 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 | |
123 | void 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 | |