• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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*
boot_property_alloc(const char * name,int namelen,const char * value,int valuelen)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
boot_property_clear_all()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
boot_property_add2(const char * name,int namelen,const char * value,int valuelen)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
boot_property_raise_warning(int ret,const char * name,int namelen,const char * value,int valuelen)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
boot_property_add(const char * name,const char * value)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
boot_property_save_property(QEMUFile * f,BootProperty * p)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
boot_property_load_property(QEMUFile * f)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
boot_property_save_count(QEMUFile * f,BootProperty * p)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
boot_property_save(QEMUFile * f,QemudService * service,void * opaque)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
boot_property_load(QEMUFile * f,QemudService * service,void * opaque)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
boot_property_client_recv(void * opaque,uint8_t * msg,int msglen,QemudClient * client)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*
boot_property_service_connect(void * opaque,QemudService * serv,int channel,const char * client_param)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
boot_property_init_service(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
boot_property_parse_option(const char * param)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