--- trunk/mkinitrd-magellan/grubby/grubby.c 2007/09/01 22:45:15 532 +++ trunk/grubby/grubby.c 2012/02/18 00:51:28 1720 @@ -1,20 +1,26 @@ -/* Copyright (C) 2001-2005 Red Hat, Inc. - - This program is free software; you can redistribute it and/or - modify it under the terms of the General Public License as published - by the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public - License along with this program; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ +/* + * grubby.c + * + * Copyright (C) 2001-2008 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif #include #include #include @@ -25,22 +31,57 @@ #include #include #include +#include +#include +#include +#include -#include "mount_by_label.h" +#ifndef DEBUG +#define DEBUG 0 +#endif + +#if DEBUG +#define dbgPrintf(format, args...) fprintf(stderr, format , ## args) +#else +#define dbgPrintf(format, args...) +#endif #define _(A) (A) +#define MAX_EXTRA_INITRDS 16 /* code segment checked by --bootloader-probe */ #define CODE_SEG_SIZE 128 /* code segment checked by --bootloader-probe */ +#define NOOP_OPCODE 0x90 +#define JMP_SHORT_OPCODE 0xeb + /* comments get lumped in with indention */ struct lineElement { char * item; char * indent; }; -enum lineType_e { LT_WHITESPACE, LT_TITLE, LT_KERNEL, LT_INITRD, LT_DEFAULT, - LT_UNKNOWN, LT_ROOT, LT_FALLBACK, LT_KERNELARGS, LT_BOOT, - LT_BOOTROOT, LT_LBA, LT_MBMODULE, LT_OTHER, LT_GENERIC }; +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_ECHO = 1 << 16, + LT_MENUENTRY = 1 << 17, + LT_ENTRY_END = 1 << 18, + LT_SET_VARIABLE = 1 << 19, + LT_UNKNOWN = 1 << 20, +}; struct singleLine { char * indent; @@ -61,11 +102,13 @@ #define GRUB_CONFIG_NO_DEFAULT (1 << 0) /* don't write out default=0 */ -#define KERNEL_KERNEL (1 << 0) -#define KERNEL_INITRD (1 << 2) -#define KERNEL_TITLE (1 << 3) -#define KERNEL_ARGS (1 << 4) -#define KERNEL_MB (1 << 5) +/* These defines are (only) used in addNewKernel() */ +#define NEED_KERNEL (1 << 0) +#define NEED_INITRD (1 << 1) +#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 @@ -74,18 +117,34 @@ char * key; enum lineType_e type; char nextChar; -} ; + 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; + int mbAllowExtraInitRds; }; struct keywordTypes grubKeywords[] = { @@ -94,21 +153,105 @@ { "default", LT_DEFAULT, ' ' }, { "fallback", LT_FALLBACK, ' ' }, { "kernel", LT_KERNEL, ' ' }, - { "initrd", LT_INITRD, ' ' }, + { "initrd", LT_INITRD, ' ', ' ' }, { "module", LT_MBMODULE, ' ' }, + { "kernel", LT_HYPER, ' ' }, { NULL, 0, 0 }, }; +const char *grubFindConfig(struct configFileInfo *cfi) { + static const char *configFiles[] = { + "/etc/grub.conf", + "/boot/grub/grub.conf", + "/boot/grub/menu.lst", + 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 grubConfigType = { - "/boot/grub/grub.conf", /* defaultConfig */ - grubKeywords, /* keywords */ - 1, /* defaultIsIndex */ - 1, /* defaultSupportSaved */ - LT_TITLE, /* entrySeparator */ - 1, /* needsBootPrefix */ - 0, /* argsInQuotes */ - 0, /* maxTitleLength */ - 0, /* titleBracketed */ + .findConfig = grubFindConfig, + .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; + static const char *grub_cfg = "/boot/grub/grub.cfg"; + + 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]; + } + } + } + + /* Ubuntu renames grub2 to grub, so check for the grub.d directory + * that isn't in grub1, and if it exists, return the config file path + * that they use. */ + if (configFiles[i] == NULL && !access("/etc/grub.d/", R_OK)) { + dbgPrintf("found\n"); + return grub_cfg; + } + + 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[] = { @@ -142,7 +285,7 @@ { "partition", LT_GENERIC, '=' }, { "device", LT_GENERIC, '=' }, { "fstype", LT_GENERIC, '=' }, - { "initrd", LT_INITRD, '=' }, + { "initrd", LT_INITRD, '=', ';' }, { "append", LT_KERNELARGS, '=' }, { "boot", LT_BOOT, '=' }, { "lba", LT_LBA, ' ' }, @@ -162,6 +305,17 @@ { NULL, 0, 0 }, }; +struct keywordTypes eliloKeywords[] = { + { "label", LT_TITLE, '=' }, + { "root", LT_ROOT, '=' }, + { "default", LT_DEFAULT, '=' }, + { "image", LT_KERNEL, '=' }, + { "initrd", LT_INITRD, '=' }, + { "append", LT_KERNELARGS, '=' }, + { "vmm", LT_HYPER, '=' }, + { NULL, 0, 0 }, +}; + struct keywordTypes siloKeywords[] = { { "label", LT_TITLE, '=' }, { "root", LT_ROOT, '=' }, @@ -183,64 +337,68 @@ { NULL, 0, 0 }, }; +struct keywordTypes extlinuxKeywords[] = { + { "label", LT_TITLE, ' ' }, + { "root", LT_ROOT, ' ' }, + { "default", LT_DEFAULT, ' ' }, + { "kernel", LT_KERNEL, ' ' }, + { "initrd", LT_INITRD, ' ', ',' }, + { "append", LT_KERNELARGS, ' ' }, + { "prompt", LT_UNKNOWN, ' ' }, + { NULL, 0, 0 }, +}; +int useextlinuxmenu; struct configFileInfo eliloConfigType = { - "/boot/efi/EFI/redhat/elilo.conf", /* defaultConfig */ - liloKeywords, /* keywords */ - 0, /* defaultIsIndex */ - 0, /* defaultSupportSaved */ - LT_KERNEL, /* entrySeparator */ - 1, /* needsBootPrefix */ - 1, /* argsInQuotes */ - 0, /* maxTitleLength */ - 0, /* titleBracketed */ + .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 */ + .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 */ + .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 */ + .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 */ - 15, /* maxTitleLength */ - 1, /* titleBracketed */ + .defaultConfig = "/etc/zipl.conf", + .keywords = ziplKeywords, + .entryStart = LT_TITLE, + .argsInQuotes = 1, + .titleBracketed = 1, +}; + +struct configFileInfo extlinuxConfigType = { + .defaultConfig = "/boot/extlinux/extlinux.conf", + .keywords = extlinuxKeywords, + .entryStart = LT_TITLE, + .needsBootPrefix = 1, + .maxTitleLength = 255, + .mbAllowExtraInitRds = 1, }; struct grubConfig { @@ -255,30 +413,42 @@ 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, int * index); -static char * strndup(char * from, int len); static int readFile(int fd, char ** bufPtr); static void lineInit(struct singleLine * line); +struct singleLine * lineDup(struct singleLine * line); static void lineFree(struct singleLine * line); static int lineWrite(FILE * out, struct singleLine * line, struct configFileInfo * cfi); static int getNextLine(char ** bufPtr, struct singleLine * line, struct configFileInfo * cfi); static char * getRootSpecifier(char * str); - -static char * strndup(char * from, int len) { - char * to; - - to = malloc(len + 1); - strncpy(to, from, len); - to[len] = '\0'; - - return to; -} +static void requote(struct singleLine *line, struct configFileInfo * cfi); +static void insertElement(struct singleLine * line, + const char * item, int insertHere, + struct configFileInfo * cfi); +static void removeElement(struct singleLine * line, int removeHere); +static struct keywordTypes * getKeywordByType(enum lineType_e type, + struct configFileInfo * cfi); +static enum lineType_e getTypeByKeyword(char * keyword, + struct configFileInfo * cfi); +static struct singleLine * getLineByType(enum lineType_e type, + struct singleLine * line); +static int checkForExtLinux(struct grubConfig * config); +struct singleLine * addLineTmpl(struct singleEntry * entry, + struct singleLine * tmplLine, + struct singleLine * prevLine, + const char * val, + struct configFileInfo * cfi); +struct singleLine * addLine(struct singleEntry * entry, + struct configFileInfo * cfi, + enum lineType_e type, char * defaultIndent, + const char * val); static char * sdupprintf(const char *format, ...) #ifdef __GNUC__ @@ -313,8 +483,61 @@ return buf; } +static struct keywordTypes * getKeywordByType(enum lineType_e type, + struct configFileInfo * cfi) { + struct keywordTypes * kw; + for (kw = cfi->keywords; kw->key; kw++) { + if (kw->type == type) + return kw; + } + return NULL; +} + +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; + for (kw = cfi->keywords; kw->key; kw++) { + if (!strcmp(keyword, kw->key)) + return kw->type; + } + return LT_UNKNOWN; +} + +static struct singleLine * getLineByType(enum lineType_e type, + struct singleLine * line) { + dbgPrintf("getLineByType(%d): ", type); + for (; line; line = line->next) { + dbgPrintf("%d:%s ", line->type, + line->numElements ? line->elements[0].item : "(empty)"); + if (line->type & type) break; + } + dbgPrintf(line ? "\n" : " (failed)\n"); + return line; +} + static int isBracketedTitle(struct singleLine * line) { - if ((*line->elements[0].item == '[') && (line->numElements == 1)) { + if (line->numElements == 1 && *line->elements[0].item == '[') { int len = strlen(line->elements[0].item); if (*(line->elements[0].item + len - 1) == ']') { /* FIXME: this is a hack... */ @@ -326,19 +549,10 @@ return 0; } -/* figure out if this is a entry separator */ -static int isEntrySeparator(struct singleLine * line, +static int isEntryStart(struct singleLine * line, struct configFileInfo * cfi) { - if (line->type == LT_WHITESPACE) - return 0; - if (line->type == cfi->entrySeparator) - return 1; - if (line->type == LT_OTHER) - return 1; - if (cfi->titleBracketed && isBracketedTitle(line)) { - return 1; - } - return 0; + return line->type == cfi->entryStart || line->type == LT_OTHER || + (cfi->titleBracketed && isBracketedTitle(line)); } /* extract the title from within brackets (for zipl) */ @@ -389,6 +603,25 @@ line->next = NULL; } +struct singleLine * lineDup(struct singleLine * line) { + int i; + struct singleLine * newLine = malloc(sizeof(*newLine)); + + newLine->indent = strdup(line->indent); + newLine->next = NULL; + newLine->type = line->type; + newLine->numElements = line->numElements; + newLine->elements = malloc(sizeof(*newLine->elements) * + newLine->numElements); + + for (i = 0; i < newLine->numElements; i++) { + newLine->elements[i].indent = strdup(line->elements[i].indent); + newLine->elements[i].item = strdup(line->elements[i].item); + } + + return newLine; +} + static void lineFree(struct singleLine * line) { int i; @@ -414,7 +647,8 @@ if (fputc('"', out) == EOF) return -1; if (fprintf(out, "%s", line->elements[i].item) == -1) return -1; - if (fprintf(out, "%s", line->elements[i].indent) == -1) return -1; + if (i < line->numElements - 1) + if (fprintf(out, "%s", line->elements[i].indent) == -1) return -1; } if (line->type == LT_KERNELARGS && cfi->argsInQuotes) @@ -433,9 +667,7 @@ char * chptr; int elementsAlloced = 0; struct lineElement * element; - struct keywordTypes * keywords = cfi->keywords; int first = 1; - int i; lineFree(line); @@ -489,14 +721,8 @@ if (!line->numElements) line->type = LT_WHITESPACE; else { - for (i = 0; keywords[i].key; i++) - if (!strcmp(line->elements[0].item, keywords[i].key)) break; - - if (keywords[i].key) { - line->type = keywords[i].type; - } else { - line->type = LT_UNKNOWN; - + line->type = getTypeByKeyword(line->elements[0].item, cfi); + if (line->type == LT_UNKNOWN) { /* zipl does [title] instead of something reasonable like all * the other boot loaders. kind of ugly */ if (cfi->titleBracketed && isBracketedTitle(line)) { @@ -532,6 +758,59 @@ line->type = LT_WHITESPACE; line->numElements = 0; } + } else { + struct keywordTypes *kw; + + kw = getKeywordByType(line->type, cfi); + + /* space isn't the only separator, we need to split + * elements up more + */ + if (!isspace(kw->separatorChar)) { + int i; + char indent[2] = ""; + indent[0] = kw->separatorChar; + for (i = 1; i < line->numElements; i++) { + char *p; + int j; + int numNewElements; + + numNewElements = 0; + p = line->elements[i].item; + while (*p != '\0') { + if (*p == kw->separatorChar) + numNewElements++; + p++; + } + if (line->numElements + numNewElements >= elementsAlloced) { + elementsAlloced += numNewElements + 5; + line->elements = realloc(line->elements, + sizeof(*line->elements) * elementsAlloced); + } + + for (j = line->numElements; j > i; j--) { + line->elements[j + numNewElements] = line->elements[j]; + } + line->numElements += numNewElements; + + p = line->elements[i].item; + while (*p != '\0') { + + while (*p != kw->separatorChar && *p != '\0') p++; + if (*p == '\0') { + break; + } + + free(line->elements[i].indent); + line->elements[i].indent = strdup(indent); + *p++ = '\0'; + i++; + line->elements[i].item = strdup(p); + line->elements[i].indent = strdup(""); + p = line->elements[i].item; + } + } + } } } @@ -595,7 +874,7 @@ cfg->secondaryIndent = strdup(line->indent); } - if (isEntrySeparator(line, cfi)) { + if (isEntryStart(line, cfi)) { sawEntry = 1; if (!entry) { cfg->entries = malloc(sizeof(*entry)); @@ -611,14 +890,56 @@ 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; + + } else if (line->type == LT_KERNEL) { + /* 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 + */ + if (entry->multiboot) + line->type = LT_HYPER; + } else if (line->type == LT_MBMODULE) { + /* go back and fix the LT_KERNEL line to indicate LT_HYPER + * instead, now that we know this is a multiboot entry. + * This only applies to grub, but that's the only place we + * should find LT_MBMODULE lines anyway. + */ + struct singleLine * l; + for (l = entry->lines; l; l = l->next) { + if (l->type == LT_HYPER) + break; + else if (l->type == LT_KERNEL) { + l->type = LT_HYPER; + break; + } + } entry->multiboot = 1; + + } else if (line->type == LT_HYPER) { + entry->multiboot = 1; + } else if (line->type == LT_FALLBACK && line->numElements == 2) { cfg->fallbackImage = strtol(line->elements[1].item, &end, 10); if (*end) cfg->fallbackImage = -1; + } else if (line->type == LT_TITLE && line->numElements > 1) { /* make the title a single argument (undoing our parsing) */ len = 0; @@ -643,6 +964,7 @@ line->elements[line->numElements - 1].indent; line->elements[1].item = buf; line->numElements = 2; + } else if (line->type == LT_KERNELARGS && cfi->argsInQuotes) { /* Strip off any " which may be present; they'll be put back on write. This is one of the few (the only?) places that grubby @@ -652,15 +974,14 @@ int last, len; if (*line->elements[1].item == '"') - memcpy(line->elements[1].item, line->elements[1].item + 1, - strlen(line->elements[1].item + 1) + 1); + memmove(line->elements[1].item, line->elements[1].item + 1, + strlen(line->elements[1].item + 1) + 1); last = line->numElements - 1; len = strlen(line->elements[last].item) - 1; if (line->elements[last].item[len] == '"') line->elements[last].item[len] = '\0'; } - } /* If we find a generic config option which should live at the @@ -680,6 +1001,7 @@ movedLine = 1; continue; /* without setting 'last' */ } + /* If a second line of whitespace happens after a generic option which was moved, drop it. */ if (movedLine && line->type == LT_WHITESPACE && last->type == LT_WHITESPACE) { @@ -695,12 +1017,18 @@ entry->lines = line; else last->next = line; + 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 { + else last->next = line; - } + dbgPrintf("readConfig added %s to cfg\n", getKeyByType(line->type, cfi)); } last = line; @@ -708,8 +1036,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) { @@ -730,10 +1069,17 @@ extractTitle(line))) break; } i++; + entry = NULL; } - if (entry) cfg->defaultImage = i; + if (entry){ + cfg->defaultImage = i; + }else{ + cfg->defaultImage = -1; + } } + } else { + cfg->defaultImage = 0; } return cfg; @@ -751,8 +1097,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; @@ -769,8 +1120,7 @@ if (!entry) return; - line = entry->lines; - while (line && line->type != LT_TITLE) line = line->next; + line = getLineByType(LT_TITLE, entry->lines); if (line && line->numElements >= 2) fprintf(out, "%sdefault%s%s\n", indent, separator, @@ -804,11 +1154,11 @@ int rc; /* most likely the symlink is relative, so change our - directory to / */ - rc = chdir("/"); + directory to the dir of the symlink */ + rc = chdir(dirname(strdupa(outName))); do { buf = alloca(len + 1); - rc = readlink(outName, buf, len); + rc = readlink(basename(outName), buf, len); if (rc == len) len += 256; } while (rc == len); @@ -843,8 +1193,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) { @@ -912,22 +1268,89 @@ 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 * end; char * rootspec; + char * rootdev; - line = entry->lines; - while (line && line->type != LT_KERNEL) line = line->next; - - if (!line) return 0; if (skipRemoved && entry->skip) return 0; - if (line->numElements < 2) return 0; + + line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines); + if (!line || line->numElements < 2) return 0; if (flags & GRUBBY_BADIMAGE_OKAY) return 1; @@ -935,8 +1358,7 @@ strlen(line->elements[1].item) + 1); rootspec = getRootSpecifier(line->elements[1].item); sprintf(fullName, "%s%s", bootPrefix, - line->elements[1].item + ((rootspec != NULL) ? - strlen(rootspec) : 0)); + line->elements[1].item + (rootspec ? strlen(rootspec) : 0)); if (access(fullName, R_OK)) return 0; for (i = 2; i < line->numElements; i++) @@ -945,19 +1367,15 @@ dev = line->elements[i].item + 5; } else { /* look for a lilo style LT_ROOT line */ - line = entry->lines; - while (line && line->type != LT_ROOT) line = line->next; + line = getLineByType(LT_ROOT, entry->lines); if (line && line->numElements >= 2) { dev = line->elements[1].item; } else { - int type; - /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS */ - line = entry->lines; - - type = ((entry->multiboot) ? LT_MBMODULE : LT_KERNELARGS); - - while (line && line->type != type) line = line->next; + /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS. + * grub+multiboot uses LT_MBMODULE for the args, so check that too. + */ + line = getLineByType(LT_KERNELARGS|LT_MBMODULE, entry->lines); /* failed to find one */ if (!line) return 0; @@ -973,24 +1391,25 @@ } } - if (!strncmp(dev, "LABEL=", 6)) { - dev += 6; - - /* check which device has this label */ - dev = get_spec_by_volume_label(dev, &i, &i); - if (!dev) return 0; + dev = getpathbyspec(dev); + if (!dev) + return 0; + + rootdev = findDiskForRoot(); + if (!rootdev) + return 0; + + if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) { + free(rootdev); + return 0; } - if (*dev == '/') { - if (stat(dev, &sb)) - return 0; - } else { - sb.st_rdev = strtol(dev, &end, 16); - if (*end) return 0; + if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) { + free(rootdev); + return 0; } - stat("/", &sb2); - if (sb.st_rdev != sb2.st_dev) return 0; + free(rootdev); return 1; } @@ -1034,10 +1453,7 @@ entry = findEntryByIndex(config, indexVars[i]); if (!entry) return NULL; - line = entry->lines; - while (line && line->type != LT_KERNEL) - line = line->next; - + line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines); if (!line) return NULL; if (index) *index = indexVars[i]; @@ -1079,49 +1495,38 @@ kernel += 6; } - while ((entry = findEntryByIndex(config, i))) { - line = entry->lines; - while (line && line->type != checkType) line=line->next; + for (entry = findEntryByIndex(config, i); entry; entry = entry->next, i++) { + if (entry->skip) continue; + dbgPrintf("findEntryByPath looking for %d %s in %p\n", checkType, kernel, entry); - if (line && line->numElements >= 2 && !entry->skip) { - rootspec = getRootSpecifier(line->elements[1].item); - if (!strcmp(line->elements[1].item + - ((rootspec != NULL) ? strlen(rootspec) : 0), - kernel + strlen(prefix))) - break; - } - - /* have to check multiboot lines too */ - if (entry->multiboot) { - while (line && line->type != LT_MBMODULE) line = line->next; - if (line && line->numElements >= 2 && !entry->skip) { - rootspec = getRootSpecifier(line->elements[1].item); - if (!strcmp(line->elements[1].item + - ((rootspec != NULL) ? strlen(rootspec) : 0), - kernel + strlen(prefix))) - break; - } - } + /* 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 */ + + if (line && line->numElements >= 2) { + rootspec = getRootSpecifier(line->elements[1].item); + if (!strcmp(line->elements[1].item + + ((rootspec != NULL) ? strlen(rootspec) : 0), + kernel + strlen(prefix))) + break; + } + } - i++; + /* make sure this entry has a kernel identifier; this skips + * non-Linux boot entries (could find netbsd etc, though, which is + * unfortunate) + */ + if (line && getLineByType(LT_KERNEL|LT_HYPER, entry->lines)) + break; /* found 'im! */ } if (index) *index = i; } - if (!entry) return NULL; - - /* make sure this entry has a kernel identifier; this skips non-Linux - boot entries (could find netbsd etc, though, which is unfortunate) */ - line = entry->lines; - while (line && line->type != LT_KERNEL) line = line->next; - if (!line) { - if (!index) index = &i; - (*index)++; - return findEntryByPath(config, kernel, prefix, index); - } - return entry; } @@ -1286,11 +1691,14 @@ char * root = NULL; int i; - line = entry->lines; - while (line && line->type != LT_KERNEL) line = line->next; - printf("index=%d\n", index); + line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines); + if (!line) { + printf("non linux entry\n"); + return; + } + printf("kernel=%s\n", line->elements[1].item); if (line->numElements >= 3) { @@ -1308,9 +1716,7 @@ } printf("\"\n"); } else { - line = entry->lines; - while (line && line->type != LT_KERNELARGS) line=line->next; - + line = getLineByType(LT_KERNELARGS, entry->lines); if (line) { char * s; @@ -1334,9 +1740,7 @@ } if (!root) { - line = entry->lines; - while (line && line->type != LT_ROOT) line = line->next; - + line = getLineByType(LT_ROOT, entry->lines); if (line && line->numElements >= 2) root=line->elements[1].item; } @@ -1351,8 +1755,7 @@ printf("root=%s\n", s); } - line = entry->lines; - while (line && line->type != LT_INITRD) line = line->next; + line = getLineByType(LT_INITRD, entry->lines); if (line && line->numElements >= 2) { printf("initrd=%s", prefix); @@ -1369,7 +1772,7 @@ char * start; char * param; - in = fopen("/etc/sysconfig/grub", "r"); + in = fopen("/etc/conf.d/grub", "r"); if (!in) return 1; if (lbaPtr) *lbaPtr = 0; @@ -1431,19 +1834,17 @@ return 1; } - /* this is a horrible hack to support /etc/sysconfig/grub; there must + /* this is a horrible hack to support /etc/conf.d/grub; there must be a better way */ if (config->cfi == &grubConfigType) { dumpSysconfigGrub(); } else { - line = config->theLines; - while (line && line->type != LT_BOOT) line = line->next; + line = getLineByType(LT_BOOT, config->theLines); if (line && line->numElements >= 1) { printf("boot=%s\n", line->elements[1].item); } - line = config->theLines; - while (line && line->type != LT_LBA) line = line->next; + line = getLineByType(LT_LBA, config->theLines); if (line) printf("lba\n"); } @@ -1458,77 +1859,140 @@ return 0; } +struct singleLine * addLineTmpl(struct singleEntry * entry, + struct singleLine * tmplLine, + struct singleLine * prevLine, + const char * val, + struct configFileInfo * cfi) +{ + struct singleLine * newLine = lineDup(tmplLine); + + if (val) { + /* override the inherited value with our own. + * This is a little weak because it only applies to elements[1] + */ + if (newLine->numElements > 1) + removeElement(newLine, 1); + 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)) { + char * rootspec = getRootSpecifier(tmplLine->elements[1].item); + if (rootspec != NULL) { + free(newLine->elements[1].item); + newLine->elements[1].item = + sdupprintf("%s%s", rootspec, val); + } + } + } + + dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ? + newLine->elements[0].item : ""); + + if (!entry->lines) { + /* first one on the list */ + entry->lines = newLine; + } else if (prevLine) { + /* add after prevLine */ + newLine->next = prevLine->next; + prevLine->next = newLine; + } + + return newLine; +} + /* val may be NULL */ struct singleLine * addLine(struct singleEntry * entry, struct configFileInfo * cfi, - enum lineType_e type, const char * defaultIndent, - char * val) { + enum lineType_e type, char * defaultIndent, + const char * val) { struct singleLine * line, * prev; - int i; + struct keywordTypes * kw; + struct singleLine tmpl; - for (i = 0; cfi->keywords[i].key; i++) - if (cfi->keywords[i].type == type) break; - if (type != LT_TITLE || !cfi->titleBracketed) - if (!cfi->keywords[i].key) abort(); + /* 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; + tmpl.numElements = 1; + tmpl.elements = alloca(sizeof(*tmpl.elements)); + tmpl.elements[0].item = alloca(strlen(val)+3); + 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) { + 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); + tmpl.elements[0].item = kw->key; + tmpl.elements[0].indent = alloca(2); + sprintf(tmpl.elements[0].indent, "%c", kw->nextChar); + if (val) { + tmpl.elements[1].item = (char *)val; + tmpl.elements[1].indent = ""; + } + } /* The last non-empty line gives us the indention to us and the line to insert after. Note that comments are considered empty lines, which may not be ideal? If there are no lines or we are looking at the first line, we use defaultIndent (the first line is normally indented differently from the rest) */ - if (entry->lines) { - line = entry->lines; - prev = NULL; - while (line) { - if (line->numElements) prev = line; - line = line->next; - } - if (!prev) { - /* just use the last line */ - prev = entry->lines; - while (prev->next) prev = prev->next; - } - - line = prev->next; - prev->next = malloc(sizeof(*line)); - prev->next->next = line; - line = prev->next; - - if (prev == entry->lines) - line->indent = strdup(defaultIndent); - else - line->indent = strdup(prev->indent); - } else { - line = malloc(sizeof(*line)); - line->indent = strdup(defaultIndent); - line->next = NULL; + for (line = entry->lines, prev = NULL; line; line = line->next) { + if (line->numElements) prev = line; + /* fall back on the last line if prev isn't otherwise set */ + if (!line->next && !prev) prev = line; } - if (type != LT_TITLE || !cfi->titleBracketed) { - line->type = type; - line->numElements = val ? 2 : 1; - line->elements = malloc(sizeof(*line->elements) * line->numElements); - line->elements[0].item = strdup(cfi->keywords[i].key); - line->elements[0].indent = malloc(2); - line->elements[0].indent[0] = cfi->keywords[i].nextChar; - line->elements[0].indent[1] = '\0'; - - if (val) { - line->elements[1].item = val; - line->elements[1].indent = strdup(""); - } - } else { - /* we're doing the title of a bracketed title (zipl) */ - line->type = type; - line->numElements = 1; - line->elements = malloc(sizeof(*line->elements) * line->numElements); - - line->elements[0].item = malloc(strlen(val) + 3); - sprintf(line->elements[0].item, "[%s]", val); - line->elements[0].indent = strdup(""); + 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 line; + return addLineTmpl(entry, &tmpl, prev, val, cfi); } void removeLine(struct singleEntry * entry, struct singleLine * line) { @@ -1553,6 +2017,154 @@ 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) +{ + struct keywordTypes * kw; + char indent[2] = ""; + + /* sanity check */ + if (insertHere > line->numElements) { + dbgPrintf("insertElement() adjusting insertHere from %d to %d\n", + insertHere, line->numElements); + insertHere = line->numElements; + } + + line->elements = realloc(line->elements, (line->numElements + 1) * + sizeof(*line->elements)); + memmove(&line->elements[insertHere+1], + &line->elements[insertHere], + (line->numElements - insertHere) * + sizeof(*line->elements)); + line->elements[insertHere].item = strdup(item); + + kw = getKeywordByType(line->type, cfi); + + if (line->numElements == 0) { + indent[0] = '\0'; + } else if (insertHere == 0) { + indent[0] = kw->nextChar; + } else if (kw->separatorChar != '\0') { + indent[0] = kw->separatorChar; + } else { + indent[0] = ' '; + } + + if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') { + /* move the end-of-line forward */ + line->elements[insertHere].indent = + line->elements[insertHere-1].indent; + line->elements[insertHere-1].indent = strdup(indent); + } else { + line->elements[insertHere].indent = strdup(indent); + } + + line->numElements++; + + dbgPrintf("insertElement(%s, '%s%s', %d)\n", + line->elements[0].item, + line->elements[insertHere].item, + line->elements[insertHere].indent, + insertHere); +} + +static void removeElement(struct singleLine * line, int removeHere) { + int i; + + /* sanity check */ + if (removeHere >= line->numElements) return; + + dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item, + removeHere, line->elements[removeHere].item); + + free(line->elements[removeHere].item); + + if (removeHere > 1) { + /* previous argument gets this argument's post-indentation */ + free(line->elements[removeHere-1].indent); + line->elements[removeHere-1].indent = + line->elements[removeHere].indent; + } else { + free(line->elements[removeHere].indent); + } + + /* now collapse the array, but don't bother to realloc smaller */ + for (i = removeHere; i < line->numElements - 1; i++) + line->elements[i] = line->elements[i + 1]; + + line->numElements--; +} + int argMatch(const char * one, const char * two) { char * first, * second; char * chptr; @@ -1575,14 +2187,13 @@ struct singleEntry * entry; struct singleLine * line, * rootLine; int index = 0; - int i, j, k; + int i, k; const char ** newArgs, ** oldArgs; const char ** arg; - const char * chptr; - int useKernelArgs = 0; - int useRoot = 0; + int useKernelArgs, useRoot; int firstElement; - int *usedElements, *usedArgs; + int *usedElements; + int doreplace; if (!image) return 0; @@ -1609,147 +2220,161 @@ } } - for (i = 0; cfg->cfi->keywords[i].key; i++) - if (cfg->cfi->keywords[i].type == LT_KERNELARGS) break; - if (cfg->cfi->keywords[i].key) - useKernelArgs = 1; + useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi) + && (!multibootArgs || cfg->cfi->mbConcatArgs)); - for (i = 0; cfg->cfi->keywords[i].key; i++) - if (cfg->cfi->keywords[i].type == LT_ROOT) break; + useRoot = (getKeywordByType(LT_ROOT, cfg->cfi) + && !multibootArgs); - if (cfg->cfi->keywords[i].key) - useRoot = 1; + for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) { - k = 0; - for (arg = newArgs; *arg; arg++) - k++; - usedArgs = calloc(k, sizeof(int)); + if (multibootArgs && !entry->multiboot) + continue; + + /* Determine where to put the args. If this config supports + * LT_KERNELARGS, use that. Otherwise use + * LT_HYPER/LT_KERNEL/LT_MBMODULE lines. + */ + if (useKernelArgs) { + line = getLineByType(LT_KERNELARGS, entry->lines); + if (!line) { + /* no LT_KERNELARGS, need to add it */ + line = addLine(entry, cfg->cfi, LT_KERNELARGS, + cfg->secondaryIndent, NULL); + } + firstElement = 1; - while ((entry = findEntryByPath(cfg, image, prefix, &index))) { - index++; + } else if (multibootArgs) { + line = getLineByType(LT_HYPER, entry->lines); + if (!line) { + /* a multiboot entry without LT_HYPER? */ + continue; + } + firstElement = 2; - line = entry->lines; - while (line && line->type != LT_KERNEL) line = line->next; - if (!line) continue; - firstElement = 2; - - if (entry->multiboot && !multibootArgs) { - /* first mb module line is the real kernel */ - while (line && line->type != LT_MBMODULE) line = line->next; - firstElement = 2; - } else if (useKernelArgs) { - while (line && line->type != LT_KERNELARGS) line = line->next; - firstElement = 1; + } else { + line = getLineByType(LT_KERNEL|LT_MBMODULE, entry->lines); + if (!line) { + /* no LT_KERNEL or LT_MBMODULE in this entry? */ + continue; + } + firstElement = 2; } - if (!line && useKernelArgs) { - /* no append in there, need to add it */ - line = addLine(entry, cfg->cfi, LT_KERNELARGS, NULL, NULL); + /* handle the elilo case which does: + * append="hypervisor args -- kernel args" + */ + if (entry->multiboot && cfg->cfi->mbConcatArgs) { + /* this is a multiboot entry, make sure there's + * -- on the args line + */ + for (i = firstElement; i < line->numElements; i++) { + if (!strcmp(line->elements[i].item, "--")) + break; + } + if (i == line->numElements) { + /* assume all existing args are kernel args, + * prepend -- to make it official + */ + insertElement(line, "--", firstElement, cfg->cfi); + i = firstElement; + } + if (!multibootArgs) { + /* kernel args start after the -- */ + firstElement = i + 1; + } + } else if (cfg->cfi->mbConcatArgs) { + /* this is a non-multiboot entry, remove hyper args */ + for (i = firstElement; i < line->numElements; i++) { + if (!strcmp(line->elements[i].item, "--")) + break; + } + if (i < line->numElements) { + /* remove args up to -- */ + while (strcmp(line->elements[firstElement].item, "--")) + removeElement(line, firstElement); + /* remove -- */ + removeElement(line, firstElement); + } } - usedElements = calloc(line->numElements, sizeof(int)); - - k = 0; - for (arg = newArgs; *arg; arg++) { - if (usedArgs[k]) { - k++; - continue; - } + usedElements = calloc(line->numElements, sizeof(*usedElements)); + + for (k = 0, arg = newArgs; *arg; arg++, k++) { + + doreplace = 1; for (i = firstElement; i < line->numElements; i++) { + if (multibootArgs && cfg->cfi->mbConcatArgs && + !strcmp(line->elements[i].item, "--")) + { + /* reached the end of hyper args, insert here */ + doreplace = 0; + break; + } if (usedElements[i]) continue; if (!argMatch(line->elements[i].item, *arg)) { usedElements[i]=1; - usedArgs[k]=1; break; } } - chptr = strchr(*arg, '='); - if (i < line->numElements) { - /* replace */ + if (i < line->numElements && doreplace) { + /* direct replacement */ free(line->elements[i].item); line->elements[i].item = strdup(*arg); - } else if (useRoot && !strncmp(*arg, "root=/dev/", 10) && *chptr) { - rootLine = entry->lines; - while (rootLine && rootLine->type != LT_ROOT) - rootLine = rootLine->next; - if (!rootLine) { - rootLine = addLine(entry, cfg->cfi, LT_ROOT, NULL, NULL); - rootLine->elements = realloc(rootLine->elements, - 2 * sizeof(*rootLine->elements)); - rootLine->numElements++; - rootLine->elements[1].indent = strdup(""); - rootLine->elements[1].item = strdup(""); - } - free(rootLine->elements[1].item); - rootLine->elements[1].item = strdup(chptr + 1); - } else { - /* append */ - line->elements = realloc(line->elements, - (line->numElements + 1) * sizeof(*line->elements)); - line->elements[line->numElements].item = strdup(*arg); - usedElements = realloc(usedElements, - (line->numElements + 1) * sizeof(int)); - usedElements[line->numElements] = 1; - - if (line->numElements > 1) { - /* add to existing list of arguments */ - line->elements[line->numElements].indent = - line->elements[line->numElements - 1].indent; - line->elements[line->numElements - 1].indent = strdup(" "); + } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) { + /* root= replacement */ + rootLine = getLineByType(LT_ROOT, entry->lines); + if (rootLine) { + free(rootLine->elements[1].item); + rootLine->elements[1].item = strdup(*arg + 5); } else { - /* First thing on this line; treat a bit differently. Note - this is only possible if we've added a LT_KERNELARGS - entry */ - line->elements[line->numElements].indent = strdup(""); + rootLine = addLine(entry, cfg->cfi, LT_ROOT, + cfg->secondaryIndent, *arg + 5); } + } - line->numElements++; + else { + /* insert/append */ + insertElement(line, *arg, i, cfg->cfi); + usedElements = realloc(usedElements, line->numElements * + sizeof(*usedElements)); + memmove(&usedElements[i + 1], &usedElements[i], + line->numElements - i - 1); + usedElements[i] = 1; /* if we updated a root= here even though there is a LT_ROOT available we need to remove the LT_ROOT entry (this will happen if we switch from a device to a label) */ if (useRoot && !strncmp(*arg, "root=", 5)) { - rootLine = entry->lines; - while (rootLine && rootLine->type != LT_ROOT) - rootLine = rootLine->next; - if (rootLine) { + rootLine = getLineByType(LT_ROOT, entry->lines); + if (rootLine) removeLine(entry, rootLine); - } } } - k++; } free(usedElements); - /* no arguments to remove (i.e. no append line) */ - if (!line) continue; - - /* this won't remove an LT_ROOT item properly (but then again, - who cares? */ for (arg = oldArgs; *arg; arg++) { - for (i = firstElement; i < line->numElements; i++) - if (!argMatch(line->elements[i].item, *arg)) + for (i = firstElement; i < line->numElements; i++) { + if (multibootArgs && cfg->cfi->mbConcatArgs && + !strcmp(line->elements[i].item, "--")) + /* reached the end of hyper args, stop here */ + break; + if (!argMatch(line->elements[i].item, *arg)) { + removeElement(line, i); break; - - if (i < line->numElements) { - /* if this isn't the first argument the previous argument - gets this arguments post-indention */ - if (i > firstElement) { - free(line->elements[i - 1].indent); - line->elements[i - 1].indent = line->elements[i].indent; } - - free(line->elements[i].item); - - for (j = i + 1; j < line->numElements; j++) - line->elements[j - 1] = line->elements[j]; - - line->numElements--; + } + /* handle removing LT_ROOT line too */ + if (useRoot && !strncmp(*arg, "root=", 5)) { + rootLine = getLineByType(LT_ROOT, entry->lines); + if (rootLine) + removeLine(entry, rootLine); } } @@ -1760,7 +2385,6 @@ } } - free(usedArgs); free(newArgs); free(oldArgs); @@ -1786,6 +2410,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]; @@ -1809,12 +2471,22 @@ if (memcmp(boot, bootSect, 3)) return 0; - if (boot[1] == 0xeb) { + if (boot[1] == JMP_SHORT_OPCODE) { offset = boot[2] + 2; } else if (boot[1] == 0xe8 || boot[1] == 0xe9) { offset = (boot[3] << 8) + boot[2] + 2; - } else if (boot[0] == 0xeb) { - offset = boot[1] + 2; + } else if (boot[0] == JMP_SHORT_OPCODE) { + offset = boot[1] + 2; + /* + * it looks like grub, when copying stage1 into the mbr, patches stage1 + * right after the JMP location, replacing other instructions such as + * JMPs for NOOPs. So, relax the check a little bit by skipping those + * different bytes. + */ + if ((bootSect[offset + 1] == NOOP_OPCODE) + && (bootSect[offset + 2] == NOOP_OPCODE)) { + offset = offset + 3; + } } else if (boot[0] == 0xe8 || boot[0] == 0xe9) { offset = (boot[2] << 8) + boot[1] + 2; } else { @@ -1956,6 +2628,13 @@ return checkDeviceBootloader(line->elements[1].item, boot); } +int checkForGrub2(struct grubConfig * config) { + if (!access("/etc/grub.d/", R_OK)) + return 2; + + return 1; +} + int checkForGrub(struct grubConfig * config) { int fd; unsigned char bootSect[512]; @@ -1976,6 +2655,37 @@ if (read(fd, bootSect, 512) != 512) { fprintf(stderr, _("grubby: unable to read %s: %s\n"), "/boot/grub/stage1", strerror(errno)); + close(fd); + return 1; + } + close(fd); + + return checkDeviceBootloader(boot, bootSect); +} + +int checkForExtLinux(struct grubConfig * config) { + int fd; + unsigned char bootSect[512]; + char * boot; + char executable[] = "/boot/extlinux/extlinux"; + + printf("entered: checkForExtLinux()\n"); + + if (parseSysconfigGrub(NULL, &boot)) + return 0; + + /* assume grub is not installed -- not an error condition */ + if (!boot) + return 0; + + fd = open(executable, O_RDONLY); + if (fd < 0) + /* this doesn't exist if grub hasn't been installed */ + return 0; + + if (read(fd, bootSect, 512) != 512) { + fprintf(stderr, _("grubby: unable to read %s: %s\n"), + executable, strerror(errno)); return 1; } close(fd); @@ -1994,19 +2704,62 @@ return rootspec; } +static char * getInitrdVal(struct grubConfig * config, + const char * prefix, struct singleLine *tmplLine, + const char * newKernelInitrd, + char ** extraInitrds, int extraInitrdCount) +{ + char *initrdVal, *end; + int i; + size_t totalSize; + size_t prefixLen; + char separatorChar; + + prefixLen = strlen(prefix); + totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */; + + for (i = 0; i < extraInitrdCount; i++) { + totalSize += sizeof(separatorChar); + totalSize += strlen(extraInitrds[i]) - prefixLen; + } + + initrdVal = end = malloc(totalSize); + + end = stpcpy (end, newKernelInitrd + prefixLen); + + separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar; + for (i = 0; i < extraInitrdCount; i++) { + const char *extraInitrd; + int j; + + extraInitrd = extraInitrds[i] + prefixLen; + /* Don't add entries that are already there */ + if (tmplLine != NULL) { + for (j = 2; j < tmplLine->numElements; j++) + if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0) + break; + + if (j != tmplLine->numElements) + continue; + } + + *end++ = separatorChar; + end = stpcpy(end, extraInitrd); + } + + return initrdVal; +} + int addNewKernel(struct grubConfig * config, struct singleEntry * template, const char * prefix, char * newKernelPath, char * newKernelTitle, char * newKernelArgs, char * newKernelInitrd, + char ** extraInitrds, int extraInitrdCount, char * newMBKernel, char * newMBKernelArgs) { struct singleEntry * new; - struct singleLine * newLine = NULL, * tmplLine = NULL, * lastLine = NULL; + struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL; int needs; - char * indent = NULL; - char * rootspec = NULL; char * chptr; - int i; - enum lineType_e type; if (!newKernelPath) return 0; @@ -2036,236 +2789,319 @@ config->entries = new; /* copy/update from the template */ - needs = KERNEL_KERNEL | KERNEL_INITRD | KERNEL_TITLE; + needs = NEED_KERNEL | NEED_TITLE; + if (newKernelInitrd) + needs |= NEED_INITRD; if (newMBKernel) { - needs |= KERNEL_MB; + needs |= NEED_MB; new->multiboot = 1; } if (template) { - for (tmplLine = template->lines; tmplLine; tmplLine = tmplLine->next) { - /* remember the indention level; we may need it for new lines */ - if (tmplLine->numElements) - indent = tmplLine->indent; + for (masterLine = template->lines; + masterLine && (tmplLine = lineDup(masterLine)); + lineFree(tmplLine), masterLine = masterLine->next) + { + dbgPrintf("addNewKernel processing %d\n", tmplLine->type); /* skip comments */ chptr = tmplLine->indent; while (*chptr && isspace(*chptr)) chptr++; if (*chptr == '#') continue; - /* we don't need an initrd here */ - if (tmplLine->type == LT_INITRD && !newKernelInitrd) continue; - - if (tmplLine->type == LT_KERNEL && - !template->multiboot && (needs & KERNEL_MB)) { - struct singleLine *l; - needs &= ~ KERNEL_MB; - - l = addLine(new, config->cfi, LT_KERNEL, - config->secondaryIndent, - newMBKernel + strlen(prefix)); - - tmplLine = lastLine; - if (!new->lines) { - new->lines = l; - } else { - newLine->next = l; - newLine = l; - } - continue; - } else if (tmplLine->type == LT_KERNEL && - template->multiboot && !new->multiboot) { - continue; /* don't need multiboot kernel here */ - } + if (tmplLine->type == LT_KERNEL && + 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 + * hypervisor at the same time. + */ + if (config->cfi->mbHyperFirst) { + /* insert the hypervisor first */ + newLine = addLine(new, config->cfi, LT_HYPER, + tmplLine->indent, + newMBKernel + strlen(prefix)); + /* set up for adding the kernel line */ + free(tmplLine->indent); + tmplLine->indent = strdup(config->secondaryIndent); + needs &= ~NEED_MB; + } + if (needs & NEED_KERNEL) { + /* use addLineTmpl to preserve line elements, + * otherwise we could just call addLine. Unfortunately + * this means making some changes to the template + * such as the indent change above and the type + * change below. + */ + struct keywordTypes * mbm_kw = + getKeywordByType(LT_MBMODULE, config->cfi); + if (mbm_kw) { + tmplLine->type = LT_MBMODULE; + free(tmplLine->elements[0].item); + tmplLine->elements[0].item = strdup(mbm_kw->key); + } + newLine = addLineTmpl(new, tmplLine, newLine, + newKernelPath + strlen(prefix), config->cfi); + needs &= ~NEED_KERNEL; + } + if (needs & NEED_MB) { /* !mbHyperFirst */ + newLine = addLine(new, config->cfi, LT_HYPER, + config->secondaryIndent, + newMBKernel + strlen(prefix)); + needs &= ~NEED_MB; + } + } else if (needs & NEED_KERNEL) { + newLine = addLineTmpl(new, tmplLine, newLine, + newKernelPath + strlen(prefix), config->cfi); + needs &= ~NEED_KERNEL; + } - if (!new->lines) { - newLine = malloc(sizeof(*newLine)); - new->lines = newLine; - } else { - newLine->next = malloc(sizeof(*newLine)); - newLine = newLine->next; - } + } else if (tmplLine->type == LT_HYPER && + tmplLine->numElements >= 2) { + if (needs & NEED_MB) { + newLine = addLineTmpl(new, tmplLine, newLine, + newMBKernel + strlen(prefix), config->cfi); + needs &= ~NEED_MB; + } + } else if (tmplLine->type == LT_MBMODULE && + tmplLine->numElements >= 2) { + if (new->multiboot) { + if (needs & NEED_KERNEL) { + newLine = addLineTmpl(new, tmplLine, newLine, + newKernelPath + + strlen(prefix), config->cfi); + needs &= ~NEED_KERNEL; + } else if (config->cfi->mbInitRdIsModule && + (needs & NEED_INITRD)) { + char *initrdVal; + initrdVal = getInitrdVal(config, prefix, tmplLine, + newKernelInitrd, extraInitrds, + extraInitrdCount); + newLine = addLineTmpl(new, tmplLine, newLine, + initrdVal, config->cfi); + free(initrdVal); + needs &= ~NEED_INITRD; + } + } else if (needs & NEED_KERNEL) { + /* template is multi but new is not, + * insert the kernel in the first module slot + */ + tmplLine->type = LT_KERNEL; + free(tmplLine->elements[0].item); + tmplLine->elements[0].item = + strdup(getKeywordByType(LT_KERNEL, config->cfi)->key); + newLine = addLineTmpl(new, tmplLine, newLine, + 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; + free(tmplLine->elements[0].item); + tmplLine->elements[0].item = + strdup(getKeywordByType(LT_INITRD, config->cfi)->key); + initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount); + newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi); + free(initrdVal); + needs &= ~NEED_INITRD; + } - newLine->indent = strdup(tmplLine->indent); - newLine->next = NULL; - newLine->type = tmplLine->type; - newLine->numElements = tmplLine->numElements; - newLine->elements = malloc(sizeof(*newLine->elements) * - newLine->numElements); - for (i = 0; i < newLine->numElements; i++) { - newLine->elements[i].item = strdup(tmplLine->elements[i].item); - newLine->elements[i].indent = - strdup(tmplLine->elements[i].indent); - } - - lastLine = tmplLine; - if (tmplLine->type == LT_KERNEL && tmplLine->numElements >= 2) { - char * repl; - if (!template->multiboot) { - needs &= ~KERNEL_KERNEL; - repl = newKernelPath; - } else { - needs &= ~KERNEL_MB; - repl = newMBKernel; - } - if (new->multiboot && !template->multiboot) { - free(newLine->elements[0].item); - newLine->elements[0].item = strdup("module"); - newLine->type = LT_MBMODULE; - } - free(newLine->elements[1].item); - rootspec = getRootSpecifier(tmplLine->elements[1].item); - if (rootspec != NULL) { - newLine->elements[1].item = sdupprintf("%s%s", - rootspec, - repl + - strlen(prefix)); - } else { - newLine->elements[1].item = strdup(repl + - strlen(prefix)); - } - } else if (tmplLine->type == LT_MBMODULE && - tmplLine->numElements >= 2 && (needs & KERNEL_KERNEL)) { - needs &= ~KERNEL_KERNEL; - if (!new->multiboot && template->multiboot) { - free(newLine->elements[0].item); - newLine->elements[0].item = strdup("kernel"); - newLine->type = LT_KERNEL; - } - free(newLine->elements[1].item); - rootspec = getRootSpecifier(tmplLine->elements[1].item); - if (rootspec != NULL) { - newLine->elements[1].item = sdupprintf("%s%s", - rootspec, - newKernelPath + - strlen(prefix)); - } else { - newLine->elements[1].item = strdup(newKernelPath + - strlen(prefix)); - } } else if (tmplLine->type == LT_INITRD && - tmplLine->numElements >= 2) { - needs &= ~KERNEL_INITRD; - free(newLine->elements[1].item); - if (new->multiboot && !template->multiboot) { - free(newLine->elements[0].item); - newLine->elements[0].item = strdup("module"); - newLine->type = LT_MBMODULE; - } - rootspec = getRootSpecifier(tmplLine->elements[1].item); - if (rootspec != NULL) { - newLine->elements[1].item = sdupprintf("%s%s", - rootspec, - newKernelInitrd + - strlen(prefix)); - } else { - newLine->elements[1].item = strdup(newKernelInitrd + - strlen(prefix)); - } - } else if (tmplLine->type == LT_MBMODULE && - tmplLine->numElements >= 2 && (needs & KERNEL_INITRD)) { - needs &= ~KERNEL_INITRD; - if (!new->multiboot && template->multiboot) { - free(newLine->elements[0].item); - newLine->elements[0].item = strdup("initrd"); - newLine->type = LT_INITRD; - } - free(newLine->elements[1].item); - rootspec = getRootSpecifier(tmplLine->elements[1].item); - if (rootspec != NULL) { - newLine->elements[1].item = sdupprintf("%s%s", - rootspec, - newKernelInitrd + - strlen(prefix)); - } else { - newLine->elements[1].item = strdup(newKernelInitrd + - strlen(prefix)); - } - } else if (tmplLine->type == LT_TITLE && - tmplLine->numElements >= 2) { - needs &= ~KERNEL_TITLE; - - for (i = 1; i < newLine->numElements; i++) { - free(newLine->elements[i].item); - free(newLine->elements[i].indent); + tmplLine->numElements >= 2) { + if (needs & NEED_INITRD && + new->multiboot && !template->multiboot && + config->cfi->mbInitRdIsModule) { + /* make sure we don't insert the module initrd + * before the module kernel... if we don't do it here, + * it will be inserted following the template. + */ + if (!needs & NEED_KERNEL) { + char *initrdVal; + + initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount); + newLine = addLine(new, config->cfi, LT_MBMODULE, + config->secondaryIndent, + initrdVal); + free(initrdVal); + needs &= ~NEED_INITRD; + } + } else if (needs & NEED_INITRD) { + char *initrdVal; + initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount); + newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi); + free(initrdVal); + needs &= ~NEED_INITRD; } - newLine->elements[1].item = strdup(newKernelTitle); - newLine->elements[1].indent = strdup(""); - newLine->numElements = 2; + } 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 && - config->cfi->titleBracketed && - tmplLine->numElements == 1) { - needs &= ~KERNEL_TITLE; - free(newLine->elements[0].item); - free(newLine->elements[0].indent); - newLine->elements = malloc(sizeof(*newLine->elements) * - newLine->numElements); - - newLine->elements[0].item = malloc(strlen(newKernelTitle) + 3); - sprintf(newLine->elements[0].item, "[%s]", newKernelTitle); - newLine->elements[0].indent = strdup(""); - newLine->numElements = 1; - } + (needs & NEED_TITLE)) { + if (tmplLine->numElements >= 2) { + newLine = addLineTmpl(new, tmplLine, newLine, + newKernelTitle, config->cfi); + needs &= ~NEED_TITLE; + } else if (tmplLine->numElements == 1 && + config->cfi->titleBracketed) { + /* addLineTmpl doesn't handle titleBracketed */ + newLine = addLine(new, config->cfi, LT_TITLE, + 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); + } } + } else { - for (i = 0; config->cfi->keywords[i].key; i++) { - if ((config->cfi->keywords[i].type == config->cfi->entrySeparator) || (config->cfi->keywords[i].type == LT_OTHER)) + /* don't have a template, so start the entry with the + * appropriate starting line + */ + switch (config->cfi->entryStart) { + case LT_KERNEL: + if (new->multiboot && config->cfi->mbHyperFirst) { + /* fall through to LT_HYPER */ + } else { + newLine = addLine(new, config->cfi, LT_KERNEL, + config->primaryIndent, + newKernelPath + strlen(prefix)); + needs &= ~NEED_KERNEL; + break; + } + + case LT_HYPER: + newLine = addLine(new, config->cfi, LT_HYPER, + config->primaryIndent, + newMBKernel + strlen(prefix)); + needs &= ~NEED_MB; break; - } - switch (config->cfi->keywords[i].type) { - case LT_KERNEL: needs &= ~KERNEL_KERNEL, - chptr = newKernelPath + strlen(prefix); - type = LT_KERNEL; break; - case LT_TITLE: needs &= ~KERNEL_TITLE, chptr = newKernelTitle; - type = LT_TITLE; break; - default: - /* zipl strikes again */ - if (config->cfi->titleBracketed) { - needs &= ~KERNEL_TITLE; - chptr = newKernelTitle; - type = LT_TITLE; - break; - } else { - abort(); - } - } + 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; + int x = 0, y = 0; + + templabel = strdup(newKernelTitle); + while( templabel[x]){ + if( templabel[x] == ' ' ){ + y = x; + while( templabel[y] ){ + templabel[y] = templabel[y+1]; + y++; + } + } + x++; + } + newLine = addLine(new, config->cfi, LT_TITLE, + config->primaryIndent, templabel); + free(templabel); + }else{ + newLine = addLine(new, config->cfi, LT_TITLE, + config->primaryIndent, newKernelTitle); + } + needs &= ~NEED_TITLE; + break; - newLine = addLine(new, config->cfi, type, config->primaryIndent, chptr); - new->lines = newLine; + default: + abort(); + } } - if (new->multiboot) { - if (needs & KERNEL_MB) - newLine = addLine(new, config->cfi, LT_KERNEL, - config->secondaryIndent, - newMBKernel + strlen(prefix)); - if (needs & KERNEL_KERNEL) - newLine = addLine(new, config->cfi, LT_MBMODULE, - config->secondaryIndent, - newKernelPath + strlen(prefix)); - /* don't need to check for title as it's guaranteed to have been - * done as we only do multiboot with grub which uses title as - * a separator */ - if (needs & KERNEL_INITRD && newKernelInitrd) - newLine = addLine(new, config->cfi, LT_MBMODULE, - config->secondaryIndent, - newKernelInitrd + strlen(prefix)); - } else { - if (needs & KERNEL_KERNEL) - newLine = addLine(new, config->cfi, LT_KERNEL, - config->secondaryIndent, - newKernelPath + strlen(prefix)); - if (needs & KERNEL_TITLE) - newLine = addLine(new, config->cfi, LT_TITLE, - config->secondaryIndent, - newKernelTitle); - if (needs & KERNEL_INITRD && newKernelInitrd) - newLine = addLine(new, config->cfi, LT_INITRD, - config->secondaryIndent, - newKernelInitrd + strlen(prefix)); + /* 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 entryStart. + */ + if (needs & NEED_TITLE) { + newLine = addLine(new, config->cfi, LT_TITLE, + config->secondaryIndent, + newKernelTitle); + needs &= ~NEED_TITLE; + } + if ((needs & NEED_MB) && config->cfi->mbHyperFirst) { + newLine = addLine(new, config->cfi, LT_HYPER, + config->secondaryIndent, + newMBKernel + strlen(prefix)); + needs &= ~NEED_MB; + } + if (needs & NEED_KERNEL) { + newLine = addLine(new, config->cfi, + (new->multiboot && getKeywordByType(LT_MBMODULE, + config->cfi)) ? + LT_MBMODULE : LT_KERNEL, + config->secondaryIndent, + newKernelPath + strlen(prefix)); + needs &= ~NEED_KERNEL; + } + if (needs & NEED_MB) { + newLine = addLine(new, config->cfi, LT_HYPER, + config->secondaryIndent, + newMBKernel + strlen(prefix)); + needs &= ~NEED_MB; + } + if (needs & NEED_INITRD) { + char *initrdVal; + 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->secondaryIndent, + initrdVal); + 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); + abort(); } if (updateImage(config, "0", prefix, newKernelArgs, NULL, @@ -2274,16 +3110,34 @@ return 0; } +static void traceback(int signum) +{ + void *array[40]; + size_t size; + + signal(SIGSEGV, SIG_DFL); + memset(array, '\0', sizeof (array)); + size = backtrace(array, 40); + + fprintf(stderr, "grubby recieved SIGSEGV! Backtrace (%ld):\n", + (unsigned long)size); + backtrace_symbols_fd(array, size, STDERR_FILENO); + exit(1); +} + 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; int bootloaderProbe = 0; + int extraInitrdCount = 0; char * updateKernelPath = NULL; char * newKernelPath = NULL; char * removeKernelPath = NULL; @@ -2299,12 +3153,14 @@ char * defaultKernel = NULL; char * removeArgs = NULL; char * kernelInfo = NULL; + char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL }; const char * chptr = NULL; struct configFileInfo * cfi = NULL; struct grubConfig * config; struct singleEntry * template = NULL; int copyDefault = 0, makeDefault = 0; int displayDefault = 0; + int displayDefaultIndex = 0; struct poptOption options[] = { { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0, _("add an entry for the specified kernel"), _("kernel-path") }, @@ -2337,15 +3193,23 @@ "template"), NULL }, { "default-kernel", 0, 0, &displayDefault, 0, _("display the path of the default kernel") }, + { "default-index", 0, 0, &displayDefaultIndex, 0, + _("display the index of the default kernel") }, { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0, _("configure elilo bootloader") }, + { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0, + _("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") }, { "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") }, { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0, _("configure lilo bootloader") }, { "make-default", 0, 0, &makeDefault, 0, @@ -2382,6 +3246,10 @@ { 0, 0, 0, 0, 0 } }; + useextlinuxmenu=0; + + signal(SIGSEGV, traceback); + optCon = poptGetContext("grubby", argc, argv, options, 0); poptReadDefaultConfig(optCon, 1); @@ -2391,6 +3259,14 @@ printf("grubby version %s\n", VERSION); exit(0); break; + case 'i': + if (extraInitrdCount < MAX_EXTRA_INITRDS) { + extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon)); + } else { + fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount); + return 1; + } + break; } } @@ -2406,14 +3282,17 @@ return 1; } - if ((configureLilo + configureGrub + configureELilo + - configureYaboot + configureSilo + configureZipl) > 1) { + if ((configureLilo + configureGrub2 + configureGrub + configureELilo + + configureYaboot + configureSilo + configureZipl + + configureExtLinux ) > 1) { fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n")); return 1; } else if (bootloaderProbe && grubConfig) { 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) { @@ -2426,6 +3305,9 @@ cfi = &siloConfigType; } else if (configureZipl) { cfi = &ziplConfigType; + } else if (configureExtLinux) { + cfi = &extlinuxConfigType; + useextlinuxmenu=1; } if (!cfi) { @@ -2440,16 +3322,23 @@ #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 || - defaultKernel)) { + defaultKernel || displayDefaultIndex)) { fprintf(stderr, _("grubby: --bootloader-probe may not be used with " "specified option")); return 1; @@ -2465,9 +3354,9 @@ if (newKernelPath && !newKernelTitle) { fprintf(stderr, _("grubby: kernel title must be specified\n")); return 1; - } else if (!newKernelPath && (newKernelTitle || newKernelInitrd || - newKernelInitrd || copyDefault || - makeDefault)) { + } else if (!newKernelPath && (newKernelTitle || copyDefault || + (newKernelInitrd && !updateKernelPath)|| + makeDefault || extraInitrdCount > 0)) { fprintf(stderr, _("grubby: kernel path expected\n")); return 1; } @@ -2492,7 +3381,7 @@ defaultKernel = NULL; } - if (!strcmp(grubConfig, "-") && !outputFile) { + if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) { fprintf(stderr, _("grubby: output file must be specified if stdin " "is used\n")); return 1; @@ -2500,7 +3389,7 @@ if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel && !kernelInfo && !bootloaderProbe && !updateKernelPath - && !removeMBKernel) { + && !removeMBKernel && !displayDefaultIndex) { fprintf(stderr, _("grubby: no action specified\n")); return 1; } @@ -2520,12 +3409,28 @@ bootPrefix = ""; } + if (!cfi->mbAllowExtraInitRds && + extraInitrdCount > 0) { + fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig); + return 1; + } + if (bootloaderProbe) { - int lrc = 0, grc = 0; + int lrc = 0, grc = 0, gr2c = 0, erc = 0; struct grubConfig * lconfig, * gconfig; - if (!access(grubConfigType.defaultConfig, F_OK)) { - gconfig = readConfig(grubConfigType.defaultConfig, &grubConfigType); + const char *grub2config = grub2FindConfig(&grub2ConfigType); + if (grub2config) { + gconfig = readConfig(grub2config, &grub2ConfigType); + if (!gconfig) + gr2c = 1; + else + gr2c = checkForGrub2(gconfig); + } + + const char *grubconfig = grubFindConfig(&grubConfigType); + if (!access(grubconfig, F_OK)) { + gconfig = readConfig(grubconfig, &grubConfigType); if (!gconfig) grc = 1; else @@ -2540,10 +3445,20 @@ lrc = checkForLilo(lconfig); } - if (lrc == 1 || grc == 1) return 1; + if (!access(extlinuxConfigType.defaultConfig, F_OK)) { + lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType); + if (!lconfig) + erc = 1; + else + erc = checkForExtLinux(lconfig); + } + + 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"); return 0; } @@ -2561,8 +3476,7 @@ if (!entry) return 0; if (!suitableImage(entry, bootPrefix, 0, flags)) return 0; - line = entry->lines; - while (line && line->type != LT_KERNEL) line = line->next; + line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines); if (!line) return 0; rootspec = getRootSpecifier(line->elements[1].item); @@ -2570,6 +3484,11 @@ ((rootspec != NULL) ? strlen(rootspec) : 0)); return 0; + + } else if (displayDefaultIndex) { + if (config->defaultImage == -1) return 0; + printf("%i\n", config->defaultImage); + } else if (kernelInfo) return displayInfo(config, kernelInfo, bootPrefix); @@ -2585,8 +3504,13 @@ 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, newMBKernel, newMBKernelArgs)) return 1; @@ -2597,7 +3521,7 @@ } if (!outputFile) - outputFile = grubConfig; + outputFile = (char *)grubConfig; return writeConfig(config, outputFile, bootPrefix); }