Add system info messages
[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 static int read_u32(const char **pos, unsigned int *len, unsigned int *p)
145 {
146         if (*len < sizeof(uint32_t))
147                 return -1;
148
149         *p = (unsigned int)__be32_to_cpu(*(uint32_t *)(*pos));
150         *pos += sizeof(uint32_t);
151         *len -= sizeof(uint32_t);
152
153         return 0;
154 }
155
156 char *pb_protocol_deserialise_string(void *ctx,
157                 const struct pb_protocol_message *message)
158 {
159         const char *buf;
160         char *str;
161         unsigned int len;
162
163         len = message->payload_len;
164         buf = message->payload;
165
166         if (read_string(ctx, &buf, &len, &str))
167                 return NULL;
168
169         return str;
170 }
171
172 static int optional_strlen(const char *str)
173 {
174         if (!str)
175                 return 0;
176         return strlen(str);
177 }
178
179 int pb_protocol_device_len(const struct device *dev)
180 {
181         return  4 + optional_strlen(dev->id) +
182                 sizeof(dev->type) +
183                 4 + optional_strlen(dev->name) +
184                 4 + optional_strlen(dev->description) +
185                 4 + optional_strlen(dev->icon_file);
186 }
187
188 int pb_protocol_boot_option_len(const struct boot_option *opt)
189 {
190
191         return  4 + optional_strlen(opt->device_id) +
192                 4 + optional_strlen(opt->id) +
193                 4 + optional_strlen(opt->name) +
194                 4 + optional_strlen(opt->description) +
195                 4 + optional_strlen(opt->icon_file) +
196                 4 + optional_strlen(opt->boot_image_file) +
197                 4 + optional_strlen(opt->initrd_file) +
198                 4 + optional_strlen(opt->dtb_file) +
199                 4 + optional_strlen(opt->boot_args) +
200                 sizeof(opt->is_default);
201 }
202
203 int pb_protocol_boot_len(const struct boot_command *boot)
204 {
205         return  4 + optional_strlen(boot->option_id) +
206                 4 + optional_strlen(boot->boot_image_file) +
207                 4 + optional_strlen(boot->initrd_file) +
208                 4 + optional_strlen(boot->dtb_file) +
209                 4 + optional_strlen(boot->boot_args);
210 }
211
212 int pb_protocol_boot_status_len(const struct boot_status *status)
213 {
214         return  4 +
215                 4 + optional_strlen(status->message) +
216                 4 + optional_strlen(status->detail) +
217                 4;
218 }
219
220 int pb_protocol_system_info_len(const struct system_info *sysinfo)
221 {
222         unsigned int len, i;
223
224         len =   4 + optional_strlen(sysinfo->type) +
225                 4 + optional_strlen(sysinfo->identifier) +
226                 4;
227
228         for (i = 0; i < sysinfo->n_interfaces; i++) {
229                 struct interface_info *if_info = sysinfo->interfaces[i];
230                 len +=  4 + if_info->hwaddr_size +
231                         4 + optional_strlen(if_info->name);
232         }
233
234         return len;
235 }
236
237 int pb_protocol_serialise_device(const struct device *dev,
238                 char *buf, int buf_len)
239 {
240         char *pos = buf;
241
242         pos += pb_protocol_serialise_string(pos, dev->id);
243         *(enum device_type *)pos = dev->type;
244         pos += sizeof(enum device_type);
245         pos += pb_protocol_serialise_string(pos, dev->name);
246         pos += pb_protocol_serialise_string(pos, dev->description);
247         pos += pb_protocol_serialise_string(pos, dev->icon_file);
248
249         assert(pos <= buf + buf_len);
250         (void)buf_len;
251
252         return 0;
253 }
254
255 int pb_protocol_serialise_boot_option(const struct boot_option *opt,
256                 char *buf, int buf_len)
257 {
258         char *pos = buf;
259
260         pos += pb_protocol_serialise_string(pos, opt->device_id);
261         pos += pb_protocol_serialise_string(pos, opt->id);
262         pos += pb_protocol_serialise_string(pos, opt->name);
263         pos += pb_protocol_serialise_string(pos, opt->description);
264         pos += pb_protocol_serialise_string(pos, opt->icon_file);
265         pos += pb_protocol_serialise_string(pos, opt->boot_image_file);
266         pos += pb_protocol_serialise_string(pos, opt->initrd_file);
267         pos += pb_protocol_serialise_string(pos, opt->dtb_file);
268         pos += pb_protocol_serialise_string(pos, opt->boot_args);
269
270         *(bool *)pos = opt->is_default;
271         pos += sizeof(bool);
272
273         assert(pos <= buf + buf_len);
274         (void)buf_len;
275
276         return 0;
277 }
278
279 int pb_protocol_serialise_boot_command(const struct boot_command *boot,
280                 char *buf, int buf_len)
281 {
282         char *pos = buf;
283
284         pos += pb_protocol_serialise_string(pos, boot->option_id);
285         pos += pb_protocol_serialise_string(pos, boot->boot_image_file);
286         pos += pb_protocol_serialise_string(pos, boot->initrd_file);
287         pos += pb_protocol_serialise_string(pos, boot->dtb_file);
288         pos += pb_protocol_serialise_string(pos, boot->boot_args);
289
290         assert(pos <= buf + buf_len);
291         (void)buf_len;
292
293         return 0;
294 }
295
296 int pb_protocol_serialise_boot_status(const struct boot_status *status,
297                 char *buf, int buf_len)
298 {
299         char *pos = buf;
300
301         *(uint32_t *)pos = __cpu_to_be32(status->type);
302         pos += sizeof(uint32_t);
303
304         pos += pb_protocol_serialise_string(pos, status->message);
305         pos += pb_protocol_serialise_string(pos, status->detail);
306
307         *(uint32_t *)pos = __cpu_to_be32(status->type);
308         pos += sizeof(uint32_t);
309
310         assert(pos <= buf + buf_len);
311         (void)buf_len;
312
313         return 0;
314 }
315
316 int pb_protocol_serialise_system_info(const struct system_info *sysinfo,
317                 char *buf, int buf_len)
318 {
319         char *pos = buf;
320         unsigned int i;
321
322         pos += pb_protocol_serialise_string(pos, sysinfo->type);
323         pos += pb_protocol_serialise_string(pos, sysinfo->identifier);
324
325         *(uint32_t *)pos = __cpu_to_be32(sysinfo->n_interfaces);
326         pos += sizeof(uint32_t);
327
328         for (i = 0; i < sysinfo->n_interfaces; i++) {
329                 struct interface_info *if_info = sysinfo->interfaces[i];
330
331                 *(uint32_t *)pos = __cpu_to_be32(if_info->hwaddr_size);
332                 pos += sizeof(uint32_t);
333
334                 memcpy(pos, if_info->hwaddr, if_info->hwaddr_size);
335                 pos += if_info->hwaddr_size;
336
337                 pos += pb_protocol_serialise_string(pos, if_info->name);
338         }
339
340         assert(pos <= buf + buf_len);
341         (void)buf_len;
342
343         return 0;
344 }
345
346 int pb_protocol_write_message(int fd, struct pb_protocol_message *message)
347 {
348         int total_len, rc;
349         char *pos;
350
351         total_len = sizeof(*message) + message->payload_len;
352
353         message->payload_len = __cpu_to_be32(message->payload_len);
354         message->action = __cpu_to_be32(message->action);
355
356         for (pos = (void *)message; total_len;) {
357                 rc = write(fd, pos, total_len);
358
359                 if (rc <= 0)
360                         break;
361
362                 total_len -= rc;
363                 pos += rc;
364         }
365
366         talloc_free(message);
367
368         if (!total_len)
369                 return 0;
370
371         pb_log("%s: failed: %s\n", __func__, strerror(errno));
372         return -1;
373 }
374
375 struct pb_protocol_message *pb_protocol_create_message(void *ctx,
376                 enum pb_protocol_action action, int payload_len)
377 {
378         struct pb_protocol_message *message;
379
380         if (payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE) {
381                 pb_log("%s: payload too big %u/%u\n", __func__, payload_len,
382                         PB_PROTOCOL_MAX_PAYLOAD_SIZE);
383                 return NULL;
384         }
385
386         message = talloc_size(ctx, sizeof(*message) + payload_len);
387
388         /* we convert these to big-endian in write_message() */
389         message->action = action;
390         message->payload_len = payload_len;
391
392         return message;
393
394 }
395
396 struct pb_protocol_message *pb_protocol_read_message(void *ctx, int fd)
397 {
398         struct pb_protocol_message *message, m;
399         int rc;
400         unsigned int len;
401
402         /* use the stack for the initial 8-byte read */
403
404         rc = read(fd, &m, sizeof(m));
405         if (rc != sizeof(m))
406                 return NULL;
407
408         m.payload_len = __be32_to_cpu(m.payload_len);
409         m.action = __be32_to_cpu(m.action);
410
411         if (m.payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE) {
412                 pb_log("%s: payload too big %u/%u\n", __func__, m.payload_len,
413                         PB_PROTOCOL_MAX_PAYLOAD_SIZE);
414                 return NULL;
415         }
416
417         message = talloc_size(ctx, sizeof(m) + m.payload_len);
418         memcpy(message, &m, sizeof(m));
419
420         for (len = 0; len < m.payload_len;) {
421                 rc = read(fd, message->payload + len, m.payload_len - len);
422
423                 if (rc <= 0) {
424                         talloc_free(message);
425                         pb_log("%s: failed (%u): %s\n", __func__, len,
426                                 strerror(errno));
427                         return NULL;
428                 }
429
430                 len += rc;
431         }
432
433         return message;
434 }
435
436
437 int pb_protocol_deserialise_device(struct device *dev,
438                 const struct pb_protocol_message *message)
439 {
440         unsigned int len;
441         const char *pos;
442         int rc = -1;
443
444         len = message->payload_len;
445         pos = message->payload;
446
447         if (read_string(dev, &pos, &len, &dev->id))
448                 goto out;
449
450         if (len < sizeof(enum device_type))
451                 goto out;
452         dev->type = *(enum device_type *)(pos);
453         pos += sizeof(enum device_type);
454         len -= sizeof(enum device_type);
455
456         if (read_string(dev, &pos, &len, &dev->name))
457                 goto out;
458
459         if (read_string(dev, &pos, &len, &dev->description))
460                 goto out;
461
462         if (read_string(dev, &pos, &len, &dev->icon_file))
463                 goto out;
464
465         rc = 0;
466
467 out:
468         return rc;
469 }
470
471 int pb_protocol_deserialise_boot_option(struct boot_option *opt,
472                 const struct pb_protocol_message *message)
473 {
474         unsigned int len;
475         const char *pos;
476         int rc = -1;
477
478         len = message->payload_len;
479         pos = message->payload;
480
481         if (read_string(opt, &pos, &len, &opt->device_id))
482                 goto out;
483
484         if (read_string(opt, &pos, &len, &opt->id))
485                 goto out;
486
487         if (read_string(opt, &pos, &len, &opt->name))
488                 goto out;
489
490         if (read_string(opt, &pos, &len, &opt->description))
491                 goto out;
492
493         if (read_string(opt, &pos, &len, &opt->icon_file))
494                 goto out;
495
496         if (read_string(opt, &pos, &len, &opt->boot_image_file))
497                 goto out;
498
499         if (read_string(opt, &pos, &len, &opt->initrd_file))
500                 goto out;
501
502         if (read_string(opt, &pos, &len, &opt->dtb_file))
503                 goto out;
504
505         if (read_string(opt, &pos, &len, &opt->boot_args))
506                 goto out;
507
508         if (len < sizeof(bool))
509                 goto out;
510         opt->is_default = *(bool *)(pos);
511
512         rc = 0;
513
514 out:
515         return rc;
516 }
517
518 int pb_protocol_deserialise_boot_command(struct boot_command *cmd,
519                 const struct pb_protocol_message *message)
520 {
521         unsigned int len;
522         const char *pos;
523         int rc = -1;
524
525         len = message->payload_len;
526         pos = message->payload;
527
528         if (read_string(cmd, &pos, &len, &cmd->option_id))
529                 goto out;
530
531         if (read_string(cmd, &pos, &len, &cmd->boot_image_file))
532                 goto out;
533
534         if (read_string(cmd, &pos, &len, &cmd->initrd_file))
535                 goto out;
536
537         if (read_string(cmd, &pos, &len, &cmd->dtb_file))
538                 goto out;
539
540         if (read_string(cmd, &pos, &len, &cmd->boot_args))
541                 goto out;
542
543         rc = 0;
544
545 out:
546         return rc;
547 }
548
549 int pb_protocol_deserialise_boot_status(struct boot_status *status,
550                 const struct pb_protocol_message *message)
551 {
552         unsigned int len;
553         const char *pos;
554         int rc = -1;
555
556         len = message->payload_len;
557         pos = message->payload;
558
559         /* first up, the type enum... */
560         if (len < sizeof(uint32_t))
561                 goto out;
562
563         status->type = __be32_to_cpu(*(uint32_t *)(pos));
564
565         switch (status->type) {
566         case BOOT_STATUS_ERROR:
567         case BOOT_STATUS_INFO:
568                 break;
569         default:
570                 goto out;
571         }
572
573         pos += sizeof(uint32_t);
574         len -= sizeof(uint32_t);
575
576         /* message and detail strings */
577         if (read_string(status, &pos, &len, &status->message))
578                 goto out;
579
580         if (read_string(status, &pos, &len, &status->detail))
581                 goto out;
582
583         /* and finally, progress */
584         if (len < sizeof(uint32_t))
585                 goto out;
586
587         status->progress = __be32_to_cpu(*(uint32_t *)(pos));
588
589         /* clamp to 100% */
590         if (status->progress > 100)
591                 status->progress = 100;
592
593         rc = 0;
594
595 out:
596         return rc;
597 }
598
599 int pb_protocol_deserialise_system_info(struct system_info *sysinfo,
600                 const struct pb_protocol_message *message)
601 {
602         unsigned int len, i;
603         const char *pos;
604         int rc = -1;
605
606         len = message->payload_len;
607         pos = message->payload;
608
609         /* type and identifier strings */
610         if (read_string(sysinfo, &pos, &len, &sysinfo->type))
611                 goto out;
612
613         if (read_string(sysinfo, &pos, &len, &sysinfo->identifier))
614                 goto out;
615
616         /* number of interfaces */
617         if (read_u32(&pos, &len, &sysinfo->n_interfaces))
618                 goto out;
619
620         sysinfo->interfaces = talloc_array(sysinfo, struct interface_info *,
621                         sysinfo->n_interfaces);
622
623         for (i = 0; i < sysinfo->n_interfaces; i++) {
624                 struct interface_info *if_info = talloc(sysinfo,
625                                                         struct interface_info);
626
627                 if (read_u32(&pos, &len, &if_info->hwaddr_size))
628                         goto out;
629
630                 if (len < if_info->hwaddr_size)
631                         goto out;
632
633                 if_info->hwaddr = talloc_memdup(if_info, pos,
634                                                 if_info->hwaddr_size);
635                 pos += if_info->hwaddr_size;
636                 len -= if_info->hwaddr_size;
637
638                 if (read_string(if_info, &pos, &len, &if_info->name))
639                         goto out;
640
641                 sysinfo->interfaces[i] = if_info;
642         }
643
644         rc = 0;
645
646 out:
647         return rc;
648 }