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