]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/mplexer.hh
dnsdist: Add HTTPStatusAction to return a specific HTTP response
[thirdparty/pdns.git] / pdns / mplexer.hh
CommitLineData
12471842
PL
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
8ecee27a
BH
22#ifndef PDNS_MPLEXER_HH
23#define PDNS_MPLEXER_HH
ab3e8a6c
BH
24#include <boost/function.hpp>
25#include <boost/any.hpp>
a1dfcec8 26#include <boost/shared_array.hpp>
0bff046b
BH
27#include <boost/tuple/tuple.hpp>
28#include <boost/tuple/tuple_comparison.hpp>
ac3da0c2
RG
29#include <boost/multi_index_container.hpp>
30#include <boost/multi_index/ordered_index.hpp>
31#include <boost/multi_index/hashed_index.hpp>
32#include <boost/multi_index/key_extractors.hpp>
0bff046b 33#include <vector>
ab3e8a6c
BH
34#include <map>
35#include <stdexcept>
36#include <string>
f1d51ff7 37#include <sys/time.h>
ab3e8a6c 38
ac3da0c2
RG
39using namespace ::boost::multi_index;
40
ab3e8a6c
BH
41class FDMultiplexerException : public std::runtime_error
42{
43public:
44 FDMultiplexerException(const std::string& str) : std::runtime_error(str)
45 {}
46};
47
a1dfcec8
BH
48
49/** Very simple FD multiplexer, based on callbacks and boost::any parameters
50 As a special service, this parameter is kept around and can be modified,
51 allowing for state to be stored inside the multiplexer.
52
53 It has some "interesting" semantics
54*/
1f4abb20 55
ab3e8a6c
BH
56class FDMultiplexer
57{
d8f6d49f 58public:
d8f6d49f 59 typedef boost::any funcparam_t;
d0ae6360 60 typedef boost::function< void(int, funcparam_t&) > callbackfunc_t;
ab3e8a6c 61protected:
d8f6d49f 62
ab3e8a6c
BH
63 struct Callback
64 {
65 callbackfunc_t d_callback;
ac3da0c2 66 mutable funcparam_t d_parameter;
0bff046b 67 struct timeval d_ttd;
ac3da0c2 68 int d_fd;
ab3e8a6c
BH
69 };
70
71public:
72 FDMultiplexer() : d_inrun(false)
73 {}
74 virtual ~FDMultiplexer()
75 {}
76
4226cfd0 77 static FDMultiplexer* getMultiplexerSilent();
78
0e663c3b
RG
79 /* tv will be updated to 'now' before run returns */
80 /* timeout is in ms */
81 virtual int run(struct timeval* tv, int timeout=500) = 0;
ab3e8a6c 82
5bdbb83d
RG
83 /* timeout is in ms, 0 will return immediatly, -1 will block until at least one FD is ready */
84 virtual void getAvailableFDs(std::vector<int>& fds, int timeout) = 0;
85
a1dfcec8 86 //! Add an fd to the read watch list - currently an fd can only be on one list at a time!
27ae2e3c 87 virtual void addReadFD(int fd, callbackfunc_t toDo, const funcparam_t& parameter=funcparam_t(), const struct timeval* ttd=nullptr)
ab3e8a6c 88 {
27ae2e3c 89 this->addFD(d_readCallbacks, fd, toDo, parameter, ttd);
ab3e8a6c
BH
90 }
91
a1dfcec8 92 //! Add an fd to the write watch list - currently an fd can only be on one list at a time!
702b1925 93 virtual void addWriteFD(int fd, callbackfunc_t toDo, const funcparam_t& parameter=funcparam_t(), const struct timeval* ttd=nullptr)
ab3e8a6c 94 {
702b1925 95 this->addFD(d_writeCallbacks, fd, toDo, parameter, ttd);
ab3e8a6c
BH
96 }
97
a1dfcec8 98 //! Remove an fd from the read watch list. You can't call this function on an fd that is closed already!
6dcd28c3 99 /** WARNING: references to 'parameter' become invalid after this function! */
ab3e8a6c
BH
100 virtual void removeReadFD(int fd)
101 {
a1dfcec8 102 this->removeFD(d_readCallbacks, fd);
ab3e8a6c
BH
103 }
104
a1dfcec8 105 //! Remove an fd from the write watch list. You can't call this function on an fd that is closed already!
6dcd28c3 106 /** WARNING: references to 'parameter' become invalid after this function! */
a1dfcec8 107 virtual void removeWriteFD(int fd)
ab3e8a6c 108 {
a1dfcec8 109 this->removeFD(d_writeCallbacks, fd);
ab3e8a6c
BH
110 }
111
0bff046b
BH
112 virtual void setReadTTD(int fd, struct timeval tv, int timeout)
113 {
27ae2e3c
RG
114 const auto& it = d_readCallbacks.find(fd);
115 if (it == d_readCallbacks.end()) {
0bff046b 116 throw FDMultiplexerException("attempt to timestamp fd not in the multiplexer");
27ae2e3c
RG
117 }
118
ac3da0c2 119 auto newEntry = *it;
0bff046b 120 tv.tv_sec += timeout;
ac3da0c2
RG
121 newEntry.d_ttd = tv;
122 d_readCallbacks.replace(it, newEntry);
0bff046b
BH
123 }
124
702b1925
RG
125 virtual void setWriteTTD(int fd, struct timeval tv, int timeout)
126 {
127 const auto& it = d_writeCallbacks.find(fd);
128 if (it == d_writeCallbacks.end()) {
129 throw FDMultiplexerException("attempt to timestamp fd not in the multiplexer");
130 }
131
ac3da0c2 132 auto newEntry = *it;
702b1925 133 tv.tv_sec += timeout;
ac3da0c2
RG
134 newEntry.d_ttd = tv;
135 d_writeCallbacks.replace(it, newEntry);
a6ae6414
BH
136 }
137
702b1925 138 virtual std::vector<std::pair<int, funcparam_t> > getTimeouts(const struct timeval& tv, bool writes=false)
0bff046b 139 {
d8f6d49f 140 std::vector<std::pair<int, funcparam_t> > ret;
ac3da0c2
RG
141 const auto tied = boost::tie(tv.tv_sec, tv.tv_usec);
142 auto& idx = writes ? d_writeCallbacks.get<TTDOrderedTag>() : d_readCallbacks.get<TTDOrderedTag>();
27ae2e3c 143
ac3da0c2
RG
144 for (auto it = idx.begin(); it != idx.end(); ++it) {
145 if (it->d_ttd.tv_sec == 0 || tied <= boost::tie(it->d_ttd.tv_sec, it->d_ttd.tv_usec)) {
146 break;
27ae2e3c 147 }
ac3da0c2 148 ret.push_back(std::make_pair(it->d_fd, it->d_parameter));
27ae2e3c
RG
149 }
150
0bff046b
BH
151 return ret;
152 }
153
1f4abb20
BH
154 typedef FDMultiplexer* getMultiplexer_t();
155 typedef std::multimap<int, getMultiplexer_t*> FDMultiplexermap_t;
156
157 static FDMultiplexermap_t& getMultiplexerMap()
158 {
159 static FDMultiplexermap_t theMap;
160 return theMap;
161 }
162
5bdbb83d 163 virtual std::string getName() const = 0;
1f4abb20 164
696e32f5
RG
165 size_t getWatchedFDCount(bool writeFDs) const
166 {
167 return writeFDs ? d_writeCallbacks.size() : d_readCallbacks.size();
168 }
169
ab3e8a6c 170protected:
ac3da0c2
RG
171 struct FDBasedTag {};
172 struct TTDOrderedTag {};
173 struct ttd_compare
174 {
175 /* we want a 0 TTD (no timeout) to come _after_ everything else */
176 bool operator() (const struct timeval& lhs, const struct timeval& rhs) const
177 {
178 /* special treatment if at least one of the TTD is 0,
179 normal comparison otherwise */
180 if (lhs.tv_sec == 0 && rhs.tv_sec == 0) {
181 return false;
182 }
183 if (lhs.tv_sec == 0 && rhs.tv_sec != 0) {
184 return false;
185 }
186 if (lhs.tv_sec != 0 && rhs.tv_sec == 0) {
187 return true;
188 }
189
190 return std::tie(lhs.tv_sec, lhs.tv_usec) < std::tie(rhs.tv_sec, rhs.tv_usec);
191 }
192 };
193
194 typedef multi_index_container<
195 Callback,
196 indexed_by <
197 hashed_unique<tag<FDBasedTag>,
198 member<Callback,int,&Callback::d_fd>
199 >,
200 ordered_non_unique<tag<TTDOrderedTag>,
201 member<Callback,struct timeval,&Callback::d_ttd>,
202 ttd_compare
203 >
204 >
205 > callbackmap_t;
206
ab3e8a6c 207 callbackmap_t d_readCallbacks, d_writeCallbacks;
ab3e8a6c 208
27ae2e3c 209 virtual void addFD(callbackmap_t& cbmap, int fd, callbackfunc_t toDo, const funcparam_t& parameter, const struct timeval* ttd=nullptr)=0;
ab3e8a6c
BH
210 virtual void removeFD(callbackmap_t& cbmap, int fd)=0;
211 bool d_inrun;
a1dfcec8 212 callbackmap_t::iterator d_iter;
c454d11b 213
27ae2e3c 214 void accountingAddFD(callbackmap_t& cbmap, int fd, callbackfunc_t toDo, const funcparam_t& parameter, const struct timeval* ttd=nullptr)
c454d11b
BH
215 {
216 Callback cb;
ac3da0c2 217 cb.d_fd = fd;
c454d11b
BH
218 cb.d_callback=toDo;
219 cb.d_parameter=parameter;
220 memset(&cb.d_ttd, 0, sizeof(cb.d_ttd));
27ae2e3c
RG
221 if (ttd) {
222 cb.d_ttd = *ttd;
223 }
224
ac3da0c2 225 auto pair = cbmap.insert(cb);
27ae2e3c 226 if (!pair.second) {
335da0ba 227 throw FDMultiplexerException("Tried to add fd "+std::to_string(fd)+ " to multiplexer twice");
27ae2e3c 228 }
c454d11b
BH
229 }
230
231 void accountingRemoveFD(callbackmap_t& cbmap, int fd)
232 {
27ae2e3c 233 if(!cbmap.erase(fd)) {
335da0ba 234 throw FDMultiplexerException("Tried to remove unlisted fd "+std::to_string(fd)+ " from multiplexer");
27ae2e3c 235 }
c454d11b 236 }
1f4abb20
BH
237};
238
ab3e8a6c 239
a7acf71b
AT
240#endif
241