Magellan Linux

Diff of /tags/grubby-8_40_20170706/grubby.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1693 by niro, Fri Feb 17 23:21:08 2012 UTC revision 3007 by niro, Tue Jun 27 14:28:28 2017 UTC
# Line 36  Line 36 
36  #include <signal.h>  #include <signal.h>
37  #include <blkid/blkid.h>  #include <blkid/blkid.h>
38    
39    #include "log.h"
40    
41    #ifndef DEBUG
42  #define DEBUG 0  #define DEBUG 0
43    #endif
44    
45  #if DEBUG  #if DEBUG
46  #define dbgPrintf(format, args...) fprintf(stderr, format , ## args)  #define dbgPrintf(format, args...) fprintf(stderr, format , ## args)
# Line 44  Line 48 
48  #define dbgPrintf(format, args...)  #define dbgPrintf(format, args...)
49  #endif  #endif
50    
51    int debug = 0; /* Currently just for template debugging */
52    
53  #define _(A) (A)  #define _(A) (A)
54    
55  #define MAX_EXTRA_INITRDS  16 /* code segment checked by --bootloader-probe */  #define MAX_EXTRA_INITRDS  16 /* code segment checked by --bootloader-probe */
56  #define CODE_SEG_SIZE  128 /* code segment checked by --bootloader-probe */  #define CODE_SEG_SIZE  128 /* code segment checked by --bootloader-probe */
57    
58    #define NOOP_OPCODE 0x90
59    #define JMP_SHORT_OPCODE 0xeb
60    
61    int isEfi = 0;
62    
63    #if defined(__aarch64__)
64    #define isEfiOnly 1
65    #else
66    #define isEfiOnly 0
67    #endif
68    
69    char *saved_command_line = NULL;
70    
71  /* comments get lumped in with indention */  /* comments get lumped in with indention */
72  struct lineElement {  struct lineElement {
73      char * item;   char *item;
74      char * indent;   char *indent;
75  };  };
76    
77  enum lineType_e {  enum lineType_e {
78      LT_WHITESPACE = 1 << 0,   LT_UNIDENTIFIED = 0,
79      LT_TITLE      = 1 << 1,   LT_WHITESPACE = 1 << 0,
80      LT_KERNEL     = 1 << 2,   LT_TITLE = 1 << 1,
81      LT_INITRD     = 1 << 3,   LT_KERNEL = 1 << 2,
82      LT_HYPER      = 1 << 4,   LT_INITRD = 1 << 3,
83      LT_DEFAULT    = 1 << 5,   LT_HYPER = 1 << 4,
84      LT_MBMODULE   = 1 << 6,   LT_DEFAULT = 1 << 5,
85      LT_ROOT       = 1 << 7,   LT_MBMODULE = 1 << 6,
86      LT_FALLBACK   = 1 << 8,   LT_ROOT = 1 << 7,
87      LT_KERNELARGS = 1 << 9,   LT_FALLBACK = 1 << 8,
88      LT_BOOT       = 1 << 10,   LT_KERNELARGS = 1 << 9,
89      LT_BOOTROOT   = 1 << 11,   LT_BOOT = 1 << 10,
90      LT_LBA        = 1 << 12,   LT_BOOTROOT = 1 << 11,
91      LT_OTHER      = 1 << 13,   LT_LBA = 1 << 12,
92      LT_GENERIC    = 1 << 14,   LT_OTHER = 1 << 13,
93      LT_UNKNOWN    = 1 << 15,   LT_GENERIC = 1 << 14,
94     LT_ECHO = 1 << 16,
95     LT_MENUENTRY = 1 << 17,
96     LT_ENTRY_END = 1 << 18,
97     LT_SET_VARIABLE = 1 << 19,
98     LT_KERNEL_EFI = 1 << 20,
99     LT_INITRD_EFI = 1 << 21,
100     LT_KERNEL_16 = 1 << 22,
101     LT_INITRD_16 = 1 << 23,
102     LT_DEVTREE = 1 << 24,
103     LT_UNKNOWN = 1 << 25,
104  };  };
105    
106  struct singleLine {  struct singleLine {
107      char * indent;   char *indent;
108      int numElements;   int numElements;
109      struct lineElement * elements;   struct lineElement *elements;
110      struct singleLine * next;   struct singleLine *next;
111      enum lineType_e type;   enum lineType_e type;
112  };  };
113    
114  struct singleEntry {  struct singleEntry {
115      struct singleLine * lines;   struct singleLine *lines;
116      int skip;   int skip;
117      int multiboot;   int multiboot;
118      struct singleEntry * next;   struct singleEntry *next;
119  };  };
120    
121  #define GRUBBY_BADIMAGE_OKAY (1 << 0)  #define GRUBBY_BADIMAGE_OKAY (1 << 0)
# Line 99  struct singleEntry { Line 128  struct singleEntry {
128  #define NEED_TITLE   (1 << 2)  #define NEED_TITLE   (1 << 2)
129  #define NEED_ARGS    (1 << 3)  #define NEED_ARGS    (1 << 3)
130  #define NEED_MB      (1 << 4)  #define NEED_MB      (1 << 4)
131    #define NEED_END     (1 << 5)
132    #define NEED_DEVTREE (1 << 6)
133    
134  #define MAIN_DEFAULT    (1 << 0)  #define MAIN_DEFAULT    (1 << 0)
135  #define DEFAULT_SAVED       -2  #define DEFAULT_SAVED       -2
136    #define DEFAULT_SAVED_GRUB2 -3
137    
138  struct keywordTypes {  struct keywordTypes {
139      char * key;   char *key;
140      enum lineType_e type;   enum lineType_e type;
141      char nextChar;   char nextChar;
142      char separatorChar;   char separatorChar;
143  };  };
144    
145    struct configFileInfo;
146    
147    typedef const char *(*findConfigFunc) (struct configFileInfo *);
148    typedef const int (*writeLineFunc) (struct configFileInfo *,
149        struct singleLine * line);
150    typedef char *(*getEnvFunc) (struct configFileInfo *, char *name);
151    typedef int (*setEnvFunc) (struct configFileInfo *, char *name, char *value);
152    
153  struct configFileInfo {  struct configFileInfo {
154      char * defaultConfig;   char *defaultConfig;
155      struct keywordTypes * keywords;   findConfigFunc findConfig;
156      int defaultIsIndex;   writeLineFunc writeLine;
157      int defaultSupportSaved;   getEnvFunc getEnv;
158      enum lineType_e entryStart;   setEnvFunc setEnv;
159      int needsBootPrefix;   struct keywordTypes *keywords;
160      int argsInQuotes;   int caseInsensitive;
161      int maxTitleLength;   int defaultIsIndex;
162      int titleBracketed;   int defaultIsVariable;
163      int mbHyperFirst;   int defaultSupportSaved;
164      int mbInitRdIsModule;   int defaultIsSaved;
165      int mbConcatArgs;   int defaultIsUnquoted;
166      int mbAllowExtraInitRds;   enum lineType_e entryStart;
167     enum lineType_e entryEnd;
168     int needsBootPrefix;
169     int argsInQuotes;
170     int maxTitleLength;
171     int titleBracketed;
172     int titlePosition;
173     int mbHyperFirst;
174     int mbInitRdIsModule;
175     int mbConcatArgs;
176     int mbAllowExtraInitRds;
177     char *envFile;
178  };  };
179    
180  struct keywordTypes grubKeywords[] = {  struct keywordTypes grubKeywords[] = {
181      { "title",    LT_TITLE,    ' ' },   {"title", LT_TITLE, ' '},
182      { "root",    LT_BOOTROOT,    ' ' },   {"root", LT_BOOTROOT, ' '},
183      { "default",    LT_DEFAULT,    ' ' },   {"default", LT_DEFAULT, ' '},
184      { "fallback",   LT_FALLBACK,    ' ' },   {"fallback", LT_FALLBACK, ' '},
185      { "kernel",    LT_KERNEL,    ' ' },   {"kernel", LT_KERNEL, ' '},
186      { "initrd",    LT_INITRD,    ' ', ' ' },   {"initrd", LT_INITRD, ' ', ' '},
187      { "module",     LT_MBMODULE,    ' ' },   {"module", LT_MBMODULE, ' '},
188      { "kernel",     LT_HYPER,       ' ' },   {"kernel", LT_HYPER, ' '},
189      { NULL,    0, 0 },   {NULL, 0, 0},
190  };  };
191    
192    const char *grubFindConfig(struct configFileInfo *cfi)
193    {
194     static const char *configFiles[] = {
195     "/boot/grub/grub.conf",
196     "/boot/grub/menu.lst",
197     "/etc/grub.conf",
198     NULL
199     };
200     static int i = -1;
201    
202     if (i == -1) {
203     for (i = 0; configFiles[i] != NULL; i++) {
204     dbgPrintf("Checking \"%s\": ", configFiles[i]);
205     if (!access(configFiles[i], R_OK)) {
206     dbgPrintf("found\n");
207     return configFiles[i];
208     }
209     dbgPrintf("not found\n");
210     }
211     }
212     return configFiles[i];
213    }
214    
215  struct configFileInfo grubConfigType = {  struct configFileInfo grubConfigType = {
216      .defaultConfig = "/boot/grub/grub.conf",   .findConfig = grubFindConfig,
217      .keywords = grubKeywords,   .keywords = grubKeywords,
218      .defaultIsIndex = 1,   .defaultIsIndex = 1,
219      .defaultSupportSaved = 1,   .defaultSupportSaved = 1,
220      .entryStart = LT_TITLE,   .entryStart = LT_TITLE,
221      .needsBootPrefix = 1,   .needsBootPrefix = 1,
222      .mbHyperFirst = 1,   .mbHyperFirst = 1,
223      .mbInitRdIsModule = 1,   .mbInitRdIsModule = 1,
224      .mbAllowExtraInitRds = 1,   .mbAllowExtraInitRds = 1,
225     .titlePosition = 1,
226    };
227    
228    struct keywordTypes grub2Keywords[] = {
229     {"menuentry", LT_MENUENTRY, ' '},
230     {"}", LT_ENTRY_END, ' '},
231     {"echo", LT_ECHO, ' '},
232     {"set", LT_SET_VARIABLE, ' ', '='},
233     {"root", LT_BOOTROOT, ' '},
234     {"default", LT_DEFAULT, ' '},
235     {"fallback", LT_FALLBACK, ' '},
236     {"linux", LT_KERNEL, ' '},
237     {"linuxefi", LT_KERNEL_EFI, ' '},
238     {"linux16", LT_KERNEL_16, ' '},
239     {"initrd", LT_INITRD, ' ', ' '},
240     {"initrdefi", LT_INITRD_EFI, ' ', ' '},
241     {"initrd16", LT_INITRD_16, ' ', ' '},
242     {"module", LT_MBMODULE, ' '},
243     {"kernel", LT_HYPER, ' '},
244     {"devicetree", LT_DEVTREE, ' '},
245     {NULL, 0, 0},
246    };
247    
248    const char *grub2FindConfig(struct configFileInfo *cfi)
249    {
250     static const char *configFiles[] = {
251     "/boot/grub/grub-efi.cfg",
252     "/boot/grub/grub.cfg",
253     "/etc/grub2-efi.cfg",
254     "/etc/grub2.cfg",
255     "/boot/grub2/grub.cfg",
256     "/boot/grub2-efi/grub.cfg",
257     NULL
258     };
259     static int i = -1;
260     static const char *grub_cfg = "/boot/grub/grub.cfg";
261     int rc = -1;
262    
263     if (i == -1) {
264     for (i = 0; configFiles[i] != NULL; i++) {
265     dbgPrintf("Checking \"%s\": ", configFiles[i]);
266     if ((rc = access(configFiles[i], R_OK))) {
267     if (errno == EACCES) {
268     printf
269        ("Unable to access bootloader configuration file "
270         "\"%s\": %m\n", configFiles[i]);
271     exit(1);
272     }
273     continue;
274     } else {
275     dbgPrintf("found\n");
276     return configFiles[i];
277     }
278     }
279     }
280    
281     /* Ubuntu renames grub2 to grub, so check for the grub.d directory
282     * that isn't in grub1, and if it exists, return the config file path
283     * that they use. */
284     if (configFiles[i] == NULL && !access("/etc/grub.d/", R_OK)) {
285     dbgPrintf("found\n");
286     return grub_cfg;
287     }
288    
289     dbgPrintf("not found\n");
290     return configFiles[i];
291    }
292    
293    /* kind of hacky.  It'll give the first 1024 bytes, ish. */
294    static char *grub2GetEnv(struct configFileInfo *info, char *name)
295    {
296     static char buf[1025];
297     char *s = NULL;
298     char *ret = NULL;
299     char *envFile = info->envFile ? info->envFile : "/boot/grub/grubenv";
300     int rc =
301        asprintf(&s, "grub-editenv %s list | grep '^%s='", envFile, name);
302    
303     if (rc < 0)
304     return NULL;
305    
306     FILE *f = popen(s, "r");
307     if (!f)
308     goto out;
309    
310     memset(buf, '\0', sizeof(buf));
311     ret = fgets(buf, 1024, f);
312     pclose(f);
313    
314     if (ret) {
315     ret += strlen(name) + 1;
316     ret[strlen(ret) - 1] = '\0';
317     }
318     dbgPrintf("grub2GetEnv(%s): %s\n", name, ret);
319    out:
320     free(s);
321     return ret;
322    }
323    
324    static int sPopCount(const char *s, const char *c)
325    {
326     int ret = 0;
327     if (!s)
328     return -1;
329     for (int i = 0; s[i] != '\0'; i++)
330     for (int j = 0; c[j] != '\0'; j++)
331     if (s[i] == c[j])
332     ret++;
333     return ret;
334    }
335    
336    static char *shellEscape(const char *s)
337    {
338     int l = strlen(s) + sPopCount(s, "'") * 2;
339    
340     char *ret = calloc(l + 1, sizeof(*ret));
341     if (!ret)
342     return NULL;
343     for (int i = 0, j = 0; s[i] != '\0'; i++, j++) {
344     if (s[i] == '\'')
345     ret[j++] = '\\';
346     ret[j] = s[i];
347     }
348     return ret;
349    }
350    
351    static void unquote(char *s)
352    {
353     int l = strlen(s);
354    
355     if ((s[l - 1] == '\'' && s[0] == '\'')
356        || (s[l - 1] == '"' && s[0] == '"')) {
357     memmove(s, s + 1, l - 2);
358     s[l - 2] = '\0';
359     }
360    }
361    
362    static int grub2SetEnv(struct configFileInfo *info, char *name, char *value)
363    {
364     char *s = NULL;
365     int rc = 0;
366     char *envFile = info->envFile ? info->envFile : "/boot/grub/grubenv";
367    
368     unquote(value);
369     value = shellEscape(value);
370     if (!value)
371     return -1;
372    
373     rc = asprintf(&s, "grub-editenv %s set '%s=%s'", envFile, name, value);
374     free(value);
375     if (rc < 0)
376     return -1;
377    
378     dbgPrintf("grub2SetEnv(%s): %s\n", name, s);
379     rc = system(s);
380     free(s);
381     return rc;
382    }
383    
384    /* this is a gigantic hack to avoid clobbering grub2 variables... */
385    static int is_special_grub2_variable(const char *name)
386    {
387     if (!strcmp(name, "\"${next_entry}\""))
388     return 1;
389     if (!strcmp(name, "\"${prev_saved_entry}\""))
390     return 1;
391     return 0;
392    }
393    
394    int sizeOfSingleLine(struct singleLine *line)
395    {
396     int count = 0;
397    
398     for (int i = 0; i < line->numElements; i++) {
399     int indentSize = 0;
400    
401     count = count + strlen(line->elements[i].item);
402    
403     indentSize = strlen(line->elements[i].indent);
404     if (indentSize > 0)
405     count = count + indentSize;
406     else
407     /* be extra safe and add room for whitespaces */
408     count = count + 1;
409     }
410    
411     /* room for trailing terminator */
412     count = count + 1;
413    
414     return count;
415    }
416    
417    static int isquote(char q)
418    {
419     if (q == '\'' || q == '\"')
420     return 1;
421     return 0;
422    }
423    
424    static int iskernel(enum lineType_e type)
425    {
426     return (type == LT_KERNEL || type == LT_KERNEL_EFI
427     || type == LT_KERNEL_16);
428    }
429    
430    static int isinitrd(enum lineType_e type)
431    {
432     return (type == LT_INITRD || type == LT_INITRD_EFI
433     || type == LT_INITRD_16);
434    }
435    
436    char *grub2ExtractTitle(struct singleLine *line)
437    {
438     char *current;
439     char *current_indent;
440     int current_len;
441     int current_indent_len;
442     int i;
443    
444     /* bail out if line does not start with menuentry */
445     if (strcmp(line->elements[0].item, "menuentry"))
446     return NULL;
447    
448     i = 1;
449     current = line->elements[i].item;
450     current_len = strlen(current);
451    
452     /* if second word is quoted, strip the quotes and return single word */
453     if (isquote(*current) && isquote(current[current_len - 1])) {
454     char *tmp;
455    
456     tmp = strdup(current + 1);
457     if (!tmp)
458     return NULL;
459     tmp[strlen(tmp) - 1] = '\0';
460     return tmp;
461     }
462    
463     /* if no quotes, return second word verbatim */
464     if (!isquote(*current))
465     return current;
466    
467     /* second element start with a quote, so we have to find the element
468     * whose last character is also quote (assuming it's the closing one) */
469     int resultMaxSize;
470     char *result;
471     /* need to ensure that ' does not match " as we search */
472     char quote_char = *current;
473    
474     resultMaxSize = sizeOfSingleLine(line);
475     result = malloc(resultMaxSize);
476     snprintf(result, resultMaxSize, "%s", ++current);
477    
478     i++;
479     for (; i < line->numElements; ++i) {
480     current = line->elements[i].item;
481     current_len = strlen(current);
482     current_indent = line->elements[i].indent;
483     current_indent_len = strlen(current_indent);
484    
485     strncat(result, current_indent, current_indent_len);
486     if (current[current_len - 1] != quote_char) {
487     strncat(result, current, current_len);
488     } else {
489     strncat(result, current, current_len - 1);
490     break;
491     }
492     }
493     return result;
494    }
495    
496    struct configFileInfo grub2ConfigType = {
497     .findConfig = grub2FindConfig,
498     .getEnv = grub2GetEnv,
499     .setEnv = grub2SetEnv,
500     .keywords = grub2Keywords,
501     .defaultIsIndex = 1,
502     .defaultSupportSaved = 1,
503     .defaultIsVariable = 1,
504     .entryStart = LT_MENUENTRY,
505     .entryEnd = LT_ENTRY_END,
506     .titlePosition = 1,
507     .needsBootPrefix = 1,
508     .mbHyperFirst = 1,
509     .mbInitRdIsModule = 1,
510     .mbAllowExtraInitRds = 1,
511  };  };
512    
513  struct keywordTypes yabootKeywords[] = {  struct keywordTypes yabootKeywords[] = {
514      { "label",    LT_TITLE,    '=' },   {"label", LT_TITLE, '='},
515      { "root",    LT_ROOT,    '=' },   {"root", LT_ROOT, '='},
516      { "default",    LT_DEFAULT,    '=' },   {"default", LT_DEFAULT, '='},
517      { "image",    LT_KERNEL,    '=' },   {"image", LT_KERNEL, '='},
518      { "bsd",    LT_GENERIC,    '=' },   {"bsd", LT_GENERIC, '='},
519      { "macos",    LT_GENERIC,    '=' },   {"macos", LT_GENERIC, '='},
520      { "macosx",    LT_GENERIC,    '=' },   {"macosx", LT_GENERIC, '='},
521      { "magicboot",  LT_GENERIC,    '=' },   {"magicboot", LT_GENERIC, '='},
522      { "darwin",    LT_GENERIC,    '=' },   {"darwin", LT_GENERIC, '='},
523      { "timeout",    LT_GENERIC,    '=' },   {"timeout", LT_GENERIC, '='},
524      { "install",    LT_GENERIC,    '=' },   {"install", LT_GENERIC, '='},
525      { "fstype",    LT_GENERIC,    '=' },   {"fstype", LT_GENERIC, '='},
526      { "hfstype",    LT_GENERIC,    '=' },   {"hfstype", LT_GENERIC, '='},
527      { "delay",    LT_GENERIC,    '=' },   {"delay", LT_GENERIC, '='},
528      { "defaultos",  LT_GENERIC,     '=' },   {"defaultos", LT_GENERIC, '='},
529      { "init-message", LT_GENERIC,   '=' },   {"init-message", LT_GENERIC, '='},
530      { "enablecdboot", LT_GENERIC,   ' ' },   {"enablecdboot", LT_GENERIC, ' '},
531      { "enableofboot", LT_GENERIC,   ' ' },   {"enableofboot", LT_GENERIC, ' '},
532      { "enablenetboot", LT_GENERIC,  ' ' },   {"enablenetboot", LT_GENERIC, ' '},
533      { "nonvram",    LT_GENERIC,    ' ' },   {"nonvram", LT_GENERIC, ' '},
534      { "hide",    LT_GENERIC,    ' ' },   {"hide", LT_GENERIC, ' '},
535      { "protect",    LT_GENERIC,    ' ' },   {"protect", LT_GENERIC, ' '},
536      { "nobless",    LT_GENERIC,    ' ' },   {"nobless", LT_GENERIC, ' '},
537      { "nonvram",    LT_GENERIC,    ' ' },   {"nonvram", LT_GENERIC, ' '},
538      { "brokenosx",  LT_GENERIC,    ' ' },   {"brokenosx", LT_GENERIC, ' '},
539      { "usemount",   LT_GENERIC,    ' ' },   {"usemount", LT_GENERIC, ' '},
540      { "mntpoint",   LT_GENERIC,    '=' },   {"mntpoint", LT_GENERIC, '='},
541      { "partition",  LT_GENERIC,    '=' },   {"partition", LT_GENERIC, '='},
542      { "device",    LT_GENERIC,    '=' },   {"device", LT_GENERIC, '='},
543      { "fstype",    LT_GENERIC,    '=' },   {"fstype", LT_GENERIC, '='},
544      { "initrd",    LT_INITRD,    '=', ';' },   {"initrd", LT_INITRD, '=', ';'},
545      { "append",    LT_KERNELARGS,  '=' },   {"append", LT_KERNELARGS, '='},
546      { "boot",    LT_BOOT,    '=' },   {"boot", LT_BOOT, '='},
547      { "lba",    LT_LBA,    ' ' },   {"lba", LT_LBA, ' '},
548      { NULL,    0, 0 },   {NULL, 0, 0},
549  };  };
550    
551  struct keywordTypes liloKeywords[] = {  struct keywordTypes liloKeywords[] = {
552      { "label",    LT_TITLE,    '=' },   {"label", LT_TITLE, '='},
553      { "root",    LT_ROOT,    '=' },   {"root", LT_ROOT, '='},
554      { "default",    LT_DEFAULT,    '=' },   {"default", LT_DEFAULT, '='},
555      { "image",    LT_KERNEL,    '=' },   {"image", LT_KERNEL, '='},
556      { "other",    LT_OTHER,    '=' },   {"other", LT_OTHER, '='},
557      { "initrd",    LT_INITRD,    '=' },   {"initrd", LT_INITRD, '='},
558      { "append",    LT_KERNELARGS,  '=' },   {"append", LT_KERNELARGS, '='},
559      { "boot",    LT_BOOT,    '=' },   {"boot", LT_BOOT, '='},
560      { "lba",    LT_LBA,    ' ' },   {"lba", LT_LBA, ' '},
561      { NULL,    0, 0 },   {NULL, 0, 0},
562  };  };
563    
564  struct keywordTypes eliloKeywords[] = {  struct keywordTypes eliloKeywords[] = {
565      { "label",    LT_TITLE,    '=' },   {"label", LT_TITLE, '='},
566      { "root",    LT_ROOT,    '=' },   {"root", LT_ROOT, '='},
567      { "default",    LT_DEFAULT,    '=' },   {"default", LT_DEFAULT, '='},
568      { "image",    LT_KERNEL,    '=' },   {"image", LT_KERNEL, '='},
569      { "initrd",    LT_INITRD,    '=' },   {"initrd", LT_INITRD, '='},
570      { "append",    LT_KERNELARGS,  '=' },   {"append", LT_KERNELARGS, '='},
571      { "vmm",    LT_HYPER,       '=' },   {"vmm", LT_HYPER, '='},
572      { NULL,    0, 0 },   {NULL, 0, 0},
573  };  };
574    
575  struct keywordTypes siloKeywords[] = {  struct keywordTypes siloKeywords[] = {
576      { "label",    LT_TITLE,    '=' },   {"label", LT_TITLE, '='},
577      { "root",    LT_ROOT,    '=' },   {"root", LT_ROOT, '='},
578      { "default",    LT_DEFAULT,    '=' },   {"default", LT_DEFAULT, '='},
579      { "image",    LT_KERNEL,    '=' },   {"image", LT_KERNEL, '='},
580      { "other",    LT_OTHER,    '=' },   {"other", LT_OTHER, '='},
581      { "initrd",    LT_INITRD,    '=' },   {"initrd", LT_INITRD, '='},
582      { "append",    LT_KERNELARGS,  '=' },   {"append", LT_KERNELARGS, '='},
583      { "boot",    LT_BOOT,    '=' },   {"boot", LT_BOOT, '='},
584      { NULL,    0, 0 },   {NULL, 0, 0},
585  };  };
586    
587  struct keywordTypes ziplKeywords[] = {  struct keywordTypes ziplKeywords[] = {
588      { "target",     LT_BOOTROOT,    '=' },   {"target", LT_BOOTROOT, '='},
589      { "image",      LT_KERNEL,      '=' },   {"image", LT_KERNEL, '='},
590      { "ramdisk",    LT_INITRD,      '=' },   {"ramdisk", LT_INITRD, '='},
591      { "parameters", LT_KERNELARGS,  '=' },   {"parameters", LT_KERNELARGS, '='},
592      { "default",    LT_DEFAULT,     '=' },   {"default", LT_DEFAULT, '='},
593      { NULL,         0, 0 },   {NULL, 0, 0},
594  };  };
595    
596  struct keywordTypes extlinuxKeywords[] = {  struct keywordTypes extlinuxKeywords[] = {
597      { "label",    LT_TITLE,    ' ' },   {"label", LT_TITLE, ' '},
598      { "root",    LT_ROOT,    ' ' },   {"root", LT_ROOT, ' '},
599      { "default",    LT_DEFAULT,    ' ' },   {"default", LT_DEFAULT, ' '},
600      { "kernel",    LT_KERNEL,    ' ' },   {"kernel", LT_KERNEL, ' '},
601      { "initrd",    LT_INITRD,      ' ', ',' },   {"initrd", LT_INITRD, ' ', ','},
602      { "append",    LT_KERNELARGS,  ' ' },   {"append", LT_KERNELARGS, ' '},
603      { "prompt",     LT_UNKNOWN,     ' ' },   {"prompt", LT_UNKNOWN, ' '},
604      { NULL,    0, 0 },   {"fdt", LT_DEVTREE, ' '},
605     {"fdtdir", LT_DEVTREE, ' '},
606     {NULL, 0, 0},
607  };  };
608    
609  int useextlinuxmenu;  int useextlinuxmenu;
610  struct configFileInfo eliloConfigType = {  struct configFileInfo eliloConfigType = {
611      .defaultConfig = "/boot/efi/EFI/redhat/elilo.conf",   .defaultConfig = "/boot/efi/EFI/redhat/elilo.conf",
612      .keywords = eliloKeywords,   .keywords = eliloKeywords,
613      .entryStart = LT_KERNEL,   .entryStart = LT_KERNEL,
614      .needsBootPrefix = 1,   .needsBootPrefix = 1,
615      .argsInQuotes = 1,   .argsInQuotes = 1,
616      .mbConcatArgs = 1,   .mbConcatArgs = 1,
617     .titlePosition = 1,
618  };  };
619    
620  struct configFileInfo liloConfigType = {  struct configFileInfo liloConfigType = {
621      .defaultConfig = "/etc/lilo.conf",   .defaultConfig = "/etc/lilo.conf",
622      .keywords = liloKeywords,   .keywords = liloKeywords,
623      .entryStart = LT_KERNEL,   .entryStart = LT_KERNEL,
624      .argsInQuotes = 1,   .argsInQuotes = 1,
625      .maxTitleLength = 15,   .maxTitleLength = 15,
626     .titlePosition = 1,
627  };  };
628    
629  struct configFileInfo yabootConfigType = {  struct configFileInfo yabootConfigType = {
630      .defaultConfig = "/etc/yaboot.conf",   .defaultConfig = "/etc/yaboot.conf",
631      .keywords = yabootKeywords,   .keywords = yabootKeywords,
632      .entryStart = LT_KERNEL,   .entryStart = LT_KERNEL,
633      .needsBootPrefix = 1,   .needsBootPrefix = 1,
634      .argsInQuotes = 1,   .argsInQuotes = 1,
635      .maxTitleLength = 15,   .maxTitleLength = 15,
636      .mbAllowExtraInitRds = 1,   .mbAllowExtraInitRds = 1,
637     .titlePosition = 1,
638  };  };
639    
640  struct configFileInfo siloConfigType = {  struct configFileInfo siloConfigType = {
641      .defaultConfig = "/etc/silo.conf",   .defaultConfig = "/etc/silo.conf",
642      .keywords = siloKeywords,   .keywords = siloKeywords,
643      .entryStart = LT_KERNEL,   .entryStart = LT_KERNEL,
644      .needsBootPrefix = 1,   .needsBootPrefix = 1,
645      .argsInQuotes = 1,   .argsInQuotes = 1,
646      .maxTitleLength = 15,   .maxTitleLength = 15,
647     .titlePosition = 1,
648  };  };
649    
650  struct configFileInfo ziplConfigType = {  struct configFileInfo ziplConfigType = {
651      .defaultConfig = "/etc/zipl.conf",   .defaultConfig = "/etc/zipl.conf",
652      .keywords = ziplKeywords,   .keywords = ziplKeywords,
653      .entryStart = LT_TITLE,   .entryStart = LT_TITLE,
654      .argsInQuotes = 1,   .argsInQuotes = 1,
655      .titleBracketed = 1,   .titleBracketed = 1,
656  };  };
657    
658  struct configFileInfo extlinuxConfigType = {  struct configFileInfo extlinuxConfigType = {
659      .defaultConfig = "/boot/extlinux/extlinux.conf",   .defaultConfig = "/boot/extlinux/extlinux.conf",
660      .keywords = extlinuxKeywords,   .keywords = extlinuxKeywords,
661      .entryStart = LT_TITLE,   .caseInsensitive = 1,
662      .needsBootPrefix = 1,   .entryStart = LT_TITLE,
663      .maxTitleLength = 255,   .needsBootPrefix = 1,
664      .mbAllowExtraInitRds = 1,   .maxTitleLength = 255,
665     .mbAllowExtraInitRds = 1,
666     .defaultIsUnquoted = 1,
667     .titlePosition = 1,
668  };  };
669    
670  struct grubConfig {  struct grubConfig {
671      struct singleLine * theLines;   struct singleLine *theLines;
672      struct singleEntry * entries;   struct singleEntry *entries;
673      char * primaryIndent;   char *primaryIndent;
674      char * secondaryIndent;   char *secondaryIndent;
675      int defaultImage;    /* -1 if none specified -- this value is   int defaultImage; /* -1 if none specified -- this value is
676       * written out, overriding original */   * written out, overriding original */
677      int fallbackImage;    /* just like defaultImage */   int fallbackImage; /* just like defaultImage */
678      int flags;   int flags;
679      struct configFileInfo * cfi;   struct configFileInfo *cfi;
680  };  };
681    
682  blkid_cache blkid;  blkid_cache blkid;
683    
684  struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index);  struct singleEntry *findEntryByIndex(struct grubConfig *cfg, int index);
685  struct singleEntry * findEntryByPath(struct grubConfig * cfg,  struct singleEntry *findEntryByPath(struct grubConfig *cfg,
686       const char * path, const char * prefix,      const char *path, const char *prefix,
687       int * index);      int *index);
688  static int readFile(int fd, char ** bufPtr);  struct singleEntry *findEntryByTitle(struct grubConfig *cfg, char *title,
689  static void lineInit(struct singleLine * line);       int *index);
690  struct singleLine * lineDup(struct singleLine * line);  static int readFile(int fd, char **bufPtr);
691  static void lineFree(struct singleLine * line);  static void lineInit(struct singleLine *line);
692  static int lineWrite(FILE * out, struct singleLine * line,  struct singleLine *lineDup(struct singleLine *line);
693       struct configFileInfo * cfi);  static void lineFree(struct singleLine *line);
694  static int getNextLine(char ** bufPtr, struct singleLine * line,  static int lineWrite(FILE * out, struct singleLine *line,
695         struct configFileInfo * cfi);       struct configFileInfo *cfi);
696  static char * getRootSpecifier(char * str);  static int getNextLine(char **bufPtr, struct singleLine *line,
697  static void insertElement(struct singleLine * line,         struct configFileInfo *cfi);
698    const char * item, int insertHere,  static char *getRootSpecifier(char *str);
699    struct configFileInfo * cfi);  static void requote(struct singleLine *line, struct configFileInfo *cfi);
700  static void removeElement(struct singleLine * line, int removeHere);  static void insertElement(struct singleLine *line,
701  static struct keywordTypes * getKeywordByType(enum lineType_e type,    const char *item, int insertHere,
702        struct configFileInfo * cfi);    struct configFileInfo *cfi);
703  static enum lineType_e getTypeByKeyword(char * keyword,  static void removeElement(struct singleLine *line, int removeHere);
704   struct configFileInfo * cfi);  static struct keywordTypes *getKeywordByType(enum lineType_e type,
705  static struct singleLine * getLineByType(enum lineType_e type,       struct configFileInfo *cfi);
706   struct singleLine * line);  static enum lineType_e getTypeByKeyword(char *keyword,
707  static int checkForExtLinux(struct grubConfig * config);   struct configFileInfo *cfi);
708  struct singleLine * addLineTmpl(struct singleEntry * entry,  static struct singleLine *getLineByType(enum lineType_e type,
709                                  struct singleLine * tmplLine,   struct singleLine *line);
710                                  struct singleLine * prevLine,  static int checkForExtLinux(struct grubConfig *config);
711                                  const char * val,  struct singleLine *addLineTmpl(struct singleEntry *entry,
712   struct configFileInfo * cfi);         struct singleLine *tmplLine,
713  struct singleLine *  addLine(struct singleEntry * entry,         struct singleLine *prevLine,
714                               struct configFileInfo * cfi,         const char *val, struct configFileInfo *cfi);
715                               enum lineType_e type, char * defaultIndent,  struct singleLine *addLine(struct singleEntry *entry,
716                               const char * val);     struct configFileInfo *cfi,
717       enum lineType_e type, char *defaultIndent,
718       const char *val);
719    
720  static char * sdupprintf(const char *format, ...)  static char *sdupprintf(const char *format, ...)
721  #ifdef __GNUC__  #ifdef __GNUC__
722          __attribute__ ((format (printf, 1, 2)));      __attribute__ ((format(printf, 1, 2)));
723  #else  #else
724          ;  ;
725  #endif  #endif
726    
727  static char * sdupprintf(const char *format, ...) {  static char *sdupprintf(const char *format, ...)
728      char *buf = NULL;  {
729      char c;   char *buf = NULL;
730      va_list args;   char c;
731      size_t size = 0;   va_list args;
732      va_start(args, format);   size_t size = 0;
733         va_start(args, format);
734      /* XXX requires C99 vsnprintf behavior */  
735      size = vsnprintf(&c, 1, format, args) + 1;   /* XXX requires C99 vsnprintf behavior */
736      if (size == -1) {   size = vsnprintf(&c, 1, format, args) + 1;
737   printf("ERROR: vsnprintf behavior is not C99\n");   if (size == -1) {
738   abort();   printf("ERROR: vsnprintf behavior is not C99\n");
739      }   abort();
740     }
741    
742      va_end(args);   va_end(args);
743      va_start(args, format);   va_start(args, format);
744    
745      buf = malloc(size);   buf = malloc(size);
746      if (buf == NULL)   if (buf == NULL)
747   return NULL;   return NULL;
748      vsnprintf(buf, size, format, args);   vsnprintf(buf, size, format, args);
749      va_end (args);   va_end(args);
750    
751      return buf;   return buf;
752  }  }
753    
754  static struct keywordTypes * getKeywordByType(enum lineType_e type,  static inline int
755        struct configFileInfo * cfi) {  kwcmp(struct keywordTypes *kw, const char * label, int case_insensitive)
756      struct keywordTypes * kw;  {
757      for (kw = cfi->keywords; kw->key; kw++) {      int kwl = strlen(kw->key);
758   if (kw->type == type)      int ll = strlen(label);
759      return kw;      int rc;
760        int (*snc)(const char *s1, const char *s2, size_t n) =
761               case_insensitive ? strncasecmp : strncmp;
762        int (*sc)(const char *s1, const char *s2) =
763               case_insensitive ? strcasecmp : strcmp;
764    
765        rc = snc(kw->key, label, kwl);
766        if (rc)
767           return rc;
768    
769        for (int i = kwl; i < ll; i++) {
770           if (isspace(label[i]))
771               return 0;
772           if (kw->separatorChar && label[i] == kw->separatorChar)
773               return 0;
774           else if (kw->nextChar && label[i] == kw->nextChar)
775               return 0;
776           return sc(kw->key+kwl, label+kwl);
777      }      }
778      return NULL;      return 0;
779  }  }
780    
781  static char * getpathbyspec(char *device) {  static enum lineType_e preferredLineType(enum lineType_e type,
782      if (!blkid)   struct configFileInfo *cfi)
783          blkid_get_cache(&blkid, NULL);  {
784     if (isEfi && cfi == &grub2ConfigType) {
785      return blkid_get_devname(blkid, device, NULL);   switch (type) {
786     case LT_KERNEL:
787     return isEfiOnly ? LT_KERNEL : LT_KERNEL_EFI;
788     case LT_INITRD:
789     return isEfiOnly ? LT_INITRD : LT_INITRD_EFI;
790     default:
791     return type;
792     }
793    #if defined(__i386__) || defined(__x86_64__)
794     } else if (cfi == &grub2ConfigType) {
795     switch (type) {
796     case LT_KERNEL:
797     return LT_KERNEL_16;
798     case LT_INITRD:
799     return LT_INITRD_16;
800     default:
801     return type;
802     }
803    #endif
804     }
805     return type;
806  }  }
807    
808  static char * getuuidbydev(char *device) {  static struct keywordTypes *getKeywordByType(enum lineType_e type,
809      if (!blkid)       struct configFileInfo *cfi)
810   blkid_get_cache(&blkid, NULL);  {
811     for (struct keywordTypes * kw = cfi->keywords; kw->key; kw++) {
812      return blkid_get_tag_value(blkid, "UUID", device);   if (kw->type == type)
813     return kw;
814     }
815     return NULL;
816  }  }
817    
818  static enum lineType_e getTypeByKeyword(char * keyword,  static char *getKeyByType(enum lineType_e type, struct configFileInfo *cfi)
819   struct configFileInfo * cfi) {  {
820      struct keywordTypes * kw;   struct keywordTypes *kt = getKeywordByType(type, cfi);
821      for (kw = cfi->keywords; kw->key; kw++) {   if (kt)
822   if (!strcmp(keyword, kw->key))   return kt->key;
823      return kw->type;   return "unknown";
     }  
     return LT_UNKNOWN;  
824  }  }
825    
826  static struct singleLine * getLineByType(enum lineType_e type,  static char *getpathbyspec(char *device)
827   struct singleLine * line) {  {
828      dbgPrintf("getLineByType(%d): ", type);   if (!blkid)
829      for (; line; line = line->next) {   blkid_get_cache(&blkid, NULL);
830   dbgPrintf("%d:%s ", line->type,  
831    line->numElements ? line->elements[0].item : "(empty)");   return blkid_get_devname(blkid, device, NULL);
  if (line->type & type) break;  
     }  
     dbgPrintf(line ? "\n" : " (failed)\n");  
     return line;  
832  }  }
833    
834  static int isBracketedTitle(struct singleLine * line) {  static char *getuuidbydev(char *device)
835      if (line->numElements == 1 && *line->elements[0].item == '[') {  {
836          int len = strlen(line->elements[0].item);   if (!blkid)
837          if (*(line->elements[0].item + len - 1) == ']') {   blkid_get_cache(&blkid, NULL);
838              /* FIXME: this is a hack... */  
839              if (strcmp(line->elements[0].item, "[defaultboot]")) {   return blkid_get_tag_value(blkid, "UUID", device);
                 return 1;  
             }  
         }  
     }  
     return 0;  
840  }  }
841    
842  static int isEntryStart(struct singleLine * line,  static enum lineType_e getTypeByKeyword(char *keyword,
843                              struct configFileInfo * cfi) {   struct configFileInfo *cfi)
844      return line->type == cfi->entryStart || line->type == LT_OTHER ||  {
845   (cfi->titleBracketed && isBracketedTitle(line));   for (struct keywordTypes * kw = cfi->keywords; kw->key; kw++) {
846     if (!kwcmp(kw, keyword, cfi->caseInsensitive))
847     return kw->type;
848     }
849     return LT_UNKNOWN;
850  }  }
851    
852  /* extract the title from within brackets (for zipl) */  static struct singleLine *getLineByType(enum lineType_e type,
853  static char * extractTitle(struct singleLine * line) {   struct singleLine *line)
854      /* bracketed title... let's extract it (leaks a byte) */  {
855      char * title;   dbgPrintf("getLineByType(%d): ", type);
856      title = strdup(line->elements[0].item);   for (; line; line = line->next) {
857      title++;   dbgPrintf("%d:%s ", line->type,
858      *(title + strlen(title) - 1) = '\0';    line->numElements ? line->elements[0].
859      return title;    item : "(empty)");
860  }   if (line->type & type)
861     break;
862  static int readFile(int fd, char ** bufPtr) {   }
863      int alloced = 0, size = 0, i = 0;   dbgPrintf(line ? "\n" : " (failed)\n");
864      char * buf = NULL;   return line;
865    }
     do {  
  size += i;  
  if ((size + 1024) > alloced) {  
     alloced += 4096;  
     buf = realloc(buf, alloced + 1);  
  }  
     } while ((i = read(fd, buf + size, 1024)) > 0);  
   
     if (i < 0) {  
  fprintf(stderr, _("error reading input: %s\n"), strerror(errno));  
         free(buf);  
  return 1;  
     }  
866    
867      buf = realloc(buf, size + 2);  static int isBracketedTitle(struct singleLine *line)
868      if (size == 0)  {
869          buf[size++] = '\n';   if (line->numElements == 1 && *line->elements[0].item == '[') {
870      else   int len = strlen(line->elements[0].item);
871          if (buf[size - 1] != '\n')   if (*(line->elements[0].item + len - 1) == ']') {
872              buf[size++] = '\n';   /* FIXME: this is a hack... */
873      buf[size] = '\0';   if (strcmp(line->elements[0].item, "[defaultboot]")) {
874     return 1;
875     }
876     }
877     }
878     return 0;
879    }
880    
881      *bufPtr = buf;  static int isEntryStart(struct singleLine *line, struct configFileInfo *cfi)
882    {
883     return line->type == cfi->entryStart || line->type == LT_OTHER ||
884        (cfi->titleBracketed && isBracketedTitle(line));
885    }
886    
887      return 0;  /* extract the title from within brackets (for zipl) */
888    static char *extractTitle(struct grubConfig *cfg, struct singleLine *line)
889    {
890     /* bracketed title... let's extract it */
891     char *title = NULL;
892     if (line->type == LT_TITLE) {
893     char *tmp = line->elements[cfg->cfi->titlePosition].item;
894     if (cfg->cfi->titleBracketed) {
895     tmp++;
896     title = strdup(tmp);
897     *(title + strlen(title) - 1) = '\0';
898     } else {
899     title = strdup(tmp);
900     }
901     } else if (line->type == LT_MENUENTRY)
902     title = strdup(line->elements[1].item);
903     else
904     return NULL;
905     return title;
906  }  }
907    
908  static void lineInit(struct singleLine * line) {  static int readFile(int fd, char **bufPtr)
909      line->indent = NULL;  {
910      line->elements = NULL;   int alloced = 0, size = 0, i = 0;
911      line->numElements = 0;   char *buf = NULL;
     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);  
     }  
