Home | History | Annotate | Download | only in android
      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, -EINVAL);
    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