protocol: Add boot_status (de-)serialisation functions
[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_boot_status_len(const struct boot_status *status)
192 {
193         return  4 +
194                 4 + optional_strlen(status->message) +
195                 4 + optional_strlen(status->detail) +
196                 4;
197 }
198
199 int pb_protocol_serialise_device(const struct device *dev,
200                 char *buf, int buf_len)
201 {
202         char *pos = buf;
203
204         pos += pb_protocol_serialise_string(pos, dev->id);
205         pos += pb_protocol_serialise_string(pos, dev->name);
206         pos += pb_protocol_serialise_string(pos, dev->description);
207         pos += pb_protocol_serialise_string(pos, dev->icon_file);
208
209         assert(pos <= buf + buf_len);
210         (void)buf_len;
211
212         return 0;
213 }
214
215 int pb_protocol_serialise_boot_option(const struct boot_option *opt,
216                 char *buf, int buf_len)
217 {
218         char *pos = buf;
219
220         pos += pb_protocol_serialise_string(pos, opt->device_id);
221         pos += pb_protocol_serialise_string(pos, opt->id);
222         pos += pb_protocol_serialise_string(pos, opt->name);
223         pos += pb_protocol_serialise_string(pos, opt->description);
224         pos += pb_protocol_serialise_string(pos, opt->icon_file);
225         pos += pb_protocol_serialise_string(pos, opt->boot_image_file);
226         pos += pb_protocol_serialise_string(pos, opt->initrd_file);
227         pos += pb_protocol_serialise_string(pos, opt->boot_args);
228
229         assert(pos <= buf + buf_len);
230         (void)buf_len;
231
232         return 0;
233 }
234
235 int pb_protocol_serialise_boot_command(const struct boot_command *boot,
236                 char *buf, int buf_len)
237 {
238         char *pos = buf;
239
240         pos += pb_protocol_serialise_string(pos, boot->option_id);
241         pos += pb_protocol_serialise_string(pos, boot->boot_image_file);
242         pos += pb_protocol_serialise_string(pos, boot->initrd_file);
243         pos += pb_protocol_serialise_string(pos, boot->boot_args);
244
245         assert(pos <= buf + buf_len);
246         (void)buf_len;
247
248         return 0;
249 }
250
251 int pb_protocol_serialise_boot_status(const struct boot_status *status,
252                 char *buf, int buf_len)
253 {
254         char *pos = buf;
255
256         *(uint32_t *)pos = __cpu_to_be32(status->type);
257         pos += sizeof(uint32_t);
258
259         pos += pb_protocol_serialise_string(pos, status->message);
260         pos += pb_protocol_serialise_string(pos, status->detail);
261
262         *(uint32_t *)pos = __cpu_to_be32(status->type);
263         pos += sizeof(uint32_t);
264
265         assert(pos <= buf + buf_len);
266         (void)buf_len;
267
268         return 0;
269 }
270
271 int pb_protocol_write_message(int fd, struct pb_protocol_message *message)
272 {
273         int total_len, rc;
274         char *pos;
275
276         total_len = sizeof(*message) + message->payload_len;
277
278         message->payload_len = __cpu_to_be32(message->payload_len);
279         message->action = __cpu_to_be32(message->action);
280
281         for (pos = (void *)message; total_len;) {
282                 rc = write(fd, pos, total_len);
283
284                 if (rc <= 0)
285                         break;
286
287                 total_len -= rc;
288                 pos += rc;
289         }
290
291         talloc_free(message);
292
293         if (!total_len)
294                 return 0;
295
296         pb_log("%s: failed: %s\n", __func__, strerror(errno));
297         return -1;
298 }
299
300 struct pb_protocol_message *pb_protocol_create_message(void *ctx,
301                 enum pb_protocol_action action, int payload_len)
302 {
303         struct pb_protocol_message *message;
304
305         if (payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE) {
306                 pb_log("%s: payload too big %u/%u\n", __func__, payload_len,
307                         PB_PROTOCOL_MAX_PAYLOAD_SIZE);
308                 return NULL;
309         }
310
311         message = talloc_size(ctx, sizeof(*message) + payload_len);
312
313         /* we convert these to big-endian in write_message() */
314         message->action = action;
315         message->payload_len = payload_len;
316
317         return message;
318
319 }
320
321 struct pb_protocol_message *pb_protocol_read_message(void *ctx, int fd)
322 {
323         struct pb_protocol_message *message, m;
324         int rc;
325         unsigned int len;
326
327         /* use the stack for the initial 8-byte read */
328
329         rc = read(fd, &m, sizeof(m));
330         if (rc != sizeof(m))
331                 return NULL;
332
333         m.payload_len = __be32_to_cpu(m.payload_len);
334         m.action = __be32_to_cpu(m.action);
335
336         if (m.payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE) {
337                 pb_log("%s: payload too big %u/%u\n", __func__, m.payload_len,
338                         PB_PROTOCOL_MAX_PAYLOAD_SIZE);
339                 return NULL;
340         }
341
342         message = talloc_size(ctx, sizeof(m) + m.payload_len);
343         memcpy(message, &m, sizeof(m));
344
345         for (len = 0; len < m.payload_len;) {
346                 rc = read(fd, message->payload + len, m.payload_len - len);
347
348                 if (rc <= 0) {
349                         talloc_free(message);
350                         pb_log("%s: failed (%u): %s\n", __func__, len,
351                                 strerror(errno));
352                         return NULL;
353                 }
354
355                 len += rc;
356         }
357
358         return message;
359 }
360
361
362 int pb_protocol_deserialise_device(struct device *dev,
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(dev, &pos, &len, &dev->id))
373                 goto out;
374
375         if (read_string(dev, &pos, &len, &dev->name))
376                 goto out;
377
378         if (read_string(dev, &pos, &len, &dev->description))
379                 goto out;
380
381         if (read_string(dev, &pos, &len, &dev->icon_file))
382                 goto out;
383
384         rc = 0;
385
386 out:
387         return rc;
388 }
389
390 int pb_protocol_deserialise_boot_option(struct boot_option *opt,
391                 const struct pb_protocol_message *message)
392 {
393         unsigned int len;
394         const char *pos;
395         int rc = -1;
396
397         len = message->payload_len;
398         pos = message->payload;
399
400         if (read_string(opt, &pos, &len, &opt->device_id))
401                 goto out;
402
403         if (read_string(opt, &pos, &len, &opt->id))
404                 goto out;
405
406         if (read_string(opt, &pos, &len, &opt->name))
407                 goto out;
408
409         if (read_string(opt, &pos, &len, &opt->description))
410                 goto out;
411
412         if (read_string(opt, &pos, &len, &opt->icon_file))
413                 goto out;
414
415         if (read_string(opt, &pos, &len, &opt->boot_image_file))
416                 goto out;
417
418         if (read_string(opt, &pos, &len, &opt->initrd_file))
419                 goto out;
420
421         if (read_string(opt, &pos, &len, &opt->boot_args))
422                 goto out;
423
424         rc = 0;
425
426 out:
427         return rc;
428 }
429
430 int pb_protocol_deserialise_boot_command(struct boot_command *cmd,
431                 const struct pb_protocol_message *message)
432 {
433         unsigned int len;
434         const char *pos;
435         int rc = -1;
436
437         len = message->payload_len;
438         pos = message->payload;
439
440         if (read_string(cmd, &pos, &len, &cmd->option_id))
441                 goto out;
442
443         if (read_string(cmd, &pos, &len, &cmd->boot_image_file))
444                 goto out;
445
446         if (read_string(cmd, &pos, &len, &cmd->initrd_file))
447                 goto out;
448
449         if (read_string(cmd, &pos, &len, &cmd->boot_args))
450                 goto out;
451
452         rc = 0;
453
454 out:
455         return rc;
456 }
457
458 int pb_protocol_deserialise_boot_status(struct boot_status *status,
459                 const struct pb_protocol_message *message)
460 {
461         unsigned int len;
462         const char *pos;
463         int rc = -1;
464
465         len = message->payload_len;
466         pos = message->payload;
467
468         /* first up, the type enum... */
469         if (len < sizeof(uint32_t))
470                 goto out;
471
472         status->type = __be32_to_cpu(*(uint32_t *)(pos));
473
474         switch (status->type) {
475         case BOOT_STATUS_ERROR:
476         case BOOT_STATUS_INFO:
477                 break;
478         default:
479                 goto out;
480         }
481
482         pos += sizeof(uint32_t);
483         len -= sizeof(uint32_t);
484
485         /* message and detail strings */
486         if (read_string(status, &pos, &len, &status->message))
487                 goto out;
488
489         if (read_string(status, &pos, &len, &status->detail))
490                 goto out;
491
492         /* and finally, progress */
493         if (len < sizeof(uint32_t))
494                 goto out;
495
496         status->progress = __be32_to_cpu(*(uint32_t *)(pos));
497
498         /* clamp to 100% */
499         if (status->progress > 100)
500                 status->progress = 100;
501
502         rc = 0;
503
504 out:
505         return rc;
506 }