The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 5ac0235a0241dec31018cc97df49406ce488a9bb $
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: 5ac0235a0241dec31018cc97df49406ce488a9bb $")
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 read MUST be non-blocking for this to work.
85 */
86 rcode = read(self_pipe[0], buffer, sizeof(buffer));
87 if (rcode > 0) {
88 ssize_t i;
89
90 for (i = 0; i < rcode; i++) {
91 buffer[0] |= buffer[i];
92 }
93 } else {
94 buffer[0] = 0;
95 }
96
97 buffer[0] |= flag;
98
99 if (write(self_pipe[1], buffer, 1) < 0) {
100 FR_FAULT_LOG("Failed to write to self-pipe: %s", fr_syserror(errno));
101 fr_exit(0);
102 }
103}
104
105static void main_loop_signal_process(int flag)
106{
107 if ((flag & (RADIUS_SIGNAL_SELF_EXIT | RADIUS_SIGNAL_SELF_TERM)) != 0) {
108 if ((flag & RADIUS_SIGNAL_SELF_EXIT) != 0) {
109 INFO("Signalled to exit");
111 } else {
112 INFO("Signalled to terminate");
114 }
115
116 return;
117 } /* else exit/term flags weren't set */
118
119 /*
120 * Tell the even loop to stop processing.
121 */
122 if ((flag & RADIUS_SIGNAL_SELF_HUP) != 0) {
123 fr_time_t when;
124 static fr_time_t last_hup = fr_time_wrap(0);
125
126 when = fr_time();
127 if (fr_time_delta_lt(fr_time_sub(when, last_hup), fr_time_delta_from_sec(5))) {
128 INFO("Ignoring HUP (less than 5s since last one)");
129 return;
130 }
131
132 INFO("Received HUP signal");
133
134 last_hup = when;
135
136 trigger_exec(unlang_interpret_get_thread_default(), NULL, "server.signal.hup", true, NULL);
138 }
139}
140
141/** I/O handler listening on the signal pipe
142 *
143 */
145 UNUSED int fd, UNUSED int flags, UNUSED void *ctx)
146{
147 ssize_t i, rcode;
148 uint8_t buffer[32];
149
150 rcode = read(self_pipe[0], buffer, sizeof(buffer));
151 if (rcode <= 0) return;
152
153 /*
154 * Merge pending signals.
155 */
156 for (i = 0; i < rcode; i++) buffer[0] |= buffer[i];
157
159}
160
161/** Return the main loop event list
162 *
163 */
168
169#ifdef HAVE_SYSTEMD_WATCHDOG
170void main_loop_set_sd_watchdog_interval(void)
171{
172 uint64_t interval_usec;
173
174 if (sd_watchdog_enabled(0, &interval_usec) > 0) {
175 /*
176 * Convert microseconds to nanoseconds
177 * and set the interval to be half what
178 * systemd uses as its timeout value.
179 */
180 sd_watchdog_interval = fr_time_delta_wrap((interval_usec * 1000) / 2);
181
182 INFO("systemd watchdog interval is %pVs", fr_box_time_delta(sd_watchdog_interval));
183 } else {
184 INFO("systemd watchdog is disabled");
185 }
186}
187#endif
188
190{
191 TALLOC_FREE(event_list);
192}
193
195{
196 int ret;
197
198#ifdef HAVE_SYSTEMD_WATCHDOG
199 bool under_systemd = (getenv("NOTIFY_SOCKET") != NULL);
200#endif
201
202 if (!event_list) return 0;
203
204#ifdef HAVE_SYSTEMD_WATCHDOG
205 /*
206 * Tell systemd we're ready!
207 */
208 if (under_systemd) sd_notify(0, "READY=1");
209
210 /*
211 * Start placating the watchdog (if told to do so).
212 */
213 if (fr_time_delta_ispos(sd_watchdog_interval)) sd_watchdog_event(event_list->tl, fr_time_wrap(0), NULL);
214#endif
215
217#ifdef HAVE_SYSTEMD_WATCHDOG
218 if (ret != 0x80) { /* Not HUP */
219 if (under_systemd) {
220 INFO("Informing systemd we're stopping");
221 sd_notify(0, "STOPPING=1");
222 FR_TIMER_DELETE(&sd_watchdog_ev);
223 }
224 }
225#endif
226
227 /*
228 * Clear up the signal pipe we created in
229 * main loop init.
230 */
232 close(self_pipe[0]);
233
234 return ret;
235}
236
237static int _loop_status(UNUSED fr_time_t now, fr_time_delta_t wake, UNUSED void *ctx)
238{
239 if (fr_time_delta_unwrap(wake) > (NSEC / 10)) DEBUG4("Main loop waking up in %pV seconds", fr_box_time_delta(wake));
240
241 return 0;
242}
243
244/** Initialise the main event loop, setting up signal handlers
245 *
246 * This has to be done post-fork in case we're using kqueue, where the
247 * queue isn't inherited by the child process.
248 *
249 * @return
250 * - 0 on success.
251 * - -1 on failure.
252 */
254{
255 event_list = fr_event_list_alloc(NULL, _loop_status, NULL); /* Must not be allocated in mprotected ctx */
256 if (!event_list) return -1;
257
258 /*
259 * Not actually running the server, just exit.
260 */
261 if (check_config) return 0;
262
263 /*
264 * Child threads need a pipe to signal us, as do the
265 * signal handlers.
266 */
267 if (pipe(self_pipe) < 0) {
268 ERROR("Error opening self-signal pipe: %s", fr_syserror(errno));
269 return -1;
270 }
271 if ((fcntl(self_pipe[0], F_SETFL, O_NONBLOCK) < 0) ||
272 (fcntl(self_pipe[0], F_SETFD, FD_CLOEXEC) < 0)) {
273 ERROR("Error setting self-signal pipe flags: %s", fr_syserror(errno));
274 return -1;
275 }
276 if ((fcntl(self_pipe[1], F_SETFL, O_NONBLOCK) < 0) ||
277 (fcntl(self_pipe[1], F_SETFD, FD_CLOEXEC) < 0)) {
278 ERROR("Error setting self-signal pipe flags: %s", fr_syserror(errno));
279 return -1;
280 }
281 DEBUG4("Created self-signal pipe. Read end FD %i, write end FD %i", self_pipe[0], self_pipe[1]);
282
283 if (fr_event_fd_insert(NULL, NULL, event_list, self_pipe[0],
285 NULL,
286 NULL,
287 event_list) < 0) {
288 PERROR("Failed creating self-signal pipe handler");
289 return -1;
290 }
291
292 return 0;
293}
static int const char char buffer[256]
Definition acutest.h:576
#define RCSID(id)
Definition build.h:485
#define UNUSED
Definition build.h:317
bool check_config
Definition cf_file.c:62
#define fr_exit(_x)
Exit, producing a log message in debug builds.
Definition debug.h:228
#define FR_FAULT_LOG(_fmt,...)
Definition debug.h:49
#define ERROR(fmt,...)
Definition dhcpclient.c:41
#define DEBUG(fmt,...)
Definition dhcpclient.c:39
#define fr_event_fd_insert(...)
Definition event.h:248
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition event.h:84
unlang_interpret_t * unlang_interpret_get_thread_default(void)
Get the default interpreter for this thread.
Definition interpret.c:1845
#define PERROR(_fmt,...)
Definition log.h:228
#define DEBUG4(_fmt,...)
Definition log.h:267
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
void fr_event_loop_exit(fr_event_list_t *el, int code)
Signal an event loop exit with the specified code.
Definition event.c:2372
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:2394
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:194
fr_event_list_t * main_loop_event_list(void)
Return the main loop event list.
Definition main_loop.c:164
static void main_loop_signal_process(int flag)
Definition main_loop.c:105
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:237
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:144
int main_loop_init(void)
Initialise the main event loop, setting up signal handlers.
Definition main_loop.c:253
void main_loop_free(void)
Definition main_loop.c:189
@ 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
#define INFO(fmt,...)
Definition radict.c:54
#define FD_CLOEXEC
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition state_test.c:8
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:75
#define FR_TIMER_DELETE(_ev_p)
Definition timer.h:102
#define fr_timer_in(...)
Definition timer.h:86
int trigger_exec(unlang_interpret_t *intp, CONF_SECTION const *cs, char const *name, bool rate_limit, fr_pair_list_t *args)
Execute a trigger - call an executable to process an event.
Definition trigger.c:228
close(uq->fd)
#define fr_box_time_delta(_val)
Definition value.h:354