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.
diff --git a/src/haproxy.c b/src/haproxy.c
index 263e699..905fe84 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -1458,85 +1458,133 @@
return tainted_state;
}
-/*
- * This function initializes all the necessary variables. It only returns
- * if everything is OK. If something fails, it exits.
+
+/* 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(int argc, char **argv)
+static void init_early(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 *change_dir = NULL;
- struct proxy *px;
- struct post_check_fct *pcf;
- int ideal_maxconn;
- char *check_condition = NULL;
+ 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;
- 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);
- }
+ /* if we were in mworker mode, we should restart in mworker mode */
+ if (getenv("HAPROXY_MWORKER_REEXEC") != NULL)
+ global.mode |= MODE_MWORKER;
- /* 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.
+ /* 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);
- if ((localpeer = strdup(hostname)) == NULL) {
+ /* 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);
}
- 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.
+ /* 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
- totalconn = actconn = listeners = stopping = 0;
- killed = 0;
+ /* 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
- global.rlimit_memmax_all = HAPROXY_MEMMAX;
+ /* 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);
+ }
- tzset();
- clock_init_process_date();
- start_date = now;
+ /* 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;
- ha_random_boot(argv);
+ len = strlen(progname);
+ progname = strdup(progname);
+ if (!progname) {
+ ha_alert("Cannot allocate memory for log_tag.\n");
+ exit(EXIT_FAILURE);
+ }
- if (init_acl() != 0)
- exit(1);
+ chunk_initlen(&global.log_tag, progname, len, len);
+}
-#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");
+/*
+ * This function initializes all the necessary variables. It only returns
+ * if everything is OK. If something fails, it exits.
+ */
+static void init(int argc, char **argv)
+{
+ int arg_mode = 0; /* MODE_DEBUG, ... */
+ char *cfg_pidfile = NULL;
+ int err_code = 0;
+ char *err_msg = NULL;
+ struct wordlist *wl;
+ 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;
+
+ if (!init_trash_buffers(1)) {
+ ha_alert("failed to initialize trash buffers.\n");
exit(1);
}
-#endif
+
+ if (init_acl() != 0)
+ exit(1);
/* 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 @@
#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 @@
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 @@
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);