The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
io.h
Go to the documentation of this file.
1 #pragma once
2 
3 /*
4  * This program is is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or (at
7  * your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 /**
20  * $Id: a027da416621b73fe9ea483a71b5a68ce52e2fb0 $
21  * @file lib/redis/io.h
22  * @brief Redis asynchronous I/O connection allocation
23  *
24  * @copyright 2019 The FreeRADIUS server project
25  * @copyright 2019 Network RADIUS SAS (legal@networkradius.com)
26  *
27  * @author Arran Cudbard-Bell (a.cudbardb@freeradius.org)
28  */
29 RCSIDH(redis_io_h, "$Id: a027da416621b73fe9ea483a71b5a68ce52e2fb0 $")
30 
31 #include <freeradius-devel/redis/base.h>
32 #include <freeradius-devel/util/event.h>
33 #include <freeradius-devel/server/connection.h>
34 
35 #include <hiredis/async.h>
36 
37 #ifdef __cplusplus
38 extern "C" {
39 #endif
40 
41 typedef struct {
42  char *hostname;
44  uint32_t database; //!< number on Redis server.
45 
46  char const *password; //!< to authenticate to Redis.
49  char const *log_prefix;
51 
52 typedef uint64_t fr_redis_sqn_t;
53 
54 typedef struct {
58 
59 /** Store I/O state
60  *
61  * There are three layers of wrapping structures
62  *
63  * connection_t -> fr_redis_handle_t -> redisAsyncContext
64  *
65  */
66 typedef struct {
67  bool read_set; //!< We're listening for reads.
68  bool write_set; //!< We're listening for writes.
69  bool ignore_disconnect_cb; //!< Ensure that redisAsyncFree doesn't cause
70  ///< a callback loop.
71  fr_event_timer_t const *timer; //!< Connection timer.
72 
73 
74  redisAsyncContext *ac; //!< Async handle for hiredis.
75 
76  fr_dlist_head_t ignore; //!< Contains SQNs for responses that should be ignored.
77 
78  fr_redis_sqn_t req_sqn; //!< Current redis request number.
79  ///< Note: It would take 5.8 million years running
80  ///< at 100,000 requests/s to overflow, but my OCD
81  ///< requires that the max uses for trunk connections
82  ///< is set to UINT64_MAX if not specified by
83  ///< the user. It's one branch, and it makes me
84  ///< happy, deal with it.
85  fr_redis_sqn_t rsp_sqn; //!< Current redis response number.
87 
88 /** Tell the handle we sent a command, and get the SQN that command was assigned
89  *
90  * *MUST* be called for every command sent using the handle. Relies on the fact
91  * that responses from REDIS are FIFO with requests.
92  *
93  * @param[in] h the request was sent on.
94  * @return the handle specific SQN.
95  */
97 {
98  return h->req_sqn++;
99 }
100 
101 /** Ignore a response with a specific sequence number
102  *
103  * @param[in] h to ignore the response on.
104  * @param[in] sqn the command to ignore.
105  */
107 {
108  fr_redis_sqn_ignore_t *ignore;
109 
110  fr_assert(sqn <= h->rsp_sqn);
111 
112  MEM(ignore = talloc_zero(h, fr_redis_sqn_ignore_t));
113  ignore->sqn = sqn;
114  fr_dlist_insert_tail(&h->ignore, ignore);
115 }
116 
117 /** Update the response sequence number and check if we should ignore the response
118  *
119  * *MUST* be called for every reply received using the handle. Relies on the fact
120  * that responses from REDIS are FIFO with requests.
121  *
122  * @param[in] h to check for ignored responses.
123  */
125 {
128 
129  fr_assert(h->rsp_sqn <= h->req_sqn); /* Can't have more responses than requests */
130 
131  head = fr_dlist_head(&h->ignore);
132  if (!head || (head->sqn > check)) return true; /* First response to ignore is some time after this one */
133  fr_assert(head->sqn == check);
134 
136  talloc_free(head);
137 
138  return false;
139 }
140 
142  connection_conf_t const *conn_conf,
143  fr_redis_io_conf_t const *io_conf,
144  char const *log_prefix);
145 
146 redisAsyncContext *fr_redis_connection_get_async_ctx(connection_t *conn);
147 
148 #ifdef __cplusplus
149 }
150 #endif
#define RCSIDH(h, id)
Definition: build.h:482
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
Definition: dlist.h:486
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition: dlist.h:378
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition: dlist.h:638
Head of a doubly linked list.
Definition: dlist.h:51
Entry in a doubly linked list.
Definition: dlist.h:41
fr_redis_sqn_t rsp_sqn
Current redis response number.
Definition: io.h:85
bool read_set
We're listening for reads.
Definition: io.h:67
redisAsyncContext * ac
Async handle for hiredis.
Definition: io.h:74
fr_event_timer_t const * timer
Connection timer.
Definition: io.h:71
fr_redis_sqn_t sqn
Definition: io.h:56
fr_dlist_head_t ignore
Contains SQNs for responses that should be ignored.
Definition: io.h:76
static void fr_redis_connection_ignore_response(fr_redis_handle_t *h, fr_redis_sqn_t sqn)
Ignore a response with a specific sequence number.
Definition: io.h:106
bool ignore_disconnect_cb
Ensure that redisAsyncFree doesn't cause a callback loop.
Definition: io.h:69
char * hostname
Definition: io.h:42
connection_t * fr_redis_connection_alloc(TALLOC_CTX *ctx, fr_event_list_t *el, connection_conf_t const *conn_conf, fr_redis_io_conf_t const *io_conf, char const *log_prefix)
Allocate an async redis I/O connection.
Definition: io.c:422
uint32_t database
number on Redis server.
Definition: io.h:44
char const * password
to authenticate to Redis.
Definition: io.h:46
bool write_set
We're listening for writes.
Definition: io.h:68
char const * log_prefix
Definition: io.h:49
fr_dlist_t entry
Definition: io.h:55
static bool fr_redis_connection_process_response(fr_redis_handle_t *h)
Update the response sequence number and check if we should ignore the response.
Definition: io.h:124
fr_time_delta_t reconnection_delay
Definition: io.h:48
fr_time_delta_t connection_timeout
Definition: io.h:47
uint16_t port
Definition: io.h:43
redisAsyncContext * fr_redis_connection_get_async_ctx(connection_t *conn)
Return the redisAsyncContext associated with the connection.
Definition: io.c:455
static fr_redis_sqn_t fr_redis_connection_sent_request(fr_redis_handle_t *h)
Tell the handle we sent a command, and get the SQN that command was assigned.
Definition: io.h:96
uint64_t fr_redis_sqn_t
Definition: io.h:52
fr_redis_sqn_t req_sqn
Current redis request number.
Definition: io.h:78
Store I/O state.
Definition: io.h:66
talloc_free(reap)
Stores all information relating to an event list.
Definition: event.c:411
A timer event.
Definition: event.c:102
unsigned short uint16_t
Definition: merged_model.c:31
unsigned int uint32_t
Definition: merged_model.c:33
#define check(_handle, _len_p)
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
A time delta, a difference in time measured in nanoseconds.
Definition: time.h:80
static fr_event_list_t * el
static fr_slen_t head
Definition: xlat.h:406