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