File indexing completed on 2024-04-28 15:20:27

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 <sys/stat.h>
0015 #include <sys/types.h>
0016 #include <unistd.h>
0017 #endif
0018 
0019 namespace KCrash
0020 {
0021 #ifdef Q_OS_LINUX
0022 MetadataINIWriter::MetadataINIWriter(const QByteArray &path)
0023     : MetadataWriter()
0024 {
0025     if (path.isEmpty()) {
0026         return;
0027     }
0028 
0029     fd = ::open(path.constData(), O_WRONLY | O_CREAT | O_NONBLOCK | 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     const int ret = snprintf(iniLine.data(), iniLine.max_size(), "%s=%s\n", key + 2 /** skip the leading -- */, value);
0061     if (ret < 0) {
0062         fprintf(stderr, "Failed to generate metadata line for '%s', '%s'\n", key, value);
0063         return;
0064     }
0065     // Cannot be negative anymore.
0066     const std::make_unsigned<decltype(ret)>::type lineLength = ret;
0067     fprintf(stderr, "%d -- %s", lineLength, iniLine.data());
0068     Q_ASSERT(lineLength <= iniLine.max_size()); // is not truncated41
0069     write(fd, iniLine.data(), lineLength);
0070 }
0071 
0072 bool MetadataINIWriter::isWritable() const
0073 {
0074     return writable;
0075 }
0076 #endif
0077 
0078 Metadata::Metadata(const char *cmd)
0079 {
0080     // NB: cmd may be null! Just because we create metadata doesn't mean we'll execute drkonqi (we may only need the
0081     // backing writers)
0082     Q_ASSERT(argc == 0);
0083     argv.at(argc++) = cmd;
0084 }
0085 
0086 void Metadata::setAdditionalWriter(MetadataWriter *writer)
0087 {
0088     // Once set the writer oughtn't be reset as we have no use case for this and should we get one in the future
0089     // it'll need at least review of the existing code to handle writer switching correctly.
0090     Q_ASSERT(m_writer == nullptr);
0091     Q_ASSERT(writer != nullptr);
0092     m_writer = writer;
0093 }
0094 
0095 void Metadata::add(const char *key, const char *value)
0096 {
0097     add(key, value, BoolValue::No);
0098 }
0099 
0100 void Metadata::addBool(const char *key)
0101 {
0102     add(key, "true", BoolValue::Yes);
0103 }
0104 
0105 void Metadata::close()
0106 {
0107     // NULL terminated list
0108     argv.at(argc) = nullptr;
0109 
0110     if (m_writer) {
0111         m_writer->close();
0112     }
0113 }
0114 
0115 void Metadata::add(const char *key, const char *value, BoolValue boolValue)
0116 {
0117     Q_ASSERT(key);
0118     Q_ASSERT(value);
0119     Q_ASSERT(key[0] == '-' && key[1] == '-'); // well-formed '--' prefix. This is important. MetadataWriter presume this
0120     Q_ASSERT(argc < argv.max_size()); // argv has a static max size. guard against exhaustion
0121 
0122     argv.at(argc++) = key;
0123     if (!boolValue) {
0124         argv.at(argc++) = value;
0125     }
0126 
0127     if (m_writer) {
0128         m_writer->add(key, value, boolValue);
0129     }
0130 }
0131 
0132 } // namespace KCrash