--- /home/johnm/libmemcache/libmemcache-1.1.0/memcache.c 2004-12-08 19:56:58.000000000 +0000 +++ memcache.c 2004-12-13 22:34:31.000000000 +0000 @@ -64,7 +64,7 @@ static u_int32_t mcm_atomic_cmd(const static void mcm_fetch_cmd(const struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_req *req, const char *cmd, const size_t cmd_len); static char *mcm_get_line(const struct memcache_ctxt *ctxt, struct memcache *mc, - struct memcache_server *ms); + struct memcache_server *ms, CMD_TYPE_ENUM cmd_type); static struct memcache_res *mcm_res_new(const struct memcache_ctxt *ctxt); static void mcm_server_free(const struct memcache_ctxt *ctxt, struct memcache_server *ms); static struct memcache_server *mcm_server_new(const struct memcache_ctxt *ctxt, struct memcache *mc); @@ -405,7 +405,7 @@ mcm_atomic_cmd(const struct memcache_ctx mcm_server_block(ms, 1); mcm_reset_buf(mc); - cur = mcm_get_line(ctxt, mc, ms); + cur = mcm_get_line(ctxt, mc, ms, INCR_DECR_CMD); if (cur != NULL && memcmp(cur, "NOT_FOUND", CSTRLEN("NOT_FOUND")) == 0) { mcm_server_block(ms, 0); return 0; @@ -527,7 +527,7 @@ mcm_delete(const struct memcache_ctxt *c mcm_server_block(ms, 1); mcm_reset_buf(mc); - cur = mcm_get_line(ctxt, mc, ms); + cur = mcm_get_line(ctxt, mc, ms, DELETE_CMD); if (cur != NULL && memcmp(cur, "DELETED", CSTRLEN("DELETED")) == 0) { mcm_server_block(ms, 0); return 0; @@ -617,7 +617,7 @@ mcm_fetch_cmd(const struct memcache_ctxt mcm_reset_buf(mc); /* Grab a line from the server */ - cur = mcm_get_line(ctxt, mc, ms); + cur = mcm_get_line(ctxt, mc, ms, RETRIEVAL_CMD); /* Set res to NULL that way we reset our scan for keys from the * start of the list. */ @@ -717,7 +717,7 @@ mcm_fetch_cmd(const struct memcache_ctxt /* ...and grab a new line so we can start the fun all over * again! */ - cur = mcm_get_line(ctxt, mc, ms); + cur = mcm_get_line(ctxt, mc, ms, RETRIEVAL_CMD); continue; } @@ -792,7 +792,7 @@ mcm_fetch_cmd(const struct memcache_ctxt /* ...and grab a new line so we can start the fun all over * again! */ - cur = mcm_get_line(ctxt, mc, ms); + cur = mcm_get_line(ctxt, mc, ms, RETRIEVAL_CMD); } else if (memcmp(cur, "END", CSTRLEN("END")) == 0) { /* We're done looking for values, or the server didn't find * anything. */ @@ -854,7 +854,7 @@ mcm_flush_all(const struct memcache_ctxt mcm_server_block(ms, 1); mcm_reset_buf(mc); - cur = mcm_get_line(ctxt, mc, ms); + cur = mcm_get_line(ctxt, mc, ms, OTHER_CMD); if (cur != NULL && memcmp(cur, "OK", CSTRLEN("OK")) == 0) { mcm_server_block(ms, 0); return 0; @@ -894,130 +894,184 @@ mcm_get(const struct memcache_ctxt *ctxt static char * -mcm_get_line(const struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_server *ms) { +mcm_get_line(const struct memcache_ctxt *ctxt, struct memcache *mc, struct memcache_server *ms, CMD_TYPE_ENUM cmd) { ssize_t rb; char *cp; char *new_start; + size_t read_cur_offset; #ifdef HAVE_SELECT int ret; + #endif if (mc->read_cur == NULL) { - mc->read_cur = mc->buf; - goto try_read; - } - - for(;;) { - /* We're guaranteed to have data in our buffer at this point. - * Search for a newline. If we don't have one, we need to either - * move data around and read(2) again, or mcRealloc(3) and read(2) - * again. */ - cp = memchr(mc->start, (int)'\n', mc->size - (size_t)(mc->cur - mc->buf)); - if (cp == NULL) { - get_more_bits: - /* If our cursor is at the start of the buffer, we need to - * allocate more memory. */ - if (mc->buf == mc->cur) { - cp = (char *)ctxt->mcRealloc(mc->buf, mc->size * 2); - if (cp == NULL) { - /* No sense in continuing if we can't read(2) in a whole - * line. There are likely bigger problems on a system if its - * experiencing a problem like this. */ - warn("%s:%u\tmcRealloc()", __FILE__, __LINE__); - return NULL; - } - - mc->buf = mc->cur = mc->start = cp; - mc->read_cur = &mc->cur[mc->size]; - mc->size *= 2; /* Note the change in buffer size */ - } else { - /* bcopy(3) the data from mc->cur to the end of mc->buf back - * to the start of mc->buf. Use bcopy(3) in case there is - * overlap. */ - bcopy(mc->cur, mc->buf, mc->size - (size_t)(mc->cur - mc->buf)); - - /* Advance where the read(2) should place new data. */ - mc->read_cur = &mc->buf[mc->size - (size_t)(mc->cur - mc->buf)]; - - /* Reset the cursor to the front of the buffer */ - mc->cur = mc->buf; - } - - /* Now that we've got space to read(2) in bits, read(2) in - * data. */ - try_read: - + mc->read_cur = mc->start = mc->cur = mc->buf; + +try_read: #ifdef HAVE_SELECT - /* Before we read(2) anything, check to make sure there is data - * available to be read(2). No sense in wastefully calling - * read(2) constantly in a loop. */ - ret = select(1, &ms->fds, NULL, NULL, &ms->select_tv); - if (ret == -1) - errx(EX_OSERR, "%s:%u\tselect()", __FILE__, __LINE__); + /* Before we read(2) anything, check to make sure there is data + * available to be read(2). No sense in wastefully calling + * read(2) constantly in a loop. */ + ret = select(1, &ms->fds, NULL, NULL, &ms->select_tv); + if (ret == -1) + errx(EX_OSERR, "%s:%u\tselect()", __FILE__, __LINE__); #endif - rb = read(ms->fd, mc->read_cur, mc->size - (size_t)(mc->cur - mc->buf)); - switch(rb) { - case -1: - /* We're in non-blocking mode, don't abort because of EAGAIN or - * EINTR */ - if (errno == EAGAIN || errno == EINTR) - goto try_read; - warn("%s:%u\tread()", __FILE__, __LINE__); - mcm_deactivate_server(ctxt, mc, ms); - return NULL; /* Should we try again depending on errno? */ - case 0: - /* Seems like an error to me if the server closes its connection - * here. deactivate the server instead of just closing it. */ - mcm_deactivate_server(ctxt, mc, ms); - return NULL; - } + rb = read(ms->fd, mc->read_cur, mc->size - (size_t)(mc->read_cur - mc->buf)); + switch(rb) { + case -1: + /* We're in non-blocking mode, don't abort because of EAGAIN or + * EINTR */ + if (errno == EAGAIN || errno == EINTR) + goto try_read; + warn("%s:%u\tread() failed.\n", __FILE__, __LINE__); + mcm_deactivate_server(ctxt, mc, ms); + return NULL; /* Should we try again depending on errno? */ + case 0: + /* Seems like an error to me if the server closes its connection + * here. deactivate the server instead of just closing it. */ + mcm_deactivate_server(ctxt, mc, ms); + warnx("%s:%u\tServer unexpectedly closed connection.\n", __FILE__, __LINE__); + return NULL; + default: + mc->read_cur += rb; + } + /* If we got the same amount of data as we said was max, then we + * probably need to read(2) more data. Worst case scenario + * being that we double our buffer and read(2) returns zero new + * bytes. If people are hitting this case a lot, + * GET_INIT_BUF_SIZE should be increased. */ + if (mc->size - (size_t)(mc->read_cur - mc->buf) == 0) { + /* Store the difference between mc->read_cur and mc->buf + * so that it can be restored after mcRealloc. */ + read_cur_offset = mc->read_cur - mc->buf; + cp = (char *)ctxt->mcRealloc(mc->buf, mc->size * 2); + if (cp == NULL) { + /* No sense in continuing if we can't read(2) in a whole + * line. There are likely bigger problems on a system if its + * experiencing a problem like this. */ + warn("%s:%u\tmcRealloc()", __FILE__, __LINE__); + return NULL; + } + + /* Fix mc->read_cur from remalloc mangling. */ + mc->read_cur = cp + read_cur_offset; - /* If we got the same amount of data as we said was max, then we - * probably need to read(2) more data. Worst case scenario - * being that we double our buffer and read(2) returns zero new - * bytes. If people are hitting this case a lot, - * GET_INIT_BUF_SIZE should be increased. */ - if (mc->size - (size_t)(mc->cur - mc->buf) == (size_t)rb) { - goto get_more_bits; - } + /* Fix the other pointers */ + mc->buf = mc->cur = mc->start = cp; - /* Search for a newline again, starting from where we read(2) - * data in. Slight optimization over just starting the loop - * over again since in most cases, we'll find a newline in the - * newly read(2) chunk of data. */ - cp = memchr(mc->cur, (int)'\n', mc->size - (size_t)(mc->cur - mc->buf)); - if (cp == NULL) { - /* We suck. Try again. This is nearly the same as a continue, - * but saves us from doing an extra memchr(). */ - goto get_more_bits; - } - } + /* Note the change in buffer size */ + mc->size *= 2; + + goto try_read; + } else { + /* Check if any of the valid reponse ending tags where + * found at the end of the read data. */ + switch(cmd) { + case STORAGE_CMD: + cp = mc->read_cur - CSTRLEN("STORED\r\n"); + if(cp >= mc->buf && memcmp(cp, "STORED\r\n", CSTRLEN("STORED\r\n"))==0) { + goto done_reading; + } + cp = mc->read_cur - CSTRLEN("NOT_STORED\r\n"); + if(cp >= mc->buf && memcmp(cp, "NOT_STORED\r\n", CSTRLEN("NOT_STORED\r\n"))==0) { + goto done_reading; + } + break; + case RETRIEVAL_CMD: + case STATS_CMD: + cp = mc->read_cur - CSTRLEN("END\r\n"); + if(cp >= mc->buf && memcmp(cp, "END\r\n", CSTRLEN("END\r\n"))==0) { + goto done_reading; + } + break; + case INCR_DECR_CMD: + cp = mc->read_cur - CSTRLEN("\r\n"); + if(cp >= mc->buf && memcmp(cp, "\r\n", CSTRLEN("\r\n"))==0) { + goto done_reading; + } + cp = mc->read_cur - CSTRLEN("NOT_FOUND\r\n"); + if(cp > mc->buf && memcmp(cp, "NOT_FOUND\r\n", CSTRLEN("NOT_FOUND\r\n"))==0) { + goto done_reading; + } + break; + case DELETE_CMD: + cp = mc->read_cur - CSTRLEN("DELETED\r\n"); + if(cp >= mc->buf && memcmp(cp, "DELETED\r\n", CSTRLEN("DELETED\r\n"))==0) { + goto done_reading; + } + cp = mc->read_cur - CSTRLEN("NOT_FOUND\r\n"); + if(cp >= mc->buf && memcmp(cp, "NOT_FOUND\r\n", CSTRLEN("NOT_FOUND\r\n"))==0) { + goto done_reading; + } + break; + case OTHER_CMD: + default: + cp = mc->read_cur - CSTRLEN("\r\n"); + if(cp >= mc->buf && memcmp(cp, "\r\n", CSTRLEN("\r\n"))==0) { + goto done_reading; + } + break; + } + /* A valid response ending command was not found, + * check if an error was found. */ + cp = mc->read_cur - CSTRLEN("ERROR\r\n"); + if(cp >= mc->buf && memcmp(cp, "ERROR\r\n", CSTRLEN("ERROR\r\n"))==0) { + warnx("%s:%u\t, Server returned ERROR\n", __FILE__, __LINE__); + mcm_deactivate_server(ctxt, mc, ms); + return NULL; + } else if(memcmp(mc->buf, "CLIENT_ERROR", CSTRLEN("CLIENT_ERROR"))==0) { + warnx("%s:%u\t, Server returned CLIENT_ERROR\n", __FILE__, __LINE__); + mcm_deactivate_server(ctxt, mc, ms); + return NULL; + } else if( memcmp(mc->buf, "SERVER_ERROR", CSTRLEN("SERVER_ERROR"))==0) { + warnx("%s:%u\t, Server returned SERVER_ERROR\n", __FILE__, __LINE__); + mcm_deactivate_server(ctxt, mc, ms); + return NULL; + } + + /* No acceptable end of command output was read, + * and there was no error indicated. Try to read more data + * we probably just didn't get it all the first time through. */ + goto try_read; + } + } +done_reading: + /* There should always be a newline in our response now. + * Try to find it so we can put a null in and return just the line to the + * caller. */ + cp = memchr(mc->start, (int)'\n', mc->size - (size_t)(mc->read_cur - mc->buf)); + if (cp == NULL) { + /* We suck. Return null. */ + warnx("%s:%u\tProtocol violation, no \n anywhere in server response.\n", __FILE__, __LINE__); + mcm_deactivate_server(ctxt, mc, ms); + return NULL; + } #ifdef PEDANTIC - /* At this point, we're guaranteed to have a complete line in the - * buffer. cp should be pointing to a newline character. */ - if (*cp != '\n') abort(); - - /* Protocol check, make sure there's a carige return before the - * newline */ - if (*(cp - 1) != '\r') { - warnx("%s:%u\tProtocol violation, no \\r before the \\n", __FILE__, __LINE__); - mcm_deactivate_server(ctxt, mc, ms); - return NULL; - } + /* At this point, we're guaranteed to have a complete line in the + * buffer. cp should be pointing to a newline character. */ + if (*cp != '\n') abort(); + + /* Protocol check, make sure there's a carige return before the + * newline. + * We need to also allow a \0 here since we modify it below and as such + * subsequent calls may find that. */ + if (*(cp - 1) != '\r' && *(cp - 1) != '\0') { + warnx("%s:%u\tProtocol violation, no \\r before the \\n", __FILE__, __LINE__); + mcm_deactivate_server(ctxt, mc, ms); + return NULL; + } #endif + new_start = cp + 1; /* Advance the start of the next line */ + *(cp - 1) = '\0'; /* Add a null character */ + cp = mc->start; + mc->start = new_start; + + /* Handy little debugging function: */ + /* warnx("Line reads: \"%.*s\"", (size_t)(mc->start - cp - 2), cp); */ + return cp; + - new_start = cp + 1; /* Advance the start of the next line */ - *(cp - 1) = '\0'; /* Add a null character */ - cp = mc->start; - mc->start = new_start; - - /* Handy little debugging function: */ - /* warnx("Line reads: \"%.*s\"", (size_t)(mc->start - cp - 2), cp); */ - return cp; - } - - errx(EX_PROTOCOL, "%s:%u\tProtocol violation: Unable to obtain a new line", __FILE__, __LINE__); } @@ -1564,7 +1618,7 @@ mcm_server_stats(const struct memcache_c mcm_reset_buf(mc); for(;;) { - cur = mcm_get_line(ctxt, mc, ms); + cur = mcm_get_line(ctxt, mc, ms, STATS_CMD); if (cur != NULL && memcmp(cur, "STAT ", CSTRLEN("STAT ")) == 0) { cur = &cur[CSTRLEN("STAT ")]; @@ -1917,7 +1971,7 @@ mcm_storage_cmd(const struct memcache_ct mcm_server_block(ms, 1); mcm_reset_buf(mc); - cur = mcm_get_line(ctxt, mc, ms); + cur = mcm_get_line(ctxt, mc, ms, STORAGE_CMD); if (cur != NULL && memcmp(cur, "STORED", CSTRLEN("STORED")) == 0) { /* Groovy Tuesday */ mcm_server_block(ms, 0); --- /home/johnm/libmemcache/libmemcache-1.1.0/memcache.h 2004-12-08 19:56:58.000000000 +0000 +++ memcache.h 2004-12-13 20:17:12.000000000 +0000 @@ -166,6 +166,15 @@ struct memcache_server { }; +typedef enum { + STORAGE_CMD = 0x00000000, + RETRIEVAL_CMD = 0x00000001, + DELETE_CMD = 0x00000002, + INCR_DECR_CMD = 0x00000003, + STATS_CMD = 0x00000004, + OTHER_CMD = 0x00000005 +} CMD_TYPE_ENUM; + struct memcache_server_stats { pid_t pid; time_t uptime;