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