ded35eb5a1f5888b01374ac028fe9af2d68f2d0b
[petitboot] / lib / pb-protocol / pb-protocol.c
1
2 #include <assert.h>
3 #include <errno.h>
4 #include <string.h>
5 #include <stdint.h>
6 #include <asm/byteorder.h>
7
8 #include <talloc/talloc.h>
9 #include <list/list.h>
10 #include <log/log.h>
11
12 #include "pb-protocol.h"
13
14
15 /* Message format:
16  *
17  * 4-byte action, determines the remaining message content
18  * 4-byte total payload len
19  *   - not including action and payload len header
20  *
21  * action = 0x1: device add message
22  *  payload:
23  *   4-byte len, id
24  *   4-byte len, name
25  *   4-byte len, description
26  *   4-byte len, icon_file
27  *
28  *   4-byte option count
29  *   for each option:
30  *    4-byte len, id
31  *    4-byte len, name
32  *    4-byte len, description
33  *    4-byte len, icon_file
34  *    4-byte len, boot_image_file
35  *    4-byte len, initrd_file
36  *    4-byte len, boot_args
37  *
38  * action = 0x2: device remove message
39  *  payload:
40  *   4-byte len, id
41  *
42  * action = 0x3: boot
43  *  payload:
44  *   4-byte len, boot option id
45  *   4-byte len, boot_image_file
46  *   4-byte len, initrd_file
47  *   4-byte len, boot_args
48  *
49  */
50
51 void pb_protocol_dump_device(const struct device *dev, const char *text,
52         FILE *stream)
53 {
54         struct boot_option *opt;
55
56         fprintf(stream, "%snew dev:\n", text);
57         fprintf(stream, "%s\tid:   %s\n", text, dev->id);
58         fprintf(stream, "%s\tname: %s\n", text, dev->name);
59         fprintf(stream, "%s\tdesc: %s\n", text, dev->description);
60         fprintf(stream, "%s\ticon: %s\n", text, dev->icon_file);
61         fprintf(stream, "%s\tboot options:\n", text);
62         list_for_each_entry(&dev->boot_options, opt, list) {
63                 fprintf(stream, "%s\t\tid:   %s\n", text, opt->id);
64                 fprintf(stream, "%s\t\tname: %s\n", text, opt->name);
65                 fprintf(stream, "%s\t\tdesc: %s\n", text, opt->description);
66                 fprintf(stream, "%s\t\ticon: %s\n", text, opt->icon_file);
67                 fprintf(stream, "%s\t\tboot: %s\n", text, opt->boot_image_file);
68                 fprintf(stream, "%s\t\tinit: %s\n", text, opt->initrd_file);
69                 fprintf(stream, "%s\t\targs: %s\n", text, opt->boot_args);
70         }
71 }
72
73 int pb_protocol_device_cmp(const struct device *a, const struct device *b)
74 {
75         return !strcmp(a->id, b->id);
76 }
77
78 int pb_protocol_boot_option_cmp(const struct boot_option *a,
79         const struct boot_option *b)
80 {
81         return !strcmp(a->id, b->id);
82 }
83
84 /* Write a string into the buffer, starting at pos.
85  *
86  * Returns the total length used for the write, including length header.
87  */
88 int pb_protocol_serialise_string(char *pos, const char *str)
89 {
90         int len = 0;
91
92         if (str)
93                 len = strlen(str);
94
95         *(uint32_t *)pos = __cpu_to_be32(len);
96         pos += sizeof(uint32_t);
97
98         memcpy(pos, str, len);
99
100         return len + sizeof(uint32_t);
101 }
102
103 /* Read a string from a buffer, allocating the new string as necessary.
104  *
105  * @param[in] ctx       The talloc context to base the allocation on
106  * @param[in,out] pos   Where to start reading
107  * @param[in,out] len   The amount of data remaining in the buffer
108  * @param[out] str      Pointer to resuling string
109  * @return              zero on success, non-zero on failure
110  */
111 static int read_string(void *ctx, const char **pos, unsigned int *len,
112         char **str)
113 {
114         uint32_t str_len, read_len;
115
116         if (*len < sizeof(uint32_t))
117                 return -1;
118
119         str_len = __be32_to_cpu(*(uint32_t *)(*pos));
120         read_len = sizeof(uint32_t);
121
122         if (read_len + str_len > *len)
123                 return -1;
124
125         if (str_len == 0)
126                 *str = NULL;
127         else
128                 *str = talloc_strndup(ctx, *pos + read_len, str_len);
129
130         read_len += str_len;
131
132         /* all ok, update the caller's pointers */
133         *pos += read_len;
134         *len -= read_len;
135
136         return 0;
137 }
138
139 char *pb_protocol_deserialise_string(void *ctx,
140                 const struct pb_protocol_message *message)
141 {
142         const char *buf;
143         char *str;
144         unsigned int len;
145
146         len = message->payload_len;
147         buf = message->payload;
148
149         if (read_string(ctx, &buf, &len, &str))
150                 return NULL;
151
152         return str;
153 }
154
155 static int optional_strlen(const char *str)
156 {
157         if (!str)
158                 return 0;
159         return strlen(str);
160 }
161
162 int pb_protocol_device_len(const struct device *dev)
163 {
164         return  4 + optional_strlen(dev->id) +
165                 4 + optional_strlen(dev->name) +
166                 4 + optional_strlen(dev->description) +
167                 4 + optional_strlen(dev->icon_file);
168 }
169
170 int pb_protocol_boot_option_len(const struct boot_option *opt)
171 {
172
173         return  4 + optional_strlen(opt->device_id) +
174                 4 + optional_strlen(opt->id) +
175                 4 + optional_strlen(opt->name) +
176                 4 + optional_strlen(opt->description) +
177                 4 + optional_strlen(opt->icon_file) +
178                 4 + optional_strlen(opt->boot_image_file) +
179                 4 + optional_strlen(opt->initrd_file) +
180                 4 + optional_strlen(opt->boot_args);
181 }
182
183 int pb_protocol_boot_len(const struct boot_command *boot)
184 {
185         return  4 + optional_strlen(boot->option_id) +
186                 4 + optional_strlen(boot->boot_image_file) +
187                 4 + optional_strlen(boot->initrd_file) +
188                 4 + optional_strlen(boot->boot_args);
189 }
190
191 int pb_protocol_serialise_device(const struct device *dev,
192                 char *buf, int buf_len)
193 {
194         char *pos = buf;
195
196         pos += pb_protocol_serialise_string(pos, dev->id);
197         pos += pb_protocol_serialise_string(pos, dev->name);
198         pos += pb_protocol_serialise_string(pos, dev->description);
199         pos += pb_protocol_serialise_string(pos, dev->icon_file);
200
201         assert(pos <= buf + buf_len);
202         (void)buf_len;
203
204         return 0;
205 }
206
207 int pb_protocol_serialise_boot_option(const struct boot_option *opt,
208                 char *buf, int buf_len)
209 {
210         char *pos = buf;
211
212         pos += pb_protocol_serialise_string(pos, opt->device_id);
213         pos += pb_protocol_serialise_string(pos, opt->id);
214         pos += pb_protocol_serialise_string(pos, opt->name);
215         pos += pb_protocol_serialise_string(pos, opt->description);
216         pos += pb_protocol_serialise_string(pos, opt->icon_file);
217         pos += pb_protocol_serialise_string(pos, opt->boot_image_file);
218         pos += pb_protocol_serialise_string(pos, opt->initrd_file);
219         pos += pb_protocol_serialise_string(pos, opt->boot_args);
220
221         assert(pos <= buf + buf_len);
222         (void)buf_len;
223
224         return 0;
225 }
226
227 int pb_protocol_serialise_boot_command(const struct boot_command *boot,
228                 char *buf, int buf_len)
229 {
230         char *pos = buf;
231
232         pos += pb_protocol_serialise_string(pos, boot->option_id);
233         pos += pb_protocol_serialise_string(pos, boot->boot_image_file);
234         pos += pb_protocol_serialise_string(pos, boot->initrd_file);
235         pos += pb_protocol_serialise_string(pos, boot->boot_args);
236
237         assert(pos <= buf + buf_len);
238         (void)buf_len;
239
240         return 0;
241 }
242
243 int pb_protocol_write_message(int fd, struct pb_protocol_message *message)
244 {
245         int total_len, rc;
246         char *pos;
247
248         total_len = sizeof(*message) + message->payload_len;
249
250         message->payload_len = __cpu_to_be32(message->payload_len);
251         message->action = __cpu_to_be32(message->action);
252
253         for (pos = (void *)message; total_len;) {
254                 rc = write(fd, pos, total_len);
255
256                 if (rc <= 0)
257                         break;
258
259                 total_len -= rc;
260                 pos += rc;
261         }
262
263         talloc_free(message);
264
265         if (!total_len)
266                 return 0;
267
268         pb_log("%s: failed: %s\n", __func__, strerror(errno));
269         return -1;
270 }
271
272 struct pb_protocol_message *pb_protocol_create_message(void *ctx,
273                 enum pb_protocol_action action, int payload_len)
274 {
275         struct pb_protocol_message *message;
276
277         if (payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE) {
278                 pb_log("%s: payload too big %u/%u\n", __func__, payload_len,
279                         PB_PROTOCOL_MAX_PAYLOAD_SIZE);
280                 return NULL;
281         }
282
283         message = talloc_size(ctx, sizeof(*message) + payload_len);
284
285         /* we convert these to big-endian in write_message() */
286         message->action = action;
287         message->payload_len = payload_len;
288
289         return message;
290
291 }
292
293 struct pb_protocol_message *pb_protocol_read_message(void *ctx, int fd)
294 {
295         struct pb_protocol_message *message, m;
296         int rc;
297         unsigned int len;
298
299         /* use the stack for the initial 8-byte read */
300
301         rc = read(fd, &m, sizeof(m));
302         if (rc != sizeof(m))
303                 return NULL;
304
305         m.payload_len = __be32_to_cpu(m.payload_len);
306         m.action = __be32_to_cpu(m.action);
307
308         if (m.payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE) {
309                 pb_log("%s: payload too big %u/%u\n", __func__, m.payload_len,
310                         PB_PROTOCOL_MAX_PAYLOAD_SIZE);
311                 return NULL;
312         }
313
314         message = talloc_size(ctx, sizeof(m) + m.payload_len);
315         memcpy(message, &m, sizeof(m));
316
317         for (len = 0; len < m.payload_len;) {
318                 rc = read(fd, message->payload + len, m.payload_len - len);
319
320                 if (rc <= 0) {
321                         talloc_free(message);
322                         pb_log("%s: failed (%u): %s\n", __func__, len,
323                                 strerror(errno));
324                         return NULL;
325                 }
326
327                 len += rc;
328         }
329
330         return message;
331 }
332
333
334 int pb_protocol_deserialise_device(struct device *dev,
335                 const struct pb_protocol_message *message)
336 {
337         unsigned int len;
338         const char *pos;
339         int rc = -1;
340
341         len = message->payload_len;
342         pos = message->payload;
343
344         if (read_string(dev, &pos, &len, &dev->id))
345                 goto out;
346
347         if (read_string(dev, &pos, &len, &dev->name))
348                 goto out;
349
350         if (read_string(dev, &pos, &len, &dev->description))
351                 goto out;
352
353         if (read_string(dev, &pos, &len, &dev->icon_file))
354                 goto out;
355
356         rc = 0;
357
358 out:
359         return rc;
360 }
361
362 int pb_protocol_deserialise_boot_option(struct boot_option *opt,
363                 const struct pb_protocol_message *message)
364 {
365         unsigned int len;
366         const char *pos;
367         int rc = -1;
368
369         len = message->payload_len;
370         pos = message->payload;
371
372         if (read_string(opt, &pos, &len, &opt->device_id))
373                 goto out;
374
375         if (read_string(opt, &pos, &len, &opt->id))
376                 goto out;
377
378         if (read_string(opt, &pos, &len, &opt->name))
379                 goto out;
380
381         if (read_string(opt, &pos, &len, &opt->description))
382                 goto out;
383
384         if (read_string(opt, &pos, &len, &opt->icon_file))
385                 goto out;
386
387         if (read_string(opt, &pos, &len, &opt->boot_image_file))
388                 goto out;
389
390         if (read_string(opt, &pos, &len, &opt->initrd_file))
391                 goto out;
392
393         if (read_string(opt, &pos, &len, &opt->boot_args))
394                 goto out;
395
396         rc = 0;
397
398 out:
399         return rc;
400 }
401
402 int pb_protocol_deserialise_boot_command(struct boot_command *cmd,
403                 const struct pb_protocol_message *message)
404 {
405         unsigned int len;
406         const char *pos;
407         int rc = -1;
408
409         len = message->payload_len;
410         pos = message->payload;
411
412         if (read_string(cmd, &pos, &len, &cmd->option_id))
413                 goto out;
414
415         if (read_string(cmd, &pos, &len, &cmd->boot_image_file))
416                 goto out;
417
418         if (read_string(cmd, &pos, &len, &cmd->initrd_file))
419                 goto out;
420
421         if (read_string(cmd, &pos, &len, &cmd->boot_args))
422                 goto out;
423
424         rc = 0;
425
426 out:
427         return rc;
428 }