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
19struct file_ext {
20	const char *name;
21	enum kernel_type type;
22};
23
24static 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 */
38static 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 */
72static 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
104const 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
219bad_implicit:
220bad_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 */
241void 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
252static 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
279void ldlinux_enter_command(void)
280{
281	enter_cmdline();
282}
283
284/*
285 * Undo the work we did in openconsole().
286 */
287static void __destructor close_console(void)
288{
289	int i;
290
291	for (i = 0; i <= 2; i++)
292		close(i);
293}
294
295void 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