1 /* -*- Mode: C; tab-width: 2; c-basic-offset: 2; indent-tabs-mode: nil -*- */
3 * What is a library without an example to show you how to use the library?
4 * This example use both interfaces to implement a small memcached server.
5 * Please note that this is an exemple on how to use the library, not
6 * an implementation of a scalable memcached server. If you look closely
7 * at the example it isn't even multithreaded ;-)
9 * With that in mind, let me give you some pointers into the source:
10 * storage.c/h - Implements the item store for this server and not really
11 * interesting for this example.
12 * interface_v0.c - Shows an implementation of the memcached server by using
13 * the "raw" access to the packets as they arrive
14 * interface_v1.c - Shows an implementation of the memcached server by using
15 * the more "logical" interface.
16 * memcached_light.c - This file sets up all of the sockets and run the main
20 * config.h is included so that I can use the ntohll/htonll on platforms that
21 * doesn't have that (this is a private function inside libmemcached, so you
22 * cannot use it directly from libmemcached without special modifications to
28 #include <sys/types.h>
29 #include <sys/socket.h>
31 #include <netinet/tcp.h>
40 #include <libmemcached/protocol_handler.h>
41 #include <libmemcached/byteorder.h>
43 #include "memcached_light.h"
45 extern memcached_binary_protocol_callback_st interface_v0_impl
;
46 extern memcached_binary_protocol_callback_st interface_v1_impl
;
48 static int server_sockets
[1024];
49 static int num_server_sockets
= 0;
50 static void* socket_userdata_map
[1024];
51 static bool verbose
= false;
59 typedef struct options_st options_st
;
62 * Create a socket and bind it to a specific port number
63 * @param port the port number to bind to
65 static int server_socket(const char *port
)
68 struct addrinfo hints
= { .ai_flags
= AI_PASSIVE
,
69 .ai_family
= AF_UNSPEC
,
70 .ai_socktype
= SOCK_STREAM
};
72 int error
= getaddrinfo("127.0.0.1", port
, &hints
, &ai
);
75 if (error
!= EAI_SYSTEM
)
76 fprintf(stderr
, "getaddrinfo(): %s\n", gai_strerror(error
));
78 perror("getaddrinfo()");
83 struct linger ling
= {0, 0};
85 for (struct addrinfo
*next
= ai
; next
; next
= next
->ai_next
)
87 int sock
= socket(ai
->ai_family
, ai
->ai_socktype
, ai
->ai_protocol
);
90 perror("Failed to create socket");
94 int flags
= fcntl(sock
, F_GETFL
, 0);
97 perror("Failed to get socket flags");
102 if ((flags
& O_NONBLOCK
) != O_NONBLOCK
)
104 if (fcntl(sock
, F_SETFL
, flags
| O_NONBLOCK
) == -1)
106 perror("Failed to set socket to nonblocking mode");
113 if (setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
, (void *)&flags
, sizeof(flags
)) != 0)
114 perror("Failed to set SO_REUSEADDR");
116 if (setsockopt(sock
, SOL_SOCKET
, SO_KEEPALIVE
, (void *)&flags
, sizeof(flags
)) != 0)
117 perror("Failed to set SO_KEEPALIVE");
119 if (setsockopt(sock
, SOL_SOCKET
, SO_LINGER
, (void *)&ling
, sizeof(ling
)) != 0)
120 perror("Failed to set SO_LINGER");
122 if (setsockopt(sock
, IPPROTO_TCP
, TCP_NODELAY
, (void *)&flags
, sizeof(flags
)) != 0)
123 perror("Failed to set TCP_NODELAY");
125 if (bind(sock
, next
->ai_addr
, next
->ai_addrlen
) == -1)
127 if (errno
!= EADDRINUSE
)
136 if (listen(sock
, 1024) == -1)
143 server_sockets
[num_server_sockets
++]= sock
;
148 return (num_server_sockets
> 0) ? 0 : 1;
152 * Convert a command code to a textual string
153 * @param cmd the comcode to convert
154 * @return a textual string with the command or NULL for unknown commands
156 static const char* comcode2str(uint8_t cmd
)
158 static const char * const text
[] = {
159 "GET", "SET", "ADD", "REPLACE", "DELETE",
160 "INCREMENT", "DECREMENT", "QUIT", "FLUSH",
161 "GETQ", "NOOP", "VERSION", "GETK", "GETKQ",
162 "APPEND", "PREPEND", "STAT", "SETQ", "ADDQ",
163 "REPLACEQ", "DELETEQ", "INCREMENTQ", "DECREMENTQ",
164 "QUITQ", "FLUSHQ", "APPENDQ", "PREPENDQ"
167 if (cmd
<= PROTOCOL_BINARY_CMD_PREPENDQ
)
174 * Print out the command we are about to execute
176 static void pre_execute(const void *cookie
__attribute__((unused
)),
177 protocol_binary_request_header
*header
__attribute__((unused
)))
181 const char *cmd
= comcode2str(header
->request
.opcode
);
183 fprintf(stderr
, "pre_execute from %p: %s\n", cookie
, cmd
);
185 fprintf(stderr
, "pre_execute from %p: 0x%02x\n", cookie
, header
->request
.opcode
);
190 * Print out the command we just executed
192 static void post_execute(const void *cookie
__attribute__((unused
)),
193 protocol_binary_request_header
*header
__attribute__((unused
)))
197 const char *cmd
= comcode2str(header
->request
.opcode
);
199 fprintf(stderr
, "post_execute from %p: %s\n", cookie
, cmd
);
201 fprintf(stderr
, "post_execute from %p: 0x%02x\n", cookie
, header
->request
.opcode
);
206 * Callback handler for all unknown commands.
207 * Send an unknown command back to the client
209 static protocol_binary_response_status
unknown(const void *cookie
,
210 protocol_binary_request_header
*header
,
211 memcached_binary_protocol_raw_response_handler response_handler
)
213 protocol_binary_response_no_extras response
= {
216 .magic
= PROTOCOL_BINARY_RES
,
217 .opcode
= header
->request
.opcode
,
218 .status
= htons(PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND
),
219 .opaque
= header
->request
.opaque
224 return response_handler(cookie
, header
, (void*)&response
);
227 static void work(void);
230 * Program entry point. Bind to the specified port(s) and serve clients
232 * @param argc number of items in the argument vector
233 * @param argv argument vector
234 * @return 0 on success, 1 otherwise
236 int main(int argc
, char **argv
)
239 memcached_binary_protocol_callback_st
*interface
= &interface_v0_impl
;
241 memset(&global_options
, 0, sizeof(global_options
));
244 * We need to initialize the handlers manually due to a bug in the
245 * warnings generated by struct initialization in gcc (all the way up to 4.4)
247 initialize_interface_v0_handler();
249 while ((cmd
= getopt(argc
, argv
, "v1pP:?h")) != EOF
)
253 interface
= &interface_v1_impl
;
256 global_options
.pid_file
= strdup(optarg
);
259 global_options
.has_port
= true;
260 (void)server_socket(optarg
);
265 case 'h': /* FALLTHROUGH */
266 case '?': /* FALLTHROUGH */
268 (void)fprintf(stderr
, "Usage: %s [-p port] [-v] [-1]\n", argv
[0]);
273 if (! initialize_storage())
275 /* Error message already printed */
279 if (! global_options
.has_port
)
280 (void)server_socket("9999");
282 if (global_options
.pid_file
)
287 pid_file
= fopen(global_options
.pid_file
, "w+");
288 perror(strerror(errno
));
290 if (pid_file
== NULL
)
293 pid
= (uint32_t)getpid();
294 fprintf(pid_file
, "%u\n", pid
);
298 if (num_server_sockets
== 0)
300 fprintf(stderr
, "I don't have any server sockets\n");
305 * Create and initialize the handles to the protocol handlers. I want
306 * to be able to trace the traffic throught the pre/post handlers, and
307 * set up a common handler for unknown messages
309 interface
->pre_execute
= pre_execute
;
310 interface
->post_execute
= post_execute
;
311 interface
->unknown
= unknown
;
313 struct memcached_protocol_st
*protocol_handle
;
314 if ((protocol_handle
= memcached_protocol_create_instance()) == NULL
)
316 fprintf(stderr
, "Failed to allocate protocol handle\n");
320 memcached_binary_protocol_set_callbacks(protocol_handle
, interface
);
321 memcached_binary_protocol_set_pedantic(protocol_handle
, true);
323 for (int xx
= 0; xx
< num_server_sockets
; ++xx
)
324 socket_userdata_map
[server_sockets
[xx
]]= protocol_handle
;
326 /* Serve all of the clients */
333 static void work(void)
335 #define MAX_SERVERS_TO_POLL 100
336 struct pollfd fds
[MAX_SERVERS_TO_POLL
];
339 for (max_poll
= 0; max_poll
< num_server_sockets
; ++max_poll
)
341 fds
[max_poll
].events
= POLLIN
;
342 fds
[max_poll
].revents
= 0;
343 fds
[max_poll
].fd
= server_sockets
[max_poll
];
348 int err
= poll(fds
, (nfds_t
)max_poll
, -1);
350 if (err
== 0 || (err
== -1 && errno
!= EINTR
))
352 perror("poll() failed");
356 /* find the available filedescriptors */
357 for (int x
= max_poll
- 1; x
> -1 && err
> 0; --x
)
359 if (fds
[x
].revents
!= 0)
362 if (x
< num_server_sockets
)
364 /* accept new client */
365 struct sockaddr_storage addr
;
366 socklen_t addrlen
= sizeof(addr
);
367 int sock
= accept(fds
[x
].fd
, (struct sockaddr
*)&addr
,
372 perror("Failed to accept client");
376 struct memcached_protocol_st
*protocol
;
377 protocol
= socket_userdata_map
[fds
[x
].fd
];
379 struct memcached_protocol_client_st
* c
;
380 c
= memcached_protocol_create_client(protocol
, sock
);
383 fprintf(stderr
, "Failed to create client\n");
388 socket_userdata_map
[sock
]= c
;
389 fds
[max_poll
].events
= POLLIN
;
390 fds
[max_poll
].revents
= 0;
391 fds
[max_poll
].fd
= sock
;
397 /* drive the client */
398 struct memcached_protocol_client_st
* c
;
399 c
= socket_userdata_map
[fds
[x
].fd
];
401 fds
[max_poll
].events
= 0;
403 memcached_protocol_event_t events
= memcached_protocol_client_work(c
);
404 if (events
& MEMCACHED_PROTOCOL_WRITE_EVENT
)
405 fds
[max_poll
].events
= POLLOUT
;
407 if (events
& MEMCACHED_PROTOCOL_READ_EVENT
)
408 fds
[max_poll
].events
= POLLIN
;
410 if (!(events
& MEMCACHED_PROTOCOL_PAUSE_EVENT
||
411 fds
[max_poll
].events
!= 0))
413 memcached_protocol_client_destroy(c
);
417 if (x
!= max_poll
- 1)
418 memmove(fds
+ x
, fds
+ x
+ 1, (size_t)(max_poll
- x
));