MEDIUM: init: split the early initialization in its own function

There are some delicate chicken-and-egg situations in the initialization
code, because the init() function currently does way too much (it goes
as far as parsing the config) and due to this it must be started very
late. But it's also in charge of initializing a number of variables that
are needed in early boot (e.g. hostname/pid for error reporting, or
entropy for random generators).

This patch carefully extracts all the early code that depends on
absolutely nothing, and places it immediately after the STG_LOCK init
stage. The only possible failures at this stage are only allocation
errors and they continue to provoke an immediate exit().

Some environment variables, hostname, date, pid etc are retrieved at
this stage. The program's arguments are also copied there since they're
needed to be kept intact for the master process.
This commit is contained in:
Willy Tarreau 2022-02-17 17:45:58 +01:00
parent 3ebe4d989c
commit 34527d5354

View File

@ -1458,6 +1458,101 @@ unsigned int get_tainted()
return tainted_state;
}
/* This performs th every basic early initialization at the end of the PREPARE
* init stage. It may only assume that list heads are initialized, but not that
* anything else is correct. It will initialize a number of variables that
* depend on command line and will pre-parse the command line. If it fails, it
* directly exits.
*/
static void init_early(int argc, char **argv)
{
char *progname;
char *tmp;
int len;
/* First, let's initialize most global variables */
totalconn = actconn = listeners = stopping = 0;
killed = pid = 0;
global.maxsock = 10; /* reserve 10 fds ; will be incremented by socket eaters */
global.rlimit_memmax_all = HAPROXY_MEMMAX;
global.mode = MODE_STARTING;
/* if we were in mworker mode, we should restart in mworker mode */
if (getenv("HAPROXY_MWORKER_REEXEC") != NULL)
global.mode |= MODE_MWORKER;
/* initialize date, time, and pid */
tzset();
clock_init_process_date();
start_date = now;
pid = getpid();
/* Set local host name and adjust some environment variables.
* NB: POSIX does not make it mandatory for gethostname() to
* NULL-terminate the string in case of truncation, and at least
* FreeBSD appears not to do it.
*/
memset(hostname, 0, sizeof(hostname));
gethostname(hostname, sizeof(hostname) - 1);
/* preset some environment variables */
localpeer = strdup(hostname);
if (!localpeer || setenv("HAPROXY_LOCALPEER", localpeer, 1) < 0) {
ha_alert("Cannot allocate memory for local peer.\n");
exit(EXIT_FAILURE);
}
/* Initialize the random generators */
#ifdef USE_OPENSSL
/* Initialize SSL random generator. Must be called before chroot for
* access to /dev/urandom, and before ha_random_boot() which may use
* RAND_bytes().
*/
if (!ssl_initialize_random()) {
ha_alert("OpenSSL random data generator initialization failed.\n");
exit(EXIT_FAILURE);
}
#endif
ha_random_boot(argv); // the argv pointer brings some kernel-fed entropy
/* Some CPU affinity stuff may have to be initialized */
#ifdef USE_CPU_AFFINITY
{
int i;
ha_cpuset_zero(&cpu_map.proc);
ha_cpuset_zero(&cpu_map.proc_t1);
for (i = 0; i < MAX_THREADS; ++i) {
ha_cpuset_zero(&cpu_map.thread[i]);
}
}
#endif
/* keep a copy of original arguments for the master process */
old_argv = copy_argv(argc, argv);
if (!old_argv) {
ha_alert("failed to copy argv.\n");
exit(EXIT_FAILURE);
}
/* extract the program name from argv[0], it will be used for the logs
* and error messages.
*/
progname = *argv;
while ((tmp = strchr(progname, '/')) != NULL)
progname = tmp + 1;
len = strlen(progname);
progname = strdup(progname);
if (!progname) {
ha_alert("Cannot allocate memory for log_tag.\n");
exit(EXIT_FAILURE);
}
chunk_initlen(&global.log_tag, progname, len, len);
}
/*
* This function initializes all the necessary variables. It only returns
* if everything is OK. If something fails, it exits.
@ -1465,78 +1560,31 @@ unsigned int get_tainted()
static void init(int argc, char **argv)
{
int arg_mode = 0; /* MODE_DEBUG, ... */
char *tmp;
char *cfg_pidfile = NULL;
int err_code = 0;
char *err_msg = NULL;
struct wordlist *wl;
char *progname;
char *progname = global.log_tag.area;
char *change_dir = NULL;
struct proxy *px;
struct post_check_fct *pcf;
int ideal_maxconn;
char *check_condition = NULL;
global.mode = MODE_STARTING;
old_argv = copy_argv(argc, argv);
if (!old_argv) {
ha_alert("failed to copy argv.\n");
exit(1);
}
if (!init_trash_buffers(1)) {
ha_alert("failed to initialize trash buffers.\n");
exit(1);
}
/* NB: POSIX does not make it mandatory for gethostname() to NULL-terminate
* the string in case of truncation, and at least FreeBSD appears not to do
* it.
*/
memset(hostname, 0, sizeof(hostname));
gethostname(hostname, sizeof(hostname) - 1);
if ((localpeer = strdup(hostname)) == NULL) {
ha_alert("Cannot allocate memory for local peer.\n");
exit(EXIT_FAILURE);
}
setenv("HAPROXY_LOCALPEER", localpeer, 1);
/* we were in mworker mode, we should restart in mworker mode */
if (getenv("HAPROXY_MWORKER_REEXEC") != NULL)
global.mode |= MODE_MWORKER;
/*
* Initialize the previously static variables.
*/
totalconn = actconn = listeners = stopping = 0;
killed = 0;
global.rlimit_memmax_all = HAPROXY_MEMMAX;
tzset();
clock_init_process_date();
start_date = now;
ha_random_boot(argv);
if (init_acl() != 0)
exit(1);
#ifdef USE_OPENSSL
/* Initialize the random generator.
* Must be called before chroot for access to /dev/urandom
*/
if (!ssl_initialize_random()) {
ha_alert("OpenSSL random data generator initialization failed.\n");
exit(1);
}
#endif
/* Initialise lua. */
hlua_init();
/* pre-fill in the global tuning options before we let the cmdline
* change them.
*/
global.tune.options |= GTUNE_USE_SELECT; /* select() is always available */
#if defined(USE_POLL)
global.tune.options |= GTUNE_USE_POLL;
@ -1564,19 +1612,6 @@ static void init(int argc, char **argv)
#endif
global.tune.options |= GTUNE_STRICT_LIMITS;
pid = getpid();
progname = *argv;
while ((tmp = strchr(progname, '/')) != NULL)
progname = tmp + 1;
/* the process name is used for the logs only */
chunk_initlen(&global.log_tag, strdup(progname), strlen(progname), strlen(progname));
if (b_orig(&global.log_tag) == NULL) {
chunk_destroy(&global.log_tag);
ha_alert("Cannot allocate memory for log_tag.\n");
exit(EXIT_FAILURE);
}
argc--; argv++;
while (argc > 0) {
char *flag;
@ -1802,19 +1837,6 @@ static void init(int argc, char **argv)
exit(1);
}
global.maxsock = 10; /* reserve 10 fds ; will be incremented by socket eaters */
#ifdef USE_CPU_AFFINITY
{
int i;
ha_cpuset_zero(&cpu_map.proc);
ha_cpuset_zero(&cpu_map.proc_t1);
for (i = 0; i < MAX_THREADS; ++i) {
ha_cpuset_zero(&cpu_map.thread[i]);
}
}
#endif
usermsgs_clr("config");
if (global.mode & MODE_CHECK_CONDITION) {
@ -2907,11 +2929,17 @@ int main(int argc, char **argv)
RUN_INITCALLS(STG_PREPARE);
RUN_INITCALLS(STG_LOCK);
RUN_INITCALLS(STG_REGISTER);
/* now's time to initialize early boot variables */
init_early(argc, argv);
RUN_INITCALLS(STG_ALLOC);
RUN_INITCALLS(STG_POOL);
RUN_INITCALLS(STG_INIT);
/* this is the late init where the config is parsed */
init(argc, argv);
signal_register_fct(SIGQUIT, dump, SIGQUIT);
signal_register_fct(SIGUSR1, sig_soft_stop, SIGUSR1);
signal_register_fct(SIGHUP, sig_dump_state, SIGHUP);