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#define  D(...)  VERBOSE_PRINT(init,__VA_ARGS__)
20
21/* define T_ACTIVE to 1 to debug transport communications */
22#define  T_ACTIVE  0
23
24#if T_ACTIVE
25#define  T(...)  VERBOSE_PRINT(init,__VA_ARGS__)
26#else
27#define  T(...)   ((void)0)
28#endif
29
30/* this code supports the list of system properties that will
31 * be set on boot in the emulated system.
32 */
33
34typedef struct BootProperty {
35    struct BootProperty*  next;
36    char*                 property;
37    int                   length;
38} BootProperty;
39
40static BootProperty*
41boot_property_alloc( const char*  name,  int  namelen,
42                     const char*  value, int  valuelen )
43{
44    int            length = namelen + 1 + valuelen;
45    BootProperty*  prop = android_alloc( sizeof(*prop) + length + 1 );
46    char*          p;
47
48    prop->next     = NULL;
49    prop->property = p = (char*)(prop + 1);
50    prop->length   = length;
51
52    memcpy( p, name, namelen );
53    p += namelen;
54    *p++ = '=';
55    memcpy( p, value, valuelen );
56    p += valuelen;
57    *p = '\0';
58
59    return prop;
60}
61
62static BootProperty*   _boot_properties;
63static BootProperty**  _boot_properties_tail = &_boot_properties;
64static int             _inited;
65
66int
67boot_property_add2( const char*  name, int  namelen,
68                    const char*  value, int  valuelen )
69{
70    BootProperty*  prop;
71
72    /* check the lengths
73     */
74    if (namelen > PROPERTY_MAX_NAME)
75        return -1;
76
77    if (valuelen > PROPERTY_MAX_VALUE)
78        return -2;
79
80    /* check that there are not invalid characters in the
81     * property name
82     */
83    const char*  reject = " =$*?'\"";
84    int          nn;
85
86    for (nn = 0; nn < namelen; nn++) {
87        if (strchr(reject, name[nn]) != NULL)
88            return -3;
89    }
90
91    /* init service if needed */
92    if (!_inited) {
93        boot_property_init_service();
94        _inited = 1;
95    }
96
97    D("Adding boot property: '%.*s' = '%.*s'",
98      namelen, name, valuelen, value);
99
100    /* add to the internal list */
101    prop = boot_property_alloc(name, namelen, value, valuelen);
102
103    *_boot_properties_tail = prop;
104    _boot_properties_tail  = &prop->next;
105
106    return 0;
107}
108
109
110int
111boot_property_add( const char*  name, const char*  value )
112{
113    int  namelen = strlen(name);
114    int  valuelen = strlen(value);
115
116    return boot_property_add2(name, namelen, value, valuelen);
117}
118
119
120
121#define SERVICE_NAME  "boot-properties"
122
123static void
124boot_property_client_recv( void*         opaque,
125                           uint8_t*      msg,
126                           int           msglen,
127                           QemudClient*  client )
128{
129    /* the 'list' command shall send all boot properties
130     * to the client, then close the connection.
131     */
132    if (msglen == 4 && !memcmp(msg, "list", 4)) {
133        BootProperty*  prop;
134        for (prop = _boot_properties; prop != NULL; prop = prop->next) {
135            qemud_client_send(client, (uint8_t*)prop->property, prop->length);
136        }
137
138        /* Send a NUL to signal the end of the list. */
139        qemud_client_send(client, (uint8_t*)"", 1);
140
141        qemud_client_close(client);
142        return;
143    }
144
145    /* unknown command ? */
146    D("%s: ignoring unknown command: %.*s", __FUNCTION__, msglen, msg);
147}
148
149static QemudClient*
150boot_property_service_connect( void*          opaque,
151                               QemudService*  serv,
152                               int            channel )
153{
154    QemudClient*  client;
155
156    client = qemud_client_new( serv, channel, NULL,
157                               boot_property_client_recv,
158                               NULL );
159
160    qemud_client_set_framing(client, 1);
161    return client;
162}
163
164
165void
166boot_property_init_service( void )
167{
168    if (!_inited) {
169        QemudService*  serv = qemud_service_register( SERVICE_NAME,
170                                                    1, NULL,
171                                                    boot_property_service_connect );
172        if (serv == NULL) {
173            derror("could not register '%s' service", SERVICE_NAME);
174            return;
175        }
176        D("registered '%s' qemud service", SERVICE_NAME);
177    }
178}
179
180
181
182void
183boot_property_parse_option( const char*  param )
184{
185    char* q = strchr(param,'=');
186    const char* name;
187    const char* value;
188    int   namelen, valuelen, ret;
189
190    if (q == NULL) {
191        dwarning("boot property missing (=) separator: %s", param);
192        return;
193    }
194
195    name    = param;
196    namelen = q - param;
197
198    value    = q+1;
199    valuelen = strlen(name) - (namelen+1);
200
201    ret = boot_property_add2(name, namelen, value, valuelen);
202    if (ret < 0) {
203        switch (ret) {
204        case -1:
205            dwarning("boot property name too long: '%.*s'",
206                        namelen, name);
207            break;
208        case -2:
209            dwarning("boot property value too long: '%.*s'",
210                        valuelen, value);
211            break;
212        case -3:
213            dwarning("boot property name contains invalid chars: %.*s",
214                        namelen, name);
215            break;
216        }
217    }
218}
219