#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;
}