Merge tag 'v1.5.19' into dev

HAProxy 1.5.19

Change-Id: I8e64d81c0750f07cc2a1d6c10f61843622587c2b
diff --git a/include/types/global.h b/include/types/global.h
index f1525ae..42b4d80 100644
--- a/include/types/global.h
+++ b/include/types/global.h
@@ -96,6 +96,9 @@
 	struct freq_ctr ssl_be_keys_per_sec;
 	struct freq_ctr comp_bps_in;	/* bytes per second, before http compression */
 	struct freq_ctr comp_bps_out;	/* bytes per second, after http compression */
+	int email_alert;
+	char email_to[3][50];
+	char email_from[50];	
 	int cps_lim, cps_max;
 	int sps_lim, sps_max;
 	int ssl_lim, ssl_max;
diff --git a/include/types/proxy.h b/include/types/proxy.h
index 7331ca3..c7a913b 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -259,6 +259,7 @@
 	int  uri_len_limit;			/* character limit for uri balancing algorithm */
 	int  uri_dirs_depth1;			/* directories+1 (slashes) limit for uri balancing algorithm */
 	int  uri_whole;				/* if != 0, calculates the hash from the whole uri. Still honors the len_limit and dirs_depth1 */
+	int  uri_gerrit;				/* if != 0, calculates the hash from extracting gerrit project from uri. */
 	char *hh_name;				/* name of the header parameter used for hashing */
 	int  hh_len;				/* strlen(hh_name), computed only once */
 	int  hh_match_domain;			/* toggle use of special match function */
diff --git a/src/backend.c b/src/backend.c
index 640636d..8f95f5a 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -187,6 +187,12 @@
 	int c;
 	int slashes = 0;
 	const char *start, *end;
+	int depth = 1;
+	int orig_uri_len = uri_len;
+	const char *orig_start;
+	const char *p, *params;
+	int buflen = 0;
+	char *buf;
 
 	if (px->lbprm.tot_weight == 0)
 		return NULL;
@@ -198,21 +204,120 @@
 	if (px->uri_len_limit)
 		uri_len = MIN(uri_len, px->uri_len_limit);
 
