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: 74fb9811b44940ea836e597daedc52be6a1d85bf $
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("Synchronous done 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 has been stopped
109 *
110 * Clean up execution state
111 */
112static void _request_stop(request_t *request, void *uctx)
113{
114 unlang_interpret_synchronous_t *intps = uctx;
115
116 RDEBUG3("Synchronous request stopped");
117
118 /*
119 * The assumption here is that if the request
120 * not in the runnable queue, and it's not
121 * currently running, then it must be yielded.
122 */
123 if (fr_heap_extract(&intps->runnable, request) < 0) intps->yielded--;
124}
125
126/** Request is now runnable
127 *
128 */
129static void _request_runnable(request_t *request, void *uctx)
130{
131 unlang_interpret_synchronous_t *intps = uctx;
132
133 fr_assert(intps->yielded > 0);
134 intps->yielded--;
135
136 fr_heap_insert(&intps->runnable, request);
137}
138
139/** Interpreter yielded request
140 *
141 */
142static void _request_yield(request_t *request, void *uctx)
143{
144 unlang_interpret_synchronous_t *intps = uctx;
145
146 intps->yielded++;
147
148 RDEBUG3("synchronous request yielded");
149}
150
151/** Interpreter is starting to work on request again
152 *
153 */
154static void _request_resume(request_t *request, UNUSED void *uctx)
155{
156 RDEBUG3("synchronous request resumed");
157}
158
159static bool _request_scheduled(request_t const *request, UNUSED void *uctx)
160{
161 return fr_heap_entry_inserted(request->runnable_id);
162}
163
164/** Allocate a new temporary interpreter
165 *
166 */
168{
170
171 MEM(intps = talloc_zero(ctx, unlang_interpret_synchronous_t));
172 MEM(intps->runnable = fr_heap_talloc_alloc(intps, fr_pointer_cmp, request_t, runnable_id, 0));
173 if (el) {
174 intps->el = el;
175 } else {
176 MEM(intps->el = fr_event_list_alloc(intps, NULL, NULL));
177 }
178 MEM(intps->intp = unlang_interpret_init(intps, intps->el,
180 .init_internal = _request_init_internal,
181
182 .done_external = _request_done_external,
183 .done_internal = _request_done_internal,
184 .done_detached = _request_done_detached,
185
186 .detach = _request_detach,
187 .stop = _request_stop,
188 .yield = _request_yield,
189 .resume = _request_resume,
190 .mark_runnable = _request_runnable,
191 .scheduled = _request_scheduled
192 },
193 intps));
194
195 return intps;
196}
197
198/** Execute an unlang section synchronously
199 *
200 * Create a temporary event loop and swap it out for the one in the request.
201 * Execute unlang operations until we receive a non-yield return code then return.
202 *
203 * @note The use cases for this are very limited. If you need to use it, chances
204 * are what you're doing could be done better using one of the thread
205 * event loops.
206 *
207 * @param[in] el Event list for the temporary interpreter. If NULL a
208 * temporary list will be allocated.
209 * @param[in] request The current request.
210 * @return One of the RLM_MODULE_* macros.
211 */
213{
214 unlang_interpret_t *old_intp;
215 char const *caller;
216
218
219 rlm_rcode_t rcode;
220
221 request_t *sub_request = NULL;
222 bool dont_wait_for_event;
223 int iterations = 0;
224
225 old_intp = unlang_interpret_get(request);
226 caller = request->module;
227
228 intps = unlang_interpret_synchronous_alloc(request, el);
229 unlang_interpret_set(request, intps->intp);
230
231 el = intps->el;
232
233 rcode = unlang_interpret(request);
234
235 while ((dont_wait_for_event = (fr_heap_num_elements(intps->runnable) > 0)) ||
236 (intps->yielded > 0)) {
237 rlm_rcode_t sub_rcode;
238 int num_events;
239
240 /*
241 * Wait for a timer / IO event. If there's a
242 * failure, all kinds of bad things happen. Oh
243 * well.
244 */
245 DEBUG4("Gathering events - %s", dont_wait_for_event ? "Will not wait" : "will wait");
246 num_events = fr_event_corral(el, fr_time(), !dont_wait_for_event);
247 if (num_events < 0) {
248 RPERROR("fr_event_corral");
249 break;
250 }
251
252 DEBUG4("%d event(s) pending%s",
253 num_events == -1 ? 0 : num_events, num_events == -1 ? " - event loop exiting" : "");
254
255 /*
256 * This function ends up pushing a
257 * runnable request into the backlog, OR
258 * setting new timers.
259 */
260 if (num_events > 0) {
261 DEBUG4("Servicing event(s)");
263 }
264
265 /*
266 * If there are no runnable requests, then go
267 * back to check the timers again. Note that we
268 * only wait if there are timer events left to
269 * service.
270 *
271 * If there WAS a timer event, but servicing that
272 * timer event did not result in a runnable
273 * request, THEN we're guaranteed that there is
274 * still a timer event left.
275 */
276 sub_request = fr_heap_pop(&intps->runnable);
277 if (!sub_request) {
278 DEBUG4("No pending requests (%d yielded)", intps->yielded);
279 continue;
280 }
281
282 /*
283 * Continue interpretation until there's nothing
284 * in the backlog. If this request YIELDs, then
285 * do another loop around.
286 */
287 RDEBUG4(">>> interpreter (iteration %i)", ++iterations);
288 sub_rcode = unlang_interpret(sub_request);
289 RDEBUG4("<<< interpreter (iteration %i) - %s", iterations,
290 fr_table_str_by_value(rcode_table, sub_rcode, "<INVALID>"));
291
292 /*
293 * If the request being run was the original request
294 * then update the rcode we're returning.
295 */
296 if (sub_request == request) rcode = sub_rcode;
297
298 DEBUG3("%u runnable, %d yielded", fr_heap_num_elements(intps->runnable), intps->yielded);
299 }
300
301 talloc_free(intps);
302 unlang_interpret_set(request, old_intp);
303 request->module = caller;
304
305 return rcode;
306}
#define UNUSED
Definition build.h:315
#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
int fr_heap_extract(fr_heap_t **hp, void *data)
Remove a node from the heap.
Definition heap.c:239
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)
Run the interpreter for a current request.
Definition interpret.c:770
void unlang_interpret_set(request_t *request, unlang_interpret_t *intp)
Set a specific interpreter for a request.
Definition interpret.c:1745
unlang_interpret_t * unlang_interpret_get(request_t *request)
Get the interpreter set for a request.
Definition interpret.c:1754
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:1703
bool unlang_interpret_is_resumable(request_t *request)
Check if a request as resumable.
Definition interpret.c:1341
External functions provided by the owner of the interpret.
Definition interpret.h:100
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.
static void _request_stop(request_t *request, void *uctx)
Request has been stopped.
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:2549
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:2414
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:2899
Stores all information relating to an event list.
Definition event.c:411
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:668
@ REQUEST_STOP_PROCESSING
Request has been signalled to stop.
Definition request.h:62
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition state_test.c:8
#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