po: Minor Russian translation updates
[petitboot] / discover / pxe-parser.c
1
2 #if defined(HAVE_CONFIG_H)
3 #include "config.h"
4 #endif
5 #include <stdlib.h>
6 #include <string.h>
7
8 #include <talloc/talloc.h>
9 #include <url/url.h>
10 #include <log/log.h>
11
12 #include "parser.h"
13 #include "parser-conf.h"
14 #include "parser-utils.h"
15 #include "resource.h"
16 #include "paths.h"
17 #include "event.h"
18 #include "user-event.h"
19
20 static const char *pxelinux_prefix = "pxelinux.cfg/";
21
22 struct pxe_parser_info {
23         struct discover_boot_option *opt;
24         const char *default_name;
25 };
26
27 static void pxe_finish(struct conf_context *conf)
28 {
29         struct pxe_parser_info *info = conf->parser_info;
30         if (info->opt)
31                 discover_context_add_boot_option(conf->dc, info->opt);
32 }
33
34 /* We need a slightly modified version of pb_url_join, to allow for the
35  * pxelinux "::filename" syntax for absolute URLs
36  */
37 static struct pb_url *pxe_url_join(void *ctx, const struct pb_url *url,
38                 const char *s)
39 {
40         struct pb_url *new_url;
41         int len;
42
43         len = strlen(s);
44
45         if (len > 2 && s[0] == ':' && s[1] == ':') {
46                 char *tmp;
47
48                 if (s[2] == '/') {
49                         /* ::/path -> /path */
50                         tmp = talloc_strdup(ctx, s+2);
51                 } else {
52                         /* ::path -> /path */
53                         tmp = talloc_strdup(ctx, s+1);
54                         tmp[0] = '/';
55                 }
56
57                 new_url = pb_url_join(ctx, url, tmp);
58
59                 talloc_free(tmp);
60
61         } else {
62                 const char *tmp;
63                 /* strip leading slashes */
64                 for (tmp = s; *tmp == '/'; tmp++)
65                         ;
66                 new_url = pb_url_join(ctx, url, tmp);
67         }
68
69         return new_url;
70 }
71
72 static void pxe_append_string(struct discover_boot_option *opt,
73                 const char *str)
74 {
75         if (opt->option->boot_args)
76                 opt->option->boot_args = talloc_asprintf_append(
77                                 opt->option->boot_args, " %s", str);
78         else
79                 opt->option->boot_args = talloc_strdup(opt->option, str);
80 }
81
82 static void pxe_process_sysappend(struct discover_context *ctx,
83                 struct discover_boot_option *opt,
84                 unsigned long val)
85 {
86         struct event *event = ctx->event;
87         char *str = NULL;
88
89         if (!event)
90                 return;
91
92         if (val & 0x2) {
93                 const char *mac = event_get_param(event, "mac");
94                 if (mac) {
95                         str = talloc_asprintf(ctx, "BOOTIF=%s", mac);
96                         pxe_append_string(opt, str);
97                         talloc_free(str);
98                 }
99                 val &= ~0x2;
100         }
101
102         if (val)
103                 pb_log("pxe: unsupported features requested in "
104                                 "ipappend/sysappend: 0x%04lx", val);
105
106 }
107
108 static void pxe_process_pair(struct conf_context *ctx,
109                 const char *name, char *value)
110 {
111         struct pxe_parser_info *parser_info = ctx->parser_info;
112         struct discover_boot_option *opt = parser_info->opt;
113         struct pb_url *url;
114
115         /* quirk in the syslinux config format: initrd can be separated
116          * by an '=' */
117         if (!name && !strncasecmp(value, "initrd=", strlen("initrd="))) {
118                 name = "initrd";
119                 value += strlen("initrd=");
120         }
121
122         if (!name)
123                 return;
124
125         if (streq(name, "DEFAULT")) {
126                 parser_info->default_name = talloc_strdup(parser_info, value);
127                 return;
128         }
129
130         if (streq(name, "LABEL")) {
131                 if (opt)
132                         pxe_finish(ctx);
133
134                 opt = discover_boot_option_create(ctx->dc, ctx->dc->device);
135
136                 opt->option->name = talloc_strdup(opt, value);
137                 opt->option->id = talloc_asprintf(opt, "%s@%p",
138                                 ctx->dc->device->device->id, opt);
139
140                 opt->option->is_default = parser_info->default_name &&
141                                         streq(parser_info->default_name, value);
142
143                 parser_info->opt = opt;
144                 return;
145         }
146
147         /* all other parameters need an option */
148         if (!opt)
149                 return;
150
151         if (streq(name, "KERNEL")) {
152                 url = pxe_url_join(ctx->dc, ctx->dc->conf_url, value);
153                 opt->boot_image = create_url_resource(opt, url);
154
155         } else if (streq(name, "INITRD")) {
156                 url = pxe_url_join(ctx->dc, ctx->dc->conf_url, value);
157                 opt->initrd = create_url_resource(opt, url);
158
159         } else if (streq(name, "APPEND")) {
160                 char *str, *end;
161
162                 pxe_append_string(opt, value);
163
164                 str = strcasestr(value, "INITRD=");
165                 if (str) {
166                         str += strlen("INITRD=");
167                         end = strchrnul(str, ' ');
168                         *end = '\0';
169
170                         url = pxe_url_join(ctx->dc, ctx->dc->conf_url, str);
171                         opt->initrd = create_url_resource(opt, url);
172                 }
173         } else if (streq(name, "SYSAPPEND") || streq(name, "IPAPPEND")) {
174                 unsigned long type;
175                 char *end;
176
177                 type = strtoul(value, &end, 10);
178                 if (end != value && !(*end))
179                         pxe_process_sysappend(ctx->dc, opt, type);
180         }
181
182 }
183
184 static int pxe_parse(struct discover_context *dc)
185 {
186         struct pb_url *pxe_base_url, *url;
187         struct pxe_parser_info *parser_info;
188         char **pxe_conf_files, **filename;
189         struct conf_context *conf;
190         bool complete_url;
191         int len, rc;
192         char *buf;
193
194         /* Expects dhcp event parameters to support network boot */
195         if (!dc->event)
196                 return -1;
197
198         conf = talloc_zero(dc, struct conf_context);
199
200         if (!conf)
201                 goto out;
202
203         conf->dc = dc;
204         conf->get_pair = conf_get_pair_space;
205         conf->process_pair = pxe_process_pair;
206         conf->finish = pxe_finish;
207
208         parser_info = talloc_zero(conf, struct pxe_parser_info);
209         conf->parser_info = parser_info;
210
211         dc->conf_url = user_event_parse_conf_url(dc, dc->event, &complete_url);
212         if (!dc->conf_url)
213                 goto out_conf;
214
215         if (complete_url) {
216                 /* we have a complete URL; use this and we're done. */
217                 rc = parser_request_url(dc, dc->conf_url, &buf, &len);
218                 if (rc)
219                         goto out_conf;
220         } else {
221                 pxe_conf_files = user_event_parse_conf_filenames(dc, dc->event);
222                 if (!pxe_conf_files)
223                         goto out_conf;
224
225                 rc = -1;
226
227                 pxe_base_url = pb_url_join(dc, dc->conf_url, pxelinux_prefix);
228                 if (!pxe_base_url)
229                         goto out_pxe_conf;
230
231                 for (filename = pxe_conf_files; *filename; filename++) {
232                         url = pb_url_join(dc, pxe_base_url, *filename);
233                         if (!url)
234                                 continue;
235
236                         rc = parser_request_url(dc, url, &buf, &len);
237                         if (!rc) /* found one, just break */
238                                 break;
239
240                         talloc_free(url);
241                 }
242
243                 talloc_free(pxe_base_url);
244
245                 /* No configuration file found on the boot server */
246                 if (rc)
247                         goto out_pxe_conf;
248
249                 talloc_free(pxe_conf_files);
250         }
251
252         /* Call the config file parser with the data read from the file */
253         conf_parse_buf(conf, buf, len);
254
255         talloc_free(buf);
256         talloc_free(conf);
257
258         return 0;
259
260 out_pxe_conf:
261         talloc_free(pxe_conf_files);
262 out_conf:
263         talloc_free(conf);
264 out:
265         return -1;
266 }
267
268 static struct parser pxe_parser = {
269         .name                   = "pxe",
270         .parse                  = pxe_parse,
271 };
272
273 register_parser(pxe_parser);