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;