--- trunk/mkinitrd-magellan/grubby/grubby.c 2007/09/01 22:45:15 532 +++ trunk/mkinitrd-magellan/grubby/grubby.c 2009/10/28 00:16:16 914 @@ -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,11 +31,22 @@ #include #include #include +#include +#include +#include +#include + +#define DEBUG 0 -#include "mount_by_label.h" +#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 */ /* comments get lumped in with indention */ @@ -38,9 +55,24 @@ 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_UNKNOWN = 1 << 15, +}; struct singleLine { char * indent; @@ -61,11 +93,12 @@ #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 MAIN_DEFAULT (1 << 0) #define DEFAULT_SAVED -2 @@ -74,7 +107,8 @@ char * key; enum lineType_e type; char nextChar; -} ; + char separatorChar; +}; struct configFileInfo { char * defaultConfig; @@ -86,6 +120,10 @@ int argsInQuotes; int maxTitleLength; int titleBracketed; + int mbHyperFirst; + int mbInitRdIsModule; + int mbConcatArgs; + int mbAllowExtraInitRds; }; struct keywordTypes grubKeywords[] = { @@ -94,13 +132,14 @@ { "default", LT_DEFAULT, ' ' }, { "fallback", LT_FALLBACK, ' ' }, { "kernel", LT_KERNEL, ' ' }, - { "initrd", LT_INITRD, ' ' }, + { "initrd", LT_INITRD, ' ', ' ' }, { "module", LT_MBMODULE, ' ' }, + { "kernel", LT_HYPER, ' ' }, { NULL, 0, 0 }, }; struct configFileInfo grubConfigType = { - "/boot/grub/grub.conf", /* defaultConfig */ + "/etc/grub.conf", /* defaultConfig */ grubKeywords, /* keywords */ 1, /* defaultIsIndex */ 1, /* defaultSupportSaved */ @@ -109,6 +148,10 @@ 0, /* argsInQuotes */ 0, /* maxTitleLength */ 0, /* titleBracketed */ + 1, /* mbHyperFirst */ + 1, /* mbInitRdIsModule */ + 0, /* mbConcatArgs */ + 1, /* mbAllowExtraInitRds */ }; struct keywordTypes yabootKeywords[] = { @@ -142,7 +185,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 +205,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,9 +237,20 @@ { 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 */ + eliloKeywords, /* keywords */ 0, /* defaultIsIndex */ 0, /* defaultSupportSaved */ LT_KERNEL, /* entrySeparator */ @@ -193,6 +258,10 @@ 1, /* argsInQuotes */ 0, /* maxTitleLength */ 0, /* titleBracketed */ + 0, /* mbHyperFirst */ + 0, /* mbInitRdIsModule */ + 1, /* mbConcatArgs */ + 0, /* mbAllowExtraInitRds */ }; struct configFileInfo liloConfigType = { @@ -205,6 +274,10 @@ 1, /* argsInQuotes */ 15, /* maxTitleLength */ 0, /* titleBracketed */ + 0, /* mbHyperFirst */ + 0, /* mbInitRdIsModule */ + 0, /* mbConcatArgs */ + 0, /* mbAllowExtraInitRds */ }; struct configFileInfo yabootConfigType = { @@ -217,6 +290,10 @@ 1, /* argsInQuotes */ 15, /* maxTitleLength */ 0, /* titleBracketed */ + 0, /* mbHyperFirst */ + 0, /* mbInitRdIsModule */ + 0, /* mbConcatArgs */ + 1, /* mbAllowExtraInitRds */ }; struct configFileInfo siloConfigType = { @@ -229,6 +306,10 @@ 1, /* argsInQuotes */ 15, /* maxTitleLength */ 0, /* titleBracketed */ + 0, /* mbHyperFirst */ + 0, /* mbInitRdIsModule */ + 0, /* mbConcatArgs */ + 0, /* mbAllowExtraInitRds */ }; struct configFileInfo ziplConfigType = { @@ -239,8 +320,28 @@ LT_TITLE, /* entrySeparator */ 0, /* needsBootPrefix */ 1, /* argsInQuotes */ - 15, /* maxTitleLength */ + 0, /* maxTitleLength */ 1, /* titleBracketed */ + 0, /* mbHyperFirst */ + 0, /* mbInitRdIsModule */ + 0, /* mbConcatArgs */ + 0, /* mbAllowExtraInitRds */ +}; + +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 */ }; struct grubConfig { @@ -255,30 +356,39 @@ struct configFileInfo * cfi; }; - 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 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 +423,49 @@ 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 * getpathbyspec(char *device) { + static blkid_cache blkid; + + if (!blkid) + blkid_get_cache(&blkid, NULL); + + return blkid_get_devname(blkid, device, NULL); +} + +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 +477,10 @@ return 0; } -/* figure out if this is a entry separator */ static int isEntrySeparator(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->entrySeparator || line->type == LT_OTHER || + (cfi->titleBracketed && isBracketedTitle(line)); } /* extract the title from within brackets (for zipl) */ @@ -389,6 +531,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 +575,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 +595,7 @@ char * chptr; int elementsAlloced = 0; struct lineElement * element; - struct keywordTypes * keywords = cfi->keywords; int first = 1; - int i; lineFree(line); @@ -489,14 +649,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 +686,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; + } + } + } } } @@ -614,11 +821,39 @@ 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 +878,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 +888,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 +915,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 +931,13 @@ entry->lines = line; else last->next = line; + dbgPrintf("readConfig added %d to %p\n", line->type, entry); } else { if (!cfg->theLines) cfg->theLines = line; - else { + else last->next = line; - } + dbgPrintf("readConfig added %d to cfg\n", line->type); } last = line; @@ -730,10 +967,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; @@ -769,8 +1013,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 +1047,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); @@ -919,15 +1162,12 @@ int i; struct stat sb, sb2; char * dev; - char * end; char * rootspec; - 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 +1175,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 +1184,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 +1208,18 @@ } } - 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; + + i = stat(dev, &sb); + if (i) + return 0; - if (*dev == '/') { - if (stat(dev, &sb)) - return 0; - } else { - sb.st_rdev = strtol(dev, &end, 16); - if (*end) return 0; - } stat("/", &sb2); - if (sb.st_rdev != sb2.st_dev) return 0; + if (sb.st_rdev != sb2.st_dev) + return 0; return 1; } @@ -1034,10 +1263,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 +1305,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 +1501,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 +1526,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 +1550,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 +1565,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); @@ -1436,14 +1649,12 @@ 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 +1669,102 @@ 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 { + kw = getKeywordByType(type, cfi); + if (!kw) 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(""); - } + 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 +1789,85 @@ free(line); } +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 +1890,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 doreplace; if (!image) return 0; @@ -1609,54 +1923,102 @@ } } - 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 (k = 0, arg = newArgs; *arg; arg++, k++) ; + usedArgs = calloc(k, sizeof(*usedArgs)); - k = 0; - for (arg = newArgs; *arg; arg++) - k++; - usedArgs = calloc(k, sizeof(int)); + for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) { + + 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++) { + if (usedArgs[k]) continue; + + 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)) { @@ -1665,91 +2027,62 @@ 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); } } @@ -1976,6 +2309,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 +2358,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 +2443,274 @@ 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_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 { + /* 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->entrySeparator) { + 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_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 entrySeparator. + */ + 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) { + printf(_("grubby: needs=%d, aborting\n"), needs); + abort(); } if (updateImage(config, "0", prefix, newKernelArgs, NULL, @@ -2274,6 +2719,21 @@ 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; @@ -2283,7 +2743,9 @@ int badImageOkay = 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,6 +2761,7 @@ 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; @@ -2339,6 +2802,8 @@ _("display the path 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") }, { "info", 0, POPT_ARG_STRING, &kernelInfo, 0, @@ -2346,6 +2811,8 @@ _("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 +2849,10 @@ { 0, 0, 0, 0, 0 } }; + useextlinuxmenu=0; + + signal(SIGSEGV, traceback); + optCon = poptGetContext("grubby", argc, argv, options, 0); poptReadDefaultConfig(optCon, 1); @@ -2391,6 +2862,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; } } @@ -2407,7 +2886,8 @@ } if ((configureLilo + configureGrub + configureELilo + - configureYaboot + configureSilo + configureZipl) > 1) { + configureYaboot + configureSilo + configureZipl + + configureExtLinux ) > 1) { fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n")); return 1; } else if (bootloaderProbe && grubConfig) { @@ -2426,6 +2906,9 @@ cfi = &siloConfigType; } else if (configureZipl) { cfi = &ziplConfigType; + } else if (configureExtLinux) { + cfi = &extlinuxConfigType; + useextlinuxmenu=1; } if (!cfi) { @@ -2467,7 +2950,7 @@ return 1; } else if (!newKernelPath && (newKernelTitle || newKernelInitrd || newKernelInitrd || copyDefault || - makeDefault)) { + makeDefault || extraInitrdCount > 0)) { fprintf(stderr, _("grubby: kernel path expected\n")); return 1; } @@ -2520,8 +3003,14 @@ 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, erc = 0; struct grubConfig * lconfig, * gconfig; if (!access(grubConfigType.defaultConfig, F_OK)) { @@ -2540,10 +3029,19 @@ lrc = checkForLilo(lconfig); } + if (!access(extlinuxConfigType.defaultConfig, F_OK)) { + lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType); + if (!lconfig) + erc = 1; + else + erc = checkForExtLinux(lconfig); + } + if (lrc == 1 || grc == 1) return 1; if (lrc == 2) printf("lilo\n"); if (grc == 2) printf("grub\n"); + if (erc == 2) printf("extlinux\n"); return 0; } @@ -2561,8 +3059,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); @@ -2587,6 +3084,7 @@ removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1; if (addNewKernel(config, template, bootPrefix, newKernelPath, newKernelTitle, newKernelArgs, newKernelInitrd, + extraInitrds, extraInitrdCount, newMBKernel, newMBKernelArgs)) return 1;