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