Coverage Report

Created: 2025-06-13 06:45

/src/Fast-DDS/src/cpp/utils/SystemInfo.cpp
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2021 Proyectos y Sistemas de Mantenimiento SL (eProsima).
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#include "SystemInfo.hpp"
16
17
#ifdef __unix__
18
#   include <sys/file.h>
19
#   include <unistd.h>
20
#endif // ifdef __unix__
21
22
#ifdef _WIN32
23
#include <windows.h>
24
#else
25
#include <pwd.h>
26
#include <sys/stat.h>
27
#endif // _WIN32
28
29
#include <chrono>
30
#include <fstream>
31
#include <iomanip>
32
#include <mutex>
33
#include <string>
34
#include <thread>
35
#include <time.h>
36
37
#include <nlohmann/json.hpp>
38
39
#include <fastdds/dds/core/ReturnCode.hpp>
40
#include <fastdds/utils/IPFinder.hpp>
41
#include <utils/threading.hpp>
42
43
namespace eprosima {
44
45
using IPFinder = fastdds::rtps::IPFinder;
46
47
SystemInfo::SystemInfo()
48
0
{
49
    // From ctime(3) linux man page:
50
    // According to POSIX.1-2004, localtime() is required to behave as though tzset(3) was called, while
51
    // localtime_r() does not have this requirement. For portable code tzset(3) should be called before
52
    // localtime_r().
53
0
#if (_POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || \
54
0
    defined(_POSIX_SOURCE) || defined(__unix__)
55
0
    tzset();
56
0
#endif // if (_POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) ||
57
       // defined(_POSIX_SOURCE) || defined(__unix__)
58
59
0
    update_interfaces();
60
0
}
61
62
fastdds::dds::ReturnCode_t SystemInfo::get_env(
63
        const std::string& env_name,
64
        std::string& env_value)
65
0
{
66
0
    if (env_name.empty())
67
0
    {
68
0
        return fastdds::dds::RETCODE_BAD_PARAMETER;
69
0
    }
70
71
    // Try to read environment variable from file
72
0
    if (!environment_file_.empty() && fastdds::dds::RETCODE_OK == get_env(environment_file_, env_name, env_value))
73
0
    {
74
0
        return fastdds::dds::RETCODE_OK;
75
0
    }
76
77
0
    char* data;
78
0
#pragma warning(suppress:4996)
79
0
    data = getenv(env_name.c_str());
80
0
    if (nullptr != data)
81
0
    {
82
0
        env_value = data;
83
0
    }
84
0
    else
85
0
    {
86
0
        return fastdds::dds::RETCODE_NO_DATA;
87
0
    }
88
89
0
    return fastdds::dds::RETCODE_OK;
90
0
}
91
92
fastdds::dds::ReturnCode_t SystemInfo::get_env(
93
        const std::string& filename,
94
        const std::string& env_name,
95
        std::string& env_value)
96
0
{
97
    // Check that the file exists
98
0
    if (!SystemInfo::file_exists(filename))
99
0
    {
100
0
        return fastdds::dds::RETCODE_BAD_PARAMETER;
101
0
    }
102
103
    // Read json file
104
0
    std::ifstream file(filename);
105
0
    nlohmann::json file_content;
106
107
0
    try
108
0
    {
109
0
        file >> file_content;
110
0
    }
111
0
    catch (const nlohmann::json::exception&)
112
0
    {
113
0
        return fastdds::dds::RETCODE_ERROR;
114
0
    }
115
116
0
    try
117
0
    {
118
0
        env_value = file_content.at(env_name);
119
0
    }
120
0
    catch (const nlohmann::json::exception&)
121
0
    {
122
0
        return fastdds::dds::RETCODE_NO_DATA;
123
0
    }
124
0
    return fastdds::dds::RETCODE_OK;
125
0
}
126
127
fastdds::dds::ReturnCode_t SystemInfo::get_username(
128
        std::string& username)
129
0
{
130
#ifdef _WIN32
131
#define INFO_BUFFER_SIZE 32767
132
    char user[INFO_BUFFER_SIZE];
133
    DWORD bufCharCount = INFO_BUFFER_SIZE;
134
    if (!GetUserNameA(user, &bufCharCount))
135
    {
136
        return fastdds::dds::RETCODE_ERROR;
137
    }
138
    username = user;
139
    return fastdds::dds::RETCODE_OK;
140
#else
141
0
    uid_t user_id = geteuid();
142
0
    struct passwd* pwd = getpwuid(user_id);
143
0
    if (pwd != nullptr)
144
0
    {
145
0
        username = pwd->pw_name;
146
0
        if (!username.empty())
147
0
        {
148
0
            return fastdds::dds::RETCODE_OK;
149
0
        }
150
0
    }
151
0
    return fastdds::dds::RETCODE_ERROR;
152
0
#endif // _WIN32
153
0
}
154
155
bool SystemInfo::file_exists(
156
        const std::string& filename)
157
0
{
158
#ifdef _WIN32
159
    // modify for mingw
160
    DWORD fileAttributes = GetFileAttributesA(filename.c_str());
161
    if (fileAttributes == INVALID_FILE_ATTRIBUTES)
162
    {
163
        return false;
164
    }
165
    return !(fileAttributes & FILE_ATTRIBUTE_DIRECTORY);
166
#else
167
0
    struct stat s;
168
0
    return (stat(filename.c_str(), &s) == 0 && s.st_mode & S_IFREG);
169
0
#endif // ifdef _WIN32
170
0
}
171
172
bool SystemInfo::wait_for_file_closure(
173
        const std::string& filename,
174
        const std::chrono::seconds timeout)
175
0
{
176
0
    auto start = std::chrono::steady_clock::now();
177
178
#ifdef _MSC_VER
179
    std::ofstream os;
180
    do
181
    {
182
        // MSVC specific
183
        os.open(filename, std::ios::out | std::ios::app, _SH_DENYWR);
184
        if (!os.is_open()
185
                // If the file is lock-opened in an external editor do not hang
186
                && (std::chrono::steady_clock::now() - start) < timeout )
187
        {
188
            std::this_thread::yield();
189
        }
190
        else
191
        {
192
            break;
193
        }
194
    }
195
    while (true);
196
#elif __unix__
197
    int fd = open(filename.c_str(), O_WRONLY);
198
199
0
    while (flock(fd, LOCK_EX | LOCK_NB)
200
            // If the file is lock-opened in an external editor do not hang
201
0
            && (std::chrono::steady_clock::now() - start) < timeout )
202
0
    {
203
0
        std::this_thread::yield();
204
0
    }
205
206
0
    flock(fd, LOCK_UN | LOCK_NB);
207
0
    close(fd);
208
#else
209
    // plain wait
210
    std::this_thread::sleep_for(timeout);
211
    // avoid unused warning
212
    (void)start;
213
    (void)filename;
214
#endif // ifdef _MSC_VER
215
216
0
    return std::chrono::steady_clock::now() - start < timeout;
217
0
}
218
219
fastdds::dds::ReturnCode_t SystemInfo::set_environment_file()
220
0
{
221
0
    return SystemInfo::get_env(FASTDDS_ENVIRONMENT_FILE_ENV_VAR, SystemInfo::environment_file_);
222
0
}
223
224
const std::string& SystemInfo::get_environment_file()
225
0
{
226
0
    return SystemInfo::environment_file_;
227
0
}
228
229
FileWatchHandle SystemInfo::watch_file(
230
        std::string filename,
231
        std::function<void()> callback,
232
        const fastdds::rtps::ThreadSettings& watch_thread_config,
233
        const fastdds::rtps::ThreadSettings& callback_thread_config)
234
0
{
235
0
#if defined(_WIN32) || defined(__unix__)
236
0
    return FileWatchHandle (new filewatch::FileWatch<std::string>(filename,
237
0
                   [callback](const std::string& /*path*/, const filewatch::Event change_type)
238
0
                   {
239
0
                       switch (change_type)
240
0
                       {
241
0
                           case filewatch::Event::modified:
242
0
                               callback();
243
0
                               break;
244
0
                           default:
245
                               // No-op
246
0
                               break;
247
0
                       }
248
0
                   }, watch_thread_config, callback_thread_config));
249
#else // defined(_WIN32) || defined(__unix__)
250
    static_cast<void>(filename);
251
    static_cast<void>(callback);
252
    static_cast<void>(watch_thread_config);
253
    static_cast<void>(callback_thread_config);
254
    return FileWatchHandle();
255
#endif // defined(_WIN32) || defined(__unix__)
256
0
}
257
258
void SystemInfo::stop_watching_file(
259
        FileWatchHandle& handle)
260
0
{
261
0
#if defined(_WIN32) || defined(__unix__)
262
0
    handle.reset();
263
0
#endif // if defined(_WIN32) || defined(__unix__)
264
0
    static_cast<void>(handle);
265
0
}
266
267
std::string SystemInfo::get_timestamp(
268
        const char* format)
269
0
{
270
0
    std::stringstream stream;
271
0
    auto now = std::chrono::system_clock::now();
272
0
    std::time_t now_c = std::chrono::system_clock::to_time_t(now);
273
0
    std::chrono::system_clock::duration tp = now.time_since_epoch();
274
0
    tp -= std::chrono::duration_cast<std::chrono::seconds>(tp);
275
0
    auto ms = static_cast<unsigned>(tp / std::chrono::milliseconds(1));
276
277
#if defined(_WIN32)
278
    struct tm timeinfo;
279
    localtime_s(&timeinfo, &now_c);
280
    //#elif defined(__clang__) && !defined(std::put_time) // TODO arm64 doesn't seem to support std::put_time
281
    //    (void)now_c;
282
    //    (void)ms;
283
#elif (_POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || \
284
    defined(_POSIX_SOURCE) || defined(__unix__)
285
    std::tm timeinfo;
286
0
    localtime_r(&now_c, &timeinfo);
287
#else
288
    std::tm timeinfo = *localtime(&now_c);
289
#endif // if defined(_WIN32)
290
0
    stream << std::put_time(&timeinfo, format) << "." << std::setw(3) << std::setfill('0') << ms;
291
0
    return stream.str();
292
0
}
293
294
bool SystemInfo::update_interfaces()
295
0
{
296
0
    std::vector<IPFinder::info_IP> ifaces;
297
0
    auto ret = IPFinder::getIPs(&ifaces, true);
298
0
    if (ret)
299
0
    {
300
0
        std::lock_guard<std::mutex> lock(interfaces_mtx_);
301
        // Copy fetched interfaces to attribute
302
0
        interfaces_ = ifaces;
303
        // Set to true when successful, but not to false if lookup failed (may have been successfully cached before)
304
0
        cached_interfaces_ = true;
305
0
    }
306
0
    return ret;
307
0
}
308
309
bool SystemInfo::get_ips(
310
        std::vector<IPFinder::info_IP>& vec_name,
311
        bool return_loopback,
312
        bool force_lookup)
313
0
{
314
0
    if (force_lookup)
315
0
    {
316
0
        return IPFinder::getIPs(&vec_name, return_loopback);
317
0
    }
318
0
    else
319
0
    {
320
0
        {
321
0
            std::lock_guard<std::mutex> lock(interfaces_mtx_);
322
0
            if (cached_interfaces_)
323
0
            {
324
0
                for (const auto& iface : interfaces_)
325
0
                {
326
0
                    if (return_loopback || (iface.type != IPFinder::IPTYPE::IP4_LOCAL &&
327
0
                            iface.type != IPFinder::IPTYPE::IP6_LOCAL))
328
0
                    {
329
0
                        vec_name.push_back(iface);
330
0
                    }
331
0
                }
332
0
                return true;
333
0
            }
334
0
        }
335
        // Interfaces not cached, perform lookup
336
0
        return IPFinder::getIPs(&vec_name, return_loopback);
337
0
    }
338
0
}
339
340
std::string SystemInfo::environment_file_;
341
bool SystemInfo::cached_interfaces_;
342
std::vector<IPFinder::info_IP> SystemInfo::interfaces_;
343
std::mutex SystemInfo::interfaces_mtx_;
344
345
} // eprosima
346
347
// threading.hpp implementations
348
#ifdef _WIN32
349
#include "threading/threading_win32.ipp"
350
#include "thread_impl/thread_impl_win32.ipp"
351
#elif defined(__APPLE__)
352
#include "threading/threading_osx.ipp"
353
#include "thread_impl/thread_impl_pthread.ipp"
354
#elif defined(_POSIX_SOURCE) || defined(__QNXNTO__) || defined(__ANDROID__)
355
#include "threading/threading_pthread.ipp"
356
#include "thread_impl/thread_impl_pthread.ipp"
357
#else
358
#include "threading/threading_empty.ipp"
359
#endif // Platform selection