[Settings] Allow encapsulated options to be specified as named settings
[people/balajirrao/gpxe.git] / src / core / settings.c
1 /*
2  * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <stdint.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include <strings.h>
24 #include <byteswap.h>
25 #include <errno.h>
26 #include <assert.h>
27 #include <gpxe/in.h>
28 #include <gpxe/vsprintf.h>
29 #include <gpxe/settings.h>
30
31 /** @file
32  *
33  * Configuration settings
34  *
35  */
36
37 /** Registered configuration setting types */
38 static struct config_setting_type config_setting_types[0]
39         __table_start ( struct config_setting_type, config_setting_types );
40 static struct config_setting_type config_setting_types_end[0]
41         __table_end ( struct config_setting_type, config_setting_types );
42
43 /** Registered configuration settings */
44 static struct config_setting config_settings[0]
45         __table_start ( struct config_setting, config_settings );
46 static struct config_setting config_settings_end[0]
47         __table_end ( struct config_setting, config_settings );
48
49 struct config_setting_type config_setting_type_hex __config_setting_type;
50
51 /**
52  * Find configuration setting type
53  *
54  * @v name              Name
55  * @ret type            Configuration setting type, or NULL
56  */
57 static struct config_setting_type *
58 find_config_setting_type ( const char *name ) {
59         struct config_setting_type *type;
60
61         for ( type = config_setting_types ; type < config_setting_types_end ;
62               type++ ) {
63                 if ( strcasecmp ( name, type->name ) == 0 )
64                         return type;
65         }
66         return NULL;
67 }
68
69 /**
70  * Find configuration setting
71  *
72  * @v name              Name
73  * @ret setting         Configuration setting, or NULL
74  */
75 static struct config_setting * find_config_setting ( const char *name ) {
76         struct config_setting *setting;
77
78         for ( setting = config_settings ; setting < config_settings_end ;
79               setting++ ) {
80                 if ( strcasecmp ( name, setting->name ) == 0 )
81                         return setting;
82         }
83         return NULL;
84 }
85
86 /**
87  * Find or build configuration setting
88  *
89  * @v name              Name
90  * @v tmp_setting       Temporary buffer for constructing a setting
91  * @ret setting         Configuration setting, or NULL
92  *
93  * Find setting if it exists.  If it doesn't exist, but the name is of
94  * the form "<num>:<type>" (e.g. "12:string"), then construct a
95  * setting for that tag and data type, and return it.  The constructed
96  * setting will be placed in the temporary buffer.
97  */
98 static struct config_setting *
99 find_or_build_config_setting ( const char *name,
100                                struct config_setting *tmp_setting ) {
101         struct config_setting *setting;
102         char *separator;
103
104         /* Look in the list of registered settings first */
105         setting = find_config_setting ( name );
106         if ( setting )
107                 return setting;
108
109         /* If name is of the form "<num>:<type>", try to construct a setting */
110         setting = tmp_setting;
111         memset ( setting, 0, sizeof ( *setting ) );
112         setting->name = name;
113         for ( separator = ( char * ) name ; 1 ; separator++ ) {
114                 setting->tag = ( ( setting->tag << 8 ) |
115                                  strtoul ( separator, &separator, 0 ) );
116                 if ( *separator != '.' )
117                         break;
118         }
119
120         switch ( *separator ) {
121         case ':' :
122                 setting->type = find_config_setting_type ( separator + 1 );
123                 break;
124         case '\0' :
125                 setting->type = &config_setting_type_hex;
126                 break;
127         default :
128                 break;
129         }
130         if ( ! setting->type )
131                 return NULL;
132         return setting;
133 }
134
135 /**
136  * Show value of named setting
137  *
138  * @v context           Configuration context
139  * @v name              Configuration setting name
140  * @v buf               Buffer to contain value
141  * @v len               Length of buffer
142  * @ret len             Length of formatted value, or negative error
143  */
144 int show_named_setting ( struct config_context *context, const char *name,
145                          char *buf, size_t len ) {
146         struct config_setting *setting;
147         struct config_setting tmp_setting;
148
149         setting = find_or_build_config_setting ( name, &tmp_setting );
150         if ( ! setting )
151                 return -ENOENT;
152         return show_setting ( context, setting, buf, len );
153 }
154
155 /**
156  * Set value of named setting
157  *
158  * @v context           Configuration context
159  * @v name              Configuration setting name
160  * @v value             Setting value (as a string)
161  * @ret rc              Return status code
162  */
163 int set_named_setting ( struct config_context *context, const char *name,
164                         const char *value ) {
165         struct config_setting *setting;
166         struct config_setting tmp_setting;
167
168         setting = find_or_build_config_setting ( name, &tmp_setting );
169         if ( ! setting )
170                 return -ENOENT;
171         return setting->type->set ( context, setting, value );
172 }
173
174 /**
175  * Set value of setting
176  *
177  * @v context           Configuration context
178  * @v setting           Configuration setting
179  * @v value             Setting value (as a string), or NULL
180  * @ret rc              Return status code
181  */
182 int set_setting ( struct config_context *context,
183                   struct config_setting *setting,
184                   const char *value ) {
185         if ( ( ! value ) || ( ! *value ) ) {
186                 /* Save putting deletion logic in each individual handler */
187                 return clear_setting ( context, setting );
188         }
189         return setting->type->set ( context, setting, value );
190 }
191
192 /**
193  * Show value of string setting
194  *
195  * @v context           Configuration context
196  * @v setting           Configuration setting
197  * @v buf               Buffer to contain value
198  * @v len               Length of buffer
199  * @ret len             Length of formatted value, or negative error
200  */
201 static int show_string ( struct config_context *context,
202                          struct config_setting *setting,
203                          char *buf, size_t len ) {
204         struct dhcp_option *option;
205
206         option = find_dhcp_option ( context->options, setting->tag );
207         if ( ! option )
208                 return -ENODATA;
209         return dhcp_snprintf ( buf, len, option );
210 }
211
212 /**
213  * Set value of string setting
214  *
215  * @v context           Configuration context
216  * @v setting           Configuration setting
217  * @v value             Setting value (as a string)
218  * @ret rc              Return status code
219  */ 
220 static int set_string ( struct config_context *context,
221                         struct config_setting *setting,
222                         const char *value ) {
223         struct dhcp_option *option;
224
225         option = set_dhcp_option ( context->options, setting->tag,
226                                    value, strlen ( value ) );
227         if ( ! option )
228                 return -ENOSPC;
229         return 0;
230 }
231
232 /** A string configuration setting */
233 struct config_setting_type config_setting_type_string __config_setting_type = {
234         .name = "string",
235         .description = "Text string",
236         .show = show_string,
237         .set = set_string,
238 };
239
240 /**
241  * Show value of IPv4 setting
242  *
243  * @v context           Configuration context
244  * @v setting           Configuration setting
245  * @v buf               Buffer to contain value
246  * @v len               Length of buffer
247  * @ret len             Length of formatted value, or negative error
248  */
249 static int show_ipv4 ( struct config_context *context,
250                        struct config_setting *setting,
251                        char *buf, size_t len ) {
252         struct dhcp_option *option;
253         struct in_addr ipv4;
254
255         option = find_dhcp_option ( context->options, setting->tag );
256         if ( ! option )
257                 return -ENODATA;
258         dhcp_ipv4_option ( option, &ipv4 );
259         return snprintf ( buf, len, inet_ntoa ( ipv4 ) );
260 }
261
262 /**
263  * Set value of IPV4 setting
264  *
265  * @v context           Configuration context
266  * @v setting           Configuration setting
267  * @v value             Setting value (as a string)
268  * @ret rc              Return status code
269  */ 
270 static int set_ipv4 ( struct config_context *context,
271                       struct config_setting *setting,
272                       const char *value ) {
273         struct dhcp_option *option;
274         struct in_addr ipv4;
275         
276         if ( inet_aton ( value, &ipv4 ) == 0 )
277                 return -EINVAL;
278         option = set_dhcp_option ( context->options, setting->tag,
279                                    &ipv4, sizeof ( ipv4 ) );
280         if ( ! option )
281                 return -ENOSPC;
282         return 0;
283 }
284
285 /** An IPv4 configuration setting */
286 struct config_setting_type config_setting_type_ipv4 __config_setting_type = {
287         .name = "ipv4",
288         .description = "IPv4 address",
289         .show = show_ipv4,
290         .set = set_ipv4,
291 };
292
293 /**
294  * Show value of integer setting
295  *
296  * @v context           Configuration context
297  * @v setting           Configuration setting
298  * @v buf               Buffer to contain value
299  * @v len               Length of buffer
300  * @ret len             Length of formatted value, or negative error
301  */
302 static int show_int ( struct config_context *context,
303                       struct config_setting *setting,
304                       char *buf, size_t len ) {
305         struct dhcp_option *option;
306         long num;
307
308         option = find_dhcp_option ( context->options, setting->tag );
309         if ( ! option )
310                 return -ENODATA;
311         num = dhcp_num_option ( option );
312         return snprintf ( buf, len, "%ld", num );
313 }
314
315 /**
316  * Set value of integer setting
317  *
318  * @v context           Configuration context
319  * @v setting           Configuration setting
320  * @v value             Setting value (as a string)
321  * @v size              Size of integer (in bytes)
322  * @ret rc              Return status code
323  */ 
324 static int set_int ( struct config_context *context,
325                      struct config_setting *setting,
326                      const char *value, unsigned int size ) {
327         struct dhcp_option *option;
328         union {
329                 uint32_t num;
330                 uint8_t bytes[4];
331         } u;
332         char *endp;
333
334         /* Parse number */
335         if ( ! *value )
336                 return -EINVAL;
337         u.num = htonl ( strtoul ( value, &endp, 0 ) );
338         if ( *endp )
339                 return -EINVAL;
340
341         /* Set option */
342         option = set_dhcp_option ( context->options, setting->tag,
343                                    &u.bytes[ sizeof ( u ) - size ], size );
344         if ( ! option )
345                 return -ENOSPC;
346         return 0;
347 }
348
349 /**
350  * Set value of 8-bit integer setting
351  *
352  * @v context           Configuration context
353  * @v setting           Configuration setting
354  * @v value             Setting value (as a string)
355  * @v size              Size of integer (in bytes)
356  * @ret rc              Return status code
357  */ 
358 static int set_int8 ( struct config_context *context,
359                       struct config_setting *setting,
360                       const char *value ) {
361         return set_int ( context, setting, value, 1 );
362 }
363
364 /**
365  * Set value of 16-bit integer setting
366  *
367  * @v context           Configuration context
368  * @v setting           Configuration setting
369  * @v value             Setting value (as a string)
370  * @v size              Size of integer (in bytes)
371  * @ret rc              Return status code
372  */ 
373 static int set_int16 ( struct config_context *context,
374                        struct config_setting *setting,
375                        const char *value ) {
376         return set_int ( context, setting, value, 2 );
377 }
378
379 /**
380  * Set value of 32-bit integer setting
381  *
382  * @v context           Configuration context
383  * @v setting           Configuration setting
384  * @v value             Setting value (as a string)
385  * @v size              Size of integer (in bytes)
386  * @ret rc              Return status code
387  */ 
388 static int set_int32 ( struct config_context *context,
389                        struct config_setting *setting,
390                        const char *value ) {
391         return set_int ( context, setting, value, 4 );
392 }
393
394 /** An 8-bit integer configuration setting */
395 struct config_setting_type config_setting_type_int8 __config_setting_type = {
396         .name = "int8",
397         .description = "8-bit integer",
398         .show = show_int,
399         .set = set_int8,
400 };
401
402 /** A 16-bit integer configuration setting */
403 struct config_setting_type config_setting_type_int16 __config_setting_type = {
404         .name = "int16",
405         .description = "16-bit integer",
406         .show = show_int,
407         .set = set_int16,
408 };
409
410 /** A 32-bit integer configuration setting */
411 struct config_setting_type config_setting_type_int32 __config_setting_type = {
412         .name = "int32",
413         .description = "32-bit integer",
414         .show = show_int,
415         .set = set_int32,
416 };
417
418 /**
419  * Set value of hex-string setting
420  *
421  * @v context           Configuration context
422  * @v setting           Configuration setting
423  * @v value             Setting value (as a string)
424  * @ret rc              Return status code
425  */ 
426 static int set_hex ( struct config_context *context,
427                      struct config_setting *setting,
428                      const char *value ) {
429         struct dhcp_option *option;
430         char *ptr = ( char * ) value;
431         uint8_t bytes[ strlen ( value ) ]; /* cannot exceed strlen(value) */
432         unsigned int len = 0;
433
434         while ( 1 ) {
435                 bytes[len++] = strtoul ( ptr, &ptr, 16 );
436                 switch ( *ptr ) {
437                 case '\0' :
438                         option = set_dhcp_option ( context->options,
439                                                    setting->tag, bytes, len );
440                         if ( ! option )
441                                 return -ENOSPC;
442                         return 0;
443                 case ':' :
444                         ptr++;
445                         break;
446                 default :
447                         return -EINVAL;
448                 }
449         }
450 }
451
452 /**
453  * Show value of hex-string setting
454  *
455  * @v context           Configuration context
456  * @v setting           Configuration setting
457  * @v buf               Buffer to contain value
458  * @v len               Length of buffer
459  * @ret len             Length of formatted value, or negative error
460  */
461 static int show_hex ( struct config_context *context,
462                       struct config_setting *setting,
463                       char *buf, size_t len ) {
464         struct dhcp_option *option;
465         int used = 0;
466         int i;
467
468         option = find_dhcp_option ( context->options, setting->tag );
469         if ( ! option )
470                 return -ENODATA;
471
472         for ( i = 0 ; i < option->len ; i++ ) {
473                 used += ssnprintf ( ( buf + used ), ( len - used ),
474                                     "%s%02x", ( used ? ":" : "" ),
475                                     option->data.bytes[i] );
476         }
477         return used;
478 }
479
480 /** A hex-string configuration setting */
481 struct config_setting_type config_setting_type_hex __config_setting_type = {
482         .name = "hex",
483         .description = "Hex string",
484         .show = show_hex,
485         .set = set_hex,
486 };
487
488 /** Some basic setting definitions */
489 struct config_setting basic_config_settings[] __config_setting = {
490         {
491                 .name = "ip",
492                 .description = "IP address of this machine (e.g. 192.168.0.1)",
493                 .tag = DHCP_EB_YIADDR,
494                 .type = &config_setting_type_ipv4,
495         },
496         {
497                 .name = "hostname",
498                 .description = "Host name of this machine",
499                 .tag = DHCP_HOST_NAME,
500                 .type = &config_setting_type_string,
501         },
502         {
503                 .name = "username",
504                 .description = "User name for authentication to servers",
505                 .tag = DHCP_EB_USERNAME,
506                 .type = &config_setting_type_string,
507         },
508         {
509                 .name = "password",
510                 .description = "Password for authentication to servers",
511                 .tag = DHCP_EB_PASSWORD,
512                 .type = &config_setting_type_string,
513         },
514         {
515                 .name = "root-path",
516                 .description = "NFS/iSCSI root path",
517                 .tag = DHCP_ROOT_PATH,
518                 .type = &config_setting_type_string,
519         },
520         {
521                 .name = "priority",
522                 .description = "Priority of these options",
523                 .tag = DHCP_EB_PRIORITY,
524                 .type = &config_setting_type_int8,
525         },
526         {
527                 .name = "initiator-iqn",
528                 .description = "iSCSI qualified name of this machine",
529                 .tag = DHCP_ISCSI_INITIATOR_IQN,
530                 .type = &config_setting_type_string,
531         }
532 };