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