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