• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wirenboard / wb-mqtt-serial / 967

05 Oct 2026 09:41AM UTC coverage: 85.672% (+2.4%) from 83.268%
967

push

github

web-flow
Add config/Save RPC (#1273)

The RPC validates a configuration, writes it to the configuration file and
restarts the polling in-process. Port clients are stopped first, requests
arriving meanwhile get config-busy and queued ones get request-cancelled.

12445 of 14263 branches covered (87.25%)

1432 of 1623 new or added lines in 41 files covered. (88.23%)

8 existing lines in 4 files now uncovered.

21065 of 24588 relevant lines covered (85.67%)

1528.89 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.75
/src/file_utils.cpp
1
#include "file_utils.h"
2
#include "log.h"
3
#include <algorithm>
4
#include <fcntl.h>
5
#include <filesystem>
6
#include <unistd.h>
7

8
#define LOG(logger) ::logger.Log() << "[file utils] "
9

10
TNoDirError::TNoDirError(const std::string& msg): std::runtime_error(msg)
×
11
{}
×
12

13
bool TryOpen(const std::vector<std::string>& fnames, std::ifstream& file)
×
14
{
15
    for (auto& fname: fnames) {
×
16
        file.open(fname);
×
17
        if (file.is_open()) {
×
18
            return true;
×
19
        }
20
        file.clear();
×
21
    }
22
    return false;
×
23
}
24

25
void WriteToFile(const std::string& fileName, const std::string& value)
24 ✔
26
{
27
    std::ofstream f;
24 ✔
28
    OpenWithException(f, fileName);
24 ✔
29
    f << value;
24 ✔
30
}
24 ✔
31

32
namespace
33
{
34
    bool SyncFile(const std::string& fileName)
98 ✔
35
    {
36
        int fd = open(fileName.c_str(), O_RDONLY);
98 ✔
37
        if (fd < 0) {
98 ✔
NEW
38
            return false;
×
39
        }
40
        int res = fsync(fd);
98 ✔
41
        close(fd);
98 ✔
42
        return res == 0;
98 ✔
43
    }
44
}
45

46
void WriteFileAtomically(const std::string& fileName, const std::string& content)
51 ✔
47
{
48
    std::filesystem::path path(fileName);
51 ✔
49
    if (std::filesystem::is_symlink(path)) {
51 ✔
50
        path = std::filesystem::canonical(path);
1 ✔
51
    }
52
    auto tmpFileName = path.string() + ".tmp";
51 ✔
53
    try {
54
        {
55
            std::ofstream file;
51 ✔
56
            OpenWithException(file, tmpFileName);
51 ✔
57
            file << content;
49 ✔
58
            file.flush();
49 ✔
59
            if (!file.good()) {
49 ✔
NEW
60
                throw std::runtime_error("Failed to write file: " + tmpFileName);
×
61
            }
62
        }
51 ✔
63
        // Closing the file leaves the data in the kernel cache, without the sync the renamed file
64
        // may be empty after a power loss
65
        if (!SyncFile(tmpFileName)) {
49 ✔
NEW
66
            throw std::runtime_error("Failed to sync file: " + tmpFileName);
×
67
        }
68
        std::filesystem::rename(tmpFileName, path);
49 ✔
69
    } catch (...) {
2 ✔
70
        std::error_code ec;
2 ✔
71
        std::filesystem::remove(tmpFileName, ec);
2 ✔
72
        throw;
2 ✔
73
    }
2 ✔
74
    // The rename is in the kernel cache too, without the sync the old file may come back after a power loss.
75
    // Nothing is known about the disk after a failed sync, the file is replaced for the readers anyway
76
    auto dirName = path.parent_path().empty() ? std::string(".") : path.parent_path().string();
49 ✔
77
    if (!SyncFile(dirName)) {
49 ✔
NEW
78
        LOG(Warn) << "Failed to sync directory " << dirName << ", " << path.string()
×
NEW
79
                  << " may be old after a power loss";
×
80
    }
81
}
53 ✔
82

83
void IterateDir(const std::string& dirName, std::function<bool(const std::string&)> fn)
381 ✔
84
{
85
    try {
86
        const std::filesystem::path dirPath{dirName};
381 ✔
87

88
        for (const auto& entry: std::filesystem::directory_iterator(dirPath)) {
9,617 ✔
89
            const auto filenameStr = entry.path().filename().string();
9,236 ✔
90
            if (fn(filenameStr)) {
9,236 ✔
91
                return;
×
92
            }
93
        }
9,617 ✔
94
    } catch (std::filesystem::filesystem_error const& ex) {
381 ✔
95
        throw TNoDirError(ex.what());
×
96
    }
×
97
}
98

99
std::string IterateDirByPattern(const std::string& dirName,
196 ✔
100
                                const std::string& pattern,
101
                                std::function<bool(const std::string&)> fn,
102
                                bool sort)
103
{
104
    if (sort) {
196 ✔
105
        std::vector<std::string> files;
196 ✔
106
        IterateDir(dirName, [&](const auto& name) {
196 ✔
107
            if (name.find(pattern) != std::string::npos) {
6,727 ✔
108
                files.push_back(dirName + "/" + name);
6,727 ✔
109
            }
110
            return false;
6,727 ✔
111
        });
112
        std::sort(files.begin(), files.end());
196 ✔
113
        for (const auto& fileName: files) {
6,923 ✔
114
            if (fn(fileName)) {
6,727 ✔
115
                return fileName;
×
116
            }
117
        }
118
        return std::string();
196 ✔
119
    }
196 ✔
120
    std::string res;
×
121
    IterateDir(dirName, [&](const auto& name) {
×
122
        if (name.find(pattern) != std::string::npos) {
×
123
            std::string d(dirName + "/" + name);
×
124
            if (fn(d)) {
×
125
                res = d;
×
126
                return true;
×
127
            }
128
        }
×
129
        return false;
×
130
    });
131
    return res;
×
132
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc