Magellan Linux

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

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

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

Legend:
Removed from v.1801  
changed lines
  Added in v.3010