46 |
#define dbgPrintf(format, args...) |
#define dbgPrintf(format, args...) |
47 |
#endif |
#endif |
48 |
|
|
49 |
|
int debug = 0; /* Currently just for template debugging */ |
50 |
|
|
51 |
#define _(A) (A) |
#define _(A) (A) |
52 |
|
|
53 |
#define MAX_EXTRA_INITRDS 16 /* code segment checked by --bootloader-probe */ |
#define MAX_EXTRA_INITRDS 16 /* code segment checked by --bootloader-probe */ |
54 |
#define CODE_SEG_SIZE 128 /* code segment checked by --bootloader-probe */ |
#define CODE_SEG_SIZE 128 /* code segment checked by --bootloader-probe */ |
55 |
|
|
56 |
|
#define NOOP_OPCODE 0x90 |
57 |
|
#define JMP_SHORT_OPCODE 0xeb |
58 |
|
|
59 |
/* comments get lumped in with indention */ |
/* comments get lumped in with indention */ |
60 |
struct lineElement { |
struct lineElement { |
61 |
char * item; |
char * item; |
876 |
cfg->secondaryIndent = strdup(line->indent); |
cfg->secondaryIndent = strdup(line->indent); |
877 |
} |
} |
878 |
|
|
879 |
if (isEntryStart(line, cfi)) { |
if (isEntryStart(line, cfi) || (cfg->entries && !sawEntry)) { |
880 |
sawEntry = 1; |
sawEntry = 1; |
881 |
if (!entry) { |
if (!entry) { |
882 |
cfg->entries = malloc(sizeof(*entry)); |
cfg->entries = malloc(sizeof(*entry)); |
1340 |
return NULL; |
return NULL; |
1341 |
} |
} |
1342 |
|
|
1343 |
|
void printEntry(struct singleEntry * entry) { |
1344 |
|
int i; |
1345 |
|
struct singleLine * line; |
1346 |
|
|
1347 |
|
for (line = entry->lines; line; line = line->next) { |
1348 |
|
fprintf(stderr, "DBG: %s", line->indent); |
1349 |
|
for (i = 0; i < line->numElements; i++) { |
1350 |
|
fprintf(stderr, "%s%s", |
1351 |
|
line->elements[i].item, line->elements[i].indent); |
1352 |
|
} |
1353 |
|
fprintf(stderr, "\n"); |
1354 |
|
} |
1355 |
|
} |
1356 |
|
|
1357 |
|
void notSuitablePrintf(struct singleEntry * entry, const char *fmt, ...) |
1358 |
|
{ |
1359 |
|
va_list argp; |
1360 |
|
|
1361 |
|
if (!debug) |
1362 |
|
return; |
1363 |
|
|
1364 |
|
va_start(argp, fmt); |
1365 |
|
fprintf(stderr, "DBG: Image entry failed: "); |
1366 |
|
vfprintf(stderr, fmt, argp); |
1367 |
|
printEntry(entry); |
1368 |
|
va_end(argp); |
1369 |
|
} |
1370 |
|
|
1371 |
|
#define beginswith(s, c) ((s) && (s)[0] == (c)) |
1372 |
|
|
1373 |
|
static int endswith(const char *s, char c) |
1374 |
|
{ |
1375 |
|
int slen; |
1376 |
|
|
1377 |
|
if (!s && !s[0]) |
1378 |
|
return 0; |
1379 |
|
slen = strlen(s) - 1; |
1380 |
|
|
1381 |
|
return s[slen] == c; |
1382 |
|
} |
1383 |
|
|
1384 |
int suitableImage(struct singleEntry * entry, const char * bootPrefix, |
int suitableImage(struct singleEntry * entry, const char * bootPrefix, |
1385 |
int skipRemoved, int flags) { |
int skipRemoved, int flags) { |
1386 |
struct singleLine * line; |
struct singleLine * line; |
1390 |
char * rootspec; |
char * rootspec; |
1391 |
char * rootdev; |
char * rootdev; |
1392 |
|
|
1393 |
if (skipRemoved && entry->skip) return 0; |
if (skipRemoved && entry->skip) { |
1394 |
|
notSuitablePrintf(entry, "marked to skip\n"); |
1395 |
|
return 0; |
1396 |
|
} |
1397 |
|
|
1398 |
line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines); |
line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines); |
1399 |
if (!line || line->numElements < 2) return 0; |
if (!line) { |
1400 |
|
notSuitablePrintf(entry, "no line found\n"); |
1401 |
|
return 0; |
1402 |
|
} |
1403 |
|
if (line->numElements < 2) { |
1404 |
|
notSuitablePrintf(entry, "line has only %d elements\n", |
1405 |
|
line->numElements); |
1406 |
|
return 0; |
1407 |
|
} |
1408 |
|
|
1409 |
if (flags & GRUBBY_BADIMAGE_OKAY) return 1; |
if (flags & GRUBBY_BADIMAGE_OKAY) return 1; |
1410 |
|
|
1411 |
fullName = alloca(strlen(bootPrefix) + |
fullName = alloca(strlen(bootPrefix) + |
1412 |
strlen(line->elements[1].item) + 1); |
strlen(line->elements[1].item) + 1); |
1413 |
rootspec = getRootSpecifier(line->elements[1].item); |
rootspec = getRootSpecifier(line->elements[1].item); |
1414 |
sprintf(fullName, "%s%s", bootPrefix, |
int rootspec_offset = rootspec ? strlen(rootspec) : 0; |
1415 |
line->elements[1].item + (rootspec ? strlen(rootspec) : 0)); |
int hasslash = endswith(bootPrefix, '/') || |
1416 |
if (access(fullName, R_OK)) return 0; |
beginswith(line->elements[1].item + rootspec_offset, '/'); |
1417 |
|
sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/", |
1418 |
|
line->elements[1].item + rootspec_offset); |
1419 |
|
if (access(fullName, R_OK)) { |
1420 |
|
notSuitablePrintf(entry, "access to %s failed\n", fullName); |
1421 |
|
return 0; |
1422 |
|
} |
1423 |
for (i = 2; i < line->numElements; i++) |
for (i = 2; i < line->numElements; i++) |
1424 |
if (!strncasecmp(line->elements[i].item, "root=", 5)) break; |
if (!strncasecmp(line->elements[i].item, "root=", 5)) break; |
1425 |
if (i < line->numElements) { |
if (i < line->numElements) { |
1437 |
line = getLineByType(LT_KERNELARGS|LT_MBMODULE, entry->lines); |
line = getLineByType(LT_KERNELARGS|LT_MBMODULE, entry->lines); |
1438 |
|
|
1439 |
/* failed to find one */ |
/* failed to find one */ |
1440 |
if (!line) return 0; |
if (!line) { |
1441 |
|
notSuitablePrintf(entry, "no line found\n"); |
1442 |
|
return 0; |
1443 |
|
} |
1444 |
|
|
1445 |
for (i = 1; i < line->numElements; i++) |
for (i = 1; i < line->numElements; i++) |
1446 |
if (!strncasecmp(line->elements[i].item, "root=", 5)) break; |
if (!strncasecmp(line->elements[i].item, "root=", 5)) break; |
1447 |
if (i < line->numElements) |
if (i < line->numElements) |
1448 |
dev = line->elements[i].item + 5; |
dev = line->elements[i].item + 5; |
1449 |
else { |
else { |
1450 |
|
notSuitablePrintf(entry, "no root= entry found\n"); |
1451 |
/* it failed too... can't find root= */ |
/* it failed too... can't find root= */ |
1452 |
return 0; |
return 0; |
1453 |
} |
} |
1455 |
} |
} |
1456 |
|
|
1457 |
dev = getpathbyspec(dev); |
dev = getpathbyspec(dev); |
1458 |
if (!dev) |
if (!getpathbyspec(dev)) { |
1459 |
|
notSuitablePrintf(entry, "can't find blkid entry for %s\n", dev); |
1460 |
return 0; |
return 0; |
1461 |
|
} else |
1462 |
|
dev = getpathbyspec(dev); |
1463 |
|
|
1464 |
rootdev = findDiskForRoot(); |
rootdev = findDiskForRoot(); |
1465 |
if (!rootdev) |
if (!rootdev) { |
1466 |
|
notSuitablePrintf(entry, "can't find root device\n"); |
1467 |
return 0; |
return 0; |
1468 |
|
} |
1469 |
|
|
1470 |
if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) { |
if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) { |
1471 |
|
notSuitablePrintf(entry, "uuid missing: rootdev %s, dev %s\n", |
1472 |
|
getuuidbydev(rootdev), getuuidbydev(dev)); |
1473 |
free(rootdev); |
free(rootdev); |
1474 |
return 0; |
return 0; |
1475 |
} |
} |
1476 |
|
|
1477 |
if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) { |
if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) { |
1478 |
|
notSuitablePrintf(entry, "uuid mismatch: rootdev %s, dev %s\n", |
1479 |
|
getuuidbydev(rootdev), getuuidbydev(dev)); |
1480 |
free(rootdev); |
free(rootdev); |
1481 |
return 0; |
return 0; |
1482 |
} |
} |
2543 |
if (memcmp(boot, bootSect, 3)) |
if (memcmp(boot, bootSect, 3)) |
2544 |
return 0; |
return 0; |
2545 |
|
|
2546 |
if (boot[1] == 0xeb) { |
if (boot[1] == JMP_SHORT_OPCODE) { |
2547 |
offset = boot[2] + 2; |
offset = boot[2] + 2; |
2548 |
} else if (boot[1] == 0xe8 || boot[1] == 0xe9) { |
} else if (boot[1] == 0xe8 || boot[1] == 0xe9) { |
2549 |
offset = (boot[3] << 8) + boot[2] + 2; |
offset = (boot[3] << 8) + boot[2] + 2; |
2550 |
} else if (boot[0] == 0xeb) { |
} else if (boot[0] == JMP_SHORT_OPCODE) { |
2551 |
offset = boot[1] + 2; |
offset = boot[1] + 2; |
2552 |
|
/* |
2553 |
|
* it looks like grub, when copying stage1 into the mbr, patches stage1 |
2554 |
|
* right after the JMP location, replacing other instructions such as |
2555 |
|
* JMPs for NOOPs. So, relax the check a little bit by skipping those |
2556 |
|
* different bytes. |
2557 |
|
*/ |
2558 |
|
if ((bootSect[offset + 1] == NOOP_OPCODE) |
2559 |
|
&& (bootSect[offset + 2] == NOOP_OPCODE)) { |
2560 |
|
offset = offset + 3; |
2561 |
|
} |
2562 |
} else if (boot[0] == 0xe8 || boot[0] == 0xe9) { |
} else if (boot[0] == 0xe8 || boot[0] == 0xe9) { |
2563 |
offset = (boot[2] << 8) + boot[1] + 2; |
offset = (boot[2] << 8) + boot[1] + 2; |
2564 |
} else { |
} else { |
3033 |
} |
} |
3034 |
} else if (tmplLine->type == LT_ECHO) { |
} else if (tmplLine->type == LT_ECHO) { |
3035 |
requote(tmplLine, config->cfi); |
requote(tmplLine, config->cfi); |
3036 |
|
static const char *prefix = "'Loading "; |
3037 |
if (tmplLine->numElements > 1 && |
if (tmplLine->numElements > 1 && |
3038 |
strstr(tmplLine->elements[1].item, "'Loading Linux ")) { |
strstr(tmplLine->elements[1].item, prefix) && |
3039 |
char *prefix = "'Loading "; |
masterLine->next && masterLine->next->type == LT_KERNEL) { |
3040 |
char *newTitle = malloc(strlen(prefix) + |
char *newTitle = malloc(strlen(prefix) + |
3041 |
strlen(newKernelTitle) + 2); |
strlen(newKernelTitle) + 2); |
3042 |
|
|
3233 |
struct singleEntry * template = NULL; |
struct singleEntry * template = NULL; |
3234 |
int copyDefault = 0, makeDefault = 0; |
int copyDefault = 0, makeDefault = 0; |
3235 |
int displayDefault = 0; |
int displayDefault = 0; |
3236 |
|
int displayDefaultIndex = 0; |
3237 |
|
int displayDefaultTitle = 0; |
3238 |
struct poptOption options[] = { |
struct poptOption options[] = { |
3239 |
{ "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0, |
{ "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0, |
3240 |
_("add an entry for the specified kernel"), _("kernel-path") }, |
_("add an entry for the specified kernel"), _("kernel-path") }, |
3265 |
"the kernel referenced by the default image does not exist, " |
"the kernel referenced by the default image does not exist, " |
3266 |
"the first linux entry whose kernel does exist is used as the " |
"the first linux entry whose kernel does exist is used as the " |
3267 |
"template"), NULL }, |
"template"), NULL }, |
3268 |
|
{ "debug", 0, 0, &debug, 0, |
3269 |
|
_("print debugging information for failures") }, |
3270 |
{ "default-kernel", 0, 0, &displayDefault, 0, |
{ "default-kernel", 0, 0, &displayDefault, 0, |
3271 |
_("display the path of the default kernel") }, |
_("display the path of the default kernel") }, |
3272 |
|
{ "default-index", 0, 0, &displayDefaultIndex, 0, |
3273 |
|
_("display the index of the default kernel") }, |
3274 |
|
{ "default-title", 0, 0, &displayDefaultTitle, 0, |
3275 |
|
_("display the title of the default kernel") }, |
3276 |
{ "elilo", 0, POPT_ARG_NONE, &configureELilo, 0, |
{ "elilo", 0, POPT_ARG_NONE, &configureELilo, 0, |
3277 |
_("configure elilo bootloader") }, |
_("configure elilo bootloader") }, |
3278 |
{ "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0, |
{ "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0, |
3416 |
|
|
3417 |
if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion || |
if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion || |
3418 |
newKernelPath || removeKernelPath || makeDefault || |
newKernelPath || removeKernelPath || makeDefault || |
3419 |
defaultKernel)) { |
defaultKernel || displayDefaultIndex || displayDefaultTitle)) { |
3420 |
fprintf(stderr, _("grubby: --bootloader-probe may not be used with " |
fprintf(stderr, _("grubby: --bootloader-probe may not be used with " |
3421 |
"specified option")); |
"specified option")); |
3422 |
return 1; |
return 1; |
3459 |
defaultKernel = NULL; |
defaultKernel = NULL; |
3460 |
} |
} |
3461 |
|
|
3462 |
if (!strcmp(grubConfig, "-") && !outputFile) { |
if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) { |
3463 |
fprintf(stderr, _("grubby: output file must be specified if stdin " |
fprintf(stderr, _("grubby: output file must be specified if stdin " |
3464 |
"is used\n")); |
"is used\n")); |
3465 |
return 1; |
return 1; |
3467 |
|
|
3468 |
if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel |
if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel |
3469 |
&& !kernelInfo && !bootloaderProbe && !updateKernelPath |
&& !kernelInfo && !bootloaderProbe && !updateKernelPath |
3470 |
&& !removeMBKernel) { |
&& !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle) { |
3471 |
fprintf(stderr, _("grubby: no action specified\n")); |
fprintf(stderr, _("grubby: no action specified\n")); |
3472 |
return 1; |
return 1; |
3473 |
} |
} |
3562 |
((rootspec != NULL) ? strlen(rootspec) : 0)); |
((rootspec != NULL) ? strlen(rootspec) : 0)); |
3563 |
|
|
3564 |
return 0; |
return 0; |
3565 |
|
|
3566 |
|
} else if (displayDefaultTitle) { |
3567 |
|
struct singleLine * line; |
3568 |
|
struct singleEntry * entry; |
3569 |
|
|
3570 |
|
if (config->defaultImage == -1) return 0; |
3571 |
|
entry = findEntryByIndex(config, config->defaultImage); |
3572 |
|
if (!entry) return 0; |
3573 |
|
|
3574 |
|
if (!configureGrub2) { |
3575 |
|
line = getLineByType(LT_TITLE, entry->lines); |
3576 |
|
if (!line) return 0; |
3577 |
|
printf("%s\n", line->elements[1].item); |
3578 |
|
|
3579 |
|
} else { |
3580 |
|
int i; |
3581 |
|
size_t len; |
3582 |
|
char * start; |
3583 |
|
char * tmp; |
3584 |
|
|
3585 |
|
dbgPrintf("This is GRUB2, default title is embeded in menuentry\n"); |
3586 |
|
line = getLineByType(LT_MENUENTRY, entry->lines); |
3587 |
|
if (!line) return 0; |
3588 |
|
|
3589 |
|
for (i = 0; i < line->numElements; i++) { |
3590 |
|
|
3591 |
|
if (!strcmp(line->elements[i].item, "menuentry")) |
3592 |
|
continue; |
3593 |
|
|
3594 |
|
if (*line->elements[i].item == '\'') |
3595 |
|
start = line->elements[i].item + 1; |
3596 |
|
else |
3597 |
|
start = line->elements[i].item; |
3598 |
|
|
3599 |
|
len = strlen(start); |
3600 |
|
if (*(start + len - 1) == '\'') { |
3601 |
|
tmp = strdup(start); |
3602 |
|
*(tmp + len - 1) = '\0'; |
3603 |
|
printf("%s", tmp); |
3604 |
|
free(tmp); |
3605 |
|
break; |
3606 |
|
} else { |
3607 |
|
printf("%s ", start); |
3608 |
|
} |
3609 |
|
} |
3610 |
|
printf("\n"); |
3611 |
|
} |
3612 |
|
return 0; |
3613 |
|
|
3614 |
|
} else if (displayDefaultIndex) { |
3615 |
|
if (config->defaultImage == -1) return 0; |
3616 |
|
printf("%i\n", config->defaultImage); |
3617 |
|
|
3618 |
} else if (kernelInfo) |
} else if (kernelInfo) |
3619 |
return displayInfo(config, kernelInfo, bootPrefix); |
return displayInfo(config, kernelInfo, bootPrefix); |
3620 |
|
|