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