librats 0.7.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/native-src/CMakeLists.txt +5 -0
- package/native-src/src/bt_client.cpp +18 -5
- package/native-src/src/bt_client.h +3 -1
- package/native-src/src/bt_network.cpp +1 -1
- package/native-src/src/bt_peer_connection.cpp +11 -0
- package/native-src/src/bt_peer_connection.h +6 -0
- package/native-src/src/bt_torrent.cpp +17 -6
- package/native-src/src/bt_torrent.h +1 -0
- package/native-src/src/bt_types.h +287 -20
- package/native-src/src/dht.cpp +26 -8
- package/native-src/src/dht.h +1 -1
- package/native-src/src/ice.cpp +1 -1
- package/native-src/src/krpc.cpp +8 -1
- package/native-src/src/krpc.h +3 -2
- package/native-src/src/librats.cpp +49 -11
- package/native-src/src/librats.h +21 -0
- package/native-src/src/librats_bittorrent.cpp +3 -2
- package/native-src/src/librats_c.cpp +12 -0
- package/native-src/src/librats_c.h +2 -0
- package/native-src/src/librats_logging.cpp +9 -0
- package/native-src/src/logger.h +40 -4
- package/native-src/src/network_utils.cpp +175 -373
- package/native-src/src/network_utils.h +2 -113
- package/native-src/src/socket.cpp +485 -883
- package/native-src/src/socket.h +36 -130
- package/native-src/src/stun.cpp +3 -5
- package/native-src/src/tracker.cpp +3 -2
- package/native-src/src/turn.cpp +4 -6
- package/package.json +1 -1
package/native-src/src/dht.h
CHANGED
|
@@ -541,7 +541,7 @@ private:
|
|
|
541
541
|
void send_krpc_ping(const Peer& peer);
|
|
542
542
|
void send_krpc_find_node(const Peer& peer, const NodeId& target);
|
|
543
543
|
void send_krpc_get_peers(const Peer& peer, const InfoHash& info_hash);
|
|
544
|
-
void send_krpc_announce_peer(const Peer& peer, const InfoHash& info_hash, uint16_t port, const std::string& token);
|
|
544
|
+
void send_krpc_announce_peer(const Peer& peer, const InfoHash& info_hash, uint16_t port, const std::string& token, bool implied_port = false);
|
|
545
545
|
|
|
546
546
|
void add_node(const DhtNode& node, bool confirmed = true, bool no_verify = false);
|
|
547
547
|
std::vector<DhtNode> find_closest_nodes(const NodeId& target, size_t count = K_BUCKET_SIZE);
|
package/native-src/src/ice.cpp
CHANGED
|
@@ -281,7 +281,7 @@ bool IceManager::ensure_socket() {
|
|
|
281
281
|
return false;
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
-
local_port_ = static_cast<uint16_t>(
|
|
284
|
+
local_port_ = static_cast<uint16_t>(get_bound_port(socket_));
|
|
285
285
|
LOG_ICE_DEBUG("Created ICE socket on port " << local_port_);
|
|
286
286
|
return true;
|
|
287
287
|
}
|
package/native-src/src/krpc.cpp
CHANGED
|
@@ -60,7 +60,7 @@ KrpcMessage KrpcProtocol::create_get_peers_query(const std::string& transaction_
|
|
|
60
60
|
return message;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
KrpcMessage KrpcProtocol::create_announce_peer_query(const std::string& transaction_id, const NodeId& sender_id, const InfoHash& info_hash, uint16_t port, const std::string& token) {
|
|
63
|
+
KrpcMessage KrpcProtocol::create_announce_peer_query(const std::string& transaction_id, const NodeId& sender_id, const InfoHash& info_hash, uint16_t port, const std::string& token, bool implied_port) {
|
|
64
64
|
KrpcMessage message;
|
|
65
65
|
message.type = KrpcMessageType::Query;
|
|
66
66
|
message.transaction_id = transaction_id;
|
|
@@ -68,6 +68,7 @@ KrpcMessage KrpcProtocol::create_announce_peer_query(const std::string& transact
|
|
|
68
68
|
message.sender_id = sender_id;
|
|
69
69
|
message.info_hash = info_hash;
|
|
70
70
|
message.port = port;
|
|
71
|
+
message.implied_port = implied_port;
|
|
71
72
|
message.token = token;
|
|
72
73
|
return message;
|
|
73
74
|
}
|
|
@@ -176,6 +177,9 @@ BencodeValue KrpcProtocol::encode_query(const KrpcMessage& message) {
|
|
|
176
177
|
args["info_hash"] = BencodeValue(node_id_to_string(message.info_hash));
|
|
177
178
|
args["port"] = BencodeValue(static_cast<int64_t>(message.port));
|
|
178
179
|
args["token"] = BencodeValue(message.token);
|
|
180
|
+
if (message.implied_port) {
|
|
181
|
+
args["implied_port"] = BencodeValue(static_cast<int64_t>(1));
|
|
182
|
+
}
|
|
179
183
|
break;
|
|
180
184
|
}
|
|
181
185
|
|
|
@@ -316,6 +320,9 @@ std::unique_ptr<KrpcMessage> KrpcProtocol::decode_query(const BencodeValue& data
|
|
|
316
320
|
if (args.has_key("token")) {
|
|
317
321
|
message->token = args["token"].as_string();
|
|
318
322
|
}
|
|
323
|
+
if (args.has_key("implied_port")) {
|
|
324
|
+
message->implied_port = (args["implied_port"].as_integer() != 0);
|
|
325
|
+
}
|
|
319
326
|
break;
|
|
320
327
|
}
|
|
321
328
|
|
package/native-src/src/krpc.h
CHANGED
|
@@ -68,6 +68,7 @@ struct KrpcMessage {
|
|
|
68
68
|
NodeId target_id;
|
|
69
69
|
InfoHash info_hash;
|
|
70
70
|
uint16_t port;
|
|
71
|
+
bool implied_port; // BEP 5: if true, use UDP source port instead of 'port' field
|
|
71
72
|
std::string token;
|
|
72
73
|
|
|
73
74
|
// For responses
|
|
@@ -79,7 +80,7 @@ struct KrpcMessage {
|
|
|
79
80
|
KrpcErrorCode error_code;
|
|
80
81
|
std::string error_message;
|
|
81
82
|
|
|
82
|
-
KrpcMessage() : type(KrpcMessageType::Query), query_type(KrpcQueryType::Ping), sender_id(), target_id(), info_hash(), port(0), response_id(), error_code(KrpcErrorCode::GenericError) {}
|
|
83
|
+
KrpcMessage() : type(KrpcMessageType::Query), query_type(KrpcQueryType::Ping), sender_id(), target_id(), info_hash(), port(0), implied_port(false), response_id(), error_code(KrpcErrorCode::GenericError) {}
|
|
83
84
|
};
|
|
84
85
|
|
|
85
86
|
/**
|
|
@@ -96,7 +97,7 @@ public:
|
|
|
96
97
|
static KrpcMessage create_ping_query(const std::string& transaction_id, const NodeId& sender_id);
|
|
97
98
|
static KrpcMessage create_find_node_query(const std::string& transaction_id, const NodeId& sender_id, const NodeId& target_id);
|
|
98
99
|
static KrpcMessage create_get_peers_query(const std::string& transaction_id, const NodeId& sender_id, const InfoHash& info_hash);
|
|
99
|
-
static KrpcMessage create_announce_peer_query(const std::string& transaction_id, const NodeId& sender_id, const InfoHash& info_hash, uint16_t port, const std::string& token);
|
|
100
|
+
static KrpcMessage create_announce_peer_query(const std::string& transaction_id, const NodeId& sender_id, const InfoHash& info_hash, uint16_t port, const std::string& token, bool implied_port = false);
|
|
100
101
|
|
|
101
102
|
static KrpcMessage create_ping_response(const std::string& transaction_id, const NodeId& response_id);
|
|
102
103
|
static KrpcMessage create_find_node_response(const std::string& transaction_id, const NodeId& response_id, const std::vector<KrpcNode>& nodes);
|
|
@@ -146,16 +146,27 @@ bool RatsClient::start() {
|
|
|
146
146
|
initialize_local_addresses();
|
|
147
147
|
|
|
148
148
|
// Create dual-stack server socket (supports both IPv4 and IPv6)
|
|
149
|
-
|
|
149
|
+
server_socket_ = create_tcp_server(listen_port_, 5, bind_address_);
|
|
150
|
+
// Fallback to free port
|
|
151
|
+
if (!is_valid_socket(server_socket_) && listen_port_ > 0) {
|
|
152
|
+
// Requested port is not available, try ephemeral port as fallback
|
|
153
|
+
int original_port = listen_port_;
|
|
154
|
+
LOG_CLIENT_WARN("TCP port " << original_port << " is not available, falling back to ephemeral port");
|
|
155
|
+
server_socket_ = create_tcp_server(0, 5, bind_address_);
|
|
156
|
+
if (is_valid_socket(server_socket_)) {
|
|
157
|
+
listen_port_ = get_bound_port(server_socket_);
|
|
158
|
+
LOG_CLIENT_INFO("Fell back from port " << original_port << " to ephemeral port " << listen_port_);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
150
161
|
if (!is_valid_socket(server_socket_)) {
|
|
151
|
-
LOG_CLIENT_ERROR("Failed to create
|
|
162
|
+
LOG_CLIENT_ERROR("Failed to create server socket on port " << listen_port_ <<
|
|
152
163
|
(bind_address_.empty() ? "" : " bound to " + bind_address_));
|
|
153
164
|
return false;
|
|
154
165
|
}
|
|
155
166
|
|
|
156
167
|
// Update listen_port_ with actual bound port if ephemeral port was requested
|
|
157
168
|
if (listen_port_ == 0) {
|
|
158
|
-
listen_port_ =
|
|
169
|
+
listen_port_ = get_bound_port(server_socket_);
|
|
159
170
|
if (listen_port_ == 0) {
|
|
160
171
|
LOG_CLIENT_WARN("Failed to get actual bound port - using port 0");
|
|
161
172
|
} else {
|
|
@@ -448,7 +459,7 @@ void RatsClient::handle_client(socket_t client_socket, const std::string& peer_h
|
|
|
448
459
|
|
|
449
460
|
// ----- 1. RECEIVE DATA -----
|
|
450
461
|
LOG_CLIENT_DEBUG("Receiving data from socket " << client_socket);
|
|
451
|
-
std::vector<uint8_t> received_bytes =
|
|
462
|
+
std::vector<uint8_t> received_bytes = receive_tcp_message(client_socket);
|
|
452
463
|
|
|
453
464
|
if (received_bytes.empty()) {
|
|
454
465
|
break; // Connection closed or error
|
|
@@ -897,7 +908,7 @@ bool RatsClient::send_handshake_unlocked(socket_t socket, const std::string& our
|
|
|
897
908
|
auto socket_mutex = get_socket_send_mutex(socket);
|
|
898
909
|
std::lock_guard<std::mutex> send_lock(*socket_mutex);
|
|
899
910
|
|
|
900
|
-
int sent =
|
|
911
|
+
int sent = send_tcp_message(socket, message_with_header);
|
|
901
912
|
if (sent <= 0) {
|
|
902
913
|
LOG_CLIENT_ERROR("Failed to send handshake to socket " << socket);
|
|
903
914
|
return false;
|
|
@@ -1412,12 +1423,12 @@ bool RatsClient::send_binary_to_peer_unlocked(socket_t socket, const std::vector
|
|
|
1412
1423
|
LOG_CLIENT_DEBUG("Sending encrypted message to " << peer_id_for_logging << " (" << ct_len << " bytes)");
|
|
1413
1424
|
|
|
1414
1425
|
// Send encrypted message using framed protocol
|
|
1415
|
-
int sent =
|
|
1426
|
+
int sent = send_tcp_message(socket, ciphertext);
|
|
1416
1427
|
return sent > 0;
|
|
1417
1428
|
}
|
|
1418
1429
|
|
|
1419
1430
|
// Unencrypted path - use framed messages for reliable large message handling
|
|
1420
|
-
int sent =
|
|
1431
|
+
int sent = send_tcp_message(socket, message_with_header);
|
|
1421
1432
|
return sent > 0;
|
|
1422
1433
|
}
|
|
1423
1434
|
|
|
@@ -1972,6 +1983,33 @@ bool RatsClient::is_automatic_discovery_running() const {
|
|
|
1972
1983
|
return auto_discovery_running_.load();
|
|
1973
1984
|
}
|
|
1974
1985
|
|
|
1986
|
+
std::chrono::seconds RatsClient::calculate_discovery_interval() const {
|
|
1987
|
+
int peer_count = get_peer_count();
|
|
1988
|
+
|
|
1989
|
+
// No peers - aggressive discovery
|
|
1990
|
+
if (peer_count == 0) {
|
|
1991
|
+
return std::chrono::seconds(15);
|
|
1992
|
+
}
|
|
1993
|
+
|
|
1994
|
+
// Calculate fill ratio
|
|
1995
|
+
float fill_ratio = static_cast<float>(peer_count) / static_cast<float>(max_peers_);
|
|
1996
|
+
|
|
1997
|
+
// Graduated intervals based on fill ratio
|
|
1998
|
+
if (fill_ratio < 0.25f) {
|
|
1999
|
+
// Less than 25% full - still fairly aggressive
|
|
2000
|
+
return std::chrono::seconds(60); // 1 minute
|
|
2001
|
+
} else if (fill_ratio < 0.50f) {
|
|
2002
|
+
// 25-50% full - moderate
|
|
2003
|
+
return std::chrono::seconds(180); // 3 minutes
|
|
2004
|
+
} else if (fill_ratio < 0.75f) {
|
|
2005
|
+
// 50-75% full - relaxed
|
|
2006
|
+
return std::chrono::seconds(600); // 10 minutes
|
|
2007
|
+
} else {
|
|
2008
|
+
// 75-100% full - very relaxed (mostly just re-announcing)
|
|
2009
|
+
return std::chrono::seconds(1800); // 30 minutes
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
|
|
1975
2013
|
void RatsClient::automatic_discovery_loop() {
|
|
1976
2014
|
LOG_CLIENT_INFO("Automatic peer discovery loop started");
|
|
1977
2015
|
|
|
@@ -1993,12 +2031,12 @@ void RatsClient::automatic_discovery_loop() {
|
|
|
1993
2031
|
auto now = std::chrono::steady_clock::now();
|
|
1994
2032
|
|
|
1995
2033
|
// Announce combines both announcing our presence and discovering peers
|
|
1996
|
-
//
|
|
1997
|
-
auto interval = (
|
|
1998
|
-
? std::chrono::seconds(15) // Aggressive when no peers
|
|
1999
|
-
: std::chrono::minutes(10); // Less aggressive when connected
|
|
2034
|
+
// Interval scales based on peer count: aggressive when empty, relaxed when nearly full
|
|
2035
|
+
auto interval = calculate_discovery_interval();
|
|
2000
2036
|
|
|
2001
2037
|
if (now - last_announce >= interval) {
|
|
2038
|
+
LOG_CLIENT_DEBUG("Discovery interval: " << interval.count() << "s (peers: "
|
|
2039
|
+
<< get_peer_count() << "/" << max_peers_ << ")");
|
|
2002
2040
|
announce_rats_peer();
|
|
2003
2041
|
last_announce = now;
|
|
2004
2042
|
}
|
package/native-src/src/librats.h
CHANGED
|
@@ -636,6 +636,13 @@ public:
|
|
|
636
636
|
*/
|
|
637
637
|
bool is_automatic_discovery_running() const;
|
|
638
638
|
|
|
639
|
+
/**
|
|
640
|
+
* Calculate discovery interval based on current peer count
|
|
641
|
+
* Uses graduated scaling: more aggressive when fewer peers, less aggressive when nearly full
|
|
642
|
+
* @return Discovery interval in seconds
|
|
643
|
+
*/
|
|
644
|
+
std::chrono::seconds calculate_discovery_interval() const;
|
|
645
|
+
|
|
639
646
|
/**
|
|
640
647
|
* Get the discovery hash for current protocol configuration
|
|
641
648
|
* @return Discovery hash based on current protocol name and version
|
|
@@ -1084,6 +1091,20 @@ public:
|
|
|
1084
1091
|
*/
|
|
1085
1092
|
void set_log_retention_count(int count);
|
|
1086
1093
|
|
|
1094
|
+
/**
|
|
1095
|
+
* Enable or disable log rotation on application startup
|
|
1096
|
+
* When enabled, the existing log file will be rotated when logging starts,
|
|
1097
|
+
* so each application run gets a fresh log file
|
|
1098
|
+
* @param enabled Whether to rotate logs on startup (default: false)
|
|
1099
|
+
*/
|
|
1100
|
+
void set_log_rotate_on_startup(bool enabled);
|
|
1101
|
+
|
|
1102
|
+
/**
|
|
1103
|
+
* Check if log rotation on startup is enabled
|
|
1104
|
+
* @return true if rotate on startup is enabled
|
|
1105
|
+
*/
|
|
1106
|
+
bool is_log_rotate_on_startup_enabled() const;
|
|
1107
|
+
|
|
1087
1108
|
/**
|
|
1088
1109
|
* Clear/reset the current log file
|
|
1089
1110
|
*/
|
|
@@ -248,12 +248,13 @@ void RatsClient::get_torrent_metadata_from_peer(const InfoHash& info_hash,
|
|
|
248
248
|
return;
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
-
LOG_CLIENT_INFO("Fetching metadata from peer " << peer_ip << ":" << peer_port << " (fast path)");
|
|
251
|
+
LOG_CLIENT_INFO("Fetching metadata from peer " << peer_ip << ":" << peer_port << " (fast path, no DHT)");
|
|
252
252
|
|
|
253
253
|
// Add magnet in metadata-only mode (empty save_path) and immediately add the peer
|
|
254
|
+
// skip_dht_search=true to avoid network-wide DHT queries - we only want this specific peer
|
|
254
255
|
std::string hash_hex = info_hash_to_hex(info_hash);
|
|
255
256
|
std::string magnet = "magnet:?xt=urn:btih:" + hash_hex;
|
|
256
|
-
auto torrent = bittorrent_client_->add_magnet(magnet, "");
|
|
257
|
+
auto torrent = bittorrent_client_->add_magnet(magnet, "", true /* skip_dht_search */);
|
|
257
258
|
|
|
258
259
|
if (!torrent) {
|
|
259
260
|
if (callback) {
|
|
@@ -237,6 +237,18 @@ void rats_set_log_retention_count(rats_client_t handle, int count) {
|
|
|
237
237
|
wrap->client->set_log_retention_count(count);
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
+
void rats_set_log_rotate_on_startup(rats_client_t handle, int enabled) {
|
|
241
|
+
if (!handle) return;
|
|
242
|
+
rats_client_wrapper* wrap = static_cast<rats_client_wrapper*>(handle);
|
|
243
|
+
wrap->client->set_log_rotate_on_startup(enabled != 0);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
int rats_is_log_rotate_on_startup_enabled(rats_client_t handle) {
|
|
247
|
+
if (!handle) return 0;
|
|
248
|
+
rats_client_wrapper* wrap = static_cast<rats_client_wrapper*>(handle);
|
|
249
|
+
return wrap->client->is_log_rotate_on_startup_enabled() ? 1 : 0;
|
|
250
|
+
}
|
|
251
|
+
|
|
240
252
|
void rats_clear_log_file(rats_client_t handle) {
|
|
241
253
|
if (!handle) return;
|
|
242
254
|
rats_client_wrapper* wrap = static_cast<rats_client_wrapper*>(handle);
|
|
@@ -53,6 +53,8 @@ RATS_API void rats_set_log_timestamps_enabled(rats_client_t client, int enabled)
|
|
|
53
53
|
RATS_API int rats_is_log_timestamps_enabled(rats_client_t client);
|
|
54
54
|
RATS_API void rats_set_log_rotation_size(rats_client_t client, size_t max_size_bytes);
|
|
55
55
|
RATS_API void rats_set_log_retention_count(rats_client_t client, int count);
|
|
56
|
+
RATS_API void rats_set_log_rotate_on_startup(rats_client_t client, int enabled);
|
|
57
|
+
RATS_API int rats_is_log_rotate_on_startup_enabled(rats_client_t client);
|
|
56
58
|
RATS_API void rats_clear_log_file(rats_client_t client);
|
|
57
59
|
|
|
58
60
|
// Error codes
|
|
@@ -136,6 +136,15 @@ void RatsClient::set_log_retention_count(int count) {
|
|
|
136
136
|
LOG_CLIENT_INFO("Log retention count set to: " << count << " files");
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
void RatsClient::set_log_rotate_on_startup(bool enabled) {
|
|
140
|
+
Logger::getInstance().set_rotate_on_startup(enabled);
|
|
141
|
+
LOG_CLIENT_INFO("Log rotation on startup " << (enabled ? "enabled" : "disabled"));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
bool RatsClient::is_log_rotate_on_startup_enabled() const {
|
|
145
|
+
return Logger::getInstance().is_rotate_on_startup_enabled();
|
|
146
|
+
}
|
|
147
|
+
|
|
139
148
|
void RatsClient::clear_log_file() {
|
|
140
149
|
Logger& logger = Logger::getInstance();
|
|
141
150
|
std::string log_path = logger.get_log_file_path();
|
package/native-src/src/logger.h
CHANGED
|
@@ -100,6 +100,17 @@ public:
|
|
|
100
100
|
max_log_files_ = count;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
// Rotate on startup configuration
|
|
104
|
+
void set_rotate_on_startup(bool enabled) {
|
|
105
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
106
|
+
rotate_on_startup_ = enabled;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
bool is_rotate_on_startup_enabled() const {
|
|
110
|
+
std::lock_guard<std::mutex> lock(mutex_);
|
|
111
|
+
return rotate_on_startup_;
|
|
112
|
+
}
|
|
113
|
+
|
|
103
114
|
// Get current file logging status
|
|
104
115
|
bool is_file_logging_enabled() const {
|
|
105
116
|
std::lock_guard<std::mutex> lock(mutex_);
|
|
@@ -178,7 +189,8 @@ public:
|
|
|
178
189
|
private:
|
|
179
190
|
Logger() : min_level_(LogLevel::INFO), colors_enabled_(true), timestamps_enabled_(true),
|
|
180
191
|
console_logging_enabled_(true), file_logging_enabled_(false),
|
|
181
|
-
max_log_file_size_(10 * 1024 * 1024), max_log_files_(5), current_file_size_(0)
|
|
192
|
+
max_log_file_size_(10 * 1024 * 1024), max_log_files_(5), current_file_size_(0),
|
|
193
|
+
rotate_on_startup_(false), startup_rotation_done_(false) {
|
|
182
194
|
// Check if we're outputting to a terminal
|
|
183
195
|
is_terminal_ = isatty(fileno(stdout));
|
|
184
196
|
|
|
@@ -330,6 +342,19 @@ private:
|
|
|
330
342
|
}
|
|
331
343
|
}
|
|
332
344
|
|
|
345
|
+
// Perform rotation on startup if enabled and not yet done this session
|
|
346
|
+
if (rotate_on_startup_ && !startup_rotation_done_) {
|
|
347
|
+
// Check if the log file exists and has content
|
|
348
|
+
if (file_exists(log_file_path_.c_str())) {
|
|
349
|
+
std::ifstream check_file(log_file_path_, std::ios::ate);
|
|
350
|
+
if (check_file.is_open() && check_file.tellg() > 0) {
|
|
351
|
+
check_file.close();
|
|
352
|
+
rotate_log_files_impl();
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
startup_rotation_done_ = true;
|
|
356
|
+
}
|
|
357
|
+
|
|
333
358
|
log_file_.open(log_file_path_, std::ios::app);
|
|
334
359
|
if (log_file_.is_open()) {
|
|
335
360
|
// Get current file size
|
|
@@ -350,6 +375,16 @@ private:
|
|
|
350
375
|
if (log_file_path_.empty() || max_log_files_ <= 0) return;
|
|
351
376
|
|
|
352
377
|
close_log_file();
|
|
378
|
+
rotate_log_files_impl();
|
|
379
|
+
|
|
380
|
+
// Reopen the log file (new empty file)
|
|
381
|
+
open_log_file();
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Internal rotation implementation - does not close/open files
|
|
385
|
+
// Called directly when we need to rotate before opening (e.g., on startup)
|
|
386
|
+
void rotate_log_files_impl() {
|
|
387
|
+
if (log_file_path_.empty() || max_log_files_ <= 0) return;
|
|
353
388
|
|
|
354
389
|
// Move existing log files
|
|
355
390
|
for (int i = max_log_files_ - 1; i >= 1; i--) {
|
|
@@ -368,9 +403,6 @@ private:
|
|
|
368
403
|
// Move current log file to .1
|
|
369
404
|
std::string backup_name = log_file_path_ + ".1";
|
|
370
405
|
std::rename(log_file_path_.c_str(), backup_name.c_str());
|
|
371
|
-
|
|
372
|
-
// Reopen the log file (new empty file)
|
|
373
|
-
open_log_file();
|
|
374
406
|
}
|
|
375
407
|
|
|
376
408
|
mutable std::mutex mutex_;
|
|
@@ -389,6 +421,10 @@ private:
|
|
|
389
421
|
size_t max_log_file_size_;
|
|
390
422
|
int max_log_files_;
|
|
391
423
|
size_t current_file_size_;
|
|
424
|
+
|
|
425
|
+
// Rotate on startup members
|
|
426
|
+
bool rotate_on_startup_;
|
|
427
|
+
bool startup_rotation_done_; // Tracks if we've already done startup rotation for this session
|
|
392
428
|
};
|
|
393
429
|
|
|
394
430
|
} // namespace librats
|