| #include <linux/list.h> |
| #include <sys/times.h> |
| #include <fcntl.h> |
| #include <stdbool.h> |
| #include <string.h> |
| #include <core.h> |
| #include <fs.h> |
| #include "cli.h" |
| #include "console.h" |
| #include "com32.h" |
| #include "menu.h" |
| #include "config.h" |
| #include "syslinux/adv.h" |
| #include "syslinux/boot.h" |
| #include "syslinux/config.h" |
| |
| #include <sys/module.h> |
| |
| struct file_ext { |
| const char *name; |
| enum kernel_type type; |
| }; |
| |
| static const struct file_ext file_extensions[] = { |
| { ".c32", IMAGE_TYPE_COM32 }, |
| { ".img", IMAGE_TYPE_FDIMAGE }, |
| { ".bss", IMAGE_TYPE_BSS }, |
| { ".bin", IMAGE_TYPE_BOOT }, |
| { ".bs", IMAGE_TYPE_BOOT }, |
| { ".0", IMAGE_TYPE_PXE }, |
| { NULL, 0 }, |
| }; |
| |
| /* |
| * Return a pointer to one byte after the last character of the |
| * command. |
| */ |
| static inline const char *find_command(const char *str) |
| { |
| const char *p; |
| |
| p = str; |
| while (*p && !my_isspace(*p)) |
| p++; |
| return p; |
| } |
| |
| __export uint32_t parse_image_type(const char *kernel) |
| { |
| const struct file_ext *ext; |
| const char *p; |
| int len; |
| |
| /* Find the end of the command */ |
| p = find_command(kernel); |
| len = p - kernel; |
| |
| for (ext = file_extensions; ext->name; ext++) { |
| int elen = strlen(ext->name); |
| |
| if (!strncmp(kernel + len - elen, ext->name, elen)) |
| return ext->type; |
| } |
| |
| /* use IMAGE_TYPE_KERNEL as default */ |
| return IMAGE_TYPE_KERNEL; |
| } |
| |
| /* |
| * Returns the kernel name with file extension if one wasn't present. |
| */ |
| static const char *get_extension(const char *kernel) |
| { |
| const struct file_ext *ext; |
| const char *p; |
| int len; |
| |
| /* Find the end of the command */ |
| p = find_command(kernel); |
| len = p - kernel; |
| |
| for (ext = file_extensions; ext->name; ext++) { |
| char *str; |
| int elen = strlen(ext->name); |
| FILE *f; |
| |
| str = malloc(len + elen + 1); |
| |
| strncpy(str, kernel, len); |
| strncpy(str + len, ext->name, elen); |
| str[len + elen] = '\0'; |
| f = findpath(str); |
| free(str); |
| |
| if (f) { |
| fclose(f); |
| return ext->name; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| const char *apply_extension(const char *kernel, const char *ext) |
| { |
| const char *p; |
| char *k; |
| int len = strlen(kernel); |
| int elen = strlen(ext); |
| |
| k = malloc(len + elen + 1); |
| if (!k) |
| return NULL; |
| |
| p = find_command(kernel); |
| |
| len = p - kernel; |
| |
| /* Copy just the kernel name */ |
| memcpy(k, kernel, len); |
| |
| /* Append the extension */ |
| if (strncmp(p - elen, ext, elen)) { |
| memcpy(k + len, ext, elen); |
| len += elen; |
| } |
| |
| /* Copy the rest of the command line */ |
| strcpy(k + len, p); |
| |
| k[len + strlen(p)] = '\0'; |
| |
| return k; |
| } |
| |
| /* |
| * Attempt to load a kernel after deciding what type of image it is. |
| * |
| * We only return from this function if something went wrong loading |
| * the the kernel. If we return the caller should call enter_cmdline() |
| * so that the user can help us out. |
| */ |
| __export void load_kernel(const char *command_line) |
| { |
| struct menu_entry *me; |
| const char *cmdline; |
| const char *kernel; |
| uint32_t type; |
| |
| kernel = strdup(command_line); |
| if (!kernel) |
| goto bad_kernel; |
| |
| /* Virtual kernel? */ |
| me = find_label(kernel); |
| if (me) { |
| const char *args; |
| char *cmd; |
| size_t len = strlen(me->cmdline) + 1; |
| |
| /* Find the end of the command */ |
| args = find_command(kernel); |
| while(*args && my_isspace(*args)) |
| args++; |
| |
| if (strlen(args)) |
| len += strlen(args) + 1; /* +1 for space (' ') */ |
| |
| cmd = malloc(len); |
| if (!cmd) |
| goto bad_kernel; |
| |
| if (strlen(args)) |
| snprintf(cmd, len, "%s %s", me->cmdline, args); |
| else |
| strncpy(cmd, me->cmdline, len); |
| |
| type = parse_image_type(cmd); |
| execute(cmd, type, false); |
| /* We shouldn't return */ |
| goto bad_kernel; |
| } |
| |
| if (!allowimplicit) |
| goto bad_implicit; |
| |
| /* Insert a null character to ignore any user-specified options */ |
| if (!allowoptions) { |
| char *p = (char *)find_command(kernel); |
| *p = '\0'; |
| } |
| |
| type = parse_image_type(kernel); |
| if (type == IMAGE_TYPE_KERNEL) { |
| const char *ext; |
| |
| /* |
| * Automatically lookup the extension if one wasn't |
| * supplied by the user. |
| */ |
| ext = get_extension(kernel); |
| if (ext) { |
| const char *k; |
| |
| k = apply_extension(kernel, ext); |
| if (!k) |
| goto bad_kernel; |
| |
| free((void *)kernel); |
| kernel = k; |
| |
| type = parse_image_type(kernel); |
| } |
| } |
| |
| execute(kernel, type, true); |
| free((void *)kernel); |
| |
| bad_implicit: |
| bad_kernel: |
| /* |
| * If we fail to boot the kernel execute the "onerror" command |
| * line. |
| */ |
| if (onerrorlen) { |
| me = find_label(onerror); |
| if (me) |
| rsprintf(&cmdline, "%s %s", me->cmdline, default_cmd); |
| else |
| rsprintf(&cmdline, "%s %s", onerror, default_cmd); |
| |
| type = parse_image_type(cmdline); |
| execute(cmdline, type, true); |
| } |
| } |
| |
| /* |
| * If this function returns you must call ldinux_enter_command() to |
| * preserve the 4.0x behaviour. |
| */ |
| void ldlinux_auto_boot(void) |
| { |
| if (!defaultlevel) { |
| if (strlen(ConfigName)) |
| printf("No DEFAULT or UI configuration directive found!\n"); |
| if (noescape) |
| kaboom(); |
| } else |
| load_kernel(default_cmd); |
| } |
| |
| static void enter_cmdline(void) |
| { |
| const char *cmdline; |
| |
| /* Enter endless command line prompt, should support "exit" */ |
| while (1) { |
| bool to = false; |
| |
| if (noescape) { |
| ldlinux_auto_boot(); |
| continue; |
| } |
| |
| cmdline = edit_cmdline("boot:", 1, NULL, cat_help_file, &to); |
| printf("\n"); |
| |
| /* return if user only press enter or we timed out */ |
| if (!cmdline || cmdline[0] == '\0') { |
| if (to && ontimeoutlen) |
| load_kernel(ontimeout); |
| else |
| ldlinux_auto_boot(); |
| } else |
| load_kernel(cmdline); |
| } |
| } |
| |
| void ldlinux_enter_command(void) |
| { |
| enter_cmdline(); |
| } |
| |
| /* |
| * Undo the work we did in openconsole(). |
| */ |
| static void __destructor close_console(void) |
| { |
| int i; |
| |
| for (i = 0; i <= 2; i++) |
| close(i); |
| } |
| |
| void ldlinux_console_init(void) |
| { |
| openconsole(&dev_stdcon_r, &dev_ansiserial_w); |
| } |
| |
| __export int main(int argc __unused, char **argv) |
| { |
| const void *adv; |
| const char *cmdline; |
| size_t count = 0; |
| |
| ldlinux_console_init(); |
| |
| parse_configs(&argv[1]); |
| |
| __syslinux_set_serial_console_info(); |
| |
| adv = syslinux_getadv(ADV_BOOTONCE, &count); |
| if (adv && count) { |
| /* |
| * We apparently have a boot-once set; clear it and |
| * then execute the boot-once. |
| */ |
| char *src, *dst; |
| size_t i; |
| |
| src = (char *)adv; |
| cmdline = dst = malloc(count + 1); |
| if (!dst) { |
| printf("Failed to allocate memory for ADV\n"); |
| ldlinux_enter_command(); |
| } |
| |
| for (i = 0; i < count; i++) |
| *dst++ = *src++; |
| *dst = '\0'; /* Null-terminate */ |
| |
| /* Clear the boot-once data from the ADV */ |
| if (!syslinux_setadv(ADV_BOOTONCE, 0, NULL)) |
| syslinux_adv_write(); |
| |
| load_kernel(cmdline); /* Shouldn't return */ |
| ldlinux_enter_command(); |
| } |
| |
| if (!forceprompt && !shift_is_held()) |
| ldlinux_auto_boot(); |
| |
| if (defaultlevel > 1) |
| ldlinux_auto_boot(); |
| |
| ldlinux_enter_command(); |
| return 0; |
| } |