/* Copyright (C) 2009 The Android Open Source Project ** ** This software is licensed under the terms of the GNU General Public ** License version 2, as published by the Free Software Foundation, and ** may be copied, distributed, and modified under those terms. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. */ #include "android/boot-properties.h" #include "android/utils/debug.h" #include "android/utils/system.h" #include "android/hw-qemud.h" #include "android/globals.h" #include "hw/hw.h" #define D(...) VERBOSE_PRINT(init,__VA_ARGS__) /* define T_ACTIVE to 1 to debug transport communications */ #define T_ACTIVE 0 #if T_ACTIVE #define T(...) VERBOSE_PRINT(init,__VA_ARGS__) #else #define T(...) ((void)0) #endif /* this code supports the list of system properties that will * be set on boot in the emulated system. */ typedef struct BootProperty { struct BootProperty* next; char* property; int length; } BootProperty; static BootProperty* boot_property_alloc( const char* name, int namelen, const char* value, int valuelen ) { int length = namelen + 1 + valuelen; BootProperty* prop = android_alloc( sizeof(*prop) + length + 1 ); char* p; prop->next = NULL; prop->property = p = (char*)(prop + 1); prop->length = length; memcpy( p, name, namelen ); p += namelen; *p++ = '='; memcpy( p, value, valuelen ); p += valuelen; *p = '\0'; return prop; } static BootProperty* _boot_properties = NULL; /* address to store pointer to next new list element */ static BootProperty** _boot_properties_tail = &_boot_properties; static int _inited; /* Clears all existing boot properties */ static void boot_property_clear_all() { /* free all elements of the linked list */ BootProperty *p = _boot_properties; BootProperty *next = NULL; while (p) { next = p->next; AFREE(p); p = next; } /* reset list administration to initial state */ _boot_properties = NULL; _boot_properties_tail = &_boot_properties; } /* Appends a new boot property to the end of the internal list. */ int boot_property_add2( const char* name, int namelen, const char* value, int valuelen ) { BootProperty* prop; /* check the lengths */ if (namelen > PROPERTY_MAX_NAME) return -1; if (valuelen > PROPERTY_MAX_VALUE) return -2; /* check that there are not invalid characters in the * property name */ const char* reject = " =$*?'\""; int nn; for (nn = 0; nn < namelen; nn++) { if (strchr(reject, name[nn]) != NULL) return -3; } /* init service if needed */ if (!_inited) { boot_property_init_service(); _inited = 1; } D("Adding boot property: '%.*s' = '%.*s'", namelen, name, valuelen, value); /* add to the end of the internal list */ prop = boot_property_alloc(name, namelen, value, valuelen); *_boot_properties_tail = prop; _boot_properties_tail = &prop->next; return 0; } /* Prints the warning string corresponding to the error code returned by * boot_propery_add2(). */ static void boot_property_raise_warning( int ret, const char* name, int namelen, const char* value, int valuelen ) { switch (ret) { case -1: dwarning("boot property name too long: '%.*s'", namelen, name); break; case -2: dwarning("boot property value too long: '%.*s'", valuelen, value); break; case -3: dwarning("boot property name contains invalid chars: %.*s", namelen, name); break; } } int boot_property_add( const char* name, const char* value ) { int namelen = strlen(name); int valuelen = strlen(value); return boot_property_add2(name, namelen, value, valuelen); } /* Saves a single BootProperty to file. */ static int boot_property_save_property( QEMUFile *f, BootProperty *p ) { /* split in key and value, so we can re-use boot_property_add (and its * sanity checks) when loading */ char *split = strchr(p->property, '='); if (split == NULL) { D("%s: save failed: illegal key/value pair \"%s\" (missing '=')\n", __FUNCTION__, p->property); qemu_file_set_error(f); return -1; } *split = '\0'; /* p->property is now "\0\0" */ uint32_t key_buf_len = (split - p->property) + 1; // +1: '\0' terminator qemu_put_be32(f, key_buf_len); qemu_put_buffer(f, (uint8_t*) p->property, key_buf_len); uint32_t value_buf_len = p->length - key_buf_len + 1; // +1: '\0' terminator qemu_put_be32(f, value_buf_len); qemu_put_buffer(f, (uint8_t*) split + 1, value_buf_len); *split = '='; /* restore property to "=\0" */ return 0; } /* Loads a single boot property from a snapshot file */ static int boot_property_load_property( QEMUFile *f ) { int ret; /* load key */ uint32_t key_buf_len = qemu_get_be32(f); char* key = android_alloc(key_buf_len); if ((ret = qemu_get_buffer(f, (uint8_t*)key, key_buf_len) != key_buf_len)) { D("%s: key load failed: expected %d bytes, got %d\n", __FUNCTION__, key_buf_len, ret); goto fail_key; } /* load value */ uint32_t value_buf_len = qemu_get_be32(f); char* value = android_alloc(value_buf_len); if ((ret = qemu_get_buffer(f, (uint8_t*)value, value_buf_len) != value_buf_len)) { D("%s: value load failed: expected %d bytes, got %d\n", __FUNCTION__, value_buf_len, ret); goto fail_value; } /* add the property */ ret = boot_property_add2(key, key_buf_len - 1, value, value_buf_len - 1); if (ret < 0) { D("%s: load failed: cannot add boot property (details follow)\n", __FUNCTION__); boot_property_raise_warning(ret, key, key_buf_len - 1, value, value_buf_len - 1); goto fail_value; } return 0; /* in case of errors, clean up before return */ fail_value: AFREE(value); fail_key: AFREE(key); return -EIO; } /* Saves the number of available boot properties to file */ static void boot_property_save_count( QEMUFile* f, BootProperty* p ) { uint32_t property_count = 0; for (; p; p = p->next) { property_count++; } qemu_put_be32(f, property_count); } /* Saves all available boot properties to snapshot. */ static void boot_property_save( QEMUFile* f, QemudService* service, void* opaque ) { boot_property_save_count(f, _boot_properties); BootProperty *p = _boot_properties; for ( ; p; p = p->next) { if (boot_property_save_property(f, p)) { break; /* abort on error */ } } } /* Replaces the currently available boot properties by those stored * in a snapshot. */ static int boot_property_load( QEMUFile* f, QemudService* service, void* opaque ) { int ret; /* remove properties from old run */ boot_property_clear_all(); /* load properties from snapshot */ uint32_t i, property_count = qemu_get_be32(f); for (i = 0; i < property_count; i++) { if ((ret = boot_property_load_property(f))) { return ret; } } return 0; } #define SERVICE_NAME "boot-properties" static void boot_property_client_recv( void* opaque, uint8_t* msg, int msglen, QemudClient* client ) { /* the 'list' command shall send all boot properties * to the client, then close the connection. */ if (msglen == 4 && !memcmp(msg, "list", 4)) { BootProperty* prop; for (prop = _boot_properties; prop != NULL; prop = prop->next) { qemud_client_send(client, (uint8_t*)prop->property, prop->length); } /* Send a NUL to signal the end of the list. */ qemud_client_send(client, (uint8_t*)"", 1); return; } /* unknown command ? */ D("%s: ignoring unknown command: %.*s", __FUNCTION__, msglen, msg); } static QemudClient* boot_property_service_connect( void* opaque, QemudService* serv, int channel, const char* client_param ) { QemudClient* client; client = qemud_client_new( serv, channel, client_param, NULL, boot_property_client_recv, NULL, NULL, NULL ); qemud_client_set_framing(client, 1); return client; } void boot_property_init_service( void ) { if (!_inited) { QemudService* serv = qemud_service_register( SERVICE_NAME, 1, NULL, boot_property_service_connect, boot_property_save, boot_property_load); if (serv == NULL) { derror("could not register '%s' service", SERVICE_NAME); return; } D("registered '%s' qemud service", SERVICE_NAME); } } void boot_property_parse_option( const char* param ) { char* q = strchr(param,'='); const char* name; const char* value; int namelen, valuelen, ret; if (q == NULL) { dwarning("boot property missing (=) separator: %s", param); return; } name = param; namelen = q - param; value = q+1; valuelen = strlen(name) - (namelen+1); ret = boot_property_add2(name, namelen, value, valuelen); if (ret < 0) { boot_property_raise_warning(ret, name, namelen, value, valuelen); } }