258fc1c7a6be3eb6d686675394714a40d5ca0946
[people/mcb30/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/settings.h>
29
30 /** @file
31  *
32  * Configuration settings
33  *
34  */
35
36 /** Registered configuration setting types */
37 static struct config_setting_type config_setting_types[0]
38         __table_start ( struct config_setting_type, config_setting_types );
39 static struct config_setting_type config_setting_types_end[0]
40         __table_end ( struct config_setting_type, config_setting_types );
41
42 /** Registered configuration settings */
43 static struct config_setting config_settings[0]
44         __table_start ( struct config_setting, config_settings );
45 static struct config_setting config_settings_end[0]
46         __table_end ( struct config_setting, config_settings );
47
48 /**
49  * Find configuration setting type
50  *
51  * @v name              Name
52  * @ret type            Configuration setting type, or NULL
53  */
54 static struct config_setting_type *
55 find_config_setting_type ( const char *name ) {
56         struct config_setting_type *type;
57
58         for ( type = config_setting_types ; type < config_setting_types_end ;
59               type++ ) {
60                 if ( strcasecmp ( name, type->name ) == 0 )
61                         return type;
62         }
63         return NULL;
64 }
65
66 /**
67  * Find configuration setting
68  *
69  * @v name              Name
70  * @ret setting         Configuration setting, or NULL
71  */
72 static struct config_setting * find_config_setting ( const char *name ) {
73         struct config_setting *setting;
74
75         for ( setting = config_settings ; setting < config_settings_end ;
76               setting++ ) {
77                 if ( strcasecmp ( name, setting->name ) == 0 )
78                         return setting;
79         }
80         return NULL;
81 }
82
83 /**
84  * Find or build configuration setting
85  *
86  * @v name              Name
87  * @v tmp_setting       Temporary buffer for constructing a setting
88  * @ret setting         Configuration setting, or NULL
89  *
90  * Find setting if it exists.  If it doesn't exist, but the name is of
91  * the form "<num>.<type>" (e.g. "12.string"), then construct a
92  * setting for that tag and data type, and return it.  The constructed
93  * setting will be placed in the temporary buffer.
94  */
95 static struct config_setting *
96 find_or_build_config_setting ( const char *name,
97                                struct config_setting *tmp_setting ) {
98         struct config_setting *setting;
99         char *separator;
100
101         /* Look in the list of registered settings first */
102         setting = find_config_setting ( name );
103         if ( setting )
104                 return setting;
105
106         /* If name is of the form "<num>.<type>", try to construct a setting */
107         setting = tmp_setting;
108         memset ( setting, 0, sizeof ( *setting ) );
109         setting->name = name;
110         setting->tag = strtoul ( name, &separator, 10 );
111         if ( *separator != '.' )
112                 return NULL;
113         setting->type = find_config_setting_type ( separator + 1 );
114         if ( ! setting->type )
115                 return NULL;
116         return setting;
117 }
118
119 /**
120  * Show value of named setting
121  *
122  * @v context           Configuration context
123  * @v name              Configuration setting name
124  * @v buf               Buffer to contain value
125  * @v len               Length of buffer
126  * @ret len             Length of formatted value, or negative error
127  */
128 int show_named_setting ( struct config_context *context, const char *name,
129                          char *buf, size_t len ) {
130         struct config_setting *setting;
131         struct config_setting tmp_setting;
132
133         setting = find_or_build_config_setting ( name, &tmp_setting );
134         if ( ! setting )
135                 return -ENOENT;
136         return show_setting ( context, setting, buf, len );
137 }
138
139 /**
140  * Set value of named setting
141  *
142  * @v context           Configuration context
143  * @v name              Configuration setting name
144  * @v value             Setting value (as a string)
145  * @ret rc              Return status code
146  */
147 int set_named_setting ( struct config_context *context, const char *name,
148                         const char *value ) {
149         struct config_setting *setting;
150         struct config_setting tmp_setting;
151
152         setting = find_or_build_config_setting ( name, &tmp_setting );
153         if ( ! setting )
154                 return -ENOENT;
155         return setting->type->set ( context, setting, value );
156 }
157
158 /**
159  * Set value of setting
160  *
161  * @v context           Configuration context
162  * @v setting           Configuration setting
163  * @v value             Setting value (as a string), or NULL
164  * @ret rc              Return status code
165  */
166 int set_setting ( struct config_context *context,
167                   struct config_setting *setting,
168                   const char *value ) {
169         if ( ( ! value ) || ( ! *value ) ) {
170                 /* Save putting deletion logic in each individual handler */
171                 return clear_setting ( context, setting );
172         }
173         return setting->type->set ( context, setting, value );
174 }
175
176 /**
177  * Show value of string setting
178  *
179  * @v context           Configuration context
180  * @v setting           Configuration setting
181  * @v buf               Buffer to contain value
182  * @v len               Length of buffer
183  * @ret len             Length of formatted value, or negative error
184  */
185 static int show_string ( struct config_context *context,
186                          struct config_setting *setting,
187                          char *buf, size_t len ) {
188         struct dhcp_option *option;
189
190         option = find_dhcp_option ( context->options, setting->tag );
191         if ( ! option )
192                 return -ENODATA;
193         return dhcp_snprintf ( buf, len, option );
194 }
195
196 /**
197  * Set value of string setting
198  *
199  * @v context           Configuration context
200  * @v setting           Configuration setting
201  * @v value             Setting value (as a string)
202  * @ret rc              Return status code
203  */ 
204 static int set_string ( struct config_context *context,
205                         struct config_setting *setting,
206                         const char *value ) {
207         struct dhcp_option *option;
208
209         option = set_dhcp_option ( context->options, setting->tag,
210                                    value, strlen ( value ) );
211         if ( ! option )
212                 return -ENOSPC;
213         return 0;
214 }
215
216 /** A string configuration setting */
217 struct config_setting_type config_setting_type_string __config_setting_type = {
218         .name = "string",
219         .description = "Text string",
220         .show = show_string,
221         .set = set_string,
222 };
223
224 /**
225  * Show value of IPv4 setting
226  *
227  * @v context           Configuration context
228  * @v setting           Configuration setting
229  * @v buf               Buffer to contain value
230  * @v len               Length of buffer
231  * @ret len             Length of formatted value, or negative error
232  */
233 static int show_ipv4 ( struct config_context *context,
234                        struct config_setting *setting,
235                        char *buf, size_t len ) {
236         struct dhcp_option *option;
237         struct in_addr ipv4;
238
239         option = find_dhcp_option ( context->options, setting->tag );
240         if ( ! option )
241                 return -ENODATA;
242         dhcp_ipv4_option ( option, &ipv4 );
243         return snprintf ( buf, len, inet_ntoa ( ipv4 ) );
244 }
245
246 /**
247  * Set value of IPV4 setting
248  *
249  * @v context           Configuration context
250  * @v setting           Configuration setting
251  * @v value             Setting value (as a string)
252  * @ret rc              Return status code
253  */ 
254 static int set_ipv4 ( struct config_context *context,
255                       struct config_setting *setting,
256                       const char *value ) {
257         struct dhcp_option *option;
258         struct in_addr ipv4;
259         
260         if ( inet_aton ( value, &ipv4 ) == 0 )
261                 return -EINVAL;
262         option = set_dhcp_option ( context->options, setting->tag,
263                                    &ipv4, sizeof ( ipv4 ) );
264         if ( ! option )
265                 return -ENOSPC;
266         return 0;
267 }
268
269 /** An IPv4 configuration setting */
270 struct config_setting_type config_setting_type_ipv4 __config_setting_type = {
271         .name = "ipv4",
272         .description = "IPv4 address",
273         .show = show_ipv4,
274         .set = set_ipv4,
275 };
276
277 /**
278  * Show value of integer setting
279  *
280  * @v context           Configuration context
281  * @v setting           Configuration setting
282  * @v buf               Buffer to contain value
283  * @v len               Length of buffer
284  * @ret len             Length of formatted value, or negative error
285  */
286 static int show_int ( struct config_context *context,
287                       struct config_setting *setting,
288                       char *buf, size_t len ) {
289         struct dhcp_option *option;
290         long num;
291
292         option = find_dhcp_option ( context->options, setting->tag );
293         if ( ! option )
294                 return -ENODATA;
295         num = dhcp_num_option ( option );
296         return snprintf ( buf, len, "%ld", num );
297 }
298
299 /**
300  * Set value of integer setting
301  *
302  * @v context           Configuration context
303  * @v setting           Configuration setting
304  * @v value             Setting value (as a string)
305  * @v size              Size of integer (in bytes)
306  * @ret rc              Return status code
307  */ 
308 static int set_int ( struct config_context *context,
309                      struct config_setting *setting,
310                      const char *value, unsigned int size ) {
311         struct dhcp_option *option;
312         union {
313                 uint32_t num;
314                 uint8_t bytes[4];
315         } u;
316         char *endp;
317
318         /* Parse number */
319         if ( ! *value )
320                 return -EINVAL;
321         u.num = htonl ( strtoul ( value, &endp, 0 ) );
322         if ( *endp )
323                 return -EINVAL;
324
325         /* Set option */
326         option = set_dhcp_option ( context->options, setting->tag,
327                                    &u.bytes[ sizeof ( u ) - size ], size );
328         if ( ! option )
329                 return -ENOSPC;
330         return 0;
331 }
332
333 /**
334  * Set value of 8-bit integer setting
335  *
336  * @v context           Configuration context
337  * @v setting           Configuration setting
338  * @v value             Setting value (as a string)
339  * @v size              Size of integer (in bytes)
340  * @ret rc              Return status code
341  */ 
342 static int set_int8 ( struct config_context *context,
343                            struct config_setting *setting,
344                            const char *value ) {
345         return set_int ( context, setting, value, 1 );
346 }
347
348 /** An 8-bit integer configuration setting */
349 struct config_setting_type config_setting_type_int8 __config_setting_type = {
350         .name = "int8",
351         .description = "8-bit integer",
352         .show = show_int,
353         .set = set_int8,
354 };
355
356 /** Some basic setting definitions */
357 struct config_setting basic_config_settings[] __config_setting = {
358         {
359                 .name = "ip",
360                 .description = "IP address of this machine (e.g. 192.168.0.1)",
361                 .tag = DHCP_EB_YIADDR,
362                 .type = &config_setting_type_ipv4,
363         },
364         {
365                 .name = "hostname",
366                 .description = "Host name of this machine",
367                 .tag = DHCP_HOST_NAME,
368                 .type = &config_setting_type_string,
369         },
370         {
371                 .name = "username",
372                 .description = "User name for authentication to servers",
373                 .tag = DHCP_EB_USERNAME,
374                 .type = &config_setting_type_string,
375         },
376         {
377                 .name = "password",
378                 .description = "Password for authentication to servers",
379                 .tag = DHCP_EB_PASSWORD,
380                 .type = &config_setting_type_string,
381         },
382         {
383                 .name = "root-path",
384                 .description = "NFS/iSCSI root path",
385                 .tag = DHCP_ROOT_PATH,
386                 .type = &config_setting_type_string,
387         },
388         {
389                 .name = "priority",
390                 .description = "Priority of these options",
391                 .tag = DHCP_EB_PRIORITY,
392                 .type = &config_setting_type_int8,
393         },
394         {
395                 .name = "initiator-iqn",
396                 .description = "iSCSI qualified name of this machine",
397                 .tag = DHCP_ISCSI_INITIATOR_IQN,
398                 .type = &config_setting_type_string,
399         }
400 };