blob: c2b30b8576fba1564cf39d1d324e09ab3d520abf [file] [log] [blame]
David 'Digit' Turner318e4f22009-05-25 18:01:03 +02001/* Copyright (C) 2009 The Android Open Source Project
2**
3** This software is licensed under the terms of the GNU General Public
4** License version 2, as published by the Free Software Foundation, and
5** may be copied, distributed, and modified under those terms.
6**
7** This program is distributed in the hope that it will be useful,
8** but WITHOUT ANY WARRANTY; without even the implied warranty of
9** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10** GNU General Public License for more details.
11*/
12
13#include "android/boot-properties.h"
14#include "android/utils/debug.h"
15#include "android/utils/system.h"
16#include "android/hw-qemud.h"
17#include "android/globals.h"
18
Ot ten Thije45041e32010-10-05 17:46:46 +010019#include "hw/hw.h"
20
David 'Digit' Turner318e4f22009-05-25 18:01:03 +020021#define D(...) VERBOSE_PRINT(init,__VA_ARGS__)
22
23/* define T_ACTIVE to 1 to debug transport communications */
24#define T_ACTIVE 0
25
26#if T_ACTIVE
27#define T(...) VERBOSE_PRINT(init,__VA_ARGS__)
28#else
29#define T(...) ((void)0)
30#endif
31
David 'Digit' Turnerd68b4872009-07-24 16:33:05 +020032/* this code supports the list of system properties that will
33 * be set on boot in the emulated system.
34 */
35
David 'Digit' Turner318e4f22009-05-25 18:01:03 +020036typedef struct BootProperty {
37 struct BootProperty* next;
38 char* property;
39 int length;
40} BootProperty;
41
42static BootProperty*
43boot_property_alloc( const char* name, int namelen,
44 const char* value, int valuelen )
45{
46 int length = namelen + 1 + valuelen;
47 BootProperty* prop = android_alloc( sizeof(*prop) + length + 1 );
48 char* p;
49
50 prop->next = NULL;
51 prop->property = p = (char*)(prop + 1);
52 prop->length = length;
53
54 memcpy( p, name, namelen );
55 p += namelen;
56 *p++ = '=';
57 memcpy( p, value, valuelen );
58 p += valuelen;
59 *p = '\0';
60
61 return prop;
62}
63
Ot ten Thije45041e32010-10-05 17:46:46 +010064static BootProperty* _boot_properties = NULL;
65/* address to store pointer to next new list element */
David 'Digit' Turner318e4f22009-05-25 18:01:03 +020066static BootProperty** _boot_properties_tail = &_boot_properties;
67static int _inited;
68
Ot ten Thije45041e32010-10-05 17:46:46 +010069/* Clears all existing boot properties
70 */
71static void
72boot_property_clear_all()
73{
74 /* free all elements of the linked list */
75 BootProperty *p = _boot_properties;
76 BootProperty *next = NULL;
77 while (p) {
78 next = p->next;
79 AFREE(p);
80 p = next;
81 }
82
83 /* reset list administration to initial state */
84 _boot_properties = NULL;
85 _boot_properties_tail = &_boot_properties;
86}
87
88/* Appends a new boot property to the end of the internal list.
89 */
David 'Digit' Turner318e4f22009-05-25 18:01:03 +020090int
91boot_property_add2( const char* name, int namelen,
92 const char* value, int valuelen )
93{
94 BootProperty* prop;
95
96 /* check the lengths
97 */
98 if (namelen > PROPERTY_MAX_NAME)
99 return -1;
100
101 if (valuelen > PROPERTY_MAX_VALUE)
102 return -2;
103
104 /* check that there are not invalid characters in the
105 * property name
106 */
107 const char* reject = " =$*?'\"";
108 int nn;
109
110 for (nn = 0; nn < namelen; nn++) {
111 if (strchr(reject, name[nn]) != NULL)
112 return -3;
113 }
114
115 /* init service if needed */
116 if (!_inited) {
117 boot_property_init_service();
118 _inited = 1;
119 }
120
121 D("Adding boot property: '%.*s' = '%.*s'",
122 namelen, name, valuelen, value);
123
Ot ten Thije45041e32010-10-05 17:46:46 +0100124 /* add to the end of the internal list */
David 'Digit' Turner318e4f22009-05-25 18:01:03 +0200125 prop = boot_property_alloc(name, namelen, value, valuelen);
126
127 *_boot_properties_tail = prop;
128 _boot_properties_tail = &prop->next;
129
130 return 0;
131}
132
Ot ten Thije45041e32010-10-05 17:46:46 +0100133/* Prints the warning string corresponding to the error code returned by
134 * boot_propery_add2().
135 */
136static void
137boot_property_raise_warning( int ret, const char* name, int namelen,
138 const char* value, int valuelen )
139{
140 switch (ret) {
141 case -1:
142 dwarning("boot property name too long: '%.*s'",
143 namelen, name);
144 break;
145 case -2:
146 dwarning("boot property value too long: '%.*s'",
147 valuelen, value);
148 break;
149 case -3:
150 dwarning("boot property name contains invalid chars: %.*s",
151 namelen, name);
152 break;
153 }
154}
David 'Digit' Turner318e4f22009-05-25 18:01:03 +0200155
156int
157boot_property_add( const char* name, const char* value )
158{
159 int namelen = strlen(name);
160 int valuelen = strlen(value);
161
162 return boot_property_add2(name, namelen, value, valuelen);
163}
164
Ot ten Thije45041e32010-10-05 17:46:46 +0100165/* Saves a single BootProperty to file.
166 */
167static int
168boot_property_save_property( QEMUFile *f, BootProperty *p )
169{
170 /* split in key and value, so we can re-use boot_property_add (and its
171 * sanity checks) when loading
172 */
David 'Digit' Turner318e4f22009-05-25 18:01:03 +0200173
Ot ten Thije45041e32010-10-05 17:46:46 +0100174 char *split = strchr(p->property, '=');
175 if (split == NULL) {
176 D("%s: save failed: illegal key/value pair \"%s\" (missing '=')\n",
177 __FUNCTION__, p->property);
David 'Digit' Turnerc79de3c2014-01-23 04:17:24 +0100178 qemu_file_set_error(f, -EINVAL);
Ot ten Thije45041e32010-10-05 17:46:46 +0100179 return -1;
180 }
181
182 *split = '\0'; /* p->property is now "<key>\0<value>\0" */
183
184 uint32_t key_buf_len = (split - p->property) + 1; // +1: '\0' terminator
185 qemu_put_be32(f, key_buf_len);
186 qemu_put_buffer(f, (uint8_t*) p->property, key_buf_len);
187
188 uint32_t value_buf_len = p->length - key_buf_len + 1; // +1: '\0' terminator
189 qemu_put_be32(f, value_buf_len);
190 qemu_put_buffer(f, (uint8_t*) split + 1, value_buf_len);
191
192 *split = '='; /* restore property to "<key>=<value>\0" */
193
194 return 0;
195}
196
197/* Loads a single boot property from a snapshot file
198 */
199static int
200boot_property_load_property( QEMUFile *f )
201{
202 int ret;
203
204 /* load key */
205 uint32_t key_buf_len = qemu_get_be32(f);
206 char* key = android_alloc(key_buf_len);
207 if ((ret = qemu_get_buffer(f, (uint8_t*)key, key_buf_len) != key_buf_len)) {
208 D("%s: key load failed: expected %d bytes, got %d\n",
209 __FUNCTION__, key_buf_len, ret);
210 goto fail_key;
211 }
212
213 /* load value */
214 uint32_t value_buf_len = qemu_get_be32(f);
215 char* value = android_alloc(value_buf_len);
216 if ((ret = qemu_get_buffer(f, (uint8_t*)value, value_buf_len) != value_buf_len)) {
217 D("%s: value load failed: expected %d bytes, got %d\n",
218 __FUNCTION__, value_buf_len, ret);
219 goto fail_value;
220 }
221
222 /* add the property */
223 ret = boot_property_add2(key, key_buf_len - 1, value, value_buf_len - 1);
224 if (ret < 0) {
225 D("%s: load failed: cannot add boot property (details follow)\n",
226 __FUNCTION__);
227 boot_property_raise_warning(ret, key, key_buf_len - 1, value, value_buf_len - 1);
228 goto fail_value;
229 }
230
231 return 0;
232
233 /* in case of errors, clean up before return */
234 fail_value:
235 AFREE(value);
236 fail_key:
237 AFREE(key);
238 return -EIO;
239}
240
241/* Saves the number of available boot properties to file
242 */
243static void
244boot_property_save_count( QEMUFile* f, BootProperty* p )
245{
246 uint32_t property_count = 0;
247 for (; p; p = p->next) {
248 property_count++;
249 }
250
251 qemu_put_be32(f, property_count);
252}
253
254/* Saves all available boot properties to snapshot.
255 */
256static void
257boot_property_save( QEMUFile* f, QemudService* service, void* opaque )
258{
259 boot_property_save_count(f, _boot_properties);
260
261 BootProperty *p = _boot_properties;
262 for ( ; p; p = p->next) {
263 if (boot_property_save_property(f, p)) {
264 break; /* abort on error */
265 }
266 }
267}
268
269/* Replaces the currently available boot properties by those stored
270 * in a snapshot.
271 */
272static int
273boot_property_load( QEMUFile* f, QemudService* service, void* opaque )
274{
275 int ret;
276
277 /* remove properties from old run */
278 boot_property_clear_all();
279
280 /* load properties from snapshot */
281 uint32_t i, property_count = qemu_get_be32(f);
282 for (i = 0; i < property_count; i++) {
283 if ((ret = boot_property_load_property(f))) {
284 return ret;
285 }
286 }
287
288 return 0;
289}
David 'Digit' Turner318e4f22009-05-25 18:01:03 +0200290
291#define SERVICE_NAME "boot-properties"
292
293static void
294boot_property_client_recv( void* opaque,
295 uint8_t* msg,
296 int msglen,
297 QemudClient* client )
298{
299 /* the 'list' command shall send all boot properties
300 * to the client, then close the connection.
301 */
302 if (msglen == 4 && !memcmp(msg, "list", 4)) {
303 BootProperty* prop;
304 for (prop = _boot_properties; prop != NULL; prop = prop->next) {
305 qemud_client_send(client, (uint8_t*)prop->property, prop->length);
306 }
Dries Harnie38f84672010-06-07 23:43:21 +0200307
308 /* Send a NUL to signal the end of the list. */
309 qemud_client_send(client, (uint8_t*)"", 1);
310
David 'Digit' Turner318e4f22009-05-25 18:01:03 +0200311 return;
312 }
313
314 /* unknown command ? */
315 D("%s: ignoring unknown command: %.*s", __FUNCTION__, msglen, msg);
316}
317
318static QemudClient*
319boot_property_service_connect( void* opaque,
320 QemudService* serv,
Vladimir Chtchetkine4c414822011-08-20 08:55:37 -0700321 int channel,
322 const char* client_param )
David 'Digit' Turner318e4f22009-05-25 18:01:03 +0200323{
324 QemudClient* client;
325
Vladimir Chtchetkine4c414822011-08-20 08:55:37 -0700326 client = qemud_client_new( serv, channel, client_param, NULL,
David 'Digit' Turner318e4f22009-05-25 18:01:03 +0200327 boot_property_client_recv,
Ot ten Thije871da2a2010-09-20 10:29:22 +0100328 NULL, NULL, NULL );
David 'Digit' Turner318e4f22009-05-25 18:01:03 +0200329
330 qemud_client_set_framing(client, 1);
331 return client;
332}
333
334
335void
336boot_property_init_service( void )
337{
338 if (!_inited) {
339 QemudService* serv = qemud_service_register( SERVICE_NAME,
Ot ten Thije45041e32010-10-05 17:46:46 +0100340 1, NULL,
341 boot_property_service_connect,
342 boot_property_save,
343 boot_property_load);
David 'Digit' Turner318e4f22009-05-25 18:01:03 +0200344 if (serv == NULL) {
345 derror("could not register '%s' service", SERVICE_NAME);
346 return;
347 }
348 D("registered '%s' qemud service", SERVICE_NAME);
349 }
350}
351
352
353
354void
355boot_property_parse_option( const char* param )
356{
357 char* q = strchr(param,'=');
358 const char* name;
359 const char* value;
360 int namelen, valuelen, ret;
361
362 if (q == NULL) {
363 dwarning("boot property missing (=) separator: %s", param);
364 return;
365 }
366
367 name = param;
368 namelen = q - param;
369
370 value = q+1;
371 valuelen = strlen(name) - (namelen+1);
372
373 ret = boot_property_add2(name, namelen, value, valuelen);
374 if (ret < 0) {
Ot ten Thije45041e32010-10-05 17:46:46 +0100375 boot_property_raise_warning(ret, name, namelen, value, valuelen);
David 'Digit' Turner318e4f22009-05-25 18:01:03 +0200376 }
377}