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