Here is what went to git HEAD, refactored the whole thing. I hope I
didn't break any thing we fixed.

Will upload in a few.

Regards
-- 
Arnaud Cornet
diff --git a/src/connection.c b/src/connection.c
index 77edd05..d1f919b 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -232,53 +232,45 @@ static int _write_socket(connection_t *cn, char *message)
 {
 	size_t size;
 	size_t tcount = 0;
-	size_t p_tcount = 0;
 	ssize_t count;
 
 	size = strlen(message);
+	if (size == 0)
+		return WRITE_OK;
+	/* loop if we wrote some data but not everything, or if error is
+	 * EINTR */
 	do {
-		while ((count = write(cn->handle,
-					((const char *)message) + tcount,
-					size - tcount)) > 0) {
+		count = write(cn->handle, ((const char *)message) + tcount,
+					size - tcount);
+		if (count > 0) {
 			tcount += count;
 			if (tcount == size)
-				break;
-			if (tcount - p_tcount == 0) {
-				/* no write at all, we give up */
-				cn->connected = CONN_ERROR;
-				return WRITE_ERROR;
-			}
-			p_tcount = tcount;
+				return WRITE_OK;
 		}
-	} while (count < 0 &&
-		(errno == EAGAIN || errno == EINTR || errno == EINPROGRESS));
+	} while (count > 0 || (count < 0 && errno == EINTR));
 
-#if 0
-	if (count <= 0 && tcount > 0)
-		fatal("shit happens errno:%d count:%d tcount:%d (%s)\n", errno,
-				count, tcount, message);
-#endif
-	if (count <= 0) {
-		/*
-		 * if no fatal error, return WRITE_KEEP, which makes caller
-		 * keep line in its FIFO
-		 *
-		 * Cannot do: we might have written a partial line
-		 * 
-		if (errno == EAGAIN || errno == EINTR || errno == EINPROGRESS)
-			return WRITE_KEEP;
-		 */
+	/* If we reach this point, we have a partial write */
+	assert(count != 0);
 
-		if (cn_is_connected(cn)) {
-			mylog(LOG_DEBUGVERB, "write(fd %d) : %s", cn->handle,
-					strerror(errno));
-			cn->connected = CONN_ERROR;
-		}
-		mylog(LOG_DEBUGVERB, "write : %s", strerror(errno));
-		return WRITE_ERROR;
+	/* if no fatal error, return WRITE_KEEP, which makes caller keep line
+	 * in its FIFO
+	 *
+	 * Shitty: we might have written a partial line, so we hack the line...
+	 * Callers of _write_socket muse provide a writable message
+	 */
+	if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINPROGRESS) {
+		memmove(message, message + tcount, size - tcount + 1);
+		return WRITE_KEEP;
+	}
+	/* other errors, EPIPE or worse, close the connection, repport error */
+	if (cn_is_connected(cn)) {
+		if (errno != EPIPE)
+			mylog(LOG_INFO, "Broken socket: %s.", strerror(errno));
+		connection_close(cn);
+		cn->connected = CONN_ERROR;
 	}
-	mylog(LOG_DEBUGVERB, "%d/%d bytes sent !", tcount, size);
-	return WRITE_OK;
+	mylog(LOG_DEBUGVERB, "write: %d, %s", cn->handle, strerror(errno));
+	return WRITE_ERROR;
 }
 
 static int write_socket(connection_t *cn, char *line)
@@ -301,7 +293,14 @@ static int real_write_all(connection_t *cn)
 	if (cn == NULL)
 		fatal("real_write_all: wrong arguments");
 
-	while ((line = list_remove_first(cn->outgoing))) {
+	if (cn->partial) {
+		line = cn->partial;
+		cn->partial = NULL;
+	} else {
+		line = list_remove_first(cn->outgoing);
+	}
+
+	do {
 		ret = write_socket(cn, line);
 
 		switch (ret) {
@@ -311,7 +310,8 @@ static int real_write_all(connection_t *cn)
 			return 1;
 		case WRITE_KEEP:
 			/* interrupted or not ready */
-			list_add_first(cn->outgoing, line);
+			assert(cn->partial == NULL);
+			cn->partial = line;
 			return 0;
 		case WRITE_OK:
 			free(line);
@@ -324,37 +324,44 @@ static int real_write_all(connection_t *cn)
 		if (cn->anti_flood)
 			/* one line at a time */
 			break;
-	}
+	} while ((line = list_remove_first(cn->outgoing)));
 	return 0;
 }
 
 void write_line_fast(connection_t *cn, char *line)
 {
 	int r;
-	r = write_socket(cn, line);
-	switch (r) {
-	case WRITE_KEEP:
-		list_add_first(cn->outgoing, bip_strdup(line));
-		break;
-	case WRITE_ERROR:
-		cn->connected = CONN_ERROR;
-		break;
-	case WRITE_OK:
-		break;
-	default:
-		fatal("internal error 7");
-		break;
+	char *nline = bip_strdup(line);
+
+	if (cn->partial) {
+		list_add_first(cn->outgoing, nline);
+	} else {
+		r = write_socket(cn, nline);
+		switch (r) {
+		case WRITE_KEEP:
+			cn->partial = nline;
+			break;
+		case WRITE_ERROR:
+		case WRITE_OK:
+			free(nline);
+			break;
+		default:
+			fatal("internal error 7");
+			break;
+		}
 	}
 }
 
 void write_lines(connection_t *cn, list_t *lines)
 {
 	list_append(cn->outgoing, lines);
+	real_write_all(cn);
 }
 
 void write_line(connection_t *cn, char *line)
 {
 	list_add_last(cn->outgoing, bip_strdup(line));
+	real_write_all(cn);
 }
 
 list_t *read_lines(connection_t *cn, int *error)
diff --git a/src/connection.h b/src/connection.h
index 62d21ee..697b53d 100644
--- a/src/connection.h
+++ b/src/connection.h
@@ -76,6 +76,7 @@ typedef struct connection {
 	char *incoming;
 	unsigned incoming_end;
 	list_t *outgoing;
+	char *partial;
 	list_t *incoming_lines;
 	void *user_data;
 	list_t *ip_list;

Reply via email to