Coverage Report

Created: 2025-11-16 07:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/kea/src/lib/util/watch_socket.h
Line
Count
Source
1
// Copyright (C) 2014-2024 Internet Systems Consortium, Inc. ("ISC")
2
//
3
// This Source Code Form is subject to the terms of the Mozilla Public
4
// License, v. 2.0. If a copy of the MPL was not distributed with this
5
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7
#ifndef WATCH_SOCKET_H
8
#define WATCH_SOCKET_H
9
10
/// @file watch_socket.h Defines the class, WatchSocket.
11
12
#include <exceptions/exceptions.h>
13
#include <boost/noncopyable.hpp>
14
#include <boost/shared_ptr.hpp>
15
16
#include <stdint.h>
17
#include <string>
18
19
namespace isc {
20
namespace util {
21
22
/// @brief Exception thrown if an error occurs during IO source open.
23
class WatchSocketError : public isc::Exception {
24
public:
25
    WatchSocketError(const char* file, size_t line, const char* what) :
26
0
        isc::Exception(file, line, what) { }
27
};
28
29
/// @brief Provides an IO "ready" semaphore for use with select() or poll()
30
/// WatchSocket exposes a single open file descriptor, the "select-fd" which
31
/// can be marked as being ready to read (i.e. !EWOULDBLOCK) and cleared
32
/// (i.e. EWOULDBLOCK).  The select-fd can be used with select(), poll(), or
33
/// their variants alongside other file descriptors.
34
///
35
/// Internally, WatchSocket uses a pipe.  The select-fd is the "read" end of
36
/// pipe.  To mark the socket as ready to read, an integer marker is written
37
/// to the pipe.  To clear the socket, the marker is read from the pipe.  Note
38
/// that WatchSocket will only write the marker if it is not already marked.
39
/// This prevents the socket's pipe from filling endlessly.
40
///
41
/// @warning Because the read or "sink" side of the pipe is used as the select_fd,
42
/// it is possible for that fd to be interfered with, albeit only from within the
43
/// process space which owns it.  Performing operations that may alter the fd's state
44
/// such as close, read, or altering behavior flags with fcntl or ioctl can have
45
/// unpredictable results.  It is intended strictly use with functions such as select()
46
/// poll() or their variants.
47
class WatchSocket : public boost::noncopyable {
48
public:
49
    /// @brief Value used to signify an invalid descriptor.
50
    static const int SOCKET_NOT_VALID = -1;
51
    /// @brief Value written to the source when marking the socket as ready.
52
    /// The value itself is arbitrarily chosen as one that is unlikely to occur
53
    /// otherwise and easy to debug.
54
    static const uint32_t MARKER = 0xDEADBEEF;
55
56
    /// @brief Constructor
57
    ///
58
    /// Constructs an instance of the WatchSocket in the cleared (EWOULDBLOCK)
59
    /// state.
60
    WatchSocket();
61
62
    /// @brief Destructor
63
    ///
64
    /// Closes all internal resources, including the select-fd.
65
    virtual ~WatchSocket();
66
67
    /// @brief Marks the select-fd as ready to read.
68
    ///
69
    /// Marks the socket as ready to read, if is not already so marked.
70
    /// If an error occurs, closeSocket is called. This will force any further
71
    /// use of the select_fd to fail rather than show the fd as READY.  Such
72
    /// an error is almost surely a programmatic error which has corrupted the
73
    /// select_fd.
74
    ///
75
    /// @throw WatchSocketError if an error occurs marking the socket.
76
    void markReady();
77
78
    /// @brief Returns true the if socket is marked as ready.
79
    ///
80
    /// This method uses a non-blocking call to  select() to test read state of the
81
    /// select_fd.  Rather than track what the status "should be" it tests the status.
82
    /// This should eliminate conditions where the select-fd appear to be perpetually
83
    /// ready.
84
    /// @return  Returns true if select_fd is not SOCKET_NOT_VALID and select() reports it
85
    /// as !EWOULDBLOCK, otherwise it returns false.
86
    /// This method is guaranteed NOT to throw.
87
    bool isReady();
88
89
    /// @brief Clears the socket's ready to read marker.
90
    ///
91
    /// Clears the socket if it is currently marked as ready to read.
92
    /// If an error occurs, closeSocket is called. This will force any further
93
    /// use of the select_fd to fail rather than show the fd as READY.  Such
94
    /// an error is almost surely a programmatic error which has corrupted the
95
    /// select_fd.
96
    ///
97
    /// @throw WatchSocketError if an error occurs clearing the socket
98
    /// marker.
99
    void clearReady();
100
101
    /// @brief Returns the file descriptor to use to monitor the socket.
102
    ///
103
    /// @note Using this file descriptor as anything other than an argument
104
    /// to select() or similar methods can have unpredictable results.
105
    ///
106
    /// @return The file descriptor associated with read end of the socket's
107
    /// pipe.
108
    int getSelectFd();
109
110
    /// @brief Closes the descriptors associated with the socket.
111
    ///
112
    /// This method is used to close the socket and capture errors that
113
    /// may occur during this operation.
114
    ///
115
    /// @param [out] error_string Holds the error string if closing
116
    /// the socket failed. It will hold empty string otherwise.
117
    ///
118
    /// @return true if the operation was successful, false otherwise.
119
    bool closeSocket(std::string& error_string);
120
121
private:
122
123
    /// @brief Closes the descriptors associated with the socket.
124
    ///
125
    /// This method is called by the class destructor and it ignores
126
    /// any errors that may occur while closing the sockets.
127
    void closeSocket();
128
129
    /// @brief The end of the pipe to which the marker is written
130
    int source_;
131
132
    /// @brief The end of the pipe from which the marker is read.
133
    /// This is the value returned as the select-fd.
134
    int sink_;
135
};
136
137
/// @brief Defines a smart pointer to an instance of a WatchSocket.
138
typedef boost::shared_ptr<WatchSocket> WatchSocketPtr;
139
140
} // namespace isc::util
141
} // namespace isc
142
143
#endif