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;
39 static const char *const syslinux_conf_files[] = {
40 "/boot/syslinux/syslinux.cfg",
41 "/syslinux/syslinux.cfg",
43 "/BOOT/SYSLINUX/SYSLINUX.CFG",
44 "/SYSLINUX/SYSLINUX.CFG",
49 static const char *const syslinux_kernel_unsupported_extensions[] = {
50 ".0", /* eventually support PXE here? */
61 static const char *const syslinux_ignored_names[] = {
96 static const char *const syslinux_unsupported_boot_names[] = {
106 static struct conf_global_option syslinux_global_options[] = {
107 { .name = "default" },
108 { .name = "implicit" },
109 { .name = "append" },
114 static void finish_boot_option(struct syslinux_options *state,
118 * in the normal this function signals a new image block which means
119 * move the current block to the list of processed items
120 * the special case is a label before an image block which we need to
121 * know whether to keep it for further processing or junk it
123 if (state->current_option) {
124 if (state->current_option->image) {
125 list_add(&state->processed_options,
126 &state->current_option->list);
127 state->current_option = NULL;
128 } else if (free_if_unused) {
129 talloc_free(state->current_option);
130 state->current_option = NULL;
135 static bool start_new_option(struct syslinux_options *state)
139 finish_boot_option(state, false);
140 if (!state->current_option)
141 state->current_option = talloc_zero(state, struct syslinux_boot_option);
143 if (state->current_option)
149 static void syslinux_process_pair(struct conf_context *conf, const char *name, char *value)
151 struct syslinux_options *state = conf->parser_info;
152 char *buf, *pos, *path;
155 /* ignore bare values */
159 if (conf_param_in_list(syslinux_ignored_names, name))
162 /* a new boot entry needs to terminate any prior one */
163 if (conf_param_in_list(syslinux_unsupported_boot_names, name)) {
164 finish_boot_option(state, true);
168 if (streq(name, "label")) {
169 finish_boot_option(state, true);
170 state->current_option = talloc_zero(state,
171 struct syslinux_boot_option);
172 if (state->current_option)
173 state->current_option->label = talloc_strdup(state, value);
177 if (streq(name, "linux")) {
179 if (start_new_option(state)) {
180 state->current_option->image = talloc_strdup(state, value);
181 if (!state->current_option->image) {
182 talloc_free(state->current_option);
183 state->current_option = NULL;
190 if (streq(name, "kernel")) {
192 if (start_new_option(state)) {
194 * by spec a linux image can not have any of these
195 * extensions, it can have no extension or anything not
198 pos = strrchr(value, '.');
200 !conf_param_in_list(syslinux_kernel_unsupported_extensions, pos)) {
201 state->current_option->image = talloc_strdup(state, value);
202 if (!state->current_option->image) {
203 talloc_free(state->current_option);
204 state->current_option = NULL;
206 } else /* clean up any possible trailing label */
207 finish_boot_option(state, true);
214 /* APPEND can be global and/or local so need special handling */
215 if (streq(name, "append")) {
216 if (state->current_option) {
217 /* by spec only take last if multiple APPENDs */
218 if (state->current_option->append)
219 talloc_free(state->current_option->append);
220 state->current_option->append = talloc_strdup(state, value);
221 if (!state->current_option->append) {
222 talloc_free(state->current_option);
223 state->current_option = NULL;
226 finish_boot_option(state, true);
227 conf_set_global_option(conf, name, value);
232 /* now the general globals */
233 if (conf_set_global_option(conf, name, value)) {
234 finish_boot_option(state, true);
238 if (streq(name, "initrd")) {
239 if (state->current_option) {
240 state->current_option->initrd = talloc_strdup(state, value);
241 if (!state->current_option->initrd) {
242 talloc_free(state->current_option);
243 state->current_option = NULL;
249 if (streq(name, "include")) {
250 if (state->include_nest_level < INCLUDE_NEST_LIMIT) {
251 state->include_nest_level++;
253 /* if absolute in as-is */
255 path = talloc_strdup(state, value);
256 else /* otherwise relative to the root config file */
257 path = join_paths(state, state->cfg_dir, value);
259 rc = parser_request_file(conf->dc, conf->dc->device, path, &buf, &len);
261 conf_parse_buf(conf, buf, len);
263 device_handler_status_dev_info(conf->dc->handler, conf->dc->device,
264 _("Parsed nested syslinux configuration from %s"), value);
267 device_handler_status_dev_info(conf->dc->handler, conf->dc->device,
268 _("Failed to parse nested syslinux configuration from %s"), value);
273 state->include_nest_level--;
275 device_handler_status_dev_err(conf->dc->handler, conf->dc->device,
276 _("Nested syslinux INCLUDE exceeds limit...ignored"));
281 pb_debug("%s: unknown name: %s\n", __func__, name);
284 static void syslinux_finalize(struct conf_context *conf)
286 struct syslinux_options *state = conf->parser_info;
287 struct syslinux_boot_option *syslinux_opt, *tmp;
288 struct discover_context *dc = conf->dc;
289 struct discover_boot_option *d_opt;
290 bool implicit_image = true;
291 char *args_sigfile_default;
292 const char *global_default;
293 const char *global_append;
294 struct boot_option *opt;
298 /* clean up any lingering boot entries */
299 finish_boot_option(state, true);
301 global_append = conf_get_global_option(conf, "append");
302 global_default = conf_get_global_option(conf, "default");
305 * by spec '0' means disable
306 * note we set the default to '1' (which is by spec) in syslinux_parse
308 if (conf_get_global_option(conf, "implicit"), "0")
309 implicit_image = false;
311 list_for_each_entry(&state->processed_options, syslinux_opt, list) {
312 /* need a valid image */
313 if (!syslinux_opt->image)
316 image = syslinux_opt->image;
317 label = syslinux_opt->label;
319 /* if implicit is disabled we must have a label */
320 if (!label && !implicit_image)
323 d_opt = discover_boot_option_create(dc, dc->device);
331 if (syslinux_opt->append) {
332 /* '-' can signal do not use global APPEND */
333 if (!strcmp(syslinux_opt->append, "-"))
334 opt->boot_args = talloc_strdup(opt, "");
335 else if (global_append)
336 opt->boot_args = talloc_asprintf(opt, "%s %s",
338 syslinux_opt->append);
340 opt->boot_args = talloc_strdup(opt, syslinux_opt->append);
341 } else if (global_append)
342 opt->boot_args = talloc_strdup(opt, global_append);
347 opt->id = talloc_asprintf(opt, "%s#%s", dc->device->device->id, image);
353 opt->name = talloc_strdup(opt, label);
354 if (!strcmp(label, global_default))
355 opt->is_default = true;
357 opt->description = talloc_asprintf(opt, "(%s) %s", label, image);
359 opt->name = talloc_strdup(opt, image);
360 opt->description = talloc_strdup(opt, image);
366 d_opt->boot_image = create_devpath_resource(d_opt, dc->device, image);
368 if(!d_opt->boot_image)
371 if (syslinux_opt->initrd) {
372 d_opt->initrd = create_devpath_resource(d_opt, dc->device,
373 syslinux_opt->initrd);
374 opt->description = talloc_asprintf_append(opt->description,
376 syslinux_opt->initrd);
382 opt->description = talloc_asprintf_append(opt->description,
386 if (!opt->description)
389 args_sigfile_default = talloc_asprintf(d_opt, "%s.cmdline.sig",
392 if (!args_sigfile_default)
395 d_opt->args_sig_file = create_devpath_resource(d_opt, dc->device,
396 args_sigfile_default);
398 if (!d_opt->args_sig_file)
401 talloc_free(args_sigfile_default);
403 conf_strip_str(opt->boot_args);
404 conf_strip_str(opt->description);
406 discover_context_add_boot_option(dc, d_opt);
414 list_for_each_entry_safe(&state->processed_options, syslinux_opt, tmp, list)
415 talloc_free(syslinux_opt);
416 list_init(&state->processed_options);
419 static int syslinux_parse(struct discover_context *dc)
421 struct syslinux_options *state;
422 const char * const *filename;
423 struct conf_context *conf;
424 struct list *found_list;
426 /* Support block device boot only at present */
430 conf = talloc_zero(dc, struct conf_context);
434 found_list = talloc(conf, struct list);
437 list_init(found_list);
440 conf->global_options = syslinux_global_options,
441 conf_init_global_options(conf);
442 conf->get_pair = conf_get_pair_space;
443 conf->process_pair = syslinux_process_pair;
445 conf->parser_info = state = talloc_zero(conf, struct syslinux_options);
446 list_init(&state->processed_options);
449 * set the global defaults
450 * by spec 'default' defaults to 'linux' and
451 * 'implicit' defaults to '1'
453 conf_set_global_option(conf, "default", "linux");
454 conf_set_global_option(conf, "implicit", "1");
456 for (filename = syslinux_conf_files; *filename; filename++) {
462 * guard against duplicate entries in case-insensitive
463 * filesystems, mainly vfat boot partitions
465 if (!parser_is_unique(dc, dc->device, *filename, found_list))
468 rc = parser_request_file(dc, dc->device, *filename, &buf, &len);
473 * save location of root config file for possible
474 * INCLUDE directives later
476 * dirname can overwrite so need local copy to work on
478 cfg_dir = talloc_strdup(conf, *filename);
479 state->cfg_dir = talloc_strdup(state, dirname(cfg_dir));
480 talloc_free(cfg_dir);
482 conf_parse_buf(conf, buf, len);
483 device_handler_status_dev_info(dc->handler, dc->device,
484 _("Parsed syslinux configuration from %s"),
488 syslinux_finalize(conf);
495 static struct parser syslinux_parser = {
497 .parse = syslinux_parse,
498 .resolve_resource = resolve_devpath_resource,
501 register_parser(syslinux_parser);