]> git.ozlabs.org Git - petitboot/blob - lib/pb-protocol/pb-protocol.c
Add boot_option_copy routine
[petitboot] / lib / pb-protocol / pb-protocol.c
1
2 #include <assert.h>
3 #include <string.h>
4 #include <stdint.h>
5 #include <asm/byteorder.h>
6
7 #include <talloc/talloc.h>
8 #include <list/list.h>
9
10 #include "pb-protocol.h"
11
12
13 /* Message format:
14  *
15  * 4-byte action, determines the remaining message content
16  * 4-byte total payload len
17  *   - not including action and payload len header
18  *
19  * action = 0x1: device add message
20  *  payload:
21  *   4-byte len, id
22  *   4-byte len, name
23  *   4-byte len, description
24  *   4-byte len, icon_file
25  *
26  *   4-byte option count
27  *   for each option:
28  *    4-byte len, id
29  *    4-byte len, name
30  *    4-byte len, description
31  *    4-byte len, icon_file
32  *    4-byte len, boot_image_file
33  *    4-byte len, initrd_file
34  *    4-byte len, boot_args
35  *
36  * action = 0x2: device remove message
37  *  payload:
38  *   4-byte len, id
39  */
40
41 /* Deep copy a boot_option.
42 */
43 struct boot_option *boot_option_copy(void* ctx, const struct boot_option *opt)
44 {
45         struct boot_option *new = talloc(ctx, struct boot_option);
46
47         if (new) {
48                 new->id = talloc_strdup(new, opt->id);
49                 new->name = talloc_strdup(new, opt->name);
50                 new->description = talloc_strdup(new, opt->description);
51                 new->icon_file = talloc_strdup(new, opt->icon_file);
52                 new->boot_image_file = talloc_strdup(new, opt->boot_image_file);
53                 new->initrd_file = talloc_strdup(new, opt->initrd_file);
54                 new->boot_args = talloc_strdup(new, opt->boot_args);
55                 memset(&new->list, 0, sizeof(new->list));
56         }
57
58         return new;
59 }
60
61 /* Write a string into the buffer, starting at pos.
62  *
63  * Returns the total length used for the write, including length header.
64  */
65 int pb_protocol_serialise_string(char *pos, const char *str)
66 {
67         int len = 0;
68
69         if (str)
70                 len = strlen(str);
71
72         *(uint32_t *)pos = __cpu_to_be32(len);
73         pos += sizeof(uint32_t);
74
75         memcpy(pos, str, len);
76
77         return len + sizeof(uint32_t);
78 }
79
80 /* Read a string from a buffer, allocating the new string as necessary.
81  *
82  * @param[in] ctx       The talloc context to base the allocation on
83  * @param[in,out] pos   Where to start reading
84  * @param[in,out] len   The amount of data remaining in the buffer
85  * @param[out] str      Pointer to resuling string
86  * @return              zero on success, non-zero on failure
87  */
88 static int read_string(void *ctx, char **pos, unsigned int *len, char **str)
89 {
90         uint32_t str_len, read_len;
91
92         if (*len < sizeof(uint32_t))
93                 return -1;
94
95         str_len = __be32_to_cpu(*(uint32_t *)(*pos));
96         read_len = sizeof(uint32_t);
97
98         if (read_len + str_len > *len)
99                 return -1;
100
101         if (str_len == 0)
102                 *str = NULL;
103         else
104                 *str = talloc_strndup(ctx, *pos + read_len, str_len);
105
106         read_len += str_len;
107
108         /* all ok, update the caller's pointers */
109         *pos += read_len;
110         *len -= read_len;
111
112         return 0;
113 }
114
115 char *pb_protocol_deserialise_string(void *ctx,
116                 struct pb_protocol_message *message)
117 {
118         char *buf, *str;
119         unsigned int len;
120
121         len = message->payload_len;
122         buf = message->payload;
123
124         if (read_string(ctx, &buf, &len, &str))
125                 return NULL;
126
127         return str;
128 }
129
130 static int optional_strlen(const char *str)
131 {
132         if (!str)
133                 return 0;
134         return strlen(str);
135 }
136
137 int pb_protocol_device_len(struct device *dev)
138 {
139         struct boot_option *opt;
140         int len;
141
142         len =   4 + optional_strlen(dev->id) +
143                 4 + optional_strlen(dev->name) +
144                 4 + optional_strlen(dev->description) +
145                 4 + optional_strlen(dev->icon_file) +
146                 4;
147
148         list_for_each_entry(&dev->boot_options, opt, list) {
149                 len +=  4 + optional_strlen(opt->id) +
150                         4 + optional_strlen(opt->name) +
151                         4 + optional_strlen(opt->description) +
152                         4 + optional_strlen(opt->icon_file) +
153                         4 + optional_strlen(opt->boot_image_file) +
154                         4 + optional_strlen(opt->initrd_file) +
155                         4 + optional_strlen(opt->boot_args);
156         }
157
158         return len;
159 }
160
161 int pb_protocol_serialise_device(struct device *dev, char *buf, int buf_len)
162 {
163         struct boot_option *opt;
164         uint32_t n;
165         char *pos;
166
167         pos = buf;
168
169         /* construct payload into buffer */
170         pos += pb_protocol_serialise_string(pos, dev->id);
171         pos += pb_protocol_serialise_string(pos, dev->name);
172         pos += pb_protocol_serialise_string(pos, dev->description);
173         pos += pb_protocol_serialise_string(pos, dev->icon_file);
174
175         /* write option count */
176         n = 0;
177
178         list_for_each_entry(&dev->boot_options, opt, list)
179                 n++;
180
181         *(uint32_t *)pos = __cpu_to_be32(n);
182         pos += sizeof(uint32_t);
183
184         /* write each option */
185         list_for_each_entry(&dev->boot_options, opt, list) {
186                 pos += pb_protocol_serialise_string(pos, opt->id);
187                 pos += pb_protocol_serialise_string(pos, opt->name);
188                 pos += pb_protocol_serialise_string(pos, opt->description);
189                 pos += pb_protocol_serialise_string(pos, opt->icon_file);
190                 pos += pb_protocol_serialise_string(pos, opt->boot_image_file);
191                 pos += pb_protocol_serialise_string(pos, opt->initrd_file);
192                 pos += pb_protocol_serialise_string(pos, opt->boot_args);
193         }
194
195         assert(pos <= buf + buf_len);
196
197         return 0;
198 }
199
200 int pb_protocol_write_message(int fd, struct pb_protocol_message *message)
201 {
202         int total_len, rc;
203         char *pos;
204
205         total_len = sizeof(*message) + message->payload_len;
206
207         message->payload_len = __cpu_to_be32(message->payload_len);
208         message->action = __cpu_to_be32(message->action);
209
210         for (pos = (void *)message; total_len;) {
211                 rc = write(fd, pos, total_len);
212
213                 if (rc <= 0)
214                         break;
215
216                 total_len -= rc;
217                 pos += rc;
218         }
219
220         talloc_free(message);
221
222         return total_len ? -1 : 0;
223 }
224
225 struct pb_protocol_message *pb_protocol_create_message(void *ctx,
226                 enum pb_protocol_action action, int payload_len)
227 {
228         struct pb_protocol_message *message;
229
230         if (payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE)
231                 return NULL;
232
233         message = talloc_size(ctx, sizeof(*message) + payload_len);
234
235         /* we convert these to big-endian in write_message() */
236         message->action = action;
237         message->payload_len = payload_len;
238
239         return message;
240
241 }
242
243 struct pb_protocol_message *pb_protocol_read_message(void *ctx, int fd)
244 {
245         struct pb_protocol_message *message, m;
246         int rc;
247         unsigned int len;
248
249         /* use the stack for the initial 8-byte read */
250
251         rc = read(fd, &m, sizeof(m));
252         if (rc != sizeof(m))
253                 return NULL;
254
255         m.payload_len = __be32_to_cpu(m.payload_len);
256         m.action = __be32_to_cpu(m.action);
257
258         if (m.payload_len > PB_PROTOCOL_MAX_PAYLOAD_SIZE)
259                 return NULL;
260
261         message = talloc_size(ctx, sizeof(m) + m.payload_len);
262         memcpy(message, &m, sizeof(m));
263
264         for (len = 0; len < m.payload_len;) {
265                 rc = read(fd, message->payload + len, m.payload_len - len);
266
267                 if (rc <= 0) {
268                         talloc_free(message);
269                         return NULL;
270                 }
271
272                 len += rc;
273         }
274
275         return message;
276 }
277
278
279 struct device *pb_protocol_deserialise_device(void *ctx,
280                 struct pb_protocol_message *message)
281 {
282         struct device *dev;
283         char *pos;
284         int i, n_options;
285         unsigned int len;
286
287         len = message->payload_len;
288         pos = message->payload;
289
290         dev = talloc(ctx, struct device);
291
292         if (read_string(dev, &pos, &len, &dev->id))
293                 goto out_err;
294
295         if (read_string(dev, &pos, &len, &dev->name))
296                 goto out_err;
297
298         if (read_string(dev, &pos, &len, &dev->description))
299                 goto out_err;
300
301         if (read_string(dev, &pos, &len, &dev->icon_file))
302                 goto out_err;
303
304         n_options = __be32_to_cpu(*(uint32_t *)pos);
305         pos += sizeof(uint32_t);
306
307         list_init(&dev->boot_options);
308
309         for (i = 0; i < n_options; i++) {
310                 struct boot_option *opt;
311
312                 opt = talloc(dev, struct boot_option);
313
314                 if (read_string(opt, &pos, &len, &opt->id))
315                         goto out_err;
316                 if (read_string(opt, &pos, &len, &opt->name))
317                         goto out_err;
318                 if (read_string(opt, &pos, &len,
319                                         &opt->description))
320                         goto out_err;
321                 if (read_string(opt, &pos, &len,
322                                         &opt->icon_file))
323                         goto out_err;
324                 if (read_string(opt, &pos, &len,
325                                         &opt->boot_image_file))
326                         goto out_err;
327                 if (read_string(opt, &pos, &len,
328                                         &opt->initrd_file))
329                         goto out_err;
330                 if (read_string(opt, &pos, &len,
331                                         &opt->boot_args))
332                         goto out_err;
333
334                 list_add(&dev->boot_options, &opt->list);
335         }
336
337         return dev;
338
339 out_err:
340         talloc_free(dev);
341         return NULL;
342 }