912    
913      return newLine;   do {
914  }   size += i;
915     if ((size + 1024) > alloced) {
916     alloced += 4096;
917     buf = realloc(buf, alloced + 1);
918     }
919     } while ((i = read(fd, buf + size, 1024)) > 0);
920    
921  static void lineFree(struct singleLine * line) {   if (i < 0) {
922      int i;   fprintf(stderr, _("error reading input: %s\n"),
923     strerror(errno));
924     free(buf);
925     return 1;
926     }
927    
928      if (line->indent) free(line->indent);   buf = realloc(buf, size + 2);
929     if (size == 0)
930     buf[size++] = '\n';
931     else if (buf[size - 1] != '\n')
932     buf[size++] = '\n';
933     buf[size] = '\0';
934    
935      for (i = 0; i < line->numElements; i++) {   *bufPtr = buf;
  free(line->elements[i].item);  
  free(line->elements[i].indent);  
     }  
936    
937      if (line->elements) free(line->elements);   return 0;
938      lineInit(line);  }
939    
940    static void lineInit(struct singleLine *line)
941    {
942     line->type = LT_UNIDENTIFIED;
943     line->indent = NULL;
944     line->elements = NULL;
945     line->numElements = 0;
946     line->next = NULL;
947  }  }
948    
949  static int lineWrite(FILE * out, struct singleLine * line,  struct singleLine *lineDup(struct singleLine *line)
950       struct configFileInfo * cfi) {  {
951      int i;   struct singleLine *newLine = malloc(sizeof(*newLine));
952    
953      if (fprintf(out, "%s", line->indent) == -1) return -1;   newLine->indent = strdup(line->indent);
954     newLine->next = NULL;
955     newLine->type = line->type;
956     newLine->numElements = line->numElements;
957     newLine->elements = malloc(sizeof(*newLine->elements) *
958       newLine->numElements);
959    
960      for (i = 0; i < line->numElements; i++) {   for (int i = 0; i < newLine->numElements; i++) {
961   if (i == 1 && line->type == LT_KERNELARGS && cfi->argsInQuotes)   newLine->elements[i].indent = strdup(line->elements[i].indent);
962      if (fputc('"', out) == EOF) return -1;   newLine->elements[i].item = strdup(line->elements[i].item);
963     }
964    
965   if (fprintf(out, "%s", line->elements[i].item) == -1) return -1;   return newLine;
966   if (i < line->numElements - 1)  }
     if (fprintf(out, "%s", line->elements[i].indent) == -1) return -1;  
     }  
967    
968      if (line->type == LT_KERNELARGS && cfi->argsInQuotes)  static void lineFree(struct singleLine *line)
969   if (fputc('"', out) == EOF) return -1;  {
970     if (line->indent)
971     free(line->indent);
972    
973      if (fprintf(out, "\n") == -1) return -1;   for (int i = 0; i < line->numElements; i++) {
974     free(line->elements[i].item);
975     free(line->elements[i].indent);
976     }
977    
978      return 0;   if (line->elements)
979     free(line->elements);
980     lineInit(line);
981  }  }
982    
983  /* we've guaranteed that the buffer ends w/ \n\0 */  static int lineWrite(FILE * out, struct singleLine *line,
984  static int getNextLine(char ** bufPtr, struct singleLine * line,       struct configFileInfo *cfi)
985                         struct configFileInfo * cfi) {  {
986      char * end;   if (fprintf(out, "%s", line->indent) == -1)
987      char * start = *bufPtr;   return -1;
     char * chptr;  
     int elementsAlloced = 0;  
     struct lineElement * element;  
     int first = 1;  
988    
989      lineFree(line);   for (int i = 0; i < line->numElements; i++) {
990     /* Need to handle this, because we strip the quotes from
991     * menuentry when read it. */
992     if (line->type == LT_MENUENTRY && i == 1) {
993     if (!isquote(*line->elements[i].item)) {
994     int substring = 0;
995     /* If the line contains nested quotes, we did
996     * not strip the "interna" quotes and we must
997     * use the right quotes again when writing
998     * the updated file. */
999     for (int j = i; j < line->numElements; j++) {
1000     if (strchr(line->elements[i].item, '\'')
1001        != NULL) {
1002     substring = 1;
1003     fprintf(out, "\"%s\"",
1004     line->elements[i].item);
1005     break;
1006     }
1007     }
1008     if (!substring)
1009     fprintf(out, "\'%s\'",
1010     line->elements[i].item);
1011     } else {
1012     fprintf(out, "%s", line->elements[i].item);
1013     }
1014     fprintf(out, "%s", line->elements[i].indent);
1015    
1016      end = strchr(start, '\n');   continue;
1017      *end = '\0';   }
     *bufPtr = end + 1;  
1018    
1019      for (chptr = start; *chptr && isspace(*chptr); chptr++) ;   if (i == 1 && line->type == LT_KERNELARGS && cfi->argsInQuotes)
1020     if (fputc('"', out) == EOF)
1021     return -1;
1022    
1023     if (fprintf(out, "%s", line->elements[i].item) == -1)
1024     return -1;
1025     if (i < line->numElements - 1 || line->type == LT_SET_VARIABLE)
1026     if (fprintf(out, "%s", line->elements[i].indent) == -1)
1027     return -1;
1028     }
1029    
1030     if (line->type == LT_KERNELARGS && cfi->argsInQuotes)
1031     if (fputc('"', out) == EOF)
1032     return -1;
1033    
1034      line->indent = strndup(start, chptr - start);   if (fprintf(out, "\n") == -1)
1035      start = chptr;   return -1;
1036    
1037      while (start < end) {   return 0;
1038   /* we know !isspace(*start) */  }
1039    
1040   if (elementsAlloced == line->numElements) {  /* we've guaranteed that the buffer ends w/ \n\0 */
1041      elementsAlloced += 5;  static int getNextLine(char **bufPtr, struct singleLine *line,
1042      line->elements = realloc(line->elements,         struct configFileInfo *cfi)
1043   sizeof(*line->elements) * elementsAlloced);  {
1044   }   char *end;
1045     char *start = *bufPtr;
1046     char *chptr;
1047     int elementsAlloced = 0;
1048     struct lineElement *element;
1049     int first = 1;
1050    
1051   element = line->elements + line->numElements;   lineFree(line);
1052    
1053   chptr = start;   end = strchr(start, '\n');
1054   while (*chptr && !isspace(*chptr)) {   *end = '\0';
1055      if (first && *chptr == '=') break;   *bufPtr = end + 1;
     chptr++;  
  }  
  element->item = strndup(start, chptr - start);  
  start = chptr;  
1056    
1057          /* lilo actually accepts the pathological case of append = " foo " */   for (chptr = start; *chptr && isspace(*chptr); chptr++) ;
         if (*start == '=')  
             chptr = start + 1;  
         else  
             chptr = start;  
   
         do {  
             for (; *chptr && isspace(*chptr); chptr++);  
             if (*chptr == '=')  
                 chptr = chptr + 1;  
         } while (isspace(*chptr));  
1058    
1059   element->indent = strndup(start, chptr - start);   line->indent = strndup(start, chptr - start);
1060   start = chptr;   start = chptr;
1061    
1062   line->numElements++;   while (start < end) {
1063   first = 0;   /* we know !isspace(*start) */
     }  
1064    
1065      if (!line->numElements)   if (elementsAlloced == line->numElements) {
1066   line->type = LT_WHITESPACE;   elementsAlloced += 5;
1067      else {   line->elements = realloc(line->elements,
1068   line->type = getTypeByKeyword(line->elements[0].item, cfi);   sizeof(*line->elements) *
1069   if (line->type == LT_UNKNOWN) {   elementsAlloced);
1070              /* zipl does [title] instead of something reasonable like all   }
              * the other boot loaders.  kind of ugly */  
             if (cfi->titleBracketed && isBracketedTitle(line)) {  
                 line->type = LT_TITLE;  
             }  
   
     /* this is awkward, but we need to be able to handle keywords  
        that begin with a # (specifically for #boot in grub.conf),  
        but still make comments lines with no elements (everything  
        stored in the indent */  
     if (*line->elements[0].item == '#') {  
  char * fullLine;  
  int len;  
  int i;  
   
  len = strlen(line->indent);  
  for (i = 0; i < line->numElements; i++)  
     len += strlen(line->elements[i].item) +  
    strlen(line->elements[i].indent);  
1071    
1072   fullLine = malloc(len + 1);   element = line->elements + line->numElements;
  strcpy(fullLine, line->indent);  
  free(line->indent);  
  line->indent = fullLine;  
1073    
1074   for (i = 0; i < line->numElements; i++) {   chptr = start;
1075      strcat(fullLine, line->elements[i].item);   while (*chptr && !isspace(*chptr)) {
1076      strcat(fullLine, line->elements[i].indent);   if (first && *chptr == '=')
1077      free(line->elements[i].item);   break;
1078      free(line->elements[i].indent);   chptr++;
1079   }   }
1080     if (line->type == LT_UNIDENTIFIED)
1081     line->type = getTypeByKeyword(start, cfi);
1082     element->item = strndup(start, chptr - start);
1083     start = chptr;
1084    
1085     /* lilo actually accepts the pathological case of
1086     * append = " foo " */
1087     if (*start == '=')
1088     chptr = start + 1;
1089     else
1090     chptr = start;
1091    
1092     do {
1093     for (; *chptr && isspace(*chptr); chptr++) ;
1094     if (*chptr == '=')
1095     chptr = chptr + 1;
1096     } while (isspace(*chptr));
1097    
1098   line->type = LT_WHITESPACE;   element->indent = strndup(start, chptr - start);
1099   line->numElements = 0;   start = chptr;
     }  
  } else {  
  struct keywordTypes *kw;  
   
  kw = getKeywordByType(line->type, cfi);  
1100    
1101   /* space isn't the only separator, we need to split   line->numElements++;
1102   * elements up more   first = 0;
1103   */   }
  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;  
1104    
1105   numNewElements = 0;   if (!line->numElements)
1106   p = line->elements[i].item;   line->type = LT_WHITESPACE;
1107   while (*p != '\0') {   else {
1108   if (*p == kw->separatorChar)   line->type = getTypeByKeyword(line->elements[0].item, cfi);
1109   numNewElements++;   if (line->type == LT_UNKNOWN) {
1110   p++;   /* zipl does [title] instead of something reasonable
1111   }   * like all the other boot loaders.  kind of ugly */
1112   if (line->numElements + numNewElements >= elementsAlloced) {   if (cfi->titleBracketed && isBracketedTitle(line)) {
1113   elementsAlloced += numNewElements + 5;   line->type = LT_TITLE;
  line->elements = realloc(line->elements,  
     sizeof(*line->elements) * elementsAlloced);  
1114   }   }
1115    
1116   for (j = line->numElements; j > i; j--) {   /* this is awkward, but we need to be able to handle
1117   line->elements[j + numNewElements] = line->elements[j];   * keywords that begin with a # (specifically for
1118     * #boot in grub.conf), but still make comments lines
1119     * with no elements (everything stored in the indent
1120     */
1121     if (*line->elements[0].item == '#') {
1122     char *fullLine;
1123     int len;
1124    
1125     len = strlen(line->indent);
1126     for (int i = 0; i < line->numElements; i++)
1127     len += strlen(line->elements[i].item) +
1128        strlen(line->elements[i].indent);
1129    
1130     fullLine = malloc(len + 1);
1131     strcpy(fullLine, line->indent);
1132     free(line->indent);
1133     line->indent = fullLine;
1134    
1135     for (int i = 0; i < line->numElements; i++) {
1136     strcat(fullLine,
1137           line->elements[i].item);
1138     strcat(fullLine,
1139           line->elements[i].indent);
1140     free(line->elements[i].item);
1141     free(line->elements[i].indent);
1142     }
1143    
1144     line->type = LT_WHITESPACE;
1145     line->numElements = 0;
1146   }   }
1147   line->numElements += numNewElements;   } else if (line->type == LT_INITRD) {
1148     struct keywordTypes *kw;
1149    
1150   p = line->elements[i].item;   kw = getKeywordByType(line->type, cfi);
  while (*p != '\0') {  
1151    
1152   while (*p != kw->separatorChar && *p != '\0') p++;   /* space isn't the only separator, we need to split
1153   if (*p == '\0') {   * elements up more
1154   break;   */
1155   }   if (!isspace(kw->separatorChar)) {
1156     char indent[2] = "";
1157     indent[0] = kw->separatorChar;
1158     for (int i = 1; i < line->numElements; i++) {
1159     char *p;
1160     int numNewElements;
1161    
1162     numNewElements = 0;
1163     p = line->elements[i].item;
1164     while (*p != '\0') {
1165     if (*p == kw->separatorChar)
1166     numNewElements++;
1167     p++;
1168     }
1169     if (line->numElements +
1170        numNewElements >= elementsAlloced) {
1171     elementsAlloced +=
1172        numNewElements + 5;
1173     line->elements =
1174        realloc(line->elements,
1175        sizeof(*line->
1176       elements) *
1177        elementsAlloced);
1178     }
1179    
1180   free(line->elements[i].indent);   for (int j = line->numElements; j > i;
1181   line->elements[i].indent = strdup(indent);       j--) {
1182   *p++ = '\0';   line->elements[j +
1183   i++;         numNewElements] =
1184   line->elements[i].item = strdup(p);      line->elements[j];
1185   line->elements[i].indent = strdup("");   }
1186   p = line->elements[i].item;   line->numElements += numNewElements;
1187    
1188     p = line->elements[i].item;
1189     while (*p != '\0') {
1190    
1191     while (*p != kw->separatorChar
1192           && *p != '\0')
1193     p++;
1194     if (*p == '\0') {
1195     break;
1196     }
1197    
1198     line->elements[i + 1].indent =
1199        line->elements[i].indent;
1200     line->elements[i].indent =
1201        strdup(indent);
1202     *p++ = '\0';
1203     i++;
1204     line->elements[i].item =
1205        strdup(p);
1206     }
1207     }
1208     }
1209     } else if (line->type == LT_SET_VARIABLE) {
1210     /* and if it's a "set blah=" we need to split it
1211     * yet a third way to avoid rhbz# XXX FIXME :/
1212     */
1213     char *eq;
1214     int l;
1215     int numElements = line->numElements;
1216     struct lineElement *newElements;
1217     eq = strchr(line->elements[1].item, '=');
1218     if (!eq)
1219     return 0;
1220     l = eq - line->elements[1].item;
1221     if (eq[1] != 0)
1222     numElements++;
1223     newElements = calloc(numElements,sizeof (*newElements));
1224     memcpy(&newElements[0], &line->elements[0],
1225           sizeof (newElements[0]));
1226     newElements[1].item =
1227     strndup(line->elements[1].item, l);
1228     newElements[1].indent = "=";
1229     *(eq++) = '\0';
1230     newElements[2].item = strdup(eq);
1231     free(line->elements[1].item);
1232     if (line->elements[1].indent)
1233     newElements[2].indent = line->elements[1].indent;
1234     for (int i = 2; i < line->numElements; i++) {
1235     newElements[i+1].item = line->elements[i].item;
1236     newElements[i+1].indent =
1237     line->elements[i].indent;
1238   }   }
1239      }   free(line->elements);
1240     line->elements = newElements;
1241     line->numElements = numElements;
1242   }   }
1243   }   }
     }  
1244    
1245      return 0;   return 0;
1246  }  }
1247    
1248  static struct grubConfig * readConfig(const char * inName,  static int isnumber(const char *s)
1249        struct configFileInfo * cfi) {  {
1250      int in;   int i;
1251      char * incoming = NULL, * head;   for (i = 0; s[i] != '\0'; i++)
1252      int rc;   if (s[i] < '0' || s[i] > '9')
1253      int sawEntry = 0;   return 0;
1254      int movedLine = 0;   return i;
1255      struct grubConfig * cfg;  }
1256      struct singleLine * last = NULL, * line, * defaultLine = NULL;  
1257      char * end;  static struct grubConfig *readConfig(const char *inName,
1258      struct singleEntry * entry = NULL;       struct configFileInfo *cfi)
1259      int i, len;  {
1260      char * buf;   int in;
1261     char *incoming = NULL, *head;
1262      if (!strcmp(inName, "-")) {   int rc;
1263   in = 0;   int sawEntry = 0;
1264      } else {   int movedLine = 0;
1265   if ((in = open(inName, O_RDONLY)) < 0) {   struct grubConfig *cfg;
1266      fprintf(stderr, _("error opening %s for read: %s\n"),   struct singleLine *last = NULL, *line, *defaultLine = NULL;
1267      inName, strerror(errno));   char *end;
1268      return NULL;   struct singleEntry *entry = NULL;
1269     int len;
1270     char *buf;
1271    
1272     if (inName == NULL) {
1273     printf("Could not find bootloader configuration\n");
1274     exit(1);
1275     } else if (!strcmp(inName, "-")) {
1276     in = 0;
1277     } else {
1278     if ((in = open(inName, O_RDONLY)) < 0) {
1279     fprintf(stderr, _("error opening %s for read: %s\n"),
1280     inName, strerror(errno));
1281     return NULL;
1282     }
1283   }   }
     }  
1284    
1285      rc = readFile(in, &incoming);   rc = readFile(in, &incoming);
1286      close(in);   close(in);
1287      if (rc) return NULL;   if (rc)
1288     return NULL;
1289      head = incoming;  
1290      cfg = malloc(sizeof(*cfg));   head = incoming;
1291      cfg->primaryIndent = strdup("");   cfg = malloc(sizeof(*cfg));
1292      cfg->secondaryIndent = strdup("\t");   cfg->primaryIndent = strdup("");
1293      cfg->flags = GRUB_CONFIG_NO_DEFAULT;   cfg->secondaryIndent = strdup("\t");
1294      cfg->cfi = cfi;   cfg->flags = GRUB_CONFIG_NO_DEFAULT;
1295      cfg->theLines = NULL;   cfg->cfi = cfi;
1296      cfg->entries = NULL;   cfg->theLines = NULL;
1297      cfg->fallbackImage = 0;   cfg->entries = NULL;
1298     cfg->fallbackImage = 0;
1299      /* copy everything we have */  
1300      while (*head) {   /* copy everything we have */
1301   line = malloc(sizeof(*line));   while (*head) {
1302   lineInit(line);   line = malloc(sizeof(*line));
1303     lineInit(line);
1304    
1305     if (getNextLine(&head, line, cfi)) {
1306     free(line);
1307     /* XXX memory leak of everything in cfg */
1308     return NULL;
1309     }
1310    
1311   if (getNextLine(&head, line, cfi)) {   if (!sawEntry && line->numElements) {
1312      free(line);   free(cfg->primaryIndent);
1313      /* XXX memory leak of everything in cfg */   cfg->primaryIndent = strdup(line->indent);
1314      return NULL;   } else if (line->numElements) {
1315   }   free(cfg->secondaryIndent);
1316     cfg->secondaryIndent = strdup(line->indent);
1317   if (!sawEntry && line->numElements) {   }
     free(cfg->primaryIndent);  
     cfg->primaryIndent = strdup(line->indent);  
  } else if (line->numElements) {  
     free(cfg->secondaryIndent);  
     cfg->secondaryIndent = strdup(line->indent);  
  }  
   
  if (isEntryStart(line, cfi)) {  
     sawEntry = 1;  
     if (!entry) {  
  cfg->entries = malloc(sizeof(*entry));  
  entry = cfg->entries;  
     } else {  
  entry->next = malloc(sizeof(*entry));  
  entry = entry->next;  
     }  
1318    
1319      entry->skip = 0;   if (isEntryStart(line, cfi) || (cfg->entries && !sawEntry)) {
1320              entry->multiboot = 0;   sawEntry = 1;
1321      entry->lines = NULL;   if (!entry) {
1322      entry->next = NULL;   cfg->entries = malloc(sizeof(*entry));
1323   }   entry = cfg->entries;
1324     } else {
1325   if (line->type == LT_DEFAULT && line->numElements == 2) {   entry->next = malloc(sizeof(*entry));
1326      cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;   entry = entry->next;
1327      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;  
     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';  
1328    
1329      for (i = 1; i < line->numElements; i++) {   entry->skip = 0;
1330   strcat(buf, line->elements[i].item);   entry->multiboot = 0;
1331   free(line->elements[i].item);   entry->lines = NULL;
1332     entry->next = NULL;
1333     }
1334    
1335   if ((i + 1) != line->numElements) {   if (line->type == LT_SET_VARIABLE) {
1336      strcat(buf, line->elements[i].indent);   dbgPrintf("found 'set' command (%d elements): ",
1337      free(line->elements[i].indent);    line->numElements);
1338   }   dbgPrintf("%s", line->indent);
1339      }   for (int i = 0; i < line->numElements; i++)
1340     dbgPrintf("\"%s\"%s", line->elements[i].item,
1341      line->elements[1].indent =    line->elements[i].indent);
1342      line->elements[line->numElements - 1].indent;   dbgPrintf("\n");
1343      line->elements[1].item = buf;   struct keywordTypes *kwType =
1344      line->numElements = 2;      getKeywordByType(LT_DEFAULT, cfi);
1345     if (kwType && line->numElements == 3
1346   } else if (line->type == LT_KERNELARGS && cfi->argsInQuotes) {      && !strcmp(line->elements[1].item, kwType->key)
1347      /* Strip off any " which may be present; they'll be put back      && !is_special_grub2_variable(
1348         on write. This is one of the few (the only?) places that grubby   line->elements[2].item)) {
1349         canonicalizes the output */   dbgPrintf("Line sets default config\n");
1350     cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
1351      if (line->numElements >= 2) {   defaultLine = line;
1352   int last, len;   }
1353     } else if (iskernel(line->type)) {
1354   if (*line->elements[1].item == '"')   /* if by some freak chance this is multiboot and the
1355      memmove(line->elements[1].item, line->elements[1].item + 1,   * "module" lines came earlier in the template, make
1356      strlen(line->elements[1].item + 1) + 1);   * sure to use LT_HYPER instead of LT_KERNEL now
1357     */
1358   last = line->numElements - 1;   if (entry && entry->multiboot)
1359   len = strlen(line->elements[last].item) - 1;   line->type = LT_HYPER;
  if (line->elements[last].item[len] == '"')  
     line->elements[last].item[len] = '\0';  
     }  
  }  
   
  /* If we find a generic config option which should live at the  
    top of the file, move it there. Old versions of grubby were  
    probably responsible for putting new images in the wrong  
    place in front of it anyway. */  
  if (sawEntry && line->type == LT_GENERIC) {  
  struct singleLine **l = &cfg->theLines;  
  struct singleLine **last_nonws = &cfg->theLines;  
  while (*l) {  
  if ((*l)->type != LT_WHITESPACE)  
  last_nonws = &((*l)->next);  
  l = &((*l)->next);  
  }  
  line->next = *last_nonws;  
  *last_nonws = line;  
  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) {  
  lineFree(line);  
  free(line);  
  movedLine = 0;  
  continue;  
  }  
  movedLine = 0;  
1360    
1361   if (sawEntry) {   } else if (line->type == LT_MBMODULE) {
1362      if (!entry->lines)   /* go back and fix the LT_KERNEL line to indicate
1363   entry->lines = line;   * LT_HYPER instead, now that we know this is a
1364      else   * multiboot entry.  This only applies to grub, but
1365   last->next = line;   * that's the only place we should find LT_MBMODULE
1366      dbgPrintf("readConfig added %d to %p\n", line->type, entry);   * lines anyway.
1367   } else {   */
1368      if (!cfg->theLines)   for (struct singleLine * l = entry->lines; l;
1369   cfg->theLines = line;       l = l->next) {
1370      else   if (l->type == LT_HYPER)
1371   last->next = line;   break;
1372      dbgPrintf("readConfig added %d to cfg\n", line->type);   else if (iskernel(l->type)) {
1373   }   l->type = LT_HYPER;
1374     break;
1375     }
1376     }
1377     entry->multiboot = 1;
1378    
1379   last = line;   } else if (line->type == LT_HYPER) {
1380      }   entry->multiboot = 1;
1381    
1382      free(incoming);   } else if (line->type == LT_FALLBACK && line->numElements == 2) {
1383     cfg->fallbackImage =
1384        strtol(line->elements[1].item, &end, 10);
1385     if (*end)
1386     cfg->fallbackImage = -1;
1387    
1388     } else if ((line->type == LT_DEFAULT && cfi->defaultIsUnquoted)
1389       || (line->type == LT_TITLE
1390           && line->numElements > 1)) {
1391     /* make the title/default a single argument (undoing
1392     * our parsing) */
1393     len = 0;
1394     for (int i = 1; i < line->numElements; i++) {
1395     len += strlen(line->elements[i].item);
1396     len += strlen(line->elements[i].indent);
1397     }
1398     buf = malloc(len + 1);
1399     *buf = '\0';
1400    
1401      if (defaultLine) {   for (int i = 1; i < line->numElements; i++) {
1402   if (cfi->defaultSupportSaved &&   strcat(buf, line->elements[i].item);
1403   !strncmp(defaultLine->elements[1].item, "saved", 5)) {   free(line->elements[i].item);
1404      cfg->defaultImage = DEFAULT_SAVED;  
1405   } else if (cfi->defaultIsIndex) {   if ((i + 1) != line->numElements) {
1406      cfg->defaultImage = strtol(defaultLine->elements[1].item, &end, 10);   strcat(buf, line->elements[i].indent);
1407      if (*end) cfg->defaultImage = -1;   free(line->elements[i].indent);
1408   } else if (defaultLine->numElements >= 2) {   }
1409      i = 0;   }
     while ((entry = findEntryByIndex(cfg, i))) {  
  for (line = entry->lines; line; line = line->next)  
     if (line->type == LT_TITLE) break;  
   
                 if (!cfi->titleBracketed) {  
                     if (line && (line->numElements >= 2) &&  
                         !strcmp(defaultLine->elements[1].item,  
                                 line->elements[1].item)) break;  
                 } else if (line) {  
                     if (!strcmp(defaultLine->elements[1].item,  
                                 extractTitle(line))) break;  
                 }  
  i++;  
  entry = NULL;  
     }  
1410    
1411      if (entry){   line->elements[1].indent =
1412          cfg->defaultImage = i;      line->elements[line->numElements - 1].indent;
1413      }else{   line->elements[1].item = buf;
1414          cfg->defaultImage = -1;   line->numElements = 2;
1415      }   } else if (line->type == LT_MENUENTRY && line->numElements > 3) {
1416   }   /* let --remove-kernel="TITLE=what" work */
1417      } else {   len = 0;
1418          cfg->defaultImage = 0;   char *extras;
1419      }   char *title;
1420     /* initially unseen value */
1421     char quote_char = '\0';
1422    
1423     for (int i = 1; i < line->numElements; i++) {
1424     len += strlen(line->elements[i].item);
1425     len += strlen(line->elements[i].indent);
1426     }
1427     buf = malloc(len + 1);
1428     *buf = '\0';
1429    
1430      return cfg;   /* allocate mem for extra flags. */
1431  }   extras = malloc(len + 1);
1432     *extras = '\0';
1433    
1434     /* get title. */
1435     for (int i = 0; i < line->numElements; i++) {
1436     if (!strcmp
1437        (line->elements[i].item, "menuentry"))
1438     continue;
1439     if (isquote(*line->elements[i].item)
1440        && quote_char == '\0') {
1441     /* ensure we properly pair off quotes */
1442     quote_char = *line->elements[i].item;
1443     title = line->elements[i].item + 1;
1444     } else {
1445     title = line->elements[i].item;
1446     }
1447    
1448  static void writeDefault(FILE * out, char * indent,   len = strlen(title);
1449   char * separator, struct grubConfig * cfg) {   if (title[len - 1] == quote_char) {
1450      struct singleEntry * entry;   strncat(buf, title, len - 1);
1451      struct singleLine * line;   break;
1452      int i;   } else {
1453     strcat(buf, title);
1454     strcat(buf, line->elements[i].indent);
1455     }
1456     }
1457    
1458      if (!cfg->defaultImage && cfg->flags == GRUB_CONFIG_NO_DEFAULT) return;   /* get extras */
1459     int count = 0;
1460     quote_char = '\0';
1461     for (int i = 0; i < line->numElements; i++) {
1462     if (count >= 2) {
1463     strcat(extras, line->elements[i].item);
1464     strcat(extras,
1465           line->elements[i].indent);
1466     }
1467    
1468      if (cfg->defaultImage == DEFAULT_SAVED)   if (!strcmp
1469   fprintf(out, "%sdefault%ssaved\n", indent, separator);      (line->elements[i].item, "menuentry"))
1470      else if (cfg->defaultImage > -1) {   continue;
1471   if (cfg->cfi->defaultIsIndex) {  
1472      fprintf(out, "%sdefault%s%d\n", indent, separator,   /* count ' or ", there should be two in menuentry line. */
1473      cfg->defaultImage);   if (isquote(*line->elements[i].item)
1474   } else {      && quote_char == '\0') {
1475      int image = cfg->defaultImage;   /* ensure we properly pair off quotes */
1476     quote_char = *line->elements[i].item;
1477     count++;
1478     }
1479    
1480      entry = cfg->entries;   len = strlen(line->elements[i].item);
     while (entry && entry->skip) entry = entry->next;  
1481    
1482      i = 0;   if (line->elements[i].item[len - 1] ==
1483      while (entry && i < image) {      quote_char)
1484   entry = entry->next;   count++;
1485    
1486   while (entry && entry->skip) entry = entry->next;   /* ok, we get the final ' or ", others are extras. */
1487   i++;   }
1488      }   line->elements[1].indent =
1489        line->elements[line->numElements - 2].indent;
1490     line->elements[1].item = buf;
1491     line->elements[2].indent =
1492        line->elements[line->numElements - 2].indent;
1493     line->elements[2].item = extras;
1494     line->numElements = 3;
1495     } else if (line->type == LT_KERNELARGS && cfi->argsInQuotes) {
1496     /* Strip off any " which may be present; they'll be
1497     * put back on write. This is one of the few (the
1498     * only?) places that grubby canonicalizes the output
1499     */
1500     if (line->numElements >= 2) {
1501     int last, len;
1502    
1503      if (!entry) return;   if (isquote(*line->elements[1].item))
1504     memmove(line->elements[1].item,
1505     line->elements[1].item + 1,
1506     strlen(line->elements[1].item +
1507           1) + 1);
1508    
1509     last = line->numElements - 1;
1510     len = strlen(line->elements[last].item) - 1;
1511     if (isquote(line->elements[last].item[len]))
1512     line->elements[last].item[len] = '\0';
1513     }
1514     }
1515    
1516      line = getLineByType(LT_TITLE, entry->lines);   if (line->type == LT_DEFAULT && line->numElements == 2) {
1517     cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
1518     defaultLine = line;
1519     }
1520    
1521      if (line && line->numElements >= 2)   /* If we find a generic config option which should live at the
1522   fprintf(out, "%sdefault%s%s\n", indent, separator,     top of the file, move it there. Old versions of grubby were
1523   line->elements[1].item);     probably responsible for putting new images in the wrong
1524              else if (line && (line->numElements == 1) &&     place in front of it anyway. */
1525                       cfg->cfi->titleBracketed) {   if (sawEntry && line->type == LT_GENERIC) {
1526   fprintf(out, "%sdefault%s%s\n", indent, separator,   struct singleLine **l = &cfg->theLines;
1527                          extractTitle(line));   struct singleLine **last_nonws = &cfg->theLines;
1528              }   while (*l) {
1529   }   if ((*l)->type != LT_WHITESPACE)
1530      }   last_nonws = &((*l)->next);
1531  }   l = &((*l)->next);
1532     }
1533     line->next = *last_nonws;
1534     *last_nonws = line;
1535     movedLine = 1;
1536     continue; /* without setting 'last' */
1537     }
1538    
1539  static int writeConfig(struct grubConfig * cfg, char * outName,   /* If a second line of whitespace happens after a generic
1540         const char * prefix) {   * option which was moved, drop it. */
1541      FILE * out;   if (movedLine && line->type == LT_WHITESPACE
1542      struct singleLine * line;      && last->type == LT_WHITESPACE) {
1543      struct singleEntry * entry;   lineFree(line);
1544      char * tmpOutName;   free(line);
1545      int needs = MAIN_DEFAULT;   movedLine = 0;
1546      struct stat sb;   continue;
1547      int i;   }
1548     movedLine = 0;
     if (!strcmp(outName, "-")) {  
  out = stdout;  
  tmpOutName = NULL;  
     } else {  
  if (!lstat(outName, &sb) && S_ISLNK(sb.st_mode)) {  
     char * buf;  
     int len = 256;  
     int rc;  
   
     /* most likely the symlink is relative, so change our  
        directory to the dir of the symlink */  
             rc = chdir(dirname(strdupa(outName)));  
     do {  
  buf = alloca(len + 1);  
  rc = readlink(basename(outName), buf, len);  
  if (rc == len) len += 256;  
     } while (rc == len);  
       
     if (rc < 0) {  
  fprintf(stderr, _("grubby: error readlink link %s: %s\n"),  
  outName, strerror(errno));  
  return 1;  
     }  
   
     outName = buf;  
     outName[rc] = '\0';  
  }  
   
  tmpOutName = alloca(strlen(outName) + 2);  
  sprintf(tmpOutName, "%s-", outName);  
  out = fopen(tmpOutName, "w");  
  if (!out) {  
     fprintf(stderr, _("grubby: error creating %s: %s\n"), tmpOutName,  
     strerror(errno));  
     return 1;  
  }  
   
  if (!stat(outName, &sb)) {  
     if (chmod(tmpOutName, sb.st_mode & ~(S_IFMT))) {  
  fprintf(stderr, _("grubby: error setting perms on %s: %s\n"),  
         tmpOutName, strerror(errno));  
  fclose(out);  
  unlink(tmpOutName);  
                 return 1;  
     }  
  }  
     }  
1549    
1550      line = cfg->theLines;   if (sawEntry) {
1551      while (line) {   if (!entry->lines)
1552   if (line->type == LT_DEFAULT) {   entry->lines = line;
1553      writeDefault(out, line->indent, line->elements[0].indent, cfg);   else
1554      needs &= ~MAIN_DEFAULT;   last->next = line;
1555   } else if (line->type == LT_FALLBACK) {   dbgPrintf("readConfig added %s to %p\n",
1556      if (cfg->fallbackImage > -1)    getKeyByType(line->type, cfi), entry);
  fprintf(out, "%s%s%s%d\n", line->indent,  
  line->elements[0].item, line->elements[0].indent,  
  cfg->fallbackImage);  
  } else {  
     if (lineWrite(out, line, cfg->cfi) == -1) {  
                 fprintf(stderr, _("grubby: error writing %s: %s\n"),  
                         tmpOutName, strerror(errno));  
                 fclose(out);  
                 unlink(tmpOutName);  
                 return 1;  
             }  
  }  
1557    
1558   line = line->next;   /* we could have seen this outside of an entry... if
1559      }   * so, we ignore it like any other line we don't grok
1560     */
1561     if (line->type == LT_ENTRY_END && sawEntry)
1562     sawEntry = 0;
1563     } else {
1564     if (!cfg->theLines)
1565     cfg->theLines = line;
1566     else
1567     last->next = line;
1568     dbgPrintf("readConfig added %s to cfg\n",
1569      getKeyByType(line->type, cfi));
1570     }
1571    
1572      if (needs & MAIN_DEFAULT) {   last = line;
1573   writeDefault(out, cfg->primaryIndent, "=", cfg);   }
  needs &= ~MAIN_DEFAULT;  
     }  
1574    
1575      i = 0;   free(incoming);
     while ((entry = findEntryByIndex(cfg, i++))) {  
  if (entry->skip) continue;  
1576    
1577   line = entry->lines;   dbgPrintf("defaultLine is %s\n", defaultLine ? "set" : "unset");
1578   while (line) {   if (defaultLine) {
1579      if (lineWrite(out, line, cfg->cfi) == -1) {   if (defaultLine->numElements > 2 &&
1580                  fprintf(stderr, _("grubby: error writing %s: %s\n"),      cfi->defaultSupportSaved &&
1581                          tmpOutName, strerror(errno));      !strncmp(defaultLine->elements[2].item,
1582                  fclose(out);       "\"${saved_entry}\"", 16)) {
1583                  unlink(tmpOutName);   cfg->cfi->defaultIsSaved = 1;
1584                  return 1;   cfg->defaultImage = DEFAULT_SAVED_GRUB2;
1585              }   if (cfg->cfi->getEnv) {
1586      line = line->next;   char *defTitle =
1587   }      cfi->getEnv(cfg->cfi, "saved_entry");
1588      }   if (defTitle) {
1589     int index = 0;
1590     if (isnumber(defTitle)) {
1591     index = atoi(defTitle);
1592     entry =
1593        findEntryByIndex(cfg,
1594         index);
1595     } else {
1596     entry =
1597        findEntryByTitle(cfg,
1598         defTitle,
1599         &index);
1600     }
1601     if (entry)
1602     cfg->defaultImage = index;
1603     }
1604     }
1605     } else if (cfi->defaultIsVariable) {
1606     if (defaultLine->numElements == 2) {
1607     char *value = defaultLine->elements[1].item + 8;
1608     while (*value && (*value == '"' ||
1609      *value == '\'' ||
1610      *value == ' ' ||
1611      *value == '\t'))
1612     value++;
1613     cfg->defaultImage = strtol(value, &end, 10);
1614     while (*end && (*end == '"' || *end == '\'' ||
1615     *end == ' ' || *end == '\t'))
1616     end++;
1617     if (*end)
1618     cfg->defaultImage = -1;
1619     } else if (defaultLine->numElements == 3) {
1620     char *value = defaultLine->elements[2].item;
1621     while (*value && (*value == '"' ||
1622      *value == '\'' ||
1623      *value == ' ' ||
1624      *value == '\t'))
1625     value++;
1626     cfg->defaultImage = strtol(value, &end, 10);
1627     while (*end && (*end == '"' || *end == '\'' ||
1628     *end == ' ' || *end == '\t'))
1629     end++;
1630     if (*end)
1631     cfg->defaultImage = -1;
1632     }
1633     } else if (cfi->defaultSupportSaved &&
1634       !strncmp(defaultLine->elements[1].item, "saved",
1635        5)) {
1636     cfg->defaultImage = DEFAULT_SAVED;
1637     } else if (cfi->defaultIsIndex) {
1638     cfg->defaultImage =
1639        strtol(defaultLine->elements[1].item, &end, 10);
1640     if (*end)
1641     cfg->defaultImage = -1;
1642     } else if (defaultLine->numElements >= 2) {
1643     int i = 0;
1644     while ((entry = findEntryByIndex(cfg, i))) {
1645     for (line = entry->lines; line;
1646         line = line->next)
1647     if (line->type == LT_TITLE)
1648     break;
1649    
1650     if (!cfi->titleBracketed) {
1651     if (line && (line->numElements >= 2) &&
1652        !strcmp(defaultLine->elements[1].
1653        item,
1654        line->elements[1].item))
1655     break;
1656     } else if (line) {
1657     if (!strcmp
1658        (defaultLine->elements[1].item,
1659         extractTitle(cfg, line)))
1660     break;
1661     }
1662     i++;
1663     entry = NULL;
1664     }
1665    
1666      if (tmpOutName) {   if (entry) {
1667   if (rename(tmpOutName, outName)) {   cfg->defaultImage = i;
1668      fprintf(stderr, _("grubby: error moving %s to %s: %s\n"),   } else {
1669      tmpOutName, outName, strerror(errno));   cfg->defaultImage = -1;
1670      unlink(outName);   }
1671              return 1;   }
1672     } else if (cfg->cfi->defaultIsSaved && cfg->cfi->getEnv) {
1673     char *defTitle = cfi->getEnv(cfg->cfi, "saved_entry");
1674     if (defTitle) {
1675     int index = 0;
1676     if (isnumber(defTitle)) {
1677     index = atoi(defTitle);
1678     entry = findEntryByIndex(cfg, index);
1679     } else {
1680     entry = findEntryByTitle(cfg, defTitle, &index);
1681     }
1682     if (entry)
1683     cfg->defaultImage = index;
1684     }
1685     } else {
1686     cfg->defaultImage = 0;
1687   }   }
     }  
   
     return 0;  
 }  
1688    
1689  static int numEntries(struct grubConfig *cfg) {   return cfg;
     int i = 0;  
     struct singleEntry * entry;  
   
     entry = cfg->entries;  
     while (entry) {  
         if (!entry->skip)  
             i++;  
         entry = entry->next;  
     }  
     return i;  
1690  }  }
1691    
1692  static char *findDiskForRoot()  static void writeDefault(FILE * out, char *indent,
1693     char *separator, struct grubConfig *cfg)
1694  {  {
1695      int fd;   struct singleEntry *entry;
1696      char buf[65536];   struct singleLine *line;
1697      char *devname;   int i;
1698      char *chptr;  
1699      int rc;   if (!cfg->defaultImage && cfg->flags == GRUB_CONFIG_NO_DEFAULT)
1700     return;
1701    
1702     if (cfg->defaultImage == DEFAULT_SAVED)
1703     fprintf(out, "%sdefault%ssaved\n", indent, separator);
1704     else if (cfg->cfi->defaultIsSaved) {
1705     fprintf(out, "%sset default=\"${saved_entry}\"\n", indent);
1706     if (cfg->defaultImage >= 0 && cfg->cfi->setEnv) {
1707     char *title;
1708     entry = findEntryByIndex(cfg, cfg->defaultImage);
1709     line = getLineByType(LT_MENUENTRY, entry->lines);
1710     if (!line)
1711     line = getLineByType(LT_TITLE, entry->lines);
1712     if (line) {
1713     title = extractTitle(cfg, line);
1714     if (title)
1715     cfg->cfi->setEnv(cfg->cfi,
1716     "saved_entry", title);
1717     }
1718     }
1719     } else if (cfg->defaultImage > -1) {
1720     if (cfg->cfi->defaultIsIndex) {
1721     if (cfg->cfi->defaultIsVariable) {
1722     fprintf(out, "%sset default=\"%d\"\n", indent,
1723     cfg->defaultImage);
1724     } else {
1725     fprintf(out, "%sdefault%s%d\n", indent,
1726     separator, cfg->defaultImage);
1727     }
1728     } else {
1729     int image = cfg->defaultImage;
1730    
1731      if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) {   entry = cfg->entries;
1732          fprintf(stderr, "grubby: failed to open %s: %s\n",   while (entry && entry->skip)
1733                  _PATH_MOUNTED, strerror(errno));   entry = entry->next;
1734          return NULL;  
1735      }   i = 0;
1736     while (entry && i < image) {
1737     entry = entry->next;
1738    
1739      rc = read(fd, buf, sizeof(buf) - 1);   while (entry && entry->skip)
1740      if (rc <= 0) {   entry = entry->next;
1741          fprintf(stderr, "grubby: failed to read %s: %s\n",   i++;
1742                  _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++;  
     }  
1743    
1744      return NULL;   if (!entry)
1745  }   return;
1746    
1747  int suitableImage(struct singleEntry * entry, const char * bootPrefix,   line = getLineByType(LT_TITLE, entry->lines);
   int skipRemoved, int flags) {  
     struct singleLine * line;  
     char * fullName;  
     int i;  
     char * dev;  
     char * rootspec;  
     char * rootdev;  
   
     if (skipRemoved && entry->skip) return 0;  
   
     line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);  
     if (!line || line->numElements < 2) 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;  
   
     for (i = 2; i < line->numElements; i++)  
  if (!strncasecmp(line->elements[i].item, "root=", 5)) break;  
     if (i < line->numElements) {  
  dev = line->elements[i].item + 5;  
     } else {  
  /* look for a lilo style LT_ROOT line */  
  line = getLineByType(LT_ROOT, entry->lines);  
1748    
1749   if (line && line->numElements >= 2) {   if (line && line->numElements >= 2)
1750      dev = line->elements[1].item;   fprintf(out, "%sdefault%s%s\n", indent,
1751   } else {   separator, line->elements[1].item);
1752      /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS.   else if (line && (line->numElements == 1)
1753       * grub+multiboot uses LT_MBMODULE for the args, so check that too.   && cfg->cfi->titleBracketed) {
1754       */   char *title = extractTitle(cfg, line);
1755      line = getLineByType(LT_KERNELARGS|LT_MBMODULE, entry->lines);   if (title) {
1756     fprintf(out, "%sdefault%s%s\n", indent,
1757              /* failed to find one */   separator, title);
1758              if (!line) return 0;   free(title);
1759     }
1760      for (i = 1; i < line->numElements; i++)   }
1761          if (!strncasecmp(line->elements[i].item, "root=", 5)) break;   }
     if (i < line->numElements)  
         dev = line->elements[i].item + 5;  
     else {  
  /* it failed too...  can't find root= */  
         return 0;  
             }  
1762   }   }
1763      }  }
   
     dev = getpathbyspec(dev);  
     if (!dev)  
         return 0;  
   
     rootdev = findDiskForRoot();  
     if (!rootdev)  
  return 0;  
   
     if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) {  
         free(rootdev);  
         return 0;  
     }  
1764    
1765      if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) {  static int writeConfig(struct grubConfig *cfg, char *outName,
1766   free(rootdev);         const char *prefix)
1767          return 0;  {
1768      }   FILE *out;
1769     struct singleLine *line;
1770     struct singleEntry *entry;
1771     char *tmpOutName;
1772     int needs = MAIN_DEFAULT;
1773     struct stat sb;
1774     int i;
1775    
1776     if (!strcmp(outName, "-")) {
1777     out = stdout;
1778     tmpOutName = NULL;
1779     } else {
1780     if (!lstat(outName, &sb) && S_ISLNK(sb.st_mode)) {
1781     char *buf;
1782     int len = 256;
1783     int rc;
1784    
1785     /* most likely the symlink is relative, so change our
1786       directory to the dir of the symlink */
1787     char *dir = strdupa(outName);
1788     rc = chdir(dirname(dir));
1789     do {
1790     buf = alloca(len + 1);
1791     rc = readlink(basename(outName), buf, len);
1792     if (rc == len)
1793     len += 256;
1794     } while (rc == len);
1795    
1796     if (rc < 0) {
1797     fprintf(stderr,
1798     _
1799     ("grubby: error readlink link %s: %s\n"),
1800     outName, strerror(errno));
1801     return 1;
1802     }
1803    
1804      free(rootdev);   outName = buf;
1805     outName[rc] = '\0';
1806     }
1807    
1808      return 1;   tmpOutName = alloca(strlen(outName) + 2);
1809  }   sprintf(tmpOutName, "%s-", outName);
1810     out = fopen(tmpOutName, "w");
1811     if (!out) {
1812     fprintf(stderr, _("grubby: error creating %s: %s\n"),
1813     tmpOutName, strerror(errno));
1814     return 1;
1815     }
1816    
1817  /* returns the first match on or after the one pointed to by index (if index   if (!stat(outName, &sb)) {
1818     is not NULL) which is not marked as skip */   if (chmod(tmpOutName, sb.st_mode & ~(S_IFMT))) {
1819  struct singleEntry * findEntryByPath(struct grubConfig * config,   fprintf(stderr,
1820       const char * kernel, const char * prefix,   _
1821       int * index) {   ("grubby: error setting perms on %s: %s\n"),
1822      struct singleEntry * entry = NULL;   tmpOutName, strerror(errno));
1823      struct singleLine * line;   fclose(out);
1824      int i;   unlink(tmpOutName);
1825      char * chptr;   return 1;
1826      char * rootspec = NULL;   }
1827      enum lineType_e checkType = LT_KERNEL;   }
1828     }
1829    
1830      if (isdigit(*kernel)) {   line = cfg->theLines;
1831   int * indexVars = alloca(sizeof(*indexVars) * strlen(kernel));   struct keywordTypes *defaultKw = getKeywordByType(LT_DEFAULT, cfg->cfi);
1832     while (line) {
1833     if (line->type == LT_SET_VARIABLE && defaultKw &&
1834        line->numElements == 3 &&
1835        !strcmp(line->elements[1].item, defaultKw->key) &&
1836        !is_special_grub2_variable(line->elements[2].item)) {
1837     writeDefault(out, line->indent,
1838         line->elements[0].indent, cfg);
1839     needs &= ~MAIN_DEFAULT;
1840     } else if (line->type == LT_DEFAULT) {
1841     writeDefault(out, line->indent,
1842         line->elements[0].indent, cfg);
1843     needs &= ~MAIN_DEFAULT;
1844     } else if (line->type == LT_FALLBACK) {
1845     if (cfg->fallbackImage > -1)
1846     fprintf(out, "%s%s%s%d\n", line->indent,
1847     line->elements[0].item,
1848     line->elements[0].indent,
1849     cfg->fallbackImage);
1850     } else {
1851     if (lineWrite(out, line, cfg->cfi) == -1) {
1852     fprintf(stderr,
1853     _("grubby: error writing %s: %s\n"),
1854     tmpOutName, strerror(errno));
1855     fclose(out);
1856     unlink(tmpOutName);
1857     return 1;
1858     }
1859     }
1860    
1861   i = 0;   line = line->next;
  indexVars[i] = strtol(kernel, &chptr, 10);  
  while (*chptr == ',') {  
     i++;  
     kernel = chptr + 1;  
     indexVars[i] = strtol(kernel, &chptr, 10);  
1862   }   }
1863    
1864   if (*chptr) {   if (needs & MAIN_DEFAULT) {
1865      /* can't parse it, bail */   writeDefault(out, cfg->primaryIndent, "=", cfg);
1866      return NULL;   needs &= ~MAIN_DEFAULT;
1867   }   }
1868    
  indexVars[i + 1] = -1;  
   
1869   i = 0;   i = 0;
1870   if (index) {   while ((entry = findEntryByIndex(cfg, i++))) {
1871      while (i < *index) i++;   if (entry->skip)
1872      if (indexVars[i] == -1) return NULL;   continue;
1873    
1874     line = entry->lines;
1875     while (line) {
1876     if (lineWrite(out, line, cfg->cfi) == -1) {
1877     fprintf(stderr,
1878     _("grubby: error writing %s: %s\n"),
1879     tmpOutName, strerror(errno));
1880     fclose(out);
1881     unlink(tmpOutName);
1882     return 1;
1883     }
1884     line = line->next;
1885     }
1886   }   }
1887    
1888   entry = findEntryByIndex(config, indexVars[i]);   if (tmpOutName) {
1889   if (!entry) return NULL;   if (rename(tmpOutName, outName)) {
1890     fprintf(stderr,
1891     _("grubby: error moving %s to %s: %s\n"),
1892     tmpOutName, outName, strerror(errno));
1893     unlink(outName);
1894     return 1;
1895     }
1896     }
1897    
1898   line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);   return 0;
1899   if (!line) return NULL;  }
1900    
1901   if (index) *index = indexVars[i];  static int numEntries(struct grubConfig *cfg)
1902   return entry;  {
1903      }   int i = 0;
1904         struct singleEntry *entry;
     if (!strcmp(kernel, "DEFAULT")) {  
  if (index && *index > config->defaultImage) {  
     entry = NULL;  
  } else {  
     entry = findEntryByIndex(config, config->defaultImage);  
     if (entry && entry->skip)  
  entry = NULL;  
     else if (index)  
  *index = config->defaultImage;  
  }  
     } else if (!strcmp(kernel, "ALL")) {  
  if (index)  
     i = *index;  
  else  
     i = 0;  
1905    
1906   while ((entry = findEntryByIndex(config, i))) {   entry = cfg->entries;
1907      if (!entry->skip) break;   while (entry) {
1908      i++;   if (!entry->skip)
1909     i++;
1910     entry = entry->next;
1911   }   }
1912     return i;
1913    }
1914    
1915   if (entry && index)  static char *findDiskForRoot()
1916      *index = i;  {
1917      } else {   int fd;
1918   if (index)   char buf[65536];
1919      i = *index;   char *devname;
1920   else   char *chptr;
1921      i = 0;   int rc;
1922    
1923   if (!strncmp(kernel, "TITLE=", 6)) {   if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) {
1924      prefix = "";   fprintf(stderr, "grubby: failed to open %s: %s\n",
1925      checkType = LT_TITLE;   _PATH_MOUNTED, strerror(errno));
1926      kernel += 6;   return NULL;
1927     }
1928    
1929     rc = read(fd, buf, sizeof(buf) - 1);
1930     if (rc <= 0) {
1931     fprintf(stderr, "grubby: failed to read %s: %s\n",
1932     _PATH_MOUNTED, strerror(errno));
1933     close(fd);
1934     return NULL;
1935   }   }
1936     close(fd);
1937     buf[rc] = '\0';
1938     chptr = buf;
1939    
1940   for (entry = findEntryByIndex(config, i); entry; entry = entry->next, i++) {   char *foundanswer = NULL;
     if (entry->skip) continue;  
1941    
1942      dbgPrintf("findEntryByPath looking for %d %s in %p\n", checkType, kernel, entry);   while (chptr && chptr != buf + rc) {
1943     devname = chptr;
1944    
1945     /*
1946     * The first column of a mtab entry is the device, but if the
1947     * entry is a special device it won't start with /, so move
1948     * on to the next line.
1949     */
1950     if (*devname != '/') {
1951     chptr = strchr(chptr, '\n');
1952     if (chptr)
1953     chptr++;
1954     continue;
1955     }
1956    
1957      /* check all the lines matching checkType */   /* Seek to the next space */
1958      for (line = entry->lines; line; line = line->next) {   chptr = strchr(chptr, ' ');
1959   line = getLineByType(entry->multiboot && checkType == LT_KERNEL ?   if (!chptr) {
1960       LT_KERNEL|LT_MBMODULE|LT_HYPER :   fprintf(stderr, "grubby: error parsing %s: %s\n",
1961       checkType, line);   _PATH_MOUNTED, strerror(errno));
1962   if (!line) break;  /* not found in this entry */   return NULL;
1963     }
1964    
1965   if (line && line->numElements >= 2) {   /*
1966      rootspec = getRootSpecifier(line->elements[1].item);   * The second column of a mtab entry is the mount point, we
1967      if (!strcmp(line->elements[1].item +   * are looking for '/' obviously.
1968   ((rootspec != NULL) ? strlen(rootspec) : 0),   */
1969   kernel + strlen(prefix)))   if (*(++chptr) == '/' && *(++chptr) == ' ') {
1970   break;   /* remember the last / entry in mtab */
1971     foundanswer = devname;
1972   }   }
     }  
