fdf24471377160e9a3c51abb87c4197e4e034e1c
[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
43 void pb_protocol_dump_device(const struct device *dev, const char *text,
44         FILE *stream)
45 {
46         struct boot_option *opt;
47
48         fprintf(stream, "%snew dev:\n", text);
49         fprintf(stream, "%s\tid:   %s\n", text, dev->id);
50         fprintf(stream, "%s\tname: %s\n", text, dev->name);
51         fprintf(stream, "%s\tdesc: %s\n", text, dev->description);
52         fprintf(stream, "%s\ticon: %s\n", text, dev->icon_file);
53         fprintf(stream, "%s\tboot options:\n", text);
54         list_for_each_entry(&dev->boot_options, opt, list) {
55                 fprintf(stream, "%s\t\tid:   %s\n", text, opt->id);
56                 fprintf(stream, "%s\t\tname: %s\n", text, opt->name);
57                 fprintf(stream, "%s\t\tdesc: %s\n", text, opt->description);
58                 fprintf(stream, "%s\t\ticon: %s\n", text, opt->icon_file);
59                 fprintf(stream, "%s\t\tboot: %s\n", text, opt->boot_image_file);
60                 fprintf(stream, "%s\t\tinit: %s\n", text, opt->initrd_file);
61                 fprintf(stream, "%s\t\targs: %s\n", text, opt->boot_args);
62         }
63 }
64
65 int pb_protocol_device_cmp(const struct device *a, const struct device *b)
66 {
67         return !strcmp(a->id, b->id);
68 }
69
70 int pb_protocol_boot_option_cmp(const struct boot_option *a,
71         const struct boot_option *b)
72 {
73         return !strcmp(a->id, b->id);
74 }
75
76 /* Write a string into the buffer, starting at pos.
77  *
78  * Returns the total length used for the write, including length header.
79  */
80 int pb_protocol_serialise_string(char *pos, const char *str)
81 {
82         int len = 0;
83
84         if (str)
85                 len = strlen(str);
86
87         *(uint32_t *)pos = __cpu_to_be32(len);
88         pos += sizeof(uint32_t);
89
90         memcpy(pos, str, len);
91
92         return len + sizeof(uint32_t);
93 }
94
95 /* Read a string from a buffer, allocating the new string as necessary.
96  *
97  * @param[in] ctx       The talloc context to base the allocation on
98  * @param[in,out] pos   Where to start reading
99  * @param[in,out] len   The amount of data remaining in the buffer
100  * @param[out] str      Pointer to resuling string
101  * @return              zero on success, non-zero on failure
102  */
103 static int read_string(void *ctx, const char **pos, unsigned int *len,
104         char **str)
105 {
106         uint32_t str_len, read_len;
107
108         if (*len < sizeof(uint32_t))
109                 return -1;
110
111         str_len = __be32_to_cpu(*(uint32_t *)(*pos));
112         read_len = sizeof(uint32_t);
113
114         if (read_len + str_len > *len)
115                 return -1;
116
117         if (str_len == 0)
118                 *str = NULL;
119         else
120                 *str = talloc_strndup(ctx, *pos + read_len, str_len);
121
122         read_len += str_len;
123
124         /* all ok, update the caller's pointers */
125         *pos += read_len;
126         *len -= read_len;
127
128         return 0;
129 }
130
131 char *pb_protocol_deserialise_string(void *ctx,
132                 const struct pb_protocol_message *message)
133 {
134         const char *buf;
135         char *str;
136         unsigned int len;
137
138         len = message->payload_len;
139         buf = message->payload;
140
141         if (read_string(ctx, &buf, &len, &str))
142                 return NULL;
143
144         return str;
145 }
146
147 static int optional_strlen(const char *str)
148 {
149         if (!str)
150                 return 0;
151         return strlen(str);
152 }
153
154 int pb_protocol_device_len(const struct device *dev)
155 {
156         struct boot_option *opt;
157         int len;
158
159         len =   4 + optional_strlen(dev->id) +
160                 4 + optional_strlen(dev->name) +
161                 4 + optional_strlen(dev->description) +
162                 4 + optional_strlen(dev->icon_file) +
163                 4;
164
165         list_for_each_entry(&dev->boot_options, opt, list) {
166                 len +=  4 + optional_strlen(opt->id) +
167                         4 + optional_strlen(opt->name) +
168                         4 + optional_strlen(opt->description) +
169                         4 + optional_strlen(opt->icon_file) +
170                         4 + optional_strlen(opt->boot_image_file) +
171                         4 + optional_strlen(opt->initrd_file) +
172                         4 + optional_strlen(opt->boot_args);
173         }
174
175         return len;
176 }
177
178 int pb_protocol_serialise_device(const struct device *dev, char *buf, int buf_len)
179 {
180         struct boot_option *opt;
181         uint32_t n;
182         char *pos;
183
184         pos = buf;
185
186         /* construct payload into buffer */
187         pos += pb_protocol_serialise_string(pos, dev->id);
188         pos += pb_protocol_serialise_string(pos, dev->name);
189         pos += pb_protocol_serialise_string(pos, dev->description);
190         pos += pb_protocol_serialise_string(pos, dev->icon_file);
191
192         /* write option count */
193         n = 0;
194
195         list_for_each_entry(&dev->boot_options, opt, list)
196                 n++;
197
198         *(uint32_t *)pos = __cpu_to_be32(n);
199         pos += sizeof(uint32_t);
200
201         /* write each option */
202         list_for_each_entry(&dev->boot_options, opt, list) {
203                 pos += pb_protocol_serialise_string(pos, opt->id);
204                 pos += pb_protocol_serialise_string(pos, opt->name);
205                 pos += pb_protocol_serialise_string(pos, opt->description);
206                 pos += pb_protocol_serialise_string(pos, opt->icon_file);
207                 pos += pb_protocol_serialise_string(pos, opt->boot_image_file);
208                 pos += pb_protocol_serialise_string(pos, opt->initrd_file);
209                 pos += pb_protocol_serialise_string(pos, opt->boot_args);
210         }
211
212         assert(pos <= buf + buf_len);
213         (void)buf_len;
214
215         return 0;
216 }
217
218 int pb_protocol_write_message(int fd, struct pb_protocol_message *message)
219 {
220         int total_len, rc;
221         char *pos;
222
223         total_len = sizeof(*message) + message->payload_len;
224
225         message->payload_len = __cpu_to_be32(message->payload_len);
226         message->action = __cpu_to_be32(message->action);
227
228         for (pos = (void *)message; total_len;) {
229                 rc = write(fd, pos, total_len);
230
231                 if (rc <= 0)
232                         break;
233
234                 total_len -= rc;
235                 pos += rc;
236         }
237
238         talloc_free(message);
239
240         if (!total_len)
241                 return 0;
242
243         pb_log("%s: failed: %s\n", __func__, strerror(errno));
244         return -1;
245 }
246
247 struct pb_protocol_message *pb_protocol_create_message(void *ctx,
248                 enum pb_protocol_action action, int payload_len)
249 {
250         struct pb_protocol_message *message;
251
252         if (payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE) {
253                 pb_log("%s: payload too big %u/%u\n", __func__, payload_len,
254                         PB_PROTOCOL_MAX_PAYLOAD_SIZE);
255                 return NULL;
256         }
257
258         message = talloc_size(ctx, sizeof(*message) + payload_len);
259
260         /* we convert these to big-endian in write_message() */
261         message->action = action;
262         message->payload_len = payload_len;
263
264         return message;
265
266 }
267
268 struct pb_protocol_message *pb_protocol_read_message(void *ctx, int fd)
269 {
270         struct pb_protocol_message *message, m;
271         int rc;
272         unsigned int len;
273
274         /* use the stack for the initial 8-byte read */
275
276         rc = read(fd, &m, sizeof(m));
277         if (rc != sizeof(m))
278                 return NULL;
279
280         m.payload_len = __be32_to_cpu(m.payload_len);
281         m.action = __be32_to_cpu(m.action);
282
283         if (m.payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE) {
284                 pb_log("%s: payload too big %u/%u\n", __func__, m.payload_len,
285                         PB_PROTOCOL_MAX_PAYLOAD_SIZE);
286                 return NULL;
287         }
288
289         message = talloc_size(ctx, sizeof(m) + m.payload_len);
290         memcpy(message, &m, sizeof(m));
291
292         for (len = 0; len < m.payload_len;) {
293                 rc = read(fd, message->payload + len, m.payload_len - len);
294
295                 if (rc <= 0) {
296                         talloc_free(message);
297                         pb_log("%s: failed (%u): %s\n", __func__, len,
298                                 strerror(errno));
299                         return NULL;
300                 }
301
302                 len += rc;
303         }
304
305         return message;
306 }
307
308
309 struct device *pb_protocol_deserialise_device(void *ctx,
310                 const struct pb_protocol_message *message)
311 {
312         struct device *dev;
313         const char *pos;
314         int i, n_options;
315         unsigned int len;
316
317         len = message->payload_len;
318         pos = message->payload;
319
320         dev = talloc(ctx, struct device);
321
322         if (read_string(dev, &pos, &len, &dev->id))
323                 goto out_err;
324
325         if (read_string(dev, &pos, &len, &dev->name))
326                 goto out_err;
327
328         if (read_string(dev, &pos, &len, &dev->description))
329                 goto out_err;
330
331         if (read_string(dev, &pos, &len, &dev->icon_file))
332                 goto out_err;
333
334         n_options = __be32_to_cpu(*(uint32_t *)pos);
335         pos += sizeof(uint32_t);
336
337         dev->n_options = n_options;
338
339         list_init(&dev->boot_options);
340
341         for (i = 0; i < n_options; i++) {
342                 struct boot_option *opt;
343
344                 opt = talloc(dev, struct boot_option);
345
346                 if (read_string(opt, &pos, &len, &opt->id))
347                         goto out_err;
348                 if (read_string(opt, &pos, &len, &opt->name))
349                         goto out_err;
350                 if (read_string(opt, &pos, &len,
351                                         &opt->description))
352                         goto out_err;
353                 if (read_string(opt, &pos, &len,
354                                         &opt->icon_file))
355                         goto out_err;
356                 if (read_string(opt, &pos, &len,
357                                         &opt->boot_image_file))
358                         goto out_err;
359                 if (read_string(opt, &pos, &len,
360                                         &opt->initrd_file))
361                         goto out_err;
362                 if (read_string(opt, &pos, &len,
363                                         &opt->boot_args))
364                         goto out_err;
365
366                 list_add(&dev->boot_options, &opt->list);
367         }
368
369         return dev;
370
371 out_err:
372         talloc_free(dev);
373         return NULL;
374 }