1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <assert.h>
5 #include <stdint.h>
6 #include <poll.h>
7 #include <signal.h>
8 #include <sys/uio.h>
9 #include <sys/types.h>
10 #include <sys/socket.h>
11 
12 #include "mcmc.h"
13 
14 // in this form the socket syscalls are handled externally to the client, so
15 // we need to parse the protocol out of a buffer directly.
show_response_buffer(void * c,char * rbuf,size_t bufsize)16 static void show_response_buffer(void *c, char *rbuf, size_t bufsize) {
17     int status;
18     mcmc_resp_t resp;
19     char *val = NULL;
20 
21     do {
22         int bread = recv(mcmc_fd(c), rbuf, bufsize, 0);
23 
24         // need to know how far to advance the buffer.
25         // resp->reslen + resp->vlen_read works, but feels awkward.
26         status = mcmc_parse_buf(rbuf, bread, &resp);
27     } while (resp.code == MCMC_WANT_READ);
28 
29     if (status == MCMC_ERR) {
30         printf("bad response\n");
31     }
32 
33     // now we need to read the value back.
34     // resp.reslen + resp.vlen is the total length.
35     // resp.reslen + resp.vlen_read is how much of the buffer was used.
36     // resp.vlen_read vs resp.vlen is how much was read vs how much still
37     // needs to be read from the socket.
38     if (resp.vlen != resp.vlen_read) {
39         // malloc and recv the rest.
40         // can/should add convenience functions for this?
41         val = malloc(resp.vlen);
42         memcpy(val, resp.value, resp.vlen_read);
43         size_t toread = resp.vlen - resp.vlen_read;
44         char *buf = val + resp.vlen_read;
45         do {
46             // TODO: bug: check for read == 0
47             int read = recv(mcmc_fd(c), buf, toread, 0);
48             toread -= read;
49         } while (toread > 0);
50     } else {
51         val = resp.value;
52     }
53 
54     // TODO: add the rest of the parser loop.
55     printf("read a response: %s\n", rbuf);
56 }
57 /*
58 static void show_response(void *c, char *rbuf, size_t bufsize) {
59     int status;
60     // buffer shouldn't change until the read is completed.
61     mcmc_resp_t resp;
62     int go = 1;
63     while (go) {
64         go = 0;
65         // TODO: return flags? VALUE_PARTIAL flag?
66         status = mcmc_read(c, rbuf, bufsize, &resp);
67         if (status == MCMC_OK) {
68             // OK means a response of some kind was read.
69             char *val;
70             // NOTE: is "it's not a miss, and vlen is 0" enough to indicate that
71             // a 0 byte value was returned?
72             if (resp.vlen != 0) {
73                 if (resp.vlen == resp.vlen_read) {
74                     val = resp.value;
75                 } else {
76                     val = malloc(resp.vlen);
77                     int read = 0;
78                     do {
79                         status = mcmc_read_value(c, val, &resp, &read);
80                     } while (status == MCMC_WANT_READ);
81                 }
82                 if (resp.vlen > 0) {
83                     val[resp.vlen-1] = '\0';
84                     printf("Response value: %s\n", val);
85                 }
86             }
87             switch (resp.type) {
88                 case MCMC_RESP_GET:
89                     // GET's need to continue until END is seen.
90                     printf("in GET mode\n");
91                     go = 1;
92                     break;
93                 case MCMC_RESP_END: // ascii done-with-get's
94                     printf("END seen\n");
95                     break;
96                 case MCMC_RESP_META: // any meta command. they all return the same.
97                     printf("META response seen\n");
98                     if (resp.rlen > 0) {
99                         resp.rline[resp.rlen-1] = '\0';
100                         printf("META response line: %s\n", resp.rline);
101                     }
102                     break;
103                 case MCMC_RESP_STAT:
104                     // STAT responses. need to call mcmc_read() in loop until
105                     // we get an end signal.
106                     go = 1;
107                     break;
108                 default:
109                     // TODO: type -> str func.
110                     fprintf(stderr, "unknown response type: %d\n", resp.type);
111                     break;
112             }
113         } else {
114             // some kind of command specific error code (management commands)
115             // or protocol error status.
116             char code[MCMC_ERROR_CODE_MAX];
117             char msg[MCMC_ERROR_MSG_MAX];
118             mcmc_get_error(c, code, MCMC_ERROR_CODE_MAX, msg, MCMC_ERROR_MSG_MAX);
119             fprintf(stderr, "Got error from mc: status [%d] code [%s] msg: [%s]\n", status, code, msg);
120             // some errors don't have a msg. in this case msg[0] will be \0
121         }
122 
123         int remain = 0;
124         // advance us to the next command in the buffer, or ready for the next
125         // mc_read().
126         char *newbuf = mcmc_buffer_consume(c, &remain);
127         printf("remains in buffer: %d\n", remain);
128         if (remain == 0) {
129             assert(newbuf == NULL);
130             // we're done.
131         } else {
132             // there're still some bytes unconsumed by the client.
133             // ensure the next time we call the client, the buffer has those
134             // bytes at the front still.
135             // NOTE: this _could_ be an entirely different buffer if we copied
136             // the data off. The client is just tracking the # of bytes it
137             // didn't gobble.
138             // In this case we shuffle the bytes back to the front of our read
139             // buffer.
140             memmove(rbuf, newbuf, remain);
141         }
142     }
143 }
144 */
buffer_mode(void)145 void buffer_mode(void) {
146     void *c = malloc(mcmc_size(MCMC_OPTION_BLANK));
147     size_t bufsize = mcmc_min_buffer_size(MCMC_OPTION_BLANK) * 2;
148     char *rbuf = malloc(bufsize);
149 
150     int status = mcmc_connect(c, "127.0.0.1", "11211", MCMC_OPTION_BLANK);
151 
152     char *requests[5] = {"get foo\r\n",
153         "get foob\r\n",
154         "mg foo s t v\r\n",
155         "mg doof s t v Omoo k\r\n",
156         ""};
157 
158     for (int x = 0; strlen(requests[x]) != 0; x++) {
159         status = mcmc_send_request(c, requests[x], strlen(requests[x]), 1);
160 
161         if (status != MCMC_OK) {
162             fprintf(stderr, "Failed to send request to memcached\n");
163             return;
164         }
165 
166         // Regardless of what command we sent, this should print out the response.
167         show_response_buffer(c, rbuf, bufsize);
168     }
169 
170 }
171 
main(int argc,char * agv[])172 int main (int argc, char *agv[]) {
173     // TODO: detect if C is pre-C11?
174     printf("C version: %ld\n", __STDC_VERSION__);
175 
176     if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
177         perror("signal");
178         exit(1);
179     }
180 /*
181     void *c = malloc(mcmc_size(MCMC_OPTION_BLANK));
182     // we only "need" the minimum buf size.
183     // buffers large enough to fit return values result in fewer syscalls.
184     size_t bufsize = mcmc_min_buffer_size(MCMC_OPTION_BLANK) * 2;
185     // buffers are also generally agnostic to clients. The buffer must be
186     // held and re-used when required by the API. When the buffer is empty,
187     // it may be released to a pool or reused with other connections.
188     char *rbuf = malloc(bufsize);
189 
190     int status;
191 
192     // API is blocking by default.
193     status = mcmc_connect(c, "127.0.0.1", "11211", MCMC_OPTION_BLANK);
194 
195     if (status != MCMC_CONNECTED) {
196         // TODO: mc_strerr(c);
197         fprintf(stderr, "Failed to connect to memcached\n");
198         return -1;
199     }
200 
201     char *requests[5] = {"get foo\r\n",
202         "get foob\r\n",
203         "mg foo s t v\r\n",
204         "mg doof s t v Omoo k\r\n",
205         ""};
206 
207     for (int x = 0; strlen(requests[x]) != 0; x++) {
208         // provide a buffer, the buffer length, and the number of responses
209         // expected. ie; if pipelining many requests, or using noreply semantics.
210         // FIXME: not confident "number of expected responses" is worth tracking
211         // internally.
212         status = mcmc_send_request(c, requests[x], strlen(requests[x]), 1);
213         //printf("sent request: %s\n", requests[x]);
214 
215         if (status != MCMC_OK) {
216             fprintf(stderr, "Failed to send request to memcached\n");
217             return -1;
218         }
219 
220         // Regardless of what command we sent, this should print out the response.
221         // TODO: mcmc_read() and friends need to be remade
222         show_response(c, rbuf, bufsize);
223 
224     }
225 
226     status = mcmc_disconnect(c);
227     // The only free'ing needed.
228     free(c);
229 */
230     // TODO: stats example.
231 
232     /*
233     // nonblocking example.
234     c = malloc(mcmc_size(MCMC_OPTION_BLANK));
235     // reuse bufsize/rbuf.
236     status = mcmc_connect(c, "127.0.0.1", "11211", MCMC_OPTION_NONBLOCK);
237     printf("nonblock connecting...\n");
238     struct pollfd pfds[1];
239     if (status == MCMC_CONNECTING) {
240         // need to wait for socket to become writeable.
241         pfds[0].fd = mcmc_fd(c);
242         pfds[0].events = POLLOUT;
243         if (poll(pfds, 1, 1000) != 1) {
244             fprintf(stderr, "poll on connect timed out or failed\n");
245             return -1;
246         }
247         int err = 0;
248         if (pfds[0].revents & POLLOUT && mcmc_check_nonblock_connect(c, &err) == MCMC_OK) {
249             printf("asynchronous connection completed: %d\n", err);
250         } else {
251             printf("failed to connect: %s\n", strerror(err));
252             return -1;
253         }
254     } else {
255         perror("connect");
256         fprintf(stderr, "bad response to nonblock connection: %d\n", status);
257         return -1;
258     }
259 
260     // TODO: check socket for errors.
261 
262     // TODO: send request
263     status = mcmc_send_request(c, requests[0], strlen(requests[0]), 1);
264     //printf("sent request: %s\n", requests[x]);
265 
266     if (status != MCMC_OK) {
267         fprintf(stderr, "Failed to send request to memcached\n");
268         return -1;
269     }
270 
271     mcmc_resp_t resp;
272     status = mcmc_read(c, rbuf, bufsize, &resp);
273     // this could race and fail, depending on the system.
274     if (status == MCMC_WANT_READ) {
275         printf("got MCMC_WANT_READ from a too-fast read as expected\n");
276         pfds[0].fd = mcmc_fd(c);
277         pfds[0].events = POLLIN;
278         if (poll(pfds, 1, 1000) != 1) {
279             fprintf(stderr, "poll on connect timed out or failed\n");
280             return -1;
281         }
282         if (pfds[0].revents & POLLIN) {
283             printf("asynchronous read ready\n");
284         }
285 
286         show_response(c, rbuf, bufsize);
287     }
288     */
289 
290     buffer_mode();
291     return 0;
292 }
293