File indexing completed on 2024-04-14 03:51:42

0001 /*
0002     SPDX-License-Identifier: LGPL-2.0-or-later
0003     SPDX-FileCopyrightText: 2021 Harald Sitter <sitter@kde.org>
0004 */
0005 
0006 #include "metadata_p.h"
0007 
0008 #include <QByteArray>
0009 #include <cerrno>
0010 
0011 #ifdef Q_OS_LINUX
0012 #include <cstring>
0013 #include <fcntl.h>
0014 #include <span>
0015 #include <sys/stat.h>
0016 #include <sys/types.h>
0017 #include <unistd.h>
0018 #endif
0019 
0020 namespace KCrash
0021 {
0022 #ifdef Q_OS_LINUX
0023 MetadataINIWriter::MetadataINIWriter(const QByteArray &path)
0024 {
0025     if (path.isEmpty()) {
0026         return;
0027     }
0028 
0029     fd = ::open(path.constData(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, S_IRUSR | S_IWUSR);
0030     if (fd == -1) {
0031         fprintf(stderr, "Failed to open metadata file: %s\n", strerror(errno));
0032     } else if (fd >= 0) {
0033         writable = true;
0034         const char *header = "[KCrash]\n";
0035         write(fd, header, strlen(header));
0036     } else {
0037         fprintf(stderr, "MetadataINIWriter: Unexpected fd %d\n", fd);
0038         Q_UNREACHABLE();
0039     }
0040 }
0041 
0042 void MetadataINIWriter::close()
0043 {
0044     if (fd >= 0 && ::close(fd) == -1) {
0045         fprintf(stderr, "Failed to close metadata file: %s\n", strerror(errno));
0046     }
0047     writable = false;
0048 }
0049 
0050 void MetadataINIWriter::add(const char *key, const char *value, BoolValue boolValue)
0051 {
0052     Q_ASSERT(key);
0053     Q_ASSERT(value);
0054     Q_ASSERT(key[0] == '-' && key[1] == '-'); // well-formed '--' prefix. This is important. MetadataWriter presume this
0055     Q_UNUSED(boolValue); // value is a bool string but we don't care, we always write the value anyway
0056 
0057     if (fd < 0) {
0058         return;
0059     }
0060 
0061     const auto valueSpan = std::span{value, strlen(value)};
0062 
0063     write(fd, key + 2, strlen(key + 2));
0064     write(fd, "=", 1);
0065     if (strstr(value, "\n")) { // if it contains \n then write literally \n (2 characters)
0066         // Could appear in the exception what() string. KConfig knows what to do with this.
0067         for (const auto &character : valueSpan) {
0068             if (character == '\n') {
0069                 write(fd, "\\n", 2);
0070             } else {
0071                 write(fd, &character, 1);
0072             }
0073         }
0074     } else { // fast write entire string in one go since it contains no newlines
0075         write(fd, valueSpan.data(), valueSpan.size());
0076     }
0077     write(fd, "\n", 1);
0078 }
0079 
0080 bool MetadataINIWriter::isWritable() const
0081 {
0082     return writable;
0083 }
0084 #endif
0085 
0086 Metadata::Metadata(const char *cmd)
0087 {
0088     // NB: cmd may be null! Just because we create metadata doesn't mean we'll execute drkonqi (we may only need the
0089     // backing writers)
0090     Q_ASSERT(argc == 0);
0091     argv.at(argc++) = cmd;
0092 }
0093 
0094 void Metadata::setAdditionalWriter(MetadataWriter *writer)
0095 {
0096     // Once set the writer oughtn't be reset as we have no use case for this and should we get one in the future
0097     // it'll need at least review of the existing code to handle writer switching correctly.
0098     Q_ASSERT(m_writer == nullptr);
0099     Q_ASSERT(writer != nullptr);
0100     m_writer = writer;
0101 }
0102 
0103 void Metadata::add(const char *key, const char *value)
0104 {
0105     add(key, value, BoolValue::No);
0106 }
0107 
0108 void Metadata::addBool(const char *key)
0109 {
0110     add(key, "true", BoolValue::Yes);
0111 }
0112 
0113 void Metadata::close()
0114 {
0115     // NULL terminated list
0116     argv.at(argc) = nullptr;
0117 
0118     if (m_writer) {
0119         m_writer->close();
0120     }
0121 }
0122 
0123 void Metadata::add(const char *key, const char *value, BoolValue boolValue)
0124 {
0125     Q_ASSERT(key);
0126     Q_ASSERT(value);
0127     Q_ASSERT(key[0] == '-' && key[1] == '-'); // well-formed '--' prefix. This is important. MetadataWriter presume this
0128     Q_ASSERT(argc < argv.max_size()); // argv has a static max size. guard against exhaustion
0129 
0130     argv.at(argc++) = key;
0131     if (!boolValue) {
0132         argv.at(argc++) = value;
0133     }
0134 
0135     if (m_writer) {
0136         m_writer->add(key, value, boolValue);
0137     }
0138 }
0139 
0140 } // namespace KCrash