Magellan Linux

Diff of /trunk/grubby/grubby.c

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

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

Legend:
Removed from v.1694  
changed lines
  Added in v.3002