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* 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 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 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 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 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 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 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 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 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 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 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* 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 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 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