--- trunk/mkinitrd-magellan/grubby/grubby.c 2009/10/28 13:29:38 926 +++ trunk/grubby/grubby.c 2012/02/17 23:46:24 1696 @@ -36,7 +36,9 @@ #include #include +#ifndef DEBUG #define DEBUG 0 +#endif #if DEBUG #define dbgPrintf(format, args...) fprintf(stderr, format , ## args) @@ -56,22 +58,26 @@ }; enum lineType_e { - LT_WHITESPACE = 1 << 0, - LT_TITLE = 1 << 1, - LT_KERNEL = 1 << 2, - LT_INITRD = 1 << 3, - LT_HYPER = 1 << 4, - LT_DEFAULT = 1 << 5, - LT_MBMODULE = 1 << 6, - LT_ROOT = 1 << 7, - LT_FALLBACK = 1 << 8, - LT_KERNELARGS = 1 << 9, - LT_BOOT = 1 << 10, - LT_BOOTROOT = 1 << 11, - LT_LBA = 1 << 12, - LT_OTHER = 1 << 13, - LT_GENERIC = 1 << 14, - LT_UNKNOWN = 1 << 15, + LT_WHITESPACE = 1 << 0, + LT_TITLE = 1 << 1, + LT_KERNEL = 1 << 2, + LT_INITRD = 1 << 3, + LT_HYPER = 1 << 4, + LT_DEFAULT = 1 << 5, + LT_MBMODULE = 1 << 6, + LT_ROOT = 1 << 7, + LT_FALLBACK = 1 << 8, + LT_KERNELARGS = 1 << 9, + LT_BOOT = 1 << 10, + LT_BOOTROOT = 1 << 11, + LT_LBA = 1 << 12, + LT_OTHER = 1 << 13, + LT_GENERIC = 1 << 14, + LT_ECHO = 1 << 16, + LT_MENUENTRY = 1 << 17, + LT_ENTRY_END = 1 << 18, + LT_SET_VARIABLE = 1 << 19, + LT_UNKNOWN = 1 << 20, }; struct singleLine { @@ -99,6 +105,7 @@ #define NEED_TITLE (1 << 2) #define NEED_ARGS (1 << 3) #define NEED_MB (1 << 4) +#define NEED_END (1 << 5) #define MAIN_DEFAULT (1 << 0) #define DEFAULT_SAVED -2 @@ -110,16 +117,27 @@ char separatorChar; }; +struct configFileInfo; + +typedef const char *(*findConfigFunc)(struct configFileInfo *); +typedef const int (*writeLineFunc)(struct configFileInfo *, + struct singleLine *line); + struct configFileInfo { char * defaultConfig; + findConfigFunc findConfig; + writeLineFunc writeLine; struct keywordTypes * keywords; int defaultIsIndex; + int defaultIsVariable; int defaultSupportSaved; - enum lineType_e entrySeparator; + enum lineType_e entryStart; + enum lineType_e entryEnd; int needsBootPrefix; int argsInQuotes; int maxTitleLength; int titleBracketed; + int titlePosition; int mbHyperFirst; int mbInitRdIsModule; int mbConcatArgs; @@ -139,19 +157,66 @@ }; struct configFileInfo grubConfigType = { - "/boot/grub/grub.conf", /* defaultConfig */ - grubKeywords, /* keywords */ - 1, /* defaultIsIndex */ - 1, /* defaultSupportSaved */ - LT_TITLE, /* entrySeparator */ - 1, /* needsBootPrefix */ - 0, /* argsInQuotes */ - 0, /* maxTitleLength */ - 0, /* titleBracketed */ - 1, /* mbHyperFirst */ - 1, /* mbInitRdIsModule */ - 0, /* mbConcatArgs */ - 1, /* mbAllowExtraInitRds */ + .defaultConfig = "/boot/grub/grub.conf", + .keywords = grubKeywords, + .defaultIsIndex = 1, + .defaultSupportSaved = 1, + .entryStart = LT_TITLE, + .needsBootPrefix = 1, + .mbHyperFirst = 1, + .mbInitRdIsModule = 1, + .mbAllowExtraInitRds = 1, +}; + +struct keywordTypes grub2Keywords[] = { + { "menuentry", LT_MENUENTRY, ' ' }, + { "}", LT_ENTRY_END, ' ' }, + { "echo", LT_ECHO, ' ' }, + { "set", LT_SET_VARIABLE,' ', '=' }, + { "root", LT_BOOTROOT, ' ' }, + { "default", LT_DEFAULT, ' ' }, + { "fallback", LT_FALLBACK, ' ' }, + { "linux", LT_KERNEL, ' ' }, + { "initrd", LT_INITRD, ' ', ' ' }, + { "module", LT_MBMODULE, ' ' }, + { "kernel", LT_HYPER, ' ' }, + { NULL, 0, 0 }, +}; + +const char *grub2FindConfig(struct configFileInfo *cfi) { + static const char *configFiles[] = { + "/boot/grub/grub-efi.cfg", + "/boot/grub/grub.cfg", + NULL + }; + static int i = -1; + + if (i == -1) { + for (i = 0; configFiles[i] != NULL; i++) { + dbgPrintf("Checking \"%s\": ", configFiles[i]); + if (!access(configFiles[i], R_OK)) { + dbgPrintf("found\n"); + return configFiles[i]; + } + dbgPrintf("not found\n"); + } + } + return configFiles[i]; +} + +struct configFileInfo grub2ConfigType = { + .findConfig = grub2FindConfig, + .keywords = grub2Keywords, + .defaultIsIndex = 1, + .defaultSupportSaved = 0, + .defaultIsVariable = 1, + .entryStart = LT_MENUENTRY, + .entryEnd = LT_ENTRY_END, + .titlePosition = 1, + .needsBootPrefix = 1, + .mbHyperFirst = 1, + .mbInitRdIsModule = 1, + .mbAllowExtraInitRds = 1, }; struct keywordTypes yabootKeywords[] = { @@ -249,99 +314,56 @@ }; int useextlinuxmenu; struct configFileInfo eliloConfigType = { - "/boot/efi/EFI/redhat/elilo.conf", /* defaultConfig */ - eliloKeywords, /* keywords */ - 0, /* defaultIsIndex */ - 0, /* defaultSupportSaved */ - LT_KERNEL, /* entrySeparator */ - 1, /* needsBootPrefix */ - 1, /* argsInQuotes */ - 0, /* maxTitleLength */ - 0, /* titleBracketed */ - 0, /* mbHyperFirst */ - 0, /* mbInitRdIsModule */ - 1, /* mbConcatArgs */ - 0, /* mbAllowExtraInitRds */ + .defaultConfig = "/boot/efi/EFI/redhat/elilo.conf", + .keywords = eliloKeywords, + .entryStart = LT_KERNEL, + .needsBootPrefix = 1, + .argsInQuotes = 1, + .mbConcatArgs = 1, }; struct configFileInfo liloConfigType = { - "/etc/lilo.conf", /* defaultConfig */ - liloKeywords, /* keywords */ - 0, /* defaultIsIndex */ - 0, /* defaultSupportSaved */ - LT_KERNEL, /* entrySeparator */ - 0, /* needsBootPrefix */ - 1, /* argsInQuotes */ - 15, /* maxTitleLength */ - 0, /* titleBracketed */ - 0, /* mbHyperFirst */ - 0, /* mbInitRdIsModule */ - 0, /* mbConcatArgs */ - 0, /* mbAllowExtraInitRds */ + .defaultConfig = "/etc/lilo.conf", + .keywords = liloKeywords, + .entryStart = LT_KERNEL, + .argsInQuotes = 1, + .maxTitleLength = 15, }; struct configFileInfo yabootConfigType = { - "/etc/yaboot.conf", /* defaultConfig */ - yabootKeywords, /* keywords */ - 0, /* defaultIsIndex */ - 0, /* defaultSupportSaved */ - LT_KERNEL, /* entrySeparator */ - 1, /* needsBootPrefix */ - 1, /* argsInQuotes */ - 15, /* maxTitleLength */ - 0, /* titleBracketed */ - 0, /* mbHyperFirst */ - 0, /* mbInitRdIsModule */ - 0, /* mbConcatArgs */ - 1, /* mbAllowExtraInitRds */ + .defaultConfig = "/etc/yaboot.conf", + .keywords = yabootKeywords, + .entryStart = LT_KERNEL, + .needsBootPrefix = 1, + .argsInQuotes = 1, + .maxTitleLength = 15, + .mbAllowExtraInitRds = 1, }; struct configFileInfo siloConfigType = { - "/etc/silo.conf", /* defaultConfig */ - siloKeywords, /* keywords */ - 0, /* defaultIsIndex */ - 0, /* defaultSupportSaved */ - LT_KERNEL, /* entrySeparator */ - 1, /* needsBootPrefix */ - 1, /* argsInQuotes */ - 15, /* maxTitleLength */ - 0, /* titleBracketed */ - 0, /* mbHyperFirst */ - 0, /* mbInitRdIsModule */ - 0, /* mbConcatArgs */ - 0, /* mbAllowExtraInitRds */ + .defaultConfig = "/etc/silo.conf", + .keywords = siloKeywords, + .entryStart = LT_KERNEL, + .needsBootPrefix = 1, + .argsInQuotes = 1, + .maxTitleLength = 15, }; struct configFileInfo ziplConfigType = { - "/etc/zipl.conf", /* defaultConfig */ - ziplKeywords, /* keywords */ - 0, /* defaultIsIndex */ - 0, /* defaultSupportSaved */ - LT_TITLE, /* entrySeparator */ - 0, /* needsBootPrefix */ - 1, /* argsInQuotes */ - 0, /* maxTitleLength */ - 1, /* titleBracketed */ - 0, /* mbHyperFirst */ - 0, /* mbInitRdIsModule */ - 0, /* mbConcatArgs */ - 0, /* mbAllowExtraInitRds */ + .defaultConfig = "/etc/zipl.conf", + .keywords = ziplKeywords, + .entryStart = LT_TITLE, + .argsInQuotes = 1, + .titleBracketed = 1, }; struct configFileInfo extlinuxConfigType = { - "/boot/extlinux/extlinux.conf", /* defaultConfig */ - extlinuxKeywords, /* keywords */ - 0, /* defaultIsIndex */ - 0, /* defaultSupportSaved */ - LT_TITLE, /* entrySeparator */ - 1, /* needsBootPrefix */ - 0, /* argsInQuotes */ - 255, /* maxTitleLength */ - 0, /* titleBracketed */ - 0, /* mbHyperFirst */ - 0, /* mbInitRdIsModule */ - 0, /* mbConcatArgs */ - 1, /* mbAllowExtraInitRds */ + .defaultConfig = "/boot/extlinux/extlinux.conf", + .keywords = extlinuxKeywords, + .entryStart = LT_TITLE, + .needsBootPrefix = 1, + .maxTitleLength = 255, + .mbAllowExtraInitRds = 1, }; struct grubConfig { @@ -356,6 +378,8 @@ struct configFileInfo * cfi; }; +blkid_cache blkid; + struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index); struct singleEntry * findEntryByPath(struct grubConfig * cfg, const char * path, const char * prefix, @@ -369,6 +393,7 @@ static int getNextLine(char ** bufPtr, struct singleLine * line, struct configFileInfo * cfi); static char * getRootSpecifier(char * str); +static void requote(struct singleLine *line, struct configFileInfo * cfi); static void insertElement(struct singleLine * line, const char * item, int insertHere, struct configFileInfo * cfi); @@ -433,15 +458,27 @@ return NULL; } -static char * getpathbyspec(char *device) { - static blkid_cache blkid; +static char *getKeyByType(enum lineType_e type, struct configFileInfo * cfi) { + struct keywordTypes *kt = getKeywordByType(type, cfi); + if (kt) + return kt->key; + return "unknown"; +} +static char * getpathbyspec(char *device) { if (!blkid) blkid_get_cache(&blkid, NULL); return blkid_get_devname(blkid, device, NULL); } +static char * getuuidbydev(char *device) { + if (!blkid) + blkid_get_cache(&blkid, NULL); + + return blkid_get_tag_value(blkid, "UUID", device); +} + static enum lineType_e getTypeByKeyword(char * keyword, struct configFileInfo * cfi) { struct keywordTypes * kw; @@ -477,9 +514,9 @@ return 0; } -static int isEntrySeparator(struct singleLine * line, +static int isEntryStart(struct singleLine * line, struct configFileInfo * cfi) { - return line->type == cfi->entrySeparator || line->type == LT_OTHER || + return line->type == cfi->entryStart || line->type == LT_OTHER || (cfi->titleBracketed && isBracketedTitle(line)); } @@ -802,7 +839,7 @@ cfg->secondaryIndent = strdup(line->indent); } - if (isEntrySeparator(line, cfi)) { + if (isEntryStart(line, cfi)) { sawEntry = 1; if (!entry) { cfg->entries = malloc(sizeof(*entry)); @@ -818,7 +855,21 @@ entry->next = NULL; } - if (line->type == LT_DEFAULT && line->numElements == 2) { + if (line->type == LT_SET_VARIABLE) { + int i; + dbgPrintf("found 'set' command (%d elements): ", line->numElements); + dbgPrintf("%s", line->indent); + for (i = 0; i < line->numElements; i++) + dbgPrintf("%s\"%s\"", line->elements[i].indent, line->elements[i].item); + dbgPrintf("\n"); + struct keywordTypes *kwType = getKeywordByType(LT_DEFAULT, cfi); + if (kwType && line->numElements == 3 && + !strcmp(line->elements[1].item, kwType->key)) { + dbgPrintf("Line sets default config\n"); + cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT; + defaultLine = line; + } + } else if (line->type == LT_DEFAULT && line->numElements == 2) { cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT; defaultLine = line; @@ -931,13 +982,18 @@ entry->lines = line; else last->next = line; - dbgPrintf("readConfig added %d to %p\n", line->type, entry); + dbgPrintf("readConfig added %s to %p\n", getKeyByType(line->type, cfi), entry); + + /* we could have seen this outside of an entry... if so, we + * ignore it like any other line we don't grok */ + if (line->type == LT_ENTRY_END && sawEntry) + sawEntry = 0; } else { if (!cfg->theLines) cfg->theLines = line; else last->next = line; - dbgPrintf("readConfig added %d to cfg\n", line->type); + dbgPrintf("readConfig added %s to cfg\n", getKeyByType(line->type, cfi)); } last = line; @@ -945,8 +1001,19 @@ free(incoming); + dbgPrintf("defaultLine is %s\n", defaultLine ? "set" : "unset"); if (defaultLine) { - if (cfi->defaultSupportSaved && + if (cfi->defaultIsVariable) { + char *value = defaultLine->elements[2].item; + while (*value && (*value == '"' || *value == '\'' || + *value == ' ' || *value == '\t')) + value++; + cfg->defaultImage = strtol(value, &end, 10); + while (*end && (*end == '"' || *end == '\'' || + *end == ' ' || *end == '\t')) + end++; + if (*end) cfg->defaultImage = -1; + } else if (cfi->defaultSupportSaved && !strncmp(defaultLine->elements[1].item, "saved", 5)) { cfg->defaultImage = DEFAULT_SAVED; } else if (cfi->defaultIsIndex) { @@ -995,8 +1062,13 @@ fprintf(out, "%sdefault%ssaved\n", indent, separator); else if (cfg->defaultImage > -1) { if (cfg->cfi->defaultIsIndex) { - fprintf(out, "%sdefault%s%d\n", indent, separator, - cfg->defaultImage); + if (cfg->cfi->defaultIsVariable) { + fprintf(out, "%sset default=\"%d\"\n", indent, + cfg->defaultImage); + } else { + fprintf(out, "%sdefault%s%d\n", indent, separator, + cfg->defaultImage); + } } else { int image = cfg->defaultImage; @@ -1086,8 +1158,14 @@ } line = cfg->theLines; + struct keywordTypes *defaultKw = getKeywordByType(LT_DEFAULT, cfg->cfi); while (line) { - if (line->type == LT_DEFAULT) { + if (line->type == LT_SET_VARIABLE && defaultKw && + line->numElements == 3 && + !strcmp(line->elements[1].item, defaultKw->key)) { + writeDefault(out, line->indent, line->elements[0].indent, cfg); + needs &= ~MAIN_DEFAULT; + } else if (line->type == LT_DEFAULT) { writeDefault(out, line->indent, line->elements[0].indent, cfg); needs &= ~MAIN_DEFAULT; } else if (line->type == LT_FALLBACK) { @@ -1155,14 +1233,84 @@ return i; } +static char *findDiskForRoot() +{ + int fd; + char buf[65536]; + char *devname; + char *chptr; + int rc; + + if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) { + fprintf(stderr, "grubby: failed to open %s: %s\n", + _PATH_MOUNTED, strerror(errno)); + return NULL; + } + + rc = read(fd, buf, sizeof(buf) - 1); + if (rc <= 0) { + fprintf(stderr, "grubby: failed to read %s: %s\n", + _PATH_MOUNTED, strerror(errno)); + close(fd); + return NULL; + } + close(fd); + buf[rc] = '\0'; + chptr = buf; + + while (chptr && chptr != buf+rc) { + devname = chptr; + + /* + * The first column of a mtab entry is the device, but if the entry is a + * special device it won't start with /, so move on to the next line. + */ + if (*devname != '/') { + chptr = strchr(chptr, '\n'); + if (chptr) + chptr++; + continue; + } + + /* Seek to the next space */ + chptr = strchr(chptr, ' '); + if (!chptr) { + fprintf(stderr, "grubby: error parsing %s: %s\n", + _PATH_MOUNTED, strerror(errno)); + return NULL; + } + + /* + * The second column of a mtab entry is the mount point, we are looking + * for '/' obviously. + */ + if (*(++chptr) == '/' && *(++chptr) == ' ') { + /* + * Move back 2, which is the first space after the device name, set + * it to \0 so strdup will just get the devicename. + */ + chptr -= 2; + *chptr = '\0'; + return strdup(devname); + } + + /* Next line */ + chptr = strchr(chptr, '\n'); + if (chptr) + chptr++; + } + + return NULL; +} + int suitableImage(struct singleEntry * entry, const char * bootPrefix, int skipRemoved, int flags) { struct singleLine * line; char * fullName; int i; - struct stat sb, sb2; char * dev; char * rootspec; + char * rootdev; if (skipRemoved && entry->skip) return 0; @@ -1212,14 +1360,21 @@ if (!dev) return 0; - i = stat(dev, &sb); - if (i) + rootdev = findDiskForRoot(); + if (!rootdev) return 0; - stat("/", &sb2); + if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) { + free(rootdev); + return 0; + } - if (sb.st_rdev != sb2.st_dev) + if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) { + free(rootdev); return 0; + } + + free(rootdev); return 1; } @@ -1733,9 +1888,36 @@ sprintf(tmpl.elements[0].item, "[%s]", val); tmpl.elements[0].indent = ""; val = NULL; + } else if (type == LT_MENUENTRY) { + char *lineend = "--class gnu-linux --class gnu --class os {"; + if (!val) { + fprintf(stderr, "Line type LT_MENUENTRY requires a value\n"); + abort(); + } + kw = getKeywordByType(type, cfi); + if (!kw) { + fprintf(stderr, "Looking up keyword for unknown type %d\n", type); + abort(); + } + tmpl.indent = ""; + tmpl.type = type; + tmpl.numElements = 3; + tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements); + tmpl.elements[0].item = kw->key; + tmpl.elements[0].indent = alloca(2); + sprintf(tmpl.elements[0].indent, "%c", kw->nextChar); + tmpl.elements[1].item = (char *)val; + tmpl.elements[1].indent = alloca(2); + sprintf(tmpl.elements[1].indent, "%c", kw->nextChar); + tmpl.elements[2].item = alloca(strlen(lineend)+1); + strcpy(tmpl.elements[2].item, lineend); + tmpl.elements[2].indent = ""; } else { kw = getKeywordByType(type, cfi); - if (!kw) abort(); + if (!kw) { + fprintf(stderr, "Looking up keyword for unknown type %d\n", type); + abort(); + } tmpl.type = type; tmpl.numElements = val ? 2 : 1; tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements); @@ -1759,10 +1941,21 @@ if (!line->next && !prev) prev = line; } - if (prev == entry->lines) - tmpl.indent = defaultIndent ?: ""; - else - tmpl.indent = prev->indent; + struct singleLine *menuEntry; + menuEntry = getLineByType(LT_MENUENTRY, entry->lines); + if (tmpl.type == LT_ENTRY_END) { + if (menuEntry) + tmpl.indent = menuEntry->indent; + else + tmpl.indent = defaultIndent ?: ""; + } else if (tmpl.type != LT_MENUENTRY) { + if (menuEntry) + tmpl.indent = "\t"; + else if (prev == entry->lines) + tmpl.indent = defaultIndent ?: ""; + else + tmpl.indent = prev->indent; + } return addLineTmpl(entry, &tmpl, prev, val, cfi); } @@ -1789,6 +1982,75 @@ free(line); } +static int isquote(char q) +{ + if (q == '\'' || q == '\"') + return 1; + return 0; +} + +static void requote(struct singleLine *tmplLine, struct configFileInfo * cfi) +{ + struct singleLine newLine = { + .indent = tmplLine->indent, + .type = tmplLine->type, + .next = tmplLine->next, + }; + int firstQuotedItem = -1; + int quoteLen = 0; + int j; + int element = 0; + char *c; + + c = malloc(strlen(tmplLine->elements[0].item) + 1); + strcpy(c, tmplLine->elements[0].item); + insertElement(&newLine, c, element++, cfi); + free(c); + c = NULL; + + for (j = 1; j < tmplLine->numElements; j++) { + if (firstQuotedItem == -1) { + quoteLen += strlen(tmplLine->elements[j].item); + + if (isquote(tmplLine->elements[j].item[0])) { + firstQuotedItem = j; + quoteLen += strlen(tmplLine->elements[j].indent); + } else { + c = malloc(quoteLen + 1); + strcpy(c, tmplLine->elements[j].item); + insertElement(&newLine, c, element++, cfi); + free(c); + quoteLen = 0; + } + } else { + int itemlen = strlen(tmplLine->elements[j].item); + quoteLen += itemlen; + quoteLen += strlen(tmplLine->elements[j].indent); + + if (isquote(tmplLine->elements[j].item[itemlen - 1])) { + c = malloc(quoteLen + 1); + c[0] = '\0'; + for (int i = firstQuotedItem; i < j+1; i++) { + strcat(c, tmplLine->elements[i].item); + strcat(c, tmplLine->elements[i].indent); + } + insertElement(&newLine, c, element++, cfi); + free(c); + + firstQuotedItem = -1; + quoteLen = 0; + } + } + } + while (tmplLine->numElements) + removeElement(tmplLine, 0); + if (tmplLine->elements) + free(tmplLine->elements); + + tmplLine->numElements = newLine.numElements; + tmplLine->elements = newLine.elements; +} + static void insertElement(struct singleLine * line, const char * item, int insertHere, struct configFileInfo * cfi) @@ -1895,7 +2157,7 @@ const char ** arg; int useKernelArgs, useRoot; int firstElement; - int *usedElements, *usedArgs; + int *usedElements; int doreplace; if (!image) return 0; @@ -1930,9 +2192,6 @@ useRoot = (getKeywordByType(LT_ROOT, cfg->cfi) && !multibootArgs); - for (k = 0, arg = newArgs; *arg; arg++, k++) ; - usedArgs = calloc(k, sizeof(*usedArgs)); - for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) { if (multibootArgs && !entry->multiboot) @@ -2008,7 +2267,6 @@ usedElements = calloc(line->numElements, sizeof(*usedElements)); for (k = 0, arg = newArgs; *arg; arg++, k++) { - if (usedArgs[k]) continue; doreplace = 1; for (i = firstElement; i < line->numElements; i++) { @@ -2023,7 +2281,6 @@ continue; if (!argMatch(line->elements[i].item, *arg)) { usedElements[i]=1; - usedArgs[k]=1; break; } } @@ -2093,7 +2350,6 @@ } } - free(usedArgs); free(newArgs); free(oldArgs); @@ -2119,6 +2375,44 @@ return rc; } +int updateInitrd(struct grubConfig * cfg, const char * image, + const char * prefix, const char * initrd) { + struct singleEntry * entry; + struct singleLine * line, * kernelLine, *endLine = NULL; + int index = 0; + + if (!image) return 0; + + for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) { + kernelLine = getLineByType(LT_KERNEL, entry->lines); + if (!kernelLine) continue; + + line = getLineByType(LT_INITRD, entry->lines); + if (line) + removeLine(entry, line); + if (prefix) { + int prefixLen = strlen(prefix); + if (!strncmp(initrd, prefix, prefixLen)) + initrd += prefixLen; + } + endLine = getLineByType(LT_ENTRY_END, entry->lines); + if (endLine) + removeLine(entry, endLine); + line = addLine(entry, cfg->cfi, LT_INITRD, kernelLine->indent, initrd); + if (!line) + return 1; + if (endLine) { + line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL); + if (!line) + return 1; + } + + break; + } + + return 0; +} + int checkDeviceBootloader(const char * device, const unsigned char * boot) { int fd; unsigned char bootSect[512]; @@ -2289,6 +2583,13 @@ return checkDeviceBootloader(line->elements[1].item, boot); } +int checkForGrub2(struct grubConfig * config) { + if (!access("/boot/grub2", R_OK)) + return 2; + + return 1; +} + int checkForGrub(struct grubConfig * config) { int fd; unsigned char bootSect[512]; @@ -2464,7 +2765,7 @@ if (*chptr == '#') continue; if (tmplLine->type == LT_KERNEL && - tmplLine->numElements >= 2) { + 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 @@ -2590,6 +2891,16 @@ needs &= ~NEED_INITRD; } + } else if (tmplLine->type == LT_MENUENTRY && + (needs & NEED_TITLE)) { + requote(tmplLine, config->cfi); + char *nkt = malloc(strlen(newKernelTitle)+3); + strcpy(nkt, "'"); + strcat(nkt, newKernelTitle); + strcat(nkt, "'"); + newLine = addLineTmpl(new, tmplLine, newLine, nkt, config->cfi); + free(nkt); + needs &= ~NEED_TITLE; } else if (tmplLine->type == LT_TITLE && (needs & NEED_TITLE)) { if (tmplLine->numElements >= 2) { @@ -2603,7 +2914,25 @@ tmplLine->indent, newKernelTitle); needs &= ~NEED_TITLE; } - + } else if (tmplLine->type == LT_ECHO) { + requote(tmplLine, config->cfi); + if (tmplLine->numElements > 1 && + strstr(tmplLine->elements[1].item, "'Loading Linux ")) { + char *prefix = "'Loading "; + char *newTitle = malloc(strlen(prefix) + + strlen(newKernelTitle) + 2); + + strcpy(newTitle, prefix); + strcat(newTitle, newKernelTitle); + strcat(newTitle, "'"); + newLine = addLine(new, config->cfi, LT_ECHO, + tmplLine->indent, newTitle); + free(newTitle); + } else { + /* pass through other lines from the template */ + newLine = addLineTmpl(new, tmplLine, newLine, NULL, + config->cfi); + } } else { /* pass through other lines from the template */ newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi); @@ -2614,7 +2943,7 @@ /* don't have a template, so start the entry with the * appropriate starting line */ - switch (config->cfi->entrySeparator) { + switch (config->cfi->entryStart) { case LT_KERNEL: if (new->multiboot && config->cfi->mbHyperFirst) { /* fall through to LT_HYPER */ @@ -2633,6 +2962,18 @@ needs &= ~NEED_MB; break; + case LT_MENUENTRY: { + char *nkt = malloc(strlen(newKernelTitle)+3); + strcpy(nkt, "'"); + strcat(nkt, newKernelTitle); + strcat(nkt, "'"); + newLine = addLine(new, config->cfi, LT_MENUENTRY, + config->primaryIndent, nkt); + free(nkt); + needs &= ~NEED_TITLE; + needs |= NEED_END; + break; + } case LT_TITLE: if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above) char * templabel; @@ -2666,7 +3007,7 @@ /* add the remainder of the lines, i.e. those that either * weren't present in the template, or in the case of no template, - * all the lines following the entrySeparator. + * all the lines following the entryStart. */ if (needs & NEED_TITLE) { newLine = addLine(new, config->cfi, LT_TITLE, @@ -2707,6 +3048,11 @@ free(initrdVal); needs &= ~NEED_INITRD; } + if (needs & NEED_END) { + newLine = addLine(new, config->cfi, LT_ENTRY_END, + config->secondaryIndent, NULL); + needs &= ~NEED_END; + } if (needs) { printf(_("grubby: needs=%d, aborting\n"), needs); @@ -2736,11 +3082,12 @@ int main(int argc, const char ** argv) { poptContext optCon; - char * grubConfig = NULL; + const char * grubConfig = NULL; char * outputFile = NULL; int arg = 0; int flags = 0; int badImageOkay = 0; + int configureGrub2 = 0; int configureLilo = 0, configureELilo = 0, configureGrub = 0; int configureYaboot = 0, configureSilo = 0, configureZipl = 0; int configureExtLinux = 0; @@ -2806,6 +3153,8 @@ _("configure extlinux bootloader (from syslinux)") }, { "grub", 0, POPT_ARG_NONE, &configureGrub, 0, _("configure grub bootloader") }, + { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0, + _("configure grub2 bootloader") }, { "info", 0, POPT_ARG_STRING, &kernelInfo, 0, _("display boot information for specified kernel"), _("kernel-path") }, @@ -2885,7 +3234,7 @@ return 1; } - if ((configureLilo + configureGrub + configureELilo + + if ((configureLilo + configureGrub2 + configureGrub + configureELilo + configureYaboot + configureSilo + configureZipl + configureExtLinux ) > 1) { fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n")); @@ -2894,6 +3243,8 @@ fprintf(stderr, _("grubby: cannot specify config file with --bootloader-probe\n")); return 1; + } else if (configureGrub2) { + cfi = &grub2ConfigType; } else if (configureLilo) { cfi = &liloConfigType; } else if (configureGrub) { @@ -2923,12 +3274,19 @@ #elif __s390x__ cfi = &ziplConfigtype; #else - cfi = &grubConfigType; + if (grub2FindConfig(&grub2ConfigType)) + cfi = &grub2ConfigType; + else + cfi = &grubConfigType; #endif } - if (!grubConfig) - grubConfig = cfi->defaultConfig; + if (!grubConfig) { + if (cfi->findConfig) + grubConfig = cfi->findConfig(cfi); + if (!grubConfig) + grubConfig = cfi->defaultConfig; + } if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion || newKernelPath || removeKernelPath || makeDefault || @@ -2948,8 +3306,8 @@ if (newKernelPath && !newKernelTitle) { fprintf(stderr, _("grubby: kernel title must be specified\n")); return 1; - } else if (!newKernelPath && (newKernelTitle || newKernelInitrd || - newKernelInitrd || copyDefault || + } else if (!newKernelPath && (newKernelTitle || copyDefault || + (newKernelInitrd && !updateKernelPath)|| makeDefault || extraInitrdCount > 0)) { fprintf(stderr, _("grubby: kernel path expected\n")); return 1; @@ -3010,9 +3368,18 @@ } if (bootloaderProbe) { - int lrc = 0, grc = 0, erc = 0; + int lrc = 0, grc = 0, gr2c = 0, erc = 0; struct grubConfig * lconfig, * gconfig; + const char *grub2config = grub2FindConfig(&grub2ConfigType); + if (grub2config) { + gconfig = readConfig(grub2config, &grub2ConfigType); + if (!gconfig) + gr2c = 1; + else + gr2c = checkForGrub2(gconfig); + } + if (!access(grubConfigType.defaultConfig, F_OK)) { gconfig = readConfig(grubConfigType.defaultConfig, &grubConfigType); if (!gconfig) @@ -3037,9 +3404,10 @@ erc = checkForExtLinux(lconfig); } - if (lrc == 1 || grc == 1) return 1; + if (lrc == 1 || grc == 1 || gr2c == 1) return 1; if (lrc == 2) printf("lilo\n"); + if (gr2c == 2) printf("grub2\n"); if (grc == 2) printf("grub\n"); if (erc == 2) printf("extlinux\n"); @@ -3082,6 +3450,10 @@ setFallbackImage(config, newKernelPath != NULL); if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs, removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1; + if (updateKernelPath && newKernelInitrd) { + if (updateInitrd(config, updateKernelPath, bootPrefix, + newKernelInitrd)) return 1; + } if (addNewKernel(config, template, bootPrefix, newKernelPath, newKernelTitle, newKernelArgs, newKernelInitrd, extraInitrds, extraInitrdCount, @@ -3095,7 +3467,7 @@ } if (!outputFile) - outputFile = grubConfig; + outputFile = (char *)grubConfig; return writeConfig(config, outputFile, bootPrefix); }