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
36typedef 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
64static BootProperty*   _boot_properties = NULL;
65/* address to store pointer to next new list element */
66static BootProperty**  _boot_properties_tail = &_boot_properties;
67static int             _inited;
68
69/* 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 */
90int
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
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 */
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}
155
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
165/* 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     */
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 */
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}
290
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        }
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
318static QemudClient*
319boot_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
335void
336boot_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
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) {
375        boot_property_raise_warning(ret, name, namelen, value, valuelen);
376    }
377}
378