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 struct conf_file_stat {
42 struct list_item list;
45 static const char *const syslinux_conf_files[] = {
46 "/boot/syslinux/syslinux.cfg",
47 "/syslinux/syslinux.cfg",
49 "/BOOT/SYSLINUX/SYSLINUX.CFG",
50 "/SYSLINUX/SYSLINUX.CFG",
55 static const char *const syslinux_kernel_unsupported_extensions[] = {
56 ".0", /* eventually support PXE here? */
67 static const char *const syslinux_ignored_names[] = {
102 static const char *const syslinux_unsupported_boot_names[] = {
112 static struct conf_global_option syslinux_global_options[] = {
113 { .name = "default" },
114 { .name = "implicit" },
115 { .name = "append" },
120 static void finish_boot_option(struct syslinux_options *state,
124 * in the normal this function signals a new image block which means
125 * move the current block to the list of processed items
126 * the special case is a label before an image block which we need to
127 * know whether to keep it for further processing or junk it
129 if (state->current_option) {
130 if (state->current_option->image) {
131 list_add(&state->processed_options,
132 &state->current_option->list);
133 state->current_option = NULL;
134 } else if (free_if_unused) {
135 talloc_free(state->current_option);
136 state->current_option = NULL;
141 static bool start_new_option(struct syslinux_options *state)
145 finish_boot_option(state, false);
146 if (!state->current_option)
147 state->current_option = talloc_zero(state, struct syslinux_boot_option);
149 if (state->current_option)
155 static void syslinux_process_pair(struct conf_context *conf, const char *name, char *value)
157 struct syslinux_options *state = conf->parser_info;
158 char *buf, *pos, *path;
161 /* ignore bare values */
165 if (conf_param_in_list(syslinux_ignored_names, name))
168 /* a new boot entry needs to terminate any prior one */
169 if (conf_param_in_list(syslinux_unsupported_boot_names, name)) {
170 finish_boot_option(state, true);
174 if (streq(name, "label")) {
175 finish_boot_option(state, true);
176 state->current_option = talloc_zero(state,
177 struct syslinux_boot_option);
178 if (state->current_option)
179 state->current_option->label = talloc_strdup(state, value);
183 if (streq(name, "linux")) {
185 if (start_new_option(state)) {
186 state->current_option->image = talloc_strdup(state, value);
187 if (!state->current_option->image) {
188 talloc_free(state->current_option);
189 state->current_option = NULL;
196 if (streq(name, "kernel")) {
198 if (start_new_option(state)) {
200 * by spec a linux image can not have any of these
201 * extensions, it can have no extension or anything not
204 pos = strrchr(value, '.');
206 !conf_param_in_list(syslinux_kernel_unsupported_extensions, pos)) {
207 state->current_option->image = talloc_strdup(state, value);
208 if (!state->current_option->image) {
209 talloc_free(state->current_option);
210 state->current_option = NULL;
212 } else /* clean up any possible trailing label */
213 finish_boot_option(state, true);
220 /* APPEND can be global and/or local so need special handling */
221 if (streq(name, "append")) {
222 if (state->current_option) {
223 /* by spec only take last if multiple APPENDs */
224 if (state->current_option->append)
225 talloc_free(state->current_option->append);
226 state->current_option->append = talloc_strdup(state, value);
227 if (!state->current_option->append) {
228 talloc_free(state->current_option);
229 state->current_option = NULL;
232 finish_boot_option(state, true);
233 conf_set_global_option(conf, name, value);
238 /* now the general globals */
239 if (conf_set_global_option(conf, name, value)) {
240 finish_boot_option(state, true);
244 if (streq(name, "initrd")) {
245 if (state->current_option) {
246 state->current_option->initrd = talloc_strdup(state, value);
247 if (!state->current_option->initrd) {
248 talloc_free(state->current_option);
249 state->current_option = NULL;
255 if (streq(name, "include")) {
256 if (state->include_nest_level < INCLUDE_NEST_LIMIT) {
257 state->include_nest_level++;
259 /* if absolute in as-is */
261 path = talloc_strdup(state, value);
262 else /* otherwise relative to the root config file */
263 path = join_paths(state, state->cfg_dir, value);
265 rc = parser_request_file(conf->dc, conf->dc->device, path, &buf, &len);
267 conf_parse_buf(conf, buf, len);
269 device_handler_status_dev_info(conf->dc->handler, conf->dc->device,
270 _("Parsed nested syslinux configuration from %s"), value);
273 device_handler_status_dev_info(conf->dc->handler, conf->dc->device,
274 _("Failed to parse nested syslinux configuration from %s"), value);
279 state->include_nest_level--;
281 device_handler_status_dev_err(conf->dc->handler, conf->dc->device,
282 _("Nested syslinux INCLUDE exceeds limit...ignored"));
287 pb_debug("%s: unknown name: %s\n", __func__, name);
290 static void syslinux_finalize(struct conf_context *conf)
292 struct syslinux_options *state = conf->parser_info;
293 struct syslinux_boot_option *syslinux_opt, *tmp;
294 struct discover_context *dc = conf->dc;
295 struct discover_boot_option *d_opt;
296 bool implicit_image = true;
297 char *args_sigfile_default;
298 const char *global_default;
299 const char *global_append;
300 struct boot_option *opt;
304 /* clean up any lingering boot entries */
305 finish_boot_option(state, true);
307 global_append = conf_get_global_option(conf, "append");
308 global_default = conf_get_global_option(conf, "default");
311 * by spec '0' means disable
312 * note we set the default to '1' (which is by spec) in syslinux_parse
314 if (conf_get_global_option(conf, "implicit"), "0")
315 implicit_image = false;
317 list_for_each_entry(&state->processed_options, syslinux_opt, list) {
318 /* need a valid image */
319 if (!syslinux_opt->image)
322 image = syslinux_opt->image;
323 label = syslinux_opt->label;
325 /* if implicit is disabled we must have a label */
326 if (!label && !implicit_image)
329 d_opt = discover_boot_option_create(dc, dc->device);
337 if (syslinux_opt->append) {
338 /* '-' can signal do not use global APPEND */
339 if (!strcmp(syslinux_opt->append, "-"))
340 opt->boot_args = talloc_strdup(opt, "");
341 else if (global_append)
342 opt->boot_args = talloc_asprintf(opt, "%s %s",
344 syslinux_opt->append);
346 opt->boot_args = talloc_strdup(opt, syslinux_opt->append);
347 } else if (global_append)
348 opt->boot_args = talloc_strdup(opt, global_append);
353 opt->id = talloc_asprintf(opt, "%s#%s", dc->device->device->id, image);
359 opt->name = talloc_strdup(opt, label);
360 if (!strcmp(label, global_default))
361 opt->is_default = true;
363 opt->description = talloc_asprintf(opt, "(%s) %s", label, image);
365 opt->name = talloc_strdup(opt, image);
366 opt->description = talloc_strdup(opt, image);
372 d_opt->boot_image = create_devpath_resource(d_opt, dc->device, image);
374 if(!d_opt->boot_image)
377 if (syslinux_opt->initrd) {
378 d_opt->initrd = create_devpath_resource(d_opt, dc->device,
379 syslinux_opt->initrd);
380 opt->description = talloc_asprintf_append(opt->description,
382 syslinux_opt->initrd);
388 opt->description = talloc_asprintf_append(opt->description,
392 if (!opt->description)
395 args_sigfile_default = talloc_asprintf(d_opt, "%s.cmdline.sig",
398 if (!args_sigfile_default)
401 d_opt->args_sig_file = create_devpath_resource(d_opt, dc->device,
402 args_sigfile_default);
404 if (!d_opt->args_sig_file)
407 talloc_free(args_sigfile_default);
409 conf_strip_str(opt->boot_args);
410 conf_strip_str(opt->description);
412 discover_context_add_boot_option(dc, d_opt);
420 list_for_each_entry_safe(&state->processed_options, syslinux_opt, tmp, list)
421 talloc_free(syslinux_opt);
422 list_init(&state->processed_options);
425 static int syslinux_parse(struct discover_context *dc)
427 struct conf_file_stat *confcmp, *confdat;
428 struct list processed_conf_files;
429 struct syslinux_options *state;
430 const char * const *filename;
431 struct conf_context *conf;
437 /* Support block device boot only at present */
441 conf = talloc_zero(dc, struct conf_context);
447 conf->global_options = syslinux_global_options,
448 conf_init_global_options(conf);
449 conf->get_pair = conf_get_pair_space;
450 conf->process_pair = syslinux_process_pair;
452 conf->parser_info = state = talloc_zero(conf, struct syslinux_options);
453 list_init(&state->processed_options);
455 list_init(&processed_conf_files);
458 * set the global defaults
459 * by spec 'default' defaults to 'linux' and
460 * 'implicit' defaults to '1'
462 conf_set_global_option(conf, "default", "linux");
463 conf_set_global_option(conf, "implicit", "1");
465 for (filename = syslinux_conf_files; *filename; filename++) {
467 * guard against duplicate entries in case-insensitive
468 * filesystems, mainly vfat boot partitions
470 rc = parser_stat_path(dc, dc->device, *filename, &statbuf);
476 list_for_each_entry(&processed_conf_files, confcmp, list) {
477 if (confcmp->stat.st_ino == statbuf.st_ino) {
478 pb_log("conf file %s is a path duplicate of %s..skipping\n",
479 *filename, confcmp->name);
488 rc = parser_request_file(dc, dc->device, *filename, &buf, &len);
492 confdat = talloc_zero(conf, struct conf_file_stat);
493 confdat->stat = statbuf;
494 confdat->name = talloc_strdup(confdat, *filename);
495 list_add(&processed_conf_files, &confdat->list);
498 * save location of root config file for possible
499 * INCLUDE directives later
501 * dirname can overwrite so need local copy to work on
503 cfg_dir = talloc_strdup(conf, *filename);
504 state->cfg_dir = talloc_strdup(state, dirname(cfg_dir));
505 talloc_free(cfg_dir);
507 conf_parse_buf(conf, buf, len);
508 device_handler_status_dev_info(dc->handler, dc->device,
509 _("Parsed syslinux configuration from %s"),
513 syslinux_finalize(conf);
520 static struct parser syslinux_parser = {
522 .parse = syslinux_parse,
523 .resolve_resource = resolve_devpath_resource,
526 register_parser(syslinux_parser);