Move {show,set,clear}_setting() to {show,set,clear}_named_setting().
[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 <string.h>
22 #include <byteswap.h>
23 #include <errno.h>
24 #include <assert.h>
25 #include <vsprintf.h>
26 #include <gpxe/in.h>
27 #include <gpxe/settings.h>
28
29 /** @file
30  *
31  * Configuration settings
32  *
33  */
34
35 /** Registered configuration setting types */
36 static struct config_setting_type
37 config_setting_types[0] __table_start ( config_setting_types );
38 static struct config_setting_type
39 config_setting_types_end[0] __table_end ( config_setting_types );
40
41 /** Registered configuration settings */
42 static struct config_setting
43 config_settings[0] __table_start ( config_settings );
44 static struct config_setting
45 config_settings_end[0] __table_end ( config_settings );
46
47 /**
48  * Find configuration setting type
49  *
50  * @v name              Name
51  * @ret type            Configuration setting type, or NULL
52  */
53 static struct config_setting_type *
54 find_config_setting_type ( const char *name ) {
55         struct config_setting_type *type;
56
57         for ( type = config_setting_types ; type < config_setting_types_end ;
58               type++ ) {
59                 if ( strcmp ( name, type->name ) == 0 )
60                         return type;
61         }
62         return NULL;
63 }
64
65 /**
66  * Find configuration setting
67  *
68  * @v name              Name
69  * @ret setting         Configuration setting, or NULL
70  */
71 static struct config_setting * find_config_setting ( const char *name ) {
72         struct config_setting *setting;
73
74         for ( setting = config_settings ; setting < config_settings_end ;
75               setting++ ) {
76                 if ( strcmp ( name, setting->name ) == 0 )
77                         return setting;
78         }
79         return NULL;
80 }
81
82 /**
83  * Find or build configuration setting
84  *
85  * @v name              Name
86  * @v tmp_setting       Temporary buffer for constructing a setting
87  * @ret setting         Configuration setting, or NULL
88  *
89  * Find setting if it exists.  If it doesn't exist, but the name is of
90  * the form "<num>.<type>" (e.g. "12.string"), then construct a
91  * setting for that tag and data type, and return it.  The constructed
92  * setting will be placed in the temporary buffer.
93  */
94 static struct config_setting *
95 find_or_build_config_setting ( const char *name,
96                                struct config_setting *tmp_setting ) {
97         struct config_setting *setting;
98         char *separator;
99
100         /* Look in the list of registered settings first */
101         setting = find_config_setting ( name );
102         if ( setting )
103                 return setting;
104
105         /* If name is of the form "<num>.<type>", try to construct a setting */
106         setting = tmp_setting;
107         memset ( setting, 0, sizeof ( *setting ) );
108         setting->name = name;
109         setting->tag = strtoul ( name, &separator, 10 );
110         if ( *separator != '.' )
111                 return NULL;
112         setting->type = find_config_setting_type ( separator + 1 );
113         if ( ! setting->type )
114                 return NULL;
115         return setting;
116 }
117
118 /**
119  * Show value of named setting
120  *
121  * @v context           Configuration context
122  * @v name              Configuration setting name
123  * @v buf               Buffer to contain value
124  * @v len               Length of buffer
125  * @ret rc              Return status code
126  */
127 int show_named_setting ( struct config_context *context, const char *name,
128                          char *buf, size_t len ) {
129         struct config_setting *setting;
130         struct config_setting tmp_setting;
131
132         setting = find_or_build_config_setting ( name, &tmp_setting );
133         if ( ! setting )
134                 return -ENOENT;
135         return show_setting ( context, setting, buf, len );
136 }
137
138 /**
139  * Set value of named setting
140  *
141  * @v context           Configuration context
142  * @v name              Configuration setting name
143  * @v value             Setting value (as a string)
144  * @ret rc              Return status code
145  */
146 int set_named_setting ( struct config_context *context, const char *name,
147                         const char *value ) {
148         struct config_setting *setting;
149         struct config_setting tmp_setting;
150
151         setting = find_or_build_config_setting ( name, &tmp_setting );
152         if ( ! setting )
153                 return -ENOENT;
154         return setting->type->set ( context, setting, value );
155 }
156
157 /**
158  * Set value of setting
159  *
160  * @v context           Configuration context
161  * @v setting           Configuration setting
162  * @v value             Setting value (as a string), or NULL
163  * @ret rc              Return status code
164  */
165 int set_setting ( struct config_context *context,
166                   struct config_setting *setting,
167                   const char *value ) {
168         if ( ( ! value ) || ( ! *value ) ) {
169                 /* Save putting deletion logic in each individual handler */
170                 return clear_setting ( context, setting );
171         }
172         return setting->type->set ( context, setting, value );
173 }
174
175 /**
176  * Show value of string setting
177  *
178  * @v context           Configuration context
179  * @v setting           Configuration setting
180  * @v buf               Buffer to contain value
181  * @v len               Length of buffer
182  * @ret rc              Return status code
183  */
184 static int show_string ( struct config_context *context,
185                          struct config_setting *setting,
186                          char *buf, size_t len ) {
187         struct dhcp_option *option;
188
189         option = find_dhcp_option ( context->options, setting->tag );
190         if ( ! option )
191                 return -ENODATA;
192         dhcp_snprintf ( buf, len, option );
193         return 0;
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         .show = show_string,
220         .set = set_string,
221 };
222
223 /**
224  * Show value of IPv4 setting
225  *
226  * @v context           Configuration context
227  * @v setting           Configuration setting
228  * @v buf               Buffer to contain value
229  * @v len               Length of buffer
230  * @ret rc              Return status code
231  */
232 static int show_ipv4 ( struct config_context *context,
233                        struct config_setting *setting,
234                        char *buf, size_t len ) {
235         struct dhcp_option *option;
236         struct in_addr ipv4;
237
238         option = find_dhcp_option ( context->options, setting->tag );
239         if ( ! option )
240                 return -ENODATA;
241         dhcp_ipv4_option ( option, &ipv4 );
242         snprintf ( buf, len, inet_ntoa ( ipv4 ) );
243         return 0;
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         .show = show_ipv4,
273         .set = set_ipv4,
274 };
275
276 /**
277  * Show value of integer setting
278  *
279  * @v context           Configuration context
280  * @v setting           Configuration setting
281  * @v buf               Buffer to contain value
282  * @v len               Length of buffer
283  * @ret rc              Return status code
284  */
285 static int show_int ( struct config_context *context,
286                       struct config_setting *setting,
287                       char *buf, size_t len ) {
288         struct dhcp_option *option;
289         long num;
290
291         option = find_dhcp_option ( context->options, setting->tag );
292         if ( ! option )
293                 return -ENODATA;
294         num = dhcp_num_option ( option );
295         snprintf ( buf, len, "%ld", num );
296         return 0;
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, 8 );
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         .show = show_int,
352         .set = set_int8,
353 };
354
355 /** Some basic setting definitions */
356 struct config_setting ip_config_setting __config_setting = {
357         .name = "ip",
358         .tag = DHCP_EB_YIADDR,
359         .type = &config_setting_type_ipv4,
360 };
361 struct config_setting hostname_config_setting __config_setting = {
362         .name = "hostname",
363         .tag = DHCP_HOST_NAME,
364         .type = &config_setting_type_string,
365 };
366 struct config_setting username_config_setting __config_setting = {
367         .name = "username",
368         .tag = DHCP_EB_USERNAME,
369         .type = &config_setting_type_string,
370 };
371 struct config_setting password_config_setting __config_setting = {
372         .name = "password",
373         .tag = DHCP_EB_PASSWORD,
374         .type = &config_setting_type_string,
375 };
376 struct config_setting root_path_config_setting __config_setting = {
377         .name = "root-path",
378         .tag = DHCP_ROOT_PATH,
379         .type = &config_setting_type_string,
380 };
381 struct config_setting priority_config_setting __config_setting = {
382         .name = "priority",
383         .tag = DHCP_EB_PRIORITY,
384         .type = &config_setting_type_int8,
385 };