The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
radiusd.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: 9210e71e4d4f016231a7f3331815e489418be1d3 $
19 *
20 * @file radiusd.c
21 * @brief Main loop of the radius server.
22 *
23 * @copyright 2000-2018 The FreeRADIUS server project
24 * @copyright 1999,2000 Miquel van Smoorenburg (miquels@cistron.nl)
25 * @copyright 2000 Alan DeKok (aland@freeradius.org)
26 * @copyright 2000 Alan Curry (pacman-radius@cqc.com)
27 * @copyright 2000 Jeff Carneal (jeff@apex.net)
28 * @copyright 2000 Chad Miller (cmiller@surfsouth.com)
29 */
30RCSID("$Id: 9210e71e4d4f016231a7f3331815e489418be1d3 $")
31
32#include <freeradius-devel/server/base.h>
33#include <freeradius-devel/server/radmin.h>
34#include <freeradius-devel/server/snmp.h>
35#include <freeradius-devel/util/size.h>
36
37#include <freeradius-devel/tls/base.h>
38#include <freeradius-devel/tls/log.h>
39
40#include <freeradius-devel/unlang/base.h>
41
42#include <freeradius-devel/util/misc.h>
43#include <freeradius-devel/util/syserror.h>
44
45#ifdef HAVE_CAPABILITY_H
46#include <freeradius-devel/util/cap.h>
47#endif
48
49#include <fcntl.h>
50#include <sys/file.h>
51#include <sys/mman.h>
52
53#ifdef HAVE_GETOPT_H
54# include <getopt.h>
55#endif
56
57#ifdef HAVE_SYS_WAIT_H
58# include <sys/wait.h>
59#endif
60#ifndef WEXITSTATUS
61# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
62#endif
63#ifndef WIFEXITED
64# define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
65#endif
66
67#ifdef HAVE_SYSTEMD
68# include <systemd/sd-daemon.h>
69#endif
70
71#ifdef WITH_TLS
72# include <freeradius-devel/tls/version.h>
73#endif
74
75char const *radiusd_version = RADIUSD_VERSION_BUILD("FreeRADIUS");
76static pid_t radius_pid;
77static char const *program = NULL;
78
79/*
80 * Configuration items.
81 */
82
83/*
84 * Static functions.
85 */
86static void usage(int status);
87
88static void sig_fatal (int);
89#ifdef SIGHUP
90static void sig_hup (int);
91#endif
92
93/** Configure talloc debugging features
94 *
95 * @param[in] config The main config.
96 * @return
97 * - 1 on config conflict.
98 * - 0 on success.
99 * - -1 on error.
100 */
102{
103 if (config->spawn_workers) {
104 if (config->talloc_memory_report) {
105 fr_strerror_printf("talloc_memory_report requires single threaded mode (-s | -X)");
106 return 1;
107 }
108 return 0;
109 }
110
111 if (!config->talloc_memory_report) {
112 talloc_disable_null_tracking();
113 return 0;
114 }
115
116 talloc_enable_null_tracking();
117
118 return 0;
119}
120
121
122/** Create module and xlat per-thread instances
123 *
124 */
125static int thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el, UNUSED void *uctx)
126{
127 if (modules_rlm_thread_instantiate(ctx, el) < 0) return -1;
128
129 if (virtual_servers_thread_instantiate(ctx, el) < 0) return -1;
130
131 if (xlat_thread_instantiate(ctx, el) < 0) return -1;
132#ifdef WITH_TLS
133 if (fr_openssl_thread_init(main_config->openssl_async_pool_init,
134 main_config->openssl_async_pool_max) < 0) return -1;
135#endif
136 return 0;
137}
138
139/** Explicitly cleanup module/xlat resources
140 *
141 */
142static void thread_detach(UNUSED void *uctx)
143{
145
147
149}
150
151#define EXIT_WITH_FAILURE \
152do { \
153 ret = EXIT_FAILURE; \
154 goto cleanup; \
155} while (0)
156
157#define EXIT_WITH_SUCCESS \
158do { \
159 ret = EXIT_SUCCESS; \
160 goto cleanup; \
161} while (0)
162
163#define EXIT_WITH_PERROR \
164do { \
165 fr_perror("%s", program); \
166 EXIT_WITH_FAILURE; \
167} while (0)
168
170
172{
174
175 (void) fr_timer_in(tl, tl, &fr_time_sync_ev, when, false, fr_time_sync_event, NULL);
176 (void) fr_time_sync();
177}
178
179#ifndef NDEBUG
180/** Encourage the server to exit after a period of time
181 *
182 * @param[in] tl The main loop.
183 * @param[in] now Current time. Should be 0, when adding the event.
184 * @param[in] uctx Pointer to a fr_time_delta_t indicating how long
185 * the server should run before exit.
186 */
187static void fr_exit_after(fr_timer_list_t *tl, fr_time_t now, void *uctx)
188{
189 static fr_timer_t *ev;
190
191 fr_time_delta_t exit_after = *(fr_time_delta_t *)uctx;
192
193 if (fr_time_eq(now, fr_time_wrap(0))) {
194 if (fr_timer_in(tl, tl, &ev, exit_after, false, fr_exit_after, uctx) < 0) {
195 PERROR("%s: Failed inserting exit event", program);
196 }
197 return;
198 }
199
201}
202#endif
203
204#ifdef HAVE_CAPABILITY_H
205#define DUMP_CAPABILITIES(_phase) \
206{ \
207 char *cap_str; \
208 if (fr_cap_set_to_str(talloc_autofree_context(), &cap_str) < 0) { \
209 PWARN("Failed retrieving %s capabilities", _phase); \
210 } else { \
211 INFO("%s capabilities: %s", _phase, cap_str); \
212 talloc_free(cap_str); \
213 } \
214}
215#else
216#define DUMP_CAPABILITIES(_phase)
217#endif
218
219/** Entry point for the daemon
220 *
221 * @hidecallgraph
222 */
223int main(int argc, char *argv[])
224{
225 int status;
226 int c;
227 bool display_version = false;
228 bool radmin = false;
229 int from_child[2] = {-1, -1};
230 fr_schedule_t *sc = NULL;
231 int ret = EXIT_SUCCESS;
232
233 TALLOC_CTX *global_ctx = NULL;
234 main_config_t *config = NULL;
235 bool talloc_memory_report = false;
236
237 bool confdir_set = false;
238
239 size_t pool_size = 0;
240 void *pool_page_start = NULL;
241 size_t pool_page_len = 0;
242 bool do_mprotect;
243 int std_fd[3];
244
245#ifndef NDEBUG
246 fr_time_delta_t exit_after = fr_time_delta_wrap(0);
247#endif
248 /*
249 * Must be called first, so the handler is called last
250 */
252
253 /*
254 * Setup talloc callbacks so we get useful errors
255 */
256 (void) fr_talloc_fault_setup();
257
258 /*
259 * Set some default values
260 */
261 program = strrchr(argv[0], FR_DIR_SEP);
262 if (!program) {
263 program = argv[0];
264 } else {
265 program++;
266 }
267
268 /*
269 * We probably don't want to free the talloc global_ctx context
270 * directly, so we'll allocate a new context beneath it, and
271 * free that before any leak reports.
272 */
273 {
274 char *env;
275
276 /*
277 * If a FR_GLOBAL_POOL value is provided and
278 * is of a valid size, we pre-allocate a global
279 * memory pool, and mprotect() it once we're done
280 * parsing the global config.
281 *
282 * This lets us catch stray writes into global
283 * memory.
284 */
285 env = getenv("FR_GLOBAL_POOL");
286 if (env) {
287 if (fr_size_from_str(&pool_size, &FR_SBUFF_IN_STR(env)) < 0) {
288 fr_perror("%s: Invalid pool size string \"%s\"", program, env);
290 }
291
292 /*
293 * Pre-allocate a global memory pool for the static
294 * config to exist in. We mprotect() this later to
295 * catch any stray writes.
296 */
298 &pool_page_start, &pool_page_len, 0, pool_size);
299 do_mprotect = true;
300 } else {
301 global_ctx = talloc_new(talloc_autofree_context());
302 do_mprotect = false;
303 }
304
305 if (!global_ctx) {
306 fprintf(stderr, "Failed allocating global context\n");
308 }
309 }
310
311 /*
312 * Allocate the main config structure.
313 * It's allocated so we can hang talloced buffers off it.
314 */
316 if (!config) {
317 fprintf(stderr, "Failed allocating main config");
319 }
320
322
323 config->daemonize = true;
324 config->spawn_workers = true;
325
326 fr_debug_lvl = 0;
328
329 /*
330 * Don't put output anywhere until we get told a little
331 * more.
332 */
334 default_log.fd = -1;
337
338 /*
339 * Set the panic action and enable other debugging facilities
340 */
341 if (fr_fault_setup(global_ctx, getenv("PANIC_ACTION"), argv[0]) < 0) {
342 fr_perror("%s: Failed installing fault handlers... continuing", program);
343 }
344
345 /* Process the options. */
346 while ((c = getopt(argc, argv, "Cd:D:e:fhl:Mmn:PrsS:tTvxX")) != -1) switch (c) {
347 case 'C':
348 check_config = true;
349 config->spawn_workers = false;
350 config->daemonize = false;
351 break;
352
353 case 'd':
355 confdir_set = true;
356 break;
357
358 case 'D':
360 break;
361
362 case 'e':
363 /*
364 * For non-debug builds, accept '-e', but ignore it.
365 */
366#ifndef NDEBUG
367 exit_after = fr_time_delta_from_sec(atoi(optarg));
368#endif
369 break;
370
371 case 'f':
372 config->daemonize = false;
373 break;
374
375 case 'h':
376 usage(EXIT_SUCCESS);
377 break;
378
379 case 'l':
380 if (strcmp(optarg, "stdout") == 0) goto do_stdout;
381
382 config->log_file = talloc_strdup(global_ctx, optarg);
385 default_log.fd = open(config->log_file, O_WRONLY | O_APPEND | O_CREAT, 0640);
386 if (default_log.fd < 0) {
387 fprintf(stderr, "%s: Failed to open log file %s: %s\n",
388 program, config->log_file, fr_syserror(errno));
390 }
391 break;
392
393 case 'm':
394 config->allow_multiple_procs = true;
395 break;
396
397 case 'M':
398 config->talloc_memory_report = true;
399 break;
400
401 case 'n':
403 break;
404
405 case 'P': /* Force the PID to be written, even in -f mode */
406 config->write_pid = true;
407 break;
408
409 case 'r': /* internal radmin-style control interface */
410 config->spawn_workers = false;
411 config->daemonize = false;
412 radmin = true;
413 break;
414
415 case 's': /* Single process mode */
416 config->spawn_workers = false;
417 config->daemonize = false;
418 break;
419
420 case 'S': /* Migration support */
421#if 0
422 if (main_config_parse_option(optarg) < 0) {
423 fprintf(stderr, "%s: Unknown configuration option '%s'\n",
424 program, optarg);
426 }
427#endif
428 break;
429
430 case 't': /* no child threads */
431 config->spawn_workers = false;
432 break;
433
434 case 'T': /* enable timestamps */
436 break;
437
438 case 'v':
439 display_version = true;
440 break;
441
442 case 'X':
443 config->spawn_workers = false;
444 config->daemonize = false;
445 fr_debug_lvl += 2;
446
447 do_stdout:
449 default_log.fd = STDOUT_FILENO;
450 break;
451
452 case 'x':
453 fr_debug_lvl++;
454 break;
455
456 default:
457 usage(EXIT_FAILURE);
458 break;
459 }
460
462
463 /*
464 * Allow the configuration directory to be set from an
465 * environment variable. This allows tests to change the
466 * configuration directory without changing the scripts
467 * being executed.
468 */
469 if (!confdir_set) {
470 char const *confdir = getenv("FREERADIUS_CONFIG_DIR");
471
472 if (confdir) main_config_confdir_set(config, confdir);
473 }
474
475 /*
476 * We've now got enough information to check to see
477 * if another process is running with the same config.
478 */
479 config->debug_level = fr_debug_lvl;
480
481 /*
482 * Mismatch between the binary and the libraries it depends on.
483 */
486 }
487
489
490#ifdef WITH_TLS
491 /*
492 * Mismatch between build time OpenSSL and linked SSL, better to die
493 * here than segfault later.
494 */
496
497 /*
498 * Initialising OpenSSL once, here, is safer than having individual modules do it.
499 * Must be called before display_version to ensure relevant engines are loaded.
500 *
501 * fr_openssl_init() must be called before *ANY* OpenSSL functions are used, which is why
502 * it's called so early.
503 */
504 if (fr_openssl_init() < 0) EXIT_WITH_FAILURE;
505#endif
506
507 /*
508 * According to the talloc peeps, no two threads may modify any part of
509 * a ctx tree with a common root without synchronisation.
510 *
511 * So we can't run with a null context and threads.
512 */
513 if (talloc_config_set(config) != 0) {
515 }
516
517 /*
518 * Better here, so it doesn't matter whether we get passed -xv or -vx.
519 */
520 if (display_version) {
521 if (fr_debug_lvl == 0) fr_debug_lvl = 1;
523 default_log.fd = STDOUT_FILENO;
524
525 INFO("%s - %s", program, radiusd_version);
528 }
529
531
532 /*
533 * Under linux CAP_SYS_PTRACE is usually only available before setuid/setguid,
534 * so we need to check whether we can attach before calling those functions
535 * (in main_config_init()).
536 */
538
539 /*
540 * Write the PID always if we're running as a daemon.
541 */
542 if (config->daemonize) config->write_pid = true;
543
544 /*
545 * Initialize the DL infrastructure, which is used by the
546 * config file parser. Note that we pass an empty path
547 * here, as we haven't yet read the configuration file.
548 */
549 modules_init(NULL);
550
551 /*
552 * Initialise the top level dictionary hashes which hold
553 * the protocols.
554 */
555 if (!fr_dict_global_ctx_init(NULL, true, config->dict_dir)) {
557 }
558
559#ifdef WITH_TLS
560 if (fr_tls_dict_init() < 0) {
562 }
563#endif
564
565 /*
566 * Setup the global structures for module lists
567 */
568 if (modules_rlm_init() < 0) {
570 }
571
572 if (virtual_servers_init() < 0) {
574 }
575
576 /*
577 * Load dictionary attributes used
578 * for requests.
579 */
580 if (request_global_init() < 0) {
582 }
583
584 /*
585 * The radmin functions need to write somewhere.
586 *
587 * The log functions redirect stdin and stdout to /dev/null, so that exec'd programs can't mangle
588 * them. But radmin needs to be able to use them.
589 */
590#define RDUP(_x) \
591do { \
592 if ((std_fd[_x] = dup(_x)) < 0) EXIT_WITH_PERROR; \
593 if (fr_cloexec(std_fd[_x]) < 0) EXIT_WITH_PERROR; \
594} while (0)
595
596 if (radmin) {
597 RDUP(STDIN_FILENO);
598 RDUP(STDOUT_FILENO);
599 RDUP(STDERR_FILENO);
600 }
601
602 /*
603 * Read the configuration files, BEFORE doing anything else.
604 */
606
607 /*
608 * Suppress secrets if asked, and if at a low debug level.
609 */
610 default_log.suppress_secrets = config->suppress_secrets && (fr_debug_lvl <= 2);
611
612 /*
613 * Check we're the only process using this config.
614 */
615 if (!config->allow_multiple_procs && !check_config) {
617 case 0: /* No other processes running */
618 break;
619
620 case -1: /* Permissions error - fail open */
621 PWARN("%s: Process concurrency checks disabled", program);
622 break;
623
624 case 1:
625 default: /* All other errors */
627 }
628 }
629
630 /*
631 * Set panic_action from the main config if one wasn't specified in the
632 * environment.
633 */
634 if (config->panic_action && !getenv("PANIC_ACTION") &&
635 (fr_fault_setup(global_ctx, config->panic_action, argv[0]) < 0)) {
636 fr_perror("%s: Failed configuring panic action", program);
638 }
639
640 /*
641 * This is very useful in figuring out why the panic_action didn't fire.
642 */
644
645 /*
646 * Track configuration versions. This lets us know if the configuration changed.
647 */
648 if (fr_debug_lvl) {
649 uint8_t digest[16];
650
651 cf_md5_final(digest);
652
653 digest[6] &= 0x0f; /* ver is 0b0100 at bits 48:51 */
654 digest[6] |= 0x40;
655 digest[8] &= ~0xc0; /* var is 0b10 at bits 64:65 */
656 digest[8] |= 0x80;
657
658 /*
659 * UUIDv4 format: 4-2-2-2-6
660 */
661 INFO("Configuration version: %02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
662 digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7],
663 digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15]);
664 }
665
666 /*
667 * Call this again now we've loaded the configuration. Yes I know...
668 */
669 if (talloc_config_set(config) != 0) {
671 }
672
673 /*
674 * Check for vulnerabilities in the version of libssl were linked against.
675 */
676#ifdef WITH_TLS
677# ifdef ENABLE_OPENSSL_VERSION_CHECK
678 if (fr_openssl_version_check(config->allow_vulnerable_openssl) < 0) EXIT_WITH_FAILURE;
679# endif
680
681 /*
682 * Toggle FIPS mode
683 */
684 if (config->openssl_fips_mode_is_set &&
685 (fr_openssl_fips_mode(config->openssl_fips_mode) < 0)) EXIT_WITH_FAILURE;
686#endif
687
688 /*
689 * The systemd watchdog enablement must be checked before we
690 * daemonize, but the watchdog notifications can come from any
691 * process.
692 */
693#ifdef HAVE_SYSTEMD_WATCHDOG
694 if (!check_config) main_loop_set_sd_watchdog_interval();
695#else
696 /*
697 * If the default systemd unit file is used, but the server wasn't
698 * built with support for systemd, the status returned by systemctl
699 * will stay permanently as "activating".
700 *
701 * We detect this condition and warn about it here, using the
702 * presence of the NOTIFY_SOCKET environmental variable to determine
703 * whether we're running under systemd.
704 */
705 if (getenv("NOTIFY_SOCKET")) INFO("Built without support for systemd watchdog, but running under systemd");
706#endif
707
708 /*
709 * Don't allow radmin when checking the config.
710 */
711 if (check_config) radmin = false;
712
713 if (fr_radmin_start(config, radmin, std_fd) < 0) EXIT_WITH_FAILURE;
714
715 /*
716 * Disconnect from session
717 */
718 if (config->daemonize) {
719 pid_t pid;
720 int devnull;
721
722 DUMP_CAPABILITIES("pre-fork");
723
724 /*
725 * Really weird things happen if we leave stdin open and call things like
726 * system() later.
727 *
728 * main_config_init() already calls fr_log_init_legacy(), which closes stdout/stderr.
729 */
730 devnull = open("/dev/null", O_RDWR);
731 if (devnull < 0) {
732 ERROR("Failed opening /dev/null: %s", fr_syserror(errno));
734 }
735 dup2(devnull, STDIN_FILENO);
736
737 close(devnull);
738
739 if (pipe(from_child) != 0) {
740 ERROR("Couldn't open pipe for child status: %s", fr_syserror(errno));
742 }
743
744 pid = fork();
745 if (pid < 0) {
746 ERROR("Couldn't fork: %s", fr_syserror(errno));
748 }
749
750 /*
751 * The parent exits, so the child can run in the background.
752 *
753 * As the child can still encounter an error during initialisation
754 * we do a blocking read on a pipe between it and the parent.
755 *
756 * Just before entering the event loop the child will send a success
757 * or failure message to the parent, via the pipe.
758 */
759 if (pid > 0) {
760 uint8_t child_ret;
761 int stat_loc;
762
763 /* So the pipe is correctly widowed if the child exits */
764 close(from_child[1]);
765
766 /*
767 * The child writes a 0x01 byte on success, and closes
768 * the pipe on error.
769 */
770 if ((read(from_child[0], &child_ret, 1) <= 0)) child_ret = 0;
771
772 /* For cleanliness... */
773 close(from_child[0]);
774
775 /* Don't turn children into zombies */
776 if (child_ret == 0) {
777 waitpid(pid, &stat_loc, WNOHANG);
779 }
780
781#ifdef HAVE_SYSTEMD
782 /*
783 * Update the systemd MAINPID to be our child,
784 * as the parent is about to exit.
785 */
786 sd_notifyf(0, "MAINPID=%lu", (unsigned long)pid);
787#endif
788
789 goto cleanup;
790 /*
791 * The child needs to increment the semaphore as the parent
792 * is going to exit, and it will decrement the semaphore.
793 */
794 } else if (pid == 0) {
796 PWARN("%s: Failed incrementing exclusive proc semaphore in child", program);
797 }
798 }
799
800 /* so the pipe is correctly widowed if the parent exits?! */
801 close(from_child[0]);
802#ifdef HAVE_SETSID
803 setsid();
804#endif
805
806 DUMP_CAPABILITIES("post-fork");
807 } else {
808 DUMP_CAPABILITIES("pre-suid-down");
809 }
810
811 /*
812 * Ensure that we're using the CORRECT pid after forking, NOT the one
813 * we started with.
814 */
815 radius_pid = getpid();
816
817 /*
818 * Initialise the interpreter, registering operations.
819 */
821
823
824 /*
825 * Everything seems to have loaded OK, exit gracefully.
826 */
827 if (check_config) {
828 DEBUG("Configuration appears to be OK");
829 goto cleanup;
830 }
831
832 /*
833 * Initialise the SNMP stats structures
834 */
835 if (fr_snmp_init() < 0) {
836 PERROR("Failed initialising SNMP");
838 }
839
840 /*
841 * Initialize the global event loop which handles things like
842 * systemd.
843 *
844 * This has to be done post-fork in case we're using kqueue, where the
845 * queue isn't inherited by the child process.
846 */
847 if (main_loop_init() < 0) {
848 PERROR("Failed initialising main event loop");
850 }
851
852 /*
853 * Start the network / worker threads.
854 */
855 {
856 fr_event_list_t *el = NULL;
857 fr_schedule_config_t *schedule;
858
859 MEM(schedule = talloc_zero(global_ctx, fr_schedule_config_t));
860 schedule->max_workers = config->max_workers;
861 schedule->max_networks = config->max_networks;
862 schedule->stats_interval = config->stats_interval;
863
864 schedule->network.max_outstanding = config->worker.max_requests;
865 schedule->worker = config->worker;
866 schedule->cs = cf_section_find(config->root_cs, "thread", CF_IDENT_ANY);
867
868 /*
869 * Single server mode: use the global event list.
870 * Otherwise, each network thread will create
871 * its own event list.
872 */
873 if (!config->spawn_workers) {
875 }
876
877 /*
878 * Fix spurious messages
879 */
883 if (!sc) {
884 PERROR("Failed starting the scheduler");
886 }
887
888 /*
889 * Tell the virtual servers to open their sockets.
890 */
892 }
893
894 /*
895 * At this point, no one has any business *ever* going
896 * back to root uid.
897 */
899
900 /*
901 * Move the current working directory to a place where it
902 * can't hurt anything.
903 */
905 if (chdir(main_config->chdir) < 0) {
906 ERROR("Failed changing working to %s: %s", main_config->chdir, fr_syserror(errno));
908 }
909 }
910
911 DUMP_CAPABILITIES("post-suid-down");
912
913 /*
914 * Dropping down may change the RLIMIT_CORE value, so
915 * reset it back to what to should be here.
916 */
918
919 /*
920 * If we're debugging, then a CTRL-C will cause the server to die
921 * immediately. Use SIGTERM to shut down the server cleanly in
922 * that case.
923 */
924 if (fr_set_signal(SIGINT, sig_fatal) < 0) {
925 set_signal_error:
926 PERROR("Failed installing signal handler");
928 }
929
930#ifdef SIGQUIT
931 if (fr_set_signal(SIGQUIT, sig_fatal) < 0) goto set_signal_error;
932#endif
933
934 /*
935 * Now that we've set everything up, we can install the signal
936 * handlers. Before this, if we get any signal, we don't know
937 * what to do, so we might as well do the default, and die.
938 */
939#ifdef SIGPIPE
940 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
941 ERROR("Failed ignoring SIGPIPE: %s", fr_syserror(errno));
942 goto cleanup;
943 }
944#endif
945
946 if (fr_set_signal(SIGHUP, sig_hup) < 0) goto set_signal_error;
947 if (fr_set_signal(SIGTERM, sig_fatal) < 0) goto set_signal_error;
948
949 /*
950 * Write the PID after we've forked, so that we write the correct one.
951 */
952 if (config->write_pid) {
953 FILE *fp;
954
955 fp = fopen(config->pid_file, "w");
956 if (fp != NULL) {
957 /*
958 * @fixme What about following symlinks,
959 * and having it over-write a normal file?
960 */
961 fprintf(fp, "%d\n", (int) radius_pid);
962 fclose(fp);
963 } else {
964 ERROR("Failed creating PID file %s: %s", config->pid_file, fr_syserror(errno));
966 }
967 }
968
969 trigger(NULL, NULL, NULL, "server.start", false, NULL);
970
971 /*
972 * Inform the parent (who should still be waiting) that the rest of
973 * initialisation went OK, and that it should exit with a 0 status.
974 * If we don't get this far, then we just close the pipe on exit, and the
975 * parent gets a read failure.
976 */
977 if (config->daemonize) {
978 if (write(from_child[1], "\001", 1) < 0) {
979 WARN("Failed informing parent of successful start: %s",
980 fr_syserror(errno));
981 }
982 close(from_child[1]);
983 }
984
985 /*
986 * Clear the libfreeradius error buffer.
987 */
989
990 /*
991 * Prevent anything from modifying the dictionaries
992 * they're now immutable.
993 */
995
996 /*
997 * Protect global memory - If something attempts
998 * to write to this memory we get a SIGBUS.
999 */
1000 if (do_mprotect) {
1001 if (mprotect(pool_page_start, pool_page_len, PROT_READ) < 0) {
1002 ERROR("Protecting global memory failed: %s", fr_syserror(errno));
1004 }
1005 DEBUG("Global memory protected");
1006 }
1007
1009#ifndef NDEBUG
1010 if (fr_time_delta_ispos(exit_after)) fr_exit_after(main_loop_event_list()->tl, fr_time_wrap(0), &exit_after);
1011#endif
1012 /*
1013 * Process requests until HUP or exit.
1014 */
1015 INFO("Ready to process requests"); /* we were actually ready a while ago, but oh well */
1016 while ((status = main_loop_start()) == 0x80) {
1018 }
1019
1020 /*
1021 * Ignore the TERM signal: we're about to die.
1022 */
1023 if (unlikely(signal(SIGTERM, SIG_IGN) == SIG_ERR)) {
1024 ERROR("Failed blocking SIGTERM, we may receive spurious signals: %s",
1025 fr_syserror(errno));
1026 }
1027
1028 /*
1029 * Unprotect global memory
1030 */
1031 if (do_mprotect) {
1032 if (mprotect(pool_page_start, pool_page_len,
1033 PROT_READ | PROT_WRITE) < 0) {
1034 ERROR("Unprotecting global memory failed: %s", fr_syserror(errno));
1036 }
1037 DEBUG("Global memory unprotected");
1038 }
1039
1040 if (status < 0) {
1041 PERROR("Exiting due to internal error");
1042 ret = EXIT_FAILURE;
1043 } else {
1044 INFO("Exiting normally");
1045 ret = EXIT_SUCCESS;
1046 }
1047
1049
1050 /*
1051 * Fire signal and stop triggers after ignoring SIGTERM, so handlers are
1052 * not killed with the rest of the process group, below.
1053 */
1054 if (status == 2) trigger(NULL, NULL, NULL, "server.signal.term", true, NULL);
1055 trigger(NULL, NULL, NULL, "server.stop", false, NULL);
1056
1057 /*
1058 * Stop the scheduler, this signals the network and worker threads
1059 * to exit gracefully. fr_schedule_destroy only returns once all
1060 * threads have been joined.
1061 */
1062 (void) fr_schedule_destroy(&sc);
1063
1064 /*
1065 * We're exiting, so we can delete the PID file.
1066 * (If it doesn't exist, we can ignore the error returned by unlink)
1067 */
1068 if (config->daemonize) {
1069 DEBUG3("Unlinking PID file %s", config->pid_file);
1070 unlink(config->pid_file);
1071 }
1072
1073 /*
1074 * Free memory in an explicit and consistent order
1075 *
1076 * We could let everything be freed by the global_ctx
1077 * context, but in some cases there are odd interactions
1078 * with destructors that may cause double frees and
1079 * SEGVs.
1080 */
1081 if (!config->spawn_workers) {
1083
1086 }
1087
1089
1090 /*
1091 * Send a TERM signal to all associated processes
1092 * (including us, which gets ignored.)
1093 *
1094 * This _shouldn't_ be needed, but may help with
1095 * processes created by the exec code or triggers.
1096 */
1097 if (config->spawn_workers) {
1098 INFO("All threads have exited, sending SIGTERM to remaining children");
1099
1100 /*
1101 * If pid is negative, but not -1, sig
1102 * shall be sent to all processes
1103 * (excluding an unspecified set of system processes)
1104 * whose process group ID is equal to the absolute value
1105 * of pid, and for which the process has permission
1106 * to send a signal.
1107 */
1108 kill(-getpgid(radius_pid), SIGTERM);
1109 }
1110
1111 /*
1112 * Remove the semaphore, allowing other processes
1113 * to start. We do this before the cleanup label
1114 * as the parent process MUST NOT call this
1115 * function as it exits, otherwise the semaphore
1116 * is removed and there's no exclusivity.
1117 */
1119
1120cleanup:
1121 /*
1122 * This may not have been done earlier if we're
1123 * exiting due to a startup error.
1124 */
1125 (void) fr_schedule_destroy(&sc);
1126
1127 /*
1128 * Ensure all thread local memory is cleaned up
1129 * before we start cleaning up global resources.
1130 * This is necessary for single threaded mode
1131 * to ensure that thread local resources that
1132 * depend on global resources are freed at the
1133 * appropriate time.
1134 */
1136
1137 server_free();
1138
1139#ifdef WITH_TLS
1140 fr_openssl_free(); /* Cleanup any memory alloced by OpenSSL and placed into globals */
1141#endif
1142
1143 if (config) talloc_memory_report = config->talloc_memory_report; /* Grab this before we free the config */
1144
1145 /*
1146 * Virtual servers need to be freed before modules
1147 * as state entries containing data with module-specific
1148 * destructors may exist.
1149 */
1151
1152 /*
1153 * Free modules, this needs to be done explicitly
1154 * because some libraries used by modules use atexit
1155 * handlers registered after ours, and they may deinit
1156 * themselves before we free the modules and cause
1157 * crashes on exit.
1158 */
1160
1161#ifdef WITH_TLS
1162 fr_tls_dict_free();
1163#endif
1164
1165 /*
1166 * And now nothing should be left anywhere except the
1167 * parsed configuration items.
1168 */
1170
1171 /*
1172 * Cleanup everything else
1173 */
1174 if (talloc_free(global_ctx) < 0) {
1175#ifndef NDEBUG
1176 fr_perror("program");
1177 ret = EXIT_FAILURE;
1178#endif
1179 }
1180
1181 /*
1182 * Anything not cleaned up by the above is allocated in
1183 * the NULL top level context, and is likely leaked memory.
1184 */
1185 if (talloc_memory_report) fr_log_talloc_report(NULL);
1186
1187 /*
1188 * If we're running under LSAN, try and SUID back up so
1189 * we don't inteferere with the onexit() handler.
1190 */
1192 fr_strerror_clear(); /* clear error buffer */
1193
1194 /*
1195 * Ensure our atexit handlers run before any other
1196 * atexit handlers registered by third party libraries.
1197 */
1199
1200 return ret;
1201}
1202
1203/*
1204 * Display the syntax for starting this program.
1205 */
1206static NEVER_RETURNS void usage(int status)
1207{
1208 FILE *output = status ? stderr : stdout;
1209
1210 fprintf(output, "Usage: %s [options]\n", program);
1211 fprintf(output, "Options:\n");
1212 fprintf(output, " -C Check configuration and exit.\n");
1213 fprintf(output, " -d <confdir> Configuration file directory (defaults to " CONFDIR ").\n");
1214 fprintf(output, " -D <dictdir> Set main dictionary directory (defaults to " DICTDIR ").\n");
1215#ifndef NDEBUG
1216 fprintf(output, " -e <seconds> Exit after the specified number of seconds. Useful for diagnosing \"crash-on-exit\" issues.\n");
1217#endif
1218 fprintf(output, " -f Run as a foreground process, not a daemon.\n");
1219 fprintf(output, " -h Print this help message.\n");
1220 fprintf(output, " -l <log_file> Logging output will be written to this file.\n");
1221#ifndef NDEBUG
1222 fprintf(output, " -L <size> When running in memory debug mode, set a hard limit on talloced memory\n");
1223#endif
1224 fprintf(output, " -n <name> Read ${confdir}/name.conf instead of ${confdir}/%s.conf.\n", program);
1225 fprintf(output, " -m Allow multiple processes reading the same %s.conf to exist simultaneously.\n", program);
1226#ifndef NDEBUG
1227 fprintf(output, " -M Enable talloc memory debugging, and issue a memory report when the server terminates\n");
1228#endif
1229 fprintf(output, " -P Always write out PID, even with -f.\n");
1230 fprintf(output, " -s Do not spawn child processes to handle requests (same as -ft).\n");
1231
1232 /*
1233 * Place-holder in case we need it. Should be removed before the release.
1234 */
1235// fprintf(output, " -S <flag> Set migration flags to assist with upgrades from version 3.\n");
1236 fprintf(output, " -t Disable threads.\n");
1237 fprintf(output, " -T Prepend timestamps to log messages.\n");
1238 fprintf(output, " -v Print server version information.\n");
1239 fprintf(output, " -X Turn on full debugging (similar to -tfxxl stdout).\n");
1240 fprintf(output, " -x Turn on additional debugging (-xx gives more debugging).\n");
1241 fr_exit(status);
1242}
1243
1244
1245/*
1246 * We got a fatal signal.
1247 */
1248static void sig_fatal(int sig)
1249{
1250 static volatile sig_atomic_t last_sig;
1251
1252 if (getpid() != radius_pid) _exit(sig);
1253
1254 /*
1255 * Suppress duplicate signals.
1256 *
1257 * For some reason on macOS we get multiple signals
1258 * for the same event (SIGINT).
1259 *
1260 * ...this also fixes the problem of the user hammering
1261 * Ctrl-C and causing ungraceful exits as we try and
1262 * write out signals to a pipe that's already closed.
1263 */
1264 if (sig == last_sig) return;
1265 last_sig = sig;
1266
1267 switch (sig) {
1268 case SIGTERM:
1270 break;
1271
1272 case SIGINT:
1273#ifdef SIGQUIT
1274 case SIGQUIT:
1275#endif
1277 break;
1278
1279 default:
1280 fr_exit(sig);
1281 }
1282}
1283
1284#ifdef SIGHUP
1285/*
1286 * We got the hangup signal.
1287 * Re-read the configuration files.
1288 */
1289static void sig_hup(UNUSED int sig)
1290{
1291 reset_signal(SIGHUP, sig_hup);
1292
1294}
1295#endif
int fr_atexit_global_setup(void)
Setup the atexit handler, should be called at the start of a program's execution.
Definition atexit.c:159
int fr_atexit_global_trigger_all(void)
Cause all global free triggers to fire.
Definition atexit.c:285
#define fr_atexit_thread_trigger_all(...)
Definition atexit.h:232
void fr_radmin_stop(void)
Definition radmin.c:1147
int fr_radmin_start(main_config_t *config, bool cli, int std_fd[static 3])
Definition radmin.c:1079
#define RCSID(id)
Definition build.h:488
#define NEVER_RETURNS
Should be placed before the function return type.
Definition build.h:316
#define unlikely(_x)
Definition build.h:384
#define UNUSED
Definition build.h:318
bool check_config
Definition cf_file.c:61
void cf_md5_final(uint8_t *digest)
Definition cf_file.c:3310
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
Definition cf_util.c:1027
#define CF_IDENT_ANY
Definition cf_util.h:75
char const * fr_debug_state_to_msg(fr_debug_state_t state)
Return current value of debug_state.
Definition debug.c:512
int fr_log_talloc_report(TALLOC_CTX const *ctx)
Generate a talloc memory report for a context and print to stderr/stdout.
Definition debug.c:962
int fr_reset_dumpable(void)
Reset dumpable state to previously configured value.
Definition debug.c:743
int fr_fault_setup(TALLOC_CTX *ctx, char const *cmd, char const *program)
Registers signal handlers to execute panic_action on fatal signal.
Definition debug.c:1056
int fr_get_lsan_state(void)
Definition debug.c:232
void fr_debug_state_store(void)
Should be run before using setuid or setgid to get useful results.
Definition debug.c:492
void fr_talloc_fault_setup(void)
Register talloc fault handlers.
Definition debug.c:1037
fr_debug_state_t fr_debug_state
Whether we're attached to by a debugger.
Definition debug.c:74
#define fr_exit(_x)
Exit, producing a log message in debug builds.
Definition debug.h:230
#define MEM(x)
Definition debug.h:46
void dependency_version_print(void)
Definition dependency.c:365
int rad_check_lib_magic(uint64_t magic)
Check if the application linking to the library has the correct magic number.
Definition dependency.c:70
#define ERROR(fmt,...)
Definition dhcpclient.c:40
#define DEBUG(fmt,...)
Definition dhcpclient.c:38
static NEVER_RETURNS void usage(void)
Definition dhcpclient.c:113
fr_dict_t * fr_dict_unconst(fr_dict_t const *dict)
Coerce to non-const.
Definition dict_util.c:4903
void fr_dict_global_ctx_read_only(void)
Mark all dictionaries and the global dictionary ctx as read only.
Definition dict_util.c:4819
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:4928
fr_dict_gctx_t * fr_dict_global_ctx_init(TALLOC_CTX *ctx, bool free_at_exit, char const *dict_dir))
Initialise the global protocol hashes.
Definition dict_util.c:4713
talloc_free(hp)
uint32_t max_outstanding
Definition network.h:46
int server_init(CONF_SECTION *cs, char const *conf_dir, fr_dict_t *dict)
Initialize src/lib/server/.
Definition base.c:42
void server_free(void)
Free src/lib/server/.
Definition base.c:137
#define PERROR(_fmt,...)
Definition log.h:228
#define DEBUG3(_fmt,...)
Definition log.h:266
#define PWARN(_fmt,...)
Definition log.h:227
void rad_suid_up(void)
Definition util.c:767
bool rad_suid_is_down_permanent(void)
Return whether we've permanently dropped root privileges.
Definition util.c:788
void(*)(int) reset_signal(int signo, void(*func)(int))
Definition util.c:49
void rad_suid_down_permanent(void)
Definition util.c:776
int unlang_global_init(void)
Definition base.c:158
#define fr_time()
Definition event.c:60
void fr_event_loop_exit(fr_event_list_t *el, int code)
Signal an event loop exit with the specified code.
Definition event.c:2355
Stores all information relating to an event list.
Definition event.c:377
int fr_debug_lvl
Definition log.c:40
static FILE * devnull
File handle for /dev/null.
Definition log.c:56
fr_log_t default_log
Definition log.c:288
@ L_DST_NULL
Discard log messages.
Definition log.h:80
@ L_DST_FILES
Log to a file on disk.
Definition log.h:76
@ L_DST_STDOUT
Log to stdout.
Definition log.h:75
@ L_TIMESTAMP_ON
Always log timestamps.
Definition log.h:87
int main_config_free(main_config_t **config)
main_config_t * main_config_alloc(TALLOC_CTX *ctx)
Allocate a main_config_t struct, setting defaults.
void main_config_exclusive_proc_done(UNUSED main_config_t const *config)
Clean up the semaphore when the main config is freed.
void main_config_hup(main_config_t *config)
int main_config_exclusive_proc(main_config_t *config)
Check to see if we're the only process using this configuration file (or PID file if specified)
void main_config_name_set_default(main_config_t *config, char const *name, bool overwrite_config)
Set the server name.
int main_config_init(main_config_t *config)
void main_config_confdir_set(main_config_t *config, char const *name)
Set the global radius config directory.
main_config_t const * main_config
Main server configuration.
Definition main_config.c:58
int main_config_exclusive_proc_child(UNUSED main_config_t const *config)
Increment the semaphore in the child process so that it's not released when the parent exits.
void main_config_dict_dir_set(main_config_t *config, char const *name)
Set the global dictionary directory.
int main_config_parse_option(char const *value)
char const * chdir
where to chdir() to when we start.
Main server configuration.
Definition main_config.h:51
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
void main_loop_signal_raise(int flag)
Definition main_loop.c:78
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_TERM
Definition main_loop.h:37
unsigned char uint8_t
int fr_set_signal(int sig, sig_t func)
Sets a signal handler using sigaction if available, else signal.
Definition misc.c:47
void modules_rlm_thread_detach(void)
Frees thread-specific data for all registered backend modules.
Definition module_rlm.c:960
int modules_rlm_free(void)
Cleanup all global structures.
int modules_rlm_thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el)
Allocates thread-specific data for all registered backend modules.
Definition module_rlm.c:973
int modules_rlm_init(void)
Initialise the module list structure.
static const conf_parser_t config[]
Definition base.c:163
#define WARN(fmt,...)
#define INFO(fmt,...)
Definition radict.c:63
int main(int argc, char *argv[])
Entry point for the daemon.
Definition radiusd.c:223
static void thread_detach(UNUSED void *uctx)
Explicitly cleanup module/xlat resources.
Definition radiusd.c:142
#define EXIT_WITH_PERROR
Definition radiusd.c:163
static void fr_exit_after(fr_timer_list_t *tl, fr_time_t now, void *uctx)
Encourage the server to exit after a period of time.
Definition radiusd.c:187
static void fr_time_sync_event(fr_timer_list_t *tl, UNUSED fr_time_t now, UNUSED void *uctx)
Definition radiusd.c:171
#define DUMP_CAPABILITIES(_phase)
Definition radiusd.c:216
#define RDUP(_x)
#define EXIT_WITH_SUCCESS
Definition radiusd.c:157
char const * radiusd_version
Definition radiusd.c:75
static int thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el, UNUSED void *uctx)
Create module and xlat per-thread instances.
Definition radiusd.c:125
static int talloc_config_set(main_config_t *config)
Configure talloc debugging features.
Definition radiusd.c:101
static void sig_fatal(int)
Definition radiusd.c:1248
static pid_t radius_pid
Definition radiusd.c:76
#define EXIT_WITH_FAILURE
Definition radiusd.c:151
static fr_timer_t * fr_time_sync_ev
Definition radiusd.c:169
static char const * program
Definition radiusd.c:77
static bool cleanup
Definition radsniff.c:59
int request_global_init(void)
Definition request.c:596
#define FR_SBUFF_IN_STR(_start)
fr_schedule_t * fr_schedule_create(TALLOC_CTX *ctx, fr_event_list_t *el, fr_log_t *logger, fr_log_lvl_t lvl, fr_schedule_thread_instantiate_t worker_thread_instantiate, fr_schedule_thread_detach_t worker_thread_detach, fr_schedule_config_t *config)
Create a scheduler and spawn the child threads.
Definition schedule.c:468
int fr_schedule_destroy(fr_schedule_t **sc_to_free)
Destroy a scheduler, and tell its child threads to exit.
Definition schedule.c:777
The scheduler.
Definition schedule.c:125
fr_network_config_t network
configuration for each network;
Definition schedule.h:68
uint32_t max_workers
number of worker threads
Definition schedule.h:65
CONF_SECTION * cs
root config section
Definition schedule.h:72
fr_worker_config_t worker
configuration for each worker
Definition schedule.h:67
fr_time_delta_t stats_interval
print channel statistics
Definition schedule.h:70
uint32_t max_networks
number of network threads
Definition schedule.h:64
fr_slen_t fr_size_from_str(size_t *out, fr_sbuff_t *in)
Parse a size string with optional unit.
Definition size.c:40
static const uchar sc[16]
Definition smbdes.c:115
int fr_snmp_init(void)
Initialise the tree of SNMP map structures used to attach callbacks to OIDs.
Definition snmp.c:1130
void modules_init(char const *lib_dir)
Perform global initialisation for modules.
Definition module.c:1909
fr_log_dst_t dst
Log destination.
Definition log.h:94
int fd
File descriptor to write messages to.
Definition log.h:109
fr_log_timestamp_t timestamp
Prefix log messages with timestamps.
Definition log.h:107
bool suppress_secrets
suppress secrets when printing to this destination
Definition log.h:105
char const * file
Path to log file.
Definition log.h:110
bool print_level
sometimes we don't want log levels printed
Definition log.h:103
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
static TALLOC_CTX * global_ctx
Definition talloc.c:32
TALLOC_CTX * talloc_page_aligned_pool(TALLOC_CTX *ctx, void **start, size_t *end_len, unsigned int headers, size_t size)
Return a page aligned talloc memory pool.
Definition talloc.c:308
#define talloc_autofree_context
The original function is deprecated, so replace it with our version.
Definition talloc.h:48
#define talloc_strdup(_ctx, _str)
Definition talloc.h:142
int fr_time_sync(void)
Get a new fr_time_monotonic_to_realtime value.
Definition time.c:102
int fr_time_start(void)
Initialize the local time.
Definition time.c:150
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 fr_time_eq(_a, _b)
Definition time.h:241
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_in(...)
Definition timer.h:87
int fr_openssl_version_consistent(void)
Definition version.c:246
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)
Execute a trigger - call an executable to process an event.
Definition trigger.c:155
static fr_event_list_t * el
void xlat_thread_detach(void)
Destroy any thread specific xlat instances.
Definition xlat_inst.c:486
int xlat_thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el)
Create thread specific instance tree and create thread instances.
Definition xlat_inst.c:441
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition strerror.c:732
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:576
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
int fr_check_lib_magic(uint64_t magic)
Check if the application linking to the library has the correct magic number.
Definition version.c:40
#define RADIUSD_VERSION_BUILD(_x)
Create a version string for a utility in the suite of FreeRADIUS utilities.
Definition version.h:58
#define RADIUSD_MAGIC_NUMBER
Definition version.h:81
int virtual_servers_open(fr_schedule_t *sc)
Open all the listen sockets.
void virtual_servers_thread_detach(void)
Free thread-specific data for all process modules and listeners.
int virtual_servers_init(void)
Performs global initialisation for the virtual server code.
int virtual_servers_free(void)
int virtual_servers_thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el)
Perform thread instantiation for all process modules and listeners.