1973    
1974      /* make sure this entry has a kernel identifier; this skips   /* Next line */
1975       * non-Linux boot entries (could find netbsd etc, though, which is   chptr = strchr(chptr, '\n');
1976       * unfortunate)   if (chptr)
1977       */   chptr++;
     if (line && getLineByType(LT_KERNEL|LT_HYPER, entry->lines))  
  break; /* found 'im! */  
1978   }   }
1979    
1980   if (index) *index = i;   /* Return the last / entry found */
1981      }   if (foundanswer) {
1982     chptr = strchr(foundanswer, ' ');
1983     *chptr = '\0';
1984     return strdup(foundanswer);
1985     }
1986    
1987      return entry;   return NULL;
1988  }  }
1989    
1990  struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index) {  void printEntry(struct singleEntry *entry, FILE * f)
1991      struct singleEntry * entry;  {
1992     int i;
1993     struct singleLine *line;
1994    
1995      entry = cfg->entries;   for (line = entry->lines; line; line = line->next) {
1996      while (index && entry) {   log_message(f, "DBG: %s", line->indent);
1997   entry = entry->next;   for (i = 0; i < line->numElements; i++) {
1998   index--;   /* Need to handle this, because we strip the quotes from
1999      }   * menuentry when read it. */
2000     if (line->type == LT_MENUENTRY && i == 1) {
2001     if (!isquote(*line->elements[i].item))
2002     log_message(f, "\'%s\'",
2003        line->elements[i].item);
2004     else
2005     log_message(f, "%s",
2006        line->elements[i].item);
2007     log_message(f, "%s", line->elements[i].indent);
2008    
2009      return entry;   continue;
2010  }   }
2011    
2012  /* Find a good template to use for the new kernel. An entry is   log_message(f, "%s%s",
2013   * good if the kernel and mkinitrd exist (even if the entry      line->elements[i].item,
2014   * is going to be removed). Try and use the default entry, but      line->elements[i].indent);
2015   * if that doesn't work just take the first. If we can't find one,   }
2016   * bail. */   log_message(f, "\n");
 struct singleEntry * findTemplate(struct grubConfig * cfg, const char * prefix,  
  int * indexPtr, int skipRemoved, int flags) {  
     struct singleEntry * entry, * entry2;  
     int index;  
   
     if (cfg->defaultImage > -1) {  
  entry = findEntryByIndex(cfg, cfg->defaultImage);  
  if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {  
     if (indexPtr) *indexPtr = cfg->defaultImage;  
     return entry;  
2017   }   }
2018      }  }
2019    
2020    void notSuitablePrintf(struct singleEntry *entry, int okay, const char *fmt,
2021           ...)
2022    {
2023     static int once;
2024     va_list argp, argq;
2025    
2026      index = 0;   va_start(argp, fmt);
     while ((entry = findEntryByIndex(cfg, index))) {  
  if (suitableImage(entry, prefix, skipRemoved, flags)) {  
             int j;  
             for (j = 0; j < index; j++) {  
                 entry2 = findEntryByIndex(cfg, j);  
                 if (entry2->skip) index--;  
             }  
     if (indexPtr) *indexPtr = index;  
2027    
2028      return entry;   va_copy(argq, argp);
2029     if (!once) {
2030     log_time(NULL);
2031     log_message(NULL, "command line: %s\n", saved_command_line);
2032   }   }
2033     log_message(NULL, "DBG: Image entry %s: ",
2034        okay ? "succeeded" : "failed");
2035     log_vmessage(NULL, fmt, argq);
2036    
2037   index++;   printEntry(entry, NULL);
2038      }   va_end(argq);
2039    
2040      fprintf(stderr, _("grubby fatal error: unable to find a suitable template\n"));   if (!debug) {
2041     once = 1;
2042     va_end(argp);
2043     return;
2044     }
2045    
2046     if (okay) {
2047     va_end(argp);
2048     return;
2049     }
2050    
2051      return NULL;   if (!once)
2052     log_message(stderr, "DBG: command line: %s\n",
2053        saved_command_line);
2054     once = 1;
2055     fprintf(stderr, "DBG: Image entry failed: ");
2056     vfprintf(stderr, fmt, argp);
2057     printEntry(entry, stderr);
2058     va_end(argp);
2059  }  }
2060    
2061  char * findBootPrefix(void) {  #define beginswith(s, c) ((s) && (s)[0] == (c))
     struct stat sb, sb2;  
2062    
2063      stat("/", &sb);  static int endswith(const char *s, char c)
2064  #ifdef __ia64__  {
2065      stat("/boot/efi/EFI/redhat/", &sb2);   int slen;
 #else  
     stat("/boot", &sb2);  
 #endif  
2066    
2067      if (sb.st_dev == sb2.st_dev)   if (!s || !s[0])
2068   return strdup("");   return 0;
2069     slen = strlen(s) - 1;
2070    
2071  #ifdef __ia64__   return s[slen] == c;
     return strdup("/boot/efi/EFI/redhat/");  
 #else  
     return strdup("/boot");  
 #endif  
2072  }  }
2073    
2074  void markRemovedImage(struct grubConfig * cfg, const char * image,  int suitableImage(struct singleEntry *entry, const char *bootPrefix,
2075        const char * prefix) {    int skipRemoved, int flags)
2076      struct singleEntry * entry;  {
2077     struct singleLine *line;
2078      if (!image) return;   char *fullName;
2079     int i;
2080      while ((entry = findEntryByPath(cfg, image, prefix, NULL)))   char *dev;
2081   entry->skip = 1;   char *rootspec;
2082  }   char *rootdev;
2083    
2084  void setDefaultImage(struct grubConfig * config, int hasNew,   if (skipRemoved && entry->skip) {
2085       const char * defaultKernelPath, int newIsDefault,   notSuitablePrintf(entry, 0, "marked to skip\n");
2086       const char * prefix, int flags) {   return 0;
2087      struct singleEntry * entry, * entry2, * newDefault;   }
2088      int i, j;  
2089     line =
2090      if (newIsDefault) {      getLineByType(LT_KERNEL | LT_HYPER | LT_KERNEL_EFI | LT_KERNEL_16,
2091   config->defaultImage = 0;    entry->lines);
2092   return;   if (!line) {
2093      } else if (defaultKernelPath) {   notSuitablePrintf(entry, 0, "no line found\n");
2094   i = 0;   return 0;
2095   if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {   }
2096      config->defaultImage = i;   if (line->numElements < 2) {
2097   } else {   notSuitablePrintf(entry, 0, "line has only %d elements\n",
2098      config->defaultImage = -1;    line->numElements);
2099      return;   return 0;
2100   }   }
     }  
2101    
2102      /* defaultImage now points to what we'd like to use, but before any order   if (flags & GRUBBY_BADIMAGE_OKAY) {
2103         changes */   notSuitablePrintf(entry, 1, "\n");
2104      if (config->defaultImage == DEFAULT_SAVED)   return 1;
2105        /* default is set to saved, we don't want to change it */   }
       return;  
   
     if (config->defaultImage > -1)  
  entry = findEntryByIndex(config, config->defaultImage);  
     else  
  entry = NULL;  
2106    
2107      if (entry && !entry->skip) {   fullName = alloca(strlen(bootPrefix) +
2108   /* we can preserve the default */    strlen(line->elements[1].item) + 1);
2109   if (hasNew)   rootspec = getRootSpecifier(line->elements[1].item);
2110      config->defaultImage++;   int rootspec_offset = rootspec ? strlen(rootspec) : 0;
2111     int hasslash = endswith(bootPrefix, '/') ||
2112   /* count the number of entries erased before this one */      beginswith(line->elements[1].item + rootspec_offset, '/');
2113   for (j = 0; j < config->defaultImage; j++) {   sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/",
2114      entry2 = findEntryByIndex(config, j);   line->elements[1].item + rootspec_offset);
2115      if (entry2->skip) config->defaultImage--;   if (access(fullName, R_OK)) {
2116   }   notSuitablePrintf(entry, 0, "access to %s failed\n", fullName);
2117      } else if (hasNew) {   return 0;
2118   config->defaultImage = 0;   }
2119      } else {   for (i = 2; i < line->numElements; i++)
2120   /* Either we just erased the default (or the default line was bad   if (!strncasecmp(line->elements[i].item, "root=", 5))
2121   * to begin with) and didn't put a new one in. We'll use the first   break;
2122   * valid image. */   if (i < line->numElements) {
2123   newDefault = findTemplate(config, prefix, &config->defaultImage, 1,   dev = line->elements[i].item + 5;
2124    flags);   } else {
2125   if (!newDefault)   /* look for a lilo style LT_ROOT line */
2126      config->defaultImage = -1;   line = getLineByType(LT_ROOT, entry->lines);
     }  
 }  
2127    
2128  void setFallbackImage(struct grubConfig * config, int hasNew) {   if (line && line->numElements >= 2) {
2129      struct singleEntry * entry, * entry2;   dev = line->elements[1].item;
2130      int j;   } else {
2131     /* didn't succeed in finding a LT_ROOT, let's try
2132      if (config->fallbackImage == -1) return;   * LT_KERNELARGS.  grub+multiboot uses LT_MBMODULE
2133     * for the args, so check that too.
2134      entry = findEntryByIndex(config, config->fallbackImage);   */
2135      if (!entry || entry->skip) {   line =
2136   config->fallbackImage = -1;      getLineByType(LT_KERNELARGS | LT_MBMODULE,
2137   return;    entry->lines);
2138      }  
2139     /* failed to find one */
2140     if (!line) {
2141     notSuitablePrintf(entry, 0, "no line found\n");
2142     return 0;
2143     }
2144    
2145      if (hasNew)   for (i = 1; i < line->numElements; i++)
2146   config->fallbackImage++;   if (!strncasecmp
2147            (line->elements[i].item, "root=", 5))
2148      /* count the number of entries erased before this one */   break;
2149      for (j = 0; j < config->fallbackImage; j++) {   if (i < line->numElements)
2150   entry2 = findEntryByIndex(config, j);   dev = line->elements[i].item + 5;
2151   if (entry2->skip) config->fallbackImage--;   else {
2152      }   notSuitablePrintf(entry, 0,
2153      "no root= entry found\n");
2154     /* it failed too...  can't find root= */
2155     return 0;
2156     }
2157     }
2158     }
2159    
2160     dev = getpathbyspec(dev);
2161     if (!getpathbyspec(dev)) {
2162     notSuitablePrintf(entry, 0, "can't find blkid entry for %s\n",
2163      dev);
2164     return 0;
2165     } else
2166     dev = getpathbyspec(dev);
2167    
2168     rootdev = findDiskForRoot();
2169     if (!rootdev) {
2170     notSuitablePrintf(entry, 0, "can't find root device\n");
2171     return 0;
2172     }
2173    
2174     if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) {
2175     notSuitablePrintf(entry, 0,
2176      "uuid missing: rootdev %s, dev %s\n",
2177      getuuidbydev(rootdev), getuuidbydev(dev));
2178     free(rootdev);
2179     return 0;
2180     }
2181    
2182     if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) {
2183     notSuitablePrintf(entry, 0,
2184      "uuid mismatch: rootdev %s, dev %s\n",
2185      getuuidbydev(rootdev), getuuidbydev(dev));
2186     free(rootdev);
2187     return 0;
2188     }
2189    
2190     free(rootdev);
2191     notSuitablePrintf(entry, 1, "\n");
2192    
2193     return 1;
2194  }  }
2195    
2196  void displayEntry(struct singleEntry * entry, const char * prefix, int index) {  /* returns the first match on or after the one pointed to by index (if index
2197      struct singleLine * line;     is not NULL) which is not marked as skip */
2198      char * root = NULL;  struct singleEntry *findEntryByPath(struct grubConfig *config,
2199      int i;      const char *kernel, const char *prefix,
2200        int *index)
2201      printf("index=%d\n", index);  {
2202     struct singleEntry *entry = NULL;
2203      line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);   struct singleLine *line;
2204      if (!line) {   int i;
2205          printf("non linux entry\n");   char *chptr;
2206          return;   char *rootspec = NULL;
2207      }   enum lineType_e checkType = LT_KERNEL;
2208    
2209     if (isdigit(*kernel)) {
2210     int *indexVars = alloca(sizeof(*indexVars) * strlen(kernel));
2211    
2212     i = 0;
2213     indexVars[i] = strtol(kernel, &chptr, 10);
2214     while (*chptr == ',') {
2215     i++;
2216     kernel = chptr + 1;
2217     indexVars[i] = strtol(kernel, &chptr, 10);
2218     }
2219    
2220      printf("kernel=%s\n", line->elements[1].item);   if (*chptr) {
2221     /* can't parse it, bail */
2222     return NULL;
2223     }
2224    
2225      if (line->numElements >= 3) {   indexVars[i + 1] = -1;
  printf("args=\"");  
  i = 2;  
  while (i < line->numElements) {  
     if (!strncmp(line->elements[i].item, "root=", 5)) {  
  root = line->elements[i].item + 5;  
     } else {  
  printf("%s%s", line->elements[i].item,  
        line->elements[i].indent);  
     }  
2226    
2227      i++;   i = 0;
2228   }   if (index) {
2229   printf("\"\n");   while (i < *index) {
2230      } else {   i++;
2231   line = getLineByType(LT_KERNELARGS, entry->lines);   if (indexVars[i] == -1)
2232   if (line) {   return NULL;
2233      char * s;   }
2234     }
2235    
2236      printf("args=\"");   entry = findEntryByIndex(config, indexVars[i]);
2237      i = 1;   if (!entry)
2238      while (i < line->numElements) {   return NULL;
2239   if (!strncmp(line->elements[i].item, "root=", 5)) {  
2240      root = line->elements[i].item + 5;   line =
2241        getLineByType(LT_KERNEL | LT_HYPER | LT_KERNEL_EFI |
2242      LT_KERNEL_16, entry->lines);
2243     if (!line)
2244     return NULL;
2245    
2246     if (index)
2247     *index = indexVars[i];
2248     return entry;
2249     }
2250    
2251     if (!strcmp(kernel, "DEFAULT")) {
2252     if (index && *index > config->defaultImage) {
2253     entry = NULL;
2254   } else {   } else {
2255      s = line->elements[i].item;   entry = findEntryByIndex(config, config->defaultImage);
2256     if (entry && entry->skip)
2257     entry = NULL;
2258     else if (index)
2259     *index = config->defaultImage;
2260     }
2261     } else if (!strcmp(kernel, "ALL")) {
2262     if (index)
2263     i = *index;
2264     else
2265     i = 0;
2266    
2267     while ((entry = findEntryByIndex(config, i))) {
2268     if (!entry->skip)
2269     break;
2270     i++;
2271     }
2272    
2273      printf("%s%s", s, line->elements[i].indent);   if (entry && index)
2274     *index = i;
2275     } else {
2276     if (index)
2277     i = *index;
2278     else
2279     i = 0;
2280    
2281     if (!strncmp(kernel, "TITLE=", 6)) {
2282     prefix = "";
2283     checkType = LT_TITLE | LT_MENUENTRY;
2284     kernel += 6;
2285   }   }
2286    
2287   i++;   for (entry = findEntryByIndex(config, i); entry;
2288      }       entry = entry->next, i++) {
2289     if (entry->skip)
2290     continue;
2291    
2292     dbgPrintf("findEntryByPath looking for %d %s in %p\n",
2293      checkType, kernel, entry);
2294    
2295     /* check all the lines matching checkType */
2296     for (line = entry->lines; line; line = line->next) {
2297     enum lineType_e ct = checkType;
2298     if (entry->multiboot && checkType == LT_KERNEL)
2299     ct = LT_KERNEL | LT_KERNEL_EFI |
2300        LT_MBMODULE | LT_HYPER |
2301        LT_KERNEL_16;
2302     else if (checkType & LT_KERNEL)
2303     ct = checkType | LT_KERNEL_EFI |
2304        LT_KERNEL_16;
2305     line = getLineByType(ct, line);
2306     if (!line)
2307     break; /* not found in this entry */
2308    
2309     if (line && line->type != LT_MENUENTRY &&
2310        line->numElements >= 2) {
2311     rootspec =
2312        getRootSpecifier(line->elements[1].
2313         item);
2314     if (!strcmp
2315        (line->elements[1].item +
2316         ((rootspec !=
2317           NULL) ? strlen(rootspec) : 0),
2318         kernel + strlen(prefix)))
2319     break;
2320     }
2321     if (line->type == LT_MENUENTRY &&
2322        !strcmp(line->elements[1].item, kernel))
2323     break;
2324     }
2325    
2326     /* make sure this entry has a kernel identifier; this skips
2327     * non-Linux boot entries (could find netbsd etc, though, which is
2328     * unfortunate)
2329     */
2330     if (line
2331        && getLineByType(LT_KERNEL | LT_HYPER |
2332         LT_KERNEL_EFI | LT_KERNEL_16,
2333         entry->lines))
2334     break; /* found 'im! */
2335     }
2336    
2337      s = line->elements[i - 1].indent;   if (index)
2338      printf("\"\n");   *index = i;
2339   }   }
     }  
2340    
2341      if (!root) {   return entry;
2342   line = getLineByType(LT_ROOT, entry->lines);  }
  if (line && line->numElements >= 2)  
     root=line->elements[1].item;  
     }  
