1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 
6 #include "protocol_extension.h"
7 
8 /*
9  * This is an example on how you can add your own commands into the
10  * ascii protocol. You load the extensions into memcached by using
11  * the -X option:
12  * ./memcached -X .libs/example_protocol.so -E .libs/default_engine.so
13  *
14  * @todo add an example that require extra userspace data, and communicates
15  *       with the engine by getting the engine descriptor.
16  */
17 
18 static const char *get_name(const void *cmd_cookie);
19 static bool accept_command(const void *cmd_cookie, void *cookie,
20                            int argc, token_t *argv, size_t *ndata,
21                            char **ptr);
22 static ENGINE_ERROR_CODE execute_command(const void *cmd_cookie, const void *cookie,
23                                          int argc, token_t *argv,
24                                          ENGINE_ERROR_CODE (*response_handler)(const void *cookie,
25                                                                                int nbytes,
26                                                                                const char *dta));
27 static void abort_command(const void *cmd_cookie, const void *cookie);
28 
29 static EXTENSION_ASCII_PROTOCOL_DESCRIPTOR noop_descriptor = {
30     .get_name = get_name,
31     .accept = accept_command,
32     .execute = execute_command,
33     .abort = abort_command,
34     .cookie = &noop_descriptor
35 };
36 
37 static EXTENSION_ASCII_PROTOCOL_DESCRIPTOR echo_descriptor = {
38     .get_name = get_name,
39     .accept = accept_command,
40     .execute = execute_command,
41     .abort = abort_command,
42     .cookie = &echo_descriptor
43 };
44 
get_name(const void * cmd_cookie)45 static const char *get_name(const void *cmd_cookie) {
46     if (cmd_cookie == &noop_descriptor) {
47         return "noop";
48     } else {
49         return "echo";
50     }
51 }
52 
accept_command(const void * cmd_cookie,void * cookie,int argc,token_t * argv,size_t * ndata,char ** ptr)53 static bool accept_command(const void *cmd_cookie, void *cookie,
54                            int argc, token_t *argv, size_t *ndata,
55                            char **ptr) {
56     if (cmd_cookie == &noop_descriptor) {
57         return strcmp(argv[0].value, "noop") == 0;
58     } else {
59         return strcmp(argv[0].value, "echo") == 0;
60     }
61 }
62 
execute_command(const void * cmd_cookie,const void * cookie,int argc,token_t * argv,ENGINE_ERROR_CODE (* response_handler)(const void * cookie,int nbytes,const char * dta))63 static ENGINE_ERROR_CODE execute_command(const void *cmd_cookie, const void *cookie,
64                                          int argc, token_t *argv,
65                                          ENGINE_ERROR_CODE (*response_handler)(const void *cookie,
66                                                                                int nbytes,
67                                                                                const char *dta))
68 {
69     if (cmd_cookie == &noop_descriptor) {
70         return response_handler(cookie, 4, "OK\r\n");
71     } else {
72         if (response_handler(cookie, argv[0].length, argv[0].value) != ENGINE_SUCCESS) {
73             return ENGINE_DISCONNECT;
74         }
75 
76         for (int ii = 1; ii < argc; ++ii) {
77             if (response_handler(cookie, 2, " [") != ENGINE_SUCCESS ||
78                 response_handler(cookie, argv[ii].length, argv[ii].value) != ENGINE_SUCCESS ||
79                 response_handler(cookie, 1, "]") != ENGINE_SUCCESS) {
80                 return ENGINE_DISCONNECT;
81             }
82         }
83 
84         return response_handler(cookie, 2, "\r\n");
85     }
86 }
87 
abort_command(const void * cmd_cookie,const void * cookie)88 static void abort_command(const void *cmd_cookie, const void *cookie)
89 {
90     /* EMPTY */
91 }
92 
93 #if defined (__SUNPRO_C) && (__SUNPRO_C >= 0x550)
94 __global
95 #elif defined __GNUC__
96 __attribute__ ((visibility("default")))
97 #endif
memcached_extensions_initialize(const char * config,GET_SERVER_API get_server_api)98 EXTENSION_ERROR_CODE memcached_extensions_initialize(const char *config,
99                                                      GET_SERVER_API get_server_api) {
100     SERVER_HANDLE_V1 *server = get_server_api();
101     if (server == NULL) {
102         return EXTENSION_FATAL;
103     }
104     if (!server->extension->register_extension(EXTENSION_ASCII_PROTOCOL,
105                                                &noop_descriptor)) {
106         return EXTENSION_FATAL;
107     }
108 
109     if (!server->extension->register_extension(EXTENSION_ASCII_PROTOCOL,
110                                                &echo_descriptor)) {
111         return EXTENSION_FATAL;
112     }
113 
114     return EXTENSION_SUCCESS;
115 }
116