Added clear_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 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_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 setting->type->show ( context, setting, buf, len );
136 }
137
138 /**
139  * Set value of 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_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  * Clear setting
159  *
160  * @v context           Configuration context
161  * @v name              Configuration setting name
162  * @ret rc              Return status code
163  */
164 int clear_setting ( struct config_context *context, const char *name ) {
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
172         /* All types of settings get cleared the same way */
173         delete_dhcp_option ( context->options, setting->tag );
174
175         return 0;
176 }
177
178 /**
179  * Show value of string setting
180  *
181  * @v context           Configuration context
182  * @v setting           Configuration setting
183  * @v buf               Buffer to contain value
184  * @v len               Length of buffer
185  * @ret rc              Return status code
186  */
187 static int show_string ( struct config_context *context,
188                          struct config_setting *setting,
189                          char *buf, size_t len ) {
190         struct dhcp_option *option;
191
192         option = find_dhcp_option ( context->options, setting->tag );
193         if ( ! option )
194                 return -ENODATA;
195         dhcp_snprintf ( buf, len, option );
196         return 0;
197 }
198
199 /**
200  * Set value of string setting
201  *
202  * @v context           Configuration context
203  * @v setting           Configuration setting
204  * @v value             Setting value (as a string)
205  * @ret rc              Return status code
206  */ 
207 static int set_string ( struct config_context *context,
208                         struct config_setting *setting,
209                         const char *value ) {
210         struct dhcp_option *option;
211
212         option = set_dhcp_option ( context->options, setting->tag,
213                                    value, strlen ( value ) );
214         if ( ! option )
215                 return -ENOMEM;
216         return 0;
217 }
218
219 /** A string configuration setting */
220 struct config_setting_type config_setting_type_string __config_setting_type = {
221         .name = "string",
222         .show = show_string,
223         .set = set_string,
224 };
225
226 /**
227  * Show value of IPv4 setting
228  *
229  * @v context           Configuration context
230  * @v setting           Configuration setting
231  * @v buf               Buffer to contain value
232  * @v len               Length of buffer
233  * @ret rc              Return status code
234  */
235 static int show_ipv4 ( struct config_context *context,
236                        struct config_setting *setting,
237                        char *buf, size_t len ) {
238         struct dhcp_option *option;
239         struct in_addr ipv4;
240
241         option = find_dhcp_option ( context->options, setting->tag );
242         if ( ! option )
243                 return -ENODATA;
244         dhcp_ipv4_option ( option, &ipv4 );
245         snprintf ( buf, len, inet_ntoa ( ipv4 ) );
246         return 0;
247 }
248
249 /**
250  * Set value of IPV4 setting
251  *
252  * @v context           Configuration context
253  * @v setting           Configuration setting
254  * @v value             Setting value (as a string)
255  * @ret rc              Return status code
256  */ 
257 static int set_ipv4 ( struct config_context *context,
258                       struct config_setting *setting,
259                       const char *value ) {
260         struct dhcp_option *option;
261         struct in_addr ipv4;
262         int rc;
263         
264         if ( ( rc = inet_aton ( value, &ipv4 ) ) == 0 )
265                 return rc;
266         option = set_dhcp_option ( context->options, setting->tag,
267                                    &ipv4, sizeof ( ipv4 ) );
268         if ( ! option )
269                 return -ENOMEM;
270         return 0;
271 }
272
273 /** An IPv4 configuration setting */
274 struct config_setting_type config_setting_type_ipv4 __config_setting_type = {
275         .name = "ipv4",
276         .show = show_ipv4,
277         .set = set_ipv4,
278 };
279
280 /**
281  * Show value of integer setting
282  *
283  * @v context           Configuration context
284  * @v setting           Configuration setting
285  * @v buf               Buffer to contain value
286  * @v len               Length of buffer
287  * @ret rc              Return status code
288  */
289 static int show_int ( struct config_context *context,
290                       struct config_setting *setting,
291                       char *buf, size_t len ) {
292         struct dhcp_option *option;
293         long num;
294
295         option = find_dhcp_option ( context->options, setting->tag );
296         if ( ! option )
297                 return -ENODATA;
298         num = dhcp_num_option ( option );
299         snprintf ( buf, len, "%ld", num );
300         return 0;
301 }
302
303 /**
304  * Set value of integer setting
305  *
306  * @v context           Configuration context
307  * @v setting           Configuration setting
308  * @v value             Setting value (as a string)
309  * @v size              Size of integer (in bytes)
310  * @ret rc              Return status code
311  */ 
312 static int set_int ( struct config_context *context,
313                      struct config_setting *setting,
314                      const char *value, unsigned int size ) {
315         struct dhcp_option *option;
316         union {
317                 uint32_t num;
318                 uint8_t bytes[4];
319         } u;
320         char *endp;
321
322         /* Parse number */
323         if ( ! *value )
324                 return -EINVAL;
325         u.num = htonl ( strtoul ( value, &endp, 0 ) );
326         if ( *endp )
327                 return -EINVAL;
328
329         /* Set option */
330         option = set_dhcp_option ( context->options, setting->tag,
331                                    &u.bytes[ sizeof ( u ) - size ], size );
332         if ( ! option )
333                 return -ENOMEM;
334         return 0;
335 }
336
337 /**
338  * Set value of 8-bit integer setting
339  *
340  * @v context           Configuration context
341  * @v setting           Configuration setting
342  * @v value             Setting value (as a string)
343  * @v size              Size of integer (in bytes)
344  * @ret rc              Return status code
345  */ 
346 static int set_int8 ( struct config_context *context,
347                            struct config_setting *setting,
348                            const char *value ) {
349         return set_int ( context, setting, value, 8 );
350 }
351
352 /** An 8-bit integer configuration setting */
353 struct config_setting_type config_setting_type_int8 __config_setting_type = {
354         .name = "int8",
355         .show = show_int,
356         .set = set_int8,
357 };
358
359 /** Some basic setting definitions */
360 struct config_setting ip_config_setting __config_setting = {
361         .name = "ip",
362         .tag = DHCP_EB_YIADDR,
363         .type = &config_setting_type_ipv4,
364 };
365 struct config_setting hostname_config_setting __config_setting = {
366         .name = "hostname",
367         .tag = DHCP_HOST_NAME,
368         .type = &config_setting_type_string,
369 };
370 struct config_setting username_config_setting __config_setting = {
371         .name = "username",
372         .tag = DHCP_EB_USERNAME,
373         .type = &config_setting_type_string,
374 };
375 struct config_setting password_config_setting __config_setting = {
376         .name = "password",
377         .tag = DHCP_EB_PASSWORD,
378         .type = &config_setting_type_string,
379 };
380 struct config_setting root_path_config_setting __config_setting = {
381         .name = "root-path",
382         .tag = DHCP_ROOT_PATH,
383         .type = &config_setting_type_string,
384 };
385 struct config_setting priority_config_setting __config_setting = {
386         .name = "priority",
387         .tag = DHCP_EB_PRIORITY,
388         .type = &config_setting_type_int8,
389 };