BUG/MINOR: cache: alloc shctx after check config

When running haproxy -c, the cache parser is trying to allocate the size
of the cache. This can be a problem in an environment where the RAM is
limited.

This patch moves the cache allocation in the post_check callback which
is not executed during a -c.

This patch may be backported at least to 2.0 and 1.9. In 1.9, the callbacks
registration mechanism is not the same. So the patch will have to be adapted. No
need to backport it to 1.8, the code is probably too different.

(cherry picked from commit d1d1e229453a492a538245f6a72ba6929eca9de1)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/src/cache.c b/src/cache.c
index 6de0782..54e3340 100644
--- a/src/cache.c
+++ b/src/cache.c
@@ -94,6 +94,7 @@
 #define CACHE_ENTRY_MAX_AGE 2147483648U
 
 static struct list caches = LIST_HEAD_INIT(caches);
+static struct list caches_config = LIST_HEAD_INIT(caches_config); /* cache config to init */
 static struct cache *tmp_cache_config = NULL;
 
 DECLARE_STATIC_POOL(pool_head_cache_st, "cache_st", sizeof(struct cache_st));
@@ -158,16 +159,17 @@
 	struct cache *cache;
 	int comp = 0;
 
-	/* resolve the cache name to a ptr in the filter config */
-	list_for_each_entry(cache, &caches, list) {
+	/* Find the cache corresponding to the name in the filter config.  The
+	 *  cache will not be referenced now in the filter config because it is
+	 *  not fully allocated. This step will be performed during the cache
+	 *  post_check.
+	 */
+	list_for_each_entry(cache, &caches_config, list) {
 		if (!strcmp(cache->id, cconf->c.name)) {
 			/* there can be only one filter per cache, so we free it there */
 			cache->flags |= ((px->options2 & PR_O2_USE_HTX)
 					 ? CACHE_F_HTX
 					 : CACHE_F_LEGACY_HTTP);
-
-			free(cconf->c.name);
-			cconf->c.cache = cache;
 			goto found;
 		}
 	}
@@ -1618,12 +1620,9 @@
 
 int cfg_post_parse_section_cache()
 {
-	struct shared_context *shctx;
 	int err_code = 0;
-	int ret_shctx;
 
 	if (tmp_cache_config) {
-		struct cache *cache;
 
 		if (tmp_cache_config->maxblocks <= 0) {
 			ha_alert("Size not specified for cache '%s'\n", tmp_cache_config->id);
@@ -1642,8 +1641,32 @@
 			goto out;
 		}
 
+		/* add to the list of cache to init and reinit tmp_cache_config
+		 * for next cache section, if any.
+		 */
+		LIST_ADDQ(&caches_config, &tmp_cache_config->list);
+		tmp_cache_config = NULL;
+		return err_code;
+	}
+out:
+	free(tmp_cache_config);
+	tmp_cache_config = NULL;
+	return err_code;
+
+}
+
+int post_check_cache()
+{
+	struct proxy *px;
+	struct cache *back, *cache_config, *cache;
+	struct shared_context *shctx;
+	int ret_shctx;
+	int err_code = 0;
+
-		ret_shctx = shctx_init(&shctx, tmp_cache_config->maxblocks, CACHE_BLOCKSIZE,
-		                       tmp_cache_config->maxobjsz, sizeof(struct cache), 1);
+	list_for_each_entry_safe(cache_config, back, &caches_config, list) {
+
+		ret_shctx = shctx_init(&shctx, cache_config->maxblocks, CACHE_BLOCKSIZE,
+		                       cache_config->maxobjsz, sizeof(struct cache), 1);
 
 		if (ret_shctx <= 0) {
 			if (ret_shctx == SHCTX_E_INIT_LOCK)
@@ -1655,14 +1678,38 @@
 			goto out;
 		}
 		shctx->free_block = cache_free_blocks;
-		memcpy(shctx->data, tmp_cache_config, sizeof(struct cache));
+		/* the cache structure is stored in the shctx and added to the
+		 * caches list, we can remove the entry from the caches_config
+		 * list */
+		memcpy(shctx->data, cache_config, sizeof(struct cache));
 		cache = (struct cache *)shctx->data;
 		cache->entries = EB_ROOT_UNIQUE;
 		LIST_ADDQ(&caches, &cache->list);
+		LIST_DEL(&cache_config->list);
+		free(cache_config);
+
+		/* Find all references for this cache in the existing filters
+		 * (over all proxies) and reference it in matching filters.
+		 */
+		for (px = proxies_list; px; px = px->next) {
+			struct flt_conf *fconf;
+			struct cache_flt_conf *cconf;
+
+			list_for_each_entry(fconf, &px->filter_configs, list) {
+				if (fconf->id != cache_store_flt_id)
+					continue;
+
+				cconf = fconf->conf;
+				if (!strcmp(cache->id, cconf->c.name)) {
+					free(cconf->c.name);
+					cconf->c.cache = cache;
+					break;
+				}
+			}
+		}
 	}
+
 out:
-	free(tmp_cache_config);
-	tmp_cache_config = NULL;
 	return err_code;
 
 }
@@ -1678,7 +1725,7 @@
 	/* Check if the cache is used by HTX and legacy HTTP proxies in same
 	 * time
 	 */
-	list_for_each_entry(cache, &caches, list) {
+	list_for_each_entry(cache, &caches_config, list) {
 		if ((cache->flags & (CACHE_F_HTX|CACHE_F_LEGACY_HTTP)) == (CACHE_F_HTX|CACHE_F_LEGACY_HTTP)) {
 			ha_alert("Cache '%s': cannot be used by HTX and legacy HTTP proxies in same time.\n",
 				 cache->id);
@@ -1891,3 +1938,4 @@
 /* config parsers for this section */
 REGISTER_CONFIG_SECTION("cache", cfg_parse_cache, cfg_post_parse_section_cache);
 REGISTER_CONFIG_POSTPARSER("cache", cfg_cache_postparser);
+REGISTER_POST_CHECK(post_check_cache);