1 /*-
2 * This file is a part of MDCacheD
3 * (c) 2007.-2011. Ivan Voras <ivoras@gmail.com>
4 * Released under the 2-clause BSDL
5 * $Id: mc_protocol.h 443 2012-03-06 23:23:48Z ivoras $
6 */
7
8 /*
9 * MDCacheD protocol-related definitions.
10 */
11
12 #ifndef MC_PROTOCOL_H
13 #define MC_PROTOCOL_H
14
15 #include <stdint.h>
16
17 #ifdef __cplusplus
18 extern "C" {
19 #endif
20
21 #ifndef TRUE
22 #define TRUE 1
23 #define FALSE (!TRUE)
24 #endif
25
26 #define MC_PROTOCOL_VERSION 1
27 #define MC_PROTOCOL_VERSION_ID "$Id: mc_protocol.h 443 2012-03-06 23:23:48Z ivoras $"
28
29 /*
30 * Depends on: nothing.
31 * This file is usable as a client-side header if the MDCACHED_SERVER symbol
32 * is not defined.
33 */
34
35
36 /*
37 * This defines a network protocol so anything that goes over the wire must be
38 * declared here.
39 */
40
41 #define MC_DEFAULT_INET_PORT 1222
42 #define MC_BASE_UNIX_SOCKET "/tmp/mdcached"
43 #define MC_XSTR(s) #s
44 #define MC_GET_UNIX_SOCKET_PORT(port) (MC_BASE_UNIX_SOCKET "." MC_XSTR(port))
45 #define MC_DEFAULT_UNIX_SOCKET MC_GET_UNIX_SOCKET_PORT(MC_DEFAULT_INET_PORT)
46
47 /**
48 * Cutoff time in seconds. Time specifications less than this number
49 * of seconds will be treated as relative to current time.
50 */
51 #define REL_TIME_CUTOFF (60*60*24*30)
52
53 #ifdef TAGS_64BIT
54 #warning Record tags redefined as 64-bit!
55 typedef int64_t tag_value_t; /* tag names and tag values are integers */
56 typedef int64_t tag_key_t;
57 #else
58 typedef int32_t tag_key_t;
59 typedef int32_t tag_value_t;
60 #endif
61
62 #define MCMD_DATA_ENTRY MCMD_PUT_DATA
63
64 enum MCMD {
65 /* commands */
66 MCMD_INVALID = 0,
67 MCMD_HANDSHAKE = 1,
68 MCMD_PUT_DATA = 2,
69 MCMD_DEL_DATA_NAME = 3,
70 MCMD_DEL_DATA_BY_TAG_VALUES = 4,
71 MCMD_GET_DATA_NAME = 5,
72 MCMD_GET_DATA_BY_TAG_VALUES = 6,
73 MCMD_GET_ALL_KEYS = 7,
74 MCMD_ATOMIC_CMPSET = 8,
75 MCMD_ATOMIC_ADD = 9,
76 MCMD_ATOMIC_FETCHADD = 10,
77 MCMD_ATOMIC_READANDCLEAR = 11,
78 MCMD_MPUT = 12,
79 MCMD_MGET = 13,
80 MCMD_MDEL = 14,
81 MCMD_TSTACK_PUSH = 15,
82 MCMD_TSTACK_POP = 16,
83 MCMD_SET_TAGS = 17,
84
85 /* responses */
86 MCRESP_STATUS = 100,
87 MCRESP_HANDSHAKE = 101,
88 MCRESP_DATA = 102,
89 MCRESP_MULTIDATA = 103,
90 MCRESP_MULTISTRING = 104,
91 MCRESP_ATOMIC = 105,
92 MCRESP_TSTACK_PUSH = 106,
93 MCRESP_TSTACK_POP = 107
94 };
95
96
97 enum MCSTATUS {
98 MCSTATUS_OK,
99 MCSTATUS_ERROR
100 };
101
102
103 enum MCREASON {
104 MCREASON_OK = 0,
105 MCREASON_INVALID,
106 MCREASON_ENDIANESS,
107 MCREASON_VERSION,
108 MCREASON_INVALIDCMD,
109 MCREASON_CACHE,
110 MCREASON_NOTFOUND,
111 MCREASON_SERVER,
112 MCREASON_PARAM,
113 MCREASON_ENOMEM
114 };
115
116 /* Header flags and definitions */
117 #define MC_ENDIAN_SIGNATURE 0x1234abcd
118 #define MC_VER_MAJOR 0
119 #define MC_VER_MINOR 1
120 #define MC_FLAG_REPLY 0x01
121
122 /* Command flags */
123 #define MCMD_FLAG_ATOMICALL 0x1
124
125 /*
126 * Command and response packets use the same over the wire format, starting with
127 * mc_header and then continuing with the data.
128 * Data layout in packets:
129 * - header (mc_header)
130 * - packet specific struct (mc_handshake | mc_add_data | ...)
131 * - variable-length data specific to command
132 * General layout of the variable section is: (not applicable fields are skipped)
133 * - tags (num_tags, tags)
134 * - name (name_length, name)
135 * - data (data_length, data)
136 * - name and data are variable-length, non-0-terminated binary strings.
137 * - name is limited to 64 kB, data to 4 GB.
138 */
139
140 #ifndef __packed
141 #define __packed __attribute__((__packed__))
142 #endif
143
144
145 /* Record tag */
146 struct __packed mc_tag {
147 tag_key_t key;
148 tag_value_t value;
149 };
150
151
152 /**
153 * Key name simple definition
154 */
155 struct __packed mc_key_name {
156 uint16_t name_size;
157 char name[];
158 };
159
160
161 /**
162 * Key data simple definition
163 */
164 struct __packed mc_key_data {
165 uint32_t data_size;
166 char data[];
167 };
168
169
170 /**
171 * Command packet header. This tructure is at the beginning of *every* command
172 * packet and response in the client-server protocol.
173 */
174 struct __packed mc_header {
175 uint8_t mcmd; /* Command type */
176 uint8_t flags; /* Command flags */
177 uint16_t seq; /* Command id (sequential) */
178 uint32_t total_size; /* Total size of the command packet, including this header */
179 };
180
181
182 /** "Handshake" command packet */
183 struct __packed mc_handshake {
184 struct mc_header header;
185 int32_t endian_signature;
186 uint16_t proto_ver_major;
187 uint16_t proto_ver_minor;
188 };
189
190
191 /** A "simple request" packet. Doesn't carry any data. */
192 struct __packed mc_simple {
193 struct mc_header header;
194 uint32_t flags;
195 uint32_t param;
196 };
197
198
199 /**
200 * "Add data" command packet. The payload (data field) contains an array of
201 * struct mc_tag (n_tags items), followed by the name (of the length name_size),
202 * followed by the record value/data (of the length data_size).
203 */
204 struct __packed mc_data_entry {
205 struct mc_header header;
206 uint16_t n_tags;
207 uint16_t name_size;
208 uint32_t data_size;
209 uint32_t exptime;
210 char data[]; /* Dummy placeholder */
211 };
212
213 #define mc_add_data mc_data_entry
214 #define mc_resp_data mc_data_entry
215
mc_data_entry_tags(struct mc_data_entry * md)216 static inline struct mc_tag* mc_data_entry_tags(struct mc_data_entry *md) {
217 return (struct mc_tag*)(md->data);
218 }
219
mc_data_entry_tag(struct mc_data_entry * md,unsigned n)220 static inline struct mc_tag* mc_data_entry_tag(struct mc_data_entry *md, unsigned n) {
221 return &(mc_data_entry_tags(md)[n]);
222 }
223
mc_data_entry_n_tags(struct mc_data_entry * md)224 static inline unsigned mc_data_entry_n_tags(struct mc_data_entry *md) {
225 return md->n_tags;
226 }
227
mc_data_entry_key(struct mc_data_entry * md)228 static inline char* mc_data_entry_key(struct mc_data_entry *md) {
229 return md->data + md->n_tags * sizeof(struct mc_tag);
230 }
231
mc_data_entry_key_size(struct mc_data_entry * md)232 static inline unsigned mc_data_entry_key_size(struct mc_data_entry *md) {
233 return md->name_size;
234 }
235
mc_data_entry_value(struct mc_data_entry * md)236 static inline char* mc_data_entry_value(struct mc_data_entry *md) {
237 return md->data + md->n_tags * sizeof(struct mc_tag) + md->name_size;
238 }
239
mc_data_entry_value_size(struct mc_data_entry * md)240 static inline unsigned mc_data_entry_value_size(struct mc_data_entry *md) {
241 return md->data_size;
242 }
243
244 /** "Delete data by name" command packet */
245 struct __packed mc_del_data_name {
246 struct mc_header header;
247 uint16_t name_size;
248 uint16_t _unused;
249 char data[];
250 };
251
252
253 /** "Delete data by tag values" command packet */
254 struct __packed mc_del_data_by_tag_values {
255 struct mc_header header;
256 uint32_t n_values;
257 tag_key_t tag_key;
258 char data[];
259 };
260
261
262 /** "Get data by name" command packet */
263 struct __packed mc_get_data_name {
264 struct mc_header header;
265 uint16_t name_size;
266 uint16_t _unused;
267 char data[];
268 };
269
270
271 /** "Get data by tags" command packet */
272 struct __packed mc_get_data_by_tag_values {
273 struct mc_header header;
274 uint32_t n_values;
275 tag_key_t tag_key;
276 char data[];
277 };
278
279
280 /** A common command packet for simple atomic ops */
281 struct __packed mc_atomic_op {
282 struct mc_header header;
283 int64_t arg1;
284 int64_t arg2;
285 uint16_t name_size;
286 uint16_t _unused;
287 char name[];
288 };
289
290
291 /**
292 * A multi-put operation: inserts multiple records into the datastore. The data
293 * contains n_records repetitions of mc_data_entry records.
294 */
295 struct __packed mc_mput {
296 struct mc_header header;
297 uint16_t n_records;
298 uint16_t flags;
299 char data[];
300 };
301
302
303 /**
304 * A multi-get operation: gets multiple records by their names. The data
305 * contains n_names repetitions of (uint16_t name length, name bytes)
306 */
307 struct __packed mc_mget {
308 struct mc_header header;
309 uint16_t n_names;
310 uint16_t flags;
311 char data[];
312 };
313
314
315 /**
316 * A multi-delete operation: deletes multiple records by their names. The data
317 * contains n_names repetitions of (uint16_t name length, name bytes)
318 */
319 struct __packed mc_mdel {
320 struct mc_header header;
321 uint16_t n_names;
322 uint16_t flags;
323 char data[];
324 };
325
326
327 /**
328 * The tag-stack push operation. Data contains n_tags repetitions of the struct
329 * mc_tag followed by the data of data_size bytes. Response is mc_resp_tstack_push.
330 */
331 struct __packed mc_tstack_push {
332 struct mc_header header;
333 uint16_t flags;
334 uint16_t n_tags;
335 uint32_t data_size;
336 char data[];
337 };
338
339
340 /**
341 * The tag-stack pop operation. The tag (key and value) references the virtual
342 * stack from which the record is popped (returned & removed). Response is mc_resp_
343 */
344 struct __packed mc_tstack_pop {
345 struct mc_header header;
346 struct mc_tag tag;
347 uint16_t flags;
348 };
349
350
351 /**
352 * (Re)set record tags. The data consists of the record tags and the record name
353 * (in that order).
354 */
355 struct __packed mc_set_tags {
356 struct mc_header header;
357 uint16_t n_tags;
358 uint16_t name_size;
359 uint16_t flags;
360 uint16_t _unused;
361 char data[];
362 };
363
364
365 /**
366 * Status response packet
367 */
368 struct __packed mc_resp_status {
369 struct mc_header header;
370 uint16_t status;
371 uint16_t data;
372 };
373
374
375 /**
376 * "Multi data" response - when a request matches multiple data items. The data
377 * payload contains concatenated struct mc_data_entry blobs, unaligned.
378 */
379 struct __packed mc_resp_multidata {
380 struct mc_header header;
381 uint32_t n_records;
382 char data[];
383 };
384
385
386 /**
387 * "Multi string" response - when a command returns an arbitrary list of strings.
388 * The data payload consts of a concatenated list of (uint16_t name_len, char name[name_len])
389 * records, unaligned. The length is declared as short unsigned integer as this
390 * response type is intended to return record keys (key names).
391 */
392 struct __packed mc_resp_multistring {
393 struct mc_header header;
394 uint32_t n_records;
395 uint32_t data_size;
396 char data[];
397 };
398
399
400 /**
401 * A simple reply to an atomic op.
402 */
403 struct __packed mc_resp_atomic {
404 struct mc_header header;
405 int64_t result;
406 };
407
408
409 /**
410 * The response to struct mc_tstack_push. It contains the unique name generated
411 * for the record.
412 */
413 struct __packed mc_resp_tstack_push {
414 struct mc_header header;
415 uint16_t name_size;
416 char name[];
417 };
418
419
420 /**
421 * Update the common fields in the packet.
422 */
423 static inline void
mc_init_packet(void * packet,const unsigned int size,const unsigned short int seq,const short int cmd)424 mc_init_packet(void *packet, const unsigned int size, const unsigned short int seq, const short int cmd)
425 {
426 struct mc_header *pkt = (struct mc_header*) packet;
427 pkt->mcmd = cmd;
428 pkt->flags = 0;
429 pkt->seq = seq;
430 pkt->total_size = size;
431 }
432
433
434 /**
435 * Builds a mc_data_entry structure / packet from the given parts. All parameters
436 * except the tags are mandatory, and the tags and n_tags can be NULL. It
437 * allocates the resulting mde structures (the caller must free it).
438 */
439 static inline int
mc_build_data_entry(struct mc_data_entry ** mde,const char * key,unsigned int key_len,const char * data,unsigned int data_len,const struct mc_tag * tags,unsigned int n_tags,time_t exptime,unsigned short int * seq)440 mc_build_data_entry(struct mc_data_entry **mde, const char *key, unsigned int key_len, const char *data, unsigned int data_len, const struct mc_tag *tags, unsigned int n_tags, time_t exptime, unsigned short int *seq)
441 {
442 unsigned mde_size;
443 char *d;
444
445 if (key_len == 0 || data_len == 0)
446 return MCREASON_PARAM;
447 if ((tags == NULL && n_tags != 0) || (tags != NULL && n_tags == 0))
448 return MCREASON_PARAM;
449 mde_size = sizeof(struct mc_data_entry) + key_len + data_len + n_tags * sizeof(struct mc_tag);
450 *mde = (struct mc_data_entry*)malloc(mde_size);
451 if (*mde == NULL)
452 return MCREASON_ENOMEM;
453 if (seq != NULL)
454 mc_init_packet(*mde, mde_size, (*seq)++, MCMD_PUT_DATA);
455 else
456 mc_init_packet(*mde, mde_size, 0, MCMD_PUT_DATA);
457 (*mde)->n_tags = n_tags;
458 (*mde)->name_size = key_len;
459 (*mde)->data_size = data_len;
460 (*mde)->exptime = exptime;
461
462 d = (*mde)->data;
463 if (tags != NULL) {
464 memcpy(d, tags, n_tags * sizeof(struct mc_tag));
465 d += n_tags * sizeof(struct mc_tag);
466 }
467 memcpy(d, key, key_len);
468 d += key_len;
469 memcpy(d, data, data_len);
470
471 return MCREASON_OK;
472 }
473
474
475 #ifdef __cplusplus
476 }
477 #endif
478
479 #endif
480