MINOR: init: always fail when setrlimit fails

this patch introduces a strict-limits parameter which enforces the
setrlimit setting instead of a warning. This option can be forcingly
disable with the "no" keyword.
The general aim of this patch is to avoid bad surprises on a production
environment where you change the maxconn for example, a new fd limit is
calculated, but cannot be set because of sysfs setting. In that case you
might want to have an explicit failure to be aware of it before seeing
your traffic going down. During a global rollout it is also useful to
explictly fail as most progressive rollout would simply check the
general health check of the process.

As discussed, plan to use the strict by default mode starting from v2.3.

Signed-off-by: William Dauchy <w.dauchy@criteo.com>
diff --git a/doc/configuration.txt b/doc/configuration.txt
index adba268..e1257ac 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -629,6 +629,7 @@
    - wurfl-information-list
    - wurfl-information-list-separator
    - wurfl-cache-size
+   - strict-limits
 
  * Performance tuning
    - busy-polling
@@ -1405,6 +1406,14 @@
   Please note that this option is only available when haproxy has been compiled
   with USE_WURFL=1.
 
+strict-limits
+  Makes process fail at startup when a setrlimit fails. Haproxy is tries to set
+  the best setrlimit according to what has been calculated. If it fails, it
+  will emit a warning. Use this option if you want an explicit failure of
+  haproxy when those limits fail. This option is disabled by default. If it has
+  been enabled, it may still be forcibly disabled by prefixing it with the "no"
+  keyword.
+
 3.2. Performance tuning
 -----------------------
 
diff --git a/include/types/global.h b/include/types/global.h
index df6614e..d5dbc7f 100644
--- a/include/types/global.h
+++ b/include/types/global.h
@@ -72,8 +72,8 @@
 #define GTUNE_BUSY_POLLING       (1<<11)
 #define GTUNE_LISTENER_MQ        (1<<12)
 #define GTUNE_SET_DUMPABLE       (1<<13)
-
 #define GTUNE_USE_EVPORTS        (1<<14)
+#define GTUNE_STRICT_LIMITS      (1<<15)
 
 /* SSL server verify mode */
 enum {
diff --git a/src/cfgparse-global.c b/src/cfgparse-global.c
index b117ebc..dd37559 100644
--- a/src/cfgparse-global.c
+++ b/src/cfgparse-global.c
@@ -1172,6 +1172,14 @@
 				env++;
 		}
 	}
+	else if (!strcmp(args[0], "strict-limits")) { /* "no strict-limits" or "strict-limits" */
+		if (alertif_too_many_args(0, file, linenum, args, &err_code))
+			goto out;
+		if (kwm == KWM_NO)
+			global.tune.options &= ~GTUNE_STRICT_LIMITS;
+		else
+			global.tune.options |= GTUNE_STRICT_LIMITS;
+	}
 	else {
 		struct cfg_kw_list *kwl;
 		int index;
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 0cad16b..eaad6c2 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2149,10 +2149,10 @@
 
 		if (kwm != KWM_STD && strcmp(args[0], "option") != 0 &&
 		    strcmp(args[0], "log") != 0 && strcmp(args[0], "busy-polling") != 0 &&
-		    strcmp(args[0], "set-dumpable") != 0) {
+		    strcmp(args[0], "set-dumpable") != 0 && strcmp(args[0], "strict-limits") != 0) {
 			ha_alert("parsing [%s:%d]: negation/default currently "
-				 "supported only for options, log, busy-polling and "
-				 "set-dumpable.\n", file, linenum);
+				 "supported only for options, log, busy-polling, "
+				 "set-dumpable and strict-limits.\n", file, linenum);
 			err_code |= ERR_ALERT | ERR_FATAL;
 		}
 
diff --git a/src/haproxy.c b/src/haproxy.c
index a7f294d..cf23c39 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -2829,14 +2829,24 @@
 		limit.rlim_max = MAX(rlim_fd_max_at_boot, limit.rlim_cur);
 
 		if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
-			/* try to set it to the max possible at least */
 			getrlimit(RLIMIT_NOFILE, &limit);
-			limit.rlim_cur = limit.rlim_max;
-			if (setrlimit(RLIMIT_NOFILE, &limit) != -1)
-				getrlimit(RLIMIT_NOFILE, &limit);
+			if (global.tune.options & GTUNE_STRICT_LIMITS) {
+				ha_alert("[%s.main()] Cannot raise FD limit to %d, limit is %d.\n",
+					 argv[0], global.rlimit_nofile, (int)limit.rlim_cur);
+				if (!(global.mode & MODE_MWORKER))
+					exit(1);
+			}
+			else {
+				/* try to set it to the max possible at least */
+				limit.rlim_cur = limit.rlim_max;
+				if (setrlimit(RLIMIT_NOFILE, &limit) != -1)
+					getrlimit(RLIMIT_NOFILE, &limit);
 
-			ha_warning("[%s.main()] Cannot raise FD limit to %d, limit is %d.\n", argv[0], global.rlimit_nofile, (int)limit.rlim_cur);
-			global.rlimit_nofile = limit.rlim_cur;
+				ha_warning("[%s.main()] Cannot raise FD limit to %d, limit is %d. "
+				           "This will fail in >= v2.3\n",
+					   argv[0], global.rlimit_nofile, (int)limit.rlim_cur);
+				global.rlimit_nofile = limit.rlim_cur;
+			}
 		}
 	}
 