2343    
2344      if (root) {  struct singleEntry *findEntryByTitle(struct grubConfig *cfg, char *title,
2345   char * s = alloca(strlen(root) + 1);       int *index)
2346    {
2347   strcpy(s, root);   struct singleEntry *entry;
2348   if (s[strlen(s) - 1] == '"')   struct singleLine *line;
2349      s[strlen(s) - 1] = '\0';   int i;
2350   /* make sure the root doesn't have a trailing " */   char *newtitle;
2351   printf("root=%s\n", s);  
2352      }   for (i = 0, entry = cfg->entries; entry; entry = entry->next, i++) {
2353     if (index && i < *index)
2354     continue;
2355     line = getLineByType(LT_TITLE, entry->lines);
2356     if (!line)
2357     line = getLineByType(LT_MENUENTRY, entry->lines);
2358     if (!line)
2359     continue;
2360     newtitle = grub2ExtractTitle(line);
2361     if (!newtitle)
2362     continue;
2363     if (!strcmp(title, newtitle))
2364     break;
2365     }
2366    
2367      line = getLineByType(LT_INITRD, entry->lines);   if (!entry)
2368     return NULL;
2369    
2370      if (line && line->numElements >= 2) {   if (index)
2371   printf("initrd=%s", prefix);   *index = i;
2372   for (i = 1; i < line->numElements; i++)   return entry;
     printf("%s%s", line->elements[i].item, line->elements[i].indent);  
  printf("\n");  
     }  
2373  }  }
2374    
2375  int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {  struct singleEntry *findEntryByIndex(struct grubConfig *cfg, int index)
2376      FILE * in;  {
2377      char buf[1024];   struct singleEntry *entry;
     char * chptr;  
     char * start;  
     char * param;  
   
     in = fopen("/etc/conf.d/grub", "r");  
     if (!in) return 1;  
   
     if (lbaPtr) *lbaPtr = 0;  
     if (bootPtr) *bootPtr = NULL;  
   
     while (fgets(buf, sizeof(buf), in)) {  
  start = buf;  
  while (isspace(*start)) start++;  
  if (*start == '#') continue;  
   
  chptr = strchr(start, '=');  
  if (!chptr) continue;  
  chptr--;  
  while (*chptr && isspace(*chptr)) chptr--;  
  chptr++;  
  *chptr = '\0';  
   
  param = chptr + 1;  
  while (*param && isspace(*param)) param++;  
  if (*param == '=') {  
     param++;  
     while (*param && isspace(*param)) param++;  
  }  
   
  chptr = param;  
  while (*chptr && !isspace(*chptr)) chptr++;  
  *chptr = '\0';  
   
  if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)  
     *lbaPtr = 1;  
  else if (!strcmp(start, "boot") && bootPtr)  
     *bootPtr = strdup(param);  
     }  
   
     fclose(in);  
2378    
2379      return 0;   entry = cfg->entries;
2380  }   while (index && entry) {
2381     entry = entry->next;
2382     index--;
2383     }
2384    
2385  void dumpSysconfigGrub(void) {   return entry;
     char * boot;  
     int lba;  
   
     if (!parseSysconfigGrub(&lba, &boot)) {  
  if (lba) printf("lba\n");  
  if (boot) printf("boot=%s\n", boot);  
     }  
2386  }  }
2387    
2388  int displayInfo(struct grubConfig * config, char * kernel,  /* Find a good template to use for the new kernel. An entry is
2389   const char * prefix) {   * good if the kernel and mkinitrd exist (even if the entry
2390      int i = 0;   * is going to be removed). Try and use the default entry, but
2391      struct singleEntry * entry;   * if that doesn't work just take the first. If we can't find one,
2392      struct singleLine * line;   * bail. */
2393    struct singleEntry *findTemplate(struct grubConfig *cfg, const char *prefix,
2394      entry = findEntryByPath(config, kernel, prefix, &i);   int *indexPtr, int skipRemoved, int flags)
2395      if (!entry) {  {
2396   fprintf(stderr, _("grubby: kernel not found\n"));   struct singleEntry *entry, *entry2;
2397   return 1;   int index;
     }  
2398    
2399      /* this is a horrible hack to support /etc/conf.d/grub; there must   if (cfg->cfi->defaultIsSaved) {
2400         be a better way */   if (cfg->cfi->getEnv) {
2401      if (config->cfi == &grubConfigType) {   char *defTitle =
2402   dumpSysconfigGrub();      cfg->cfi->getEnv(cfg->cfi, "saved_entry");
2403      } else {   if (defTitle) {
2404   line = getLineByType(LT_BOOT, config->theLines);   int index = 0;
2405   if (line && line->numElements >= 1) {   if (isnumber(defTitle)) {
2406      printf("boot=%s\n", line->elements[1].item);   index = atoi(defTitle);
2407     entry = findEntryByIndex(cfg, index);
2408     } else {
2409     entry =
2410        findEntryByTitle(cfg, defTitle,
2411         &index);
2412     }
2413     if (entry
2414        && suitableImage(entry, prefix, skipRemoved,
2415         flags)) {
2416     cfg->defaultImage = index;
2417     if (indexPtr)
2418     *indexPtr = index;
2419     return entry;
2420     }
2421     }
2422     }
2423     } else if (cfg->defaultImage > -1) {
2424     entry = findEntryByIndex(cfg, cfg->defaultImage);
2425     if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
2426     if (indexPtr)
2427     *indexPtr = cfg->defaultImage;
2428     return entry;
2429     }
2430   }   }
2431    
2432   line = getLineByType(LT_LBA, config->theLines);   index = 0;
2433   if (line) printf("lba\n");   while ((entry = findEntryByIndex(cfg, index))) {
2434      }   if (suitableImage(entry, prefix, skipRemoved, flags)) {
2435     int j;
2436      displayEntry(entry, prefix, i);   for (j = 0; j < index; j++) {
2437     entry2 = findEntryByIndex(cfg, j);
2438      i++;   if (entry2->skip)
2439      while ((entry = findEntryByPath(config, kernel, prefix, &i))) {   index--;
2440   displayEntry(entry, prefix, i);   }
2441   i++;   if (indexPtr)
2442      }   *indexPtr = index;
2443    
2444      return 0;   return entry;
2445  }   }
2446    
2447  struct singleLine * addLineTmpl(struct singleEntry * entry,   index++;
  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);  
     }  
2448   }   }
     }  
2449    
2450      dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?   fprintf(stderr,
2451        newLine->elements[0].item : "");   _("grubby fatal error: unable to find a suitable template\n"));
   
     if (!entry->lines) {  
  /* first one on the list */  
  entry->lines = newLine;  
     } else if (prevLine) {  
  /* add after prevLine */  
  newLine->next = prevLine->next;  
  prevLine->next = newLine;  
     }  
