The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
interpret_synchronous.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
5 * (at 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: c5da642067f02ed5ac8a36ac6e822f968d3577af $
19 *
20 * @file unlang/interpret_synchronous.c
21 * @brief Synchronous interpreter
22 *
23 * @copyright 2021 The FreeRADIUS server project
24 */
25
26#include "interpret_priv.h"
27#include <freeradius-devel/server/module_rlm.h>
28
35
36/** Internal request (i.e. one generated by the interpreter) is now complete
37 *
38 */
39static void _request_init_internal(request_t *request, void *uctx)
40{
42
43 RDEBUG3("Initialising internal synchronous request");
44 unlang_interpret_set(request, intps->intp);
45 fr_heap_insert(&intps->runnable, request);
46}
47
48/** External request is now complete
49 *
50 */
51static void _request_done_external(request_t *request, UNUSED rlm_rcode_t rcode, UNUSED void *uctx)
52{
53 if (request->master_state != REQUEST_STOP_PROCESSING) {
54 /*
55 * Previously, there was a check to ensure that external requests
56 * had an indentation level of zero, but this does not apply
57 * if the request was passed in to the synchronous interpreter
58 * as its base indentation level may not have been zero to begin
59 * with.
60 *
61 * Also check that the request is NOT marked as "yielded", but
62 * is in fact done.
63 *
64 * @todo - check that the stack is at frame 0, otherwise
65 * more things have gone wrong.
66 */
68 "Request %s is marked as yielded at end of processing", request->name);
69 }
70
71 RDEBUG3("Done synchronous external request");
72}
73
74/** Internal request (i.e. one generated by the interpreter) is now complete
75 *
76 */
77static void _request_done_internal(request_t *request, UNUSED rlm_rcode_t rcode, UNUSED void *uctx)
78{
79 RDEBUG3("Done synchronous internal request");
80
81 /* Request will be cleaned up by whatever created it */
82}
83
84static void _request_done_detached(request_t *request, UNUSED rlm_rcode_t rcode, UNUSED void *uctx)
85{
86 RDEBUG3("Done synchronous detached request");
87
88 /*
89 * Detached requests have to be freed by us
90 * as nothing else can free them.
91 *
92 * All other requests must be freed by the
93 * code which allocated them.
94 */
95 talloc_free(request);
96}
97
98/** We don't need to do anything for internal -> detached
99 *
100 */
101static void _request_detach(request_t *request, UNUSED void *uctx)
102{
103 RDEBUG3("Synchronous request detached");
104
105 if (request_detach(request) < 0) RPEDEBUG("Failed detaching request");
106}
107
108/** Request is now runnable
109 *
110 */
111static void _request_runnable(request_t *request, void *uctx)
112{
113 unlang_interpret_synchronous_t *intps = uctx;
114
115 fr_assert(intps->yielded > 0);
116 intps->yielded--;
117
118 fr_heap_insert(&intps->runnable, request);
119}
120
121/** Interpreter yielded request
122 *
123 */
124static void _request_yield(request_t *request, void *uctx)
125{
126 unlang_interpret_synchronous_t *intps = uctx;
127
128 intps->yielded++;
129
130 RDEBUG3("synchronous request yielded");
131}
132
133/** Interpreter is starting to work on request again
134 *
135 */
136static void _request_resume(request_t *request, UNUSED void *uctx)
137{
138 RDEBUG3("synchronous request resumed");
139}
140
141static bool _request_scheduled(request_t const *request, UNUSED void *uctx)
142{
143 return fr_heap_entry_inserted(request->runnable);
144}
145
146/** Allocate a new temporary interpreter
147 *
148 */
150{
152
153 MEM(intps = talloc_zero(ctx, unlang_interpret_synchronous_t));
154 MEM(intps->runnable = fr_heap_talloc_alloc(intps, fr_pointer_cmp, request_t, runnable, 0));
155 if (el) {
156 intps->el = el;
157 } else {
158 MEM(intps->el = fr_event_list_alloc(intps, NULL, NULL));
159 }
160 MEM(intps->intp = unlang_interpret_init(intps, intps->el,
162 .init_internal = _request_init_internal,
163
164 .done_external = _request_done_external,
165 .done_internal = _request_done_internal,
166 .done_detached = _request_done_detached,
167
168 .detach = _request_detach,
169 .yield = _request_yield,
170 .resume = _request_resume,
171 .mark_runnable = _request_runnable,
172 .scheduled = _request_scheduled,
173 },
174 intps));
175
176 return intps;
177}
178
179/** Execute an unlang section synchronously
180 *
181 * Create a temporary event loop and swap it out for the one in the request.
182 * Execute unlang operations until we receive a non-yield return code then return.
183 *
184 * @note The use cases for this are very limited. If you need to use it, chances
185 * are what you're doing could be done better using one of the thread
186 * event loops.
187 *
188 * @param[in] el Event list for the temporary interpreter. If NULL a
189 * temporary list will be allocated.
190 * @param[in] request The current request.
191 * @return One of the RLM_MODULE_* macros.
192 */
194{
195 unlang_interpret_t *old_intp;
196 char const *caller;
197
199
200 rlm_rcode_t rcode;
201
202 request_t *sub_request = NULL;
203 bool dont_wait_for_event;
204 int iterations = 0;
205
206 old_intp = unlang_interpret_get(request);
207 caller = request->module;
208
209 intps = unlang_interpret_synchronous_alloc(request, el);
210 unlang_interpret_set(request, intps->intp);
211
212 el = intps->el;
213
214 rcode = unlang_interpret(request, UNLANG_REQUEST_RESUME);
215
216 while ((dont_wait_for_event = (fr_heap_num_elements(intps->runnable) > 0)) ||
217 (intps->yielded > 0)) {
218 rlm_rcode_t sub_rcode;
219 int num_events;
220
221 /*
222 * Wait for a timer / IO event. If there's a
223 * failure, all kinds of bad things happen. Oh
224 * well.
225 */
226 DEBUG4("Gathering events - %s", dont_wait_for_event ? "Will not wait" : "will wait");
227 num_events = fr_event_corral(el, el->tl->time(), !dont_wait_for_event);
228 if (num_events < 0) {
229 RPERROR("fr_event_corral");
230 break;
231 }
232
233 DEBUG4("%d event(s) pending%s",
234 num_events == -1 ? 0 : num_events, num_events == -1 ? " - event loop exiting" : "");
235
236 /*
237 * This function ends up pushing a
238 * runnable request into the backlog, OR
239 * setting new timers.
240 */
241 if (num_events > 0) {
242 DEBUG4("Servicing event(s)");
244 }
245
246 /*
247 * If there are no runnable requests, then go
248 * back to check the timers again. Note that we
249 * only wait if there are timer events left to
250 * service.
251 *
252 * If there WAS a timer event, but servicing that
253 * timer event did not result in a runnable
254 * request, THEN we're guaranteed that there is
255 * still a timer event left.
256 */
257 sub_request = fr_heap_pop(&intps->runnable);
258 if (!sub_request) {
259 DEBUG4("No pending requests (%d yielded)", intps->yielded);
260 continue;
261 }
262
263 /*
264 * Continue interpretation until there's nothing
265 * in the backlog. If this request YIELDs, then
266 * do another loop around.
267 */
268 RDEBUG4(">>> interpreter (iteration %i)", ++iterations);
269 sub_rcode = unlang_interpret(sub_request, UNLANG_REQUEST_RESUME);
270 RDEBUG4("<<< interpreter (iteration %i) - %s", iterations,
271 fr_table_str_by_value(rcode_table, sub_rcode, "<INVALID>"));
272
273 /*
274 * If the request being run was the original request
275 * then update the rcode we're returning.
276 */
277 if (sub_request == request) rcode = sub_rcode;
278
279 DEBUG3("%u runnable, %d yielded", fr_heap_num_elements(intps->runnable), intps->yielded);
280 }
281
282 talloc_free(intps);
283 unlang_interpret_set(request, old_intp);
284 request->module = caller;
285
286 return rcode;
287}
#define UNUSED
Definition build.h:317
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:210
#define MEM(x)
Definition debug.h:36
int fr_heap_insert(fr_heap_t **hp, void *data)
Insert a new element into the heap.
Definition heap.c:146
void * fr_heap_pop(fr_heap_t **hp)
Remove a node from the heap.
Definition heap.c:322
static bool fr_heap_entry_inserted(fr_heap_index_t heap_idx)
Check if an entry is inserted into a heap.
Definition heap.h:124
static unsigned int fr_heap_num_elements(fr_heap_t *h)
Return the number of elements in the heap.
Definition heap.h:179
#define fr_heap_talloc_alloc(_ctx, _cmp, _talloc_type, _field, _init)
Creates a heap that verifies elements are of a specific talloc type.
Definition heap.h:115
The main heap structure.
Definition heap.h:66
rlm_rcode_t unlang_interpret(request_t *request, bool running)
Run the interpreter for a current request.
Definition interpret.c:712
void unlang_interpret_set(request_t *request, unlang_interpret_t *intp)
Set a specific interpreter for a request.
Definition interpret.c:1738
unlang_interpret_t * unlang_interpret_get(request_t *request)
Get the interpreter set for a request.
Definition interpret.c:1747
unlang_interpret_t * unlang_interpret_init(TALLOC_CTX *ctx, fr_event_list_t *el, unlang_request_func_t *funcs, void *uctx)
Initialize a unlang compiler / interpret.
Definition interpret.c:1697
bool unlang_interpret_is_resumable(request_t *request)
Check if a request as resumable.
Definition interpret.c:1342
#define UNLANG_REQUEST_RESUME
Definition interpret.h:42
External functions provided by the owner of the interpret.
Definition interpret.h:109
Private declarations for the unlang interpreter.
static void _request_yield(request_t *request, void *uctx)
Interpreter yielded request.
static void _request_done_external(request_t *request, UNUSED rlm_rcode_t rcode, UNUSED void *uctx)
External request is now complete.
static void _request_resume(request_t *request, UNUSED void *uctx)
Interpreter is starting to work on request again.
static void _request_runnable(request_t *request, void *uctx)
Request is now runnable.
static void _request_done_internal(request_t *request, UNUSED rlm_rcode_t rcode, UNUSED void *uctx)
Internal request (i.e.
static bool _request_scheduled(request_t const *request, UNUSED void *uctx)
static void _request_detach(request_t *request, UNUSED void *uctx)
We don't need to do anything for internal -> detached.
rlm_rcode_t unlang_interpret_synchronous(fr_event_list_t *el, request_t *request)
Execute an unlang section synchronously.
static void _request_done_detached(request_t *request, UNUSED rlm_rcode_t rcode, UNUSED void *uctx)
static unlang_interpret_synchronous_t * unlang_interpret_synchronous_alloc(TALLOC_CTX *ctx, fr_event_list_t *el)
Allocate a new temporary interpreter.
static void _request_init_internal(request_t *request, void *uctx)
Internal request (i.e.
#define DEBUG3(_fmt,...)
Definition log.h:266
#define RDEBUG3(fmt,...)
Definition log.h:343
#define DEBUG4(_fmt,...)
Definition log.h:267
#define RPERROR(fmt,...)
Definition log.h:302
#define RPEDEBUG(fmt,...)
Definition log.h:376
#define RDEBUG4(fmt,...)
Definition log.h:344
void fr_event_service(fr_event_list_t *el)
Service any outstanding timer or file descriptor events.
Definition event.c:2194
int fr_event_corral(fr_event_list_t *el, fr_time_t now, bool wait)
Gather outstanding timer and file descriptor events.
Definition event.c:2059
talloc_free(reap)
fr_event_list_t * fr_event_list_alloc(TALLOC_CTX *ctx, fr_event_status_cb_t status, void *status_uctx)
Initialise a new event list.
Definition event.c:2523
Stores all information relating to an event list.
Definition event.c:377
int8_t fr_pointer_cmp(void const *a, void const *b)
Compares two pointers.
Definition misc.c:417
#define fr_assert(_expr)
Definition rad_assert.h:38
fr_table_num_sorted_t const rcode_table[]
Definition rcode.c:35
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
int request_detach(request_t *child)
Unlink a subrequest from its parent.
Definition request.c:542
@ REQUEST_STOP_PROCESSING
Request has been signalled to stop.
Definition request.h:88
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
static fr_event_list_t * el