1 #if defined(HAVE_CONFIG_H)
12 #include "list/list.h"
13 #include "file/file.h"
14 #include "talloc/talloc.h"
15 #include "types/types.h"
16 #include "parser-conf.h"
17 #include "parser-utils.h"
21 struct syslinux_boot_option {
26 struct list_item list;
29 /* by spec 16 is allowed */
30 #define INCLUDE_NEST_LIMIT 16
32 struct syslinux_options {
33 struct list processed_options;
34 struct syslinux_boot_option *current_option;
35 int include_nest_level;
40 static const char *const syslinux_conf_files[] = {
41 "/boot/syslinux/syslinux.cfg",
42 "/syslinux/syslinux.cfg",
44 "/BOOT/SYSLINUX/SYSLINUX.CFG",
45 "/SYSLINUX/SYSLINUX.CFG",
50 static const char *const syslinux_kernel_unsupported_extensions[] = {
51 ".0", /* eventually support PXE here? */
62 static const char *const syslinux_ignored_names[] = {
97 static const char *const syslinux_unsupported_boot_names[] = {
107 static struct conf_global_option syslinux_global_options[] = {
108 { .name = "default" },
109 { .name = "implicit" },
110 { .name = "append" },
115 static void finish_boot_option(struct syslinux_options *state,
119 * in the normal this function signals a new image block which means
120 * move the current block to the list of processed items
121 * the special case is a label before an image block which we need to
122 * know whether to keep it for further processing or junk it
124 if (state->current_option) {
125 if (state->current_option->image) {
126 list_add(&state->processed_options,
127 &state->current_option->list);
128 state->current_option = NULL;
129 } else if (free_if_unused) {
130 talloc_free(state->current_option);
131 state->current_option = NULL;
136 static bool start_new_option(struct syslinux_options *state)
140 finish_boot_option(state, false);
141 if (!state->current_option)
142 state->current_option = talloc_zero(state, struct syslinux_boot_option);
144 if (state->current_option)
150 static void syslinux_process_pair(struct conf_context *conf, const char *name, char *value)
152 struct syslinux_options *state = conf->parser_info;
153 char *buf, *pos, *path;
156 /* ignore bare values */
160 if (conf_param_in_list(syslinux_ignored_names, name))
163 /* a new boot entry needs to terminate any prior one */
164 if (conf_param_in_list(syslinux_unsupported_boot_names, name)) {
165 finish_boot_option(state, true);
169 if (streq(name, "label")) {
170 finish_boot_option(state, true);
171 state->current_option = talloc_zero(state,
172 struct syslinux_boot_option);
173 if (state->current_option)
174 state->current_option->label = talloc_strdup(state, value);
178 if (streq(name, "linux")) {
180 if (start_new_option(state)) {
181 state->current_option->image = talloc_strdup(state, value);
182 if (!state->current_option->image) {
183 talloc_free(state->current_option);
184 state->current_option = NULL;
191 if (streq(name, "kernel")) {
193 if (start_new_option(state)) {
195 * by spec a linux image can not have any of these
196 * extensions, it can have no extension or anything not
199 pos = strrchr(value, '.');
201 !conf_param_in_list(syslinux_kernel_unsupported_extensions, pos)) {
202 state->current_option->image = talloc_strdup(state, value);
203 if (!state->current_option->image) {
204 talloc_free(state->current_option);
205 state->current_option = NULL;
207 } else /* clean up any possible trailing label */
208 finish_boot_option(state, true);
215 /* APPEND can be global and/or local so need special handling */
216 if (streq(name, "append")) {
217 if (state->current_option) {
218 /* by spec only take last if multiple APPENDs */
219 if (state->current_option->append)
220 talloc_free(state->current_option->append);
221 state->current_option->append = talloc_strdup(state, value);
222 if (!state->current_option->append) {
223 talloc_free(state->current_option);
224 state->current_option = NULL;
227 finish_boot_option(state, true);
228 conf_set_global_option(conf, name, value);
233 /* now the general globals */
234 if (conf_set_global_option(conf, name, value)) {
235 finish_boot_option(state, true);
239 if (streq(name, "initrd")) {
240 if (state->current_option) {
241 state->current_option->initrd = talloc_strdup(state, value);
242 if (!state->current_option->initrd) {
243 talloc_free(state->current_option);
244 state->current_option = NULL;
250 if (streq(name, "include")) {
251 if (state->include_nest_level < INCLUDE_NEST_LIMIT) {
252 state->include_nest_level++;
254 /* if absolute in as-is */
256 path = talloc_strdup(state, value);
257 else /* otherwise relative to the root config file */
258 path = join_paths(state, state->cfg_dir, value);
260 rc = parser_request_file(conf->dc, conf->dc->device, path, &buf, &len);
262 conf_parse_buf(conf, buf, len);
264 device_handler_status_dev_info(conf->dc->handler, conf->dc->device,
265 _("Parsed nested syslinux configuration from %s"), value);
268 device_handler_status_dev_info(conf->dc->handler, conf->dc->device,
269 _("Failed to parse nested syslinux configuration from %s"), value);
274 state->include_nest_level--;
276 device_handler_status_dev_err(conf->dc->handler, conf->dc->device,
277 _("Nested syslinux INCLUDE exceeds limit...ignored"));
282 pb_debug("%s: unknown name: %s\n", __func__, name);
285 static void syslinux_finalize(struct conf_context *conf)
287 struct syslinux_options *state = conf->parser_info;
288 struct syslinux_boot_option *syslinux_opt;
289 struct discover_context *dc = conf->dc;
290 struct discover_boot_option *d_opt;
291 bool implicit_image = true;
292 char *args_sigfile_default;
293 const char *global_default;
294 const char *global_append;
295 struct boot_option *opt;
299 /* clean up any lingering boot entries */
300 finish_boot_option(state, true);
302 global_append = conf_get_global_option(conf, "append");
303 global_default = conf_get_global_option(conf, "default");
306 * by spec '0' means disable
307 * note we set the default to '1' (which is by spec) in syslinux_parse
309 if (conf_get_global_option(conf, "implicit"), "0")
310 implicit_image = false;
312 list_for_each_entry(&state->processed_options, syslinux_opt, list) {
313 /* need a valid image */
314 if (!syslinux_opt->image)
317 image = syslinux_opt->image;
318 label = syslinux_opt->label;
320 /* if implicit is disabled we must have a label */
321 if (!label && !implicit_image)
324 d_opt = discover_boot_option_create(dc, dc->device);
332 if (syslinux_opt->append) {
333 /* '-' can signal do not use global APPEND */
334 if (!strcmp(syslinux_opt->append, "-"))
335 opt->boot_args = talloc_strdup(opt, "");
337 opt->boot_args = talloc_asprintf(opt, "%s %s",
339 syslinux_opt->append);
341 opt->boot_args = talloc_strdup(opt, global_append);
346 opt->id = talloc_asprintf(opt, "%s#%s", dc->device->device->id, image);
352 opt->name = talloc_strdup(opt, label);
353 if (!strcmp(label, global_default))
354 opt->is_default = true;
356 opt->description = talloc_asprintf(opt, "(%s) %s", label, image);
358 opt->name = talloc_strdup(opt, image);
359 opt->description = talloc_strdup(opt, image);
365 d_opt->boot_image = create_devpath_resource(d_opt, dc->device, image);
367 if(!d_opt->boot_image)
370 if (syslinux_opt->initrd) {
371 d_opt->initrd = create_devpath_resource(d_opt, dc->device,
372 syslinux_opt->initrd);
373 opt->description = talloc_asprintf_append(opt->description,
375 syslinux_opt->initrd);
381 opt->description = talloc_asprintf_append(opt->description,
385 if (!opt->description)
388 args_sigfile_default = talloc_asprintf(d_opt, "%s.cmdline.sig",
391 if (!args_sigfile_default)
394 d_opt->args_sig_file = create_devpath_resource(d_opt, dc->device,
395 args_sigfile_default);
397 if (!d_opt->args_sig_file)
400 talloc_free(args_sigfile_default);
402 conf_strip_str(opt->boot_args);
403 conf_strip_str(opt->description);
405 discover_context_add_boot_option(dc, d_opt);
412 static int syslinux_parse(struct discover_context *dc)
414 struct syslinux_options *state;
415 const char * const *filename;
416 struct conf_context *conf;
421 /* Support block device boot only at present */
425 conf = talloc_zero(dc, struct conf_context);
431 conf->global_options = syslinux_global_options,
432 conf_init_global_options(conf);
433 conf->get_pair = conf_get_pair_space;
434 conf->process_pair = syslinux_process_pair;
436 conf->parser_info = state = talloc_zero(conf, struct syslinux_options);
437 list_init(&state->processed_options);
440 * set the global defaults
441 * by spec 'default' defaults to 'linux' and
442 * 'implicit' defaults to '1', we also just set
443 * and empty string in 'append' to make it easier
446 conf_set_global_option(conf, "default", "linux");
447 conf_set_global_option(conf, "implicit", "1");
448 conf_set_global_option(conf, "append", "");
450 for (filename = syslinux_conf_files; *filename; filename++) {
451 rc = parser_request_file(dc, dc->device, *filename, &buf, &len);
456 * save location of root config file for possible
457 * INCLUDE directives later
459 * dirname can overwrite so need local copy to work on
461 cfg_dir = talloc_strdup(conf, *filename);
462 state->cfg_dir = talloc_strdup(state, dirname(cfg_dir));
463 talloc_free(cfg_dir);
465 conf_parse_buf(conf, buf, len);
466 device_handler_status_dev_info(dc->handler, dc->device,
467 _("Parsed syslinux configuration from %s"),
471 syslinux_finalize(conf);
478 static struct parser syslinux_parser = {
480 .parse = syslinux_parse,
481 .resolve_resource = resolve_devpath_resource,
484 register_parser(syslinux_parser);