[Settings] Add int16, int32 and hex-string configuration setting types
[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         setting->tag = strtoul ( name, &separator, 10 );
114         switch ( *separator ) {
115         case '.' :
116                 setting->type = find_config_setting_type ( separator + 1 );
117                 break;
118         case '\0' :
119                 setting->type = &config_setting_type_hex;
120                 break;
121         default :
122                 break;
123         }
124         if ( ! setting->type )
125                 return NULL;
126         return setting;
127 }
128
129 /**
130  * Show value of named setting
131  *
132  * @v context           Configuration context
133  * @v name              Configuration setting name
134  * @v buf               Buffer to contain value
135  * @v len               Length of buffer
136  * @ret len             Length of formatted value, or negative error
137  */
138 int show_named_setting ( struct config_context *context, const char *name,
139                          char *buf, size_t len ) {
140         struct config_setting *setting;
141         struct config_setting tmp_setting;
142
143         setting = find_or_build_config_setting ( name, &tmp_setting );
144         if ( ! setting )
145                 return -ENOENT;
146         return show_setting ( context, setting, buf, len );
147 }
148
149 /**
150  * Set value of named setting
151  *
152  * @v context           Configuration context
153  * @v name              Configuration setting name
154  * @v value             Setting value (as a string)
155  * @ret rc              Return status code
156  */
157 int set_named_setting ( struct config_context *context, const char *name,
158                         const char *value ) {
159         struct config_setting *setting;
160         struct config_setting tmp_setting;
161
162         setting = find_or_build_config_setting ( name, &tmp_setting );
163         if ( ! setting )
164                 return -ENOENT;
165         return setting->type->set ( context, setting, value );
166 }
167
168 /**
169  * Set value of setting
170  *
171  * @v context           Configuration context
172  * @v setting           Configuration setting
173  * @v value             Setting value (as a string), or NULL
174  * @ret rc              Return status code
175  */
176 int set_setting ( struct config_context *context,
177                   struct config_setting *setting,
178                   const char *value ) {
179         if ( ( ! value ) || ( ! *value ) ) {
180                 /* Save putting deletion logic in each individual handler */
181                 return clear_setting ( context, setting );
182         }
183         return setting->type->set ( context, setting, value );
184 }
185
186 /**
187  * Show value of string setting
188  *
189  * @v context           Configuration context
190  * @v setting           Configuration setting
191  * @v buf               Buffer to contain value
192  * @v len               Length of buffer
193  * @ret len             Length of formatted value, or negative error
194  */
195 static int show_string ( struct config_context *context,
196                          struct config_setting *setting,
197                          char *buf, size_t len ) {
198         struct dhcp_option *option;
199
200         option = find_dhcp_option ( context->options, setting->tag );
201         if ( ! option )
202                 return -ENODATA;
203         return dhcp_snprintf ( buf, len, option );
204 }
205
206 /**
207  * Set value of string setting
208  *
209  * @v context           Configuration context
210  * @v setting           Configuration setting
211  * @v value             Setting value (as a string)
212  * @ret rc              Return status code
213  */ 
214 static int set_string ( struct config_context *context,
215                         struct config_setting *setting,
216                         const char *value ) {
217         struct dhcp_option *option;
218
219         option = set_dhcp_option ( context->options, setting->tag,
220                                    value, strlen ( value ) );
221         if ( ! option )
222                 return -ENOSPC;
223         return 0;
224 }
225
226 /** A string configuration setting */
227 struct config_setting_type config_setting_type_string __config_setting_type = {
228         .name = "string",
229         .description = "Text string",
230         .show = show_string,
231         .set = set_string,
232 };
233
234 /**
235  * Show value of IPv4 setting
236  *
237  * @v context           Configuration context
238  * @v setting           Configuration setting
239  * @v buf               Buffer to contain value
240  * @v len               Length of buffer
241  * @ret len             Length of formatted value, or negative error
242  */
243 static int show_ipv4 ( struct config_context *context,
244                        struct config_setting *setting,
245                        char *buf, size_t len ) {
246         struct dhcp_option *option;
247         struct in_addr ipv4;
248
249         option = find_dhcp_option ( context->options, setting->tag );
250         if ( ! option )
251                 return -ENODATA;
252         dhcp_ipv4_option ( option, &ipv4 );
253         return snprintf ( buf, len, inet_ntoa ( ipv4 ) );
254 }
255
256 /**
257  * Set value of IPV4 setting
258  *
259  * @v context           Configuration context
260  * @v setting           Configuration setting
261  * @v value             Setting value (as a string)
262  * @ret rc              Return status code
263  */ 
264 static int set_ipv4 ( struct config_context *context,
265                       struct config_setting *setting,
266                       const char *value ) {
267         struct dhcp_option *option;
268         struct in_addr ipv4;
269         
270         if ( inet_aton ( value, &ipv4 ) == 0 )
271                 return -EINVAL;
272         option = set_dhcp_option ( context->options, setting->tag,
273                                    &ipv4, sizeof ( ipv4 ) );
274         if ( ! option )
275                 return -ENOSPC;
276         return 0;
277 }
278
279 /** An IPv4 configuration setting */
280 struct config_setting_type config_setting_type_ipv4 __config_setting_type = {
281         .name = "ipv4",
282         .description = "IPv4 address",
283         .show = show_ipv4,
284         .set = set_ipv4,
285 };
286
287 /**
288  * Show value of integer setting
289  *
290  * @v context           Configuration context
291  * @v setting           Configuration setting
292  * @v buf               Buffer to contain value
293  * @v len               Length of buffer
294  * @ret len             Length of formatted value, or negative error
295  */
296 static int show_int ( struct config_context *context,
297                       struct config_setting *setting,
298                       char *buf, size_t len ) {
299         struct dhcp_option *option;
300         long num;
301
302         option = find_dhcp_option ( context->options, setting->tag );
303         if ( ! option )
304                 return -ENODATA;
305         num = dhcp_num_option ( option );
306         return snprintf ( buf, len, "%ld", num );
307 }
308
309 /**
310  * Set value of integer setting
311  *
312  * @v context           Configuration context
313  * @v setting           Configuration setting
314  * @v value             Setting value (as a string)
315  * @v size              Size of integer (in bytes)
316  * @ret rc              Return status code
317  */ 
318 static int set_int ( struct config_context *context,
319                      struct config_setting *setting,
320                      const char *value, unsigned int size ) {
321         struct dhcp_option *option;
322         union {
323                 uint32_t num;
324                 uint8_t bytes[4];
325         } u;
326         char *endp;
327
328         /* Parse number */
329         if ( ! *value )
330                 return -EINVAL;
331         u.num = htonl ( strtoul ( value, &endp, 0 ) );
332         if ( *endp )
333                 return -EINVAL;
334
335         /* Set option */
336         option = set_dhcp_option ( context->options, setting->tag,
337                                    &u.bytes[ sizeof ( u ) - size ], size );
338         if ( ! option )
339                 return -ENOSPC;
340         return 0;
341 }
342
343 /**
344  * Set value of 8-bit integer setting
345  *
346  * @v context           Configuration context
347  * @v setting           Configuration setting
348  * @v value             Setting value (as a string)
349  * @v size              Size of integer (in bytes)
350  * @ret rc              Return status code
351  */ 
352 static int set_int8 ( struct config_context *context,
353                       struct config_setting *setting,
354                       const char *value ) {
355         return set_int ( context, setting, value, 1 );
356 }
357
358 /**
359  * Set value of 16-bit integer setting
360  *
361  * @v context           Configuration context
362  * @v setting           Configuration setting
363  * @v value             Setting value (as a string)
364  * @v size              Size of integer (in bytes)
365  * @ret rc              Return status code
366  */ 
367 static int set_int16 ( struct config_context *context,
368                        struct config_setting *setting,
369                        const char *value ) {
370         return set_int ( context, setting, value, 2 );
371 }
372
373 /**
374  * Set value of 32-bit integer setting
375  *
376  * @v context           Configuration context
377  * @v setting           Configuration setting
378  * @v value             Setting value (as a string)
379  * @v size              Size of integer (in bytes)
380  * @ret rc              Return status code
381  */ 
382 static int set_int32 ( struct config_context *context,
383                        struct config_setting *setting,
384                        const char *value ) {
385         return set_int ( context, setting, value, 4 );
386 }
387
388 /** An 8-bit integer configuration setting */
389 struct config_setting_type config_setting_type_int8 __config_setting_type = {
390         .name = "int8",
391         .description = "8-bit integer",
392         .show = show_int,
393         .set = set_int8,
394 };
395
396 /** A 16-bit integer configuration setting */
397 struct config_setting_type config_setting_type_int16 __config_setting_type = {
398         .name = "int16",
399         .description = "16-bit integer",
400         .show = show_int,
401         .set = set_int16,
402 };
403
404 /** A 32-bit integer configuration setting */
405 struct config_setting_type config_setting_type_int32 __config_setting_type = {
406         .name = "int32",
407         .description = "32-bit integer",
408         .show = show_int,
409         .set = set_int32,
410 };
411
412 /**
413  * Set value of hex-string setting
414  *
415  * @v context           Configuration context
416  * @v setting           Configuration setting
417  * @v value             Setting value (as a string)
418  * @ret rc              Return status code
419  */ 
420 static int set_hex ( struct config_context *context,
421                      struct config_setting *setting,
422                      const char *value ) {
423         struct dhcp_option *option;
424         char *ptr = ( char * ) value;
425         uint8_t bytes[ strlen ( value ) ]; /* cannot exceed strlen(value) */
426         unsigned int len = 0;
427
428         while ( 1 ) {
429                 bytes[len++] = strtoul ( ptr, &ptr, 16 );
430                 switch ( *ptr ) {
431                 case '\0' :
432                         option = set_dhcp_option ( context->options,
433                                                    setting->tag, bytes, len );
434                         if ( ! option )
435                                 return -ENOSPC;
436                         return 0;
437                 case ':' :
438                         ptr++;
439                         break;
440                 default :
441                         return -EINVAL;
442                 }
443         }
444 }
445
446 /**
447  * Show value of hex-string setting
448  *
449  * @v context           Configuration context
450  * @v setting           Configuration setting
451  * @v buf               Buffer to contain value
452  * @v len               Length of buffer
453  * @ret len             Length of formatted value, or negative error
454  */
455 static int show_hex ( struct config_context *context,
456                       struct config_setting *setting,
457                       char *buf, size_t len ) {
458         struct dhcp_option *option;
459         int used = 0;
460         int i;
461
462         option = find_dhcp_option ( context->options, setting->tag );
463         if ( ! option )
464                 return -ENODATA;
465
466         for ( i = 0 ; i < option->len ; i++ ) {
467                 used += ssnprintf ( ( buf + used ), ( len - used ),
468                                     "%s%02x", ( used ? ":" : "" ),
469                                     option->data.bytes[i] );
470         }
471         return used;
472 }
473
474 /** A hex-string configuration setting */
475 struct config_setting_type config_setting_type_hex __config_setting_type = {
476         .name = "hex",
477         .description = "Hex string",
478         .show = show_hex,
479         .set = set_hex,
480 };
481
482 /** Some basic setting definitions */
483 struct config_setting basic_config_settings[] __config_setting = {
484         {
485                 .name = "ip",
486                 .description = "IP address of this machine (e.g. 192.168.0.1)",
487                 .tag = DHCP_EB_YIADDR,
488                 .type = &config_setting_type_ipv4,
489         },
490         {
491                 .name = "hostname",
492                 .description = "Host name of this machine",
493                 .tag = DHCP_HOST_NAME,
494                 .type = &config_setting_type_string,
495         },
496         {
497                 .name = "username",
498                 .description = "User name for authentication to servers",
499                 .tag = DHCP_EB_USERNAME,
500                 .type = &config_setting_type_string,
501         },
502         {
503                 .name = "password",
504                 .description = "Password for authentication to servers",
505                 .tag = DHCP_EB_PASSWORD,
506                 .type = &config_setting_type_string,
507         },
508         {
509                 .name = "root-path",
510                 .description = "NFS/iSCSI root path",
511                 .tag = DHCP_ROOT_PATH,
512                 .type = &config_setting_type_string,
513         },
514         {
515                 .name = "priority",
516                 .description = "Priority of these options",
517                 .tag = DHCP_EB_PRIORITY,
518                 .type = &config_setting_type_int8,
519         },
520         {
521                 .name = "initiator-iqn",
522                 .description = "iSCSI qualified name of this machine",
523                 .tag = DHCP_ISCSI_INITIATOR_IQN,
524                 .type = &config_setting_type_string,
525         }
526 };