Add iSCSI initiator IQN as a 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 <strings.h>
23 #include <byteswap.h>
24 #include <errno.h>
25 #include <assert.h>
26 #include <vsprintf.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
38 config_setting_types[0] __table_start ( config_setting_types );
39 static struct config_setting_type
40 config_setting_types_end[0] __table_end ( config_setting_types );
41
42 /** Registered configuration settings */
43 static struct config_setting
44 config_settings[0] __table_start ( config_settings );
45 static struct config_setting
46 config_settings_end[0] __table_end ( 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 rc              Return status code
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 rc              Return status code
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         dhcp_snprintf ( buf, len, option );
194         return 0;
195 }
196
197 /**
198  * Set value of string setting
199  *
200  * @v context           Configuration context
201  * @v setting           Configuration setting
202  * @v value             Setting value (as a string)
203  * @ret rc              Return status code
204  */ 
205 static int set_string ( struct config_context *context,
206                         struct config_setting *setting,
207                         const char *value ) {
208         struct dhcp_option *option;
209
210         option = set_dhcp_option ( context->options, setting->tag,
211                                    value, strlen ( value ) );
212         if ( ! option )
213                 return -ENOSPC;
214         return 0;
215 }
216
217 /** A string configuration setting */
218 struct config_setting_type config_setting_type_string __config_setting_type = {
219         .name = "string",
220         .description = "Text string",
221         .show = show_string,
222         .set = set_string,
223 };
224
225 /**
226  * Show value of IPv4 setting
227  *
228  * @v context           Configuration context
229  * @v setting           Configuration setting
230  * @v buf               Buffer to contain value
231  * @v len               Length of buffer
232  * @ret rc              Return status code
233  */
234 static int show_ipv4 ( struct config_context *context,
235                        struct config_setting *setting,
236                        char *buf, size_t len ) {
237         struct dhcp_option *option;
238         struct in_addr ipv4;
239
240         option = find_dhcp_option ( context->options, setting->tag );
241         if ( ! option )
242                 return -ENODATA;
243         dhcp_ipv4_option ( option, &ipv4 );
244         snprintf ( buf, len, inet_ntoa ( ipv4 ) );
245         return 0;
246 }
247
248 /**
249  * Set value of IPV4 setting
250  *
251  * @v context           Configuration context
252  * @v setting           Configuration setting
253  * @v value             Setting value (as a string)
254  * @ret rc              Return status code
255  */ 
256 static int set_ipv4 ( struct config_context *context,
257                       struct config_setting *setting,
258                       const char *value ) {
259         struct dhcp_option *option;
260         struct in_addr ipv4;
261         
262         if ( inet_aton ( value, &ipv4 ) == 0 )
263                 return -EINVAL;
264         option = set_dhcp_option ( context->options, setting->tag,
265                                    &ipv4, sizeof ( ipv4 ) );
266         if ( ! option )
267                 return -ENOSPC;
268         return 0;
269 }
270
271 /** An IPv4 configuration setting */
272 struct config_setting_type config_setting_type_ipv4 __config_setting_type = {
273         .name = "ipv4",
274         .description = "IPv4 address",
275         .show = show_ipv4,
276         .set = set_ipv4,
277 };
278
279 /**
280  * Show value of integer setting
281  *
282  * @v context           Configuration context
283  * @v setting           Configuration setting
284  * @v buf               Buffer to contain value
285  * @v len               Length of buffer
286  * @ret rc              Return status code
287  */
288 static int show_int ( struct config_context *context,
289                       struct config_setting *setting,
290                       char *buf, size_t len ) {
291         struct dhcp_option *option;
292         long num;
293
294         option = find_dhcp_option ( context->options, setting->tag );
295         if ( ! option )
296                 return -ENODATA;
297         num = dhcp_num_option ( option );
298         snprintf ( buf, len, "%ld", num );
299         return 0;
300 }
301
302 /**
303  * Set value of integer setting
304  *
305  * @v context           Configuration context
306  * @v setting           Configuration setting
307  * @v value             Setting value (as a string)
308  * @v size              Size of integer (in bytes)
309  * @ret rc              Return status code
310  */ 
311 static int set_int ( struct config_context *context,
312                      struct config_setting *setting,
313                      const char *value, unsigned int size ) {
314         struct dhcp_option *option;
315         union {
316                 uint32_t num;
317                 uint8_t bytes[4];
318         } u;
319         char *endp;
320
321         /* Parse number */
322         if ( ! *value )
323                 return -EINVAL;
324         u.num = htonl ( strtoul ( value, &endp, 0 ) );
325         if ( *endp )
326                 return -EINVAL;
327
328         /* Set option */
329         option = set_dhcp_option ( context->options, setting->tag,
330                                    &u.bytes[ sizeof ( u ) - size ], size );
331         if ( ! option )
332                 return -ENOSPC;
333         return 0;
334 }
335
336 /**
337  * Set value of 8-bit integer setting
338  *
339  * @v context           Configuration context
340  * @v setting           Configuration setting
341  * @v value             Setting value (as a string)
342  * @v size              Size of integer (in bytes)
343  * @ret rc              Return status code
344  */ 
345 static int set_int8 ( struct config_context *context,
346                            struct config_setting *setting,
347                            const char *value ) {
348         return set_int ( context, setting, value, 1 );
349 }
350
351 /** An 8-bit integer configuration setting */
352 struct config_setting_type config_setting_type_int8 __config_setting_type = {
353         .name = "int8",
354         .description = "8-bit integer",
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         .description = "IP address of this machine (e.g. 192.168.0.1)",
363         .tag = DHCP_EB_YIADDR,
364         .type = &config_setting_type_ipv4,
365 };
366 struct config_setting hostname_config_setting __config_setting = {
367         .name = "hostname",
368         .description = "Host name of this machine",
369         .tag = DHCP_HOST_NAME,
370         .type = &config_setting_type_string,
371 };
372 struct config_setting username_config_setting __config_setting = {
373         .name = "username",
374         .description = "User name for authentication to servers",
375         .tag = DHCP_EB_USERNAME,
376         .type = &config_setting_type_string,
377 };
378 struct config_setting password_config_setting __config_setting = {
379         .name = "password",
380         .description = "Password for authentication to servers",
381         .tag = DHCP_EB_PASSWORD,
382         .type = &config_setting_type_string,
383 };
384 struct config_setting root_path_config_setting __config_setting = {
385         .name = "root-path",
386         .description = "NFS/iSCSI root path",
387         .tag = DHCP_ROOT_PATH,
388         .type = &config_setting_type_string,
389 };
390 struct config_setting priority_config_setting __config_setting = {
391         .name = "priority",
392         .description = "Priority of these options",
393         .tag = DHCP_EB_PRIORITY,
394         .type = &config_setting_type_int8,
395 };
396 struct config_setting iscsi_initiator_setting __config_setting = {
397         .name = "initiator-iqn",
398         .description = "iSCSI qualified name of this machine",
399         .tag = DHCP_ISCSI_INITIATOR_IQN,
400         .type = &config_setting_type_string,
401 };