Home | History | Annotate | Download | only in ldlinux
      1 #include <linux/list.h>
      2 #include <sys/times.h>
      3 #include <fcntl.h>
      4 #include <stdbool.h>
      5 #include <string.h>
      6 #include <core.h>
      7 #include <fs.h>
      8 #include "cli.h"
      9 #include "console.h"
     10 #include "com32.h"
     11 #include "menu.h"
     12 #include "config.h"
     13 #include "syslinux/adv.h"
     14 #include "syslinux/boot.h"
     15 #include "syslinux/config.h"
     16 
     17 #include <sys/module.h>
     18 
     19 struct file_ext {
     20 	const char *name;
     21 	enum kernel_type type;
     22 };
     23 
     24 static const struct file_ext file_extensions[] = {
     25 	{ ".c32", IMAGE_TYPE_COM32 },
     26 	{ ".img", IMAGE_TYPE_FDIMAGE },
     27 	{ ".bss", IMAGE_TYPE_BSS },
     28 	{ ".bin", IMAGE_TYPE_BOOT },
     29 	{ ".bs", IMAGE_TYPE_BOOT },
     30 	{ ".0", IMAGE_TYPE_PXE },
     31 	{ NULL, 0 },
     32 };
     33 
     34 /*
     35  * Return a pointer to one byte after the last character of the
     36  * command.
     37  */
     38 static inline const char *find_command(const char *str)
     39 {
     40 	const char *p;
     41 
     42 	p = str;
     43 	while (*p && !my_isspace(*p))
     44 		p++;
     45 	return p;
     46 }
     47 
     48 __export uint32_t parse_image_type(const char *kernel)
     49 {
     50 	const struct file_ext *ext;
     51 	const char *p;
     52 	int len;
     53 
     54 	/* Find the end of the command */
     55 	p = find_command(kernel);
     56 	len = p - kernel;
     57 
     58 	for (ext = file_extensions; ext->name; ext++) {
     59 		int elen = strlen(ext->name);
     60 
     61 		if (!strncmp(kernel + len - elen, ext->name, elen))
     62 			return ext->type;
     63 	}
     64 
     65 	/* use IMAGE_TYPE_KERNEL as default */
     66 	return IMAGE_TYPE_KERNEL;
     67 }
     68 
     69 /*
     70  * Returns the kernel name with file extension if one wasn't present.
     71  */
     72 static const char *get_extension(const char *kernel)
     73 {
     74 	const struct file_ext *ext;
     75 	const char *p;
     76 	int len;
     77 
     78 	/* Find the end of the command */
     79 	p = find_command(kernel);
     80 	len = p - kernel;
     81 
     82 	for (ext = file_extensions; ext->name; ext++) {
     83 		char *str;
     84 		int elen = strlen(ext->name);
     85 		FILE *f;
     86 
     87 		str = malloc(len + elen + 1);
     88 
     89 		strncpy(str, kernel, len);
     90 		strncpy(str + len, ext->name, elen);
     91 		str[len + elen] = '\0';
     92 		f = findpath(str);
     93 		free(str);
     94 
     95 		if (f) {
     96 			fclose(f);
     97 			return ext->name;
     98 		}
     99 	}
    100 
    101 	return NULL;
    102 }
    103 
    104 const char *apply_extension(const char *kernel, const char *ext)
    105 {
    106 	const char *p;
    107 	char *k;
    108 	int len = strlen(kernel);
    109 	int elen = strlen(ext);
    110 
    111 	k = malloc(len + elen + 1);
    112 	if (!k)
    113 		return NULL;
    114 
    115 	p = find_command(kernel);
    116 
    117 	len = p - kernel;
    118 
    119 	/* Copy just the kernel name */
    120 	memcpy(k, kernel, len);
    121 
    122 	/* Append the extension */
    123 	if (strncmp(p - elen, ext, elen)) {
    124 		memcpy(k + len, ext, elen);
    125 		len += elen;
    126 	}
    127 
    128 	/* Copy the rest of the command line */
    129 	strcpy(k + len, p);
    130 
    131 	k[len + strlen(p)] = '\0';
    132 
    133 	return k;
    134 }
    135 
    136 /*
    137  * Attempt to load a kernel after deciding what type of image it is.
    138  *
    139  * We only return from this function if something went wrong loading
    140  * the the kernel. If we return the caller should call enter_cmdline()
    141  * so that the user can help us out.
    142  */
    143 __export void load_kernel(const char *command_line)
    144 {
    145 	struct menu_entry *me;
    146 	const char *cmdline;
    147 	const char *kernel;
    148 	uint32_t type;
    149 
    150 	kernel = strdup(command_line);
    151 	if (!kernel)
    152 		goto bad_kernel;
    153 
    154 	/* Virtual kernel? */
    155 	me = find_label(kernel);
    156 	if (me) {
    157 		const char *args;
    158 		char *cmd;
    159 		size_t len = strlen(me->cmdline) + 1;
    160 
    161 		/* Find the end of the command */
    162 		args = find_command(kernel);
    163 		while(*args && my_isspace(*args))
    164 			args++;
    165 
    166 		if (strlen(args))
    167 			len += strlen(args) + 1; /* +1 for space (' ') */
    168 
    169 		cmd = malloc(len);
    170 		if (!cmd)
    171 			goto bad_kernel;
    172 
    173 		if (strlen(args))
    174 			snprintf(cmd, len, "%s %s", me->cmdline, args);
    175 		else
    176 			strncpy(cmd, me->cmdline, len);
    177 
    178 		type = parse_image_type(cmd);
    179 		execute(cmd, type, false);
    180 		/* We shouldn't return */
    181 		goto bad_kernel;
    182 	}
    183 
    184 	if (!allowimplicit)
    185 		goto bad_implicit;
    186 
    187 	/* Insert a null character to ignore any user-specified options */
    188 	if (!allowoptions) {
    189 		char *p = (char *)find_command(kernel);
    190 		*p = '\0';
    191 	}
    192 
    193 	type = parse_image_type(kernel);
    194 	if (type == IMAGE_TYPE_KERNEL) {
    195 		const char *ext;
    196 
    197 		/*
    198 		 * Automatically lookup the extension if one wasn't
    199 		 * supplied by the user.
    200 		 */
    201 		ext = get_extension(kernel);
    202 		if (ext) {
    203 			const char *k;
    204 
    205 			k = apply_extension(kernel, ext);
    206 			if (!k)
    207 				goto bad_kernel;
    208 
    209 			free((void *)kernel);
    210 			kernel = k;
    211 
    212 			type = parse_image_type(kernel);
    213 		}
    214 	}
    215 
    216 	execute(kernel, type, true);
    217 	free((void *)kernel);
    218 
    219 bad_implicit:
    220 bad_kernel:
    221 	/*
    222 	 * If we fail to boot the kernel execute the "onerror" command
    223 	 * line.
    224 	 */
    225 	if (onerrorlen) {
    226 		me = find_label(onerror);
    227 		if (me)
    228 			rsprintf(&cmdline, "%s %s", me->cmdline, default_cmd);
    229 		else
    230 			rsprintf(&cmdline, "%s %s", onerror, default_cmd);
    231 
    232 		type = parse_image_type(cmdline);
    233 		execute(cmdline, type, true);
    234 	}
    235 }
    236 
    237 /*
    238  * If this function returns you must call ldinux_enter_command() to
    239  * preserve the 4.0x behaviour.
    240  */
    241 void ldlinux_auto_boot(void)
    242 {
    243 	if (!defaultlevel) {
    244 		if (strlen(ConfigName))
    245 			printf("No DEFAULT or UI configuration directive found!\n");
    246 		if (noescape)
    247 			kaboom();
    248 	} else
    249 		load_kernel(default_cmd);
    250 }
    251 
    252 static void enter_cmdline(void)
    253 {
    254 	const char *cmdline;
    255 
    256 	/* Enter endless command line prompt, should support "exit" */
    257 	while (1) {
    258 		bool to = false;
    259 
    260 		if (noescape) {
    261 			ldlinux_auto_boot();
    262 			continue;
    263 		}
    264 
    265 		cmdline = edit_cmdline("boot:", 1, NULL, cat_help_file, &to);
    266 		printf("\n");
    267 
    268 		/* return if user only press enter or we timed out */
    269 		if (!cmdline || cmdline[0] == '\0') {
    270 			if (to && ontimeoutlen)
    271 				load_kernel(ontimeout);
    272 			else
    273 				ldlinux_auto_boot();
    274 		} else
    275 			load_kernel(cmdline);
    276 	}
    277 }
    278 
    279 void ldlinux_enter_command(void)
    280 {
    281 	enter_cmdline();
    282 }
    283 
    284 /*
    285  * Undo the work we did in openconsole().
    286  */
    287 static void __destructor close_console(void)
    288 {
    289 	int i;
    290 
    291 	for (i = 0; i <= 2; i++)
    292 		close(i);
    293 }
    294 
    295 void ldlinux_console_init(void)
    296 {
    297 	openconsole(&dev_stdcon_r, &dev_ansiserial_w);
    298 }
    299 
    300 __export int main(int argc __unused, char **argv)
    301 {
    302 	const void *adv;
    303 	const char *cmdline;
    304 	size_t count = 0;
    305 
    306 	ldlinux_console_init();
    307 
    308 	parse_configs(&argv[1]);
    309 
    310 	__syslinux_set_serial_console_info();
    311 
    312 	adv = syslinux_getadv(ADV_BOOTONCE, &count);
    313 	if (adv && count) {
    314 		/*
    315 		 * We apparently have a boot-once set; clear it and
    316 		 * then execute the boot-once.
    317 		 */
    318 		char *src, *dst;
    319 		size_t i;
    320 
    321 		src = (char *)adv;
    322 		cmdline = dst = malloc(count + 1);
    323 		if (!dst) {
    324 			printf("Failed to allocate memory for ADV\n");
    325 			ldlinux_enter_command();
    326 		}
    327 
    328 		for (i = 0; i < count; i++)
    329 			*dst++ = *src++;
    330 		*dst = '\0';	/* Null-terminate */
    331 
    332 		/* Clear the boot-once data from the ADV */
    333 		if (!syslinux_setadv(ADV_BOOTONCE, 0, NULL))
    334 			syslinux_adv_write();
    335 
    336 		load_kernel(cmdline); /* Shouldn't return */
    337 		ldlinux_enter_command();
    338 	}
    339 
    340 	if (!forceprompt && !shift_is_held())
    341 		ldlinux_auto_boot();
    342 
    343 	if (defaultlevel > 1)
    344 		ldlinux_auto_boot();
    345 
    346 	ldlinux_enter_command();
    347 	return 0;
    348 }
    349