--- trunk/grubby/grubby.c 2012/07/02 13:22:30 1868 +++ trunk/grubby/grubby.c 2013/10/21 13:56:22 2250 @@ -36,6 +36,8 @@ #include #include +#include "log.h" + #ifndef DEBUG #define DEBUG 0 #endif @@ -56,6 +58,10 @@ #define NOOP_OPCODE 0x90 #define JMP_SHORT_OPCODE 0xeb +int isEfi = 0; + +char *saved_command_line = NULL; + /* comments get lumped in with indention */ struct lineElement { char * item; @@ -82,7 +88,9 @@ LT_MENUENTRY = 1 << 17, LT_ENTRY_END = 1 << 18, LT_SET_VARIABLE = 1 << 19, - LT_UNKNOWN = 1 << 20, + LT_KERNEL_EFI = 1 << 20, + LT_INITRD_EFI = 1 << 21, + LT_UNKNOWN = 1 << 22, }; struct singleLine { @@ -134,6 +142,7 @@ findConfigFunc findConfig; writeLineFunc writeLine; struct keywordTypes * keywords; + int caseInsensitive; int defaultIsIndex; int defaultIsVariable; int defaultSupportSaved; @@ -205,7 +214,9 @@ { "default", LT_DEFAULT, ' ' }, { "fallback", LT_FALLBACK, ' ' }, { "linux", LT_KERNEL, ' ' }, + { "linuxefi", LT_KERNEL_EFI, ' ' }, { "initrd", LT_INITRD, ' ', ' ' }, + { "initrdefi", LT_INITRD_EFI, ' ', ' ' }, { "module", LT_MBMODULE, ' ' }, { "kernel", LT_HYPER, ' ' }, { NULL, 0, 0 }, @@ -219,11 +230,19 @@ }; static int i = -1; static const char *grub_cfg = "/boot/grub/grub.cfg"; + int rc = -1; if (i == -1) { for (i = 0; configFiles[i] != NULL; i++) { dbgPrintf("Checking \"%s\": ", configFiles[i]); - if (!access(configFiles[i], R_OK)) { + if ((rc = access(configFiles[i], R_OK))) { + if (errno == EACCES) { + printf("Unable to access bootloader configuration file " + "\"%s\": %m\n", configFiles[i]); + exit(1); + } + continue; + } else { dbgPrintf("found\n"); return configFiles[i]; } @@ -271,6 +290,14 @@ return 0; } +static int iskernel(enum lineType_e type) { + return (type == LT_KERNEL || type == LT_KERNEL_EFI); +} + +static int isinitrd(enum lineType_e type) { + return (type == LT_INITRD || type == LT_INITRD_EFI); +} + char *grub2ExtractTitle(struct singleLine * line) { char * current; char * current_indent; @@ -482,6 +509,7 @@ struct configFileInfo extlinuxConfigType = { .defaultConfig = "/boot/extlinux/extlinux.conf", .keywords = extlinuxKeywords, + .caseInsensitive = 1, .entryStart = LT_TITLE, .needsBootPrefix = 1, .maxTitleLength = 255, @@ -570,6 +598,21 @@ return buf; } +static enum lineType_e preferredLineType(enum lineType_e type, + struct configFileInfo *cfi) { + if (isEfi && cfi == &grub2ConfigType) { + switch (type) { + case LT_KERNEL: + return LT_KERNEL_EFI; + case LT_INITRD: + return LT_INITRD_EFI; + default: + return type; + } + } + return type; +} + static struct keywordTypes * getKeywordByType(enum lineType_e type, struct configFileInfo * cfi) { for (struct keywordTypes *kw = cfi->keywords; kw->key; kw++) { @@ -603,8 +646,13 @@ static enum lineType_e getTypeByKeyword(char * keyword, struct configFileInfo * cfi) { for (struct keywordTypes *kw = cfi->keywords; kw->key; kw++) { - if (!strcmp(keyword, kw->key)) - return kw->type; + if (cfi->caseInsensitive) { + if (!strcasecmp(keyword, kw->key)) + return kw->type; + } else { + if (!strcmp(keyword, kw->key)) + return kw->type; + } } return LT_UNKNOWN; } @@ -918,7 +966,10 @@ int len; char * buf; - if (!strcmp(inName, "-")) { + if (inName == NULL) { + printf("Could not find bootloader configuration\n"); + exit(1); + } else if (!strcmp(inName, "-")) { in = 0; } else { if ((in = open(inName, O_RDONLY)) < 0) { @@ -994,7 +1045,7 @@ cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT; defaultLine = line; - } else if (line->type == LT_KERNEL) { + } else if (iskernel(line->type)) { /* if by some freak chance this is multiboot and the "module" * lines came earlier in the template, make sure to use LT_HYPER * instead of LT_KERNEL now @@ -1011,7 +1062,7 @@ for (struct singleLine *l = entry->lines; l; l = l->next) { if (l->type == LT_HYPER) break; - else if (l->type == LT_KERNEL) { + else if (iskernel(l->type)) { l->type = LT_HYPER; break; } @@ -1311,8 +1362,7 @@ /* most likely the symlink is relative, so change our directory to the dir of the symlink */ char *dir = strdupa(outName); - rc = chdir(dirname(dir)); - free(dir); + rc = chdir(dirname(dir)); do { buf = alloca(len + 1); rc = readlink(basename(outName), buf, len); @@ -1499,43 +1549,67 @@ return NULL; } -void printEntry(struct singleEntry * entry) { +void printEntry(struct singleEntry * entry, FILE *f) { int i; struct singleLine * line; for (line = entry->lines; line; line = line->next) { - fprintf(stderr, "DBG: %s", line->indent); + log_message(f, "DBG: %s", line->indent); for (i = 0; i < line->numElements; i++) { /* Need to handle this, because we strip the quotes from * menuentry when read it. */ if (line->type == LT_MENUENTRY && i == 1) { if(!isquote(*line->elements[i].item)) - fprintf(stderr, "\'%s\'", line->elements[i].item); + log_message(f, "\'%s\'", line->elements[i].item); else - fprintf(stderr, "%s", line->elements[i].item); - fprintf(stderr, "%s", line->elements[i].indent); + log_message(f, "%s", line->elements[i].item); + log_message(f, "%s", line->elements[i].indent); continue; } - fprintf(stderr, "%s%s", + log_message(f, "%s%s", line->elements[i].item, line->elements[i].indent); } - fprintf(stderr, "\n"); + log_message(f, "\n"); } } -void notSuitablePrintf(struct singleEntry * entry, const char *fmt, ...) +void notSuitablePrintf(struct singleEntry * entry, int okay, const char *fmt, ...) { - va_list argp; + static int once; + va_list argp, argq; + + va_start(argp, fmt); + + va_copy(argq, argp); + if (!once) { + log_time(NULL); + log_message(NULL, "command line: %s\n", saved_command_line); + } + log_message(NULL, "DBG: Image entry %s: ", okay ? "succeeded" : "failed"); + log_vmessage(NULL, fmt, argq); + + printEntry(entry, NULL); + va_end(argq); - if (!debug) + if (!debug) { + once = 1; + va_end(argp); return; + } - va_start(argp, fmt); + if (okay) { + va_end(argp); + return; + } + + if (!once) + log_message(stderr, "DBG: command line: %s\n", saved_command_line); + once = 1; fprintf(stderr, "DBG: Image entry failed: "); vfprintf(stderr, fmt, argp); - printEntry(entry); + printEntry(entry, stderr); va_end(argp); } @@ -1562,22 +1636,25 @@ char * rootdev; if (skipRemoved && entry->skip) { - notSuitablePrintf(entry, "marked to skip\n"); + notSuitablePrintf(entry, 0, "marked to skip\n"); return 0; } - line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines); + line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI, entry->lines); if (!line) { - notSuitablePrintf(entry, "no line found\n"); + notSuitablePrintf(entry, 0, "no line found\n"); return 0; } if (line->numElements < 2) { - notSuitablePrintf(entry, "line has only %d elements\n", + notSuitablePrintf(entry, 0, "line has only %d elements\n", line->numElements); return 0; } - if (flags & GRUBBY_BADIMAGE_OKAY) return 1; + if (flags & GRUBBY_BADIMAGE_OKAY) { + notSuitablePrintf(entry, 1, "\n"); + return 1; + } fullName = alloca(strlen(bootPrefix) + strlen(line->elements[1].item) + 1); @@ -1588,7 +1665,7 @@ sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/", line->elements[1].item + rootspec_offset); if (access(fullName, R_OK)) { - notSuitablePrintf(entry, "access to %s failed\n", fullName); + notSuitablePrintf(entry, 0, "access to %s failed\n", fullName); return 0; } for (i = 2; i < line->numElements; i++) @@ -1609,7 +1686,7 @@ /* failed to find one */ if (!line) { - notSuitablePrintf(entry, "no line found\n"); + notSuitablePrintf(entry, 0, "no line found\n"); return 0; } @@ -1618,7 +1695,7 @@ if (i < line->numElements) dev = line->elements[i].item + 5; else { - notSuitablePrintf(entry, "no root= entry found\n"); + notSuitablePrintf(entry, 0, "no root= entry found\n"); /* it failed too... can't find root= */ return 0; } @@ -1627,32 +1704,33 @@ dev = getpathbyspec(dev); if (!getpathbyspec(dev)) { - notSuitablePrintf(entry, "can't find blkid entry for %s\n", dev); + notSuitablePrintf(entry, 0, "can't find blkid entry for %s\n", dev); return 0; } else dev = getpathbyspec(dev); rootdev = findDiskForRoot(); if (!rootdev) { - notSuitablePrintf(entry, "can't find root device\n"); + notSuitablePrintf(entry, 0, "can't find root device\n"); return 0; } if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) { - notSuitablePrintf(entry, "uuid missing: rootdev %s, dev %s\n", + notSuitablePrintf(entry, 0, "uuid missing: rootdev %s, dev %s\n", getuuidbydev(rootdev), getuuidbydev(dev)); free(rootdev); return 0; } if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) { - notSuitablePrintf(entry, "uuid mismatch: rootdev %s, dev %s\n", + notSuitablePrintf(entry, 0, "uuid mismatch: rootdev %s, dev %s\n", getuuidbydev(rootdev), getuuidbydev(dev)); free(rootdev); return 0; } free(rootdev); + notSuitablePrintf(entry, 1, "\n"); return 1; } @@ -1696,7 +1774,7 @@ entry = findEntryByIndex(config, indexVars[i]); if (!entry) return NULL; - line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines); + line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI, entry->lines); if (!line) return NULL; if (index) *index = indexVars[i]; @@ -1745,10 +1823,14 @@ /* check all the lines matching checkType */ for (line = entry->lines; line; line = line->next) { - line = getLineByType(entry->multiboot && checkType == LT_KERNEL ? - LT_KERNEL|LT_MBMODULE|LT_HYPER : - checkType, line); - if (!line) break; /* not found in this entry */ + enum lineType_e ct = checkType; + if (entry->multiboot && checkType == LT_KERNEL) + ct = LT_KERNEL|LT_KERNEL_EFI|LT_MBMODULE|LT_HYPER; + else if (checkType & LT_KERNEL) + ct = checkType | LT_KERNEL_EFI; + line = getLineByType(ct, line); + if (!line) + break; /* not found in this entry */ if (line && line->type != LT_MENUENTRY && line->numElements >= 2) { @@ -1767,7 +1849,7 @@ * non-Linux boot entries (could find netbsd etc, though, which is * unfortunate) */ - if (line && getLineByType(LT_KERNEL|LT_HYPER, entry->lines)) + if (line && getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI, entry->lines)) break; /* found 'im! */ } @@ -1956,7 +2038,7 @@ printf("index=%d\n", index); - line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines); + line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI, entry->lines); if (!line) { printf("non linux entry\n"); return; @@ -2021,7 +2103,7 @@ printf("root=%s\n", s); } - line = getLineByType(LT_INITRD, entry->lines); + line = getLineByType(LT_INITRD|LT_INITRD_EFI, entry->lines); if (line && line->numElements >= 2) { if (!strncmp(prefix, line->elements[1].item, strlen(prefix))) @@ -2422,6 +2504,13 @@ { struct singleLine * newLine = lineDup(tmplLine); + if (isEfi && cfi == &grub2ConfigType) { + enum lineType_e old = newLine->type; + newLine->type = preferredLineType(newLine->type, cfi); + if (old != newLine->type) + newLine->elements[0].item = getKeyByType(newLine->type, cfi); + } + if (val) { /* override the inherited value with our own. * This is a little weak because it only applies to elements[1] @@ -2431,7 +2520,7 @@ insertElement(newLine, val, 1, cfi); /* but try to keep the rootspec from the template... sigh */ - if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD)) { + if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD|LT_KERNEL_EFI|LT_INITRD_EFI)) { char * rootspec = getRootSpecifier(tmplLine->elements[1].item); if (rootspec != NULL) { free(newLine->elements[1].item); @@ -2468,7 +2557,6 @@ /* NB: This function shouldn't allocate items on the heap, rather on the * stack since it calls addLineTmpl which will make copies. */ - if (type == LT_TITLE && cfi->titleBracketed) { /* we're doing a bracketed title (zipl) */ tmpl.type = type; @@ -2802,7 +2890,7 @@ firstElement = 2; } else { - line = getLineByType(LT_KERNEL|LT_MBMODULE, entry->lines); + line = getLineByType(LT_KERNEL|LT_MBMODULE|LT_KERNEL_EFI, entry->lines); if (!line) { /* no LT_KERNEL or LT_MBMODULE in this entry? */ continue; @@ -2967,10 +3055,10 @@ if (!image) return 0; for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) { - kernelLine = getLineByType(LT_KERNEL, entry->lines); + kernelLine = getLineByType(LT_KERNEL|LT_KERNEL_EFI, entry->lines); if (!kernelLine) continue; - line = getLineByType(LT_INITRD, entry->lines); + line = getLineByType(LT_INITRD|LT_INITRD_EFI, entry->lines); if (line) removeLine(entry, line); if (prefix) { @@ -2981,7 +3069,8 @@ endLine = getLineByType(LT_ENTRY_END, entry->lines); if (endLine) removeLine(entry, endLine); - line = addLine(entry, cfg->cfi, LT_INITRD, kernelLine->indent, initrd); + line = addLine(entry, cfg->cfi, preferredLineType(LT_INITRD, cfg->cfi), + kernelLine->indent, initrd); if (!line) return 1; if (endLine) { @@ -3394,8 +3483,7 @@ while (*chptr && isspace(*chptr)) chptr++; if (*chptr == '#') continue; - if (tmplLine->type == LT_KERNEL && - tmplLine->numElements >= 2) { + if (iskernel(tmplLine->type) && tmplLine->numElements >= 2) { if (!template->multiboot && (needs & NEED_MB)) { /* it's not a multiboot template and this is the kernel * line. Try to be intelligent about inserting the @@ -3472,30 +3560,32 @@ /* template is multi but new is not, * insert the kernel in the first module slot */ - tmplLine->type = LT_KERNEL; + tmplLine->type = preferredLineType(LT_KERNEL, config->cfi); free(tmplLine->elements[0].item); tmplLine->elements[0].item = - strdup(getKeywordByType(LT_KERNEL, config->cfi)->key); + strdup(getKeywordByType(tmplLine->type, + config->cfi)->key); newLine = addLineTmpl(new, tmplLine, newLine, - newKernelPath + strlen(prefix), config->cfi); + newKernelPath + strlen(prefix), + config->cfi); needs &= ~NEED_KERNEL; } else if (needs & NEED_INITRD) { char *initrdVal; /* template is multi but new is not, * insert the initrd in the second module slot */ - tmplLine->type = LT_INITRD; + tmplLine->type = preferredLineType(LT_INITRD, config->cfi); free(tmplLine->elements[0].item); tmplLine->elements[0].item = - strdup(getKeywordByType(LT_INITRD, config->cfi)->key); + strdup(getKeywordByType(tmplLine->type, + config->cfi)->key); initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount); newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi); free(initrdVal); needs &= ~NEED_INITRD; } - } else if (tmplLine->type == LT_INITRD && - tmplLine->numElements >= 2) { + } else if (isinitrd(tmplLine->type) && tmplLine->numElements >= 2) { if (needs & NEED_INITRD && new->multiboot && !template->multiboot && config->cfi->mbInitRdIsModule) { @@ -3549,7 +3639,8 @@ static const char *prefix = "'Loading "; if (tmplLine->numElements > 1 && strstr(tmplLine->elements[1].item, prefix) && - masterLine->next && masterLine->next->type == LT_KERNEL) { + masterLine->next && + iskernel(masterLine->next->type)) { char *newTitle = malloc(strlen(prefix) + strlen(newKernelTitle) + 2); @@ -3576,10 +3667,12 @@ */ switch (config->cfi->entryStart) { case LT_KERNEL: + case LT_KERNEL_EFI: if (new->multiboot && config->cfi->mbHyperFirst) { /* fall through to LT_HYPER */ } else { - newLine = addLine(new, config->cfi, LT_KERNEL, + newLine = addLine(new, config->cfi, + preferredLineType(LT_KERNEL, config->cfi), config->primaryIndent, newKernelPath + strlen(prefix)); needs &= ~NEED_KERNEL; @@ -3655,8 +3748,9 @@ if (needs & NEED_KERNEL) { newLine = addLine(new, config->cfi, (new->multiboot && getKeywordByType(LT_MBMODULE, - config->cfi)) ? - LT_MBMODULE : LT_KERNEL, + config->cfi)) + ? LT_MBMODULE + : preferredLineType(LT_KERNEL, config->cfi), config->secondaryIndent, newKernelPath + strlen(prefix)); needs &= ~NEED_KERNEL; @@ -3672,8 +3766,9 @@ initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount); newLine = addLine(new, config->cfi, (new->multiboot && getKeywordByType(LT_MBMODULE, - config->cfi)) ? - LT_MBMODULE : LT_INITRD, + config->cfi)) + ? LT_MBMODULE + : preferredLineType(LT_INITRD, config->cfi), config->secondaryIndent, initrdVal); free(initrdVal); @@ -3705,7 +3800,7 @@ memset(array, '\0', sizeof (array)); size = backtrace(array, 40); - fprintf(stderr, "grubby recieved SIGSEGV! Backtrace (%ld):\n", + fprintf(stderr, "grubby received SIGSEGV! Backtrace (%ld):\n", (unsigned long)size); backtrace_symbols_fd(array, size, STDERR_FILENO); exit(1); @@ -3789,6 +3884,8 @@ _("display the title of the default kernel") }, { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0, _("configure elilo bootloader") }, + { "efi", 0, POPT_ARG_NONE, &isEfi, 0, + _("force grub2 stanzas to use efi") }, { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0, _("configure extlinux bootloader (from syslinux)") }, { "grub", 0, POPT_ARG_NONE, &configureGrub, 0, @@ -3801,7 +3898,7 @@ { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0, _("initrd image for the new kernel"), _("initrd-path") }, { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i', - _("auxilliary initrd image for things other than the new kernel"), _("initrd-path") }, + _("auxiliary initrd image for things other than the new kernel"), _("initrd-path") }, { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0, _("configure lilo bootloader") }, { "make-default", 0, 0, &makeDefault, 0, @@ -3845,6 +3942,20 @@ signal(SIGSEGV, traceback); + int i = 0; + for (int j = 1; j < argc; j++) + i += strlen(argv[j]) + 1; + saved_command_line = malloc(i); + if (!saved_command_line) { + fprintf(stderr, "grubby: %m\n"); + exit(1); + } + saved_command_line[0] = '\0'; + for (int j = 1; j < argc; j++) { + strcat(saved_command_line, argv[j]); + strncat(saved_command_line, j == argc -1 ? "" : " ", 1); + } + optCon = poptGetContext("grubby", argc, argv, options, 0); poptReadDefaultConfig(optCon, 1); @@ -4088,6 +4199,11 @@ return 0; } + if (grubConfig == NULL) { + printf("Could not find bootloader configuration file.\n"); + exit(1); + } + config = readConfig(grubConfig, cfi); if (!config) return 1; @@ -4101,7 +4217,7 @@ if (!entry) return 0; if (!suitableImage(entry, bootPrefix, 0, flags)) return 0; - line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines); + line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI, entry->lines); if (!line) return 0; rootspec = getRootSpecifier(line->elements[1].item); @@ -4138,6 +4254,7 @@ } else if (displayDefaultIndex) { if (config->defaultImage == -1) return 0; printf("%i\n", config->defaultImage); + return 0; } else if (kernelInfo) return displayInfo(config, kernelInfo, bootPrefix);