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