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  */
find_command(const char * str)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 
parse_image_type(const char * kernel)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  */
get_extension(const char * kernel)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 
apply_extension(const char * kernel,const char * ext)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  */
load_kernel(const char * command_line)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  */
ldlinux_auto_boot(void)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 
enter_cmdline(void)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 
ldlinux_enter_command(void)279 void ldlinux_enter_command(void)
280 {
281 	enter_cmdline();
282 }
283 
284 /*
285  * Undo the work we did in openconsole().
286  */
close_console(void)287 static void __destructor close_console(void)
288 {
289 	int i;
290 
291 	for (i = 0; i <= 2; i++)
292 		close(i);
293 }
294 
ldlinux_console_init(void)295 void ldlinux_console_init(void)
296 {
297 	openconsole(&dev_stdcon_r, &dev_ansiserial_w);
298 }
299 
main(int argc __unused,char ** argv)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