2452    
2453      return newLine;   return NULL;
2454  }  }
2455    
2456  /* val may be NULL */  char *findBootPrefix(void)
2457  struct singleLine *  addLine(struct singleEntry * entry,  {
2458       struct configFileInfo * cfi,   struct stat sb, sb2;
      enum lineType_e type, char * defaultIndent,  
      const char * val) {  
     struct singleLine * line, * prev;  
     struct keywordTypes * kw;  
     struct singleLine tmpl;  
   
     /* 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 = "";  
  }  
     }  
2459    
2460      /* The last non-empty line gives us the indention to us and the line   stat("/", &sb);
2461         to insert after. Note that comments are considered empty lines, which  #ifdef __ia64__
2462         may not be ideal? If there are no lines or we are looking at the   stat("/boot/efi/EFI/redhat/", &sb2);
2463         first line, we use defaultIndent (the first line is normally indented  #else
2464         differently from the rest) */   stat("/boot", &sb2);
2465      for (line = entry->lines, prev = NULL; line; line = line->next) {  #endif
  if (line->numElements) prev = line;  
  /* fall back on the last line if prev isn't otherwise set */  
  if (!line->next && !prev) prev = line;  
     }  
2466    
2467      if (prev == entry->lines)   if (sb.st_dev == sb2.st_dev)
2468   tmpl.indent = defaultIndent ?: "";   return strdup("");
     else  
  tmpl.indent = prev->indent;  
2469    
2470      return addLineTmpl(entry, &tmpl, prev, val, cfi);  #ifdef __ia64__
2471     return strdup("/boot/efi/EFI/redhat/");
2472    #else
2473     return strdup("/boot");
2474    #endif
2475  }  }
2476    
2477  void removeLine(struct singleEntry * entry, struct singleLine * line) {  void markRemovedImage(struct grubConfig *cfg, const char *image,
2478      struct singleLine * prev;        const char *prefix)
2479      int i;  {
2480     struct singleEntry *entry;
2481    
2482      for (i = 0; i < line->numElements; i++) {   if (!image)
2483   free(line->elements[i].item);   return;
  free(line->elements[i].indent);  
     }  
     free(line->elements);  
     free(line->indent);  
2484    
2485      if (line == entry->lines) {   /* check and see if we're removing the default image */
2486   entry->lines = line->next;   if (isdigit(*image)) {
2487      } else {   entry = findEntryByPath(cfg, image, prefix, NULL);
2488   prev = entry->lines;   if (entry)
2489   while (prev->next != line) prev = prev->next;   entry->skip = 1;
2490   prev->next = line->next;   return;
2491      }   }
2492    
2493      free(line);   while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
2494     entry->skip = 1;
2495  }  }
2496    
2497  static void insertElement(struct singleLine * line,  void setDefaultImage(struct grubConfig *config, int hasNew,
2498    const char * item, int insertHere,       const char *defaultKernelPath, int newIsDefault,
2499    struct configFileInfo * cfi)       const char *prefix, int flags, int index)
2500  {  {
2501      struct keywordTypes * kw;   struct singleEntry *entry, *entry2, *newDefault;
2502      char indent[2] = "";   int i, j;
   
     /* sanity check */  
     if (insertHere > line->numElements) {  
  dbgPrintf("insertElement() adjusting insertHere from %d to %d\n",  
   insertHere, line->numElements);  
  insertHere = line->numElements;  
     }  
2503    
2504      line->elements = realloc(line->elements, (line->numElements + 1) *   if (newIsDefault) {
2505       sizeof(*line->elements));   config->defaultImage = 0;
2506      memmove(&line->elements[insertHere+1],   return;
2507      &line->elements[insertHere],   } else if ((index >= 0) && config->cfi->defaultIsIndex) {
2508      (line->numElements - insertHere) *   if (findEntryByIndex(config, index))
2509      sizeof(*line->elements));   config->defaultImage = index;
2510      line->elements[insertHere].item = strdup(item);   else
2511     config->defaultImage = -1;
2512      kw = getKeywordByType(line->type, cfi);   return;
2513     } else if (defaultKernelPath) {
2514      if (line->numElements == 0) {   i = 0;
2515   indent[0] = '\0';   if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
2516      } else if (insertHere == 0) {   config->defaultImage = i;
2517   indent[0] = kw->nextChar;   } else {
2518      } else if (kw->separatorChar != '\0') {   config->defaultImage = -1;
2519   indent[0] = kw->separatorChar;   return;
2520      } else {   }
2521   indent[0] = ' ';   }
     }  
2522    
2523      if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') {   /* defaultImage now points to what we'd like to use, but before any
2524   /* move the end-of-line forward */   * order changes */
2525   line->elements[insertHere].indent =   if ((config->defaultImage == DEFAULT_SAVED) ||
2526      line->elements[insertHere-1].indent;      (config->defaultImage == DEFAULT_SAVED_GRUB2))
2527   line->elements[insertHere-1].indent = strdup(indent);   /* default is set to saved, we don't want to change it */
2528      } else {   return;
  line->elements[insertHere].indent = strdup(indent);  
     }  
2529    
2530      line->numElements++;   if (config->defaultImage > -1)
2531     entry = findEntryByIndex(config, config->defaultImage);
2532     else
2533     entry = NULL;
2534    
2535      dbgPrintf("insertElement(%s, '%s%s', %d)\n",   if (entry && !entry->skip) {
2536        line->elements[0].item,   /* we can preserve the default */
2537        line->elements[insertHere].item,   if (hasNew)
2538        line->elements[insertHere].indent,   config->defaultImage++;
2539        insertHere);  
2540     /* count the number of entries erased before this one */
2541     for (j = 0; j < config->defaultImage; j++) {
2542     entry2 = findEntryByIndex(config, j);
2543     if (entry2->skip)
2544     config->defaultImage--;
2545     }
2546     } else if (hasNew) {
2547     config->defaultImage = 0;
2548     } else {
2549     /* Either we just erased the default (or the default line was
2550     * bad to begin with) and didn't put a new one in. We'll use
2551     * the first valid image. */
2552     newDefault =
2553        findTemplate(config, prefix, &config->defaultImage, 1,
2554     flags);
2555     if (!newDefault)
2556     config->defaultImage = -1;
2557     }
2558  }  }
2559    
2560  static void removeElement(struct singleLine * line, int removeHere) {  void setFallbackImage(struct grubConfig *config, int hasNew)
2561      int i;  {
2562     struct singleEntry *entry, *entry2;
2563      /* sanity check */   int j;
     if (removeHere >= line->numElements) return;  
2564    
2565      dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,   if (config->fallbackImage == -1)
2566        removeHere, line->elements[removeHere].item);   return;
2567    
2568      free(line->elements[removeHere].item);   entry = findEntryByIndex(config, config->fallbackImage);
2569     if (!entry || entry->skip) {
2570     config->fallbackImage = -1;
2571     return;
2572     }
2573    
2574      if (removeHere > 1) {   if (hasNew)
2575   /* previous argument gets this argument's post-indentation */   config->fallbackImage++;
  free(line->elements[removeHere-1].indent);  
  line->elements[removeHere-1].indent =  
     line->elements[removeHere].indent;  
     } else {  
  free(line->elements[removeHere].indent);  
     }  
2576    
2577      /* now collapse the array, but don't bother to realloc smaller */   /* count the number of entries erased before this one */
2578      for (i = removeHere; i < line->numElements - 1; i++)   for (j = 0; j < config->fallbackImage; j++) {
2579   line->elements[i] = line->elements[i + 1];   entry2 = findEntryByIndex(config, j);
2580     if (entry2->skip)
2581      line->numElements--;   config->fallbackImage--;
 }  
   
 int argMatch(const char * one, const char * two) {  
     char * first, * second;  
     char * chptr;  
   
     first = strcpy(alloca(strlen(one) + 1), one);  
     second = strcpy(alloca(strlen(two) + 1), two);  
   
     chptr = strchr(first, '=');  
     if (chptr) *chptr = '\0';  
   
     chptr = strchr(second, '=');  
     if (chptr) *chptr = '\0';  
   
     return strcmp(first, second);  
 }  
   
 int updateActualImage(struct grubConfig * cfg, const char * image,  
                       const char * prefix, const char * addArgs,  
                       const char * removeArgs, int multibootArgs) {  
     struct singleEntry * entry;  
     struct singleLine * line, * rootLine;  
     int index = 0;  
     int i, k;  
     const char ** newArgs, ** oldArgs;  
     const char ** arg;  
     int useKernelArgs, useRoot;  
     int firstElement;  
     int *usedElements;  
     int doreplace;  
   
     if (!image) return 0;  
   
     if (!addArgs) {  
  newArgs = malloc(sizeof(*newArgs));  
  *newArgs = NULL;  
     } else {  
  if (poptParseArgvString(addArgs, NULL, &newArgs)) {  
     fprintf(stderr,  
     _("grubby: error separating arguments '%s'\n"), addArgs);  
     return 1;  
2582   }   }
2583      }  }
2584    
2585    void displayEntry(struct singleEntry *entry, const char *prefix, int index)
2586    {
2587     struct singleLine *line;
2588     char *root = NULL;
2589     int i;
2590     int j;
2591    
2592     printf("index=%d\n", index);
2593    
2594      if (!removeArgs) {   line =
2595   oldArgs = malloc(sizeof(*oldArgs));      getLineByType(LT_KERNEL | LT_HYPER | LT_KERNEL_EFI | LT_KERNEL_16,
2596   *oldArgs = NULL;    entry->lines);
2597      } else {   if (!line) {
2598   if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {   printf("non linux entry\n");
2599      fprintf(stderr,   return;
     _("grubby: error separating arguments '%s'\n"), removeArgs);  
             free(newArgs);  
     return 1;  
2600   }   }
     }  
2601    
2602     if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2603     printf("kernel=%s\n", line->elements[1].item);
2604     else
2605     printf("kernel=%s%s\n", prefix, line->elements[1].item);
2606    
2607      useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)   if (line->numElements >= 3) {
2608       && (!multibootArgs || cfg->cfi->mbConcatArgs));   printf("args=\"");
2609     i = 2;
2610     while (i < line->numElements) {
2611     if (!strncmp(line->elements[i].item, "root=", 5)) {
2612     root = line->elements[i].item + 5;
2613     } else {
2614     printf("%s%s", line->elements[i].item,
2615           line->elements[i].indent);
2616     }
2617    
2618      useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)   i++;
2619         && !multibootArgs);   }
2620     printf("\"\n");
2621     } else {
2622     line = getLineByType(LT_KERNELARGS, entry->lines);
2623     if (line) {
2624     char *s;
2625    
2626     printf("args=\"");
2627     i = 1;
2628     while (i < line->numElements) {
2629     if (!strncmp
2630        (line->elements[i].item, "root=", 5)) {
2631     root = line->elements[i].item + 5;
2632     } else {
2633     s = line->elements[i].item;
2634    
2635      for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {   printf("%s%s", s,
2636           line->elements[i].indent);
2637     }
2638    
2639   if (multibootArgs && !entry->multiboot)   i++;
2640      continue;   }
2641    
2642   /* Determine where to put the args.  If this config supports   s = line->elements[i - 1].indent;
2643   * LT_KERNELARGS, use that.  Otherwise use   printf("\"\n");
2644   * 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;  
   
  } else if (multibootArgs) {  
     line = getLineByType(LT_HYPER, entry->lines);  
     if (!line) {  
  /* a multiboot entry without LT_HYPER? */  
  continue;  
     }  
     firstElement = 2;  
   
  } else {  
     line = getLineByType(LT_KERNEL|LT_MBMODULE, entry->lines);  
     if (!line) {  
  /* no LT_KERNEL or LT_MBMODULE in this entry? */  
  continue;  
     }  
     firstElement = 2;  
2645   }   }
2646    
2647   /* handle the elilo case which does:   if (!root) {
2648   *   append="hypervisor args -- kernel args"   line = getLineByType(LT_ROOT, entry->lines);
2649   */   if (line && line->numElements >= 2)
2650   if (entry->multiboot && cfg->cfi->mbConcatArgs) {   root = line->elements[1].item;
2651      /* 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(*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;  
     break;  
                 }  
             }  
2652    
2653      if (i < line->numElements && doreplace) {   if (root) {
2654   /* direct replacement */   char *s = alloca(strlen(root) + 1);
  free(line->elements[i].item);  
  line->elements[i].item = strdup(*arg);  
2655    
2656      } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {   strcpy(s, root);
2657   /* root= replacement */   if (s[strlen(s) - 1] == '"')
2658   rootLine = getLineByType(LT_ROOT, entry->lines);   s[strlen(s) - 1] = '\0';
2659   if (rootLine) {   /* make sure the root doesn't have a trailing " */
2660      free(rootLine->elements[1].item);   printf("root=%s\n", s);
2661      rootLine->elements[1].item = strdup(*arg + 5);   }
2662   } else {  
2663      rootLine = addLine(entry, cfg->cfi, LT_ROOT,   line =
2664         cfg->secondaryIndent, *arg + 5);      getLineByType(LT_INITRD | LT_INITRD_EFI | LT_INITRD_16,
2665   }    entry->lines);
     }  
2666    
2667      else {   if (line && line->numElements >= 2) {
2668   /* insert/append */   if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2669   insertElement(line, *arg, i, cfg->cfi);   printf("initrd=");
2670   usedElements = realloc(usedElements, line->numElements *   else
2671         sizeof(*usedElements));   printf("initrd=%s", prefix);
2672   memmove(&usedElements[i + 1], &usedElements[i],  
2673   line->numElements - i - 1);   for (i = 1; i < line->numElements; i++)
2674   usedElements[i] = 1;   printf("%s%s", line->elements[i].item,
2675           line->elements[i].indent);
2676   /* if we updated a root= here even though there is a   printf("\n");
    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 = getLineByType(LT_ROOT, entry->lines);  
     if (rootLine)  
  removeLine(entry, rootLine);  
  }  
     }  
  }  
   
         free(usedElements);  
   
  for (arg = oldArgs; *arg; 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;  
  }  
     }  
     /* handle removing LT_ROOT line too */  
     if (useRoot && !strncmp(*arg, "root=", 5)) {  
  rootLine = getLineByType(LT_ROOT, entry->lines);  
  if (rootLine)  
     removeLine(entry, rootLine);  
     }  
  }  
   
  if (line->numElements == 1) {  
     /* don't need the line at all (note it has to be a  
        LT_KERNELARGS for this to happen */  
     removeLine(entry, line);  
2677   }   }
     }  
2678    
2679      free(newArgs);   line = getLineByType(LT_TITLE, entry->lines);
2680      free(oldArgs);   if (line) {
2681     printf("title=%s\n", line->elements[1].item);
2682     } else {
2683     char *title;
2684     line = getLineByType(LT_MENUENTRY, entry->lines);
2685     if (line) {
2686     title = grub2ExtractTitle(line);
2687     if (title)
2688     printf("title=%s\n", title);
2689     }
2690     }
2691    
2692      return 0;   for (j = 0, line = entry->lines; line; line = line->next) {
2693     if ((line->type & LT_MBMODULE) && line->numElements >= 2) {
2694     if (!strncmp
2695        (prefix, line->elements[1].item, strlen(prefix)))
2696     printf("mbmodule%d=", j);
2697     else
2698     printf("mbmodule%d=%s", j, prefix);
2699    
2700     for (i = 1; i < line->numElements; i++)
2701     printf("%s%s", line->elements[i].item,
2702           line->elements[i].indent);
2703     printf("\n");
2704     j++;
2705     }
2706     }
2707  }  }
2708    
2709  int updateImage(struct grubConfig * cfg, const char * image,  int isSuseSystem(void)
2710                  const char * prefix, const char * addArgs,  {
2711                  const char * removeArgs,   const char *path;
2712                  const char * addMBArgs, const char * removeMBArgs) {   const static char default_path[] = "/etc/SuSE-release";
     int rc = 0;  
   
     if (!image) return rc;  
   
     /* update the main args first... */  
     if (addArgs || removeArgs)  
         rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs, 0);  
     if (rc) return rc;  
   
     /* and now any multiboot args */  
     if (addMBArgs || removeMBArgs)  
         rc = updateActualImage(cfg, image, prefix, addMBArgs, removeMBArgs, 1);  
     return rc;  
 }  
   
 int updateInitrd(struct grubConfig * cfg, const char * image,  
                  const char * prefix, const char * initrd) {  
     struct singleEntry * entry;  
     struct singleLine * line, * kernelLine;  
     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;  
         }  
         line = addLine(entry, cfg->cfi, LT_INITRD, kernelLine->indent, initrd);  
         if (!line) return 1;  
         break;  
     }  
2713    
2714      return 0;   if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2715     path = default_path;
2716    
2717     if (!access(path, R_OK))
2718     return 1;
2719     return 0;
2720  }  }
2721    
2722  int checkDeviceBootloader(const char * device, const unsigned char * boot) {  int isSuseGrubConf(const char *path)
2723      int fd;  {
2724      unsigned char bootSect[512];   FILE *grubConf;
2725      int offset;   char *line = NULL;
2726     size_t len = 0, res = 0;
2727      fd = open(device, O_RDONLY);  
2728      if (fd < 0) {   grubConf = fopen(path, "r");
2729   fprintf(stderr, _("grubby: unable to open %s: %s\n"),   if (!grubConf) {
2730   device, strerror(errno));   dbgPrintf("Could not open SuSE configuration file '%s'\n",
2731   return 1;    path);
2732      }   return 0;
2733     }
2734    
2735     while ((res = getline(&line, &len, grubConf)) != -1) {
2736     if (!strncmp(line, "setup", 5)) {
2737     fclose(grubConf);
2738     free(line);
2739     return 1;
2740     }
2741     }
2742    
2743      if (read(fd, bootSect, 512) != 512) {   dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2744   fprintf(stderr, _("grubby: unable to read %s: %s\n"),    path);
  device, strerror(errno));  
  return 1;  
     }  
     close(fd);  
2745    
2746      /* first three bytes should match, a jmp short should be in there */   fclose(grubConf);
2747      if (memcmp(boot, bootSect, 3))   free(line);
2748   return 0;   return 0;
2749    }
2750    
2751      if (boot[1] == 0xeb) {  int suseGrubConfGetLba(const char *path, int *lbaPtr)
2752   offset = boot[2] + 2;  {
2753      } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {   FILE *grubConf;
2754   offset = (boot[3] << 8) + boot[2] + 2;   char *line = NULL;
2755      } else if (boot[0] == 0xeb) {   size_t res = 0, len = 0;
  offset = boot[1] + 2;  
     } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {  
  offset = (boot[2] << 8) + boot[1] + 2;  
     } else {  
  return 0;  
     }  
2756    
2757      if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))   if (!path)
2758   return 0;   return 1;
2759     if (!lbaPtr)
2760     return 1;
2761    
2762      return 2;   grubConf = fopen(path, "r");
2763  }   if (!grubConf)
2764     return 1;
2765    
2766  int checkLiloOnRaid(char * mdDev, const unsigned char * boot) {   while ((res = getline(&line, &len, grubConf)) != -1) {
2767      int fd;   if (line[res - 1] == '\n')
2768      char buf[65536];   line[res - 1] = '\0';
2769      char * end;   else if (len > res)
2770      char * chptr;   line[res] = '\0';
2771      char * chptr2;   else {
2772      int rc;   line = realloc(line, res + 1);
2773     line[res] = '\0';
2774     }
2775    
2776      /* it's on raid; we need to parse /proc/mdstat and check all of the   if (!strncmp(line, "setup", 5)) {
2777         *raw* devices listed in there */   if (strstr(line, "--force-lba")) {
2778     *lbaPtr = 1;
2779     } else {
2780     *lbaPtr = 0;
2781     }
2782     dbgPrintf("lba: %i\n", *lbaPtr);
2783     break;
2784     }
2785     }
2786    
2787      if (!strncmp(mdDev, "/dev/", 5))   free(line);
2788   mdDev += 5;   fclose(grubConf);
2789     return 0;
2790    }
2791    
2792      if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {  int suseGrubConfGetInstallDevice(const char *path, char **devicePtr)
2793   fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),  {
2794   strerror(errno));   FILE *grubConf;
2795   return 2;   char *line = NULL;
2796      }   size_t res = 0, len = 0;
2797     char *lastParamPtr = NULL;
2798     char *secLastParamPtr = NULL;
2799     char installDeviceNumber = '\0';
2800     char *bounds = NULL;
2801    
2802      rc = read(fd, buf, sizeof(buf) - 1);   if (!path)
2803      if (rc < 0 || rc == (sizeof(buf) - 1)) {   return 1;
2804   fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),   if (!devicePtr)
2805   strerror(errno));   return 1;
  close(fd);  
  return 2;  
     }  
     close(fd);  
     buf[rc] = '\0';  
2806    
2807      chptr = buf;   grubConf = fopen(path, "r");
2808      while (*chptr) {   if (!grubConf)
2809   end = strchr(chptr, '\n');   return 1;
  if (!end) break;  
  *end = '\0';  
2810    
2811   if (!strncmp(chptr, mdDev, strlen(mdDev)) &&   while ((res = getline(&line, &len, grubConf)) != -1) {
2812      chptr[strlen(mdDev)] == ' ') {   if (strncmp(line, "setup", 5))
2813     continue;
2814    
2815     if (line[res - 1] == '\n')
2816     line[res - 1] = '\0';
2817     else if (len > res)
2818     line[res] = '\0';
2819     else {
2820     line = realloc(line, res + 1);
2821     line[res] = '\0';
2822     }
2823    
2824      /* found the device */   lastParamPtr = bounds = line + res;
     while (*chptr && *chptr != ':') chptr++;  
     chptr++;  
     while (*chptr && isspace(*chptr)) chptr++;  
   
     /* skip the "active" bit */  
     while (*chptr && !isspace(*chptr)) chptr++;  
     while (*chptr && isspace(*chptr)) chptr++;  
   
     /* skip the raid level */  
     while (*chptr && !isspace(*chptr)) chptr++;  
     while (*chptr && isspace(*chptr)) chptr++;  
   
     /* everything else is partition stuff */  
     while (*chptr) {  
  chptr2 = chptr;  
  while (*chptr2 && *chptr2 != '[') chptr2++;  
  if (!*chptr2) break;  
   
  /* yank off the numbers at the end */  
  chptr2--;  
  while (isdigit(*chptr2) && chptr2 > chptr) chptr2--;  
  chptr2++;  
  *chptr2 = '\0';  
   
  /* Better, now we need the /dev/ back. We're done with  
  * everything before this point, so we can just put  
  * the /dev/ part there. There will always be room. */  
  memcpy(chptr - 5, "/dev/", 5);  
  rc = checkDeviceBootloader(chptr - 5, boot);  
  if (rc != 2) {  
     return rc;  
  }  
   
  chptr = chptr2 + 1;  
  /* skip the [11] bit */  
  while (*chptr && !isspace(*chptr)) chptr++;  
  /* and move to the next one */  
  while (*chptr && isspace(*chptr)) chptr++;  
     }  
2825    
2826      /*  we're good to go */   /* Last parameter in grub may be an optional IMAGE_DEVICE */
2827      return 2;   while (!isspace(*lastParamPtr))
2828   }   lastParamPtr--;
2829     lastParamPtr++;
2830    
2831     secLastParamPtr = lastParamPtr - 2;
2832     dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2833    
2834     if (lastParamPtr + 3 > bounds) {
2835     dbgPrintf("lastParamPtr going over boundary");
2836     fclose(grubConf);
2837     free(line);
2838     return 1;
2839     }
2840     if (!strncmp(lastParamPtr, "(hd", 3))
2841     lastParamPtr += 3;
2842     dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);
2843    
2844     /*
2845     * Second last parameter will decide wether last parameter is
2846     * an IMAGE_DEVICE or INSTALL_DEVICE
2847     */
2848     while (!isspace(*secLastParamPtr))
2849     secLastParamPtr--;
2850     secLastParamPtr++;
2851    
2852     if (secLastParamPtr + 3 > bounds) {
2853     dbgPrintf("secLastParamPtr going over boundary");
2854     fclose(grubConf);
2855     free(line);
2856     return 1;
2857     }
2858     dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);
2859     if (!strncmp(secLastParamPtr, "(hd", 3)) {
2860     secLastParamPtr += 3;
2861     dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);
2862     installDeviceNumber = *secLastParamPtr;
2863     } else {
2864     installDeviceNumber = *lastParamPtr;
2865     }
2866    
2867   chptr = end + 1;   *devicePtr = malloc(6);
2868      }   snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);
2869     dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);
2870     fclose(grubConf);
2871     free(line);
2872     return 0;
2873     }
2874    
2875      fprintf(stderr,   free(line);
2876      _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),   fclose(grubConf);
2877      mdDev);   return 1;
     return 0;  
2878  }  }
2879    
2880  int checkForLilo(struct grubConfig * config) {  int grubGetBootFromDeviceMap(const char *device, char **bootPtr)
2881      int fd;  {
2882      unsigned char boot[512];   FILE *deviceMap;
2883      struct singleLine * line;   char *line = NULL;
2884     size_t res = 0, len = 0;
2885      for (line = config->theLines; line; line = line->next)   char *devicePtr;
2886   if (line->type == LT_BOOT) break;   char *bounds = NULL;
2887     const char *path;
2888      if (!line) {   const static char default_path[] = "/boot/grub/device.map";
  fprintf(stderr,  
  _("grubby: no boot line found in lilo configuration\n"));  
  return 1;  
     }  
2889    
2890      if (line->numElements != 2) return 1;   if (!device)
2891     return 1;
2892     if (!bootPtr)
2893     return 1;
2894    
2895      fd = open("/boot/boot.b", O_RDONLY);   if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
2896      if (fd < 0) {   path = default_path;
  fprintf(stderr, _("grubby: unable to open %s: %s\n"),  
  "/boot/boot.b", strerror(errno));  
  return 1;  
     }  
2897    
2898      if (read(fd, boot, 512) != 512) {   dbgPrintf("opening grub device.map file from: %s\n", path);
2899   fprintf(stderr, _("grubby: unable to read %s: %s\n"),   deviceMap = fopen(path, "r");
2900   "/boot/boot.b", strerror(errno));   if (!deviceMap)
2901   return 1;   return 1;
     }  
     close(fd);  
2902    
2903      if (!strncmp("/dev/md", line->elements[1].item, 7))   while ((res = getline(&line, &len, deviceMap)) != -1) {
2904   return checkLiloOnRaid(line->elements[1].item, boot);   if (!strncmp(line, "#", 1))
2905     continue;
2906    
2907     if (line[res - 1] == '\n')
2908     line[res - 1] = '\0';
2909     else if (len > res)
2910     line[res] = '\0';
2911     else {
2912     line = realloc(line, res + 1);
2913     line[res] = '\0';
2914     }
2915    
2916      return checkDeviceBootloader(line->elements[1].item, boot);   devicePtr = line;
2917  }   bounds = line + res;
2918    
2919  int checkForGrub(struct grubConfig * config) {   while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
2920      int fd;   devicePtr++;
2921      unsigned char bootSect[512];   dbgPrintf("device: %s\n", devicePtr);
2922      char * boot;  
2923     if (!strncmp(devicePtr, device, strlen(device))) {
2924     devicePtr += strlen(device);
2925     while (isspace(*devicePtr)
2926           && ((devicePtr + 1) <= bounds))
2927     devicePtr++;
2928    
2929      if (parseSysconfigGrub(NULL, &boot))   *bootPtr = strdup(devicePtr);
2930   return 0;   break;
2931     }
2932     }
2933    
2934      /* assume grub is not installed -- not an error condition */   free(line);
2935      if (!boot)   fclose(deviceMap);
2936   return 0;   return 0;
2937    }
2938    
2939      fd = open("/boot/grub/stage1", O_RDONLY);  int suseGrubConfGetBoot(const char *path, char **bootPtr)
2940      if (fd < 0)  {
2941   /* this doesn't exist if grub hasn't been installed */   char *grubDevice;
  return 0;  
2942    
2943      if (read(fd, bootSect, 512) != 512) {   if (suseGrubConfGetInstallDevice(path, &grubDevice))
2944   fprintf(stderr, _("grubby: unable to read %s: %s\n"),   dbgPrintf("error looking for grub installation device\n");
2945   "/boot/grub/stage1", strerror(errno));   else
2946   close(fd);   dbgPrintf("grubby installation device: %s\n", grubDevice);
2947   return 1;  
2948      }   if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
2949      close(fd);   dbgPrintf("error looking for grub boot device\n");
2950     else
2951     dbgPrintf("grubby boot device: %s\n", *bootPtr);
2952    
2953      return checkDeviceBootloader(boot, bootSect);   free(grubDevice);
2954     return 0;
2955  }  }
2956    
2957  int checkForExtLinux(struct grubConfig * config) {  int parseSuseGrubConf(int *lbaPtr, char **bootPtr)
2958      int fd;  {
2959      unsigned char bootSect[512];   /*
2960      char * boot;   * This SuSE grub configuration file at this location is not your
2961      char executable[] = "/boot/extlinux/extlinux";   * average grub configuration file, but instead the grub commands
2962     * used to setup grub on that system.
2963     */
2964     const char *path;
2965     const static char default_path[] = "/etc/grub.conf";
2966    
2967      printf("entered: checkForExtLinux()\n");   if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
2968     path = default_path;
2969    
2970      if (parseSysconfigGrub(NULL, &boot))   if (!isSuseGrubConf(path))
2971   return 0;   return 1;
2972    
2973      /* assume grub is not installed -- not an error condition */   if (lbaPtr) {
2974      if (!boot)   *lbaPtr = 0;
2975   return 0;   if (suseGrubConfGetLba(path, lbaPtr))
2976     return 1;
2977     }
2978    
2979     if (bootPtr) {
2980     *bootPtr = NULL;
2981     suseGrubConfGetBoot(path, bootPtr);
2982     }
2983    
     fd = open(executable, O_RDONLY);  
     if (fd < 0)  
  /* this doesn't exist if grub hasn't been installed */  
2984   return 0;   return 0;
2985    }
2986    
2987      if (read(fd, bootSect, 512) != 512) {  int parseSysconfigGrub(int *lbaPtr, char **bootPtr)
2988   fprintf(stderr, _("grubby: unable to read %s: %s\n"),  {
2989   executable, strerror(errno));   FILE *in;
2990   return 1;   char buf[1024];
2991      }   char *chptr;
2992      close(fd);   char *start;
2993     char *param;
2994    
2995      return checkDeviceBootloader(boot, bootSect);   in = fopen("/etc/sysconfig/grub", "r");
2996  }   if (!in)
2997     return 1;
2998    
2999  static char * getRootSpecifier(char * str) {   if (lbaPtr)
3000      char * idx, * rootspec = NULL;   *lbaPtr = 0;
3001     if (bootPtr)
3002     *bootPtr = NULL;
3003    
3004     while (fgets(buf, sizeof(buf), in)) {
3005     start = buf;
3006     while (isspace(*start))
3007     start++;
3008     if (*start == '#')
3009     continue;
3010    
3011     chptr = strchr(start, '=');
3012     if (!chptr)
3013     continue;
3014     chptr--;
3015     while (*chptr && isspace(*chptr))
3016     chptr--;
3017     chptr++;
3018     *chptr = '\0';
3019    
3020     param = chptr + 1;
3021     while (*param && isspace(*param))
3022     param++;
3023     if (*param == '=') {
3024     param++;
3025     while (*param && isspace(*param))
3026     param++;
3027     }
3028    
3029      if (*str == '(') {   chptr = param;
3030          idx = rootspec = strdup(str);   while (*chptr && !isspace(*chptr))
3031          while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;   chptr++;
3032          *(++idx) = '\0';   *chptr = '\0';
3033      }  
3034      return rootspec;   if (!strcmp(start, "forcelba") && !strcmp(param, "1") && lbaPtr)
3035     *lbaPtr = 1;
3036     else if (!strcmp(start, "boot") && bootPtr)
3037     *bootPtr = strdup(param);
3038     }
3039    
3040     fclose(in);
3041    
3042     return 0;
3043  }  }
3044    
3045  static char * getInitrdVal(struct grubConfig * config,  void dumpSysconfigGrub(void)
3046     const char * prefix, struct singleLine *tmplLine,  {
3047     const char * newKernelInitrd,   char *boot = NULL;
3048     char ** extraInitrds, int extraInitrdCount)   int lba;
 {  
     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;  
     }  
3049    
3050      initrdVal = end = malloc(totalSize);   if (isSuseSystem()) {
3051     if (parseSuseGrubConf(&lba, &boot)) {
3052     free(boot);
3053     return;
3054     }
3055     } else {
3056     if (parseSysconfigGrub(&lba, &boot)) {
3057     free(boot);
3058     return;
3059     }
3060     }
3061    
3062      end = stpcpy (end, newKernelInitrd + prefixLen);   if (lba)
3063     printf("lba\n");
3064     if (boot) {
3065     printf("boot=%s\n", boot);
3066     free(boot);
3067     }
3068    }
3069    
3070      separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;  int displayInfo(struct grubConfig *config, char *kernel, const char *prefix)
3071      for (i = 0; i < extraInitrdCount; i++) {  {
3072   const char *extraInitrd;   int i = 0;
3073   int j;   struct singleEntry *entry;
3074     struct singleLine *line;
3075    
3076     entry = findEntryByPath(config, kernel, prefix, &i);
3077     if (!entry) {
3078     fprintf(stderr, _("grubby: kernel not found\n"));
3079     return 1;
3080     }
3081    
3082   extraInitrd = extraInitrds[i] + prefixLen;   /* this is a horrible hack to support /etc/sysconfig/grub; there must
3083   /* Don't add entries that are already there */     be a better way */
3084   if (tmplLine != NULL) {   if (config->cfi == &grubConfigType) {
3085      for (j = 2; j < tmplLine->numElements; j++)   dumpSysconfigGrub();
3086   if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)   } else {
3087      break;   line = getLineByType(LT_BOOT, config->theLines);
3088     if (line && line->numElements >= 1) {
3089     printf("boot=%s\n", line->elements[1].item);
3090     }
3091    
3092      if (j != tmplLine->numElements)   line = getLineByType(LT_LBA, config->theLines);
3093   continue;   if (line)
3094     printf("lba\n");
3095   }   }
3096    
3097   *end++ = separatorChar;   displayEntry(entry, prefix, i);
  end = stpcpy(end, extraInitrd);  
     }  
