The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
io.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 67feb949dbf74d254871d1261cef730c30029929 $
19 * @file curl/io.c
20 * @brief Implement asynchronous callbacks for curl
21 *
22 * @copyright 2020 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 */
24#include <freeradius-devel/curl/base.h>
25#include <freeradius-devel/util/debug.h>
26#include <freeradius-devel/util/syserror.h>
27
28/*
29 * CURL headers do:
30 *
31 * #define curl_easy_setopt(handle,opt,param) curl_easy_setopt(handle,opt,param)
32 */
33DIAG_OFF_OPTIONAL(disabled-macro-expansion)
34#define SET_MOPTION(_mandle, _opt, _val)\
35do {\
36 if ((ret = curl_multi_setopt(mandle, _opt, _val)) != CURLM_OK) {\
37 option = STRINGIFY(_opt);\
38 goto error;\
39 }\
40} while (0)
41
42/** De-queue curl requests and wake up the requests that initiated them
43 *
44 * @param[in] mhandle containing the event loop and request counter.
45 * @param[in] mandle to dequeue curl easy handles/responses from.
46 */
47static inline void _fr_curl_io_demux(fr_curl_handle_t *mhandle, CURLM *mandle)
48{
49 struct CURLMsg *m;
50 int msg_queued = 0;
51
52 while ((m = curl_multi_info_read(mandle, &msg_queued))) {
53 switch (m->msg) {
54 case CURLMSG_DONE:
55 {
57 request_t *request = NULL;
58 CURL *candle = m->easy_handle;
59 CURLcode ret;
60
61 fr_assert(candle);
62
63 mhandle->transfers--;
64
65 ret = curl_easy_getinfo(candle, CURLINFO_PRIVATE, &randle);
66 /*
67 * In curl 7.61 we got bad request data returned
68 * here after cancellations were processed.
69 *
70 * For debug builds if the value is equal to
71 * 0xdeadc341, we know the request was cancelled.
72 *
73 * There is no good work around for this other than
74 * upgrading to a newer version of curl.
75 */
76 if (!fr_cond_assert_msg(ret == CURLE_OK,
77 "Failed retrieving request data from CURL easy handle (candle)")) {
78 curl_multi_remove_handle(mandle, candle);
79 return;
80 }
81 talloc_get_type_abort(randle, fr_curl_io_request_t);
82 request = randle->request;
83
85
86 /*
87 * If the request failed, say why...
88 */
89 if (m->data.result != CURLE_OK) {
90 REDEBUG("curl request failed: %s (%u)",
91 curl_easy_strerror(m->data.result), m->data.result);
92 }
93 randle->result = m->data.result;
94
95 /*
96 * This needs to be done last, else m->data.result
97 * ends up being junk.
98 */
99 curl_multi_remove_handle(mandle, candle);
100
102 }
103 break;
104
105 default:
106#ifndef NDEBUG
107 DEBUG4("Got unknown msg (%u) when dequeueing curl responses", m->msg);
108#endif
109 break;
110 }
111 }
112}
113
114/** libcurl's timer expired
115 *
116 * @param[in] tl the timer was inserted into.
117 * @param[in] now The current time according to the event loop.
118 * @param[in] uctx The rlm_fr_curl_thread_t specific to this thread.
119 */
121{
122 fr_curl_handle_t *mhandle = talloc_get_type_abort(uctx, fr_curl_handle_t);
123 CURLM *mandle = mhandle->mandle;
124 CURLMcode ret;
125 int running = 0;
126
127 DEBUG4("multi-handle %p - Timer fired", mandle);
128
129 ret = curl_multi_socket_action(mandle, CURL_SOCKET_TIMEOUT, 0, &running);
130 if (ret != CURLM_OK) {
131 ERROR("multi-handle %p - Servicing timer failed -%s (%i)", mandle, curl_multi_strerror(ret), ret);
132 return;
133 }
134
135 DEBUG3("multi-handle %p - Serviced by timer. %i request(s) in progress, %" PRIu64 " requests(s) to dequeue",
136 mandle, running, mhandle->transfers - (uint64_t)running);
137
138 _fr_curl_io_demux(mhandle, mandle);
139}
140
141/** Service an IO event on a file descriptor
142 *
143 * @param[in] mhandle containing the event loop and request counter.
144 * @param[in] fd the IO event occurred for.
145 * @param[in] event type.
146 */
147static inline void _fr_curl_io_service(fr_curl_handle_t *mhandle, int fd, int event)
148{
149 CURLMcode ret;
150 CURLM *mandle = mhandle->mandle;
151 int running = 0;
152
153 ret = curl_multi_socket_action(mandle, fd, event, &running);
154 if (ret != CURLM_OK) {
155 ERROR("multi-handle %p - Servicing I/O failed - %s (%i)", mandle, curl_multi_strerror(ret), ret);
156 return;
157 }
158
159 if (DEBUG_ENABLED3) {
160 char const *event_str;
161
162 switch (event) {
163 case CURL_CSELECT_ERR:
164 event_str = "error";
165 break;
166
167 case CURL_CSELECT_OUT:
168 event_str = "socket-writable";
169 break;
170
171 case CURL_CSELECT_IN:
172 event_str = "socket-readable";
173 break;
174
175 case 0:
176 event_str = "closed"; /* Not really closed, more do your own eval! */
177 break;
178
179 default:
180 event_str = "<INVALID>";
181 break;
182 }
183
184 DEBUG3("multi-handle %p - Serviced on fd %i event (%s). "
185 "%i request(s) in progress, %" PRIu64 " requests(s) to dequeue",
186 mandle, fd, event_str, running, mhandle->transfers - (uint64_t)running);
187 }
188
189
190 _fr_curl_io_demux(mhandle, mandle);
191}
192
193/** File descriptor experienced an error
194 *
195 * @param[in] el fd was registered with.
196 * @param[in] fd that errored.
197 * @param[in] flags from kevent.
198 * @param[in] fd_errno from kevent.
199 * @param[in] uctx The rlm_fr_curl_thread_t specific to this thread.
200 */
201static void _fr_curl_io_service_errored(UNUSED fr_event_list_t *el, int fd, int flags, int fd_errno, void *uctx)
202{
203 fr_curl_handle_t *mhandle = talloc_get_type_abort(uctx, fr_curl_handle_t);
204
205 DEBUG4("multi-handle %p - fd %i errored: %s", mhandle->mandle, fd, fr_syserror(fd_errno));
206
207 /*
208 * The remote server closed the socket
209 *
210 * Instead of signalling curl with CURL_CSELECT_ERR
211 * which always results in spurious errors being
212 * printed, pass in '0', which causes libcurl to do
213 * its own evaluation on the socket state, and hopefully
214 * run the right code for socket closure.
215 */
216 if (flags & EV_EOF) {
217 _fr_curl_io_service(mhandle, fd, 0);
218 return;
219 }
220
221 _fr_curl_io_service(mhandle, fd, CURL_CSELECT_ERR);
222}
223
224/** File descriptor became writable
225 *
226 * @param[in] el fd was registered with.
227 * @param[in] fd that became writable.
228 * @param[in] flags from kevent.
229 * @param[in] uctx The rlm_fr_curl_thread_t specific to this thread.
230 */
231static void _fr_curl_io_service_writable(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
232{
233 fr_curl_handle_t *mhandle = talloc_get_type_abort(uctx, fr_curl_handle_t);
234
235 DEBUG4("multi-handle %p - fd %i now writable", mhandle->mandle, fd);
236
237 _fr_curl_io_service(mhandle, fd, CURL_CSELECT_OUT);
238}
239
240/** File descriptor became readable
241 *
242 * @param[in] el fd was registered with.
243 * @param[in] fd that became readable.
244 * @param[in] flags from kevent.
245 * @param[in] uctx The rlm_fr_curl_thread_t specific to this thread.
246 */
247static void _fr_curl_io_service_readable(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
248{
249 fr_curl_handle_t *mhandle = talloc_get_type_abort(uctx, fr_curl_handle_t);
250
251 DEBUG4("multi-handle %p - fd %i now readable", mhandle->mandle, fd);
252
253 _fr_curl_io_service(mhandle, fd, CURL_CSELECT_IN);
254}
255
256/** Callback called by libcurl to set/unset timers
257 *
258 * Each rlm_fr_curl_thread_t has a timer event which is controller by libcurl.
259 * This allows libcurl to honour timeouts set on requests to remote hosts,
260 * and means we don't need to set timeouts for individual I/O events.
261 *
262 * @param[in] mandle handle requesting the timer be set/unset.
263 * @param[in] timeout_ms If > 0, how long to wait before calling curl_multi_socket_action.
264 * If == 0, we call curl_multi_socket_action as soon as possible.
265 * If < 0, we delete the timer.
266 * @param[in] ctx The rlm_fr_curl_thread_t specific to this thread.
267 * @return
268 * - 0 on success.
269 * - -1 on error.
270 */
271static int _fr_curl_io_timer_modify(CURLM *mandle, long timeout_ms, void *ctx)
272{
273 fr_curl_handle_t *mhandle = talloc_get_type_abort(ctx, fr_curl_handle_t);
274
275 if (timeout_ms < 0) {
276 FR_TIMER_DISARM_RETURN(mhandle->ev);
277 DEBUG3("multi-handle %p - Timer removed", mandle);
278 return 0;
279 }
280
281 DEBUG3("multi-handle %p will need servicing in %li ms", mandle, timeout_ms);
282
283 /*
284 * https://curl.haxx.se/libcurl/c/CURLMOPT_TIMERFUNCTION.html
285 *
286 * WARNING: even if it feels tempting, avoid calling libcurl directly from within the
287 * callback itself when the timeout_ms value is zero, since it risks triggering an
288 * unpleasant recursive behavior that immediately calls another call to the callback
289 * with a zero timeout...
290 *
291 * Setting a timeout of zero when calling fr_timer_in should result in the event
292 * repeating at most twice during one iteration of the event loop.
293 *
294 * In a previous version of this code we called curl_multi_socket_action immediately
295 * if timeout_ms was 0. It was observed that this lead to this callback being called
296 * ~665 times per request which is why we no longer do that.
297 */
298 if (fr_timer_in(mhandle, mhandle->el->tl, &mhandle->ev,
299 fr_time_delta_from_msec(timeout_ms),
300 false, _fr_curl_io_timer_expired, mhandle) < 0) return -1;
301 return 0;
302}
303
304/** Called by libcurl to register a socket that it's intefr_curled in receiving IO events for
305 *
306 *
307 * @param[in] easy handle this fd relates to.
308 * @param[in] fd File descriptor curl wants to be notified about.
309 * @param[in] what Which events libcurl wants to be notified of, may be one of:
310 * - CURL_POLL_IN Wait for incoming data. For the socket
311 * to become readable.
312 * - CURL_POLL_OUT Wait for outgoing data. For the socket
313 * to become writable.
314 * - CURL_POLL_INOUT Wait for incoming and outgoing data.
315 * For the socket to become readable or writable.
316 * - CURL_POLL_REMOVE The specified socket/file descriptor is no
317 * longer used by libcurl.
318 * @param[in] ctx The fr_curl_handle_t specific to this thread.
319 * @param[in] fd_ctx Private data associated with the socket.
320 */
321static int _fr_curl_io_event_modify(UNUSED CURL *easy, curl_socket_t fd, int what, void *ctx, UNUSED void *fd_ctx)
322{
323 fr_curl_handle_t *mhandle = talloc_get_type_abort(ctx, fr_curl_handle_t);
324
325 switch (what) {
326 case CURL_POLL_IN:
327 if (fr_event_fd_insert(mhandle, NULL, mhandle->el, fd,
329 NULL,
331 mhandle) < 0) {
332 PERROR("multi-handle %p registration failed for read+error events on FD %i",
333 mhandle->mandle, fd);
334 return -1;
335 }
336 DEBUG4("multi-handle %p registered for read+error events on FD %i", mhandle->mandle, fd);
337 break;
338
339 case CURL_POLL_OUT:
340 if (fr_event_fd_insert(mhandle, NULL, mhandle->el, fd,
341 NULL,
344 mhandle) < 0) {
345 PERROR("multi-handle %p registration failed for write+error events on FD %i",
346 mhandle->mandle, fd);
347 return -1;
348 }
349 DEBUG4("multi-handle %p registered for write+error events on FD %i", mhandle->mandle, fd);
350 break;
351
352 case CURL_POLL_INOUT:
353 if (fr_event_fd_insert(mhandle, NULL, mhandle->el, fd,
357 mhandle) < 0) {
358 PERROR("multi-handle %p registration failed for read+write+error events on FD %i",
359 mhandle->mandle, fd);
360 return -1;
361 }
362 DEBUG4("multi-handle %p registered for read+write+error events on FD %i", mhandle->mandle, fd);
363 break;
364
365 case CURL_POLL_REMOVE:
366 if (fr_event_fd_delete(mhandle->el, fd, FR_EVENT_FILTER_IO) < 0) {
367 PERROR("multi-handle %p de-registration failed for FD %i", mhandle->mandle, fd);
368 return -1;
369 }
370 DEBUG4("multi-handle %p unregistered events for FD %i", mhandle->mandle, fd);
371 break;
372
373 default:
374 fr_assert(0);
375 return -1;
376 }
377
378 return CURLM_OK;
379}
380
381/** Callback to receive debugging data from libcurl
382 *
383 * @note Should only be set on a handle if RDEBUG_ENABLED3 is true.
384 *
385 * @param[in] candle Curl handle the debugging data pertains to.
386 * @param[in] type The type of debugging data we received.
387 * @param[in] data Buffer containing debug data (not \0 terminated). Despite the
388 * type being char *, this can be binary data depending on the
389 * curl_infotype.
390 * @param[in] len The length of the data in the buffer.
391 * @param[in] uctx The current request.
392 * @return
393 * - 0 (we always indicate success)
394 */
395static int curl_debug_log(UNUSED CURL *candle, curl_infotype type, char *data, size_t len, void *uctx)
396{
397 request_t *request = talloc_get_type_abort(uctx, request_t);
398 char const *p = data, *q, *end = p + len;
399 char const *verb;
400
401 switch (type) {
402 case CURLINFO_TEXT:
403 /*
404 * Curl debug output has trailing newlines, and could conceivably
405 * span multiple lines. Take care of both cases.
406 */
407 while (p < end) {
408 q = memchr(p, '\n', end - p);
409 if (!q) q = end;
410
411 RDEBUG3("libcurl - %pV", fr_box_strvalue_len(p, q - p));
412 p = q + 1;
413 }
414
415 break;
416
417 case CURLINFO_HEADER_IN:
418 verb = "received";
419 print_header:
420 while (p < end) {
421 q = memchr(p, '\n', end - p);
422 q = q ? q + 1 : end;
423
424 if (RDEBUG_ENABLED4) {
425 RHEXDUMP4((uint8_t const *)p, q - p,
426 "%s header: %pV",
427 verb, fr_box_strvalue_len(p, q - p));
428 } else {
429 RDEBUG3("%s header: %pV",
430 verb, fr_box_strvalue_len(p, q - p));
431 }
432 p = q;
433 }
434 break;
435
436 case CURLINFO_HEADER_OUT:
437 verb = "sending";
438 goto print_header;
439
440 case CURLINFO_DATA_IN:
441 RHEXDUMP4((uint8_t const *)data, len, "received data[length %zu]", len);
442 break;
443
444 case CURLINFO_DATA_OUT:
445 RHEXDUMP4((uint8_t const *)data, len, "sending data[length %zu]", len);
446 break;
447
448 case CURLINFO_SSL_DATA_OUT:
449 RHEXDUMP4((uint8_t const *)data, len, "sending ssl-data[length %zu]", len);
450 break;
451
452 case CURLINFO_SSL_DATA_IN:
453 RHEXDUMP4((uint8_t const *)data, len, "received ssl-data[length %zu]", len);
454 break;
455
456 default:
457 RHEXDUMP3((uint8_t const *)data, len, "libcurl - debug data (unknown type %i)", (int)type);
458 break;
459 }
460
461 return 0;
462}
463
464/** Sends a request using libcurl
465 *
466 * Send the actual curl request to the server. The response will be handled by
467 * the numerous callbacks configured for the easy handle.
468 *
469 * @param[in] mhandle Thread-specific mhandle wrapper.
470 * @param[in] request Current request.
471 * @param[in] randle representing the request.
472 * @return
473 * - 0 on success.
474 * - -1 on failure.
475 */
477{
478 CURLcode ret;
479 CURLMcode mret;
480
482
483 randle->request = request;
484
485 /*
486 * Set debugging functions so we can track the
487 * IO request's progress.
488 */
489 if (RDEBUG_ENABLED3) {
490 FR_CURL_REQUEST_SET_OPTION(CURLOPT_DEBUGFUNCTION, curl_debug_log);
491 FR_CURL_REQUEST_SET_OPTION(CURLOPT_DEBUGDATA, request);
492 FR_CURL_REQUEST_SET_OPTION(CURLOPT_VERBOSE, 1L);
493 }
494
495 /*
496 * Stick the current request in the curl handle's
497 * private data. This makes it simple to resume
498 * the request in the demux function later...
499 *
500 * Note: If you get 0xdeadc341 in the private data
501 * in the demux function, the result for the easy
502 * handle was erroneously delivered after a
503 * cancellation.
504 */
505 ret = curl_easy_setopt(randle->candle, CURLOPT_PRIVATE, randle);
506 if (ret != CURLE_OK) {
507 REDEBUG("Request failed: %u - %s", ret, curl_easy_strerror(ret));
508 return -1;
509 }
510
511 /*
512 * Increment here, else the debug output looks
513 * messed up is curl_multi_add_handle triggers
514 * event loop modifications calls immediately.
515 */
516 mhandle->transfers++;
517 mret = curl_multi_add_handle(mhandle->mandle, randle->candle);
518 if (mret != CURLM_OK) {
519 mhandle->transfers--;
520 REDEBUG("Request failed: %i - %s", mret, curl_multi_strerror(mret));
521 return -1;
522 }
523
524 return 0;
525
526error:
527 return -1;
528}
529
531{
532 curl_easy_cleanup(randle->candle);
533
534 return 0;
535}
536
537/** Allocate a new curl easy request and wrapper struct
538 *
539 */
541{
542 fr_curl_io_request_t *randle;
543
544 randle = talloc_zero(ctx, fr_curl_io_request_t);
545 if (unlikely(!randle)) return NULL;
546
547 randle->candle = curl_easy_init();
548 if (!randle->candle) {
549 talloc_free(randle);
550 return NULL;
551 }
552
553 talloc_set_destructor(randle, _fr_curl_io_request_free);
554
555 return randle;
556}
557
558/** Free the multi-handle
559 *
560 */
561static int _mhandle_free(fr_curl_handle_t *mhandle)
562{
563 curl_multi_cleanup(mhandle->mandle);
564
565 return 0;
566}
567
568/** Performs the libcurl initialisation of the thread
569 *
570 * @param[in] ctx to alloc handle in.
571 * @param[in] el to initial.
572 * @param[in] multiplex Run multiple requests over the same connection simultaneously.
573 * HTTP/2 only.
574 * @return
575 * - 0 on success.
576 * - -1 on error.
577 */
580#ifndef CURLPIPE_MULTIPLEX
581 UNUSED
582#endif
583 bool multiplex)
584{
585 CURLMcode ret;
586 CURLM *mandle;
587 fr_curl_handle_t *mhandle;
588 char const *option;
589
590 mandle = curl_multi_init();
591 if (!mandle) {
592 ERROR("Curl multi-handle instantiation failed");
593 return NULL;
594 }
595
596 /*
597 * Structure to store extra data.
598 *
599 * Passed to all curl I/O and timer callbacks.
600 *
601 * If uctx data is needed in the future, can be added here.
602 */
603 MEM(mhandle = talloc_zero(ctx, fr_curl_handle_t));
604 mhandle->el = el;
605 mhandle->mandle = mandle;
606 talloc_set_destructor(mhandle, _mhandle_free);
607
608 SET_MOPTION(mandle, CURLMOPT_TIMERFUNCTION, _fr_curl_io_timer_modify);
609 SET_MOPTION(mandle, CURLMOPT_TIMERDATA, mhandle);
610
611 SET_MOPTION(mandle, CURLMOPT_SOCKETFUNCTION, _fr_curl_io_event_modify);
612 SET_MOPTION(mandle, CURLMOPT_SOCKETDATA, mhandle);
613
614#ifdef CURLPIPE_MULTIPLEX
615 SET_MOPTION(mandle, CURLMOPT_PIPELINING, multiplex ? CURLPIPE_MULTIPLEX : CURLPIPE_NOTHING);
616#endif
617
618 return mhandle;
619
620error:
621 ERROR("Failed setting curl option %s: %s (%i)", option, curl_multi_strerror(ret), ret);
622
623 return NULL;
624}
static uint8_t request[4]
#define DIAG_OFF_OPTIONAL(_x)
Definition build.h:577
#define unlikely(_x)
Definition build.h:486
#define UNUSED
Definition build.h:415
fr_timer_t * ev
Multi-Handle timer.
Definition base.h:91
#define FR_CURL_REQUEST_SET_OPTION(_x, _y)
Definition base.h:65
fr_event_list_t * el
Event list servicing I/O events.
Definition base.h:90
CURLcode result
Result of executing the request.
Definition base.h:101
uint64_t transfers
How many transfers are current in progress.
Definition base.h:92
CURLM * mandle
The multi handle.
Definition base.h:93
request_t * request
Current request.
Definition base.h:102
CURL * candle
Request specific handle.
Definition base.h:100
Uctx data for timer and I/O functions.
Definition base.h:89
Structure representing an individual request being passed to curl for processing.
Definition base.h:99
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:194
#define MEM(x)
Definition debug.h:38
#define ERROR(fmt,...)
Definition dhcpclient.c:40
#define fr_event_fd_insert(...)
Definition event.h:247
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition event.h:83
talloc_free(hp)
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:2008
static int _fr_curl_io_event_modify(UNUSED CURL *easy, curl_socket_t fd, int what, void *ctx, UNUSED void *fd_ctx)
Called by libcurl to register a socket that it's intefr_curled in receiving IO events for.
Definition io.c:321
static int _fr_curl_io_request_free(fr_curl_io_request_t *randle)
Definition io.c:530
static void _fr_curl_io_service_writable(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
File descriptor became writable.
Definition io.c:231
#define SET_MOPTION(_mandle, _opt, _val)
Definition io.c:34
static void _fr_curl_io_demux(fr_curl_handle_t *mhandle, CURLM *mandle)
De-queue curl requests and wake up the requests that initiated them.
Definition io.c:47
static void _fr_curl_io_service_errored(UNUSED fr_event_list_t *el, int fd, int flags, int fd_errno, void *uctx)
File descriptor experienced an error.
Definition io.c:201
static int curl_debug_log(UNUSED CURL *candle, curl_infotype type, char *data, size_t len, void *uctx)
Callback to receive debugging data from libcurl.
Definition io.c:395
static void _fr_curl_io_service_readable(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *uctx)
File descriptor became readable.
Definition io.c:247
int fr_curl_io_request_enqueue(fr_curl_handle_t *mhandle, request_t *request, fr_curl_io_request_t *randle)
Sends a request using libcurl.
Definition io.c:476
static int _fr_curl_io_timer_modify(CURLM *mandle, long timeout_ms, void *ctx)
Callback called by libcurl to set/unset timers.
Definition io.c:271
fr_curl_io_request_t * fr_curl_io_request_alloc(TALLOC_CTX *ctx)
Allocate a new curl easy request and wrapper struct.
Definition io.c:540
fr_curl_handle_t * fr_curl_io_init(TALLOC_CTX *ctx, fr_event_list_t *el, UNUSED bool multiplex)
Performs the libcurl initialisation of the thread.
Definition io.c:578
static void _fr_curl_io_timer_expired(UNUSED fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
libcurl's timer expired
Definition io.c:120
static void _fr_curl_io_service(fr_curl_handle_t *mhandle, int fd, int event)
Service an IO event on a file descriptor.
Definition io.c:147
static int _mhandle_free(fr_curl_handle_t *mhandle)
Free the multi-handle.
Definition io.c:561
#define PERROR(_fmt,...)
Definition log.h:233
#define DEBUG3(_fmt,...)
Definition log.h:271
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition log.h:352
#define RDEBUG3(fmt,...)
Definition log.h:360
#define RHEXDUMP4(_data, _len, _fmt,...)
Definition log.h:723
#define DEBUG4(_fmt,...)
Definition log.h:272
#define RHEXDUMP3(_data, _len, _fmt,...)
Definition log.h:722
#define DEBUG_ENABLED3
True if global debug level 1-3 messages are enabled.
Definition log.h:264
#define RDEBUG_ENABLED4
True if request debug level 1-4 messages are enabled.
Definition log.h:353
int fr_event_fd_delete(fr_event_list_t *el, int fd, fr_event_filter_t filter)
Remove a file descriptor from the event loop.
Definition event.c:1204
Stores all information relating to an event list.
Definition event.c:375
unsigned char uint8_t
#define fr_assert(_expr)
Definition rad_assert.h:37
#define REDEBUG(fmt,...)
#define REQUEST_VERIFY(_x)
Definition request.h:310
fr_aka_sim_id_type_t type
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
static fr_time_delta_t fr_time_delta_from_msec(int64_t msec)
Definition time.h:575
"server local" time.
Definition time.h:69
An event timer list.
Definition timer.c:49
#define FR_TIMER_DISARM_RETURN(_ev)
Definition timer.h:98
#define fr_timer_in(...)
Definition timer.h:87
static fr_event_list_t * el
static fr_slen_t data
Definition value.h:1366
#define fr_box_strvalue_len(_val, _len)
Definition value.h:340
static fr_sbuff_err_t char size_t * len
Definition value.h:1061