The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
main_loop.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: 0b160a1f511809393f9fc7341d63e8a25489b933 $
19 *
20 * @file lib/server/main_loop.c
21 * @brief Creates a global event loop, and manages signalling between the forked child
22 * and its parent as the server starts.
23 *
24 * @copyright 2012 The FreeRADIUS server project
25 * @copyright 2012 Alan DeKok (aland@deployingradius.com)
26 */
27
28RCSID("$Id: 0b160a1f511809393f9fc7341d63e8a25489b933 $")
29
30#include <freeradius-devel/server/cf_parse.h>
31#include <freeradius-devel/server/main_loop.h>
32#include <freeradius-devel/util/debug.h>
33#include <freeradius-devel/server/state.h>
34#include <freeradius-devel/server/trigger.h>
35#include <freeradius-devel/server/util.h>
36
37#include <freeradius-devel/util/misc.h>
38#include <freeradius-devel/util/syserror.h>
39
40#include <fcntl.h>
41
42#ifdef HAVE_SYS_WAIT_H
43# include <sys/wait.h>
44#endif
45
46extern pid_t radius_pid;
48static int self_pipe[2] = { -1, -1 };
49
50#ifdef HAVE_SYSTEMD_WATCHDOG
51#include <systemd/sd-daemon.h>
52
53static fr_time_delta_t sd_watchdog_interval;
54static fr_timer_t *sd_watchdog_ev;
55
56/** Reoccurring watchdog event to inform systemd we're still alive
57 *
58 * Not actually a very good indicator of aliveness as the main event
59 * loop doesn't actually do any packet processing.
60 */
61static void sd_watchdog_event(fr_timer_list_t *tl, UNUSED fr_time_t now, void *ctx)
62{
63 DEBUG("Emitting systemd watchdog notification");
64
65 sd_notify(0, "WATCHDOG=1");
66
67 if (fr_timer_in(NULL, tl, &sd_watchdog_ev,
68 sd_watchdog_interval,
69 true, sd_watchdog_event, ctx) < 0) {
70 ERROR("Failed to insert watchdog event");
71 }
72}
73#endif
74
75/*
76 * Inform ourselves that we received a signal.
77 */
79{
80 ssize_t rcode;
81 uint8_t buffer[16];
82
83 /*
84 * The main loop is exiting, don't read from the self
85 * pipe, which avoids a fault below when that pipe
86 * doesn't exist.
87 */
88 if (self_pipe[1] < 0) return;
89
90 /*
91 * The read MUST be non-blocking for this to work.
92 */
93 rcode = read(self_pipe[0], buffer, sizeof(buffer));
94 if (rcode > 0) {
95 ssize_t i;
96
97 for (i = 0; i < rcode; i++) {
98 buffer[0] |= buffer[i];
99 }
100 } else {
101 buffer[0] = 0;
102 }
103
104 buffer[0] |= flag;
105
106 if (write(self_pipe[1], buffer, 1) < 0) {
107 FR_FAULT_LOG("Failed to write to self-pipe: %s", fr_syserror(errno));
108 fr_exit(0);
109 }
110}
111
112static void main_loop_signal_process(int flag)
113{
114 if ((flag & (RADIUS_SIGNAL_SELF_EXIT | RADIUS_SIGNAL_SELF_TERM)) != 0) {
115 if ((flag & RADIUS_SIGNAL_SELF_EXIT) != 0) {
116 INFO("Signalled to exit");
118 } else {
119 INFO("Signalled to terminate");
121 }
122
123 close(self_pipe[1]);
124 self_pipe[1] = -1;
125 return;
126 } /* else exit/term flags weren't set */
127
128 /*
129 * Tell the even loop to stop processing.
130 */
131 if ((flag & RADIUS_SIGNAL_SELF_HUP) != 0) {
132 fr_time_t when;
133 static fr_time_t last_hup = fr_time_wrap(0);
134
135 when = fr_time();
136 if (fr_time_delta_lt(fr_time_sub(when, last_hup), fr_time_delta_from_sec(5))) {
137 INFO("Ignoring HUP (less than 5s since last one)");
138 return;
139 }
140
141 INFO("Received HUP signal");
142
143 last_hup = when;
144
145 trigger(unlang_interpret_get_thread_default(), NULL, NULL, "server.signal.hup", true, NULL, NULL);
147 }
148}
149
150/** I/O handler listening on the signal pipe
151 *
152 */
154 UNUSED int fd, UNUSED int flags, UNUSED void *ctx)
155{
156 ssize_t i, rcode;
157 uint8_t buffer[32];
158
159 rcode = read(self_pipe[0], buffer, sizeof(buffer));
160 if (rcode <= 0) return;
161
162 /*
163 * Merge pending signals.
164 */
165 for (i = 0; i < rcode; i++) buffer[0] |= buffer[i];
166
168}
169
170/** Return the main loop event list
171 *
172 */
177
178#ifdef HAVE_SYSTEMD_WATCHDOG
179void main_loop_set_sd_watchdog_interval(void)
180{
181 uint64_t interval_usec;
182
183 if (sd_watchdog_enabled(0, &interval_usec) > 0) {
184 /*
185 * Convert microseconds to nanoseconds
186 * and set the interval to be half what
187 * systemd uses as its timeout value.
188 */
189 sd_watchdog_interval = fr_time_delta_wrap((interval_usec * 1000) / 2);
190
191 INFO("systemd watchdog interval is %pVs", fr_box_time_delta(sd_watchdog_interval));
192 } else {
193 INFO("systemd watchdog is disabled");
194 }
195}
196#endif
197
199{
200 TALLOC_FREE(event_list);
201}
202
204{
205 int ret;
206
207#ifdef HAVE_SYSTEMD_WATCHDOG
208 bool under_systemd = (getenv("NOTIFY_SOCKET") != NULL);
209#endif
210
211 if (!event_list) return 0;
212
213#ifdef HAVE_SYSTEMD_WATCHDOG
214 /*
215 * Tell systemd we're ready!
216 */
217 if (under_systemd) sd_notify(0, "READY=1");
218
219 /*
220 * Start placating the watchdog (if told to do so).
221 */
222 if (fr_time_delta_ispos(sd_watchdog_interval)) sd_watchdog_event(event_list->tl, fr_time_wrap(0), NULL);
223#endif
224
226#ifdef HAVE_SYSTEMD_WATCHDOG
227 if (ret != 0x80) { /* Not HUP */
228 if (under_systemd) {
229 INFO("Informing systemd we're stopping");
230 sd_notify(0, "STOPPING=1");
231 FR_TIMER_DELETE(&sd_watchdog_ev);
232 }
233 }
234#endif
235
236 /*
237 * Clear up the signal pipe we created in
238 * main loop init.
239 */
241 close(self_pipe[0]);
242
243 return ret;
244}
245
246static int _loop_status(UNUSED fr_time_t now, fr_time_delta_t wake, UNUSED void *ctx)
247{
248 if (fr_time_delta_unwrap(wake) > (NSEC / 10)) DEBUG4("Main loop waking up in %pV seconds", fr_box_time_delta(wake));
249
250 return 0;
251}
252
253/** Initialise the main event loop, setting up signal handlers
254 *
255 * This has to be done post-fork in case we're using kqueue, where the
256 * queue isn't inherited by the child process.
257 *
258 * @return
259 * - 0 on success.
260 * - -1 on failure.
261 */
263{
264 event_list = fr_event_list_alloc(NULL, _loop_status, NULL); /* Must not be allocated in mprotected ctx */
265 if (!event_list) return -1;
266
267 /*
268 * Not actually running the server, just exit.
269 */
270 if (check_config) return 0;
271
272 /*
273 * Child threads need a pipe to signal us, as do the
274 * signal handlers.
275 */
276 if (pipe(self_pipe) < 0) {
277 ERROR("Error opening self-signal pipe: %s", fr_syserror(errno));
278 return -1;
279 }
280 if ((fr_cloexec(self_pipe[0]) < 0) || (fr_cloexec(self_pipe[1]) < 0) ||
281 (fr_nonblock(self_pipe[0]) < 0) || (fr_nonblock(self_pipe[1]) < 0)) {
282 ERROR("Error setting self-signal pipe flags: %s", fr_syserror(errno));
283 close_pipe:
284 close(self_pipe[0]);
285 close(self_pipe[1]);
286 self_pipe[0] = self_pipe[1] = -1;
287 return -1;
288 }
289 DEBUG4("Created self-signal pipe. Read end FD %i, write end FD %i", self_pipe[0], self_pipe[1]);
290
291 if (fr_event_fd_insert(NULL, NULL, event_list, self_pipe[0],
293 NULL,
294 NULL,
295 event_list) < 0) {
296 PERROR("Failed creating self-signal pipe handler");
297 goto close_pipe;
298 }
299
300 return 0;
301}
static int const char char buffer[256]
Definition acutest.h:576
#define RCSID(id)
Definition build.h:560
#define UNUSED
Definition build.h:384
bool check_config
Definition cf_file.c:61
#define fr_exit(_x)
Exit, producing a log message in debug builds.
Definition debug.h:266
#define FR_FAULT_LOG(_fmt,...)
Definition debug.h:52
#define ERROR(fmt,...)
Definition dhcpclient.c:40
#define DEBUG(fmt,...)
Definition dhcpclient.c:38
#define fr_event_fd_insert(...)
Definition event.h:247
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition event.h:83
unlang_interpret_t * unlang_interpret_get_thread_default(void)
Get the default interpreter for this thread.
Definition interpret.c:2561
#define PERROR(_fmt,...)
Definition log.h:233
#define DEBUG4(_fmt,...)
Definition log.h:272
#define fr_time()
Definition event.c:60
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:2534
void fr_event_loop_exit(fr_event_list_t *el, int code)
Signal an event loop exit with the specified code.
Definition event.c:2383
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:1203
int fr_event_loop(fr_event_list_t *el)
Run an event loop.
Definition event.c:2405
Stores all information relating to an event list.
Definition event.c:377
static int self_pipe[2]
Definition main_loop.c:48
int main_loop_start(void)
Definition main_loop.c:203
fr_event_list_t * main_loop_event_list(void)
Return the main loop event list.
Definition main_loop.c:173
static void main_loop_signal_process(int flag)
Definition main_loop.c:112
static fr_event_list_t * event_list
Definition main_loop.c:47
static int _loop_status(UNUSED fr_time_t now, fr_time_delta_t wake, UNUSED void *ctx)
Definition main_loop.c:246
void main_loop_signal_raise(int flag)
Definition main_loop.c:78
pid_t radius_pid
static void main_loop_signal_recv(UNUSED fr_event_list_t *xel, UNUSED int fd, UNUSED int flags, UNUSED void *ctx)
I/O handler listening on the signal pipe.
Definition main_loop.c:153
int main_loop_init(void)
Initialise the main event loop, setting up signal handlers.
Definition main_loop.c:262
void main_loop_free(void)
Definition main_loop.c:198
@ RADIUS_SIGNAL_SELF_HUP
Definition main_loop.h:36
@ RADIUS_SIGNAL_SELF_EXIT
Definition main_loop.h:38
@ RADIUS_SIGNAL_SELF_TERM
Definition main_loop.h:37
long int ssize_t
unsigned char uint8_t
int fr_nonblock(UNUSED int fd)
Definition misc.c:293
int fr_cloexec(UNUSED int fd)
Definition misc.c:331
#define INFO(fmt,...)
Definition radict.c:63
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
static int64_t fr_time_delta_unwrap(fr_time_delta_t time)
Definition time.h:154
#define fr_time_delta_lt(_a, _b)
Definition time.h:285
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
#define fr_time_delta_wrap(_time)
Definition time.h:152
#define fr_time_wrap(_time)
Definition time.h:145
#define fr_time_delta_ispos(_a)
Definition time.h:290
#define NSEC
Definition time.h:379
#define fr_time_sub(_a, _b)
Subtract one time from another.
Definition time.h:229
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
An event timer list.
Definition timer.c:49
A timer event.
Definition timer.c:83
#define FR_TIMER_DELETE(_ev_p)
Definition timer.h:103
#define fr_timer_in(...)
Definition timer.h:87
int trigger(unlang_interpret_t *intp, CONF_SECTION const *cs, CONF_PAIR **trigger_cp, char const *name, bool rate_limit, fr_pair_list_t *args, void const *uctx)
Execute a trigger - call an executable to process an event.
Definition trigger.c:160
#define fr_box_time_delta(_val)
Definition value.h:391