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