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