d5f8b2f80a667fe058b27dec706a33f0bd6f86f6
[petitboot] / lib / pb-protocol / pb-protocol.c
1
2 #include <assert.h>
3 #include <string.h>
4 #include <stdint.h>
5 #include <asm/byteorder.h>
6
7 #include <talloc/talloc.h>
8 #include <list/list.h>
9
10 #include "pb-protocol.h"
11
12
13 /* Message format:
14  *
15  * 4-byte action, determines the remaining message content
16  * 4-byte total payload len
17  *   - not including action and payload len header
18  *
19  * action = 0x1: device add message
20  *  payload:
21  *   4-byte len, id
22  *   4-byte len, name
23  *   4-byte len, description
24  *   4-byte len, icon_file
25  *
26  *   4-byte option count
27  *   for each option:
28  *    4-byte len, id
29  *    4-byte len, name
30  *    4-byte len, description
31  *    4-byte len, icon_file
32  *    4-byte len, boot_image_file
33  *    4-byte len, initrd_file
34  *    4-byte len, boot_args
35  *
36  * action = 0x2: device remove message
37  *  payload:
38  *   4-byte len, id
39  */
40
41
42 /* Write a string into the buffer, starting at pos.
43  *
44  * Returns the total length used for the write, including length header.
45  */
46 int pb_protocol_serialise_string(char *pos, const char *str)
47 {
48         int len = 0;
49
50         if (str)
51                 len = strlen(str);
52
53         *(uint32_t *)pos = __cpu_to_be32(len);
54         pos += sizeof(uint32_t);
55
56         memcpy(pos, str, len);
57
58         return len + sizeof(uint32_t);
59 }
60
61 /* Read a string from a buffer, allocating the new string as necessary.
62  *
63  * @param[in] ctx       The talloc context to base the allocation on
64  * @param[in,out] pos   Where to start reading
65  * @param[in,out] len   The amount of data remaining in the buffer
66  * @param[out] str      Pointer to resuling string
67  * @return              zero on success, non-zero on failure
68  */
69 static int read_string(void *ctx, char **pos, unsigned int *len, char **str)
70 {
71         uint32_t str_len, read_len;
72
73         if (*len < sizeof(uint32_t))
74                 return -1;
75
76         str_len = __be32_to_cpu(*(uint32_t *)(*pos));
77         read_len = sizeof(uint32_t);
78
79         if (read_len + str_len > *len)
80                 return -1;
81
82         if (str_len == 0)
83                 *str = NULL;
84         else
85                 *str = talloc_strndup(ctx, *pos + read_len, str_len);
86
87         read_len += str_len;
88
89         /* all ok, update the caller's pointers */
90         *pos += read_len;
91         *len -= read_len;
92
93         return 0;
94 }
95
96 char *pb_protocol_deserialise_string(void *ctx,
97                 struct pb_protocol_message *message)
98 {
99         char *buf, *str;
100         unsigned int len;
101
102         len = message->payload_len;
103         buf = message->payload;
104
105         if (read_string(ctx, &buf, &len, &str))
106                 return NULL;
107
108         return str;
109 }
110
111 static int optional_strlen(const char *str)
112 {
113         if (!str)
114                 return 0;
115         return strlen(str);
116 }
117
118 int pb_protocol_device_len(struct device *dev)
119 {
120         struct boot_option *opt;
121         int len;
122
123         len =   4 + optional_strlen(dev->id) +
124                 4 + optional_strlen(dev->name) +
125                 4 + optional_strlen(dev->description) +
126                 4 + optional_strlen(dev->icon_file) +
127                 4;
128
129         list_for_each_entry(&dev->boot_options, opt, list) {
130                 len +=  4 + optional_strlen(opt->id) +
131                         4 + optional_strlen(opt->name) +
132                         4 + optional_strlen(opt->description) +
133                         4 + optional_strlen(opt->icon_file) +
134                         4 + optional_strlen(opt->boot_image_file) +
135                         4 + optional_strlen(opt->initrd_file) +
136                         4 + optional_strlen(opt->boot_args);
137         }
138
139         return len;
140 }
141
142 int pb_protocol_serialise_device(struct device *dev, char *buf, int buf_len)
143 {
144         struct boot_option *opt;
145         uint32_t n;
146         char *pos;
147
148         pos = buf;
149
150         /* construct payload into buffer */
151         pos += pb_protocol_serialise_string(pos, dev->id);
152         pos += pb_protocol_serialise_string(pos, dev->name);
153         pos += pb_protocol_serialise_string(pos, dev->description);
154         pos += pb_protocol_serialise_string(pos, dev->icon_file);
155
156         /* write option count */
157         n = 0;
158
159         list_for_each_entry(&dev->boot_options, opt, list)
160                 n++;
161
162         *(uint32_t *)pos = __cpu_to_be32(n);
163         pos += sizeof(uint32_t);
164
165         /* write each option */
166         list_for_each_entry(&dev->boot_options, opt, list) {
167                 pos += pb_protocol_serialise_string(pos, opt->id);
168                 pos += pb_protocol_serialise_string(pos, opt->name);
169                 pos += pb_protocol_serialise_string(pos, opt->description);
170                 pos += pb_protocol_serialise_string(pos, opt->icon_file);
171                 pos += pb_protocol_serialise_string(pos, opt->boot_image_file);
172                 pos += pb_protocol_serialise_string(pos, opt->initrd_file);
173                 pos += pb_protocol_serialise_string(pos, opt->boot_args);
174         }
175
176         assert(pos <= buf + buf_len);
177
178         return 0;
179 }
180
181 int pb_protocol_write_message(int fd, struct pb_protocol_message *message)
182 {
183         int total_len, rc;
184         char *pos;
185
186         total_len = sizeof(*message) + message->payload_len;
187
188         message->payload_len = __cpu_to_be32(message->payload_len);
189         message->action = __cpu_to_be32(message->action);
190
191         for (pos = (void *)message; total_len;) {
192                 rc = write(fd, pos, total_len);
193
194                 if (rc <= 0)
195                         break;
196
197                 total_len -= rc;
198                 pos += rc;
199         }
200
201         talloc_free(message);
202
203         return total_len ? -1 : 0;
204 }
205
206 struct pb_protocol_message *pb_protocol_create_message(void *ctx,
207                 enum pb_protocol_action action, int payload_len)
208 {
209         struct pb_protocol_message *message;
210
211         if (payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE)
212                 return NULL;
213
214         message = talloc_size(ctx, sizeof(*message) + payload_len);
215
216         /* we convert these to big-endian in write_message() */
217         message->action = action;
218         message->payload_len = payload_len;
219
220         return message;
221
222 }
223
224 struct pb_protocol_message *pb_protocol_read_message(void *ctx, int fd)
225 {
226         struct pb_protocol_message *message, m;
227         int rc;
228         unsigned int len;
229
230         /* use the stack for the initial 8-byte read */
231
232         rc = read(fd, &m, sizeof(m));
233         if (rc != sizeof(m))
234                 return NULL;
235
236         m.payload_len = __be32_to_cpu(m.payload_len);
237         m.action = __be32_to_cpu(m.action);
238
239         if (m.payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE)
240                 return NULL;
241
242         message = talloc_size(ctx, sizeof(m) + m.payload_len);
243         memcpy(message, &m, sizeof(m));
244
245         for (len = 0; len < m.payload_len;) {
246                 rc = read(fd, message->payload + len, m.payload_len - len);
247
248                 if (rc <= 0) {
249                         talloc_free(message);
250                         return NULL;
251                 }
252
253                 len += rc;
254         }
255
256         return message;
257 }
258
259
260 struct device *pb_protocol_deserialise_device(void *ctx,
261                 struct pb_protocol_message *message)
262 {
263         struct device *dev;
264         char *pos;
265         int i, n_options;
266         unsigned int len;
267
268         len = message->payload_len;
269         pos = message->payload;
270
271         dev = talloc(ctx, struct device);
272
273         if (read_string(dev, &pos, &len, &dev->id))
274                 goto out_err;
275
276         if (read_string(dev, &pos, &len, &dev->name))
277                 goto out_err;
278
279         if (read_string(dev, &pos, &len, &dev->description))
280                 goto out_err;
281
282         if (read_string(dev, &pos, &len, &dev->icon_file))
283                 goto out_err;
284
285         n_options = __be32_to_cpu(*(uint32_t *)pos);
286         pos += sizeof(uint32_t);
287
288         list_init(&dev->boot_options);
289
290         for (i = 0; i < n_options; i++) {
291                 struct boot_option *opt;
292
293                 opt = talloc(dev, struct boot_option);
294
295                 if (read_string(opt, &pos, &len, &opt->id))
296                         goto out_err;
297                 if (read_string(opt, &pos, &len, &opt->name))
298                         goto out_err;
299                 if (read_string(opt, &pos, &len,
300                                         &opt->description))
301                         goto out_err;
302                 if (read_string(opt, &pos, &len,
303                                         &opt->icon_file))
304                         goto out_err;
305                 if (read_string(opt, &pos, &len,
306                                         &opt->boot_image_file))
307                         goto out_err;
308                 if (read_string(opt, &pos, &len,
309                                         &opt->initrd_file))
310                         goto out_err;
311                 if (read_string(opt, &pos, &len,
312                                         &opt->boot_args))
313                         goto out_err;
314
315                 list_add(&dev->boot_options, &opt->list);
316         }
317
318         return dev;
319
320 out_err:
321         talloc_free(dev);
322         return NULL;
323 }