3098    
3099      return initrdVal;   i++;
3100     while ((entry = findEntryByPath(config, kernel, prefix, &i))) {
3101     displayEntry(entry, prefix, i);
3102     i++;
3103     }
3104    
3105     return 0;
3106  }  }
3107    
3108  int addNewKernel(struct grubConfig * config, struct singleEntry * template,  struct singleLine *addLineTmpl(struct singleEntry *entry,
3109           const char * prefix,         struct singleLine *tmplLine,
3110   char * newKernelPath, char * newKernelTitle,         struct singleLine *prevLine,
3111   char * newKernelArgs, char * newKernelInitrd,         const char *val, struct configFileInfo *cfi)
3112   char ** extraInitrds, int extraInitrdCount,  {
3113                   char * newMBKernel, char * newMBKernelArgs) {   struct singleLine *newLine = lineDup(tmplLine);
     struct singleEntry * new;  
     struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;  
     int needs;  
     char * chptr;  
   
     if (!newKernelPath) return 0;  
   
     /* if the newKernelTitle is too long silently munge it into something  
      * we can live with. truncating is first check, then we'll just mess with  
      * it until it looks better */  
     if (config->cfi->maxTitleLength &&  
     (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {  
  char * buf = alloca(config->cfi->maxTitleLength + 7);  
  char * numBuf = alloca(config->cfi->maxTitleLength + 1);  
  int i = 1;  
3114    
3115   sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength, newKernelTitle);   if (isEfi && cfi == &grub2ConfigType) {
3116   while (findEntryByPath(config, buf, NULL, NULL)) {   enum lineType_e old = newLine->type;
3117      sprintf(numBuf, "%d", i++);   newLine->type = preferredLineType(newLine->type, cfi);
3118      strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);   if (old != newLine->type)
3119     newLine->elements[0].item =
3120        getKeyByType(newLine->type, cfi);
3121   }   }
3122    
3123   newKernelTitle = buf + 6;   if (val) {
3124      }   /* override the inherited value with our own.
3125     * This is a little weak because it only applies to elements[1]
3126     */
3127     if (newLine->numElements > 1)
3128     removeElement(newLine, 1);
3129     insertElement(newLine, val, 1, cfi);
3130    
3131     /* but try to keep the rootspec from the template... sigh */
3132     if (tmplLine->
3133        type & (LT_HYPER | LT_KERNEL | LT_MBMODULE | LT_INITRD |
3134        LT_KERNEL_EFI | LT_INITRD_EFI | LT_KERNEL_16 |
3135        LT_INITRD_16)) {
3136     char *rootspec =
3137        getRootSpecifier(tmplLine->elements[1].item);
3138     if (rootspec != NULL) {
3139     free(newLine->elements[1].item);
3140     newLine->elements[1].item =
3141        sdupprintf("%s%s", rootspec, val);
3142     }
3143     }
3144     }
3145    
3146      new = malloc(sizeof(*new));   dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
3147      new->skip = 0;    newLine->elements[0].item : "");
     new->multiboot = 0;  
     new->next = config->entries;  
     new->lines = NULL;  
     config->entries = new;  
   
     /* copy/update from the template */  
     needs = NEED_KERNEL | NEED_TITLE;  
     if (newKernelInitrd)  
  needs |= NEED_INITRD;  
     if (newMBKernel) {  
         needs |= NEED_MB;  
         new->multiboot = 1;  
     }  
3148    
3149      if (template) {   if (!entry->lines) {
3150   for (masterLine = template->lines;   /* first one on the list */
3151       masterLine && (tmplLine = lineDup(masterLine));   entry->lines = newLine;
3152       lineFree(tmplLine), masterLine = masterLine->next)   } else if (prevLine) {
3153   {   /* add after prevLine */
3154      dbgPrintf("addNewKernel processing %d\n", tmplLine->type);   newLine->next = prevLine->next;
3155     prevLine->next = newLine;
3156      /* skip comments */   }
3157      chptr = tmplLine->indent;  
3158      while (*chptr && isspace(*chptr)) chptr++;   return newLine;
3159      if (*chptr == '#') continue;  }
3160    
3161      if (tmplLine->type == LT_KERNEL &&  /* val may be NULL */
3162   tmplLine->numElements >= 2) {  struct singleLine *addLine(struct singleEntry *entry,
3163   if (!template->multiboot && (needs & NEED_MB)) {     struct configFileInfo *cfi,
3164      /* it's not a multiboot template and this is the kernel     enum lineType_e type, char *defaultIndent,
3165       * line.  Try to be intelligent about inserting the     const char *val)
3166       * hypervisor at the same time.  {
3167       */   struct singleLine *line, *prev;
3168      if (config->cfi->mbHyperFirst) {   struct keywordTypes *kw;
3169   /* insert the hypervisor first */   struct singleLine tmpl;
3170   newLine = addLine(new, config->cfi, LT_HYPER,  
3171    tmplLine->indent,   /* NB: This function shouldn't allocate items on the heap, rather on
3172    newMBKernel + strlen(prefix));   * the stack since it calls addLineTmpl which will make copies.
  /* 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;  
  }  
   
     } 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;  
  }  
   
     } else if (tmplLine->type == LT_INITRD &&  
        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;  
  }  
   
     } else if (tmplLine->type == LT_TITLE &&  
        (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 {  
  /* don't have a template, so start the entry with the  
  * appropriate starting line  
3173   */   */
3174   switch (config->cfi->entryStart) {   if (type == LT_TITLE && cfi->titleBracketed) {
3175      case LT_KERNEL:   /* we're doing a bracketed title (zipl) */
3176   if (new->multiboot && config->cfi->mbHyperFirst) {   tmpl.type = type;
3177      /* fall through to LT_HYPER */   tmpl.numElements = 1;
3178   } else {   tmpl.elements = alloca(sizeof(*tmpl.elements));
3179      newLine = addLine(new, config->cfi, LT_KERNEL,   tmpl.elements[0].item = alloca(strlen(val) + 3);
3180        config->primaryIndent,   sprintf(tmpl.elements[0].item, "[%s]", val);
3181        newKernelPath + strlen(prefix));   tmpl.elements[0].indent = "";
3182      needs &= ~NEED_KERNEL;   val = NULL;
3183      break;   } else if (type == LT_MENUENTRY) {
3184     char *lineend = "--class gnu-linux --class gnu --class os {";
3185     if (!val) {
3186     fprintf(stderr,
3187     "Line type LT_MENUENTRY requires a value\n");
3188     abort();
3189     }
3190     kw = getKeywordByType(type, cfi);
3191     if (!kw) {
3192     fprintf(stderr,
3193     "Looking up keyword for unknown type %d\n",
3194     type);
3195     abort();
3196   }   }
3197     tmpl.indent = "";
3198     tmpl.type = type;
3199     tmpl.numElements = 3;
3200     tmpl.elements =
3201        alloca(sizeof(*tmpl.elements) * tmpl.numElements);
3202     tmpl.elements[0].item = kw->key;
3203     tmpl.elements[0].indent = alloca(2);
3204     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
3205     tmpl.elements[1].item = (char *)val;
3206     tmpl.elements[1].indent = alloca(2);
3207     sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
3208     tmpl.elements[2].item = alloca(strlen(lineend) + 1);
3209     strcpy(tmpl.elements[2].item, lineend);
3210     tmpl.elements[2].indent = "";
3211     } else {
3212     kw = getKeywordByType(type, cfi);
3213     if (!kw) {
3214     fprintf(stderr,
3215     "Looking up keyword for unknown type %d\n",
3216     type);
3217     abort();
3218     }
3219     tmpl.type = type;
3220     tmpl.numElements = val ? 2 : 1;
3221     tmpl.elements =
3222        alloca(sizeof(*tmpl.elements) * tmpl.numElements);
3223     tmpl.elements[0].item = kw->key;
3224     tmpl.elements[0].indent = alloca(2);
3225     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
3226     if (val) {
3227     tmpl.elements[1].item = (char *)val;
3228     tmpl.elements[1].indent = "";
3229     }
3230     }
3231    
3232      case LT_HYPER:   /* The last non-empty line gives us the indention to us and the line
3233   newLine = addLine(new, config->cfi, LT_HYPER,   * to insert after. Note that comments are considered empty lines,
3234    config->primaryIndent,   * which may not be ideal? If there are no lines or we are looking at
3235    newMBKernel + strlen(prefix));   * the first line, we use defaultIndent (the first line is normally
3236   needs &= ~NEED_MB;   * indented differently from the rest) */
3237   break;   for (line = entry->lines, prev = NULL; line; line = line->next) {
3238     if (line->numElements)
3239     prev = line;
3240     /* fall back on the last line if prev isn't otherwise set */
3241     if (!line->next && !prev)
3242     prev = line;
3243     }
3244    
3245     struct singleLine *menuEntry;
3246     menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
3247     if (tmpl.type == LT_ENTRY_END) {
3248     if (menuEntry)
3249     tmpl.indent = menuEntry->indent;
3250     else
3251     tmpl.indent = defaultIndent ? : "";
3252     } else if (tmpl.type != LT_MENUENTRY) {
3253     if (menuEntry)
3254     tmpl.indent = "\t";
3255     else if (prev == entry->lines)
3256     tmpl.indent = defaultIndent ? : "";
3257     else
3258     tmpl.indent = prev->indent;
3259     }
3260    
3261      case LT_TITLE:   return addLineTmpl(entry, &tmpl, prev, val, cfi);
3262   if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)  }
3263   char * templabel;  
3264   int x = 0, y = 0;  void removeLine(struct singleEntry *entry, struct singleLine *line)
3265    {
3266   templabel = strdup(newKernelTitle);   struct singleLine *prev;
3267   while( templabel[x]){   int i;
3268   if( templabel[x] == ' ' ){  
3269   y = x;   for (i = 0; i < line->numElements; i++) {
3270   while( templabel[y] ){   free(line->elements[i].item);
3271   templabel[y] = templabel[y+1];   free(line->elements[i].indent);
3272   y++;   }
3273   }   free(line->elements);
3274     free(line->indent);
3275    
3276     if (line == entry->lines) {
3277     entry->lines = line->next;
3278     } else {
3279     prev = entry->lines;
3280     while (prev->next != line)
3281     prev = prev->next;
3282     prev->next = line->next;
3283     }
3284    
3285     free(line);
3286    }
3287    
3288    static void requote(struct singleLine *tmplLine, struct configFileInfo *cfi)
3289    {
3290     struct singleLine newLine = {
3291     .indent = tmplLine->indent,
3292     .type = tmplLine->type,
3293     .next = tmplLine->next,
3294     };
3295     int firstQuotedItem = -1;
3296     int quoteLen = 0;
3297     int j;
3298     int element = 0;
3299     char *c;
3300    
3301     c = malloc(strlen(tmplLine->elements[0].item) + 1);
3302     strcpy(c, tmplLine->elements[0].item);
3303     insertElement(&newLine, c, element++, cfi);
3304     free(c);
3305     c = NULL;
3306    
3307     for (j = 1; j < tmplLine->numElements; j++) {
3308     if (firstQuotedItem == -1) {
3309     quoteLen += strlen(tmplLine->elements[j].item);
3310    
3311     if (isquote(tmplLine->elements[j].item[0])) {
3312     firstQuotedItem = j;
3313     quoteLen +=
3314        strlen(tmplLine->elements[j].indent);
3315     } else {
3316     c = malloc(quoteLen + 1);
3317     strcpy(c, tmplLine->elements[j].item);
3318     insertElement(&newLine, c, element++, cfi);
3319     free(c);
3320     quoteLen = 0;
3321     }
3322     } else {
3323     int itemlen = strlen(tmplLine->elements[j].item);
3324     quoteLen += itemlen;
3325     quoteLen += strlen(tmplLine->elements[j].indent);
3326    
3327     if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
3328     c = malloc(quoteLen + 1);
3329     c[0] = '\0';
3330     for (int i = firstQuotedItem; i < j + 1; i++) {
3331     strcat(c, tmplLine->elements[i].item);
3332     strcat(c, tmplLine->elements[i].indent);
3333   }   }
3334   x++;   insertElement(&newLine, c, element++, cfi);
3335     free(c);
3336    
3337     firstQuotedItem = -1;
3338     quoteLen = 0;
3339   }   }
  newLine = addLine(new, config->cfi, LT_TITLE,  
   config->primaryIndent, templabel);  
  free(templabel);  
  }else{  
  newLine = addLine(new, config->cfi, LT_TITLE,  
   config->primaryIndent, newKernelTitle);  
3340   }   }
  needs &= ~NEED_TITLE;  
  break;  
   
     default:  
  abort();  
3341   }   }
3342      }   while (tmplLine->numElements)
3343     removeElement(tmplLine, 0);
3344     if (tmplLine->elements)
3345     free(tmplLine->elements);
3346    
3347     tmplLine->numElements = newLine.numElements;
3348     tmplLine->elements = newLine.elements;
3349    }
3350    
3351      /* add the remainder of the lines, i.e. those that either  static void insertElement(struct singleLine *line,
3352       * weren't present in the template, or in the case of no template,    const char *item, int insertHere,
3353       * all the lines following the entryStart.    struct configFileInfo *cfi)
3354       */  {
3355      if (needs & NEED_TITLE) {   struct keywordTypes *kw;
3356   newLine = addLine(new, config->cfi, LT_TITLE,   char indent[2] = "";
   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;  
     }  
3357    
3358      if (needs) {   /* sanity check */
3359   printf(_("grubby: needs=%d, aborting\n"), needs);   if (insertHere > line->numElements) {
3360   abort();   dbgPrintf
3361      }      ("insertElement() adjusting insertHere from %d to %d\n",
3362         insertHere, line->numElements);
3363     insertHere = line->numElements;
3364     }
3365    
3366     line->elements = realloc(line->elements, (line->numElements + 1) *
3367     sizeof(*line->elements));
3368     memmove(&line->elements[insertHere + 1],
3369     &line->elements[insertHere],
3370     (line->numElements - insertHere) * sizeof(*line->elements));
3371     line->elements[insertHere].item = strdup(item);
3372    
3373     kw = getKeywordByType(line->type, cfi);
3374    
3375     if (line->numElements == 0) {
3376     indent[0] = '\0';
3377     } else if (insertHere == 0) {
3378     indent[0] = kw->nextChar;
3379     } else if (kw->separatorChar != '\0') {
3380     indent[0] = kw->separatorChar;
3381     } else {
3382     indent[0] = ' ';
3383     }
3384    
3385     if (insertHere > 0 && line->elements[insertHere - 1].indent[0] == '\0') {
3386     /* move the end-of-line forward */
3387     line->elements[insertHere].indent =
3388        line->elements[insertHere - 1].indent;
3389     line->elements[insertHere - 1].indent = strdup(indent);
3390     } else {
3391     line->elements[insertHere].indent = strdup(indent);
3392     }
3393    
3394      if (updateImage(config, "0", prefix, newKernelArgs, NULL,   line->numElements++;
                     newMBKernelArgs, NULL)) return 1;  
3395    
3396      return 0;   dbgPrintf("insertElement(%s, '%s%s', %d)\n",
3397      line->elements[0].item,
3398      line->elements[insertHere].item,
3399      line->elements[insertHere].indent, insertHere);
3400  }  }
3401    
3402  static void traceback(int signum)  static void removeElement(struct singleLine *line, int removeHere)
3403  {  {
3404      void *array[40];   int i;
     size_t size;  
3405    
3406      signal(SIGSEGV, SIG_DFL);   /* sanity check */
3407      memset(array, '\0', sizeof (array));   if (removeHere >= line->numElements)
3408      size = backtrace(array, 40);   return;
3409    
3410      fprintf(stderr, "grubby recieved SIGSEGV!  Backtrace (%ld):\n",   dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
3411              (unsigned long)size);    removeHere, line->elements[removeHere].item);
3412      backtrace_symbols_fd(array, size, STDERR_FILENO);  
3413      exit(1);   free(line->elements[removeHere].item);
3414  }  
3415     if (removeHere > 1) {
3416  int main(int argc, const char ** argv) {   /* previous argument gets this argument's post-indentation */
3417      poptContext optCon;   free(line->elements[removeHere - 1].indent);
3418      char * grubConfig = NULL;   line->elements[removeHere - 1].indent =
3419      char * outputFile = NULL;      line->elements[removeHere].indent;
3420      int arg = 0;   } else {
3421      int flags = 0;   free(line->elements[removeHere].indent);
     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;  
     char * newKernelArgs = NULL;  
     char * newKernelInitrd = NULL;  
     char * newKernelTitle = NULL;  
     char * newKernelVersion = NULL;  
     char * newMBKernel = NULL;  
     char * newMBKernelArgs = NULL;  
     char * removeMBKernelArgs = NULL;  
     char * removeMBKernel = NULL;  
     char * bootPrefix = NULL;  
     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;  
     struct poptOption options[] = {  
  { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,  
     _("add an entry for the specified kernel"), _("kernel-path") },  
  { "add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,  
     _("add an entry for the specified multiboot kernel"), NULL },  
  { "args", 0, POPT_ARG_STRING, &newKernelArgs, 0,  
     _("default arguments for the new kernel or new arguments for "  
       "kernel being updated"), _("args") },  
  { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,  
     _("default arguments for the new multiboot kernel or "  
               "new arguments for multiboot kernel being updated"), NULL },  
  { "bad-image-okay", 0, 0, &badImageOkay, 0,  
     _("don't sanity check images in boot entries (for testing only)"),  
     NULL },  
  { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,  
     _("filestystem which contains /boot directory (for testing only)"),  
     _("bootfs") },  
 #if defined(__i386__) || defined(__x86_64__)  
  { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,  
     _("check if lilo is installed on lilo.conf boot sector") },  
 #endif  
  { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,  
     _("path to grub config file to update (\"-\" for stdin)"),  
     _("path") },  
  { "copy-default", 0, 0, &copyDefault, 0,  
     _("use the default boot entry as a template for the new entry "  
       "being added; if the default is not a linux image, or if "  
       "the kernel referenced by the default image does not exist, "  
       "the first linux entry whose kernel does exist is used as the "  
       "template"), NULL },  
  { "default-kernel", 0, 0, &displayDefault, 0,  
     _("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,  
     _("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,  
     _("make the newly added entry the default boot entry"), NULL },  
  { "output-file", 'o', POPT_ARG_STRING, &outputFile, 0,  
     _("path to output updated config file (\"-\" for stdout)"),  
     _("path") },  
  { "remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,  
             _("remove kernel arguments"), NULL },  
         { "remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,  
     _("remove multiboot kernel arguments"), NULL },  
  { "remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,  
     _("remove all entries for the specified kernel"),  
     _("kernel-path") },  
  { "remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,  
             _("remove all entries for the specified multiboot kernel"), NULL },  
  { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,  
     _("make the first entry referencing the specified kernel "  
       "the default"), _("kernel-path") },  
  { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,  
     _("configure silo bootloader") },  
  { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,  
     _("title to use for the new kernel entry"), _("entry-title") },  
  { "update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,  
     _("updated information for the specified kernel"),  
     _("kernel-path") },  
  { "version", 'v', 0, NULL, 'v',  
     _("print the version of this program and exit"), NULL },  
  { "yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,  
     _("configure yaboot bootloader") },  
  { "zipl", 0, POPT_ARG_NONE, &configureZipl, 0,  
     _("configure zipl bootloader") },  
  POPT_AUTOHELP  
  { 0, 0, 0, 0, 0 }  
     };  
   
     useextlinuxmenu=0;  
   
     signal(SIGSEGV, traceback);  
   
     optCon = poptGetContext("grubby", argc, argv, options, 0);  
     poptReadDefaultConfig(optCon, 1);  
   
     while ((arg = poptGetNextOpt(optCon)) >= 0) {  
  switch (arg) {  
   case 'v':  
     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;  
3422   }   }
     }  
3423    
3424      if (arg < -1) {   /* now collapse the array, but don't bother to realloc smaller */
3425   fprintf(stderr, _("grubby: bad argument %s: %s\n"),   for (i = removeHere; i < line->numElements - 1; i++)
3426   poptBadOption(optCon, POPT_BADOPTION_NOALIAS),   line->elements[i] = line->elements[i + 1];
  poptStrerror(arg));  
  return 1;  
     }  
3427    
3428      if ((chptr = poptGetArg(optCon))) {   line->numElements--;
3429   fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);  }
  return 1;  
     }  
3430    
3431      if ((configureLilo + configureGrub + configureELilo +  int argMatch(const char *one, const char *two)
3432   configureYaboot + configureSilo + configureZipl +  {
3433   configureExtLinux ) > 1) {   char *first, *second;
3434   fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));   char *chptr;
  return 1;  
     } else if (bootloaderProbe && grubConfig) {  
  fprintf(stderr,  
     _("grubby: cannot specify config file with --bootloader-probe\n"));  
  return 1;  
     } else if (configureLilo) {  
  cfi = &liloConfigType;  
     } else if (configureGrub) {  
  cfi = &grubConfigType;  
     } else if (configureELilo) {  
  cfi = &eliloConfigType;  
     } else if (configureYaboot) {  
  cfi = &yabootConfigType;  
     } else if (configureSilo) {  
         cfi = &siloConfigType;  
     } else if (configureZipl) {  
         cfi = &ziplConfigType;  
     } else if (configureExtLinux) {  
  cfi = &extlinuxConfigType;  
  useextlinuxmenu=1;  
     }  
3435    
3436      if (!cfi) {   first = strcpy(alloca(strlen(one) + 1), one);
3437        #ifdef __ia64__   second = strcpy(alloca(strlen(two) + 1), two);
  cfi = &eliloConfigType;  
       #elif __powerpc__  
  cfi = &yabootConfigType;  
       #elif __sparc__  
         cfi = &siloConfigType;  
       #elif __s390__  
         cfi = &ziplConfigType;  
       #elif __s390x__  
         cfi = &ziplConfigtype;  
       #else  
  cfi = &grubConfigType;  
       #endif  
     }  
3438    
3439      if (!grubConfig)   chptr = strchr(first, '=');
3440   grubConfig = cfi->defaultConfig;   if (chptr)
3441     *chptr = '\0';
3442    
3443      if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion ||   chptr = strchr(second, '=');
3444    newKernelPath || removeKernelPath || makeDefault ||   if (chptr)
3445    defaultKernel)) {   *chptr = '\0';
  fprintf(stderr, _("grubby: --bootloader-probe may not be used with "  
   "specified option"));  
  return 1;  
     }  
3446    
3447      if ((displayDefault || kernelInfo) && (newKernelVersion || newKernelPath ||   return strcmp(first, second);
3448     removeKernelPath)) {  }
  fprintf(stderr, _("grubby: --default-kernel and --info may not "  
   "be used when adding or removing kernels\n"));  
  return 1;  
     }  
