--- trunk/grubby/grubby.c 2012/02/18 00:52:28 1721 +++ trunk/grubby/grubby.c 2012/07/02 12:59:07 1844 @@ -46,6 +46,8 @@ #define dbgPrintf(format, args...) #endif +int debug = 0; /* Currently just for template debugging */ + #define _(A) (A) #define MAX_EXTRA_INITRDS 16 /* code segment checked by --bootloader-probe */ @@ -112,6 +114,7 @@ #define MAIN_DEFAULT (1 << 0) #define DEFAULT_SAVED -2 +#define DEFAULT_SAVED_GRUB2 -3 struct keywordTypes { char * key; @@ -239,11 +242,96 @@ return configFiles[i]; } +int sizeOfSingleLine(struct singleLine * line) { + int i; + int count = 0; + + for (i = 0; i < line->numElements; i++) { + int indentSize = 0; + + count = count + strlen(line->elements[i].item); + + indentSize = strlen(line->elements[i].indent); + if (indentSize > 0) + count = count + indentSize; + else + /* be extra safe and add room for whitespaces */ + count = count + 1; + } + + /* room for trailing terminator */ + count = count + 1; + + return count; +} + +static int isquote(char q) +{ + if (q == '\'' || q == '\"') + return 1; + return 0; +} + +char *grub2ExtractTitle(struct singleLine * line) { + char * current; + char * current_indent; + int current_len; + int current_indent_len; + int i; + + /* bail out if line does not start with menuentry */ + if (strcmp(line->elements[0].item, "menuentry")) + return NULL; + + i = 1; + current = line->elements[i].item; + current_len = strlen(current); + + /* if second word is quoted, strip the quotes and return single word */ + if (isquote(*current) && isquote(current[current_len - 1])) { + char *tmp; + + tmp = strdup(current); + *(tmp + current_len - 1) = '\0'; + return ++tmp; + } + + /* if no quotes, return second word verbatim */ + if (!isquote(*current)) + return current; + + /* second element start with a quote, so we have to find the element + * whose last character is also quote (assuming it's the closing one) */ + int resultMaxSize; + char * result; + + resultMaxSize = sizeOfSingleLine(line); + result = malloc(resultMaxSize); + snprintf(result, resultMaxSize, "%s", ++current); + + i++; + for (; i < line->numElements; ++i) { + current = line->elements[i].item; + current_len = strlen(current); + current_indent = line->elements[i].indent; + current_indent_len = strlen(current_indent); + + strncat(result, current_indent, current_indent_len); + if (!isquote(current[current_len-1])) { + strncat(result, current, current_len); + } else { + strncat(result, current, current_len - 1); + break; + } + } + return result; +} + struct configFileInfo grub2ConfigType = { .findConfig = grub2FindConfig, .keywords = grub2Keywords, .defaultIsIndex = 1, - .defaultSupportSaved = 0, + .defaultSupportSaved = 1, .defaultIsVariable = 1, .entryStart = LT_MENUENTRY, .entryEnd = LT_ENTRY_END, @@ -643,6 +731,18 @@ if (fprintf(out, "%s", line->indent) == -1) return -1; for (i = 0; i < line->numElements; i++) { + /* Need to handle this, because we strip the quotes from + * menuentry when read it. */ + if (line->type == LT_MENUENTRY && i == 1) { + if(!isquote(*line->elements[i].item)) + fprintf(out, "\'%s\'", line->elements[i].item); + else + fprintf(out, "%s", line->elements[i].item); + fprintf(out, "%s", line->elements[i].indent); + + continue; + } + if (i == 1 && line->type == LT_KERNELARGS && cfi->argsInQuotes) if (fputc('"', out) == EOF) return -1; @@ -801,13 +901,11 @@ break; } - free(line->elements[i].indent); + line->elements[i + 1].indent = 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; } } } @@ -874,7 +972,7 @@ cfg->secondaryIndent = strdup(line->indent); } - if (isEntryStart(line, cfi)) { + if (isEntryStart(line, cfi) || (cfg->entries && !sawEntry)) { sawEntry = 1; if (!entry) { cfg->entries = malloc(sizeof(*entry)); @@ -895,7 +993,7 @@ 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("\"%s\"%s", line->elements[i].item, line->elements[i].indent); dbgPrintf("\n"); struct keywordTypes *kwType = getKeywordByType(LT_DEFAULT, cfi); if (kwType && line->numElements == 3 && @@ -964,7 +1062,71 @@ line->elements[line->numElements - 1].indent; line->elements[1].item = buf; line->numElements = 2; + } else if (line->type == LT_MENUENTRY && line->numElements > 3) { + /* let --remove-kernel="TITLE=what" work */ + len = 0; + char *extras; + char *title; + + for (i = 1; i < line->numElements; i++) { + len += strlen(line->elements[i].item); + len += strlen(line->elements[i].indent); + } + buf = malloc(len + 1); + *buf = '\0'; + + /* allocate mem for extra flags. */ + extras = malloc(len + 1); + *extras = '\0'; + + /* get title. */ + for (i = 0; i < line->numElements; i++) { + if (!strcmp(line->elements[i].item, "menuentry")) + continue; + if (isquote(*line->elements[i].item)) + title = line->elements[i].item + 1; + else + title = line->elements[i].item; + + len = strlen(title); + if (isquote(title[len-1])) { + strncat(buf, title,len-1); + break; + } else { + strcat(buf, title); + strcat(buf, line->elements[i].indent); + } + } + /* get extras */ + int count = 0; + for (i = 0; i < line->numElements; i++) { + if (count >= 2) { + strcat(extras, line->elements[i].item); + strcat(extras, line->elements[i].indent); + } + + if (!strcmp(line->elements[i].item, "menuentry")) + continue; + + /* count ' or ", there should be two in menuentry line. */ + if (isquote(*line->elements[i].item)) + count++; + + len = strlen(line->elements[i].item); + + if (isquote(line->elements[i].item[len -1])) + count++; + + /* ok, we get the final ' or ", others are extras. */ + } + line->elements[1].indent = + line->elements[line->numElements - 2].indent; + line->elements[1].item = buf; + line->elements[2].indent = + line->elements[line->numElements - 2].indent; + line->elements[2].item = extras; + line->numElements = 3; } 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 @@ -973,13 +1135,13 @@ if (line->numElements >= 2) { int last, len; - if (*line->elements[1].item == '"') + if (isquote(*line->elements[1].item)) 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] == '"') + if (isquote(line->elements[last].item[len])) line->elements[last].item[len] = '\0'; } } @@ -1038,7 +1200,11 @@ dbgPrintf("defaultLine is %s\n", defaultLine ? "set" : "unset"); if (defaultLine) { - if (cfi->defaultIsVariable) { + if (defaultLine->numElements > 2 && + cfi->defaultSupportSaved && + !strncmp(defaultLine->elements[2].item,"\"${saved_entry}\"", 16)) { + cfg->defaultImage = DEFAULT_SAVED_GRUB2; + } else if (cfi->defaultIsVariable) { char *value = defaultLine->elements[2].item; while (*value && (*value == '"' || *value == '\'' || *value == ' ' || *value == '\t')) @@ -1095,6 +1261,8 @@ if (cfg->defaultImage == DEFAULT_SAVED) fprintf(out, "%sdefault%ssaved\n", indent, separator); + else if (cfg->defaultImage == DEFAULT_SAVED_GRUB2) + fprintf(out, "%sset default=\"${saved_entry}\"\n", indent); else if (cfg->defaultImage > -1) { if (cfg->cfi->defaultIsIndex) { if (cfg->cfi->defaultIsVariable) { @@ -1293,6 +1461,8 @@ buf[rc] = '\0'; chptr = buf; + char *foundanswer = NULL; + while (chptr && chptr != buf+rc) { devname = chptr; @@ -1320,13 +1490,8 @@ * 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); + /* remember the last / entry in mtab */ + foundanswer = devname; } /* Next line */ @@ -1335,9 +1500,69 @@ chptr++; } + /* Return the last / entry found */ + if (foundanswer) { + chptr = strchr(foundanswer, ' '); + *chptr = '\0'; + return strdup(foundanswer); + } + return NULL; } +void printEntry(struct singleEntry * entry) { + int i; + struct singleLine * line; + + for (line = entry->lines; line; line = line->next) { + fprintf(stderr, "DBG: %s", line->indent); + for (i = 0; i < line->numElements; i++) { + /* Need to handle this, because we strip the quotes from + * menuentry when read it. */ + if (line->type == LT_MENUENTRY && i == 1) { + if(!isquote(*line->elements[i].item)) + fprintf(stderr, "\'%s\'", line->elements[i].item); + else + fprintf(stderr, "%s", line->elements[i].item); + fprintf(stderr, "%s", line->elements[i].indent); + + continue; + } + + fprintf(stderr, "%s%s", + line->elements[i].item, line->elements[i].indent); + } + fprintf(stderr, "\n"); + } +} + +void notSuitablePrintf(struct singleEntry * entry, const char *fmt, ...) +{ + va_list argp; + + if (!debug) + return; + + va_start(argp, fmt); + fprintf(stderr, "DBG: Image entry failed: "); + vfprintf(stderr, fmt, argp); + printEntry(entry); + va_end(argp); +} + +#define beginswith(s, c) ((s) && (s)[0] == (c)) + +static int endswith(const char *s, char c) +{ + int slen; + + if (!s || !s[0]) + return 0; + slen = strlen(s) - 1; + + return s[slen] == c; +} + int suitableImage(struct singleEntry * entry, const char * bootPrefix, int skipRemoved, int flags) { struct singleLine * line; @@ -1347,20 +1572,36 @@ char * rootspec; char * rootdev; - if (skipRemoved && entry->skip) return 0; + if (skipRemoved && entry->skip) { + notSuitablePrintf(entry, "marked to skip\n"); + return 0; + } line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines); - if (!line || line->numElements < 2) return 0; + if (!line) { + notSuitablePrintf(entry, "no line found\n"); + return 0; + } + if (line->numElements < 2) { + notSuitablePrintf(entry, "line has only %d elements\n", + line->numElements); + return 0; + } if (flags & GRUBBY_BADIMAGE_OKAY) return 1; fullName = alloca(strlen(bootPrefix) + strlen(line->elements[1].item) + 1); rootspec = getRootSpecifier(line->elements[1].item); - sprintf(fullName, "%s%s", bootPrefix, - line->elements[1].item + (rootspec ? strlen(rootspec) : 0)); - if (access(fullName, R_OK)) return 0; - + int rootspec_offset = rootspec ? strlen(rootspec) : 0; + int hasslash = endswith(bootPrefix, '/') || + beginswith(line->elements[1].item + rootspec_offset, '/'); + sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/", + line->elements[1].item + rootspec_offset); + if (access(fullName, R_OK)) { + notSuitablePrintf(entry, "access to %s failed\n", fullName); + return 0; + } for (i = 2; i < line->numElements; i++) if (!strncasecmp(line->elements[i].item, "root=", 5)) break; if (i < line->numElements) { @@ -1378,13 +1619,17 @@ line = getLineByType(LT_KERNELARGS|LT_MBMODULE, entry->lines); /* failed to find one */ - if (!line) return 0; + if (!line) { + notSuitablePrintf(entry, "no line found\n"); + return 0; + } for (i = 1; i < line->numElements; i++) if (!strncasecmp(line->elements[i].item, "root=", 5)) break; if (i < line->numElements) dev = line->elements[i].item + 5; else { + notSuitablePrintf(entry, "no root= entry found\n"); /* it failed too... can't find root= */ return 0; } @@ -1392,19 +1637,28 @@ } dev = getpathbyspec(dev); - if (!dev) + if (!getpathbyspec(dev)) { + notSuitablePrintf(entry, "can't find blkid entry for %s\n", dev); return 0; + } else + dev = getpathbyspec(dev); rootdev = findDiskForRoot(); - if (!rootdev) + if (!rootdev) { + notSuitablePrintf(entry, "can't find root device\n"); return 0; + } if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) { + notSuitablePrintf(entry, "uuid missing: rootdev %s, dev %s\n", + getuuidbydev(rootdev), getuuidbydev(dev)); free(rootdev); return 0; } if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) { + notSuitablePrintf(entry, "uuid mismatch: rootdev %s, dev %s\n", + getuuidbydev(rootdev), getuuidbydev(dev)); free(rootdev); return 0; } @@ -1491,7 +1745,7 @@ if (!strncmp(kernel, "TITLE=", 6)) { prefix = ""; - checkType = LT_TITLE; + checkType = LT_TITLE|LT_MENUENTRY; kernel += 6; } @@ -1507,13 +1761,17 @@ checkType, line); if (!line) break; /* not found in this entry */ - if (line && line->numElements >= 2) { + if (line && line->type != LT_MENUENTRY && + line->numElements >= 2) { rootspec = getRootSpecifier(line->elements[1].item); if (!strcmp(line->elements[1].item + ((rootspec != NULL) ? strlen(rootspec) : 0), kernel + strlen(prefix))) break; } + if(line->type == LT_MENUENTRY && + !strcmp(line->elements[1].item, kernel)) + break; } /* make sure this entry has a kernel identifier; this skips @@ -1605,7 +1863,16 @@ const char * prefix) { struct singleEntry * entry; - if (!image) return; + if (!image) + return; + + /* check and see if we're removing the default image */ + if (isdigit(*image)) { + entry = findEntryByPath(cfg, image, prefix, NULL); + if(entry) + entry->skip = 1; + return; + } while ((entry = findEntryByPath(cfg, image, prefix, NULL))) entry->skip = 1; @@ -1632,7 +1899,8 @@ /* defaultImage now points to what we'd like to use, but before any order changes */ - if (config->defaultImage == DEFAULT_SAVED) + if ((config->defaultImage == DEFAULT_SAVED) || + (config->defaultImage == DEFAULT_SAVED_GRUB2)) /* default is set to saved, we don't want to change it */ return; @@ -1699,7 +1967,7 @@ return; } - printf("kernel=%s\n", line->elements[1].item); + printf("kernel=%s%s\n", prefix, line->elements[1].item); if (line->numElements >= 3) { printf("args=\""); @@ -1763,6 +2031,17 @@ printf("%s%s", line->elements[i].item, line->elements[i].indent); printf("\n"); } + + line = getLineByType(LT_TITLE, entry->lines); + if (line) { + printf("title=%s\n", line->elements[1].item); + } else { + char * title; + line = getLineByType(LT_MENUENTRY, entry->lines); + title = grub2ExtractTitle(line); + if (title) + printf("title=%s\n", title); + } } int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) { @@ -2017,13 +2296,6 @@ 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 = { @@ -2707,7 +2979,7 @@ static char * getInitrdVal(struct grubConfig * config, const char * prefix, struct singleLine *tmplLine, const char * newKernelInitrd, - char ** extraInitrds, int extraInitrdCount) + const char ** extraInitrds, int extraInitrdCount) { char *initrdVal, *end; int i; @@ -2752,10 +3024,10 @@ 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) { + const char * newKernelPath, const char * newKernelTitle, + const char * newKernelArgs, const char * newKernelInitrd, + const char ** extraInitrds, int extraInitrdCount, + const char * newMBKernel, const char * newMBKernelArgs) { struct singleEntry * new; struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL; int needs; @@ -2961,9 +3233,10 @@ } } else if (tmplLine->type == LT_ECHO) { requote(tmplLine, config->cfi); + static const char *prefix = "'Loading "; if (tmplLine->numElements > 1 && - strstr(tmplLine->elements[1].item, "'Loading Linux ")) { - char *prefix = "'Loading "; + strstr(tmplLine->elements[1].item, prefix) && + masterLine->next && masterLine->next->type == LT_KERNEL) { char *newTitle = malloc(strlen(prefix) + strlen(newKernelTitle) + 2); @@ -3192,6 +3465,8 @@ "the kernel referenced by the default image does not exist, " "the first linux entry whose kernel does exist is used as the " "template"), NULL }, + { "debug", 0, 0, &debug, 0, + _("print debugging information for failures") }, { "default-kernel", 0, 0, &displayDefault, 0, _("display the path of the default kernel") }, { "default-index", 0, 0, &displayDefaultIndex, 0, @@ -3314,20 +3589,20 @@ } if (!cfi) { + if (grub2FindConfig(&grub2ConfigType)) + cfi = &grub2ConfigType; + else #ifdef __ia64__ - cfi = &eliloConfigType; + cfi = &eliloConfigType; #elif __powerpc__ - cfi = &yabootConfigType; + cfi = &yabootConfigType; #elif __sparc__ - cfi = &siloConfigType; + cfi = &siloConfigType; #elif __s390__ - cfi = &ziplConfigType; + cfi = &ziplConfigType; #elif __s390x__ - cfi = &ziplConfigtype; + cfi = &ziplConfigtype; #else - if (grub2FindConfig(&grub2ConfigType)) - cfi = &grub2ConfigType; - else cfi = &grubConfigType; #endif } @@ -3502,37 +3777,14 @@ printf("%s\n", line->elements[1].item); } else { - int i; - size_t len; - char * start; - char * tmp; + char * title; dbgPrintf("This is GRUB2, default title is embeded in menuentry\n"); line = getLineByType(LT_MENUENTRY, entry->lines); if (!line) return 0; - - for (i = 0; i < line->numElements; i++) { - - if (!strcmp(line->elements[i].item, "menuentry")) - continue; - - if (*line->elements[i].item == '\'') - start = line->elements[i].item + 1; - else - start = line->elements[i].item; - - len = strlen(start); - if (*(start + len - 1) == '\'') { - tmp = strdup(start); - *(tmp + len - 1) = '\0'; - printf("%s", tmp); - free(tmp); - break; - } else { - printf("%s ", start); - } - } - printf("\n"); + title = grub2ExtractTitle(line); + if (title) + printf("%s\n", title); } return 0; @@ -3561,7 +3813,7 @@ } if (addNewKernel(config, template, bootPrefix, newKernelPath, newKernelTitle, newKernelArgs, newKernelInitrd, - extraInitrds, extraInitrdCount, + (const char **)extraInitrds, extraInitrdCount, newMBKernel, newMBKernelArgs)) return 1;