349 lines
6.5 KiB
C
349 lines
6.5 KiB
C
#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;
|
|
}
|