3449    
3450      if (newKernelPath && !newKernelTitle) {  int updateActualImage(struct grubConfig *cfg, const char *image,
3451   fprintf(stderr, _("grubby: kernel title must be specified\n"));        const char *prefix, const char *addArgs,
3452   return 1;        const char *removeArgs, int multibootArgs)
3453      } else if (!newKernelPath && (newKernelTitle  || copyDefault ||  {
3454    (newKernelInitrd && !updateKernelPath)||   struct singleEntry *entry;
3455    makeDefault || extraInitrdCount > 0)) {   struct singleLine *line, *rootLine;
3456   fprintf(stderr, _("grubby: kernel path expected\n"));   int index = 0;
3457   return 1;   int i, k;
3458      }   const char **newArgs, **oldArgs;
3459     const char **arg;
3460     int useKernelArgs, useRoot;
3461     int firstElement;
3462     int *usedElements;
3463     int doreplace;
3464    
3465     if (!image)
3466     return 0;
3467    
3468     if (!addArgs) {
3469     newArgs = malloc(sizeof(*newArgs));
3470     *newArgs = NULL;
3471     } else {
3472     if (poptParseArgvString(addArgs, NULL, &newArgs)) {
3473     fprintf(stderr,
3474     _("grubby: error separating arguments '%s'\n"),
3475     addArgs);
3476     return 1;
3477     }
3478     }
3479    
3480      if (newKernelPath && updateKernelPath) {   if (!removeArgs) {
3481   fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"   oldArgs = malloc(sizeof(*oldArgs));
3482            "not be used together"));   *oldArgs = NULL;
3483   return 1;   } else {
3484      }   if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {
3485     fprintf(stderr,
3486     _("grubby: error separating arguments '%s'\n"),
3487     removeArgs);
3488     free(newArgs);
3489     return 1;
3490     }
3491     }
3492    
3493     useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
3494     && (!multibootArgs || cfg->cfi->mbConcatArgs));
3495    
3496     useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
3497       && !multibootArgs);
3498    
3499     for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3500    
3501     if (multibootArgs && !entry->multiboot)
3502     continue;
3503    
3504     /* Determine where to put the args.  If this config supports
3505     * LT_KERNELARGS, use that.  Otherwise use
3506     * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
3507     */
3508     if (useKernelArgs) {
3509     line = getLineByType(LT_KERNELARGS, entry->lines);
3510     if (!line) {
3511     /* no LT_KERNELARGS, need to add it */
3512     line = addLine(entry, cfg->cfi, LT_KERNELARGS,
3513           cfg->secondaryIndent, NULL);
3514     }
3515     firstElement = 1;
3516    
3517     } else if (multibootArgs) {
3518     line = getLineByType(LT_HYPER, entry->lines);
3519     if (!line) {
3520     /* a multiboot entry without LT_HYPER? */
3521     continue;
3522     }
3523     firstElement = 2;
3524    
3525     } else {
3526     line =
3527        getLineByType(LT_KERNEL | LT_MBMODULE |
3528      LT_KERNEL_EFI | LT_KERNEL_16,
3529      entry->lines);
3530     if (!line) {
3531     /* no LT_KERNEL or LT_MBMODULE in this entry? */
3532     continue;
3533     }
3534     firstElement = 2;
3535     }
3536    
3537     /* handle the elilo case which does:
3538     *   append="hypervisor args -- kernel args"
3539     */
3540     if (entry->multiboot && cfg->cfi->mbConcatArgs) {
3541     /* this is a multiboot entry, make sure there's
3542     * -- on the args line
3543     */
3544     for (i = firstElement; i < line->numElements; i++) {
3545     if (!strcmp(line->elements[i].item, "--"))
3546     break;
3547     }
3548     if (i == line->numElements) {
3549     /* assume all existing args are kernel args,
3550     * prepend -- to make it official
3551     */
3552     insertElement(line, "--", firstElement,
3553          cfg->cfi);
3554     i = firstElement;
3555     }
3556     if (!multibootArgs) {
3557     /* kernel args start after the -- */
3558     firstElement = i + 1;
3559     }
3560     } else if (cfg->cfi->mbConcatArgs) {
3561     /* this is a non-multiboot entry, remove hyper args */
3562     for (i = firstElement; i < line->numElements; i++) {
3563     if (!strcmp(line->elements[i].item, "--"))
3564     break;
3565     }
3566     if (i < line->numElements) {
3567     /* remove args up to -- */
3568     while (strcmp
3569           (line->elements[firstElement].item,
3570     "--"))
3571     removeElement(line, firstElement);
3572     /* remove -- */
3573     removeElement(line, firstElement);
3574     }
3575     }
3576    
3577     usedElements = calloc(line->numElements, sizeof(*usedElements));
3578    
3579     for (k = 0, arg = newArgs; *arg; arg++, k++) {
3580    
3581     doreplace = 1;
3582     for (i = firstElement; i < line->numElements; i++) {
3583     if (multibootArgs && cfg->cfi->mbConcatArgs &&
3584        !strcmp(line->elements[i].item, "--")) {
3585     /* reached the end of hyper args, insert here */
3586     doreplace = 0;
3587     break;
3588     }
3589     if (usedElements[i])
3590     continue;
3591     if (!argMatch(line->elements[i].item, *arg)) {
3592     usedElements[i] = 1;
3593     break;
3594     }
3595     }
3596    
3597     if (i < line->numElements && doreplace) {
3598     /* direct replacement */
3599     free(line->elements[i].item);
3600     line->elements[i].item = strdup(*arg);
3601    
3602     } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
3603     /* root= replacement */
3604     rootLine = getLineByType(LT_ROOT, entry->lines);
3605     if (rootLine) {
3606     free(rootLine->elements[1].item);
3607     rootLine->elements[1].item =
3608        strdup(*arg + 5);
3609     } else {
3610     rootLine =
3611        addLine(entry, cfg->cfi, LT_ROOT,
3612        cfg->secondaryIndent,
3613        *arg + 5);
3614     }
3615     }
3616    
3617     else {
3618     /* insert/append */
3619     insertElement(line, *arg, i, cfg->cfi);
3620     usedElements =
3621        realloc(usedElements,
3622        line->numElements *
3623        sizeof(*usedElements));
3624     memmove(&usedElements[i + 1], &usedElements[i],
3625     line->numElements - i - 1);
3626     usedElements[i] = 1;
3627    
3628     /* if we updated a root= here even though
3629     * there is a LT_ROOT available we need to
3630     * remove the LT_ROOT entry (this will happen
3631     * if we switch from a device to a label) */
3632     if (useRoot && !strncmp(*arg, "root=", 5)) {
3633     rootLine =
3634        getLineByType(LT_ROOT,
3635      entry->lines);
3636     if (rootLine)
3637     removeLine(entry, rootLine);
3638     }
3639     }
3640     }
3641    
3642     free(usedElements);
3643    
3644     for (arg = oldArgs; *arg; arg++) {
3645     for (i = firstElement; i < line->numElements; i++) {
3646     if (multibootArgs && cfg->cfi->mbConcatArgs &&
3647        !strcmp(line->elements[i].item, "--"))
3648     /* reached the end of hyper args, stop here */
3649     break;
3650     if (!argMatch(line->elements[i].item, *arg)) {
3651     removeElement(line, i);
3652     break;
3653     }
3654     }
3655     /* handle removing LT_ROOT line too */
3656     if (useRoot && !strncmp(*arg, "root=", 5)) {
3657     rootLine = getLineByType(LT_ROOT, entry->lines);
3658     if (rootLine)
3659     removeLine(entry, rootLine);
3660     }
3661     }
3662    
3663     if (line->numElements == 1) {
3664     /* don't need the line at all (note it has to be a
3665       LT_KERNELARGS for this to happen */
3666     removeLine(entry, line);
3667     }
3668     }
3669    
3670     free(newArgs);
3671     free(oldArgs);
3672    
3673     return 0;
3674    }
3675    
3676    int updateImage(struct grubConfig *cfg, const char *image,
3677     const char *prefix, const char *addArgs,
3678     const char *removeArgs,
3679     const char *addMBArgs, const char *removeMBArgs)
3680    {
3681     int rc = 0;
3682    
3683     if (!image)
3684     return rc;
3685    
3686     /* update the main args first... */
3687     if (addArgs || removeArgs)
3688     rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs,
3689           0);
3690     if (rc)
3691     return rc;
3692    
3693     /* and now any multiboot args */
3694     if (addMBArgs || removeMBArgs)
3695     rc = updateActualImage(cfg, image, prefix, addMBArgs,
3696           removeMBArgs, 1);
3697     return rc;
3698    }
3699    
3700    int addMBInitrd(struct grubConfig *cfg, const char *newMBKernel,
3701     const char *image, const char *prefix, const char *initrd,
3702     const char *title)
3703    {
3704     struct singleEntry *entry;
3705     struct singleLine *line, *kernelLine, *endLine = NULL;
3706     int index = 0;
3707    
3708     if (!image)
3709     return 0;
3710    
3711     for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3712     kernelLine = getLineByType(LT_MBMODULE, entry->lines);
3713     if (!kernelLine)
3714     continue;
3715    
3716     /* if title is supplied, the entry's title must match it. */
3717     if (title) {
3718     char *linetitle;
3719    
3720     line =
3721        getLineByType(LT_TITLE | LT_MENUENTRY,
3722      entry->lines);
3723     if (!line)
3724     continue;
3725    
3726     linetitle = extractTitle(cfg, line);
3727     if (!linetitle)
3728     continue;
3729     if (strcmp(title, linetitle)) {
3730     free(linetitle);
3731     continue;
3732     }
3733     free(linetitle);
3734     }
3735    
3736     if (prefix) {
3737     int prefixLen = strlen(prefix);
3738     if (!strncmp(initrd, prefix, prefixLen))
3739     initrd += prefixLen;
3740     }
3741     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3742     if (endLine)
3743     removeLine(entry, endLine);
3744     line =
3745        addLine(entry, cfg->cfi,
3746        preferredLineType(LT_MBMODULE, cfg->cfi),
3747        kernelLine->indent, initrd);
3748     if (!line)
3749     return 1;
3750     if (endLine) {
3751     line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3752     if (!line)
3753     return 1;
3754     }
3755    
3756     break;
3757     }
3758    
3759     return 0;
3760    }
3761    
3762    int updateInitrd(struct grubConfig *cfg, const char *image,
3763     const char *prefix, const char *initrd, const char *title)
3764    {
3765     struct singleEntry *entry;
3766     struct singleLine *line, *kernelLine, *endLine = NULL;
3767     int index = 0;
3768    
3769     if (!image)
3770     return 0;
3771    
3772     for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3773     kernelLine =
3774        getLineByType(LT_KERNEL | LT_KERNEL_EFI | LT_KERNEL_16,
3775      entry->lines);
3776     if (!kernelLine)
3777     continue;
3778    
3779     /* if title is supplied, the entry's title must match it. */
3780     if (title) {
3781     char *linetitle;
3782    
3783     line =
3784        getLineByType(LT_TITLE | LT_MENUENTRY,
3785      entry->lines);
3786     if (!line)
3787     continue;
3788    
3789     linetitle = extractTitle(cfg, line);
3790     if (!linetitle)
3791     continue;
3792     if (strcmp(title, linetitle)) {
3793     free(linetitle);
3794     continue;
3795     }
3796     free(linetitle);
3797     }
3798    
3799     line =
3800        getLineByType(LT_INITRD | LT_INITRD_EFI | LT_INITRD_16,
3801      entry->lines);
3802     if (line)
3803     removeLine(entry, line);
3804     if (prefix) {
3805     int prefixLen = strlen(prefix);
3806     if (!strncmp(initrd, prefix, prefixLen))
3807     initrd += prefixLen;
3808     }
3809     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3810     if (endLine)
3811     removeLine(entry, endLine);
3812     enum lineType_e lt;
3813     switch (kernelLine->type) {
3814     case LT_KERNEL:
3815     lt = LT_INITRD;
3816     break;
3817     case LT_KERNEL_EFI:
3818     lt = LT_INITRD_EFI;
3819     break;
3820     case LT_KERNEL_16:
3821     lt = LT_INITRD_16;
3822     break;
3823     default:
3824     lt = preferredLineType(LT_INITRD, cfg->cfi);
3825     }
3826     line = addLine(entry, cfg->cfi, lt, kernelLine->indent, initrd);
3827     if (!line)
3828     return 1;
3829     if (endLine) {
3830     line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3831     if (!line)
3832     return 1;
3833     }
3834    
3835     break;
3836     }
3837    
3838     return 0;
3839    }
3840    
3841    int checkDeviceBootloader(const char *device, const unsigned char *boot)
3842    {
3843     int fd;
3844     unsigned char bootSect[512];
3845     int offset;
3846    
3847     fd = open(device, O_RDONLY);
3848     if (fd < 0) {
3849     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3850     device, strerror(errno));
3851     return 1;
3852     }
3853    
3854     if (read(fd, bootSect, 512) != 512) {
3855     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3856     device, strerror(errno));
3857     return 1;
3858     }
3859     close(fd);
3860    
3861     /* first three bytes should match, a jmp short should be in there */
3862     if (memcmp(boot, bootSect, 3))
3863     return 0;
3864    
3865     if (boot[1] == JMP_SHORT_OPCODE) {
3866     offset = boot[2] + 2;
3867     } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
3868     offset = (boot[3] << 8) + boot[2] + 2;
3869     } else if (boot[0] == JMP_SHORT_OPCODE) {
3870     offset = boot[1] + 2;
3871     /*
3872     * it looks like grub, when copying stage1 into the mbr,
3873     * patches stage1 right after the JMP location, replacing
3874     * other instructions such as JMPs for NOOPs. So, relax the
3875     * check a little bit by skipping those different bytes.
3876     */
3877     if ((bootSect[offset + 1] == NOOP_OPCODE)
3878        && (bootSect[offset + 2] == NOOP_OPCODE)) {
3879     offset = offset + 3;
3880     }
3881     } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
3882     offset = (boot[2] << 8) + boot[1] + 2;
3883     } else {
3884     return 0;
3885     }
3886    
3887     if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
3888     return 0;
3889    
3890     return 2;
3891    }
3892    
3893    int checkLiloOnRaid(char *mdDev, const unsigned char *boot)
3894    {
3895     int fd;
3896     char buf[65536];
3897     char *end;
3898     char *chptr;
3899     char *chptr2;
3900     int rc;
3901    
3902     /* it's on raid; we need to parse /proc/mdstat and check all of the
3903     *raw* devices listed in there */
3904    
3905     if (!strncmp(mdDev, "/dev/", 5))
3906     mdDev += 5;
3907    
3908     if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {
3909     fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),
3910     strerror(errno));
3911     return 2;
3912     }
3913    
3914     rc = read(fd, buf, sizeof(buf) - 1);
3915     if (rc < 0 || rc == (sizeof(buf) - 1)) {
3916     fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),
3917     strerror(errno));
3918     close(fd);
3919     return 2;
3920     }
3921     close(fd);
3922     buf[rc] = '\0';
3923    
3924     chptr = buf;
3925     while (*chptr) {
3926     end = strchr(chptr, '\n');
3927     if (!end)
3928     break;
3929     *end = '\0';
3930    
3931     if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
3932        chptr[strlen(mdDev)] == ' ') {
3933    
3934     /* found the device */
3935     while (*chptr && *chptr != ':')
3936     chptr++;
3937     chptr++;
3938     while (*chptr && isspace(*chptr))
3939     chptr++;
3940    
3941     /* skip the "active" bit */
3942     while (*chptr && !isspace(*chptr))
3943     chptr++;
3944     while (*chptr && isspace(*chptr))
3945     chptr++;
3946    
3947     /* skip the raid level */
3948     while (*chptr && !isspace(*chptr))
3949     chptr++;
3950     while (*chptr && isspace(*chptr))
3951     chptr++;
3952    
3953     /* everything else is partition stuff */
3954     while (*chptr) {
3955     chptr2 = chptr;
3956     while (*chptr2 && *chptr2 != '[')
3957     chptr2++;
3958     if (!*chptr2)
3959     break;
3960    
3961     /* yank off the numbers at the end */
3962     chptr2--;
3963     while (isdigit(*chptr2) && chptr2 > chptr)
3964     chptr2--;
3965     chptr2++;
3966     *chptr2 = '\0';
3967    
3968     /* Better, now we need the /dev/ back. We're
3969     * done with everything before this point, so
3970     * we can just put the /dev/ part there.
3971     * There will always be room. */
3972     memcpy(chptr - 5, "/dev/", 5);
3973     rc = checkDeviceBootloader(chptr - 5, boot);
3974     if (rc != 2) {
3975     return rc;
3976     }
3977    
3978     chptr = chptr2 + 1;
3979     /* skip the [11] bit */
3980     while (*chptr && !isspace(*chptr))
3981     chptr++;
3982     /* and move to the next one */
3983     while (*chptr && isspace(*chptr))
3984     chptr++;
3985     }
3986    
3987     /*  we're good to go */
3988     return 2;
3989     }
3990    
3991     chptr = end + 1;
3992     }
3993    
3994     fprintf(stderr,
3995     _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
3996     mdDev);
3997     return 0;
3998    }
3999    
4000    int checkForLilo(struct grubConfig *config)
4001    {
4002     int fd;
4003     unsigned char boot[512];
4004     struct singleLine *line;
4005    
4006     for (line = config->theLines; line; line = line->next)
4007     if (line->type == LT_BOOT)
4008     break;
4009    
4010     if (!line) {
4011     fprintf(stderr,
4012     _
4013     ("grubby: no boot line found in lilo configuration\n"));
4014     return 1;
4015     }
4016    
4017     if (line->numElements != 2)
4018     return 1;
4019    
4020     fd = open("/boot/boot.b", O_RDONLY);
4021     if (fd < 0) {
4022     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
4023     "/boot/boot.b", strerror(errno));
4024     return 1;
4025     }
4026    
4027     if (read(fd, boot, 512) != 512) {
4028     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
4029     "/boot/boot.b", strerror(errno));
4030     return 1;
4031     }
4032     close(fd);
4033    
4034     if (!strncmp("/dev/md", line->elements[1].item, 7))
4035     return checkLiloOnRaid(line->elements[1].item, boot);
4036    
4037     return checkDeviceBootloader(line->elements[1].item, boot);
4038    }
4039    
4040    int checkForGrub2(struct grubConfig *config)
4041    {
4042     if (!access("/etc/grub.d/", R_OK))
4043     return 2;
4044    
     if (makeDefault && defaultKernel) {  
  fprintf(stderr, _("grubby: --make-default and --default-kernel "  
   "may not be used together\n"));  
  return 1;  
     } else if (defaultKernel && removeKernelPath &&  
  !strcmp(defaultKernel, removeKernelPath)) {  
  fprintf(stderr, _("grubby: cannot make removed kernel the default\n"));  
4045   return 1;   return 1;
4046      } else if (defaultKernel && newKernelPath &&  }
4047   !strcmp(defaultKernel, newKernelPath)) {  
4048   makeDefault = 1;  int checkForGrub(struct grubConfig *config)
4049   defaultKernel = NULL;  {
4050      }   int fd;
4051     unsigned char bootSect[512];
4052     char *boot;
4053     int onSuse = isSuseSystem();
4054    
4055     if (onSuse) {
4056     if (parseSuseGrubConf(NULL, &boot))
4057     return 0;
4058     } else {
4059     if (parseSysconfigGrub(NULL, &boot))
4060     return 0;
4061     }
4062    
4063     /* assume grub is not installed -- not an error condition */
4064     if (!boot)
4065     return 0;
4066    
4067     fd = open("/boot/grub/stage1", O_RDONLY);
4068     if (fd < 0)
4069     /* this doesn't exist if grub hasn't been installed */
4070     return 0;
4071    
4072     if (read(fd, bootSect, 512) != 512) {
4073     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
4074     "/boot/grub/stage1", strerror(errno));
4075     close(fd);
4076     return 1;
4077     }
4078     close(fd);
4079    
4080     /* The more elaborate checks do not work on SuSE. The checks done
4081     * seem to be reasonble (at least for now), so just return success
4082     */
4083     if (onSuse)
4084     return 2;
4085    
4086     return checkDeviceBootloader(boot, bootSect);
4087    }
4088    
4089    int checkForExtLinux(struct grubConfig *config)
4090    {
4091     int fd;
4092     unsigned char bootSect[512];
4093     char *boot;
4094     char executable[] = "/boot/extlinux/extlinux";
4095    
4096     printf("entered: checkForExtLinux()\n");
4097    
4098     if (parseSysconfigGrub(NULL, &boot))
4099     return 0;
4100    
4101     /* assume grub is not installed -- not an error condition */
4102     if (!boot)
4103     return 0;
4104    
4105     fd = open(executable, O_RDONLY);
4106     if (fd < 0)
4107     /* this doesn't exist if grub hasn't been installed */
4108     return 0;
4109    
4110     if (read(fd, bootSect, 512) != 512) {
4111     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
4112     executable, strerror(errno));
4113     return 1;
4114     }
4115     close(fd);
4116    
4117     return checkDeviceBootloader(boot, bootSect);
4118    }
4119    
4120    int checkForYaboot(struct grubConfig *config)
4121    {
4122     /*
4123     * This is a simplistic check that we consider good enough for own puporses
4124     *
4125     * If we were to properly check if yaboot is *installed* we'd need to:
4126     * 1) get the system boot device (LT_BOOT)
4127     * 2) considering it's a raw filesystem, check if the yaboot binary matches
4128     *    the content on the boot device
4129     * 3) if not, copy the binary to a temporary file and run "addnote" on it
4130     * 4) check again if binary and boot device contents match
4131     */
4132     if (!access("/etc/yaboot.conf", R_OK))
4133     return 2;
4134    
     if (!strcmp(grubConfig, "-") && !outputFile) {  
  fprintf(stderr, _("grubby: output file must be specified if stdin "  
  "is used\n"));  
4135   return 1;   return 1;
4136      }  }
4137    
4138    int checkForElilo(struct grubConfig *config)
4139    {
4140     if (!access("/etc/elilo.conf", R_OK))
4141     return 2;
4142    
     if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel  
  && !kernelInfo && !bootloaderProbe && !updateKernelPath  
         && !removeMBKernel) {  
  fprintf(stderr, _("grubby: no action specified\n"));  
4143   return 1;   return 1;
4144      }  }
4145    
4146      flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;  static char *getRootSpecifier(char *str)
4147    {
4148     char *idx, *rootspec = NULL;
4149    
4150      if (cfi->needsBootPrefix) {   if (*str == '(') {
4151   if (!bootPrefix) {   idx = rootspec = strdup(str);
4152      bootPrefix = findBootPrefix();   while (*idx && (*idx != ')') && (!isspace(*idx)))
4153      if (!bootPrefix) return 1;   idx++;
4154   } else {   *(++idx) = '\0';
     /* this shouldn't end with a / */  
     if (bootPrefix[strlen(bootPrefix) - 1] == '/')  
  bootPrefix[strlen(bootPrefix) - 1] = '\0';  
4155   }   }
4156      } else {   return rootspec;
4157   bootPrefix = "";  }
     }  
4158    
4159      if (!cfi->mbAllowExtraInitRds &&  static char *getInitrdVal(struct grubConfig *config,
4160   extraInitrdCount > 0) {    const char *prefix, struct singleLine *tmplLine,
4161   fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);    const char *newKernelInitrd,
4162   return 1;    const char **extraInitrds, int extraInitrdCount)
4163      }  {
4164     char *initrdVal, *end;
4165     int i;
4166     size_t totalSize;
4167     size_t prefixLen;
4168     char separatorChar;
4169    
4170     prefixLen = strlen(prefix);
4171     totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */ ;
4172    
4173     for (i = 0; i < extraInitrdCount; i++) {
4174     totalSize += sizeof(separatorChar);
4175     totalSize += strlen(extraInitrds[i]) - prefixLen;
4176     }
4177    
4178     initrdVal = end = malloc(totalSize);
4179    
4180     end = stpcpy(end, newKernelInitrd + prefixLen);
4181    
4182     separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
4183     for (i = 0; i < extraInitrdCount; i++) {
4184     const char *extraInitrd;
4185     int j;
4186    
4187     extraInitrd = extraInitrds[i] + prefixLen;
4188     /* Don't add entries that are already there */
4189     if (tmplLine != NULL) {
4190     for (j = 2; j < tmplLine->numElements; j++)
4191     if (strcmp
4192        (extraInitrd,
4193         tmplLine->elements[j].item) == 0)
4194     break;
4195    
4196      if (bootloaderProbe) {   if (j != tmplLine->numElements)
4197   int lrc = 0, grc = 0, erc = 0;   continue;
4198   struct grubConfig * lconfig, * gconfig;   }
   
  if (!access(grubConfigType.defaultConfig, F_OK)) {  
     gconfig = readConfig(grubConfigType.defaultConfig, &grubConfigType);  
     if (!gconfig)  
  grc = 1;  
     else  
  grc = checkForGrub(gconfig);  
  }  
   
  if (!access(liloConfigType.defaultConfig, F_OK)) {  
     lconfig = readConfig(liloConfigType.defaultConfig, &liloConfigType);  
     if (!lconfig)  
  lrc = 1;  
     else  
  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");  
4199    
4200   return 0;   *end++ = separatorChar;
4201      }   end = stpcpy(end, extraInitrd);
4202     }
4203    
4204      config = readConfig(grubConfig, cfi);   return initrdVal;
4205      if (!config) return 1;  }
4206    
4207      if (displayDefault) {  int addNewKernel(struct grubConfig *config, struct singleEntry *template,
4208   struct singleLine * line;   const char *prefix,
4209   struct singleEntry * entry;   const char *newKernelPath, const char *newKernelTitle,
4210          char * rootspec;   const char *newKernelArgs, const char *newKernelInitrd,
4211     const char **extraInitrds, int extraInitrdCount,
4212   if (config->defaultImage == -1) return 0;   const char *newMBKernel, const char *newMBKernelArgs,
4213   entry = findEntryByIndex(config, config->defaultImage);   const char *newDevTreePath, int newIndex)
4214   if (!entry) return 0;  {
4215   if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;   struct singleEntry *new, *entry, *prev = NULL;
4216     struct singleLine *newLine = NULL, *tmplLine = NULL, *masterLine = NULL;
4217   line = getLineByType(LT_KERNEL|LT_HYPER, entry->lines);   int needs;
4218   if (!line) return 0;   char *chptr;
4219    
4220          rootspec = getRootSpecifier(line->elements[1].item);   if (!newKernelPath)
4221          printf("%s%s\n", bootPrefix, line->elements[1].item +   return 0;
4222                 ((rootspec != NULL) ? strlen(rootspec) : 0));  
4223     /* if the newKernelTitle is too long silently munge it into something
4224     * we can live with. truncating is first check, then we'll just mess with
4225     * it until it looks better */
4226     if (config->cfi->maxTitleLength &&
4227        (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
4228     char *buf = alloca(config->cfi->maxTitleLength + 7);
4229     char *numBuf = alloca(config->cfi->maxTitleLength + 1);
4230     int i = 1;
4231    
4232     sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength,
4233     newKernelTitle);
4234     while (findEntryByPath(config, buf, NULL, NULL)) {
4235     sprintf(numBuf, "%d", i++);
4236     strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
4237     }
4238    
4239     newKernelTitle = buf + 6;
4240     }
4241    
4242     new = malloc(sizeof(*new));
4243     new->skip = 0;
4244     new->multiboot = 0;
4245     new->lines = NULL;
4246     entry = config->entries;
4247     for (unsigned int i = 0; i < newIndex; i++) {
4248     if (!entry)
4249     break;
4250     prev = entry;
4251     entry = entry->next;
4252     }
4253     new->next = entry;
4254    
4255     if (prev)
4256     prev->next = new;
4257     else
4258     config->entries = new;
4259    
4260     /* copy/update from the template */
4261     needs = NEED_KERNEL | NEED_TITLE;
4262     if (newKernelInitrd)
4263     needs |= NEED_INITRD;
4264     if (newMBKernel) {
4265     needs |= NEED_MB;
4266     new->multiboot = 1;
4267     }
4268     if (newDevTreePath && getKeywordByType(LT_DEVTREE, config->cfi))
4269     needs |= NEED_DEVTREE;
4270    
4271     if (template) {
4272     for (masterLine = template->lines;
4273         masterLine && (tmplLine = lineDup(masterLine));
4274         lineFree(tmplLine), masterLine = masterLine->next) {
4275     dbgPrintf("addNewKernel processing %d\n",
4276      tmplLine->type);
4277    
4278     /* skip comments */
4279     chptr = tmplLine->indent;
4280     while (*chptr && isspace(*chptr))
4281     chptr++;
4282     if (*chptr == '#')
4283     continue;
4284    
4285     if (iskernel(tmplLine->type)
4286        && tmplLine->numElements >= 2) {
4287     if (!template->multiboot && (needs & NEED_MB)) {
4288     /* it's not a multiboot template and
4289     * this is the kernel line.  Try to
4290     * be intelligent about inserting the
4291     * hypervisor at the same time.
4292     */
4293     if (config->cfi->mbHyperFirst) {
4294     /* insert the hypervisor first */
4295     newLine =
4296        addLine(new, config->cfi,
4297        LT_HYPER,
4298        tmplLine->indent,
4299        newMBKernel +
4300        strlen(prefix));
4301     /* set up for adding the
4302     * kernel line */
4303     free(tmplLine->indent);
4304     tmplLine->indent =
4305        strdup(config->
4306       secondaryIndent);
4307     needs &= ~NEED_MB;
4308     }
4309     if (needs & NEED_KERNEL) {
4310     /* use addLineTmpl to
4311     * preserve line elements,
4312     * otherwise we could just
4313     * call addLine.
4314     * Unfortunately this means
4315     * making some changes to the
4316     * template such as the
4317     * indent change above and
4318     * the type change below.
4319     */
4320     struct keywordTypes *mbm_kw =
4321        getKeywordByType
4322        (LT_MBMODULE, config->cfi);
4323     if (mbm_kw) {
4324     tmplLine->type =
4325        LT_MBMODULE;
4326     free(tmplLine->
4327         elements[0].item);
4328     tmplLine->elements[0].
4329        item =
4330        strdup(mbm_kw->key);
4331     }
4332     newLine =
4333        addLineTmpl(new, tmplLine,
4334     newLine,
4335     newKernelPath +
4336     strlen(prefix),
4337     config->cfi);
4338     needs &= ~NEED_KERNEL;
4339     }
4340     if (needs & NEED_MB) { /* !mbHyperFirst */
4341     newLine =
4342        addLine(new, config->cfi,
4343        LT_HYPER,
4344        config->
4345        secondaryIndent,
4346        newMBKernel +
4347        strlen(prefix));
4348     needs &= ~NEED_MB;
4349     }
4350     } else if (needs & NEED_KERNEL) {
4351     newLine =
4352        addLineTmpl(new, tmplLine, newLine,
4353     newKernelPath +
4354     strlen(prefix),
4355     config->cfi);
4356     needs &= ~NEED_KERNEL;
4357     }
4358    
4359     } else if (tmplLine->type == LT_HYPER &&
4360       tmplLine->numElements >= 2) {
4361     if (needs & NEED_MB) {
4362     newLine =
4363        addLineTmpl(new, tmplLine, newLine,
4364     newMBKernel +
4365     strlen(prefix),
4366     config->cfi);
4367     needs &= ~NEED_MB;
4368     }
4369    
4370     } else if (tmplLine->type == LT_MBMODULE &&
4371       tmplLine->numElements >= 2) {
4372     if (new->multiboot) {
4373     if (needs & NEED_KERNEL) {
4374     newLine =
4375        addLineTmpl(new, tmplLine,
4376     newLine,
4377     newKernelPath +
4378     strlen(prefix),
4379     config->cfi);
4380     needs &= ~NEED_KERNEL;
4381     } else if (config->cfi->mbInitRdIsModule
4382       && (needs & NEED_INITRD)) {
4383     char *initrdVal;
4384     initrdVal =
4385        getInitrdVal(config, prefix,
4386     tmplLine,
4387     newKernelInitrd,
4388     extraInitrds,
4389     extraInitrdCount);
4390     newLine =
4391        addLineTmpl(new, tmplLine,
4392     newLine,
4393     initrdVal,
4394     config->cfi);
4395     free(initrdVal);
4396     needs &= ~NEED_INITRD;
4397     }
4398     } else if (needs & NEED_KERNEL) {
4399     /* template is multi but new is not,
4400     * insert the kernel in the first
4401     * module slot
4402     */
4403     tmplLine->type =
4404        preferredLineType(LT_KERNEL,
4405          config->cfi);
4406     free(tmplLine->elements[0].item);
4407     tmplLine->elements[0].item =
4408        strdup(getKeywordByType
4409       (tmplLine->type,
4410        config->cfi)->key);
4411     newLine =
4412        addLineTmpl(new, tmplLine, newLine,
4413     newKernelPath +
4414     strlen(prefix),
4415     config->cfi);
4416     needs &= ~NEED_KERNEL;
4417     } else if (needs & NEED_INITRD) {
4418     char *initrdVal;
4419     /* template is multi but new is not,
4420     * insert the initrd in the second
4421     * module slot
4422     */
4423     tmplLine->type =
4424        preferredLineType(LT_INITRD,
4425          config->cfi);
4426     free(tmplLine->elements[0].item);
4427     tmplLine->elements[0].item =
4428        strdup(getKeywordByType
4429       (tmplLine->type,
4430        config->cfi)->key);
4431     initrdVal =
4432        getInitrdVal(config, prefix,
4433     tmplLine,
4434     newKernelInitrd,
4435     extraInitrds,
4436     extraInitrdCount);
4437     newLine =
4438        addLineTmpl(new, tmplLine, newLine,
4439     initrdVal, config->cfi);
4440     free(initrdVal);
4441     needs &= ~NEED_INITRD;
4442     }
4443    
4444     } else if (isinitrd(tmplLine->type)
4445       && tmplLine->numElements >= 2) {
4446     if (needs & NEED_INITRD && new->multiboot
4447        && !template->multiboot
4448        && config->cfi->mbInitRdIsModule) {
4449     /* make sure we don't insert the
4450     * module initrd before the module
4451     * kernel... if we don't do it here,
4452     * it will be inserted following the
4453     * template.
4454     */
4455     if (!needs & NEED_KERNEL) {
4456     char *initrdVal;
4457    
4458     initrdVal =
4459        getInitrdVal(config, prefix,
4460     tmplLine,
4461     newKernelInitrd,
4462     extraInitrds,
4463     extraInitrdCount);
4464     newLine =
4465        addLine(new, config->cfi,
4466        LT_MBMODULE,
4467        config->
4468        secondaryIndent,
4469        initrdVal);
4470     free(initrdVal);
4471     needs &= ~NEED_INITRD;
4472     }
4473     } else if (needs & NEED_INITRD) {
4474     char *initrdVal;
4475     initrdVal =
4476        getInitrdVal(config, prefix,
4477     tmplLine,
4478     newKernelInitrd,
4479     extraInitrds,
4480     extraInitrdCount);
4481     newLine =
4482        addLineTmpl(new, tmplLine, newLine,
4483     initrdVal, config->cfi);
4484     free(initrdVal);
4485     needs &= ~NEED_INITRD;
4486     }
4487    
4488     } else if (tmplLine->type == LT_MENUENTRY &&
4489       (needs & NEED_TITLE)) {
4490     requote(tmplLine, config->cfi);
4491     char *nkt = malloc(strlen(newKernelTitle) + 3);
4492     strcpy(nkt, "'");
4493     strcat(nkt, newKernelTitle);
4494     strcat(nkt, "'");
4495     newLine =
4496        addLineTmpl(new, tmplLine, newLine, nkt,
4497     config->cfi);
4498     free(nkt);
4499     needs &= ~NEED_TITLE;
4500     } else if (tmplLine->type == LT_TITLE &&
4501       (needs & NEED_TITLE)) {
4502     if (tmplLine->numElements >= 2) {
4503     newLine =
4504        addLineTmpl(new, tmplLine, newLine,
4505     newKernelTitle,
4506     config->cfi);
4507     needs &= ~NEED_TITLE;
4508     } else if (tmplLine->numElements == 1 &&
4509       config->cfi->titleBracketed) {
4510     /* addLineTmpl doesn't handle
4511     * titleBracketed */
4512     newLine =
4513        addLine(new, config->cfi, LT_TITLE,
4514        tmplLine->indent,
4515        newKernelTitle);
4516     needs &= ~NEED_TITLE;
4517     }
4518     } else if (tmplLine->type == LT_ECHO) {
4519     requote(tmplLine, config->cfi);
4520     static const char *prefix = "'Loading ";
4521     if (tmplLine->numElements > 1 &&
4522        strstr(tmplLine->elements[1].item, prefix)
4523        && masterLine->next
4524        && iskernel(masterLine->next->type)) {
4525     char *newTitle =
4526        malloc(strlen(prefix) +
4527       strlen(newKernelTitle) + 2);
4528    
4529     strcpy(newTitle, prefix);
4530     strcat(newTitle, newKernelTitle);
4531     strcat(newTitle, "'");
4532     newLine =
4533        addLine(new, config->cfi, LT_ECHO,
4534        tmplLine->indent, newTitle);
4535     free(newTitle);
4536     } else {
4537     /* pass through other lines from the
4538     * template */
4539     newLine =
4540        addLineTmpl(new, tmplLine, newLine,
4541     NULL, config->cfi);
4542     }
4543     } else if (tmplLine->type == LT_DEVTREE &&
4544       tmplLine->numElements == 2
4545       && newDevTreePath) {
4546     newLine =
4547        addLineTmpl(new, tmplLine, newLine,
4548     newDevTreePath + strlen(prefix),
4549     config->cfi);
4550     needs &= ~NEED_DEVTREE;
4551     } else if (tmplLine->type == LT_ENTRY_END
4552       && needs & NEED_DEVTREE) {
4553     const char *ndtp = newDevTreePath;
4554     if (!strncmp
4555        (newDevTreePath, prefix, strlen(prefix)))
4556     ndtp += strlen(prefix);
4557     newLine = addLine(new, config->cfi, LT_DEVTREE,
4558      config->secondaryIndent,
4559      ndtp);
4560     needs &= ~NEED_DEVTREE;
4561     newLine =
4562        addLineTmpl(new, tmplLine, newLine, NULL,
4563     config->cfi);
4564     } else {
4565     /* pass through other lines from the template */
4566     newLine =
4567        addLineTmpl(new, tmplLine, newLine, NULL,
4568     config->cfi);
4569     }
4570     }
4571    
4572     } else {
4573     /* don't have a template, so start the entry with the
4574     * appropriate starting line
4575     */
4576     switch (config->cfi->entryStart) {
4577     case LT_KERNEL:
4578     case LT_KERNEL_EFI:
4579     case LT_KERNEL_16:
4580     if (new->multiboot && config->cfi->mbHyperFirst) {
4581     /* fall through to LT_HYPER */
4582     } else {
4583     newLine = addLine(new, config->cfi,
4584      preferredLineType(LT_KERNEL,
4585        config->
4586        cfi),
4587      config->primaryIndent,
4588      newKernelPath +
4589      strlen(prefix));
4590     needs &= ~NEED_KERNEL;
4591     break;
4592     }
4593    
4594     case LT_HYPER:
4595     newLine = addLine(new, config->cfi, LT_HYPER,
4596      config->primaryIndent,
4597      newMBKernel + strlen(prefix));
4598     needs &= ~NEED_MB;
4599     break;
4600    
4601     case LT_MENUENTRY:{
4602     char *nkt = malloc(strlen(newKernelTitle) + 3);
4603     strcpy(nkt, "'");
4604     strcat(nkt, newKernelTitle);
4605     strcat(nkt, "'");
4606     newLine =
4607        addLine(new, config->cfi, LT_MENUENTRY,
4608        config->primaryIndent, nkt);
4609     free(nkt);
4610     needs &= ~NEED_TITLE;
4611     needs |= NEED_END;
4612     break;
4613     }
4614     case LT_TITLE:
4615     if (useextlinuxmenu != 0) { // We just need useextlinuxmenu to not be zero (set above)
4616     char *templabel;
4617     int x = 0, y = 0;
4618    
4619     templabel = strdup(newKernelTitle);
4620     while (templabel[x]) {
4621     if (templabel[x] == ' ') {
4622     y = x;
4623     while (templabel[y]) {
4624     templabel[y] =
4625        templabel[y + 1];
4626     y++;
4627     }
4628     }
4629     x++;
4630     }
4631     newLine = addLine(new, config->cfi, LT_TITLE,
4632      config->primaryIndent,
4633      templabel);
4634     free(templabel);
4635     } else {
4636     newLine = addLine(new, config->cfi, LT_TITLE,
4637      config->primaryIndent,
4638      newKernelTitle);
4639     }
4640     needs &= ~NEED_TITLE;
4641     break;
4642    
4643     default:
4644     abort();
4645     }
4646     }
4647    
4648     struct singleLine *endLine = NULL;
4649     endLine = getLineByType(LT_ENTRY_END, new->lines);
4650     if (endLine) {
4651     removeLine(new, endLine);
4652     needs |= NEED_END;
4653     }
4654    
4655     /* add the remainder of the lines, i.e. those that either
4656     * weren't present in the template, or in the case of no template,
4657     * all the lines following the entryStart.
4658     */
4659     if (needs & NEED_TITLE) {
4660     newLine = addLine(new, config->cfi, LT_TITLE,
4661      config->secondaryIndent, newKernelTitle);
4662     needs &= ~NEED_TITLE;
4663     }
4664     if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
4665     newLine = addLine(new, config->cfi, LT_HYPER,
4666      config->secondaryIndent,
4667      newMBKernel + strlen(prefix));
4668     needs &= ~NEED_MB;
4669     }
4670     if (needs & NEED_KERNEL) {
4671     newLine = addLine(new, config->cfi,
4672      (new->multiboot
4673       && getKeywordByType(LT_MBMODULE,
4674           config->cfi))
4675      ? LT_MBMODULE : preferredLineType(LT_KERNEL,
4676        config->
4677        cfi),
4678      config->secondaryIndent,
4679      newKernelPath + strlen(prefix));
4680     needs &= ~NEED_KERNEL;
4681     }
4682     if (needs & NEED_MB) {
4683     newLine = addLine(new, config->cfi, LT_HYPER,
4684      config->secondaryIndent,
4685      newMBKernel + strlen(prefix));
4686     needs &= ~NEED_MB;
4687     }
4688     if (needs & NEED_INITRD) {
4689     char *initrdVal;
4690     initrdVal =
4691        getInitrdVal(config, prefix, NULL, newKernelInitrd,
4692     extraInitrds, extraInitrdCount);
4693     newLine =
4694        addLine(new, config->cfi,
4695        (new->multiboot
4696         && getKeywordByType(LT_MBMODULE, config->cfi))
4697        ? LT_MBMODULE : preferredLineType(LT_INITRD,
4698          config->cfi),
4699        config->secondaryIndent, initrdVal);
4700     free(initrdVal);
4701     needs &= ~NEED_INITRD;
4702     }
4703     if (needs & NEED_DEVTREE) {
4704     newLine = addLine(new, config->cfi, LT_DEVTREE,
4705      config->secondaryIndent, newDevTreePath);
4706     needs &= ~NEED_DEVTREE;
4707     }
4708    
4709     /* NEEDS_END must be last on bootloaders that need it... */
4710     if (needs & NEED_END) {
4711     newLine = addLine(new, config->cfi, LT_ENTRY_END,
4712      config->secondaryIndent, NULL);
4713     needs &= ~NEED_END;
4714     }
4715    
4716     if (needs) {
4717     printf(_("grubby: needs=%d, aborting\n"), needs);
4718     abort();
4719     }
4720    
4721     if (updateImage(config, "0", prefix, newKernelArgs, NULL,
4722     newMBKernelArgs, NULL))
4723     return 1;
4724    
4725   return 0;   return 0;
4726      } else if (kernelInfo)  }
  return displayInfo(config, kernelInfo, bootPrefix);  
