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: 6d2e28c463485709e1f215e66c6ab12f65b96bd7 $
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: 6d2e28c463485709e1f215e66c6ab12f65b96bd7 $")
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 <signal.h>
41#include <fcntl.h>
42
43#ifdef HAVE_SYS_WAIT_H
44# include <sys/wait.h>
45#endif
46
47extern pid_t radius_pid;
49static int self_pipe[2] = { -1, -1 };
50
51#ifdef HAVE_SYSTEMD_WATCHDOG
52#include <systemd/sd-daemon.h>
53
54static fr_time_delta_t sd_watchdog_interval;
55static fr_event_timer_t const *sd_watchdog_ev;
56
57/** Reoccurring watchdog event to inform systemd we're still alive
58 *
59 * Note actually a very good indicator of aliveness as the main event
60 * loop doesn't actually do any packet processing.
61 */
62static void sd_watchdog_event(fr_event_list_t *our_el, UNUSED fr_time_t now, void *ctx)
63{
64 DEBUG("Emitting systemd watchdog notification");
65
66 sd_notify(0, "WATCHDOG=1");
67
68 if (fr_event_timer_in(NULL, our_el, &sd_watchdog_ev,
69 sd_watchdog_interval,
70 sd_watchdog_event, ctx) < 0) {
71 ERROR("Failed to insert watchdog event");
72 }
73}
74#endif
75
76/*
77 * Inform ourselves that we received a signal.
78 */
80{
81 ssize_t rcode;
82 uint8_t buffer[16];
83
84 /*
85 * The read MUST be non-blocking for this to work.
86 */
87 rcode = read(self_pipe[0], buffer, sizeof(buffer));
88 if (rcode > 0) {
89 ssize_t i;
90
91 for (i = 0; i < rcode; i++) {
92 buffer[0] |= buffer[i];
93 }
94 } else {
95 buffer[0] = 0;
96 }
97
98 buffer[0] |= flag;
99
100 if (write(self_pipe[1], buffer, 1) < 0) {
101 FR_FAULT_LOG("Failed to write to self-pipe: %s", fr_syserror(errno));
102 fr_exit(0);
103 }
104}
105
106static void main_loop_signal_process(int flag)
107{
108 if ((flag & (RADIUS_SIGNAL_SELF_EXIT | RADIUS_SIGNAL_SELF_TERM)) != 0) {
109 if ((flag & RADIUS_SIGNAL_SELF_EXIT) != 0) {
110 INFO("Signalled to exit");
112 } else {
113 INFO("Signalled to terminate");
115 }
116
117 return;
118 } /* else exit/term flags weren't set */
119
120 /*
121 * Tell the even loop to stop processing.
122 */
123 if ((flag & RADIUS_SIGNAL_SELF_HUP) != 0) {
124 fr_time_t when;
125 static fr_time_t last_hup = fr_time_wrap(0);
126
127 when = fr_time();
128 if (fr_time_delta_lt(fr_time_sub(when, last_hup), fr_time_delta_from_sec(5))) {
129 INFO("Ignoring HUP (less than 5s since last one)");
130 return;
131 }
132
133 INFO("Received HUP signal");
134
135 last_hup = when;
136
137 trigger_exec(unlang_interpret_get_thread_default(), NULL, "server.signal.hup", true, NULL);
139 }
140}
141
142/** I/O handler listening on the signal pipe
143 *
144 */
146 UNUSED int fd, UNUSED int flags, UNUSED void *ctx)
147{
148 ssize_t i, rcode;
149 uint8_t buffer[32];
150
151 rcode = read(self_pipe[0], buffer, sizeof(buffer));
152 if (rcode <= 0) return;
153
154 /*
155 * Merge pending signals.
156 */
157 for (i = 0; i < rcode; i++) buffer[0] |= buffer[i];
158
160}
161
162/** Return the main loop event list
163 *
164 */
169
170#ifdef HAVE_SYSTEMD_WATCHDOG
171void main_loop_set_sd_watchdog_interval(void)
172{
173 uint64_t interval_usec;
174
175 if (sd_watchdog_enabled(0, &interval_usec) > 0) {
176 /*
177 * Convert microseconds to nanoseconds
178 * and set the interval to be half what
179 * systemd uses as its timeout value.
180 */
181 sd_watchdog_interval = fr_time_delta_wrap((interval_usec * 1000) / 2);
182
183 INFO("systemd watchdog interval is %pVs", fr_box_time_delta(sd_watchdog_interval));
184 } else {
185 INFO("systemd watchdog is disabled");
186 }
187}
188#endif
189
191{
192 TALLOC_FREE(event_list);
193}
194
196{
197 int ret;
198
199#ifdef HAVE_SYSTEMD_WATCHDOG
200 bool under_systemd = (getenv("NOTIFY_SOCKET") != NULL);
201#endif
202
203 if (!event_list) return 0;
204
205#ifdef HAVE_SYSTEMD_WATCHDOG
206 /*
207 * Tell systemd we're ready!
208 */
209 if (under_systemd) sd_notify(0, "READY=1");
210
211 /*
212 * Start placating the watchdog (if told to do so).
213 */
214 if (fr_time_delta_ispos(sd_watchdog_interval)) sd_watchdog_event(event_list, fr_time_wrap(0), NULL);
215#endif
216
218#ifdef HAVE_SYSTEMD_WATCHDOG
219 if (ret != 0x80) { /* Not HUP */
220 if (under_systemd) {
221 INFO("Informing systemd we're stopping");
222 sd_notify(0, "STOPPING=1");
223 fr_event_timer_delete(&sd_watchdog_ev);
224 }
225 }
226#endif
227
228 /*
229 * Clear up the signal pipe we created in
230 * main loop init.
231 */
233 close(self_pipe[0]);
234
235 return ret;
236}
237
238static int _loop_status(UNUSED fr_time_t now, fr_time_delta_t wake, UNUSED void *ctx)
239{
240 if (fr_time_delta_unwrap(wake) > (NSEC / 10)) DEBUG4("Main loop waking up in %pV seconds", fr_box_time_delta(wake));
241
242 return 0;
243}
244
245/** Initialise the main event loop, setting up signal handlers
246 *
247 * This has to be done post-fork in case we're using kqueue, where the
248 * queue isn't inherited by the child process.
249 *
250 * @return
251 * - 0 on success.
252 * - -1 on failure.
253 */
255{
256 event_list = fr_event_list_alloc(NULL, _loop_status, NULL); /* Must not be allocated in mprotected ctx */
257 if (!event_list) return -1;
258
259 /*
260 * Not actually running the server, just exit.
261 */
262 if (check_config) return 0;
263
264 /*
265 * Child threads need a pipe to signal us, as do the
266 * signal handlers.
267 */
268 if (pipe(self_pipe) < 0) {
269 ERROR("Error opening self-signal pipe: %s", fr_syserror(errno));
270 return -1;
271 }
272 if ((fcntl(self_pipe[0], F_SETFL, O_NONBLOCK) < 0) ||
273 (fcntl(self_pipe[0], F_SETFD, FD_CLOEXEC) < 0)) {
274 ERROR("Error setting self-signal pipe flags: %s", fr_syserror(errno));
275 return -1;
276 }
277 if ((fcntl(self_pipe[1], F_SETFL, O_NONBLOCK) < 0) ||
278 (fcntl(self_pipe[1], F_SETFD, FD_CLOEXEC) < 0)) {
279 ERROR("Error setting self-signal pipe flags: %s", fr_syserror(errno));
280 return -1;
281 }
282 DEBUG4("Created self-signal pipe. Read end FD %i, write end FD %i", self_pipe[0], self_pipe[1]);
283
284 if (fr_event_fd_insert(NULL, NULL, event_list, self_pipe[0],
286 NULL,
287 NULL,
288 event_list) < 0) {
289 PERROR("Failed creating self-signal pipe handler");
290 return -1;
291 }
292
293 return 0;
294}
static int const char char buffer[256]
Definition acutest.h:576
#define RCSID(id)
Definition build.h:483
#define UNUSED
Definition build.h:315
bool check_config
Definition cf_file.c:67
#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:232
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
Definition event.h:62
#define fr_event_timer_in(...)
Definition event.h:255
unlang_interpret_t * unlang_interpret_get_thread_default(void)
Get the default interpreter for this thread.
Definition interpret.c:1787
#define PERROR(_fmt,...)
Definition log.h:228
#define DEBUG4(_fmt,...)
Definition log.h:267
int fr_event_timer_delete(fr_event_timer_t const **ev_p)
Delete a timer event from the event list.
Definition event.c:1611
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
void fr_event_loop_exit(fr_event_list_t *el, int code)
Signal an event loop exit with the specified code.
Definition event.c:2744
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:1260
int fr_event_loop(fr_event_list_t *el)
Run an event loop.
Definition event.c:2766
Stores all information relating to an event list.
Definition event.c:411
A timer event.
Definition event.c:102
static int self_pipe[2]
Definition main_loop.c:49
int main_loop_start(void)
Definition main_loop.c:195
fr_event_list_t * main_loop_event_list(void)
Return the main loop event list.
Definition main_loop.c:165
static void main_loop_signal_process(int flag)
Definition main_loop.c:106
static fr_event_list_t * event_list
Definition main_loop.c:48
static int _loop_status(UNUSED fr_time_t now, fr_time_delta_t wake, UNUSED void *ctx)
Definition main_loop.c:238
void main_loop_signal_raise(int flag)
Definition main_loop.c:79
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:145
int main_loop_init(void)
Initialise the main event loop, setting up signal handlers.
Definition main_loop.c:254
void main_loop_free(void)
Definition main_loop.c:190
@ 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
Signals that can be sent to a request.
#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
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:233
close(uq->fd)
#define fr_box_time_delta(_val)
Definition value.h:343