MEDIUM: ssl/cli: 'set ssl cert' updates a certificate from the CLI

    $ echo -e "set ssl cert certificate.pem <<\n$(cat certificate2.pem)\n" | \
    socat stdio /var/run/haproxy.stat
    Certificate updated!

The operation is locked at the ckch level with a HA_SPINLOCK_T which
prevents the ckch architecture (ckch_store, ckch_inst..) to be modified
at the same time. So you can't do a certificate update at the same time
from multiple CLI connections.

SNI trees are also locked with a HA_RWLOCK_T so reading operations are
locked only during a certificate update.

Bundles are supported but you need to update each file (.rsa|ecdsa|.dsa)
independently. If a file is used in the configuration as a bundle AND
as a unique certificate, both will be updated.

Bundles, directories and crt-list are supported, however filters in
crt-list are currently unsupported.

The code tries to allocate every SNIs and certificate instances first,
so it can rollback the operation if that was unsuccessful.

If you have too much instances of the certificate (at least 20000 in my
tests on my laptop), the function can take too much time and be killed
by the watchdog. This will be fixed later. Also with too much
certificates it's possible that socat exits before the end of the
generation without displaying a message, consider changing the socat
timeout in this case (-t2 for example).

The size of the certificate is currently limited by the maximum size of
a payload, that must fit in a buffer.
diff --git a/include/common/hathreads.h b/include/common/hathreads.h
index b05215b..8e78014 100644
--- a/include/common/hathreads.h
+++ b/include/common/hathreads.h
@@ -563,6 +563,8 @@
 	LOGSRV_LOCK,
 	DICT_LOCK,
 	PROTO_LOCK,
+	CKCH_LOCK,
+	SNI_LOCK,
 	OTHER_LOCK,
 	LOCK_LABELS
 };
@@ -681,6 +683,8 @@
 	case LOGSRV_LOCK:          return "LOGSRV";
 	case DICT_LOCK:            return "DICT";
 	case PROTO_LOCK:           return "PROTO";
+	case CKCH_LOCK:            return "CKCH";
+	case SNI_LOCK:             return "SNI";
 	case OTHER_LOCK:           return "OTHER";
 	case LOCK_LABELS:          break; /* keep compiler happy */
 	};
diff --git a/include/proto/listener.h b/include/proto/listener.h
index 761bff1..3648882 100644
--- a/include/proto/listener.h
+++ b/include/proto/listener.h
@@ -157,6 +157,7 @@
 	bind_conf->xprt = xprt;
 	bind_conf->frontend = fe;
 	bind_conf->severity_output = CLI_SEVERITY_NONE;
+	HA_RWLOCK_INIT(&bind_conf->sni_lock);
 	bind_conf->sni_ctx = EB_ROOT;
 	bind_conf->sni_w_ctx = EB_ROOT;
 
diff --git a/include/types/listener.h b/include/types/listener.h
index 79517a2..1098b42 100644
--- a/include/types/listener.h
+++ b/include/types/listener.h
@@ -150,6 +150,7 @@
 	struct ssl_bind_conf *default_ssl_conf; /* custom SSL conf of default_ctx */
 	int strict_sni;            /* refuse negotiation if sni doesn't match a certificate */
 	int ssl_options;           /* ssl options */
+	__decl_hathreads(HA_RWLOCK_T sni_lock); /* lock the SNI trees during add/del operations */
 	struct eb_root sni_ctx;    /* sni_ctx tree of all known certs full-names sorted by name */
 	struct eb_root sni_w_ctx;  /* sni_ctx tree of all known certs wildcards sorted by name */
 	struct tls_keys_ref *keys_ref; /* TLS ticket keys reference */
diff --git a/include/types/ssl_sock.h b/include/types/ssl_sock.h
index f7960c2..dd30ed4 100644
--- a/include/types/ssl_sock.h
+++ b/include/types/ssl_sock.h
@@ -106,7 +106,8 @@
  */
 struct ckch_store {
 	struct cert_key_and_chain *ckch;
-	int multi; /* is it a multi-cert bundle ? */
+	int multi:1; /* is it a multi-cert bundle ? */
+	int filters:1; /* one of the instances is using filters, TODO:remove this flag once filters are supported */
 	struct list ckch_inst; /* list of ckch_inst which uses this ckch_node */
 	struct ebmb_node node;
 	char path[0];
@@ -121,6 +122,7 @@
  */
 struct ckch_inst {
 	struct bind_conf *bind_conf; /* pointer to the bind_conf that uses this ckch_inst */
+	struct ssl_bind_conf *ssl_conf; /* pointer to the ssl_conf which is used by every sni_ctx of this inst */
 	struct list sni_ctx; /* list of sni_ctx using this ckch_inst */
 	struct list by_ckchs; /* chained in ckch_store's list of ckch_inst */
 };