Update pingcheck to work with cmake 3.28
[pingcheck] / src / link / linkstatus.cpp
CommitLineData
91fcc471
TJ
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on this file might be covered by the GNU General Public License.
19*/
72e54d1c 20#include "link/linkstatus.h"
ddf41c89
GMF
21
22#include <iostream>
23
301610ca
GMF
24#include <logfunc.hpp>
25
e638894d
CH
26#include "dns/dnsmaster.h"
27
780b0bca
CH
28#include "boost_assert_handler.h"
29
ddf41c89 30using namespace std;
2bf8720f
GMF
31using boost::posix_time::microsec_clock;
32using boost::posix_time::ptime;
301610ca 33using I2n::Logger::GlobalLogger;
ddf41c89
GMF
34
35//-----------------------------------------------------------------------------
72e54d1c 36// LinkStatus
ddf41c89
GMF
37//-----------------------------------------------------------------------------
38
5a3f6189
GMF
39/**
40 * @brief Creates a link status object.
41 *
72e54d1c 42 * @param hosts_down_limit The maximum amount of different hosts that can be
5a3f6189 43 * down before the system take any action.
97837af8 44 * @param link_up_interval_in_sec The amount of time required to the link to
5a3f6189 45 * stay up before notify.
97837af8 46 * @param link_down_interval_in_sec The amount of time required to the link to
5a3f6189 47 * stay down before notify.
72e54d1c 48 * @param status_notifier_cmd The command used to notify about link status
5a3f6189
GMF
49 * changes.
50 */
72e54d1c 51LinkStatus::LinkStatus(
a341119a 52 const int hosts_down_limit,
97837af8
CH
53 const int link_up_interval_in_sec,
54 const int link_down_interval_in_sec,
b279ae09 55 const string &status_notifier_cmd
c1fff16a 56) :
a341119a 57 HostsDownLimit( hosts_down_limit ),
f1bf3249 58 HostsDownList(),
97837af8
CH
59 LinkUpIntervalInSec( link_up_interval_in_sec ),
60 LinkDownIntervalInSec( link_down_interval_in_sec ),
72e54d1c 61 CurrentLinkStatus( Status_Down ),
f1bf3249
GMF
62 CurrentNotificationStatus( NotificationStatus_NotReported ),
63 TimeLinkStatusChanged( microsec_clock::universal_time() ),
365036be 64 StatusNotifierCmd( new StatusNotifierCommand( status_notifier_cmd ) )
ddf41c89 65{
a341119a 66 BOOST_ASSERT( 0 <= hosts_down_limit );
97837af8
CH
67 BOOST_ASSERT( 0 <= link_up_interval_in_sec );
68 BOOST_ASSERT( 0 <= link_down_interval_in_sec );
ddf41c89
GMF
69}
70
72e54d1c 71LinkStatus::~LinkStatus()
ddf41c89
GMF
72{
73}
74
353fb7f0
CH
75std::string LinkStatus::log_prefix() const
76{
77 stringstream temp;
78 temp << "Status (" << HostsDownList.size() << " down, "
79 << "limit=" << HostsDownLimit << ", ";
80 if ( can_report_link_status() )
81 {
82 ptime now = microsec_clock::universal_time();
83 long seconds_missing;
84 if ( CurrentLinkStatus == Status_Down )
85 {
86 seconds_missing = LinkDownIntervalInSec
87 - (now - TimeLinkStatusChanged).total_seconds();
88 temp << "notify down ";
89 }
90 else // (assume CurrentLinkStatus == Status_Up)
91 {
92 seconds_missing = LinkUpIntervalInSec
93 - (now - TimeLinkStatusChanged).total_seconds();
94 temp << "notify up ";
95 }
96 if (seconds_missing < 0)
97 temp << "now";
98 else
99 temp << "in " << seconds_missing << "s";
100 }
101 else
102 temp << "no notify";
103 temp << "): ";
104 return temp.str();
105}
106
5a3f6189
GMF
107/**
108 * @brief Notify the system that a given host is up. The object takes an
109 * appropriated action to deal with that.
110 * Note: this object does not resolves IPs, thus you have to send the same host
111 * address in order to the object to consider the same host.
112 *
113 * @param host_address the DNS/IP address of the host that is up.
114 */
72e54d1c 115void LinkStatus::notify_host_up( const string &host_address )
ddf41c89 116{
c1fff16a
GMF
117 BOOST_ASSERT( !host_address.empty() );
118
f5ffc5df 119 bool has_changed = add_host_up( host_address );
c1fff16a 120
a341119a 121 if ( !exceeded_host_down_limit() )
1266407a 122 {
fb469ffa 123 notify_link_up();
1266407a
GMF
124 }
125
353fb7f0
CH
126 if (has_changed)
127 GlobalLogger.notice() << log_prefix() << "now up again is "
128 << DnsMaster::get_cname_chain_str(host_address) << endl;
129 else // less important so log only at info level
130 GlobalLogger.info() << log_prefix() << "still up is "
131 << DnsMaster::get_cname_chain_str(host_address) << endl;
132
b279ae09 133 // removed from the list?
1266407a 134 BOOST_ASSERT( HostsDownList.count( host_address ) == 0 );
529e5587 135} //lint !e1788
ddf41c89 136
5a3f6189
GMF
137/**
138 * @brief Notify the system that a given host is down. The object takes an
139 * appropriated action to deal with that.
140 * Note: this object does not resolves IPs, thus you have to send the same host
141 * address in order to the object to consider the same host.
142 *
72e54d1c 143 * @param host_address The DNS/IP address of the host that is down.
5a3f6189 144 */
72e54d1c 145void LinkStatus::notify_host_down( const string &host_address )
ddf41c89 146{
c1fff16a
GMF
147 BOOST_ASSERT( !host_address.empty() );
148
1266407a 149 add_host_down( host_address );
c1fff16a 150
a341119a 151 if ( exceeded_host_down_limit() )
1266407a 152 {
fb469ffa 153 notify_link_down();
1266407a 154 }
c1fff16a 155
353fb7f0
CH
156 // report this always at notice level
157 GlobalLogger.notice() << log_prefix() << "down is "
158 << DnsMaster::get_cname_chain_str(host_address) << endl;
159
b279ae09 160 // inserted in the list?
1266407a 161 BOOST_ASSERT( HostsDownList.count( host_address ) == 1 );
529e5587 162} //lint !e1788
c1fff16a 163
f5ffc5df
CH
164// returns true if this did change something (i.e. host had been down)
165bool LinkStatus::add_host_up( const string &host_address )
1266407a
GMF
166{
167 if ( HostsDownList.count( host_address ) > 0 )
168 {
a4049623
GMF
169 size_t erased_host_count = HostsDownList.erase( host_address );
170
171 BOOST_ASSERT( erased_host_count == 1 );
f5ffc5df
CH
172
173 return true;
1266407a 174 }
f5ffc5df
CH
175 else
176 return false;
177
1266407a 178}
c1fff16a 179
72e54d1c 180void LinkStatus::add_host_down( const string &host_address )
1266407a 181{
a4049623 182 (void) HostsDownList.insert( host_address );
1266407a 183}
c1fff16a 184
72e54d1c 185bool LinkStatus::exceeded_host_down_limit() const
1266407a 186{
cf289c98 187 int host_down_count = static_cast<int>( HostsDownList.size() );
6cfbd37c 188
a341119a 189 return ( host_down_count > HostsDownLimit );
ddf41c89 190}
b279ae09 191
72e54d1c 192void LinkStatus::notify_link_up()
b279ae09 193{
72e54d1c 194 set_link_status( Status_Up );
b279ae09 195
1634f2a1
GMF
196 // report the link status only if: it is up longer than a configured amount
197 // of time, and if we haven't reported the new status yet
f1bf3249
GMF
198 if ( is_link_up_enough_time() && can_report_link_status() )
199 {
72e54d1c 200 BOOST_ASSERT( CurrentLinkStatus == Status_Up );
f1bf3249
GMF
201 BOOST_ASSERT( CurrentNotificationStatus == NotificationStatus_NotReported );
202
709ded82 203 StatusNotifierCmd->set_token_value(
f1bf3249
GMF
204 StatusNotifierCommand::StatusToken,
205 "up"
2a686bc0 206 ); //lint !e534
f1bf3249 207
353fb7f0 208 GlobalLogger.notice() << log_prefix() << "report link up" << endl;
709ded82 209 bool executed = StatusNotifierCmd->execute();
f1bf3249 210
2a686bc0 211 if ( executed )
a4049623
GMF
212 {
213 CurrentNotificationStatus = NotificationStatus_Reported;
353fb7f0 214 LastReportedStatus = CurrentLinkStatus;
a4049623 215 }
f1bf3249 216 }
b279ae09
GMF
217}
218
72e54d1c 219void LinkStatus::notify_link_down()
b279ae09 220{
72e54d1c 221 set_link_status( Status_Down );
f1bf3249 222
1634f2a1
GMF
223 // report the link status only if: it is down longer than a configured amount
224 // of time, and if we haven't reported the new status yet
225 if ( is_link_down_enough_time() && can_report_link_status() )
f1bf3249 226 {
72e54d1c 227 BOOST_ASSERT( CurrentLinkStatus == Status_Down );
f1bf3249 228 BOOST_ASSERT( CurrentNotificationStatus == NotificationStatus_NotReported );
b279ae09 229
353fb7f0 230 GlobalLogger.notice() << log_prefix() << "report link down" << endl;
709ded82 231 StatusNotifierCmd->set_token_value(
f1bf3249
GMF
232 StatusNotifierCommand::StatusToken,
233 "down"
2a686bc0 234 ); //lint !e534
f1bf3249 235
709ded82 236 bool executed = StatusNotifierCmd->execute();
f1bf3249 237
2a686bc0 238 if ( executed )
a4049623
GMF
239 {
240 CurrentNotificationStatus = NotificationStatus_Reported;
353fb7f0 241 LastReportedStatus = CurrentLinkStatus;
a4049623 242 }
f1bf3249
GMF
243 }
244}
245
72e54d1c 246bool LinkStatus::is_link_up_enough_time() const
f1bf3249 247{
72e54d1c 248 if ( CurrentLinkStatus == Status_Up )
f1bf3249
GMF
249 {
250 ptime now = microsec_clock::universal_time();
251 long amount_time_link_is_up = (now - TimeLinkStatusChanged).total_seconds();
f1bf3249 252
97837af8 253 if ( amount_time_link_is_up >= LinkUpIntervalInSec )
f1bf3249
GMF
254 {
255 return true;
256 }
257 }
258
259 return false;
260}
261
72e54d1c 262bool LinkStatus::is_link_down_enough_time() const
1634f2a1 263{
72e54d1c 264 if ( CurrentLinkStatus == Status_Down )
1634f2a1
GMF
265 {
266 ptime now = microsec_clock::universal_time();
267 long amount_time_link_is_down = (now - TimeLinkStatusChanged).total_seconds();
1634f2a1 268
97837af8 269 if ( amount_time_link_is_down >= LinkDownIntervalInSec )
1634f2a1
GMF
270 {
271 return true;
272 }
273 }
274
275 return false;
276}
277
353fb7f0
CH
278/**
279 * @brief determine if we should report the current link status
280 *
281 * checks if the current status has already been reported; does not take
282 * LinkUp/DownInterval into account
283 *
284 * @returns true if status should be reported
285 */
72e54d1c 286bool LinkStatus::can_report_link_status() const
f1bf3249 287{
353fb7f0
CH
288 return ( CurrentNotificationStatus == NotificationStatus_NotReported
289 && LastReportedStatus != CurrentLinkStatus );
f1bf3249
GMF
290}
291
72e54d1c
GMF
292void LinkStatus::set_link_status(
293 const LinkStatus::Status new_link_status
f1bf3249
GMF
294)
295{
296 // only reset the control flags if the link status has changed
297 if ( new_link_status != CurrentLinkStatus )
298 {
299 CurrentLinkStatus = new_link_status;
300 TimeLinkStatusChanged = microsec_clock::universal_time();
301
302 // have to report the link status change
303 CurrentNotificationStatus = NotificationStatus_NotReported;
304 }
b279ae09 305}