@@ -2845,13 +2855,29 @@
 			global.rlimit_memmax * 1048576ULL;
 #ifdef RLIMIT_AS
 		if (setrlimit(RLIMIT_AS, &limit) == -1) {
-			ha_warning("[%s.main()] Cannot fix MEM limit to %d megs.\n",
-				   argv[0], global.rlimit_memmax);
+			if (global.tune.options & GTUNE_STRICT_LIMITS) {
+				ha_alert("[%s.main()] Cannot fix MEM limit to %d megs.\n",
+					 argv[0], global.rlimit_memmax);
+				if (!(global.mode & MODE_MWORKER))
+					exit(1);
+			}
+			else
+				ha_warning("[%s.main()] Cannot fix MEM limit to %d megs."
+					   "This will fail in >= v2.3\n",
+					   argv[0], global.rlimit_memmax);
 		}
 #else
 		if (setrlimit(RLIMIT_DATA, &limit) == -1) {
-			ha_warning("[%s.main()] Cannot fix MEM limit to %d megs.\n",
-				   argv[0], global.rlimit_memmax);
+			if (global.tune.options & GTUNE_STRICT_LIMITS) {
+				ha_alert("[%s.main()] Cannot fix MEM limit to %d megs.\n",
+					 argv[0], global.rlimit_memmax);
+				if (!(global.mode & MODE_MWORKER))
+					exit(1);
+			}
+			else
+				ha_warning("[%s.main()] Cannot fix MEM limit to %d megs.",
+					   "This will fail in >= v2.3\n",
+					   argv[0], global.rlimit_memmax);
 		}
 #endif
 	}
@@ -3050,13 +3076,31 @@
 		limit.rlim_cur = limit.rlim_max = RLIM_INFINITY;
 
 #if defined(RLIMIT_FSIZE)
-		if (setrlimit(RLIMIT_FSIZE, &limit) == -1)
-			ha_warning("[%s.main()] Failed to set the raise the maximum file size.\n", argv[0]);
+		if (setrlimit(RLIMIT_FSIZE, &limit) == -1) {
+			if (global.tune.options & GTUNE_STRICT_LIMITS) {
+				ha_alert("[%s.main()] Failed to set the raise the maximum "
+					 "file size.\n", argv[0]);
+				if (!(global.mode & MODE_MWORKER))
+					exit(1);
+			}
+			else
+				ha_warning("[%s.main()] Failed to set the raise the maximum "
+					   "file size. This will fail in >= v2.3\n", argv[0]);
+		}
 #endif
 
 #if defined(RLIMIT_CORE)
-		if (setrlimit(RLIMIT_CORE, &limit) == -1)
-			ha_warning("[%s.main()] Failed to set the raise the core dump size.\n", argv[0]);
+		if (setrlimit(RLIMIT_CORE, &limit) == -1) {
+			if (global.tune.options & GTUNE_STRICT_LIMITS) {
+				ha_alert("[%s.main()] Failed to set the raise the core "
+					 "dump size.\n", argv[0]);
+				if (!(global.mode & MODE_MWORKER))
+					exit(1);
+			}
+			else
+				ha_warning("[%s.main()] Failed to set the raise the core "
+					   "dump size. This will fail in >= v2.3\n", argv[0]);
+		}
 #endif
 
 #if defined(USE_PRCTL)
@@ -3069,8 +3113,20 @@
 	limit.rlim_cur = limit.rlim_max = 0;
 	getrlimit(RLIMIT_NOFILE, &limit);
 	if (limit.rlim_cur < global.maxsock) {
-		ha_warning("[%s.main()] FD limit (%d) too low for maxconn=%d/maxsock=%d. Please raise 'ulimit-n' to %d or more to avoid any trouble.\n",
-			   argv[0], (int)limit.rlim_cur, global.maxconn, global.maxsock, global.maxsock);
+		if (global.tune.options & GTUNE_STRICT_LIMITS) {
+			ha_alert("[%s.main()] FD limit (%d) too low for maxconn=%d/maxsock=%d. "
+				 "Please raise 'ulimit-n' to %d or more to avoid any trouble.\n",
+			         argv[0], (int)limit.rlim_cur, global.maxconn, global.maxsock,
+				 global.maxsock);
+			if (!(global.mode & MODE_MWORKER))
+				exit(1);
+		}
+		else
+			ha_alert("[%s.main()] FD limit (%d) too low for maxconn=%d/maxsock=%d. "
+				 "Please raise 'ulimit-n' to %d or more to avoid any trouble."
+				 "This will fail in >= v2.3\n",
+			         argv[0], (int)limit.rlim_cur, global.maxconn, global.maxsock,
+				 global.maxsock);
 	}
 
 	if (global.mode & (MODE_DAEMON | MODE_MWORKER | MODE_MWORKER_WAIT)) {