1 /* 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
19 #include "hw/hw.h"
20
21 #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
32 /* this code supports the list of system properties that will
33 * be set on boot in the emulated system.
34 */
35
36 typedef struct BootProperty {
37 struct BootProperty* next;
38 char* property;
39 int length;
40 } BootProperty;
41
42 static BootProperty*
boot_property_alloc(const char * name,int namelen,const char * value,int valuelen)43 boot_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
64 static BootProperty* _boot_properties = NULL;
65 /* address to store pointer to next new list element */
66 static BootProperty** _boot_properties_tail = &_boot_properties;
67 static int _inited;
68
69 /* Clears all existing boot properties
70 */
71 static void
boot_property_clear_all()72 boot_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 */
90 int
boot_property_add2(const char * name,int namelen,const char * value,int valuelen)91 boot_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
124 /* add to the end of the internal list */
125 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
133 /* Prints the warning string corresponding to the error code returned by
134 * boot_propery_add2().
135 */
136 static void
boot_property_raise_warning(int ret,const char * name,int namelen,const char * value,int valuelen)137 boot_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 }
155
156 int
boot_property_add(const char * name,const char * value)157 boot_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
165 /* Saves a single BootProperty to file.
166 */
167 static int
boot_property_save_property(QEMUFile * f,BootProperty * p)168 boot_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 */
173
174 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);
178 qemu_file_set_error(f);
179 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 */
199 static int
boot_property_load_property(QEMUFile * f)200 boot_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 */
243 static void
boot_property_save_count(QEMUFile * f,BootProperty * p)244 boot_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 */
256 static void
boot_property_save(QEMUFile * f,QemudService * service,void * opaque)257 boot_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 */
272 static int
boot_property_load(QEMUFile * f,QemudService * service,void * opaque)273 boot_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 }
290
291 #define SERVICE_NAME "boot-properties"
292
293 static void
boot_property_client_recv(void * opaque,uint8_t * msg,int msglen,QemudClient * client)294 boot_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 }
307
308 /* Send a NUL to signal the end of the list. */
309 qemud_client_send(client, (uint8_t*)"", 1);
310
311 return;
312 }
313
314 /* unknown command ? */
315 D("%s: ignoring unknown command: %.*s", __FUNCTION__, msglen, msg);
316 }
317
318 static QemudClient*
boot_property_service_connect(void * opaque,QemudService * serv,int channel,const char * client_param)319 boot_property_service_connect( void* opaque,
320 QemudService* serv,
321 int channel,
322 const char* client_param )
323 {
324 QemudClient* client;
325
326 client = qemud_client_new( serv, channel, client_param, NULL,
327 boot_property_client_recv,
328 NULL, NULL, NULL );
329
330 qemud_client_set_framing(client, 1);
331 return client;
332 }
333
334
335 void
boot_property_init_service(void)336 boot_property_init_service( void )
337 {
338 if (!_inited) {
339 QemudService* serv = qemud_service_register( SERVICE_NAME,
340 1, NULL,
341 boot_property_service_connect,
342 boot_property_save,
343 boot_property_load);
344 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
354 void
boot_property_parse_option(const char * param)355 boot_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) {
375 boot_property_raise_warning(ret, name, namelen, value, valuelen);
376 }
377 }
378