Git init
[pkgs/e/elektra.git] / src / backends / daemon / message.c
1 /***************************************************************************
2                 message.c  -  Class for a protocol messages
3                              -------------------
4     begin                : Sun Mar 12 2006
5     copyright            : (C) 2006 by Yannick Lecaillez, Avi Alkalay
6     email                : avi@unix.sh
7  ***************************************************************************/
8
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the BSD License (revised).                      *
13  *                                                                         *
14  ***************************************************************************/
15
16 /***************************************************************************
17  *                                                                         *
18  * Class for messages, to be passed over a protocol line.                  *
19  *                                                                         *
20  ***************************************************************************/
21
22
23
24 /* Subversion stuff
25
26 $Id$
27
28 */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include <assert.h>
35 #include <errno.h>
36 #include <stdio.h>
37 #include <stdarg.h>
38 #ifdef HAVE_STDLIB_H
39 #include <stdlib.h>
40 #endif
41 #ifdef HAVE_STRING_H
42 #include <string.h>
43 #endif
44
45 #include "datatype.h"
46 #include "serial.h"
47 #include "message.h"
48
49 #include "kdb.h"
50 #include "kdbprivate.h"
51
52 /**
53  * @defgroup message Message
54  * @brief Encapsulate procedure call for IPC between libelektra-daemon and kdbd
55  *
56  * To use it:
57  * @code
58  * #include "message.h"
59  * @endcode
60  *
61  * Message is a flat struct (i.e: without pointer) which encapsulate a
62  * procedure with arguments. Its the basis class for elektra daemon IPC.
63  * a Message pointer contain the Message struct and the eventual serialized
64  * arguments. Here a in memory schematic representation:
65  * @code
66  * #|MESSAGE STRUCT|ARG0-SERIALIZED|ARGN-SERIALIZED|#
67  * @endcode
68  * 
69  * where # is the boundaries of the memory space allocated for the Message.
70  * Message depends of serializer.
71  *
72  * Refer to @link protocol Protocol@endlink for sending/receiving message.
73  * 
74  */
75
76 /**
77  * Message constructor.
78  *
79  * Create a new message with associated arguments.
80  * Message is a serialized procedure and arguments targeted to be
81  * send throught a medium.
82  * Arguments are copied, so you could free these after using messageNew
83  *
84  * @code
85  * Message *msg
86  *
87  * // Create a request containing key and integer as arguments
88  * msg = messageNew(MESSAGE_REQUEST, 1, DATATYPE_KEY, key,
89  *                                      DATATYPE_INTEGER, &myInt,
90  *                                      DATATYPE_LAST);
91  * @endcode
92  *
93  * @param msgType Message type (request, reply, internal error)
94  * @param procedure Procedure ID
95  * 
96  * @see DataType
97  * @see MessageType
98  * @see messageExtractArgs()
99  * @see messageDel()
100  * @return newly allocated Message (must be freed with messageDel()) or NULL if error occured
101  *              
102  * @ingroup message
103  *
104  */
105 Message *messageNew(MessageType msgType, int procedure, ...)
106 {
107         DataType        type;
108         va_list         va;
109         Message         *msg;
110         int             nbArgs;
111         char            *buf;
112         size_t          size;
113         ssize_t         serializedSize;
114         
115         /* Compute size of Message + serialized args */
116         size = sizeof(Message);
117         
118         va_start(va, procedure);
119         type = va_arg(va, DataType);
120         nbArgs = 0;
121         while ( (type != DATATYPE_LAST) && (nbArgs < MSG_MAX_ARGS) ) {
122                 serializedSize = serializeGetSize(type, va_arg(va, void *));
123                 if ( serializedSize == -1 ) {
124                         fprintf(stderr, "SerializedGetSize = -1 for args %d of type %d !\n", nbArgs, type);
125                         va_end(va);
126                         return NULL;
127                 }
128                 size += serializedSize;
129                 nbArgs++;
130                 
131                 type = va_arg(va, DataType);
132         }
133         va_end(va);
134         
135         if ( nbArgs == MSG_MAX_ARGS ) {
136                 errno = ERANGE; 
137                 return NULL;
138         }
139
140         /* Allocate some memory */
141         msg = (Message *) malloc(size);
142         if ( msg == NULL ) 
143                 return NULL;
144                 
145         /* Fill message struct */
146         memset(msg, 0, size);
147         msg->type = msgType;
148         msg->procId = procedure;
149         msg->nbArgs = nbArgs;
150         msg->size = size;
151         memset(msg->args, 0, sizeof(msg->args));
152         buf = (char *) msg;
153         buf += sizeof(Message);
154         
155         /* Add serialized args ... */
156         nbArgs = 0;
157         va_start(va, procedure);
158         type = va_arg(va, DataType);
159         while ( type != DATATYPE_LAST ) {
160                 serializedSize = serialize(type, va_arg(va, void *), buf);
161                 if ( serializedSize == -1 ) {
162                         free(msg);
163                         va_end(va);
164                         return NULL;
165                 }
166                 msg->args[nbArgs++] = type;
167                 buf += serializedSize;
168
169                 type = va_arg(va, DataType);
170         }
171         va_end(va);
172
173         return msg;
174 }
175
176 /**
177  * Get type of a message
178  *
179  * @param msg Message
180  * @return Type of the message
181  *
182  * @see MessageType
183  * @ingroup message
184  */
185 MessageType messageGetType(const Message *msg)
186 {
187         assert(msg != NULL);
188         
189         return msg->type;
190 }
191
192 /**
193  * Get procedure of a message
194  *
195  * @param msg Message
196  * @return Type of the message
197  * @ingroup message
198  */
199 int messageGetProcedure(const Message *msg)
200 {
201         assert(msg != NULL);
202
203         return msg->procId;
204 }
205
206 /**
207  * Get number of arguments contained in a message
208  *
209  * @param msg Message
210  * @return # of args
211  * @ingroup message
212  */
213 int messageGetNbArgs(const Message *msg)
214 {
215         assert(msg != NULL);
216
217         return msg->nbArgs;
218 }
219
220 /**
221  * Extract arguments from a message
222  * This methods extract arguments contained from a message
223  * into the suite of arguments passed to this method
224  *
225  * @code
226  * int ret
227  *  
228  * ret = messageExtractArgs(msg, DATATYPE_KEY, key,
229  *                               DATATYPE_INTEGER, &myInt,
230  *                               DATATYPE_LAST);
231  * @endcode
232  *
233  * @param msg Message
234  * @return 0 if Ok, -1 on error.
235  * 
236  * @see messageNew()
237  * @see DataType
238  * @ingroup message
239  */
240 int messageExtractArgs(const Message *msg, ...)
241 {
242         DataType type;
243         va_list va;
244         const char *buf;
245         ssize_t serializedSize;
246         int     args;
247
248         assert(msg != NULL);
249         
250         /* Skip message struct */
251         buf = (const char *) msg;
252         buf += sizeof(Message);
253                 
254         /* Unserialize args */
255         args = 0;
256         va_start(va, msg);
257         type = va_arg(va, DataType);
258         while ( (type != DATATYPE_LAST) && (args < MSG_MAX_ARGS) ) {
259                 if ( msg->args[args] != type ) {
260                         va_end(va);
261                         errno = EBADF;
262                         return -1;
263                 }
264                 
265                 serializedSize = unserialize(type, buf, va_arg(va, void *));
266                 if ( serializedSize == -1 ) {
267                         va_end(va);
268                         return -1;
269                 }
270                 
271                 buf += serializedSize;
272                 args++;
273
274                 type = va_arg(va, DataType);
275         }
276         va_end(va);
277
278         if ( args == MSG_MAX_ARGS ) {
279                 errno = ERANGE;
280                 return -1;
281         }
282
283         return 0;
284 }
285
286 /**
287  * Delete a message
288  * Free all memory took by this message.
289  * This doesn't free arguments passed when message was created.
290  *
291  * @param msg Message to delete
292  *
293  * @see messageNew()
294  * @ingroup message
295  */
296 void messageDel(Message *msg)
297 {
298         free(msg);
299 }