4727    
4728      if (copyDefault) {  int main(int argc, const char **argv)
4729   template = findTemplate(config, bootPrefix, NULL, 0, flags);  {
4730   if (!template) return 1;   poptContext optCon;
4731      }   const char *grubConfig = NULL;
4732     char *outputFile = NULL;
4733     int arg = 0;
4734     int flags = 0;
4735     int badImageOkay = 0;
4736     int configureGrub2 = 0;
4737     int configureLilo = 0, configureELilo = 0, configureGrub = 0;
4738     int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
4739     int configureExtLinux = 0;
4740     int bootloaderProbe = 0;
4741     int extraInitrdCount = 0;
4742     char *updateKernelPath = NULL;
4743     char *newKernelPath = NULL;
4744     char *removeKernelPath = NULL;
4745     char *newKernelArgs = NULL;
4746     char *newKernelInitrd = NULL;
4747     char *newKernelTitle = NULL;
4748     char *newDevTreePath = NULL;
4749     char *newMBKernel = NULL;
4750     char *newMBKernelArgs = NULL;
4751     int newIndex = 0;
4752     char *removeMBKernelArgs = NULL;
4753     char *removeMBKernel = NULL;
4754     char *bootPrefix = NULL;
4755     char *defaultKernel = NULL;
4756     char *removeArgs = NULL;
4757     char *kernelInfo = NULL;
4758     char *extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
4759     char *envPath = NULL;
4760     const char *chptr = NULL;
4761     struct configFileInfo *cfi = NULL;
4762     struct grubConfig *config;
4763     struct singleEntry *template = NULL;
4764     int copyDefault = 0, makeDefault = 0;
4765     int displayDefault = 0;
4766     int displayDefaultIndex = 0;
4767     int displayDefaultTitle = 0;
4768     int defaultIndex = -1;
4769     struct poptOption options[] = {
4770     {"add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
4771     _("add an entry for the specified kernel"), _("kernel-path")},
4772     {"add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
4773     _("add an entry for the specified multiboot kernel"), NULL},
4774     {"args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
4775     _("default arguments for the new kernel or new arguments for "
4776       "kernel being updated"), _("args")},
4777     {"mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
4778     _("default arguments for the new multiboot kernel or "
4779       "new arguments for multiboot kernel being updated"), NULL},
4780     {"bad-image-okay", 0, 0, &badImageOkay, 0,
4781     _
4782     ("don't sanity check images in boot entries (for testing only)"),
4783     NULL},
4784     {"boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
4785     _
4786     ("filestystem which contains /boot directory (for testing only)"),
4787     _("bootfs")},
4788    #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
4789     {"bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
4790     _("check which bootloader is installed on boot sector")},
4791    #endif
4792     {"config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
4793     _("path to grub config file to update (\"-\" for stdin)"),
4794     _("path")},
4795     {"copy-default", 0, 0, &copyDefault, 0,
4796     _("use the default boot entry as a template for the new entry "
4797       "being added; if the default is not a linux image, or if "
4798       "the kernel referenced by the default image does not exist, "
4799       "the first linux entry whose kernel does exist is used as the "
4800       "template"), NULL},
4801     {"debug", 0, 0, &debug, 0,
4802     _("print debugging information for failures")},
4803     {"default-kernel", 0, 0, &displayDefault, 0,
4804     _("display the path of the default kernel")},
4805     {"default-index", 0, 0, &displayDefaultIndex, 0,
4806     _("display the index of the default kernel")},
4807     {"default-title", 0, 0, &displayDefaultTitle, 0,
4808     _("display the title of the default kernel")},
4809     {"devtree", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4810     _("device tree file for new stanza"), _("dtb-path")},
4811     {"devtreedir", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4812     _("device tree directory for new stanza"), _("dtb-path")},
4813     {"elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
4814     _("configure elilo bootloader")},
4815     {"efi", 0, POPT_ARG_NONE, &isEfi, 0,
4816     _("force grub2 stanzas to use efi")},
4817     {"env", 0, POPT_ARG_STRING, &envPath, 0,
4818     _("path for environment data"),
4819     _("path")},
4820     {"extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
4821     _("configure extlinux bootloader (from syslinux)")},
4822     {"grub", 0, POPT_ARG_NONE, &configureGrub, 0,
4823     _("configure grub bootloader")},
4824     {"grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
4825     _("configure grub2 bootloader")},
4826     {"info", 0, POPT_ARG_STRING, &kernelInfo, 0,
4827     _("display boot information for specified kernel"),
4828     _("kernel-path")},
4829     {"initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
4830     _("initrd image for the new kernel"), _("initrd-path")},
4831     {"extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
4832     _
4833     ("auxiliary initrd image for things other than the new kernel"),
4834     _("initrd-path")},
4835     {"lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
4836     _("configure lilo bootloader")},
4837     {"make-default", 0, 0, &makeDefault, 0,
4838     _("make the newly added entry the default boot entry"), NULL},
4839     {"output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
4840     _("path to output updated config file (\"-\" for stdout)"),
4841     _("path")},
4842     {"remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
4843     _("remove kernel arguments"), NULL},
4844     {"remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
4845     _("remove multiboot kernel arguments"), NULL},
4846     {"remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
4847     _("remove all entries for the specified kernel"),
4848     _("kernel-path")},
4849     {"remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
4850     _("remove all entries for the specified multiboot kernel"),
4851     NULL},
4852     {"set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
4853     _("make the first entry referencing the specified kernel "
4854       "the default"), _("kernel-path")},
4855     {"set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
4856     _("make the given entry index the default entry"),
4857     _("entry-index")},
4858     {"set-index", 0, POPT_ARG_INT, &newIndex, 0,
4859     _("use the given index when creating a new entry"),
4860     _("entry-index")},
4861     {"silo", 0, POPT_ARG_NONE, &configureSilo, 0,
4862     _("configure silo bootloader")},
4863     {"title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
4864     _("title to use for the new kernel entry"), _("entry-title")},
4865     {"update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
4866     _("updated information for the specified kernel"),
4867     _("kernel-path")},
4868     {"version", 'v', 0, NULL, 'v',
4869     _("print the version of this program and exit"), NULL},
4870     {"yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
4871     _("configure yaboot bootloader")},
4872     {"zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
4873     _("configure zipl bootloader")},
4874     POPT_AUTOHELP {0, 0, 0, 0, 0}
4875     };
4876    
4877     useextlinuxmenu = 0;
4878    
4879     int i = 0;
4880     for (int j = 1; j < argc; j++)
4881     i += strlen(argv[j]) + 1;
4882     saved_command_line = malloc(i);
4883     if (!saved_command_line) {
4884     fprintf(stderr, "grubby: %m\n");
4885     exit(1);
4886     }
4887     saved_command_line[0] = '\0';
4888     for (int j = 1; j < argc; j++) {
4889     strcat(saved_command_line, argv[j]);
4890     strncat(saved_command_line, j == argc - 1 ? "" : " ", 1);
4891     }
4892    
4893     optCon = poptGetContext("grubby", argc, argv, options, 0);
4894     poptReadDefaultConfig(optCon, 1);
4895    
4896     while ((arg = poptGetNextOpt(optCon)) >= 0) {
4897     switch (arg) {
4898     case 'v':
4899     printf("grubby version %s\n", VERSION);
4900     exit(0);
4901     break;
4902     case 'i':
4903     if (extraInitrdCount < MAX_EXTRA_INITRDS) {
4904     extraInitrds[extraInitrdCount++] =
4905        strdup(poptGetOptArg(optCon));
4906     } else {
4907     fprintf(stderr,
4908     _
4909     ("grubby: extra initrd maximum is %d\n"),
4910     extraInitrdCount);
4911     return 1;
4912     }
4913     break;
4914     }
4915     }
4916    
4917      markRemovedImage(config, removeKernelPath, bootPrefix);   if (arg < -1) {
4918      markRemovedImage(config, removeMBKernel, bootPrefix);   fprintf(stderr, _("grubby: bad argument %s: %s\n"),
4919      setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,   poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
4920      bootPrefix, flags);   poptStrerror(arg));
4921      setFallbackImage(config, newKernelPath != NULL);   return 1;
4922      if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,   }
4923                      removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;  
4924      if (updateKernelPath && newKernelInitrd) {   if ((chptr = poptGetArg(optCon))) {
4925              if (updateInitrd(config, updateKernelPath, bootPrefix,   fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
4926                               newKernelInitrd)) return 1;   return 1;
4927      }   }
4928      if (addNewKernel(config, template, bootPrefix, newKernelPath,  
4929                       newKernelTitle, newKernelArgs, newKernelInitrd,   if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
4930                       extraInitrds, extraInitrdCount,       configureYaboot + configureSilo + configureZipl +
4931                       newMBKernel, newMBKernelArgs)) return 1;       configureExtLinux) > 1) {
4932         fprintf(stderr,
4933     _("grubby: cannot specify multiple bootloaders\n"));
4934      if (numEntries(config) == 0) {   return 1;
4935          fprintf(stderr, _("grubby: doing this would leave no kernel entries. "   } else if (bootloaderProbe && grubConfig) {
4936                            "Not writing out new config.\n"));   fprintf(stderr,
4937          return 1;   _
4938      }   ("grubby: cannot specify config file with --bootloader-probe\n"));
4939     return 1;
4940     } else if (configureGrub2) {
4941     cfi = &grub2ConfigType;
4942     if (envPath)
4943     cfi->envFile = envPath;
4944     } else if (configureLilo) {
4945     cfi = &liloConfigType;
4946     } else if (configureGrub) {
4947     cfi = &grubConfigType;
4948     } else if (configureELilo) {
4949     cfi = &eliloConfigType;
4950     } else if (configureYaboot) {
4951     cfi = &yabootConfigType;
4952     } else if (configureSilo) {
4953     cfi = &siloConfigType;
4954     } else if (configureZipl) {
4955     cfi = &ziplConfigType;
4956     } else if (configureExtLinux) {
4957     cfi = &extlinuxConfigType;
4958     useextlinuxmenu = 1;
4959     }
4960    
4961     if (!cfi) {
4962     if (grub2FindConfig(&grub2ConfigType)) {
4963     cfi = &grub2ConfigType;
4964     if (envPath)
4965     cfi->envFile = envPath;
4966     } else
4967    #ifdef __ia64__
4968     cfi = &eliloConfigType;
4969    #elif __powerpc__
4970     cfi = &yabootConfigType;
4971    #elif __sparc__
4972     cfi = &siloConfigType;
4973    #elif __s390__
4974     cfi = &ziplConfigType;
4975    #elif __s390x__
4976     cfi = &ziplConfigtype;
4977    #else
4978     cfi = &grubConfigType;
4979    #endif
4980     }
4981    
4982     if (!grubConfig) {
4983     if (cfi->findConfig)
4984     grubConfig = cfi->findConfig(cfi);
4985     if (!grubConfig)
4986     grubConfig = cfi->defaultConfig;
4987     }
4988    
4989     if (bootloaderProbe && (displayDefault || kernelInfo ||
4990     newKernelPath || removeKernelPath || makeDefault
4991     || defaultKernel || displayDefaultIndex
4992     || displayDefaultTitle
4993     || (defaultIndex >= 0))) {
4994     fprintf(stderr,
4995     _("grubby: --bootloader-probe may not be used with "
4996      "specified option"));
4997     return 1;
4998     }
4999    
5000     if ((displayDefault || kernelInfo) && (newKernelPath ||
5001           removeKernelPath)) {
5002     fprintf(stderr, _("grubby: --default-kernel and --info may not "
5003      "be used when adding or removing kernels\n"));
5004     return 1;
5005     }
5006    
5007     if (newKernelPath && !newKernelTitle) {
5008     fprintf(stderr, _("grubby: kernel title must be specified\n"));
5009     return 1;
5010     } else if (!newKernelPath && (copyDefault ||
5011          (newKernelInitrd && !updateKernelPath) ||
5012          makeDefault || extraInitrdCount > 0)) {
5013     fprintf(stderr, _("grubby: kernel path expected\n"));
5014     return 1;
5015     }
5016    
5017     if (newKernelPath && updateKernelPath) {
5018     fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
5019      "not be used together"));
5020     return 1;
5021     }
5022    
5023     if (makeDefault && defaultKernel) {
5024     fprintf(stderr, _("grubby: --make-default and --default-kernel "
5025      "may not be used together\n"));
5026     return 1;
5027     } else if (defaultKernel && removeKernelPath &&
5028       !strcmp(defaultKernel, removeKernelPath)) {
5029     fprintf(stderr,
5030     _("grubby: cannot make removed kernel the default\n"));
5031     return 1;
5032     } else if (defaultKernel && newKernelPath &&
5033       !strcmp(defaultKernel, newKernelPath)) {
5034     makeDefault = 1;
5035     defaultKernel = NULL;
5036     } else if (defaultKernel && (defaultIndex >= 0)) {
5037     fprintf(stderr,
5038     _("grubby: --set-default and --set-default-index "
5039      "may not be used together\n"));
5040     return 1;
5041     }
5042    
5043     if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
5044     fprintf(stderr,
5045     _("grubby: output file must be specified if stdin "
5046      "is used\n"));
5047     return 1;
5048     }
5049    
5050     if (!removeKernelPath && !newKernelPath && !displayDefault
5051        && !defaultKernel && !kernelInfo && !bootloaderProbe
5052        && !updateKernelPath && !removeMBKernel && !displayDefaultIndex
5053        && !displayDefaultTitle && (defaultIndex == -1)) {
5054     fprintf(stderr, _("grubby: no action specified\n"));
5055     return 1;
5056     }
5057    
5058     flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
5059    
5060     if (cfi->needsBootPrefix) {
5061     if (!bootPrefix) {
5062     bootPrefix = findBootPrefix();
5063     if (!bootPrefix)
5064     return 1;
5065     } else {
5066     /* this shouldn't end with a / */
5067     if (bootPrefix[strlen(bootPrefix) - 1] == '/')
5068     bootPrefix[strlen(bootPrefix) - 1] = '\0';
5069     }
5070     } else {
5071     bootPrefix = "";
5072     }
5073    
5074     if (!cfi->mbAllowExtraInitRds && extraInitrdCount > 0) {
5075     fprintf(stderr,
5076     _("grubby: %s doesn't allow multiple initrds\n"),
5077     cfi->defaultConfig);
5078     return 1;
5079     }
5080    
5081     if (bootloaderProbe) {
5082     int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
5083     struct grubConfig *lconfig, *gconfig, *yconfig, *econfig;
5084    
5085     const char *grub2config = grub2FindConfig(&grub2ConfigType);
5086     if (grub2config) {
5087     gconfig = readConfig(grub2config, &grub2ConfigType);
5088     if (!gconfig)
5089     gr2c = 1;
5090     else
5091     gr2c = checkForGrub2(gconfig);
5092     }
5093    
5094     const char *grubconfig = grubFindConfig(&grubConfigType);
5095     if (!access(grubconfig, F_OK)) {
5096     gconfig = readConfig(grubconfig, &grubConfigType);
5097     if (!gconfig)
5098     grc = 1;
5099     else
5100     grc = checkForGrub(gconfig);
5101     }
5102    
5103     if (!access(liloConfigType.defaultConfig, F_OK)) {
5104     lconfig =
5105        readConfig(liloConfigType.defaultConfig,
5106           &liloConfigType);
5107     if (!lconfig)
5108     lrc = 1;
5109     else
5110     lrc = checkForLilo(lconfig);
5111     }
5112    
5113     if (!access(eliloConfigType.defaultConfig, F_OK)) {
5114     econfig = readConfig(eliloConfigType.defaultConfig,
5115         &eliloConfigType);
5116     if (!econfig)
5117     erc = 1;
5118     else
5119     erc = checkForElilo(econfig);
5120     }
5121    
5122     if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
5123     lconfig =
5124        readConfig(extlinuxConfigType.defaultConfig,
5125           &extlinuxConfigType);
5126     if (!lconfig)
5127     extrc = 1;
5128     else
5129     extrc = checkForExtLinux(lconfig);
5130     }
5131    
5132     if (!access(yabootConfigType.defaultConfig, F_OK)) {
5133     yconfig = readConfig(yabootConfigType.defaultConfig,
5134         &yabootConfigType);
5135     if (!yconfig)
5136     yrc = 1;
5137     else
5138     yrc = checkForYaboot(yconfig);
5139     }
5140    
5141     if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1
5142        || erc == 1)
5143     return 1;
5144    
5145     if (lrc == 2)
5146     printf("lilo\n");
5147     if (gr2c == 2)
5148     printf("grub2\n");
5149     if (grc == 2)
5150     printf("grub\n");
5151     if (extrc == 2)
5152     printf("extlinux\n");
5153     if (yrc == 2)
5154     printf("yaboot\n");
5155     if (erc == 2)
5156     printf("elilo\n");
5157    
5158     return 0;
5159     }
5160    
5161     if (grubConfig == NULL) {
5162     printf("Could not find bootloader configuration file.\n");
5163     exit(1);
5164     }
5165    
5166     config = readConfig(grubConfig, cfi);
5167     if (!config)
5168     return 1;
5169    
5170     if (displayDefault) {
5171     struct singleLine *line;
5172     struct singleEntry *entry;
5173     char *rootspec;
5174    
5175     if (config->defaultImage == -1)
5176     return 0;
5177     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5178        cfi->defaultIsSaved)
5179     config->defaultImage = 0;
5180     entry = findEntryByIndex(config, config->defaultImage);
5181     if (!entry)
5182     return 0;
5183     if (!suitableImage(entry, bootPrefix, 0, flags))
5184     return 0;
5185    
5186     line =
5187        getLineByType(LT_KERNEL | LT_HYPER | LT_KERNEL_EFI |
5188      LT_KERNEL_16, entry->lines);
5189     if (!line)
5190     return 0;
5191    
5192     rootspec = getRootSpecifier(line->elements[1].item);
5193     printf("%s%s\n", bootPrefix, line->elements[1].item +
5194           ((rootspec != NULL) ? strlen(rootspec) : 0));
5195    
5196     return 0;
5197    
5198     } else if (displayDefaultTitle) {
5199     struct singleLine *line;
5200     struct singleEntry *entry;
5201    
5202     if (config->defaultImage == -1)
5203     return 0;
5204     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5205        cfi->defaultIsSaved)
5206     config->defaultImage = 0;
5207     entry = findEntryByIndex(config, config->defaultImage);
5208     if (!entry)
5209     return 0;
5210    
5211     if (!configureGrub2) {
5212     char *title;
5213     line = getLineByType(LT_TITLE, entry->lines);
5214     if (!line)
5215     return 0;
5216     title = extractTitle(config, line);
5217     if (!title)
5218     return 0;
5219     printf("%s\n", title);
5220     free(title);
5221     } else {
5222     char *title;
5223    
5224     dbgPrintf
5225        ("This is GRUB2, default title is embeded in menuentry\n");
5226     line = getLineByType(LT_MENUENTRY, entry->lines);
5227     if (!line)
5228     return 0;
5229     title = grub2ExtractTitle(line);
5230     if (title)
5231     printf("%s\n", title);
5232     }
5233     return 0;
5234    
5235     } else if (displayDefaultIndex) {
5236     if (config->defaultImage == -1)
5237     return 0;
5238     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5239        cfi->defaultIsSaved)
5240     config->defaultImage = 0;
5241     printf("%i\n", config->defaultImage);
5242     return 0;
5243    
5244     } else if (kernelInfo)
5245     return displayInfo(config, kernelInfo, bootPrefix);
5246    
5247     if (copyDefault) {
5248     template = findTemplate(config, bootPrefix, NULL, 0, flags);
5249     if (!template)
5250     return 1;
5251     }
5252    
5253     markRemovedImage(config, removeKernelPath, bootPrefix);
5254     markRemovedImage(config, removeMBKernel, bootPrefix);
5255     setDefaultImage(config, newKernelPath != NULL, defaultKernel,
5256     makeDefault, bootPrefix, flags, defaultIndex);
5257     setFallbackImage(config, newKernelPath != NULL);
5258     if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
5259     removeArgs, newMBKernelArgs, removeMBKernelArgs))
5260     return 1;
5261     if (updateKernelPath && newKernelInitrd) {
5262     if (newMBKernel) {
5263     if (addMBInitrd(config, newMBKernel, updateKernelPath,
5264     bootPrefix, newKernelInitrd,
5265     newKernelTitle))
5266     return 1;
5267     } else {
5268     if (updateInitrd(config, updateKernelPath, bootPrefix,
5269     newKernelInitrd, newKernelTitle))
5270     return 1;
5271     }
5272     }
5273     if (addNewKernel(config, template, bootPrefix, newKernelPath,
5274     newKernelTitle, newKernelArgs, newKernelInitrd,
5275     (const char **)extraInitrds, extraInitrdCount,
5276     newMBKernel, newMBKernelArgs, newDevTreePath,
5277     newIndex))
5278     return 1;
5279    
5280     if (numEntries(config) == 0) {
5281     fprintf(stderr,
5282     _("grubby: doing this would leave no kernel entries. "
5283      "Not writing out new config.\n"));
5284     return 1;
5285     }
5286    
5287      if (!outputFile)   if (!outputFile)
5288   outputFile = grubConfig;   outputFile = (char *)grubConfig;
5289    
5290      return writeConfig(config, outputFile, bootPrefix);   return writeConfig(config, outputFile, bootPrefix);
5291  }  }

Legend:
Removed from v.1693  
changed lines
  Added in v.3007