-	start = end = uri;
-	while (uri_len--) {
-		c = *end;
-		if (c == '/') {
-			slashes++;
-			if (slashes == px->uri_dirs_depth1) /* depth+1 */
+	orig_start = start = end = uri;
+	if (px->uri_gerrit == 1) {
+		p = memchr(uri, '?', uri_len);
+		params = p;
+		if (p) {
+			p++;
+			uri_len = p - uri;
+			params = p;
+			if (p[7] == '=') {
+				if (memcmp(p, "service", 7) == 0) {
+					p += 8;
+					depth = 2;
+				}
+			}
+		}
+
+		end = uri + uri_len - 1;
+		while (uri_len--) {
+			c = *end;
+			if (c == '/') {
+				if (p == NULL)
+					p = end + 1;
+				slashes++;
+				if (slashes == depth)
+					break;
+			}
+			end--;
+		}
+		if (p != NULL && (!strncmp(p, "git-receive-pack", 16) || !strncmp(p, "git-upload-pack", 15))) {
+			if (!strncmp(start, "/a/", 3) || !strncmp(start, "/p/", 3))
+                                start += 2;
+			// we have git request here
+			// get rid of redundant '/'
+			while ((end - start) > 1 && *(start+1) == '/')
+				start++;
+			while ((end - start) > 1 && *(end-1) == '/')
+				end--;
+			// get rid of trailing ".git"
+			if ((end - start) > 4 && strncmp(end-4, ".git", 4) == 0)
+				end -= 4;
+			// get rid of redundant '/'
+			while ((end - start) > 1 && *(end-1) == '/')
+				end--;
+			hash = gen_hash(px, start, (end - start));
+		} else {
+			if (px->uri_dirs_depth1 > 0){
+				// reset state       
+				uri_len = orig_uri_len;
+				if (params != NULL)
+					uri_len = params - uri;
+				depth = px->uri_dirs_depth1;
+				slashes = 0;
+				buf = (char *)malloc(uri_len);
+				orig_start = start = end = uri;
+				while (uri_len--) {
+					c = *end;
+					if (c == '/') {
+						if (slashes == 1 && end - start == 2) {
+							depth += 1;
+							orig_start = start = end;
+						}
+						slashes++;
+						if (slashes == depth - 1) {
+							if (memcmp(start, "/changes", end - start) != 0 && memcmp(start, "/projects", end - start) != 0)
+								break;
+							start = end;
+							p = start;
+						}
+						else if (slashes == depth) { /* depth+1 */
+							memcpy(buf + buflen, p, end - p);
+							buflen += (end - p);
+							break;
+						}
+					}
+					else if (c == '%') {
+						if (orig_start != start && uri_len > 2 && (memcmp(end, "%2F", 3) == 0 || memcmp(end, "%2f", 3) == 0)) {
+							memcpy(buf + buflen, p, end - p);
+							buflen += (end - p) + 1;
+							buf[buflen - 1] = '/';
+							p = end + 3;
+						}
+					}
+					else if (c == '~') {
+						if (orig_start != start) {
+						  memcpy(buf + buflen, p, end - p);
+						  buflen += (end - p);
+						  break;
+						}
+					}
+					end++;
+				}
+			}
+			if (buflen > 0)
+				hash = gen_hash(px, buf, buflen);
+			if (buf != NULL)
+				free(buf);
+			if (buflen <= 0) /*let's fallback to round robin */
+				return NULL;
+		}
+	} else {
+		while (uri_len--) {
+			c = *end;
+			if (c == '/') {
+				slashes++;
+				if (slashes == px->uri_dirs_depth1) /* depth+1 */
+					break;
+			}
+			else if (c == '?' && !px->uri_whole)
 				break;
+			end++;
 		}
-		else if (c == '?' && !px->uri_whole)
-			break;
-		end++;
+		hash = gen_hash(px, start, (end - start));
 	}
-
-	hash = gen_hash(px, start, (end - start));
-
+	
 	if ((px->lbprm.algo & BE_LB_HASH_MOD) == BE_LB_HMOD_AVAL)
 		hash = full_hash(hash);
  hash_done:
@@ -1361,6 +1466,7 @@
 		curproxy->lbprm.algo |= BE_LB_ALGO_UH;
 
 		curproxy->uri_whole = 0;
+		curproxy->uri_gerrit = 0;
 
 		while (*args[arg]) {
 			if (!strcmp(args[arg], "len")) {
@@ -1386,6 +1492,16 @@
 				curproxy->uri_whole = 1;
 				arg += 1;
 			}
+			else if (!strcmp(args[arg], "gerrit")) {
+				if (!*args[arg+1] || (atoi(args[arg+1]) < 0)) {
+					memprintf(err, "%s : '%s' expects a non negative integer  (got '%s').", args[0], args[arg], args[arg+1]);
+					return -1;
+				}
+				curproxy->uri_gerrit = 1;
+				// 0 means whole uri
+				curproxy->uri_dirs_depth1 = atoi(args[arg+1]);
+				arg += 2;
+			}
 			else {
 				memprintf(err, "%s only accepts parameters 'len', 'depth', and 'whole' (got '%s').", args[0], args[arg]);
 				return -1;
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 9331415..8d04950 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -539,6 +539,7 @@
  */
 int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
 {
+	int i = 0;
 	int err_code = 0;
 	char *errmsg = NULL;
 
@@ -584,6 +585,21 @@
 		goto out;
 #endif
 	}
+  else if (!strcmp(args[0], "email_alert")) {
+    global.email_alert = 1;
+    if (*(args[1]) == 0) {
+      Alert("parsing [%s:%d] : email_alert Expects email address as argument.\n", file, linenum);
+      global.email_alert = 0;
+      err_code |= ERR_ALERT;
+      goto out;
+    }
+    strncpy(global.email_from,args[1],50);
+    for (i=0; i<3; i++) {
+      if (*(args[i+2]) != 0)
+      	strncpy(global.email_to[i],args[i+2],50);
+    }
+    
+  } 
 	else if (!strcmp(args[0], "daemon")) {
 		global.mode |= MODE_DAEMON;
 	}
diff --git a/src/checks.c b/src/checks.c
index 27a23b2..91ec4c9 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -20,6 +20,7 @@
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
+#include <stdarg.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <netinet/in.h>
@@ -294,6 +295,7 @@
 			     (check->health >= check->rise) ? (s->uweight ? "UP" : "DRAIN") : "DOWN");
 
 		Warning("%s.\n", trash.str);
+		
 		send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.str);
 	}
 }
diff --git a/src/log.c b/src/log.c
index 53ef33d..888deb7 100644
--- a/src/log.c
+++ b/src/log.c
@@ -587,6 +587,71 @@
 	}
 }
 
+void make_literal(char const *input, char *output) { 
+  // the following two arrays must be maintained in matching order:
+  static char inputs[] = "\a\b\f\n\r\t\v\\\"\'";
+  static char outputs[] = "abfnrtv\\\"\'";
+
+  char *p, *pos;
+
+  for (;*input;input++) {
+    if (NULL!= (pos=strchr(inputs, *input))) {
+      *output++ = '\\';
+      *output++ = outputs[pos-inputs];
+    }
+    else
+      *output++ = *input;
+  }
+  *output = '\0';
+}
+
+void Emaila(const char *fmt, ...)
+{
+  va_list argp;
+  char tmp[256];
+  char buf[500];
+  char alertcmd[1024];
+  int i;
+  int sysreturn;  
+  size_t len = 0, len1 = 0, len2 = 0, len3 = 0;
+
+  if (global.email_alert) {
+    va_start(argp, fmt);
+    vsnprintf(tmp,128,fmt, argp);
+    make_literal(tmp, buf);
+    
+    len1 = strlen(global.email_to[0]);
+    len2 = strlen(global.email_to[1]);
+    len3 = strlen(global.email_to[2]);
+    memcpy(tmp, global.email_to[0], len1);
+    tmp[len1] = ' ';    
+    len = len1 + 1;
+    if (len2 > 0) {
+      memcpy(tmp + len, global.email_to[1], len2); // includes terminating null    	
+      len += len2;
+      tmp[len] = ' ';
+      len += 1;
+      if (len3 > 0) {
+      	memcpy(tmp + len, global.email_to[2], len3); // includes terminating null    	
+        len += len3;
+        tmp[len] = ' ';
+        len += 1;
+      }
+    }
+    tmp[len] = '\0';
+    snprintf(alertcmd, 1024, "echo \"%s\" | mail -s \"$(echo -e \"[HAProxy alert %s] %.60s...\nFrom:HAProxy <%s>\n\")\" %s&", buf, global.email_from, buf, global.email_from, tmp);
+    	  
+    sysreturn = system(alertcmd);
+    if (sysreturn == -1) {
+      Warning("There was an error sending the email alert");
+    }
+    vfprintf(stderr, fmt, argp);
+    fflush(stderr);
+    va_end(argp);
+  }
+}
+
+
 /*
  * Displays the message on <out> only if quiet mode is not set.
  */
diff --git a/src/server.c b/src/server.c
index aef0250..5f63f0d 100644
--- a/src/server.c
+++ b/src/server.c
@@ -255,7 +255,8 @@
 
 	srv_append_status(&trash, s, reason, xferred, 0);
 	Warning("%s.\n", trash.str);
-
+	Emaila("%s \n", trash.str);
+  
 	/* we don't send an alert if the server was previously paused */
 	send_log(s->proxy, srv_was_stopping ? LOG_NOTICE : LOG_ALERT, "%s.\n", trash.str);
 
@@ -326,6 +327,7 @@
 
 	srv_append_status(&trash, s, reason, xferred, 0);
 	Warning("%s.\n", trash.str);
+	Emaila("%s \n", trash.str);
 	send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.str);
 
 	for (srv = s->trackers; srv; srv = srv->tracknext)