Magellan Linux

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

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

revision 2992 by niro, Thu Jun 30 10:35:14 2016 UTC revision 3003 by niro, Tue Jun 27 14:14:00 2017 UTC
# Line 48  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 70  char *saved_command_line = NULL; Line 70  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_KERNEL_EFI   = 1 << 20,   LT_SET_VARIABLE = 1 << 19,
98      LT_INITRD_EFI   = 1 << 21,   LT_KERNEL_EFI = 1 << 20,
99      LT_KERNEL_16    = 1 << 22,   LT_INITRD_EFI = 1 << 21,
100      LT_INITRD_16    = 1 << 23,   LT_KERNEL_16 = 1 << 22,
101      LT_DEVTREE      = 1 << 24,   LT_INITRD_16 = 1 << 23,
102      LT_UNKNOWN      = 1 << 25,   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 135  struct singleEntry { Line 136  struct singleEntry {
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);  typedef char *(*getEnvFunc) (struct configFileInfo *, char *name);
151  typedef int (*setEnvFunc)(struct configFileInfo *, char *name, char *value);  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      getEnvFunc getEnv;   getEnvFunc getEnv;
158      setEnvFunc setEnv;   setEnvFunc setEnv;
159      struct keywordTypes * keywords;   struct keywordTypes *keywords;
160      int caseInsensitive;   int caseInsensitive;
161      int defaultIsIndex;   int defaultIsIndex;
162      int defaultIsVariable;   int defaultIsVariable;
163      int defaultSupportSaved;   int defaultSupportSaved;
164      int defaultIsSaved;   int defaultIsSaved;
165      int defaultIsUnquoted;   int defaultIsUnquoted;
166      enum lineType_e entryStart;   enum lineType_e entryStart;
167      enum lineType_e entryEnd;   enum lineType_e entryEnd;
168      int needsBootPrefix;   int needsBootPrefix;
169      int argsInQuotes;   int argsInQuotes;
170      int maxTitleLength;   int maxTitleLength;
171      int titleBracketed;   int titleBracketed;
172      int titlePosition;   int titlePosition;
173      int mbHyperFirst;   int mbHyperFirst;
174      int mbInitRdIsModule;   int mbInitRdIsModule;
175      int mbConcatArgs;   int mbConcatArgs;
176      int mbAllowExtraInitRds;   int mbAllowExtraInitRds;
177      char *envFile;   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   "/boot/grub/grub.conf",   static const char *configFiles[] = {
195   "/boot/grub/menu.lst",   "/boot/grub/grub.conf",
196   "/etc/grub.conf",   "/boot/grub/menu.lst",
197   "/boot/grub2/grub.cfg",   "/etc/grub.conf",
198   "/boot/grub2-efi/grub.cfg",   NULL
199   NULL   };
200      };   static int i = -1;
201      static int i = -1;  
202     if (i == -1) {
203      if (i == -1) {   for (i = 0; configFiles[i] != NULL; i++) {
204   for (i = 0; configFiles[i] != NULL; i++) {   dbgPrintf("Checking \"%s\": ", configFiles[i]);
205      dbgPrintf("Checking \"%s\": ", configFiles[i]);   if (!access(configFiles[i], R_OK)) {
206      if (!access(configFiles[i], R_OK)) {   dbgPrintf("found\n");
207   dbgPrintf("found\n");   return configFiles[i];
208   return configFiles[i];   }
209      }   dbgPrintf("not found\n");
210      dbgPrintf("not found\n");   }
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,   .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      { "linuxefi",   LT_KERNEL_EFI,  ' ' },   {"linuxefi", LT_KERNEL_EFI, ' '},
238      { "linux16",    LT_KERNEL_16,   ' ' },   {"linux16", LT_KERNEL_16, ' '},
239      { "initrd",     LT_INITRD,      ' ', ' ' },   {"initrd", LT_INITRD, ' ', ' '},
240      { "initrdefi",  LT_INITRD_EFI,  ' ', ' ' },   {"initrdefi", LT_INITRD_EFI, ' ', ' '},
241      { "initrd16",   LT_INITRD_16,   ' ', ' ' },   {"initrd16", LT_INITRD_16, ' ', ' '},
242      { "module",     LT_MBMODULE,    ' ' },   {"module", LT_MBMODULE, ' '},
243      { "kernel",     LT_HYPER,       ' ' },   {"kernel", LT_HYPER, ' '},
244      { "devicetree", LT_DEVTREE,  ' ' },   {"devicetree", LT_DEVTREE, ' '},
245      { NULL, 0, 0 },   {NULL, 0, 0},
246  };  };
247    
248  const char *grub2FindConfig(struct configFileInfo *cfi) {  const char *grub2FindConfig(struct configFileInfo *cfi)
249      static const char *configFiles[] = {  {
250   "/boot/grub/grub-efi.cfg",   static const char *configFiles[] = {
251   "/boot/grub/grub.cfg",   "/boot/grub/grub-efi.cfg",
252   NULL   "/boot/grub/grub.cfg",
253      };   "/etc/grub2-efi.cfg",
254      static int i = -1;   "/etc/grub2.cfg",
255      static const char *grub_cfg = "/boot/grub/grub.cfg";   "/boot/grub2/grub.cfg",
256      int rc = -1;   "/boot/grub2-efi/grub.cfg",
257     NULL
258      if (i == -1) {   };
259   for (i = 0; configFiles[i] != NULL; i++) {   static int i = -1;
260      dbgPrintf("Checking \"%s\": ", configFiles[i]);   static const char *grub_cfg = "/boot/grub/grub.cfg";
261      if ((rc = access(configFiles[i], R_OK))) {   int rc = -1;
262   if (errno == EACCES) {  
263      printf("Unable to access bootloader configuration file "   if (i == -1) {
264         "\"%s\": %m\n", configFiles[i]);   for (i = 0; configFiles[i] != NULL; i++) {
265      exit(1);   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   }   }
  continue;  
     } else {  
  dbgPrintf("found\n");  
  return configFiles[i];  
     }  
279   }   }
     }  
280    
281      /* Ubuntu renames grub2 to grub, so check for the grub.d directory   /* 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   * that isn't in grub1, and if it exists, return the config file path
283       * that they use. */   * that they use. */
284      if (configFiles[i] == NULL && !access("/etc/grub.d/", R_OK)) {   if (configFiles[i] == NULL && !access("/etc/grub.d/", R_OK)) {
285   dbgPrintf("found\n");   dbgPrintf("found\n");
286   return grub_cfg;   return grub_cfg;
287      }   }
288    
289      dbgPrintf("not found\n");   dbgPrintf("not found\n");
290      return configFiles[i];   return configFiles[i];
291  }  }
292    
293  /* kind of hacky.  It'll give the first 1024 bytes, ish. */  /* kind of hacky.  It'll give the first 1024 bytes, ish. */
294  static char *grub2GetEnv(struct configFileInfo *info, char *name)  static char *grub2GetEnv(struct configFileInfo *info, char *name)
295  {  {
296      static char buf[1025];   static char buf[1025];
297      char *s = NULL;   char *s = NULL;
298      char *ret = NULL;   char *ret = NULL;
299      char *envFile = info->envFile ? info->envFile : "/boot/grub/grubenv";   char *envFile = info->envFile ? info->envFile : "/boot/grub/grubenv";
300      int rc = asprintf(&s, "grub-editenv %s list | grep '^%s='", envFile, name);   int rc =
301        asprintf(&s, "grub-editenv %s list | grep '^%s='", envFile, name);
302      if (rc < 0)  
303   return NULL;   if (rc < 0)
304     return NULL;
305      FILE *f = popen(s, "r");  
306      if (!f)   FILE *f = popen(s, "r");
307   goto out;   if (!f)
308     goto out;
309      memset(buf, '\0', sizeof (buf));  
310      ret = fgets(buf, 1024, f);   memset(buf, '\0', sizeof(buf));
311      pclose(f);   ret = fgets(buf, 1024, f);
312     pclose(f);
313      if (ret) {  
314   ret += strlen(name) + 1;   if (ret) {
315   ret[strlen(ret) - 1] = '\0';   ret += strlen(name) + 1;
316      }   ret[strlen(ret) - 1] = '\0';
317      dbgPrintf("grub2GetEnv(%s): %s\n", name, ret);   }
318     dbgPrintf("grub2GetEnv(%s): %s\n", name, ret);
319  out:  out:
320      free(s);   free(s);
321      return ret;   return ret;
322  }  }
323    
324  static int sPopCount(const char *s, const char *c)  static int sPopCount(const char *s, const char *c)
325  {  {
326      int ret = 0;   int ret = 0;
327      if (!s)   if (!s)
328   return -1;   return -1;
329      for (int i = 0; s[i] != '\0'; i++)   for (int i = 0; s[i] != '\0'; i++)
330   for (int j = 0; c[j] != '\0'; j++)   for (int j = 0; c[j] != '\0'; j++)
331      if (s[i] == c[j])   if (s[i] == c[j])
332   ret++;   ret++;
333      return ret;   return ret;
334  }  }
335    
336  static char *shellEscape(const char *s)  static char *shellEscape(const char *s)
337  {  {
338      int l = strlen(s) + sPopCount(s, "'") * 2;   int l = strlen(s) + sPopCount(s, "'") * 2;
339    
340      char *ret = calloc(l+1, sizeof (*ret));   char *ret = calloc(l + 1, sizeof(*ret));
341      if (!ret)   if (!ret)
342   return NULL;   return NULL;
343      for (int i = 0, j = 0; s[i] != '\0'; i++, j++) {   for (int i = 0, j = 0; s[i] != '\0'; i++, j++) {
344   if (s[i] == '\'')   if (s[i] == '\'')
345      ret[j++] = '\\';   ret[j++] = '\\';
346   ret[j] = s[i];   ret[j] = s[i];
347      }   }
348      return ret;   return ret;
349  }  }
350    
351  static void unquote(char *s)  static void unquote(char *s)
352  {  {
353      int l = strlen(s);   int l = strlen(s);
354    
355      if ((s[l-1] == '\'' && s[0] == '\'') || (s[l-1] == '"' && s[0] == '"')) {   if ((s[l - 1] == '\'' && s[0] == '\'')
356   memmove(s, s+1, l-2);      || (s[l - 1] == '"' && s[0] == '"')) {
357   s[l-2] = '\0';   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)  static int grub2SetEnv(struct configFileInfo *info, char *name, char *value)
363  {  {
364      char *s = NULL;   char *s = NULL;
365      int rc = 0;   int rc = 0;
366      char *envFile = info->envFile ? info->envFile : "/boot/grub/grubenv";   char *envFile = info->envFile ? info->envFile : "/boot/grub/grubenv";
367    
368      unquote(value);   unquote(value);
369      value = shellEscape(value);   value = shellEscape(value);
370      if (!value)   if (!value)
371      return -1;   return -1;
372    
373      rc = asprintf(&s, "grub-editenv %s set '%s=%s'", envFile, name, value);   rc = asprintf(&s, "grub-editenv %s set '%s=%s'", envFile, name, value);
374      free(value);   free(value);
375      if (rc <0)   if (rc < 0)
376   return -1;   return -1;
377    
378      dbgPrintf("grub2SetEnv(%s): %s\n", name, s);   dbgPrintf("grub2SetEnv(%s): %s\n", name, s);
379      rc = system(s);   rc = system(s);
380      free(s);   free(s);
381      return rc;   return rc;
382  }  }
383    
384  /* this is a gigantic hack to avoid clobbering grub2 variables... */  /* this is a gigantic hack to avoid clobbering grub2 variables... */
385  static int is_special_grub2_variable(const char *name)  static int is_special_grub2_variable(const char *name)
386  {  {
387      if (!strcmp(name,"\"${next_entry}\""))   if (!strcmp(name, "\"${next_entry}\""))
388   return 1;   return 1;
389      if (!strcmp(name,"\"${prev_saved_entry}\""))   if (!strcmp(name, "\"${prev_saved_entry}\""))
390   return 1;   return 1;
391      return 0;   return 0;
392  }  }
393    
394  int sizeOfSingleLine(struct singleLine * line) {  int sizeOfSingleLine(struct singleLine *line)
395    int count = 0;  {
396     int count = 0;
397    
398    for (int 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  static int iskernel(enum lineType_e type) {  static int iskernel(enum lineType_e type)
425      return (type == LT_KERNEL || type == LT_KERNEL_EFI || type == LT_KERNEL_16);  {
426     return (type == LT_KERNEL || type == LT_KERNEL_EFI
427     || type == LT_KERNEL_16);
428  }  }
429    
430  static int isinitrd(enum lineType_e type) {  static int isinitrd(enum lineType_e type)
431      return (type == LT_INITRD || type == LT_INITRD_EFI || type == LT_INITRD_16);  {
432     return (type == LT_INITRD || type == LT_INITRD_EFI
433     || type == LT_INITRD_16);
434  }  }
435    
436  char *grub2ExtractTitle(struct singleLine * line) {  char *grub2ExtractTitle(struct singleLine *line)
437      char * current;  {
438      char * current_indent;   char *current;
439      int current_len;   char *current_indent;
440      int current_indent_len;   int current_len;
441      int i;   int current_indent_len;
442     int i;
443      /* bail out if line does not start with menuentry */  
444      if (strcmp(line->elements[0].item, "menuentry"))   /* bail out if line does not start with menuentry */
445   return NULL;   if (strcmp(line->elements[0].item, "menuentry"))
446     return NULL;
     i = 1;  
     current = line->elements[i].item;  
     current_len = strlen(current);  
   
     /* if second word is quoted, strip the quotes and return single word */  
     if (isquote(*current) && isquote(current[current_len - 1])) {  
  char *tmp;  
   
  tmp = strdup(current+1);  
  if (!tmp)  
     return NULL;  
  tmp[strlen(tmp)-1] = '\0';  
  return tmp;  
     }  
   
     /* if no quotes, return second word verbatim */  
     if (!isquote(*current))  
  return current;  
   
     /* second element start with a quote, so we have to find the element  
      * whose last character is also quote (assuming it's the closing one) */  
     int resultMaxSize;  
     char * result;  
     /* need to ensure that ' does not match " as we search */  
     char quote_char = *current;  
   
     resultMaxSize = sizeOfSingleLine(line);  
     result = malloc(resultMaxSize);  
     snprintf(result, resultMaxSize, "%s", ++current);  
447    
448      i++;   i = 1;
     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 (current[current_len-1] != quote_char) {   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      .getEnv = grub2GetEnv,   .getEnv = grub2GetEnv,
499      .setEnv = grub2SetEnv,   .setEnv = grub2SetEnv,
500      .keywords = grub2Keywords,   .keywords = grub2Keywords,
501      .defaultIsIndex = 1,   .defaultIsIndex = 1,
502      .defaultSupportSaved = 1,   .defaultSupportSaved = 1,
503      .defaultIsVariable = 1,   .defaultIsVariable = 1,
504      .entryStart = LT_MENUENTRY,   .entryStart = LT_MENUENTRY,
505      .entryEnd = LT_ENTRY_END,   .entryEnd = LT_ENTRY_END,
506      .titlePosition = 1,   .titlePosition = 1,
507      .needsBootPrefix = 1,   .needsBootPrefix = 1,
508      .mbHyperFirst = 1,   .mbHyperFirst = 1,
509      .mbInitRdIsModule = 1,   .mbInitRdIsModule = 1,
510      .mbAllowExtraInitRds = 1,   .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      { "fdt",        LT_DEVTREE,     ' ' },   {"fdt", LT_DEVTREE, ' '},
605      { "fdtdir",     LT_DEVTREE,     ' ' },   {"fdtdir", LT_DEVTREE, ' '},
606      { NULL,    0, 0 },   {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,   .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,   .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,   .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,   .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      .caseInsensitive = 1,   .caseInsensitive = 1,
662      .entryStart = LT_TITLE,   .entryStart = LT_TITLE,
663      .needsBootPrefix = 1,   .needsBootPrefix = 1,
664      .maxTitleLength = 255,   .maxTitleLength = 255,
665      .mbAllowExtraInitRds = 1,   .mbAllowExtraInitRds = 1,
666      .defaultIsUnquoted = 1,   .defaultIsUnquoted = 1,
667      .titlePosition = 1,   .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  struct singleEntry * findEntryByTitle(struct grubConfig * cfg, char *title,  struct singleEntry *findEntryByTitle(struct grubConfig *cfg, char *title,
689        int * index);       int *index);
690  static int readFile(int fd, char ** bufPtr);  static int readFile(int fd, char **bufPtr);
691  static void lineInit(struct singleLine * line);  static void lineInit(struct singleLine *line);
692  struct singleLine * lineDup(struct singleLine * line);  struct singleLine *lineDup(struct singleLine *line);
693  static void lineFree(struct singleLine * line);  static void lineFree(struct singleLine *line);
694  static int lineWrite(FILE * out, struct singleLine * line,  static int lineWrite(FILE * out, struct singleLine *line,
695       struct configFileInfo * cfi);       struct configFileInfo *cfi);
696  static int getNextLine(char ** bufPtr, struct singleLine * line,  static int getNextLine(char **bufPtr, struct singleLine *line,
697         struct configFileInfo * cfi);         struct configFileInfo *cfi);
698  static char * getRootSpecifier(char * str);  static char *getRootSpecifier(char *str);
699  static void requote(struct singleLine *line, struct configFileInfo * cfi);  static void requote(struct singleLine *line, struct configFileInfo *cfi);
700  static void insertElement(struct singleLine * line,  static void insertElement(struct singleLine *line,
701    const char * item, int insertHere,    const char *item, int insertHere,
702    struct configFileInfo * cfi);    struct configFileInfo *cfi);
703  static void removeElement(struct singleLine * line, int removeHere);  static void removeElement(struct singleLine *line, int removeHere);
704  static struct keywordTypes * getKeywordByType(enum lineType_e type,  static struct keywordTypes *getKeywordByType(enum lineType_e type,
705        struct configFileInfo * cfi);       struct configFileInfo *cfi);
706  static enum lineType_e getTypeByKeyword(char * keyword,  static enum lineType_e getTypeByKeyword(char *keyword,
707   struct configFileInfo * cfi);   struct configFileInfo *cfi);
708  static struct singleLine * getLineByType(enum lineType_e type,  static struct singleLine *getLineByType(enum lineType_e type,
709   struct singleLine * line);   struct singleLine *line);
710  static int checkForExtLinux(struct grubConfig * config);  static int checkForExtLinux(struct grubConfig *config);
711  struct singleLine * addLineTmpl(struct singleEntry * entry,  struct singleLine *addLineTmpl(struct singleEntry *entry,
712                                  struct singleLine * tmplLine,         struct singleLine *tmplLine,
713                                  struct singleLine * prevLine,         struct singleLine *prevLine,
714                                  const char * val,         const char *val, struct configFileInfo *cfi);
715   struct configFileInfo * cfi);  struct singleLine *addLine(struct singleEntry *entry,
716  struct singleLine *  addLine(struct singleEntry * entry,     struct configFileInfo *cfi,
717                               struct configFileInfo * cfi,     enum lineType_e type, char *defaultIndent,
718                               enum lineType_e type, char * defaultIndent,     const char *val);
                              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;
752    }
753    
754      return buf;  static inline int
755    kwcmp(struct keywordTypes *kw, const char * label, int case_insensitive)
756    {
757        int kwl = strlen(kw->key);
758        int ll = strlen(label);
759        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 0;
779  }  }
780    
781  static enum lineType_e preferredLineType(enum lineType_e type,  static enum lineType_e preferredLineType(enum lineType_e type,
782   struct configFileInfo *cfi) {   struct configFileInfo *cfi)
783      if (isEfi && cfi == &grub2ConfigType) {  {
784   switch (type) {   if (isEfi && cfi == &grub2ConfigType) {
785   case LT_KERNEL:   switch (type) {
786      return isEfiOnly ? LT_KERNEL : LT_KERNEL_EFI;   case LT_KERNEL:
787   case LT_INITRD:   return isEfiOnly ? LT_KERNEL : LT_KERNEL_EFI;
788      return isEfiOnly ? LT_INITRD : LT_INITRD_EFI;   case LT_INITRD:
789   default:   return isEfiOnly ? LT_INITRD : LT_INITRD_EFI;
790      return type;   default:
791   }   return type;
792     }
793  #if defined(__i386__) || defined(__x86_64__)  #if defined(__i386__) || defined(__x86_64__)
794      } else if (cfi == &grub2ConfigType) {   } else if (cfi == &grub2ConfigType) {
795   switch (type) {   switch (type) {
796   case LT_KERNEL:   case LT_KERNEL:
797      return LT_KERNEL_16;   return LT_KERNEL_16;
798   case LT_INITRD:   case LT_INITRD:
799      return LT_INITRD_16;   return LT_INITRD_16;
800   default:   default:
801      return type;   return type;
802   }   }
803  #endif  #endif
804      }   }
805      return type;   return type;
806  }  }
807    
808  static struct keywordTypes * getKeywordByType(enum lineType_e type,  static struct keywordTypes *getKeywordByType(enum lineType_e type,
809        struct configFileInfo * cfi) {       struct configFileInfo *cfi)
810      for (struct keywordTypes *kw = cfi->keywords; kw->key; kw++) {  {
811   if (kw->type == type)   for (struct keywordTypes * kw = cfi->keywords; kw->key; kw++) {
812      return kw;   if (kw->type == type)
813      }   return kw;
814      return NULL;   }
815     return NULL;
816  }  }
817    
818  static char *getKeyByType(enum lineType_e type, struct configFileInfo * cfi) {  static char *getKeyByType(enum lineType_e type, struct configFileInfo *cfi)
819      struct keywordTypes *kt = getKeywordByType(type, cfi);  {
820      if (kt)   struct keywordTypes *kt = getKeywordByType(type, cfi);
821   return kt->key;   if (kt)
822      return "unknown";   return kt->key;
823     return "unknown";
824  }  }
825    
826  static char * getpathbyspec(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_devname(blkid, device, NULL);   return blkid_get_devname(blkid, device, NULL);
832  }  }
833    
834  static char * getuuidbydev(char *device) {  static char *getuuidbydev(char *device)
835      if (!blkid)  {
836   blkid_get_cache(&blkid, NULL);   if (!blkid)
837     blkid_get_cache(&blkid, NULL);
838    
839      return blkid_get_tag_value(blkid, "UUID", device);   return blkid_get_tag_value(blkid, "UUID", device);
840  }  }
841    
842  static enum lineType_e getTypeByKeyword(char * keyword,  static enum lineType_e getTypeByKeyword(char *keyword,
843   struct configFileInfo * cfi) {   struct configFileInfo *cfi)
844      for (struct keywordTypes *kw = cfi->keywords; kw->key; kw++) {  {
845   if (cfi->caseInsensitive) {   for (struct keywordTypes * kw = cfi->keywords; kw->key; kw++) {
846      if (!strcasecmp(keyword, kw->key))   if (!kwcmp(kw, keyword, cfi->caseInsensitive))
847                  return kw->type;   return kw->type;
  } else {  
     if (!strcmp(keyword, kw->key))  
         return kw->type;  
848   }   }
849      }   return LT_UNKNOWN;
     return LT_UNKNOWN;  
850  }  }
851    
852  static struct singleLine * getLineByType(enum lineType_e type,  static struct singleLine *getLineByType(enum lineType_e type,
853   struct singleLine * line) {   struct singleLine *line)
854      dbgPrintf("getLineByType(%d): ", type);  {
855      for (; line; line = line->next) {   dbgPrintf("getLineByType(%d): ", type);
856   dbgPrintf("%d:%s ", line->type,   for (; line; line = line->next) {
857    line->numElements ? line->elements[0].item : "(empty)");   dbgPrintf("%d:%s ", line->type,
858   if (line->type & type) break;    line->numElements ? line->elements[0].
859      }    item : "(empty)");
860      dbgPrintf(line ? "\n" : " (failed)\n");   if (line->type & type)
861      return line;   break;
862     }
863     dbgPrintf(line ? "\n" : " (failed)\n");
864     return line;
865  }  }
866    
867  static int isBracketedTitle(struct singleLine * line) {  static int isBracketedTitle(struct singleLine *line)
868      if (line->numElements == 1 && *line->elements[0].item == '[') {  {
869          int len = strlen(line->elements[0].item);   if (line->numElements == 1 && *line->elements[0].item == '[') {
870          if (*(line->elements[0].item + len - 1) == ']') {   int len = strlen(line->elements[0].item);
871              /* FIXME: this is a hack... */   if (*(line->elements[0].item + len - 1) == ']') {
872              if (strcmp(line->elements[0].item, "[defaultboot]")) {   /* FIXME: this is a hack... */
873                  return 1;   if (strcmp(line->elements[0].item, "[defaultboot]")) {
874              }   return 1;
875          }   }
876      }   }
877      return 0;   }
878     return 0;
879  }  }
880    
881  static int isEntryStart(struct singleLine * line,  static int isEntryStart(struct singleLine *line, struct configFileInfo *cfi)
882                              struct configFileInfo * cfi) {  {
883      return line->type == cfi->entryStart || line->type == LT_OTHER ||   return line->type == cfi->entryStart || line->type == LT_OTHER ||
884   (cfi->titleBracketed && isBracketedTitle(line));      (cfi->titleBracketed && isBracketedTitle(line));
885  }  }
886    
887  /* extract the title from within brackets (for zipl) */  /* extract the title from within brackets (for zipl) */
888  static char * extractTitle(struct grubConfig *cfg, struct singleLine * line) {  static char *extractTitle(struct grubConfig *cfg, struct singleLine *line)
889      /* bracketed title... let's extract it */  {
890      char * title = NULL;   /* bracketed title... let's extract it */
891      if (line->type == LT_TITLE) {   char *title = NULL;
892   char *tmp = line->elements[cfg->cfi->titlePosition].item;   if (line->type == LT_TITLE) {
893   if (cfg->cfi->titleBracketed) {   char *tmp = line->elements[cfg->cfi->titlePosition].item;
894      tmp++;   if (cfg->cfi->titleBracketed) {
895      title = strdup(tmp);   tmp++;
896      *(title + strlen(title) - 1) = '\0';   title = strdup(tmp);
897   } else {   *(title + strlen(title) - 1) = '\0';
898      title = strdup(tmp);   } else {
899   }   title = strdup(tmp);
900      } else if (line->type == LT_MENUENTRY)   }
901   title = strdup(line->elements[1].item);   } else if (line->type == LT_MENUENTRY)
902      else   title = strdup(line->elements[1].item);
903   return NULL;   else
904      return title;   return NULL;
905     return title;
906  }  }
907    
908  static int readFile(int fd, char ** bufPtr) {  static int readFile(int fd, char **bufPtr)
909      int alloced = 0, size = 0, i = 0;  {
910      char * buf = NULL;   int alloced = 0, size = 0, i = 0;
911     char *buf = NULL;
912    
913      do {   do {
914   size += i;   size += i;
915   if ((size + 1024) > alloced) {   if ((size + 1024) > alloced) {
916      alloced += 4096;   alloced += 4096;
917      buf = realloc(buf, alloced + 1);   buf = realloc(buf, alloced + 1);
918   }   }
919      } while ((i = read(fd, buf + size, 1024)) > 0);   } while ((i = read(fd, buf + size, 1024)) > 0);
920    
921      if (i < 0) {   if (i < 0) {
922   fprintf(stderr, _("error reading input: %s\n"), strerror(errno));   fprintf(stderr, _("error reading input: %s\n"),
923          free(buf);   strerror(errno));
924   return 1;   free(buf);
925      }   return 1;
926     }
927    
928      buf = realloc(buf, size + 2);   buf = realloc(buf, size + 2);
929      if (size == 0)   if (size == 0)
930          buf[size++] = '\n';   buf[size++] = '\n';
931      else   else if (buf[size - 1] != '\n')
932          if (buf[size - 1] != '\n')   buf[size++] = '\n';
933              buf[size++] = '\n';   buf[size] = '\0';
     buf[size] = '\0';  
934    
935      *bufPtr = buf;   *bufPtr = buf;
936    
937      return 0;   return 0;
938  }  }
939    
940  static void lineInit(struct singleLine * line) {  static void lineInit(struct singleLine *line)
941      line->indent = NULL;  {
942      line->elements = NULL;   line->type = LT_UNIDENTIFIED;
943      line->numElements = 0;   line->indent = NULL;
944      line->next = NULL;   line->elements = NULL;
945  }   line->numElements = 0;
946     line->next = NULL;
 struct singleLine * lineDup(struct singleLine * line) {  
     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 (int i = 0; i < newLine->numElements; i++) {  
  newLine->elements[i].indent = strdup(line->elements[i].indent);  
  newLine->elements[i].item = strdup(line->elements[i].item);  
     }  
   
     return newLine;  
947  }  }
948    
949  static void lineFree(struct singleLine * line) {  struct singleLine *lineDup(struct singleLine *line)
950      if (line->indent) free(line->indent);  {
951     struct singleLine *newLine = malloc(sizeof(*newLine));
952    
953      for (int i = 0; i < line->numElements; i++) {   newLine->indent = strdup(line->indent);
954   free(line->elements[i].item);   newLine->next = NULL;
955   free(line->elements[i].indent);   newLine->type = line->type;
956      }   newLine->numElements = line->numElements;
957     newLine->elements = malloc(sizeof(*newLine->elements) *
958       newLine->numElements);
959    
960      if (line->elements) free(line->elements);   for (int i = 0; i < newLine->numElements; i++) {
961      lineInit(line);   newLine->elements[i].indent = strdup(line->elements[i].indent);
962  }   newLine->elements[i].item = strdup(line->elements[i].item);
963     }
964    
965  static int lineWrite(FILE * out, struct singleLine * line,   return newLine;
966       struct configFileInfo * cfi) {  }
     if (fprintf(out, "%s", line->indent) == -1) return -1;  
   
     for (int i = 0; i < line->numElements; i++) {  
  /* Need to handle this, because we strip the quotes from  
  * menuentry when read it. */  
  if (line->type == LT_MENUENTRY && i == 1) {  
     if(!isquote(*line->elements[i].item)) {  
  int substring = 0;  
  /* If the line contains nested quotes, we did not strip  
  * the "interna" quotes and we must use the right quotes  
  * again when writing the updated file. */  
  for (int j = i; j < line->numElements; j++) {  
     if (strchr(line->elements[i].item, '\'') != NULL) {  
        substring = 1;  
        fprintf(out, "\"%s\"", line->elements[i].item);  
        break;  
     }  
  }  
  if (!substring)  
     fprintf(out, "\'%s\'", line->elements[i].item);  
     } else {  
  fprintf(out, "%s", line->elements[i].item);  
     }  
     fprintf(out, "%s", line->elements[i].indent);  
   
     continue;  
  }  
   
  if (i == 1 && line->type == LT_KERNELARGS && cfi->argsInQuotes)  
     if (fputc('"', out) == EOF) return -1;  
   
  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;  
   
  len = strlen(line->indent);  
  for (int 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 (int i = 0; i < line->numElements; i++) {   chptr = start;
1075      strcat(fullLine, line->elements[i].item);   while (*chptr && !isspace(*chptr)) {
1076      strcat(fullLine, line->elements[i].indent);   if (first && *chptr == '=')
1077      free(line->elements[i].item);   break;
1078      free(line->elements[i].indent);   chptr++;
1079   }   }
1080     if (line->type == LT_UNIDENTIFIED)
1081     line->type = getTypeByKeyword(start, cfi);
1082     element->item = strndup(start, chptr - start);
1083     start = chptr;
1084    
1085     /* lilo actually accepts the pathological case of
1086     * append = " foo " */
1087     if (*start == '=')
1088     chptr = start + 1;
1089     else
1090     chptr = start;
1091    
1092     do {
1093     for (; *chptr && isspace(*chptr); chptr++) ;
1094     if (*chptr == '=')
1095     chptr = chptr + 1;
1096     } while (isspace(*chptr));
1097    
1098     element->indent = strndup(start, chptr - start);
1099     start = chptr;
1100    
1101     line->numElements++;
1102     first = 0;
1103     }
1104    
1105     if (!line->numElements)
1106   line->type = LT_WHITESPACE;   line->type = LT_WHITESPACE;
1107   line->numElements = 0;   else {
1108      }   line->type = getTypeByKeyword(line->elements[0].item, cfi);
1109   } else {   if (line->type == LT_UNKNOWN) {
1110   struct keywordTypes *kw;   /* zipl does [title] instead of something reasonable
1111     * like all the other boot loaders.  kind of ugly */
1112     if (cfi->titleBracketed && isBracketedTitle(line)) {
1113     line->type = LT_TITLE;
1114     }
1115    
1116   kw = getKeywordByType(line->type, cfi);   /* this is awkward, but we need to be able to handle
1117     * 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   /* space isn't the only separator, we need to split   line->type = LT_WHITESPACE;
1145   * elements up more   line->numElements = 0;
  */  
  if (!isspace(kw->separatorChar)) {  
     char indent[2] = "";  
     indent[0] = kw->separatorChar;  
     for (int i = 1; i < line->numElements; i++) {  
  char *p;  
  int numNewElements;  
   
  numNewElements = 0;  
  p = line->elements[i].item;  
  while (*p != '\0') {  
  if (*p == kw->separatorChar)  
  numNewElements++;  
  p++;  
  }  
  if (line->numElements + numNewElements >= elementsAlloced) {  
  elementsAlloced += numNewElements + 5;  
  line->elements = realloc(line->elements,  
     sizeof(*line->elements) * elementsAlloced);  
  }  
   
  for (int j = line->numElements; j > i; j--) {  
  line->elements[j + numNewElements] = line->elements[j];  
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   line->elements[i + 1].indent = 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     }
1186     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 int isnumber(const char *s)  static int isnumber(const char *s)
1249  {  {
1250      int i;   int i;
1251      for (i = 0; s[i] != '\0'; i++)   for (i = 0; s[i] != '\0'; i++)
1252   if (s[i] < '0' || s[i] > '9')   if (s[i] < '0' || s[i] > '9')
1253      return 0;   return 0;
1254      return i;   return i;
1255  }  }
1256    
1257  static struct grubConfig * readConfig(const char * inName,  static struct grubConfig *readConfig(const char *inName,
1258        struct configFileInfo * cfi) {       struct configFileInfo *cfi)
1259      int in;  {
1260      char * incoming = NULL, * head;   int in;
1261      int rc;   char *incoming = NULL, *head;
1262      int sawEntry = 0;   int rc;
1263      int movedLine = 0;   int sawEntry = 0;
1264      struct grubConfig * cfg;   int movedLine = 0;
1265      struct singleLine * last = NULL, * line, * defaultLine = NULL;   struct grubConfig *cfg;
1266      char * end;   struct singleLine *last = NULL, *line, *defaultLine = NULL;
1267      struct singleEntry * entry = NULL;   char *end;
1268      int len;   struct singleEntry *entry = NULL;
1269      char * buf;   int len;
1270     char *buf;
1271      if (inName == NULL) {  
1272          printf("Could not find bootloader configuration\n");   if (inName == NULL) {
1273          exit(1);   printf("Could not find bootloader configuration\n");
1274      } else if (!strcmp(inName, "-")) {   exit(1);
1275   in = 0;   } else if (!strcmp(inName, "-")) {
1276      } else {   in = 0;
1277   if ((in = open(inName, O_RDONLY)) < 0) {   } else {
1278      fprintf(stderr, _("error opening %s for read: %s\n"),   if ((in = open(inName, O_RDONLY)) < 0) {
1279      inName, strerror(errno));   fprintf(stderr, _("error opening %s for read: %s\n"),
1280      return NULL;   inName, strerror(errno));
1281     return NULL;
1282     }
1283   }   }
     }  
   
     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);  
   
  if (getNextLine(&head, line, cfi)) {  
     free(line);  
     /* XXX memory leak of everything in cfg */  
     return NULL;  
  }  
   
  if (!sawEntry && line->numElements) {  
     free(cfg->primaryIndent);  
     cfg->primaryIndent = strdup(line->indent);  
  } else if (line->numElements) {  
     free(cfg->secondaryIndent);  
     cfg->secondaryIndent = strdup(line->indent);  
  }  
   
  if (isEntryStart(line, cfi) || (cfg->entries && !sawEntry)) {  
     sawEntry = 1;  
     if (!entry) {  
  cfg->entries = malloc(sizeof(*entry));  
  entry = cfg->entries;  
     } else {  
  entry->next = malloc(sizeof(*entry));  
  entry = entry->next;  
     }  
   
     entry->skip = 0;  
             entry->multiboot = 0;  
     entry->lines = NULL;  
     entry->next = NULL;  
  }  
   
  if (line->type == LT_SET_VARIABLE) {  
     dbgPrintf("found 'set' command (%d elements): ", line->numElements);  
     dbgPrintf("%s", line->indent);  
     for (int i = 0; i < line->numElements; i++)  
  dbgPrintf("\"%s\"%s", line->elements[i].item, line->elements[i].indent);  
     dbgPrintf("\n");  
     struct keywordTypes *kwType = getKeywordByType(LT_DEFAULT, cfi);  
     if (kwType && line->numElements == 3 &&  
     !strcmp(line->elements[1].item, kwType->key) &&  
     !is_special_grub2_variable(line->elements[2].item)) {  
  dbgPrintf("Line sets default config\n");  
  cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;  
  defaultLine = line;  
     }  
   
         } else if (iskernel(line->type)) {  
     /* 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 && 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.  
      */  
     for (struct singleLine *l = entry->lines; l; l = l->next) {  
  if (l->type == LT_HYPER)  
     break;  
  else if (iskernel(l->type)) {  
     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_DEFAULT && cfi->defaultIsUnquoted) ||  
                 (line->type == LT_TITLE && line->numElements > 1)) {  
     /* make the title/default a single argument (undoing our parsing) */  
     len = 0;  
     for (int i = 1; i < line->numElements; i++) {  
  len += strlen(line->elements[i].item);  
  len += strlen(line->elements[i].indent);  
     }  
     buf = malloc(len + 1);  
     *buf = '\0';  
   
     for (int i = 1; i < line->numElements; i++) {  
  strcat(buf, line->elements[i].item);  
  free(line->elements[i].item);  
1284    
1285   if ((i + 1) != line->numElements) {   rc = readFile(in, &incoming);
1286      strcat(buf, line->elements[i].indent);   close(in);
1287      free(line->elements[i].indent);   if (rc)
1288   }   return NULL;
1289      }  
1290     head = incoming;
1291      line->elements[1].indent =   cfg = malloc(sizeof(*cfg));
1292      line->elements[line->numElements - 1].indent;   cfg->primaryIndent = strdup("");
1293      line->elements[1].item = buf;   cfg->secondaryIndent = strdup("\t");
1294      line->numElements = 2;   cfg->flags = GRUB_CONFIG_NO_DEFAULT;
1295   } else if (line->type == LT_MENUENTRY && line->numElements > 3) {   cfg->cfi = cfi;
1296      /* let --remove-kernel="TITLE=what" work */   cfg->theLines = NULL;
1297      len = 0;   cfg->entries = NULL;
1298      char *extras;   cfg->fallbackImage = 0;
1299      char *title;  
1300      /* initially unseen value */   /* copy everything we have */
1301      char quote_char = '\0';   while (*head) {
1302     line = malloc(sizeof(*line));
1303      for (int i = 1; i < line->numElements; i++) {   lineInit(line);
1304   len += strlen(line->elements[i].item);  
1305   len += strlen(line->elements[i].indent);   if (getNextLine(&head, line, cfi)) {
1306      }   free(line);
1307      buf = malloc(len + 1);   /* XXX memory leak of everything in cfg */
1308      *buf = '\0';   return NULL;
   
     /* allocate mem for extra flags. */  
     extras = malloc(len + 1);  
     *extras = '\0';  
   
     /* get title. */  
     for (int i = 0; i < line->numElements; i++) {  
  if (!strcmp(line->elements[i].item, "menuentry"))  
     continue;  
  if (isquote(*line->elements[i].item) && quote_char == '\0') {  
     /* ensure we properly pair off quotes */  
     quote_char = *line->elements[i].item;  
     title = line->elements[i].item + 1;  
  } else {  
     title = line->elements[i].item;  
1309   }   }
1310    
1311   len = strlen(title);   if (!sawEntry && line->numElements) {
1312          if (title[len-1] == quote_char) {   free(cfg->primaryIndent);
1313      strncat(buf, title,len-1);   cfg->primaryIndent = strdup(line->indent);
1314      break;   } else if (line->numElements) {
1315   } else {   free(cfg->secondaryIndent);
1316      strcat(buf, title);   cfg->secondaryIndent = strdup(line->indent);
     strcat(buf, line->elements[i].indent);  
1317   }   }
     }  
1318    
1319      /* get extras */   if (isEntryStart(line, cfi) || (cfg->entries && !sawEntry)) {
1320      int count = 0;   sawEntry = 1;
1321      quote_char = '\0';   if (!entry) {
1322      for (int i = 0; i < line->numElements; i++) {   cfg->entries = malloc(sizeof(*entry));
1323   if (count >= 2) {   entry = cfg->entries;
1324      strcat(extras, line->elements[i].item);   } else {
1325      strcat(extras, line->elements[i].indent);   entry->next = malloc(sizeof(*entry));
1326   }   entry = entry->next;
1327     }
  if (!strcmp(line->elements[i].item, "menuentry"))  
     continue;  
   
  /* count ' or ", there should be two in menuentry line. */  
  if (isquote(*line->elements[i].item) && quote_char == '\0') {  
     /* ensure we properly pair off quotes */  
             quote_char = *line->elements[i].item;  
     count++;  
  }  
   
  len = strlen(line->elements[i].item);  
   
  if (line->elements[i].item[len -1] == quote_char)  
     count++;  
   
  /* ok, we get the final ' or ", others are extras. */  
             }  
     line->elements[1].indent =  
  line->elements[line->numElements - 2].indent;  
     line->elements[1].item = buf;  
     line->elements[2].indent =  
  line->elements[line->numElements - 2].indent;  
     line->elements[2].item = extras;  
     line->numElements = 3;  
  } else if (line->type == LT_KERNELARGS && cfi->argsInQuotes) {  
     /* Strip off any " which may be present; they'll be put back  
        on write. This is one of the few (the only?) places that grubby  
        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 (line->type == LT_DEFAULT && line->numElements == 2) {  
     cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;  
     defaultLine = line;  
  }  
   
  /* 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;  
1328    
1329   if (sawEntry) {   entry->skip = 0;
1330      if (!entry->lines)   entry->multiboot = 0;
1331   entry->lines = line;   entry->lines = NULL;
1332      else   entry->next = NULL;
1333   last->next = line;   }
     dbgPrintf("readConfig added %s to %p\n", getKeyByType(line->type, cfi), entry);  
   
     /* we could have seen this outside of an entry... if so, we  
      * ignore it like any other line we don't grok */  
     if (line->type == LT_ENTRY_END && sawEntry)  
  sawEntry = 0;  
  } else {  
     if (!cfg->theLines)  
  cfg->theLines = line;  
     else  
  last->next = line;  
     dbgPrintf("readConfig added %s to cfg\n", getKeyByType(line->type, cfi));  
  }  
1334    
1335   last = line;   if (line->type == LT_SET_VARIABLE) {
1336      }   dbgPrintf("found 'set' command (%d elements): ",
1337      line->numElements);
1338     dbgPrintf("%s", line->indent);
1339     for (int i = 0; i < line->numElements; i++)
1340     dbgPrintf("\"%s\"%s", line->elements[i].item,
1341      line->elements[i].indent);
1342     dbgPrintf("\n");
1343     struct keywordTypes *kwType =
1344        getKeywordByType(LT_DEFAULT, cfi);
1345     if (kwType && line->numElements == 3
1346        && !strcmp(line->elements[1].item, kwType->key)
1347        && !is_special_grub2_variable(
1348     line->elements[2].item)) {
1349     dbgPrintf("Line sets default config\n");
1350     cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
1351     defaultLine = line;
1352     }
1353     } else if (iskernel(line->type)) {
1354     /* if by some freak chance this is multiboot and the
1355     * "module" lines came earlier in the template, make
1356     * sure to use LT_HYPER instead of LT_KERNEL now
1357     */
1358     if (entry && entry->multiboot)
1359     line->type = LT_HYPER;
1360    
1361     } else if (line->type == LT_MBMODULE) {
1362     /* go back and fix the LT_KERNEL line to indicate
1363     * LT_HYPER instead, now that we know this is a
1364     * multiboot entry.  This only applies to grub, but
1365     * that's the only place we should find LT_MBMODULE
1366     * lines anyway.
1367     */
1368     for (struct singleLine * l = entry->lines; l;
1369         l = l->next) {
1370     if (l->type == LT_HYPER)
1371     break;
1372     else if (iskernel(l->type)) {
1373     l->type = LT_HYPER;
1374     break;
1375     }
1376     }
1377     entry->multiboot = 1;
1378    
1379      free(incoming);   } else if (line->type == LT_HYPER) {
1380     entry->multiboot = 1;
1381    
1382      dbgPrintf("defaultLine is %s\n", defaultLine ? "set" : "unset");   } else if (line->type == LT_FALLBACK && line->numElements == 2) {
1383      if (defaultLine) {   cfg->fallbackImage =
1384          if (defaultLine->numElements > 2 &&      strtol(line->elements[1].item, &end, 10);
1385      cfi->defaultSupportSaved &&   if (*end)
1386      !strncmp(defaultLine->elements[2].item,"\"${saved_entry}\"", 16)) {   cfg->fallbackImage = -1;
1387   cfg->cfi->defaultIsSaved = 1;  
1388   cfg->defaultImage = DEFAULT_SAVED_GRUB2;   } else if ((line->type == LT_DEFAULT && cfi->defaultIsUnquoted)
1389   if (cfg->cfi->getEnv) {     || (line->type == LT_TITLE
1390      char *defTitle = cfi->getEnv(cfg->cfi, "saved_entry");         && line->numElements > 1)) {
1391      if (defTitle) {   /* make the title/default a single argument (undoing
1392   int index = 0;   * our parsing) */
1393   if (isnumber(defTitle)) {   len = 0;
1394      index = atoi(defTitle);   for (int i = 1; i < line->numElements; i++) {
1395      entry = findEntryByIndex(cfg, index);   len += strlen(line->elements[i].item);
1396   } else {   len += strlen(line->elements[i].indent);
     entry = findEntryByTitle(cfg, defTitle, &index);  
1397   }   }
1398   if (entry)   buf = malloc(len + 1);
1399      cfg->defaultImage = index;   *buf = '\0';
     }  
  }  
  } else if (cfi->defaultIsVariable) {  
     char *value = defaultLine->elements[2].item;  
     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) {  
     int 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(cfg, line))) break;  
                 }  
  i++;  
  entry = NULL;  
     }  
1400    
1401      if (entry){   for (int i = 1; i < line->numElements; i++) {
1402          cfg->defaultImage = i;   strcat(buf, line->elements[i].item);
1403      }else{   free(line->elements[i].item);
1404          cfg->defaultImage = -1;  
1405      }   if ((i + 1) != line->numElements) {
1406   }   strcat(buf, line->elements[i].indent);
1407      } else if (cfg->cfi->defaultIsSaved && cfg->cfi->getEnv) {   free(line->elements[i].indent);
1408   char *defTitle = cfi->getEnv(cfg->cfi, "saved_entry");   }
1409   if (defTitle) {   }
     int index = 0;  
     if (isnumber(defTitle)) {  
  index = atoi(defTitle);  
  entry = findEntryByIndex(cfg, index);  
     } else {  
  entry = findEntryByTitle(cfg, defTitle, &index);  
     }  
     if (entry)  
  cfg->defaultImage = index;  
  }  
     } else {  
         cfg->defaultImage = 0;  
     }  
1410    
1411      return cfg;   line->elements[1].indent =
1412  }      line->elements[line->numElements - 1].indent;
1413     line->elements[1].item = buf;
1414     line->numElements = 2;
1415     } else if (line->type == LT_MENUENTRY && line->numElements > 3) {
1416     /* let --remove-kernel="TITLE=what" work */
1417     len = 0;
1418     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  static void writeDefault(FILE * out, char * indent,   /* allocate mem for extra flags. */
1431   char * separator, struct grubConfig * cfg) {   extras = malloc(len + 1);
1432      struct singleEntry * entry;   *extras = '\0';
1433      struct singleLine * line;  
1434      int i;   /* get title. */
1435     for (int i = 0; i < line->numElements; i++) {
1436      if (!cfg->defaultImage && cfg->flags == GRUB_CONFIG_NO_DEFAULT) return;   if (!strcmp
1437        (line->elements[i].item, "menuentry"))
1438      if (cfg->defaultImage == DEFAULT_SAVED)   continue;
1439   fprintf(out, "%sdefault%ssaved\n", indent, separator);   if (isquote(*line->elements[i].item)
1440      else if (cfg->cfi->defaultIsSaved) {      && quote_char == '\0') {
1441   fprintf(out, "%sset default=\"${saved_entry}\"\n", indent);   /* ensure we properly pair off quotes */
1442   if (cfg->defaultImage >= 0 && cfg->cfi->setEnv) {   quote_char = *line->elements[i].item;
1443      char *title;   title = line->elements[i].item + 1;
1444      entry = findEntryByIndex(cfg, cfg->defaultImage);   } else {
1445      line = getLineByType(LT_MENUENTRY, entry->lines);   title = line->elements[i].item;
1446      if (!line)   }
  line = getLineByType(LT_TITLE, entry->lines);  
     if (line) {  
  title = extractTitle(cfg, line);  
  if (title)  
     cfg->cfi->setEnv(cfg->cfi, "saved_entry", title);  
     }  
  }  
     } 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;  
1447    
1448      entry = cfg->entries;   len = strlen(title);
1449      while (entry && entry->skip) entry = entry->next;   if (title[len - 1] == quote_char) {
1450     strncat(buf, title, len - 1);
1451     break;
1452     } else {
1453     strcat(buf, title);
1454     strcat(buf, line->elements[i].indent);
1455     }
1456     }
1457    
1458      i = 0;   /* get extras */
1459      while (entry && i < image) {   int count = 0;
1460   entry = entry->next;   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   while (entry && entry->skip) entry = entry->next;   if (!strcmp
1469   i++;      (line->elements[i].item, "menuentry"))
1470      }   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      if (!entry) return;   len = strlen(line->elements[i].item);
1481    
1482      line = getLineByType(LT_TITLE, entry->lines);   if (line->elements[i].item[len - 1] ==
1483        quote_char)
1484     count++;
1485    
1486      if (line && line->numElements >= 2)   /* ok, we get the final ' or ", others are extras. */
1487   fprintf(out, "%sdefault%s%s\n", indent, separator,   }
1488   line->elements[1].item);   line->elements[1].indent =
1489              else if (line && (line->numElements == 1) &&      line->elements[line->numElements - 2].indent;
1490                       cfg->cfi->titleBracketed) {   line->elements[1].item = buf;
1491   fprintf(out, "%sdefault%s%s\n", indent, separator,   line->elements[2].indent =
1492                          extractTitle(cfg, line));      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  static int writeConfig(struct grubConfig * cfg, char * outName,   if (isquote(*line->elements[1].item))
1504         const char * prefix) {   memmove(line->elements[1].item,
1505      FILE * out;   line->elements[1].item + 1,
1506      struct singleLine * line;   strlen(line->elements[1].item +
1507      struct singleEntry * entry;         1) + 1);
1508      char * tmpOutName;  
1509      int needs = MAIN_DEFAULT;   last = line->numElements - 1;
1510      struct stat sb;   len = strlen(line->elements[last].item) - 1;
1511      int i;   if (isquote(line->elements[last].item[len]))
1512     line->elements[last].item[len] = '\0';
1513      if (!strcmp(outName, "-")) {   }
1514   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 */  
     char *dir = strdupa(outName);  
     rc = chdir(dirname(dir));  
     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;  
     }  
  }  
     }  
1515    
1516      line = cfg->theLines;   if (line->type == LT_DEFAULT && line->numElements == 2) {
1517      struct keywordTypes *defaultKw = getKeywordByType(LT_DEFAULT, cfg->cfi);   cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
1518      while (line) {   defaultLine = line;
1519          if (line->type == LT_SET_VARIABLE && defaultKw &&   }
  line->numElements == 3 &&  
  !strcmp(line->elements[1].item, defaultKw->key) &&  
  !is_special_grub2_variable(line->elements[2].item)) {  
     writeDefault(out, line->indent, line->elements[0].indent, cfg);  
     needs &= ~MAIN_DEFAULT;  
  } else if (line->type == LT_DEFAULT) {  
     writeDefault(out, line->indent, line->elements[0].indent, cfg);  
     needs &= ~MAIN_DEFAULT;  
  } else if (line->type == LT_FALLBACK) {  
     if (cfg->fallbackImage > -1)  
  fprintf(out, "%s%s%s%d\n", line->indent,  
  line->elements[0].item, line->elements[0].indent,  
  cfg->fallbackImage);  
  } else {  
     if (lineWrite(out, line, cfg->cfi) == -1) {  
                 fprintf(stderr, _("grubby: error writing %s: %s\n"),  
                         tmpOutName, strerror(errno));  
                 fclose(out);  
                 unlink(tmpOutName);  
                 return 1;  
             }  
  }  
1520    
1521   line = line->next;   /* If we find a generic config option which should live at the
1522      }     top of the file, move it there. Old versions of grubby were
1523       probably responsible for putting new images in the wrong
1524       place in front of it anyway. */
1525     if (sawEntry && line->type == LT_GENERIC) {
1526     struct singleLine **l = &cfg->theLines;
1527     struct singleLine **last_nonws = &cfg->theLines;
1528     while (*l) {
1529     if ((*l)->type != LT_WHITESPACE)
1530     last_nonws = &((*l)->next);
1531     l = &((*l)->next);
1532     }
1533     line->next = *last_nonws;
1534     *last_nonws = line;
1535     movedLine = 1;
1536     continue; /* without setting 'last' */
1537     }
1538    
1539      if (needs & MAIN_DEFAULT) {   /* If a second line of whitespace happens after a generic
1540   writeDefault(out, cfg->primaryIndent, "=", cfg);   * option which was moved, drop it. */
1541   needs &= ~MAIN_DEFAULT;   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      i = 0;   if (sawEntry) {
1551      while ((entry = findEntryByIndex(cfg, i++))) {   if (!entry->lines)
1552   if (entry->skip) continue;   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   line = entry->lines;   /* we could have seen this outside of an entry... if
1559   while (line) {   * so, we ignore it like any other line we don't grok
1560      if (lineWrite(out, line, cfg->cfi) == -1) {   */
1561                  fprintf(stderr, _("grubby: error writing %s: %s\n"),   if (line->type == LT_ENTRY_END && sawEntry)
1562                          tmpOutName, strerror(errno));   sawEntry = 0;
1563                  fclose(out);   } else {
1564                  unlink(tmpOutName);   if (!cfg->theLines)
1565                  return 1;   cfg->theLines = line;
1566              }   else
1567      line = line->next;   last->next = line;
1568   }   dbgPrintf("readConfig added %s to cfg\n",
1569      }    getKeyByType(line->type, cfi));
1570     }
1571    
1572      if (tmpOutName) {   last = line;
  if (rename(tmpOutName, outName)) {  
     fprintf(stderr, _("grubby: error moving %s to %s: %s\n"),  
     tmpOutName, outName, strerror(errno));  
     unlink(outName);  
             return 1;  
1573   }   }
     }  
   
     return 0;  
 }  
1574    
1575  static int numEntries(struct grubConfig *cfg) {   free(incoming);
     int i = 0;  
     struct singleEntry * entry;  
   
     entry = cfg->entries;  
     while (entry) {  
         if (!entry->skip)  
             i++;  
         entry = entry->next;  
     }  
     return i;  
 }  
1576    
1577  static char *findDiskForRoot()   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 void writeDefault(FILE * out, char *indent,
1693     char *separator, struct grubConfig *cfg)
1694  {  {
1695      int fd;   struct singleEntry *entry;
1696      char buf[65536];   struct singleLine *line;
1697      char *devname;   int i;
1698      char *chptr;  
1699      int rc;   if (!cfg->defaultImage && cfg->flags == GRUB_CONFIG_NO_DEFAULT)
1700     return;
1701    
1702     if (cfg->defaultImage == DEFAULT_SAVED)
1703     fprintf(out, "%sdefault%ssaved\n", indent, separator);
1704     else if (cfg->cfi->defaultIsSaved) {
1705     fprintf(out, "%sset default=\"${saved_entry}\"\n", indent);
1706     if (cfg->defaultImage >= 0 && cfg->cfi->setEnv) {
1707     char *title;
1708     entry = findEntryByIndex(cfg, cfg->defaultImage);
1709     line = getLineByType(LT_MENUENTRY, entry->lines);
1710     if (!line)
1711     line = getLineByType(LT_TITLE, entry->lines);
1712     if (line) {
1713     title = extractTitle(cfg, line);
1714     if (title)
1715     cfg->cfi->setEnv(cfg->cfi,
1716     "saved_entry", title);
1717     }
1718     }
1719     } else if (cfg->defaultImage > -1) {
1720     if (cfg->cfi->defaultIsIndex) {
1721     if (cfg->cfi->defaultIsVariable) {
1722     fprintf(out, "%sset default=\"%d\"\n", indent,
1723     cfg->defaultImage);
1724     } else {
1725     fprintf(out, "%sdefault%s%d\n", indent,
1726     separator, cfg->defaultImage);
1727     }
1728     } else {
1729     int image = cfg->defaultImage;
1730    
1731      if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) {   entry = cfg->entries;
1732          fprintf(stderr, "grubby: failed to open %s: %s\n",   while (entry && entry->skip)
1733                  _PATH_MOUNTED, strerror(errno));   entry = entry->next;
1734          return NULL;  
1735      }   i = 0;
1736     while (entry && i < image) {
1737     entry = entry->next;
1738    
1739      rc = read(fd, buf, sizeof(buf) - 1);   while (entry && entry->skip)
1740      if (rc <= 0) {   entry = entry->next;
1741          fprintf(stderr, "grubby: failed to read %s: %s\n",   i++;
1742                  _PATH_MOUNTED, strerror(errno));   }
         close(fd);  
         return NULL;  
     }  
     close(fd);  
     buf[rc] = '\0';  
     chptr = buf;  
   
     char *foundanswer = NULL;  
   
     while (chptr && chptr != buf+rc) {  
         devname = chptr;  
   
         /*  
          * The first column of a mtab entry is the device, but if the entry is a  
          * special device it won't start with /, so move on to the next line.  
          */  
         if (*devname != '/') {  
             chptr = strchr(chptr, '\n');  
             if (chptr)  
                 chptr++;  
             continue;  
         }  
   
         /* Seek to the next space */  
         chptr = strchr(chptr, ' ');  
         if (!chptr) {  
             fprintf(stderr, "grubby: error parsing %s: %s\n",  
                     _PATH_MOUNTED, strerror(errno));  
             return NULL;  
         }  
   
         /*  
          * The second column of a mtab entry is the mount point, we are looking  
          * for '/' obviously.  
          */  
         if (*(++chptr) == '/' && *(++chptr) == ' ') {  
             /* remember the last / entry in mtab */  
            foundanswer = devname;  
         }  
   
         /* Next line */  
         chptr = strchr(chptr, '\n');  
         if (chptr)  
             chptr++;  
     }  
1743    
1744      /* Return the last / entry found */   if (!entry)
1745      if (foundanswer) {   return;
         chptr = strchr(foundanswer, ' ');  
         *chptr = '\0';  
         return strdup(foundanswer);  
     }  
1746    
1747      return NULL;   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  void printEntry(struct singleEntry * entry, FILE *f) {  static int writeConfig(struct grubConfig *cfg, char *outName,
1766      int i;         const char *prefix)
1767      struct singleLine * line;  {
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      for (line = entry->lines; line; line = line->next) {   tmpOutName = alloca(strlen(outName) + 2);
1809   log_message(f, "DBG: %s", line->indent);   sprintf(tmpOutName, "%s-", outName);
1810   for (i = 0; i < line->numElements; i++) {   out = fopen(tmpOutName, "w");
1811      /* Need to handle this, because we strip the quotes from   if (!out) {
1812       * menuentry when read it. */   fprintf(stderr, _("grubby: error creating %s: %s\n"),
1813      if (line->type == LT_MENUENTRY && i == 1) {   tmpOutName, strerror(errno));
1814   if(!isquote(*line->elements[i].item))   return 1;
1815      log_message(f, "\'%s\'", line->elements[i].item);   }
1816   else  
1817      log_message(f, "%s", line->elements[i].item);   if (!stat(outName, &sb)) {
1818   log_message(f, "%s", line->elements[i].indent);   if (chmod(tmpOutName, sb.st_mode & ~(S_IFMT))) {
1819     fprintf(stderr,
1820   continue;   _
1821      }   ("grubby: error setting perms on %s: %s\n"),
1822         tmpOutName, strerror(errno));
1823      log_message(f, "%s%s",   fclose(out);
1824      line->elements[i].item, line->elements[i].indent);   unlink(tmpOutName);
1825     return 1;
1826     }
1827     }
1828   }   }
1829   log_message(f, "\n");  
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  void notSuitablePrintf(struct singleEntry * entry, int okay, const char *fmt, ...)  static int numEntries(struct grubConfig *cfg)
1902  {  {
1903      static int once;   int i = 0;
1904      va_list argp, argq;   struct singleEntry *entry;
1905    
1906      va_start(argp, fmt);   entry = cfg->entries;
1907     while (entry) {
1908     if (!entry->skip)
1909     i++;
1910     entry = entry->next;
1911     }
1912     return i;
1913    }
1914    
1915      va_copy(argq, argp);  static char *findDiskForRoot()
1916      if (!once) {  {
1917   log_time(NULL);   int fd;
1918   log_message(NULL, "command line: %s\n", saved_command_line);   char buf[65536];
1919      }   char *devname;
1920      log_message(NULL, "DBG: Image entry %s: ", okay ? "succeeded" : "failed");   char *chptr;
1921      log_vmessage(NULL, fmt, argq);   int rc;
1922    
1923      printEntry(entry, NULL);   if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) {
1924      va_end(argq);   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 (!debug) {   char *foundanswer = NULL;
  once = 1;  
      va_end(argp);  
  return;  
     }  
1941    
1942      if (okay) {   while (chptr && chptr != buf + rc) {
1943   va_end(argp);   devname = chptr;
1944   return;  
1945      }   /*
1946     * The first column of a mtab entry is the device, but if the
1947     * entry is a special device it won't start with /, so move
1948     * on to the next line.
1949     */
1950     if (*devname != '/') {
1951     chptr = strchr(chptr, '\n');
1952     if (chptr)
1953     chptr++;
1954     continue;
1955     }
1956    
1957     /* 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, FILE * f)
1991    {
1992     int i;
1993     struct singleLine *line;
1994    
1995     for (line = entry->lines; line; line = line->next) {
1996     log_message(f, "DBG: %s", line->indent);
1997     for (i = 0; i < line->numElements; i++) {
1998     /* Need to handle this, because we strip the quotes from
1999     * menuentry when read it. */
2000     if (line->type == LT_MENUENTRY && i == 1) {
2001     if (!isquote(*line->elements[i].item))
2002     log_message(f, "\'%s\'",
2003        line->elements[i].item);
2004     else
2005     log_message(f, "%s",
2006        line->elements[i].item);
2007     log_message(f, "%s", line->elements[i].indent);
2008    
2009     continue;
2010     }
2011    
2012     log_message(f, "%s%s",
2013        line->elements[i].item,
2014        line->elements[i].indent);
2015     }
2016     log_message(f, "\n");
2017     }
2018    }
2019    
2020    void notSuitablePrintf(struct singleEntry *entry, int okay, const char *fmt,
2021           ...)
2022    {
2023     static int once;
2024     va_list argp, argq;
2025    
2026     va_start(argp, fmt);
2027    
2028      if (!once)   va_copy(argq, argp);
2029   log_message(stderr, "DBG: command line: %s\n", saved_command_line);   if (!once) {
2030      once = 1;   log_time(NULL);
2031      fprintf(stderr, "DBG: Image entry failed: ");   log_message(NULL, "command line: %s\n", saved_command_line);
2032      vfprintf(stderr, fmt, argp);   }
2033      printEntry(entry, stderr);   log_message(NULL, "DBG: Image entry %s: ",
2034      va_end(argp);      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 1861  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, 0, "marked to skip\n");  
  return 0;  
     }  
2083    
2084      line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);   if (skipRemoved && entry->skip) {
2085      if (!line) {   notSuitablePrintf(entry, 0, "marked to skip\n");
2086   notSuitablePrintf(entry, 0, "no line found\n");   return 0;
2087   return 0;   }
     }  
     if (line->numElements < 2) {  
  notSuitablePrintf(entry, 0, "line has only %d elements\n",  
     line->numElements);  
  return 0;  
     }  
2088    
2089      if (flags & GRUBBY_BADIMAGE_OKAY) {   line =
2090      notSuitablePrintf(entry, 1, "\n");      getLineByType(LT_KERNEL | LT_HYPER | LT_KERNEL_EFI | LT_KERNEL_16,
2091      return 1;    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, 0, "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);
      */  
     line = getLineByType(LT_KERNELARGS|LT_MBMODULE, entry->lines);  
2127    
2128              /* failed to find one */   if (line && line->numElements >= 2) {
2129              if (!line) {   dev = line->elements[1].item;
2130   notSuitablePrintf(entry, 0, "no line found\n");   } else {
2131   return 0;   /* didn't succeed in finding a LT_ROOT, let's try
2132              }   * LT_KERNELARGS.  grub+multiboot uses LT_MBMODULE
2133     * for the args, so check that too.
2134     */
2135     line =
2136        getLineByType(LT_KERNELARGS | LT_MBMODULE,
2137      entry->lines);
2138    
2139     /* failed to find one */
2140     if (!line) {
2141     notSuitablePrintf(entry, 0, "no line found\n");
2142     return 0;
2143     }
2144    
2145      for (i = 1; i < line->numElements; i++)   for (i = 1; i < line->numElements; i++)
2146          if (!strncasecmp(line->elements[i].item, "root=", 5)) break;   if (!strncasecmp
2147      if (i < line->numElements)      (line->elements[i].item, "root=", 5))
2148          dev = line->elements[i].item + 5;   break;
2149      else {   if (i < line->numElements)
2150   notSuitablePrintf(entry, 0, "no root= entry found\n");   dev = line->elements[i].item + 5;
2151   /* it failed too...  can't find root= */   else {
2152          return 0;   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, 0, "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, 0, "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, 0, "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, 0, "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");   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    
2209     if (isdigit(*kernel)) {
2210     int *indexVars = alloca(sizeof(*indexVars) * strlen(kernel));
2211    
2212     i = 0;
2213     indexVars[i] = strtol(kernel, &chptr, 10);
2214     while (*chptr == ',') {
2215     i++;
2216     kernel = chptr + 1;
2217     indexVars[i] = strtol(kernel, &chptr, 10);
2218     }
2219    
2220      if (isdigit(*kernel)) {   if (*chptr) {
2221   int * indexVars = alloca(sizeof(*indexVars) * strlen(kernel));   /* can't parse it, bail */
2222     return NULL;
2223     }
2224    
2225   i = 0;   indexVars[i + 1] = -1;
  indexVars[i] = strtol(kernel, &chptr, 10);  
  while (*chptr == ',') {  
     i++;  
     kernel = chptr + 1;  
     indexVars[i] = strtol(kernel, &chptr, 10);  
  }  
2226    
2227   if (*chptr) {   i = 0;
2228      /* can't parse it, bail */   if (index) {
2229      return NULL;   while (i < *index) {
2230   }   i++;
2231     if (indexVars[i] == -1)
2232     return NULL;
2233     }
2234     }
2235    
2236   indexVars[i + 1] = -1;   entry = findEntryByIndex(config, indexVars[i]);
2237     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   i = 0;   if (index)
2247   if (index) {   *index = indexVars[i];
2248      while (i < *index) {   return entry;
  i++;  
  if (indexVars[i] == -1) return NULL;  
     }  
2249   }   }
2250    
2251   entry = findEntryByIndex(config, indexVars[i]);   if (!strcmp(kernel, "DEFAULT")) {
2252   if (!entry) return NULL;   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|LT_KERNEL_EFI|LT_KERNEL_16, 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;
  *index = config->defaultImage;  
  }  
     } else if (!strcmp(kernel, "ALL")) {  
  if (index)  
     i = *index;  
  else  
     i = 0;  
   
  while ((entry = findEntryByIndex(config, i))) {  
     if (!entry->skip) break;  
     i++;  
  }  
2280    
2281   if (entry && index)   if (!strncmp(kernel, "TITLE=", 6)) {
2282      *index = i;   prefix = "";
2283      } else {   checkType = LT_TITLE | LT_MENUENTRY;
2284   if (index)   kernel += 6;
2285      i = *index;   }
  else  
     i = 0;  
2286    
2287   if (!strncmp(kernel, "TITLE=", 6)) {   for (entry = findEntryByIndex(config, i); entry;
2288      prefix = "";       entry = entry->next, i++) {
2289      checkType = LT_TITLE|LT_MENUENTRY;   if (entry->skip)
2290      kernel += 6;   continue;
2291   }  
2292     dbgPrintf("findEntryByPath looking for %d %s in %p\n",
2293   for (entry = findEntryByIndex(config, i); entry; entry = entry->next, i++) {    checkType, kernel, entry);
2294      if (entry->skip) continue;  
2295     /* check all the lines matching checkType */
2296      dbgPrintf("findEntryByPath looking for %d %s in %p\n", checkType, kernel, entry);   for (line = entry->lines; line; line = line->next) {
2297     enum lineType_e ct = checkType;
2298      /* check all the lines matching checkType */   if (entry->multiboot && checkType == LT_KERNEL)
2299      for (line = entry->lines; line; line = line->next) {   ct = LT_KERNEL | LT_KERNEL_EFI |
2300   enum lineType_e ct = checkType;      LT_MBMODULE | LT_HYPER |
2301   if (entry->multiboot && checkType == LT_KERNEL)      LT_KERNEL_16;
2302      ct = LT_KERNEL|LT_KERNEL_EFI|LT_MBMODULE|LT_HYPER|LT_KERNEL_16;   else if (checkType & LT_KERNEL)
2303   else if (checkType & LT_KERNEL)   ct = checkType | LT_KERNEL_EFI |
2304      ct = checkType | LT_KERNEL_EFI | LT_KERNEL_16;      LT_KERNEL_16;
2305   line = getLineByType(ct, line);   line = getLineByType(ct, line);
2306   if (!line)   if (!line)
2307      break;  /* not found in this entry */   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   if (line && line->type != LT_MENUENTRY &&   /* make sure this entry has a kernel identifier; this skips
2327   line->numElements >= 2) {   * non-Linux boot entries (could find netbsd etc, though, which is
2328      rootspec = getRootSpecifier(line->elements[1].item);   * unfortunate)
2329      if (!strcmp(line->elements[1].item +   */
2330   ((rootspec != NULL) ? strlen(rootspec) : 0),   if (line
2331   kernel + strlen(prefix)))      && getLineByType(LT_KERNEL | LT_HYPER |
2332   break;       LT_KERNEL_EFI | LT_KERNEL_16,
2333         entry->lines))
2334     break; /* found 'im! */
2335   }   }
  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|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines))  
  break; /* found 'im! */  
  }  
2336    
2337   if (index) *index = i;   if (index)
2338      }   *index = i;
2339     }
2340    
2341      return entry;   return entry;
2342  }  }
2343    
2344  struct singleEntry * findEntryByTitle(struct grubConfig * cfg, char *title,  struct singleEntry *findEntryByTitle(struct grubConfig *cfg, char *title,
2345        int * index) {       int *index)
2346      struct singleEntry * entry;  {
2347      struct singleLine * line;   struct singleEntry *entry;
2348      int i;   struct singleLine *line;
2349      char * newtitle;   int i;
2350     char *newtitle;
2351      for (i = 0, entry = cfg->entries; entry; entry = entry->next, i++) {  
2352   if (index && i < *index)   for (i = 0, entry = cfg->entries; entry; entry = entry->next, i++) {
2353      continue;   if (index && i < *index)
2354   line = getLineByType(LT_TITLE, entry->lines);   continue;
2355   if (!line)   line = getLineByType(LT_TITLE, entry->lines);
2356      line = getLineByType(LT_MENUENTRY, entry->lines);   if (!line)
2357   if (!line)   line = getLineByType(LT_MENUENTRY, entry->lines);
2358      continue;   if (!line)
2359   newtitle = grub2ExtractTitle(line);   continue;
2360   if (!newtitle)   newtitle = grub2ExtractTitle(line);
2361      continue;   if (!newtitle)
2362   if (!strcmp(title, newtitle))   continue;
2363      break;   if (!strcmp(title, newtitle))
2364      }   break;
2365     }
2366    
2367      if (!entry)   if (!entry)
2368   return NULL;   return NULL;
2369    
2370      if (index)   if (index)
2371   *index = i;   *index = i;
2372      return entry;   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;
     entry = cfg->entries;  
     while (index && entry) {  
  entry = entry->next;  
  index--;  
     }  
2378    
2379      return entry;   entry = cfg->entries;
2380     while (index && entry) {
2381     entry = entry->next;
2382     index--;
2383     }
2384    
2385     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 2143  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->cfi->defaultIsSaved) {  
2399   if (cfg->cfi->getEnv) {   if (cfg->cfi->defaultIsSaved) {
2400      char *defTitle = cfg->cfi->getEnv(cfg->cfi, "saved_entry");   if (cfg->cfi->getEnv) {
2401      if (defTitle) {   char *defTitle =
2402   int index = 0;      cfg->cfi->getEnv(cfg->cfi, "saved_entry");
2403   if (isnumber(defTitle)) {   if (defTitle) {
2404      index = atoi(defTitle);   int index = 0;
2405      entry = findEntryByIndex(cfg, index);   if (isnumber(defTitle)) {
2406   } else {   index = atoi(defTitle);
2407      entry = findEntryByTitle(cfg, defTitle, &index);   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)) {   if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
2426      cfg->defaultImage = index;   if (indexPtr)
2427      if (indexPtr)   *indexPtr = cfg->defaultImage;
2428   *indexPtr = index;   return entry;
2429      return entry;   }
  }  
     }  
  }  
     } else if (cfg->defaultImage > -1) {  
  entry = findEntryByIndex(cfg, cfg->defaultImage);  
  if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {  
     if (indexPtr) *indexPtr = cfg->defaultImage;  
     return entry;  
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;   return entry;
2445   }   }
2446    
2447   index++;   index++;
2448      }   }
2449    
2450      fprintf(stderr, _("grubby fatal error: unable to find a suitable template\n"));   fprintf(stderr,
2451     _("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, int index) {   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 ((index >= 0) && config->cfi->defaultIsIndex) {  
  if (findEntryByIndex(config, index))  
     config->defaultImage = index;  
  else  
     config->defaultImage = -1;  
  return;  
     } else if (defaultKernelPath) {  
  i = 0;  
  if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {  
     config->defaultImage = i;  
  } else {  
     config->defaultImage = -1;  
     return;  
2491   }   }
     }  
2492    
2493      /* defaultImage now points to what we'd like to use, but before any order   while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
2494         changes */   entry->skip = 1;
     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;  
   
     if (entry && !entry->skip) {  
  /* we can preserve the default */  
  if (hasNew)  
     config->defaultImage++;  
   
  /* count the number of entries erased before this one */  
  for (j = 0; j < config->defaultImage; j++) {  
     entry2 = findEntryByIndex(config, j);  
     if (entry2->skip) config->defaultImage--;  
  }  
     } else if (hasNew) {  
  config->defaultImage = 0;  
     } else {  
  /* Either we just erased the default (or the default line was bad  
  * to begin with) and didn't put a new one in. We'll use the first  
  * valid image. */  
  newDefault = findTemplate(config, prefix, &config->defaultImage, 1,  
   flags);  
  if (!newDefault)  
     config->defaultImage = -1;  
     }  
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      int j;   if (findEntryByIndex(config, index))
2509     config->defaultImage = index;
2510      printf("index=%d\n", index);   else
2511     config->defaultImage = -1;
2512      line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);   return;
2513      if (!line) {   } else if (defaultKernelPath) {
2514          printf("non linux entry\n");   i = 0;
2515          return;   if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
2516      }   config->defaultImage = i;
2517     } else {
2518     config->defaultImage = -1;
2519     return;
2520     }
2521     }
2522    
2523      if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))   /* defaultImage now points to what we'd like to use, but before any
2524   printf("kernel=%s\n", line->elements[1].item);   * order changes */
2525      else   if ((config->defaultImage == DEFAULT_SAVED) ||
2526   printf("kernel=%s%s\n", prefix, line->elements[1].item);      (config->defaultImage == DEFAULT_SAVED_GRUB2))
2527     /* default is set to saved, we don't want to change it */
2528      if (line->numElements >= 3) {   return;
2529   printf("args=\"");  
2530   i = 2;   if (config->defaultImage > -1)
2531   while (i < line->numElements) {   entry = findEntryByIndex(config, config->defaultImage);
2532      if (!strncmp(line->elements[i].item, "root=", 5)) {   else
2533   root = line->elements[i].item + 5;   entry = NULL;
     } 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;  
2559    
2560      printf("args=\"");  void setFallbackImage(struct grubConfig *config, int hasNew)
2561      i = 1;  {
2562      while (i < line->numElements) {   struct singleEntry *entry, *entry2;
2563   if (!strncmp(line->elements[i].item, "root=", 5)) {   int j;
     root = line->elements[i].item + 5;  
  } else {  
     s = line->elements[i].item;  
2564    
2565      printf("%s%s", s, line->elements[i].indent);   if (config->fallbackImage == -1)
2566   }   return;
2567    
2568   i++;   entry = findEntryByIndex(config, config->fallbackImage);
2569      }   if (!entry || entry->skip) {
2570     config->fallbackImage = -1;
2571     return;
2572     }
2573    
2574     if (hasNew)
2575     config->fallbackImage++;
2576    
2577      s = line->elements[i - 1].indent;   /* count the number of entries erased before this one */
2578      printf("\"\n");   for (j = 0; j < config->fallbackImage; j++) {
2579     entry2 = findEntryByIndex(config, j);
2580     if (entry2->skip)
2581     config->fallbackImage--;
2582   }   }
2583      }  }
2584    
2585      if (!root) {  void displayEntry(struct singleEntry *entry, const char *prefix, int index)
2586   line = getLineByType(LT_ROOT, entry->lines);  {
2587   if (line && line->numElements >= 2)   struct singleLine *line;
2588      root=line->elements[1].item;   char *root = NULL;
2589      }   int i;
2590     int j;
2591    
2592      if (root) {   printf("index=%d\n", index);
  char * s = alloca(strlen(root) + 1);  
   
  strcpy(s, root);  
  if (s[strlen(s) - 1] == '"')  
     s[strlen(s) - 1] = '\0';  
  /* make sure the root doesn't have a trailing " */  
  printf("root=%s\n", s);  
     }  
2593    
2594      line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);   line =
2595        getLineByType(LT_KERNEL | LT_HYPER | LT_KERNEL_EFI | LT_KERNEL_16,
2596      entry->lines);
2597     if (!line) {
2598     printf("non linux entry\n");
2599     return;
2600     }
2601    
     if (line && line->numElements >= 2) {  
2602   if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))   if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2603      printf("initrd=");   printf("kernel=%s\n", line->elements[1].item);
2604   else   else
2605      printf("initrd=%s", prefix);   printf("kernel=%s%s\n", prefix, line->elements[1].item);
2606    
2607   for (i = 1; i < line->numElements; i++)   if (line->numElements >= 3) {
2608      printf("%s%s", line->elements[i].item, line->elements[i].indent);   printf("args=\"");
2609   printf("\n");   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      line = getLineByType(LT_TITLE, entry->lines);   i++;
2619      if (line) {   }
2620   printf("title=%s\n", line->elements[1].item);   printf("\"\n");
2621      } else {   } else {
2622   char * title;   line = getLineByType(LT_KERNELARGS, entry->lines);
2623   line = getLineByType(LT_MENUENTRY, entry->lines);   if (line) {
2624   if (line) {   char *s;
2625      title = grub2ExtractTitle(line);  
2626      if (title)   printf("args=\"");
2627   printf("title=%s\n", title);   i = 1;
2628   }   while (i < line->numElements) {
2629      }   if (!strncmp
2630        (line->elements[i].item, "root=", 5)) {
2631     root = line->elements[i].item + 5;
2632     } else {
2633     s = line->elements[i].item;
2634    
2635      for (j = 0, line = entry->lines; line; line = line->next) {   printf("%s%s", s,
2636   if ((line->type & LT_MBMODULE) && line->numElements >= 2) {         line->elements[i].indent);
2637      if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))   }
  printf("mbmodule%d=", j);  
     else  
  printf("mbmodule%d=%s", j, prefix);  
   
     for (i = 1; i < line->numElements; i++)  
  printf("%s%s", line->elements[i].item, line->elements[i].indent);  
     printf("\n");  
     j++;  
  }  
     }  
 }  
2638    
2639  int isSuseSystem(void) {   i++;
2640      const char * path;   }
     const static char default_path[] = "/etc/SuSE-release";  
2641    
2642      if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)   s = line->elements[i - 1].indent;
2643   path = default_path;   printf("\"\n");
2644     }
2645     }
2646    
2647      if (!access(path, R_OK))   if (!root) {
2648   return 1;   line = getLineByType(LT_ROOT, entry->lines);
2649      return 0;   if (line && line->numElements >= 2)
2650  }   root = line->elements[1].item;
2651     }
2652    
2653  int isSuseGrubConf(const char * path) {   if (root) {
2654      FILE * grubConf;   char *s = alloca(strlen(root) + 1);
     char * line = NULL;  
     size_t len = 0, res = 0;  
   
     grubConf = fopen(path, "r");  
     if (!grubConf) {  
         dbgPrintf("Could not open SuSE configuration file '%s'\n", path);  
  return 0;  
     }  
2655    
2656      while ((res = getline(&line, &len, grubConf)) != -1) {   strcpy(s, root);
2657   if (!strncmp(line, "setup", 5)) {   if (s[strlen(s) - 1] == '"')
2658      fclose(grubConf);   s[strlen(s) - 1] = '\0';
2659      free(line);   /* make sure the root doesn't have a trailing " */
2660      return 1;   printf("root=%s\n", s);
2661   }   }
     }  
2662    
2663      dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",   line =
2664        path);      getLineByType(LT_INITRD | LT_INITRD_EFI | LT_INITRD_16,
2665      entry->lines);
2666    
2667      fclose(grubConf);   if (line && line->numElements >= 2) {
2668      free(line);   if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2669      return 0;   printf("initrd=");
2670  }   else
2671     printf("initrd=%s", prefix);
2672    
2673  int suseGrubConfGetLba(const char * path, int * lbaPtr) {   for (i = 1; i < line->numElements; i++)
2674      FILE * grubConf;   printf("%s%s", line->elements[i].item,
2675      char * line = NULL;         line->elements[i].indent);
2676      size_t res = 0, len = 0;   printf("\n");
   
     if (!path) return 1;  
     if (!lbaPtr) return 1;  
   
     grubConf = fopen(path, "r");  
     if (!grubConf) return 1;  
   
     while ((res = getline(&line, &len, grubConf)) != -1) {  
  if (line[res - 1] == '\n')  
     line[res - 1] = '\0';  
  else if (len > res)  
     line[res] = '\0';  
  else {  
     line = realloc(line, res + 1);  
     line[res] = '\0';  
2677   }   }
2678    
2679   if (!strncmp(line, "setup", 5)) {   line = getLineByType(LT_TITLE, entry->lines);
2680      if (strstr(line, "--force-lba")) {   if (line) {
2681          *lbaPtr = 1;   printf("title=%s\n", line->elements[1].item);
2682      } else {   } else {
2683          *lbaPtr = 0;   char *title;
2684      }   line = getLineByType(LT_MENUENTRY, entry->lines);
2685      dbgPrintf("lba: %i\n", *lbaPtr);   if (line) {
2686      break;   title = grub2ExtractTitle(line);
2687     if (title)
2688     printf("title=%s\n", title);
2689     }
2690   }   }
     }  
   
     free(line);  
     fclose(grubConf);  
     return 0;  
 }  
2691    
2692  int suseGrubConfGetInstallDevice(const char * path, char ** devicePtr) {   for (j = 0, line = entry->lines; line; line = line->next) {
2693      FILE * grubConf;   if ((line->type & LT_MBMODULE) && line->numElements >= 2) {
2694      char * line = NULL;   if (!strncmp
2695      size_t res = 0, len = 0;      (prefix, line->elements[1].item, strlen(prefix)))
2696      char * lastParamPtr = NULL;   printf("mbmodule%d=", j);
2697      char * secLastParamPtr = NULL;   else
2698      char installDeviceNumber = '\0';   printf("mbmodule%d=%s", j, prefix);
2699      char * bounds = NULL;  
2700     for (i = 1; i < line->numElements; i++)
2701      if (!path) return 1;   printf("%s%s", line->elements[i].item,
2702      if (!devicePtr) return 1;         line->elements[i].indent);
2703     printf("\n");
2704      grubConf = fopen(path, "r");   j++;
2705      if (!grubConf) return 1;   }
   
     while ((res = getline(&line, &len, grubConf)) != -1) {  
  if (strncmp(line, "setup", 5))  
     continue;  
   
  if (line[res - 1] == '\n')  
     line[res - 1] = '\0';  
  else if (len > res)  
     line[res] = '\0';  
  else {  
     line = realloc(line, res + 1);  
     line[res] = '\0';  
2706   }   }
2707    }
2708    
2709   lastParamPtr = bounds = line + res;  int isSuseSystem(void)
2710    {
2711     const char *path;
2712     const static char default_path[] = "/etc/SuSE-release";
2713    
2714   /* Last parameter in grub may be an optional IMAGE_DEVICE */   if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2715   while (!isspace(*lastParamPtr))   path = default_path;
     lastParamPtr--;  
  lastParamPtr++;  
2716    
2717   secLastParamPtr = lastParamPtr - 2;   if (!access(path, R_OK))
2718   dbgPrintf("lastParamPtr: %s\n", lastParamPtr);   return 1;
2719     return 0;
2720    }
2721    
2722   if (lastParamPtr + 3 > bounds) {  int isSuseGrubConf(const char *path)
2723      dbgPrintf("lastParamPtr going over boundary");  {
2724      fclose(grubConf);   FILE *grubConf;
2725      free(line);   char *line = NULL;
2726      return 1;   size_t len = 0, res = 0;
2727    
2728     grubConf = fopen(path, "r");
2729     if (!grubConf) {
2730     dbgPrintf("Could not open SuSE configuration file '%s'\n",
2731      path);
2732     return 0;
2733   }   }
  if (!strncmp(lastParamPtr, "(hd", 3))  
     lastParamPtr += 3;  
  dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);  
2734    
2735   /*   while ((res = getline(&line, &len, grubConf)) != -1) {
2736   * Second last parameter will decide wether last parameter is   if (!strncmp(line, "setup", 5)) {
2737   * an IMAGE_DEVICE or INSTALL_DEVICE   fclose(grubConf);
2738   */   free(line);
2739   while (!isspace(*secLastParamPtr))   return 1;
2740      secLastParamPtr--;   }
  secLastParamPtr++;  
   
  if (secLastParamPtr + 3 > bounds) {  
     dbgPrintf("secLastParamPtr going over boundary");  
     fclose(grubConf);  
     free(line);  
     return 1;  
  }  
  dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);  
  if (!strncmp(secLastParamPtr, "(hd", 3)) {  
     secLastParamPtr += 3;  
     dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);  
     installDeviceNumber = *secLastParamPtr;  
  } else {  
     installDeviceNumber = *lastParamPtr;  
2741   }   }
2742    
2743   *devicePtr = malloc(6);   dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2744   snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);    path);
2745   dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);  
2746   fclose(grubConf);   fclose(grubConf);
2747   free(line);   free(line);
2748   return 0;   return 0;
2749      }  }
   
     free(line);  
     fclose(grubConf);  
     return 1;  
 }  
   
 int grubGetBootFromDeviceMap(const char * device,  
      char ** bootPtr) {  
     FILE * deviceMap;  
     char * line = NULL;  
     size_t res = 0, len = 0;  
     char * devicePtr;  
     char * bounds = NULL;  
     const char * path;  
     const static char default_path[] = "/boot/grub/device.map";  
   
     if (!device) return 1;  
     if (!bootPtr) return 1;  
   
     if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)  
  path = default_path;  
   
     dbgPrintf("opening grub device.map file from: %s\n", path);  
     deviceMap = fopen(path, "r");  
     if (!deviceMap)  
  return 1;  
2750    
2751      while ((res = getline(&line, &len, deviceMap)) != -1) {  int suseGrubConfGetLba(const char *path, int *lbaPtr)
2752          if (!strncmp(line, "#", 1))  {
2753      continue;   FILE *grubConf;
2754     char *line = NULL;
2755   if (line[res - 1] == '\n')   size_t res = 0, len = 0;
     line[res - 1] = '\0';  
  else if (len > res)  
     line[res] = '\0';  
  else {  
     line = realloc(line, res + 1);  
     line[res] = '\0';  
  }  
2756    
2757   devicePtr = line;   if (!path)
2758   bounds = line + res;   return 1;
2759     if (!lbaPtr)
2760     return 1;
2761    
2762   while ((isspace(*line) && ((devicePtr + 1) <= bounds)))   grubConf = fopen(path, "r");
2763      devicePtr++;   if (!grubConf)
2764   dbgPrintf("device: %s\n", devicePtr);   return 1;
2765    
2766   if (!strncmp(devicePtr, device, strlen(device))) {   while ((res = getline(&line, &len, grubConf)) != -1) {
2767      devicePtr += strlen(device);   if (line[res - 1] == '\n')
2768      while (isspace(*devicePtr) && ((devicePtr + 1) <= bounds))   line[res - 1] = '\0';
2769          devicePtr++;   else if (len > res)
2770     line[res] = '\0';
2771     else {
2772     line = realloc(line, res + 1);
2773     line[res] = '\0';
2774     }
2775    
2776      *bootPtr = strdup(devicePtr);   if (!strncmp(line, "setup", 5)) {
2777      break;   if (strstr(line, "--force-lba")) {
2778     *lbaPtr = 1;
2779     } else {
2780     *lbaPtr = 0;
2781     }
2782     dbgPrintf("lba: %i\n", *lbaPtr);
2783     break;
2784     }
2785   }   }
     }  
2786    
2787      free(line);   free(line);
2788      fclose(deviceMap);   fclose(grubConf);
2789      return 0;   return 0;
 }  
   
 int suseGrubConfGetBoot(const char * path, char ** bootPtr) {  
     char * grubDevice;  
   
     if (suseGrubConfGetInstallDevice(path, &grubDevice))  
  dbgPrintf("error looking for grub installation device\n");  
     else  
  dbgPrintf("grubby installation device: %s\n", grubDevice);  
   
     if (grubGetBootFromDeviceMap(grubDevice, bootPtr))  
  dbgPrintf("error looking for grub boot device\n");  
     else  
  dbgPrintf("grubby boot device: %s\n", *bootPtr);  
   
     free(grubDevice);  
     return 0;  
2790  }  }
2791    
2792  int parseSuseGrubConf(int * lbaPtr, char ** bootPtr) {  int suseGrubConfGetInstallDevice(const char *path, char **devicePtr)
2793      /*  {
2794       * This SuSE grub configuration file at this location is not your average   FILE *grubConf;
2795       * grub configuration file, but instead the grub commands used to setup   char *line = NULL;
2796       * grub on that system.   size_t res = 0, len = 0;
2797       */   char *lastParamPtr = NULL;
2798      const char * path;   char *secLastParamPtr = NULL;
2799      const static char default_path[] = "/etc/grub.conf";   char installDeviceNumber = '\0';
2800     char *bounds = NULL;
     if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)  
  path = default_path;  
   
     if (!isSuseGrubConf(path)) return 1;  
   
     if (lbaPtr) {  
         *lbaPtr = 0;  
         if (suseGrubConfGetLba(path, lbaPtr))  
             return 1;  
     }  
2801    
2802      if (bootPtr) {   if (!path)
2803          *bootPtr = NULL;   return 1;
2804          suseGrubConfGetBoot(path, bootPtr);   if (!devicePtr)
2805      }   return 1;
2806    
2807      return 0;   grubConf = fopen(path, "r");
2808  }   if (!grubConf)
2809     return 1;
2810    
2811  int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {   while ((res = getline(&line, &len, grubConf)) != -1) {
2812      FILE * in;   if (strncmp(line, "setup", 5))
2813      char buf[1024];   continue;
2814      char * chptr;  
2815      char * start;   if (line[res - 1] == '\n')
2816      char * param;   line[res - 1] = '\0';
2817     else if (len > res)
2818      in = fopen("/etc/conf.d/grub", "r");   line[res] = '\0';
2819      if (!in) return 1;   else {
2820     line = realloc(line, res + 1);
2821      if (lbaPtr) *lbaPtr = 0;   line[res] = '\0';
2822      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);  
     }  
2823    
2824      fclose(in);   lastParamPtr = bounds = line + res;
2825    
2826      return 0;   /* Last parameter in grub may be an optional IMAGE_DEVICE */
2827  }   while (!isspace(*lastParamPtr))
2828     lastParamPtr--;
2829     lastParamPtr++;
2830    
2831     secLastParamPtr = lastParamPtr - 2;
2832     dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2833    
2834     if (lastParamPtr + 3 > bounds) {
2835     dbgPrintf("lastParamPtr going over boundary");
2836     fclose(grubConf);
2837     free(line);
2838     return 1;
2839     }
2840     if (!strncmp(lastParamPtr, "(hd", 3))
2841     lastParamPtr += 3;
2842     dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);
2843    
2844     /*
2845     * Second last parameter will decide wether last parameter is
2846     * an IMAGE_DEVICE or INSTALL_DEVICE
2847     */
2848     while (!isspace(*secLastParamPtr))
2849     secLastParamPtr--;
2850     secLastParamPtr++;
2851    
2852     if (secLastParamPtr + 3 > bounds) {
2853     dbgPrintf("secLastParamPtr going over boundary");
2854     fclose(grubConf);
2855     free(line);
2856     return 1;
2857     }
2858     dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);
2859     if (!strncmp(secLastParamPtr, "(hd", 3)) {
2860     secLastParamPtr += 3;
2861     dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);
2862     installDeviceNumber = *secLastParamPtr;
2863     } else {
2864     installDeviceNumber = *lastParamPtr;
2865     }
2866    
2867  void dumpSysconfigGrub(void) {   *devicePtr = malloc(6);
2868      char * boot = NULL;   snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);
2869      int lba;   dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);
2870     fclose(grubConf);
2871      if (isSuseSystem()) {   free(line);
2872          if (parseSuseGrubConf(&lba, &boot)) {   return 0;
     free(boot);  
     return;  
  }  
     } else {  
         if (parseSysconfigGrub(&lba, &boot)) {  
     free(boot);  
     return;  
2873   }   }
     }  
2874    
2875      if (lba) printf("lba\n");   free(line);
2876      if (boot) {   fclose(grubConf);
2877   printf("boot=%s\n", boot);   return 1;
  free(boot);  
     }  
2878  }  }
2879    
2880  int displayInfo(struct grubConfig * config, char * kernel,  int grubGetBootFromDeviceMap(const char *device, char **bootPtr)
2881   const char * prefix) {  {
2882      int i = 0;   FILE *deviceMap;
2883      struct singleEntry * entry;   char *line = NULL;
2884      struct singleLine * line;   size_t res = 0, len = 0;
2885     char *devicePtr;
2886      entry = findEntryByPath(config, kernel, prefix, &i);   char *bounds = NULL;
2887      if (!entry) {   const char *path;
2888   fprintf(stderr, _("grubby: kernel not found\n"));   const static char default_path[] = "/boot/grub/device.map";
  return 1;  
     }  
2889    
2890      /* this is a horrible hack to support /etc/conf.d/grub; there must   if (!device)
2891         be a better way */   return 1;
2892      if (config->cfi == &grubConfigType) {   if (!bootPtr)
2893   dumpSysconfigGrub();   return 1;
     } else {  
  line = getLineByType(LT_BOOT, config->theLines);  
  if (line && line->numElements >= 1) {  
     printf("boot=%s\n", line->elements[1].item);  
  }  
2894    
2895   line = getLineByType(LT_LBA, config->theLines);   if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
2896   if (line) printf("lba\n");   path = default_path;
     }  
2897    
2898      displayEntry(entry, prefix, i);   dbgPrintf("opening grub device.map file from: %s\n", path);
2899     deviceMap = fopen(path, "r");
2900     if (!deviceMap)
2901     return 1;
2902    
2903      i++;   while ((res = getline(&line, &len, deviceMap)) != -1) {
2904      while ((entry = findEntryByPath(config, kernel, prefix, &i))) {   if (!strncmp(line, "#", 1))
2905   displayEntry(entry, prefix, i);   continue;
2906   i++;  
2907      }   if (line[res - 1] == '\n')
2908     line[res - 1] = '\0';
2909     else if (len > res)
2910     line[res] = '\0';
2911     else {
2912     line = realloc(line, res + 1);
2913     line[res] = '\0';
2914     }
2915    
2916      return 0;   devicePtr = line;
2917  }   bounds = line + res;
2918    
2919  struct singleLine * addLineTmpl(struct singleEntry * entry,   while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
2920   struct singleLine * tmplLine,   devicePtr++;
2921   struct singleLine * prevLine,   dbgPrintf("device: %s\n", devicePtr);
2922   const char * val,  
2923   struct configFileInfo * cfi)   if (!strncmp(devicePtr, device, strlen(device))) {
2924  {   devicePtr += strlen(device);
2925      struct singleLine * newLine = lineDup(tmplLine);   while (isspace(*devicePtr)
2926           && ((devicePtr + 1) <= bounds))
2927      if (isEfi && cfi == &grub2ConfigType) {   devicePtr++;
  enum lineType_e old = newLine->type;  
  newLine->type = preferredLineType(newLine->type, cfi);  
  if (old != newLine->type)  
     newLine->elements[0].item = getKeyByType(newLine->type, cfi);  
     }  
2928    
2929      if (val) {   *bootPtr = strdup(devicePtr);
2930   /* override the inherited value with our own.   break;
2931   * 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|LT_KERNEL_EFI|LT_INITRD_EFI|LT_KERNEL_16|LT_INITRD_16)) {  
     char * rootspec = getRootSpecifier(tmplLine->elements[1].item);  
     if (rootspec != NULL) {  
  free(newLine->elements[1].item);  
  newLine->elements[1].item =  
     sdupprintf("%s%s", rootspec, val);  
     }  
2932   }   }
     }  
   
     dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?  
       newLine->elements[0].item : "");  
   
     if (!entry->lines) {  
  /* first one on the list */  
  entry->lines = newLine;  
     } else if (prevLine) {  
  /* add after prevLine */  
  newLine->next = prevLine->next;  
  prevLine->next = newLine;  
     }  
2933    
2934      return newLine;   free(line);
2935     fclose(deviceMap);
2936     return 0;
2937  }  }
2938    
2939  /* val may be NULL */  int suseGrubConfGetBoot(const char *path, char **bootPtr)
2940  struct singleLine *  addLine(struct singleEntry * entry,  {
2941       struct configFileInfo * cfi,   char *grubDevice;
      enum lineType_e type, char * defaultIndent,  
      const char * val) {  
     struct singleLine * line, * prev;  
     struct keywordTypes * kw;  
     struct singleLine tmpl;  
   
     /* NB: This function shouldn't allocate items on the heap, rather on the  
      * stack since it calls addLineTmpl which will make copies.  
      */  
     if (type == LT_TITLE && cfi->titleBracketed) {  
  /* we're doing a bracketed title (zipl) */  
  tmpl.type = type;  
  tmpl.numElements = 1;  
  tmpl.elements = alloca(sizeof(*tmpl.elements));  
  tmpl.elements[0].item = alloca(strlen(val)+3);  
  sprintf(tmpl.elements[0].item, "[%s]", val);  
  tmpl.elements[0].indent = "";  
  val = NULL;  
     } else 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 = "";  
  }  
     }  
   
     /* 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;  
     }  
2942    
2943      struct singleLine *menuEntry;   if (suseGrubConfGetInstallDevice(path, &grubDevice))
2944      menuEntry = getLineByType(LT_MENUENTRY, entry->lines);   dbgPrintf("error looking for grub installation device\n");
     if (tmpl.type == LT_ENTRY_END) {  
  if (menuEntry)  
     tmpl.indent = menuEntry->indent;  
2945   else   else
2946      tmpl.indent = defaultIndent ?: "";   dbgPrintf("grubby installation device: %s\n", grubDevice);
2947      } else if (tmpl.type != LT_MENUENTRY) {  
2948   if (menuEntry)   if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
2949      tmpl.indent = "\t";   dbgPrintf("error looking for grub boot device\n");
  else if (prev == entry->lines)  
     tmpl.indent = defaultIndent ?: "";  
2950   else   else
2951      tmpl.indent = prev->indent;   dbgPrintf("grubby boot device: %s\n", *bootPtr);
     }  
2952    
2953      return addLineTmpl(entry, &tmpl, prev, val, cfi);   free(grubDevice);
2954     return 0;
2955  }  }
2956    
2957  void removeLine(struct singleEntry * entry, struct singleLine * line) {  int parseSuseGrubConf(int *lbaPtr, char **bootPtr)
2958      struct singleLine * prev;  {
2959      int i;   /*
2960     * This SuSE grub configuration file at this location is not your
2961     * 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      for (i = 0; i < line->numElements; i++) {   if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
2968   free(line->elements[i].item);   path = default_path;
  free(line->elements[i].indent);  
     }  
     free(line->elements);  
     free(line->indent);  
2969    
2970      if (line == entry->lines) {   if (!isSuseGrubConf(path))
2971   entry->lines = line->next;   return 1;
     } else {  
  prev = entry->lines;  
  while (prev->next != line) prev = prev->next;  
  prev->next = line->next;  
     }  
2972    
2973      free(line);   if (lbaPtr) {
2974  }   *lbaPtr = 0;
2975     if (suseGrubConfGetLba(path, lbaPtr))
2976     return 1;
2977     }
2978    
2979  static void requote(struct singleLine *tmplLine, struct configFileInfo * cfi)   if (bootPtr) {
2980  {   *bootPtr = NULL;
2981      struct singleLine newLine = {   suseGrubConfGetBoot(path, bootPtr);
  .indent = tmplLine->indent,  
  .type = tmplLine->type,  
  .next = tmplLine->next,  
     };  
     int firstQuotedItem = -1;  
     int quoteLen = 0;  
     int j;  
     int element = 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;  
     }  
2982   }   }
     }  
     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;  
     }  
2983    
2984      line->elements = realloc(line->elements, (line->numElements + 1) *   return 0;
2985       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] = ' ';  
     }  
2986    
2987      if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') {  int parseSysconfigGrub(int *lbaPtr, char **bootPtr)
2988   /* move the end-of-line forward */  {
2989   line->elements[insertHere].indent =   FILE *in;
2990      line->elements[insertHere-1].indent;   char buf[1024];
2991   line->elements[insertHere-1].indent = strdup(indent);   char *chptr;
2992      } else {   char *start;
2993   line->elements[insertHere].indent = strdup(indent);   char *param;
     }  
2994    
2995      line->numElements++;   in = fopen("/etc/sysconfig/grub", "r");
2996     if (!in)
2997     return 1;
2998    
2999      dbgPrintf("insertElement(%s, '%s%s', %d)\n",   if (lbaPtr)
3000        line->elements[0].item,   *lbaPtr = 0;
3001        line->elements[insertHere].item,   if (bootPtr)
3002        line->elements[insertHere].indent,   *bootPtr = NULL;
3003        insertHere);  
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  static void removeElement(struct singleLine * line, int removeHere) {   chptr = param;
3030      int i;   while (*chptr && !isspace(*chptr))
3031     chptr++;
3032     *chptr = '\0';
3033    
3034     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      /* sanity check */   fclose(in);
     if (removeHere >= line->numElements) return;  
3041    
3042      dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,   return 0;
3043        removeHere, line->elements[removeHere].item);  }
3044    
3045      free(line->elements[removeHere].item);  void dumpSysconfigGrub(void)
3046    {
3047     char *boot = NULL;
3048     int lba;
3049    
3050      if (removeHere > 1) {   if (isSuseSystem()) {
3051   /* previous argument gets this argument's post-indentation */   if (parseSuseGrubConf(&lba, &boot)) {
3052   free(line->elements[removeHere-1].indent);   free(boot);
3053   line->elements[removeHere-1].indent =   return;
3054      line->elements[removeHere].indent;   }
3055      } else {   } else {
3056   free(line->elements[removeHere].indent);   if (parseSysconfigGrub(&lba, &boot)) {
3057      }   free(boot);
3058     return;
3059     }
3060     }
3061    
3062      /* now collapse the array, but don't bother to realloc smaller */   if (lba)
3063      for (i = removeHere; i < line->numElements - 1; i++)   printf("lba\n");
3064   line->elements[i] = line->elements[i + 1];   if (boot) {
3065     printf("boot=%s\n", boot);
3066      line->numElements--;   free(boot);
 }  
   
 int argMatch(const char * one, const char * two) {  
     char * first, * second;  
     char * chptr;  
   
     first = strcpy(alloca(strlen(one) + 1), one);  
     second = strcpy(alloca(strlen(two) + 1), two);  
   
     chptr = strchr(first, '=');  
     if (chptr) *chptr = '\0';  
   
     chptr = strchr(second, '=');  
     if (chptr) *chptr = '\0';  
   
     return strcmp(first, second);  
 }  
   
 int updateActualImage(struct grubConfig * cfg, const char * image,  
                       const char * prefix, const char * addArgs,  
                       const char * removeArgs, int multibootArgs) {  
     struct singleEntry * entry;  
     struct singleLine * line, * rootLine;  
     int index = 0;  
     int i, k;  
     const char ** newArgs, ** oldArgs;  
     const char ** arg;  
     int useKernelArgs, useRoot;  
     int firstElement;  
     int *usedElements;  
     int doreplace;  
   
     if (!image) return 0;  
   
     if (!addArgs) {  
  newArgs = malloc(sizeof(*newArgs));  
  *newArgs = NULL;  
     } else {  
  if (poptParseArgvString(addArgs, NULL, &newArgs)) {  
     fprintf(stderr,  
     _("grubby: error separating arguments '%s'\n"), addArgs);  
     return 1;  
3067   }   }
3068      }  }
3069    
3070      if (!removeArgs) {  int displayInfo(struct grubConfig *config, char *kernel, const char *prefix)
3071   oldArgs = malloc(sizeof(*oldArgs));  {
3072   *oldArgs = NULL;   int i = 0;
3073      } else {   struct singleEntry *entry;
3074   if (poptParseArgvString(removeArgs, NULL, &oldArgs)) {   struct singleLine *line;
3075      fprintf(stderr,  
3076      _("grubby: error separating arguments '%s'\n"), removeArgs);   entry = findEntryByPath(config, kernel, prefix, &i);
3077              free(newArgs);   if (!entry) {
3078      return 1;   fprintf(stderr, _("grubby: kernel not found\n"));
3079     return 1;
3080   }   }
     }  
3081    
3082     /* this is a horrible hack to support /etc/sysconfig/grub; there must
3083       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      useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)   line = getLineByType(LT_LBA, config->theLines);
3093       && (!multibootArgs || cfg->cfi->mbConcatArgs));   if (line)
3094     printf("lba\n");
3095     }
3096    
3097      useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)   displayEntry(entry, prefix, i);
        && !multibootArgs);  
3098    
3099      for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {   i++;
3100     while ((entry = findEntryByPath(config, kernel, prefix, &i))) {
3101     displayEntry(entry, prefix, i);
3102     i++;
3103     }
3104    
3105   if (multibootArgs && !entry->multiboot)   return 0;
3106      continue;  }
3107    
3108   /* Determine where to put the args.  If this config supports  struct singleLine *addLineTmpl(struct singleEntry *entry,
3109   * LT_KERNELARGS, use that.  Otherwise use         struct singleLine *tmplLine,
3110   * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.         struct singleLine *prevLine,
3111   */         const char *val, struct configFileInfo *cfi)
3112   if (useKernelArgs) {  {
3113      line = getLineByType(LT_KERNELARGS, entry->lines);   struct singleLine *newLine = lineDup(tmplLine);
     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;  
3114    
3115   } else {   if (isEfi && cfi == &grub2ConfigType) {
3116      line = getLineByType(LT_KERNEL|LT_MBMODULE|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);   enum lineType_e old = newLine->type;
3117      if (!line) {   newLine->type = preferredLineType(newLine->type, cfi);
3118   /* no LT_KERNEL or LT_MBMODULE in this entry? */   if (old != newLine->type)
3119   continue;   newLine->elements[0].item =
3120      }      getKeyByType(newLine->type, cfi);
     firstElement = 2;  
3121   }   }
3122    
3123   /* handle the elilo case which does:   if (val) {
3124   *   append="hypervisor args -- kernel args"   /* override the inherited value with our own.
3125   */   * This is a little weak because it only applies to elements[1]
  if (entry->multiboot && cfg->cfi->mbConcatArgs) {  
     /* this is a multiboot entry, make sure there's  
      * -- on the args line  
      */  
     for (i = firstElement; i < line->numElements; i++) {  
  if (!strcmp(line->elements[i].item, "--"))  
     break;  
     }  
     if (i == line->numElements) {  
  /* assume all existing args are kernel args,  
  * prepend -- to make it official  
3126   */   */
3127   insertElement(line, "--", firstElement, cfg->cfi);   if (newLine->numElements > 1)
3128   i = firstElement;   removeElement(newLine, 1);
3129      }   insertElement(newLine, val, 1, cfi);
3130      if (!multibootArgs) {  
3131   /* kernel args start after the -- */   /* but try to keep the rootspec from the template... sigh */
3132   firstElement = i + 1;   if (tmplLine->
3133      }      type & (LT_HYPER | LT_KERNEL | LT_MBMODULE | LT_INITRD |
3134   } else if (cfg->cfi->mbConcatArgs) {      LT_KERNEL_EFI | LT_INITRD_EFI | LT_KERNEL_16 |
3135      /* this is a non-multiboot entry, remove hyper args */      LT_INITRD_16)) {
3136      for (i = firstElement; i < line->numElements; i++) {   char *rootspec =
3137   if (!strcmp(line->elements[i].item, "--"))      getRootSpecifier(tmplLine->elements[1].item);
3138      break;   if (rootspec != NULL) {
3139      }   free(newLine->elements[1].item);
3140      if (i < line->numElements) {   newLine->elements[1].item =
3141   /* remove args up to -- */      sdupprintf("%s%s", rootspec, val);
3142   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);  
  } else {  
     rootLine = addLine(entry, cfg->cfi, LT_ROOT,  
        cfg->secondaryIndent, *arg + 5);  
3143   }   }
     }  
   
     else {  
  /* insert/append */  
  insertElement(line, *arg, i, cfg->cfi);  
  usedElements = realloc(usedElements, line->numElements *  
        sizeof(*usedElements));  
  memmove(&usedElements[i + 1], &usedElements[i],  
  line->numElements - i - 1);  
  usedElements[i] = 1;  
   
  /* if we updated a root= here even though there is a  
    LT_ROOT available we need to remove the LT_ROOT entry  
    (this will happen if we switch from a device to a label) */  
  if (useRoot && !strncmp(*arg, "root=", 5)) {  
     rootLine = 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);  
3144   }   }
     }  
3145    
3146      free(newArgs);   dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
3147      free(oldArgs);    newLine->elements[0].item : "");
3148    
3149      return 0;   if (!entry->lines) {
3150     /* first one on the list */
3151     entry->lines = newLine;
3152     } else if (prevLine) {
3153     /* add after prevLine */
3154     newLine->next = prevLine->next;
3155     prevLine->next = newLine;
3156     }
3157    
3158     return newLine;
3159  }  }
3160    
3161  int updateImage(struct grubConfig * cfg, const char * image,  /* val may be NULL */
3162                  const char * prefix, const char * addArgs,  struct singleLine *addLine(struct singleEntry *entry,
3163                  const char * removeArgs,     struct configFileInfo *cfi,
3164                  const char * addMBArgs, const char * removeMBArgs) {     enum lineType_e type, char *defaultIndent,
3165      int rc = 0;     const char *val)
3166    {
3167      if (!image) return rc;   struct singleLine *line, *prev;
3168     struct keywordTypes *kw;
3169      /* update the main args first... */   struct singleLine tmpl;
3170      if (addArgs || removeArgs)  
3171          rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs, 0);   /* NB: This function shouldn't allocate items on the heap, rather on
3172      if (rc) return rc;   * the stack since it calls addLineTmpl which will make copies.
3173     */
3174      /* and now any multiboot args */   if (type == LT_TITLE && cfi->titleBracketed) {
3175      if (addMBArgs || removeMBArgs)   /* we're doing a bracketed title (zipl) */
3176          rc = updateActualImage(cfg, image, prefix, addMBArgs, removeMBArgs, 1);   tmpl.type = type;
3177      return rc;   tmpl.numElements = 1;
3178  }   tmpl.elements = alloca(sizeof(*tmpl.elements));
3179     tmpl.elements[0].item = alloca(strlen(val) + 3);
3180  int addMBInitrd(struct grubConfig * cfg, const char *newMBKernel,   sprintf(tmpl.elements[0].item, "[%s]", val);
3181   const char * image, const char * prefix, const char * initrd,   tmpl.elements[0].indent = "";
3182   const char * title) {   val = NULL;
3183      struct singleEntry * entry;   } else if (type == LT_MENUENTRY) {
3184      struct singleLine * line, * kernelLine, *endLine = NULL;   char *lineend = "--class gnu-linux --class gnu --class os {";
3185      int index = 0;   if (!val) {
3186     fprintf(stderr,
3187      if (!image) return 0;   "Line type LT_MENUENTRY requires a value\n");
3188     abort();
3189      for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {   }
3190          kernelLine = getLineByType(LT_MBMODULE, entry->lines);   kw = getKeywordByType(type, cfi);
3191          if (!kernelLine) continue;   if (!kw) {
3192     fprintf(stderr,
3193   /* if title is supplied, the entry's title must match it. */   "Looking up keyword for unknown type %d\n",
3194   if (title) {   type);
3195      char *linetitle;   abort();
3196     }
3197      line = getLineByType(LT_TITLE|LT_MENUENTRY, entry->lines);   tmpl.indent = "";
3198      if (!line)   tmpl.type = type;
3199   continue;   tmpl.numElements = 3;
3200     tmpl.elements =
3201      linetitle = extractTitle(cfg, line);      alloca(sizeof(*tmpl.elements) * tmpl.numElements);
3202      if (!linetitle)   tmpl.elements[0].item = kw->key;
3203   continue;   tmpl.elements[0].indent = alloca(2);
3204      if (strcmp(title, linetitle)) {   sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
3205   free(linetitle);   tmpl.elements[1].item = (char *)val;
3206   continue;   tmpl.elements[1].indent = alloca(2);
3207      }   sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
3208      free(linetitle);   tmpl.elements[2].item = alloca(strlen(lineend) + 1);
3209   }   strcpy(tmpl.elements[2].item, lineend);
3210     tmpl.elements[2].indent = "";
3211          if (prefix) {   } else {
3212              int prefixLen = strlen(prefix);   kw = getKeywordByType(type, cfi);
3213              if (!strncmp(initrd, prefix, prefixLen))   if (!kw) {
3214                  initrd += prefixLen;   fprintf(stderr,
3215          }   "Looking up keyword for unknown type %d\n",
3216   endLine = getLineByType(LT_ENTRY_END, entry->lines);   type);
3217   if (endLine)   abort();
3218      removeLine(entry, endLine);   }
3219          line = addLine(entry, cfg->cfi, preferredLineType(LT_MBMODULE,cfg->cfi),   tmpl.type = type;
3220   kernelLine->indent, initrd);   tmpl.numElements = val ? 2 : 1;
3221          if (!line)   tmpl.elements =
3222      return 1;      alloca(sizeof(*tmpl.elements) * tmpl.numElements);
3223   if (endLine) {   tmpl.elements[0].item = kw->key;
3224      line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);   tmpl.elements[0].indent = alloca(2);
3225              if (!line)   sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
3226   return 1;   if (val) {
3227     tmpl.elements[1].item = (char *)val;
3228     tmpl.elements[1].indent = "";
3229     }
3230   }   }
3231    
3232          break;   /* 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 0;   return addLineTmpl(entry, &tmpl, prev, val, cfi);
3262  }  }
3263    
3264  int updateInitrd(struct grubConfig * cfg, const char * image,  void removeLine(struct singleEntry *entry, struct singleLine *line)
3265                   const char * prefix, const char * initrd, const char * title) {  {
3266      struct singleEntry * entry;   struct singleLine *prev;
3267      struct singleLine * line, * kernelLine, *endLine = NULL;   int i;
3268      int index = 0;  
3269     for (i = 0; i < line->numElements; i++) {
3270      if (!image) return 0;   free(line->elements[i].item);
3271     free(line->elements[i].indent);
     for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {  
         kernelLine = getLineByType(LT_KERNEL|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);  
         if (!kernelLine) continue;  
   
  /* if title is supplied, the entry's title must match it. */  
  if (title) {  
     char *linetitle;  
   
     line = getLineByType(LT_TITLE|LT_MENUENTRY, entry->lines);  
     if (!line)  
  continue;  
   
     linetitle = extractTitle(cfg, line);  
     if (!linetitle)  
  continue;  
     if (strcmp(title, linetitle)) {  
  free(linetitle);  
  continue;  
     }  
     free(linetitle);  
  }  
   
         line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, 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);  
  enum lineType_e lt;  
  switch(kernelLine->type) {  
     case LT_KERNEL:  
         lt = LT_INITRD;  
  break;  
     case LT_KERNEL_EFI:  
         lt = LT_INITRD_EFI;  
  break;  
     case LT_KERNEL_16:  
         lt = LT_INITRD_16;  
  break;  
     default:  
         lt = preferredLineType(LT_INITRD, cfg->cfi);  
  }  
         line = addLine(entry, cfg->cfi, lt, kernelLine->indent, initrd);  
         if (!line)  
     return 1;  
  if (endLine) {  
     line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);  
             if (!line)  
  return 1;  
3272   }   }
3273     free(line->elements);
3274     free(line->indent);
3275    
3276          break;   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      return 0;   free(line);
3286  }  }
3287    
3288  int checkDeviceBootloader(const char * device, const unsigned char * boot) {  static void requote(struct singleLine *tmplLine, struct configFileInfo *cfi)
3289      int fd;  {
3290      unsigned char bootSect[512];   struct singleLine newLine = {
3291      int offset;   .indent = tmplLine->indent,
3292     .type = tmplLine->type,
3293      fd = open(device, O_RDONLY);   .next = tmplLine->next,
3294      if (fd < 0) {   };
3295   fprintf(stderr, _("grubby: unable to open %s: %s\n"),   int firstQuotedItem = -1;
3296   device, strerror(errno));   int quoteLen = 0;
3297   return 1;   int j;
3298      }   int element = 0;
3299     char *c;
3300    
3301      if (read(fd, bootSect, 512) != 512) {   c = malloc(strlen(tmplLine->elements[0].item) + 1);
3302   fprintf(stderr, _("grubby: unable to read %s: %s\n"),   strcpy(c, tmplLine->elements[0].item);
3303   device, strerror(errno));   insertElement(&newLine, c, element++, cfi);
3304   return 1;   free(c);
3305      }   c = NULL;
3306      close(fd);  
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      /* first three bytes should match, a jmp short should be in there */   firstQuotedItem = -1;
3338      if (memcmp(boot, bootSect, 3))   quoteLen = 0;
3339   return 0;   }
3340     }
3341     }
3342     while (tmplLine->numElements)
3343     removeElement(tmplLine, 0);
3344     if (tmplLine->elements)
3345     free(tmplLine->elements);
3346    
3347     tmplLine->numElements = newLine.numElements;
3348     tmplLine->elements = newLine.elements;
3349    }
3350    
3351      if (boot[1] == JMP_SHORT_OPCODE) {  static void insertElement(struct singleLine *line,
3352   offset = boot[2] + 2;    const char *item, int insertHere,
3353      } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {    struct configFileInfo *cfi)
3354   offset = (boot[3] << 8) + boot[2] + 2;  {
3355      } else if (boot[0] == JMP_SHORT_OPCODE) {   struct keywordTypes *kw;
3356        offset = boot[1] + 2;   char indent[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;  
     }  
3357    
3358      if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))   /* sanity check */
3359   return 0;   if (insertHere > line->numElements) {
3360     dbgPrintf
3361        ("insertElement() adjusting insertHere from %d to %d\n",
3362         insertHere, line->numElements);
3363     insertHere = line->numElements;
3364     }
3365    
3366     line->elements = realloc(line->elements, (line->numElements + 1) *
3367     sizeof(*line->elements));
3368     memmove(&line->elements[insertHere + 1],
3369     &line->elements[insertHere],
3370     (line->numElements - insertHere) * sizeof(*line->elements));
3371     line->elements[insertHere].item = strdup(item);
3372    
3373     kw = getKeywordByType(line->type, cfi);
3374    
3375     if (line->numElements == 0) {
3376     indent[0] = '\0';
3377     } else if (insertHere == 0) {
3378     indent[0] = kw->nextChar;
3379     } else if (kw->separatorChar != '\0') {
3380     indent[0] = kw->separatorChar;
3381     } else {
3382     indent[0] = ' ';
3383     }
3384    
3385      return 2;   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     line->numElements++;
3395    
3396     dbgPrintf("insertElement(%s, '%s%s', %d)\n",
3397      line->elements[0].item,
3398      line->elements[insertHere].item,
3399      line->elements[insertHere].indent, insertHere);
3400  }  }
3401    
3402  int checkLiloOnRaid(char * mdDev, const unsigned char * boot) {  static void removeElement(struct singleLine *line, int removeHere)
3403      int fd;  {
3404      char buf[65536];   int i;
     char * end;  
     char * chptr;  
     char * chptr2;  
     int rc;  
3405    
3406      /* it's on raid; we need to parse /proc/mdstat and check all of the   /* sanity check */
3407         *raw* devices listed in there */   if (removeHere >= line->numElements)
3408     return;
3409    
3410     dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
3411      removeHere, line->elements[removeHere].item);
3412    
3413     free(line->elements[removeHere].item);
3414    
3415     if (removeHere > 1) {
3416     /* previous argument gets this argument's post-indentation */
3417     free(line->elements[removeHere - 1].indent);
3418     line->elements[removeHere - 1].indent =
3419        line->elements[removeHere].indent;
3420     } else {
3421     free(line->elements[removeHere].indent);
3422     }
3423    
3424      if (!strncmp(mdDev, "/dev/", 5))   /* now collapse the array, but don't bother to realloc smaller */
3425   mdDev += 5;   for (i = removeHere; i < line->numElements - 1; i++)
3426     line->elements[i] = line->elements[i + 1];
3427    
3428      if ((fd = open("/proc/mdstat", O_RDONLY)) < 0) {   line->numElements--;
3429   fprintf(stderr, _("grubby: failed to open /proc/mdstat: %s\n"),  }
  strerror(errno));  
  return 2;  
     }  
3430    
3431      rc = read(fd, buf, sizeof(buf) - 1);  int argMatch(const char *one, const char *two)
3432      if (rc < 0 || rc == (sizeof(buf) - 1)) {  {
3433   fprintf(stderr, _("grubby: failed to read /proc/mdstat: %s\n"),   char *first, *second;
3434   strerror(errno));   char *chptr;
  close(fd);  
  return 2;  
     }  
     close(fd);  
     buf[rc] = '\0';  
3435    
3436      chptr = buf;   first = strcpy(alloca(strlen(one) + 1), one);
3437      while (*chptr) {   second = strcpy(alloca(strlen(two) + 1), two);
  end = strchr(chptr, '\n');  
  if (!end) break;  
  *end = '\0';  
3438    
3439   if (!strncmp(chptr, mdDev, strlen(mdDev)) &&   chptr = strchr(first, '=');
3440      chptr[strlen(mdDev)] == ' ') {   if (chptr)
3441     *chptr = '\0';
3442    
3443      /* found the device */   chptr = strchr(second, '=');
3444      while (*chptr && *chptr != ':') chptr++;   if (chptr)
3445      chptr++;   *chptr = '\0';
     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++;  
     }  
3446    
3447      /*  we're good to go */   return strcmp(first, second);
3448      return 2;  }
3449    
3450    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   chptr = end + 1;   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      fprintf(stderr,   useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
3494      _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),   && (!multibootArgs || cfg->cfi->mbConcatArgs));
     mdDev);  
     return 0;  
 }  
3495    
3496  int checkForLilo(struct grubConfig * config) {   useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
3497      int fd;     && !multibootArgs);
     unsigned char boot[512];  
     struct singleLine * line;  
   
     for (line = config->theLines; line; line = line->next)  
  if (line->type == LT_BOOT) break;  
   
     if (!line) {  
  fprintf(stderr,  
  _("grubby: no boot line found in lilo configuration\n"));  
  return 1;  
     }  
3498    
3499      if (line->numElements != 2) return 1;   for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3500    
3501      fd = open("/boot/boot.b", O_RDONLY);   if (multibootArgs && !entry->multiboot)
3502      if (fd < 0) {   continue;
  fprintf(stderr, _("grubby: unable to open %s: %s\n"),  
  "/boot/boot.b", strerror(errno));  
  return 1;  
     }  
3503    
3504      if (read(fd, boot, 512) != 512) {   /* Determine where to put the args.  If this config supports
3505   fprintf(stderr, _("grubby: unable to read %s: %s\n"),   * LT_KERNELARGS, use that.  Otherwise use
3506   "/boot/boot.b", strerror(errno));   * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
3507   return 1;   */
3508      }   if (useKernelArgs) {
3509      close(fd);   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      if (!strncmp("/dev/md", line->elements[1].item, 7))   } else if (multibootArgs) {
3518   return checkLiloOnRaid(line->elements[1].item, boot);   line = getLineByType(LT_HYPER, entry->lines);
3519     if (!line) {
3520     /* a multiboot entry without LT_HYPER? */
3521     continue;
3522     }
3523     firstElement = 2;
3524    
3525      return checkDeviceBootloader(line->elements[1].item, boot);   } 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  int checkForGrub2(struct grubConfig * config) {   /* handle the elilo case which does:
3538      if (!access("/etc/grub.d/", R_OK))   *   append="hypervisor args -- kernel args"
3539   return 2;   */
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      return 1;   for (arg = oldArgs; *arg; arg++) {
3645     for (i = firstElement; i < line->numElements; i++) {
3646     if (multibootArgs && cfg->cfi->mbConcatArgs &&
3647        !strcmp(line->elements[i].item, "--"))
3648     /* reached the end of hyper args, stop here */
3649     break;
3650     if (!argMatch(line->elements[i].item, *arg)) {
3651     removeElement(line, i);
3652     break;
3653     }
3654     }
3655     /* handle removing LT_ROOT line too */
3656     if (useRoot && !strncmp(*arg, "root=", 5)) {
3657     rootLine = getLineByType(LT_ROOT, entry->lines);
3658     if (rootLine)
3659     removeLine(entry, rootLine);
3660     }
3661     }
3662    
3663     if (line->numElements == 1) {
3664     /* don't need the line at all (note it has to be a
3665       LT_KERNELARGS for this to happen */
3666     removeLine(entry, line);
3667     }
3668     }
3669    
3670     free(newArgs);
3671     free(oldArgs);
3672    
3673     return 0;
3674  }  }
3675    
3676  int checkForGrub(struct grubConfig * config) {  int updateImage(struct grubConfig *cfg, const char *image,
3677      int fd;   const char *prefix, const char *addArgs,
3678      unsigned char bootSect[512];   const char *removeArgs,
3679      char * boot;   const char *addMBArgs, const char *removeMBArgs)
3680      int onSuse = isSuseSystem();  {
3681     int rc = 0;
3682    
3683     if (!image)
3684     return rc;
3685    
3686      if (onSuse) {   /* update the main args first... */
3687   if (parseSuseGrubConf(NULL, &boot))   if (addArgs || removeArgs)
3688      return 0;   rc = updateActualImage(cfg, image, prefix, addArgs, removeArgs,
3689      } else {         0);
3690   if (parseSysconfigGrub(NULL, &boot))   if (rc)
3691      return 0;   return rc;
3692      }  
3693     /* and now any multiboot args */
3694     if (addMBArgs || removeMBArgs)
3695     rc = updateActualImage(cfg, image, prefix, addMBArgs,
3696           removeMBArgs, 1);
3697     return rc;
3698    }
3699    
3700    int addMBInitrd(struct grubConfig *cfg, const char *newMBKernel,
3701     const char *image, const char *prefix, const char *initrd,
3702     const char *title)
3703    {
3704     struct singleEntry *entry;
3705     struct singleLine *line, *kernelLine, *endLine = NULL;
3706     int index = 0;
3707    
3708     if (!image)
3709     return 0;
3710    
3711     for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3712     kernelLine = getLineByType(LT_MBMODULE, entry->lines);
3713     if (!kernelLine)
3714     continue;
3715    
3716     /* if title is supplied, the entry's title must match it. */
3717     if (title) {
3718     char *linetitle;
3719    
3720     line =
3721        getLineByType(LT_TITLE | LT_MENUENTRY,
3722      entry->lines);
3723     if (!line)
3724     continue;
3725    
3726     linetitle = extractTitle(cfg, line);
3727     if (!linetitle)
3728     continue;
3729     if (strcmp(title, linetitle)) {
3730     free(linetitle);
3731     continue;
3732     }
3733     free(linetitle);
3734     }
3735    
3736     if (prefix) {
3737     int prefixLen = strlen(prefix);
3738     if (!strncmp(initrd, prefix, prefixLen))
3739     initrd += prefixLen;
3740     }
3741     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3742     if (endLine)
3743     removeLine(entry, endLine);
3744     line =
3745        addLine(entry, cfg->cfi,
3746        preferredLineType(LT_MBMODULE, cfg->cfi),
3747        kernelLine->indent, initrd);
3748     if (!line)
3749     return 1;
3750     if (endLine) {
3751     line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3752     if (!line)
3753     return 1;
3754     }
3755    
3756     break;
3757     }
3758    
     /* assume grub is not installed -- not an error condition */  
     if (!boot)  
3759   return 0;   return 0;
3760    }
3761    
3762    int updateInitrd(struct grubConfig *cfg, const char *image,
3763     const char *prefix, const char *initrd, const char *title)
3764    {
3765     struct singleEntry *entry;
3766     struct singleLine *line, *kernelLine, *endLine = NULL;
3767     int index = 0;
3768    
3769     if (!image)
3770     return 0;
3771    
3772     for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3773     kernelLine =
3774        getLineByType(LT_KERNEL | LT_KERNEL_EFI | LT_KERNEL_16,
3775      entry->lines);
3776     if (!kernelLine)
3777     continue;
3778    
3779     /* if title is supplied, the entry's title must match it. */
3780     if (title) {
3781     char *linetitle;
3782    
3783     line =
3784        getLineByType(LT_TITLE | LT_MENUENTRY,
3785      entry->lines);
3786     if (!line)
3787     continue;
3788    
3789     linetitle = extractTitle(cfg, line);
3790     if (!linetitle)
3791     continue;
3792     if (strcmp(title, linetitle)) {
3793     free(linetitle);
3794     continue;
3795     }
3796     free(linetitle);
3797     }
3798    
3799     line =
3800        getLineByType(LT_INITRD | LT_INITRD_EFI | LT_INITRD_16,
3801      entry->lines);
3802     if (line)
3803     removeLine(entry, line);
3804     if (prefix) {
3805     int prefixLen = strlen(prefix);
3806     if (!strncmp(initrd, prefix, prefixLen))
3807     initrd += prefixLen;
3808     }
3809     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3810     if (endLine)
3811     removeLine(entry, endLine);
3812     enum lineType_e lt;
3813     switch (kernelLine->type) {
3814     case LT_KERNEL:
3815     lt = LT_INITRD;
3816     break;
3817     case LT_KERNEL_EFI:
3818     lt = LT_INITRD_EFI;
3819     break;
3820     case LT_KERNEL_16:
3821     lt = LT_INITRD_16;
3822     break;
3823     default:
3824     lt = preferredLineType(LT_INITRD, cfg->cfi);
3825     }
3826     line = addLine(entry, cfg->cfi, lt, kernelLine->indent, initrd);
3827     if (!line)
3828     return 1;
3829     if (endLine) {
3830     line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3831     if (!line)
3832     return 1;
3833     }
3834    
3835     break;
3836     }
3837    
     fd = open("/boot/grub/stage1", O_RDONLY);  
     if (fd < 0)  
  /* this doesn't exist if grub hasn't been installed */  
3838   return 0;   return 0;
3839    }
3840    
3841      if (read(fd, bootSect, 512) != 512) {  int checkDeviceBootloader(const char *device, const unsigned char *boot)
3842   fprintf(stderr, _("grubby: unable to read %s: %s\n"),  {
3843   "/boot/grub/stage1", strerror(errno));   int fd;
3844   close(fd);   unsigned char bootSect[512];
3845   return 1;   int offset;
3846      }  
3847      close(fd);   fd = open(device, O_RDONLY);
3848     if (fd < 0) {
3849     fprintf(stderr, _("grubby: unable to open %s: %s\n"),
3850     device, strerror(errno));
3851     return 1;
3852     }
3853    
3854      /* The more elaborate checks do not work on SuSE. The checks done   if (read(fd, bootSect, 512) != 512) {
3855       * seem to be reasonble (at least for now), so just return success   fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3856       */   device, strerror(errno));
3857      if (onSuse)   return 1;
3858   return 2;   }
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      return checkDeviceBootloader(boot, bootSect);   if (memcmp(boot + offset, bootSect + offset, CODE_SEG_SIZE))
3888     return 0;
3889    
3890     return 2;
3891  }  }
3892    
3893  int checkForExtLinux(struct grubConfig * config) {  int checkLiloOnRaid(char *mdDev, const unsigned char *boot)
3894      int fd;  {
3895      unsigned char bootSect[512];   int fd;
3896      char * boot;   char buf[65536];
3897      char executable[] = "/boot/extlinux/extlinux";   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      printf("entered: checkForExtLinux()\n");   chptr = buf;
3925     while (*chptr) {
3926     end = strchr(chptr, '\n');
3927     if (!end)
3928     break;
3929     *end = '\0';
3930    
3931      if (parseSysconfigGrub(NULL, &boot))   if (!strncmp(chptr, mdDev, strlen(mdDev)) &&
3932   return 0;      chptr[strlen(mdDev)] == ' ') {
3933    
3934      /* assume grub is not installed -- not an error condition */   /* found the device */
3935      if (!boot)   while (*chptr && *chptr != ':')
3936   return 0;   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      fd = open(executable, O_RDONLY);   /* yank off the numbers at the end */
3962      if (fd < 0)   chptr2--;
3963   /* this doesn't exist if grub hasn't been installed */   while (isdigit(*chptr2) && chptr2 > chptr)
3964   return 0;   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      if (read(fd, bootSect, 512) != 512) {   chptr = chptr2 + 1;
3979   fprintf(stderr, _("grubby: unable to read %s: %s\n"),   /* skip the [11] bit */
3980   executable, strerror(errno));   while (*chptr && !isspace(*chptr))
3981   return 1;   chptr++;
3982      }   /* and move to the next one */
3983      close(fd);   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      return checkDeviceBootloader(boot, bootSect);   fprintf(stderr,
3995     _("grubby: raid device /dev/%s not found in /proc/mdstat\n"),
3996     mdDev);
3997     return 0;
3998  }  }
3999    
4000  int checkForYaboot(struct grubConfig * config) {  int checkForLilo(struct grubConfig *config)
4001      /*  {
4002       * This is a simplistic check that we consider good enough for own puporses   int fd;
4003       *   unsigned char boot[512];
4004       * If we were to properly check if yaboot is *installed* we'd need to:   struct singleLine *line;
4005       * 1) get the system boot device (LT_BOOT)  
4006       * 2) considering it's a raw filesystem, check if the yaboot binary matches   for (line = config->theLines; line; line = line->next)
4007       *    the content on the boot device   if (line->type == LT_BOOT)
4008       * 3) if not, copy the binary to a temporary file and run "addnote" on it   break;
4009       * 4) check again if binary and boot device contents match  
4010       */   if (!line) {
4011      if (!access("/etc/yaboot.conf", R_OK))   fprintf(stderr,
4012   return 2;   _
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      return 1;   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 checkForElilo(struct grubConfig * config) {  int checkForGrub2(struct grubConfig *config)
4041      if (!access("/etc/elilo.conf", R_OK))  {
4042   return 2;   if (!access("/etc/grub.d/", R_OK))
4043     return 2;
4044    
4045      return 1;   return 1;
4046  }  }
4047    
4048  static char * getRootSpecifier(char * str) {  int checkForGrub(struct grubConfig *config)
4049      char * idx, * rootspec = NULL;  {
4050     int fd;
4051     unsigned char bootSect[512];
4052     char *boot;
4053     int onSuse = isSuseSystem();
4054    
4055     if (onSuse) {
4056     if (parseSuseGrubConf(NULL, &boot))
4057     return 0;
4058     } else {
4059     if (parseSysconfigGrub(NULL, &boot))
4060     return 0;
4061     }
4062    
4063     /* assume grub is not installed -- not an error condition */
4064     if (!boot)
4065     return 0;
4066    
4067      if (*str == '(') {   fd = open("/boot/grub/stage1", O_RDONLY);
4068          idx = rootspec = strdup(str);   if (fd < 0)
4069          while(*idx && (*idx != ')') && (!isspace(*idx))) idx++;   /* this doesn't exist if grub hasn't been installed */
4070          *(++idx) = '\0';   return 0;
4071      }  
4072      return rootspec;   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  static char * getInitrdVal(struct grubConfig * config,  int checkForExtLinux(struct grubConfig *config)
4090     const char * prefix, struct singleLine *tmplLine,  {
4091     const char * newKernelInitrd,   int fd;
4092     const char ** extraInitrds, int extraInitrdCount)   unsigned char bootSect[512];
4093  {   char *boot;
4094      char *initrdVal, *end;   char executable[] = "/boot/extlinux/extlinux";
     int i;  
     size_t totalSize;  
     size_t prefixLen;  
     char separatorChar;  
   
     prefixLen = strlen(prefix);  
     totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;  
   
     for (i = 0; i < extraInitrdCount; i++) {  
  totalSize += sizeof(separatorChar);  
  totalSize += strlen(extraInitrds[i]) - prefixLen;  
     }  
4095    
4096      initrdVal = end = malloc(totalSize);   printf("entered: checkForExtLinux()\n");
4097    
4098      end = stpcpy (end, newKernelInitrd + prefixLen);   if (parseSysconfigGrub(NULL, &boot))
4099     return 0;
4100    
4101      separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;   /* assume grub is not installed -- not an error condition */
4102      for (i = 0; i < extraInitrdCount; i++) {   if (!boot)
4103   const char *extraInitrd;   return 0;
  int j;  
4104    
4105   extraInitrd = extraInitrds[i] + prefixLen;   fd = open(executable, O_RDONLY);
4106   /* Don't add entries that are already there */   if (fd < 0)
4107   if (tmplLine != NULL) {   /* this doesn't exist if grub hasn't been installed */
4108      for (j = 2; j < tmplLine->numElements; j++)   return 0;
  if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)  
     break;  
4109    
4110      if (j != tmplLine->numElements)   if (read(fd, bootSect, 512) != 512) {
4111   continue;   fprintf(stderr, _("grubby: unable to read %s: %s\n"),
4112     executable, strerror(errno));
4113     return 1;
4114   }   }
4115     close(fd);
4116    
4117   *end++ = separatorChar;   return checkDeviceBootloader(boot, bootSect);
4118   end = stpcpy(end, extraInitrd);  }
     }  
4119    
4120      return initrdVal;  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    
4135     return 1;
4136  }  }
4137    
4138  int addNewKernel(struct grubConfig * config, struct singleEntry * template,  int checkForElilo(struct grubConfig *config)
4139           const char * prefix,  {
4140   const char * newKernelPath, const char * newKernelTitle,   if (!access("/etc/elilo.conf", R_OK))
4141   const char * newKernelArgs, const char * newKernelInitrd,   return 2;
  const char ** extraInitrds, int extraInitrdCount,  
                  const char * newMBKernel, const char * newMBKernelArgs,  
  const char * newDevTreePath) {  
     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;  
4142    
4143   sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength, newKernelTitle);   return 1;
4144   while (findEntryByPath(config, buf, NULL, NULL)) {  }
4145      sprintf(numBuf, "%d", i++);  
4146      strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);  static char *getRootSpecifier(char *str)
4147    {
4148     char *idx, *rootspec = NULL;
4149    
4150     if (*str == '(') {
4151     idx = rootspec = strdup(str);
4152     while (*idx && (*idx != ')') && (!isspace(*idx)))
4153     idx++;
4154     *(++idx) = '\0';
4155   }   }
4156     return rootspec;
4157    }
4158    
4159   newKernelTitle = buf + 6;  static char *getInitrdVal(struct grubConfig *config,
4160      }    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      new = malloc(sizeof(*new));   if (j != tmplLine->numElements)
4197      new->skip = 0;   continue;
4198      new->multiboot = 0;   }
     new->next = config->entries;  
     new->lines = NULL;  
     config->entries = new;  
   
     /* copy/update from the template */  
     needs = NEED_KERNEL | NEED_TITLE;  
     if (newKernelInitrd)  
  needs |= NEED_INITRD;  
     if (newMBKernel) {  
         needs |= NEED_MB;  
         new->multiboot = 1;  
     }  
     if (newDevTreePath && getKeywordByType(LT_DEVTREE, config->cfi))  
  needs |= NEED_DEVTREE;  
4199    
4200      if (template) {   *end++ = separatorChar;
4201   for (masterLine = template->lines;   end = stpcpy(end, extraInitrd);
      masterLine && (tmplLine = lineDup(masterLine));  
      lineFree(tmplLine), masterLine = masterLine->next)  
  {  
     dbgPrintf("addNewKernel processing %d\n", tmplLine->type);  
   
     /* skip comments */  
     chptr = tmplLine->indent;  
     while (*chptr && isspace(*chptr)) chptr++;  
     if (*chptr == '#') continue;  
   
     if (iskernel(tmplLine->type) && tmplLine->numElements >= 2) {  
  if (!template->multiboot && (needs & NEED_MB)) {  
     /* it's not a multiboot template and this is the kernel  
      * line.  Try to be intelligent about inserting the  
      * hypervisor at the same time.  
      */  
     if (config->cfi->mbHyperFirst) {  
  /* insert the hypervisor first */  
  newLine = addLine(new, config->cfi, LT_HYPER,  
   tmplLine->indent,  
   newMBKernel + strlen(prefix));  
  /* set up for adding the kernel line */  
  free(tmplLine->indent);  
  tmplLine->indent = strdup(config->secondaryIndent);  
  needs &= ~NEED_MB;  
     }  
     if (needs & NEED_KERNEL) {  
  /* use addLineTmpl to preserve line elements,  
  * otherwise we could just call addLine.  Unfortunately  
  * this means making some changes to the template  
  * such as the indent change above and the type  
  * change below.  
  */  
  struct keywordTypes * mbm_kw =  
     getKeywordByType(LT_MBMODULE, config->cfi);  
  if (mbm_kw) {  
     tmplLine->type = LT_MBMODULE;  
     free(tmplLine->elements[0].item);  
     tmplLine->elements[0].item = strdup(mbm_kw->key);  
  }  
  newLine = addLineTmpl(new, tmplLine, newLine,  
       newKernelPath + strlen(prefix), config->cfi);  
  needs &= ~NEED_KERNEL;  
     }  
     if (needs & NEED_MB) { /* !mbHyperFirst */  
  newLine = addLine(new, config->cfi, LT_HYPER,  
   config->secondaryIndent,  
   newMBKernel + strlen(prefix));  
  needs &= ~NEED_MB;  
     }  
  } else if (needs & NEED_KERNEL) {  
     newLine = addLineTmpl(new, tmplLine, newLine,  
   newKernelPath + strlen(prefix), config->cfi);  
     needs &= ~NEED_KERNEL;  
  }  
   
     } else if (tmplLine->type == LT_HYPER &&  
        tmplLine->numElements >= 2) {  
  if (needs & NEED_MB) {  
     newLine = addLineTmpl(new, tmplLine, newLine,  
   newMBKernel + strlen(prefix), config->cfi);  
     needs &= ~NEED_MB;  
  }  
   
     } else if (tmplLine->type == LT_MBMODULE &&  
        tmplLine->numElements >= 2) {  
  if (new->multiboot) {  
     if (needs & NEED_KERNEL) {  
  newLine = addLineTmpl(new, tmplLine, newLine,  
       newKernelPath +  
       strlen(prefix), config->cfi);  
  needs &= ~NEED_KERNEL;  
     } else if (config->cfi->mbInitRdIsModule &&  
        (needs & NEED_INITRD)) {  
  char *initrdVal;  
  initrdVal = getInitrdVal(config, prefix, tmplLine,  
  newKernelInitrd, extraInitrds,  
  extraInitrdCount);  
  newLine = addLineTmpl(new, tmplLine, newLine,  
       initrdVal, config->cfi);  
  free(initrdVal);  
  needs &= ~NEED_INITRD;  
     }  
  } else if (needs & NEED_KERNEL) {  
     /* template is multi but new is not,  
      * insert the kernel in the first module slot  
      */  
     tmplLine->type = preferredLineType(LT_KERNEL, config->cfi);  
     free(tmplLine->elements[0].item);  
     tmplLine->elements[0].item =  
  strdup(getKeywordByType(tmplLine->type,  
  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 = preferredLineType(LT_INITRD, config->cfi);  
     free(tmplLine->elements[0].item);  
     tmplLine->elements[0].item =  
  strdup(getKeywordByType(tmplLine->type,  
  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 (isinitrd(tmplLine->type) && 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 &&  
     iskernel(masterLine->next->type)) {  
  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 if (tmplLine->type == LT_DEVTREE &&  
        tmplLine->numElements == 2 && newDevTreePath) {  
         newLine = addLineTmpl(new, tmplLine, newLine,  
       newDevTreePath + strlen(prefix),  
       config->cfi);  
  needs &= ~NEED_DEVTREE;  
     } else if (tmplLine->type == LT_ENTRY_END && needs & NEED_DEVTREE) {  
  const char *ndtp = newDevTreePath;  
  if (!strncmp(newDevTreePath, prefix, strlen(prefix)))  
     ndtp += strlen(prefix);  
  newLine = addLine(new, config->cfi, LT_DEVTREE,  
   config->secondaryIndent,  
   ndtp);  
  needs &= ~NEED_DEVTREE;  
  newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);  
     } else {  
  /* pass through other lines from the template */  
  newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);  
     }  
4202   }   }
4203    
4204      } else {   return initrdVal;
4205   /* don't have a template, so start the entry with the  }
4206   * appropriate starting line  
4207   */  int addNewKernel(struct grubConfig *config, struct singleEntry *template,
4208   switch (config->cfi->entryStart) {   const char *prefix,
4209      case LT_KERNEL:   const char *newKernelPath, const char *newKernelTitle,
4210      case LT_KERNEL_EFI:   const char *newKernelArgs, const char *newKernelInitrd,
4211      case LT_KERNEL_16:   const char **extraInitrds, int extraInitrdCount,
4212   if (new->multiboot && config->cfi->mbHyperFirst) {   const char *newMBKernel, const char *newMBKernelArgs,
4213      /* fall through to LT_HYPER */   const char *newDevTreePath)
4214   } else {  {
4215      newLine = addLine(new, config->cfi,   struct singleEntry *new;
4216            preferredLineType(LT_KERNEL, config->cfi),   struct singleLine *newLine = NULL, *tmplLine = NULL, *masterLine = NULL;
4217        config->primaryIndent,   int needs;
4218        newKernelPath + strlen(prefix));   char *chptr;
4219      needs &= ~NEED_KERNEL;  
4220      break;   if (!newKernelPath)
4221     return 0;
4222    
4223     /* if the newKernelTitle is too long silently munge it into something
4224     * we can live with. truncating is first check, then we'll just mess with
4225     * it until it looks better */
4226     if (config->cfi->maxTitleLength &&
4227        (strlen(newKernelTitle) > config->cfi->maxTitleLength)) {
4228     char *buf = alloca(config->cfi->maxTitleLength + 7);
4229     char *numBuf = alloca(config->cfi->maxTitleLength + 1);
4230     int i = 1;
4231    
4232     sprintf(buf, "TITLE=%.*s", config->cfi->maxTitleLength,
4233     newKernelTitle);
4234     while (findEntryByPath(config, buf, NULL, NULL)) {
4235     sprintf(numBuf, "%d", i++);
4236     strcpy(buf + strlen(buf) - strlen(numBuf), numBuf);
4237   }   }
4238    
4239      case LT_HYPER:   newKernelTitle = buf + 6;
4240   newLine = addLine(new, config->cfi, LT_HYPER,   }
   config->primaryIndent,  
   newMBKernel + strlen(prefix));  
  needs &= ~NEED_MB;  
  break;  
4241    
4242      case LT_MENUENTRY: {   new = malloc(sizeof(*new));
4243   char *nkt = malloc(strlen(newKernelTitle)+3);   new->skip = 0;
4244   strcpy(nkt, "'");   new->multiboot = 0;
4245   strcat(nkt, newKernelTitle);   new->next = config->entries;
4246   strcat(nkt, "'");   new->lines = NULL;
4247          newLine = addLine(new, config->cfi, LT_MENUENTRY,   config->entries = new;
4248    config->primaryIndent, nkt);  
4249   free(nkt);   /* copy/update from the template */
4250   needs &= ~NEED_TITLE;   needs = NEED_KERNEL | NEED_TITLE;
4251   needs |= NEED_END;   if (newKernelInitrd)
4252   break;   needs |= NEED_INITRD;
4253      }   if (newMBKernel) {
4254      case LT_TITLE:   needs |= NEED_MB;
4255   if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)   new->multiboot = 1;
4256   char * templabel;   }
4257   int x = 0, y = 0;   if (newDevTreePath && getKeywordByType(LT_DEVTREE, config->cfi))
4258     needs |= NEED_DEVTREE;
4259   templabel = strdup(newKernelTitle);  
4260   while( templabel[x]){   if (template) {
4261   if( templabel[x] == ' ' ){   for (masterLine = template->lines;
4262   y = x;       masterLine && (tmplLine = lineDup(masterLine));
4263   while( templabel[y] ){       lineFree(tmplLine), masterLine = masterLine->next) {
4264   templabel[y] = templabel[y+1];   dbgPrintf("addNewKernel processing %d\n",
4265   y++;    tmplLine->type);
4266    
4267     /* skip comments */
4268     chptr = tmplLine->indent;
4269     while (*chptr && isspace(*chptr))
4270     chptr++;
4271     if (*chptr == '#')
4272     continue;
4273    
4274     if (iskernel(tmplLine->type)
4275        && tmplLine->numElements >= 2) {
4276     if (!template->multiboot && (needs & NEED_MB)) {
4277     /* it's not a multiboot template and
4278     * this is the kernel line.  Try to
4279     * be intelligent about inserting the
4280     * hypervisor at the same time.
4281     */
4282     if (config->cfi->mbHyperFirst) {
4283     /* insert the hypervisor first */
4284     newLine =
4285        addLine(new, config->cfi,
4286        LT_HYPER,
4287        tmplLine->indent,
4288        newMBKernel +
4289        strlen(prefix));
4290     /* set up for adding the
4291     * kernel line */
4292     free(tmplLine->indent);
4293     tmplLine->indent =
4294        strdup(config->
4295       secondaryIndent);
4296     needs &= ~NEED_MB;
4297     }
4298     if (needs & NEED_KERNEL) {
4299     /* use addLineTmpl to
4300     * preserve line elements,
4301     * otherwise we could just
4302     * call addLine.
4303     * Unfortunately this means
4304     * making some changes to the
4305     * template such as the
4306     * indent change above and
4307     * the type change below.
4308     */
4309     struct keywordTypes *mbm_kw =
4310        getKeywordByType
4311        (LT_MBMODULE, config->cfi);
4312     if (mbm_kw) {
4313     tmplLine->type =
4314        LT_MBMODULE;
4315     free(tmplLine->
4316         elements[0].item);
4317     tmplLine->elements[0].
4318        item =
4319        strdup(mbm_kw->key);
4320     }
4321     newLine =
4322        addLineTmpl(new, tmplLine,
4323     newLine,
4324     newKernelPath +
4325     strlen(prefix),
4326     config->cfi);
4327     needs &= ~NEED_KERNEL;
4328     }
4329     if (needs & NEED_MB) { /* !mbHyperFirst */
4330     newLine =
4331        addLine(new, config->cfi,
4332        LT_HYPER,
4333        config->
4334        secondaryIndent,
4335        newMBKernel +
4336        strlen(prefix));
4337     needs &= ~NEED_MB;
4338   }   }
4339     } else if (needs & NEED_KERNEL) {
4340     newLine =
4341        addLineTmpl(new, tmplLine, newLine,
4342     newKernelPath +
4343     strlen(prefix),
4344     config->cfi);
4345     needs &= ~NEED_KERNEL;
4346   }   }
4347   x++;  
4348     } else if (tmplLine->type == LT_HYPER &&
4349       tmplLine->numElements >= 2) {
4350     if (needs & NEED_MB) {
4351     newLine =
4352        addLineTmpl(new, tmplLine, newLine,
4353     newMBKernel +
4354     strlen(prefix),
4355     config->cfi);
4356     needs &= ~NEED_MB;
4357     }
4358    
4359     } else if (tmplLine->type == LT_MBMODULE &&
4360       tmplLine->numElements >= 2) {
4361     if (new->multiboot) {
4362     if (needs & NEED_KERNEL) {
4363     newLine =
4364        addLineTmpl(new, tmplLine,
4365     newLine,
4366     newKernelPath +
4367     strlen(prefix),
4368     config->cfi);
4369     needs &= ~NEED_KERNEL;
4370     } else if (config->cfi->mbInitRdIsModule
4371       && (needs & NEED_INITRD)) {
4372     char *initrdVal;
4373     initrdVal =
4374        getInitrdVal(config, prefix,
4375     tmplLine,
4376     newKernelInitrd,
4377     extraInitrds,
4378     extraInitrdCount);
4379     newLine =
4380        addLineTmpl(new, tmplLine,
4381     newLine,
4382     initrdVal,
4383     config->cfi);
4384     free(initrdVal);
4385     needs &= ~NEED_INITRD;
4386     }
4387     } else if (needs & NEED_KERNEL) {
4388     /* template is multi but new is not,
4389     * insert the kernel in the first
4390     * module slot
4391     */
4392     tmplLine->type =
4393        preferredLineType(LT_KERNEL,
4394          config->cfi);
4395     free(tmplLine->elements[0].item);
4396     tmplLine->elements[0].item =
4397        strdup(getKeywordByType
4398       (tmplLine->type,
4399        config->cfi)->key);
4400     newLine =
4401        addLineTmpl(new, tmplLine, newLine,
4402     newKernelPath +
4403     strlen(prefix),
4404     config->cfi);
4405     needs &= ~NEED_KERNEL;
4406     } else if (needs & NEED_INITRD) {
4407     char *initrdVal;
4408     /* template is multi but new is not,
4409     * insert the initrd in the second
4410     * module slot
4411     */
4412     tmplLine->type =
4413        preferredLineType(LT_INITRD,
4414          config->cfi);
4415     free(tmplLine->elements[0].item);
4416     tmplLine->elements[0].item =
4417        strdup(getKeywordByType
4418       (tmplLine->type,
4419        config->cfi)->key);
4420     initrdVal =
4421        getInitrdVal(config, prefix,
4422     tmplLine,
4423     newKernelInitrd,
4424     extraInitrds,
4425     extraInitrdCount);
4426     newLine =
4427        addLineTmpl(new, tmplLine, newLine,
4428     initrdVal, config->cfi);
4429     free(initrdVal);
4430     needs &= ~NEED_INITRD;
4431     }
4432    
4433     } else if (isinitrd(tmplLine->type)
4434       && tmplLine->numElements >= 2) {
4435     if (needs & NEED_INITRD && new->multiboot
4436        && !template->multiboot
4437        && config->cfi->mbInitRdIsModule) {
4438     /* make sure we don't insert the
4439     * module initrd before the module
4440     * kernel... if we don't do it here,
4441     * it will be inserted following the
4442     * template.
4443     */
4444     if (!needs & NEED_KERNEL) {
4445     char *initrdVal;
4446    
4447     initrdVal =
4448        getInitrdVal(config, prefix,
4449     tmplLine,
4450     newKernelInitrd,
4451     extraInitrds,
4452     extraInitrdCount);
4453     newLine =
4454        addLine(new, config->cfi,
4455        LT_MBMODULE,
4456        config->
4457        secondaryIndent,
4458        initrdVal);
4459     free(initrdVal);
4460     needs &= ~NEED_INITRD;
4461     }
4462     } else if (needs & NEED_INITRD) {
4463     char *initrdVal;
4464     initrdVal =
4465        getInitrdVal(config, prefix,
4466     tmplLine,
4467     newKernelInitrd,
4468     extraInitrds,
4469     extraInitrdCount);
4470     newLine =
4471        addLineTmpl(new, tmplLine, newLine,
4472     initrdVal, config->cfi);
4473     free(initrdVal);
4474     needs &= ~NEED_INITRD;
4475     }
4476    
4477     } else if (tmplLine->type == LT_MENUENTRY &&
4478       (needs & NEED_TITLE)) {
4479     requote(tmplLine, config->cfi);
4480     char *nkt = malloc(strlen(newKernelTitle) + 3);
4481     strcpy(nkt, "'");
4482     strcat(nkt, newKernelTitle);
4483     strcat(nkt, "'");
4484     newLine =
4485        addLineTmpl(new, tmplLine, newLine, nkt,
4486     config->cfi);
4487     free(nkt);
4488     needs &= ~NEED_TITLE;
4489     } else if (tmplLine->type == LT_TITLE &&
4490       (needs & NEED_TITLE)) {
4491     if (tmplLine->numElements >= 2) {
4492     newLine =
4493        addLineTmpl(new, tmplLine, newLine,
4494     newKernelTitle,
4495     config->cfi);
4496     needs &= ~NEED_TITLE;
4497     } else if (tmplLine->numElements == 1 &&
4498       config->cfi->titleBracketed) {
4499     /* addLineTmpl doesn't handle
4500     * titleBracketed */
4501     newLine =
4502        addLine(new, config->cfi, LT_TITLE,
4503        tmplLine->indent,
4504        newKernelTitle);
4505     needs &= ~NEED_TITLE;
4506     }
4507     } else if (tmplLine->type == LT_ECHO) {
4508     requote(tmplLine, config->cfi);
4509     static const char *prefix = "'Loading ";
4510     if (tmplLine->numElements > 1 &&
4511        strstr(tmplLine->elements[1].item, prefix)
4512        && masterLine->next
4513        && iskernel(masterLine->next->type)) {
4514     char *newTitle =
4515        malloc(strlen(prefix) +
4516       strlen(newKernelTitle) + 2);
4517    
4518     strcpy(newTitle, prefix);
4519     strcat(newTitle, newKernelTitle);
4520     strcat(newTitle, "'");
4521     newLine =
4522        addLine(new, config->cfi, LT_ECHO,
4523        tmplLine->indent, newTitle);
4524     free(newTitle);
4525     } else {
4526     /* pass through other lines from the
4527     * template */
4528     newLine =
4529        addLineTmpl(new, tmplLine, newLine,
4530     NULL, config->cfi);
4531     }
4532     } else if (tmplLine->type == LT_DEVTREE &&
4533       tmplLine->numElements == 2
4534       && newDevTreePath) {
4535     newLine =
4536        addLineTmpl(new, tmplLine, newLine,
4537     newDevTreePath + strlen(prefix),
4538     config->cfi);
4539     needs &= ~NEED_DEVTREE;
4540     } else if (tmplLine->type == LT_ENTRY_END
4541       && needs & NEED_DEVTREE) {
4542     const char *ndtp = newDevTreePath;
4543     if (!strncmp
4544        (newDevTreePath, prefix, strlen(prefix)))
4545     ndtp += strlen(prefix);
4546     newLine = addLine(new, config->cfi, LT_DEVTREE,
4547      config->secondaryIndent,
4548      ndtp);
4549     needs &= ~NEED_DEVTREE;
4550     newLine =
4551        addLineTmpl(new, tmplLine, newLine, NULL,
4552     config->cfi);
4553     } else {
4554     /* pass through other lines from the template */
4555     newLine =
4556        addLineTmpl(new, tmplLine, newLine, NULL,
4557     config->cfi);
4558   }   }
  newLine = addLine(new, config->cfi, LT_TITLE,  
   config->primaryIndent, templabel);  
  free(templabel);  
  }else{  
  newLine = addLine(new, config->cfi, LT_TITLE,  
   config->primaryIndent, newKernelTitle);  
4559   }   }
  needs &= ~NEED_TITLE;  
  break;  
4560    
4561      default:   } else {
4562   abort();   /* don't have a template, so start the entry with the
4563     * appropriate starting line
4564     */
4565     switch (config->cfi->entryStart) {
4566     case LT_KERNEL:
4567     case LT_KERNEL_EFI:
4568     case LT_KERNEL_16:
4569     if (new->multiboot && config->cfi->mbHyperFirst) {
4570     /* fall through to LT_HYPER */
4571     } else {
4572     newLine = addLine(new, config->cfi,
4573      preferredLineType(LT_KERNEL,
4574        config->
4575        cfi),
4576      config->primaryIndent,
4577      newKernelPath +
4578      strlen(prefix));
4579     needs &= ~NEED_KERNEL;
4580     break;
4581     }
4582    
4583     case LT_HYPER:
4584     newLine = addLine(new, config->cfi, LT_HYPER,
4585      config->primaryIndent,
4586      newMBKernel + strlen(prefix));
4587     needs &= ~NEED_MB;
4588     break;
4589    
4590     case LT_MENUENTRY:{
4591     char *nkt = malloc(strlen(newKernelTitle) + 3);
4592     strcpy(nkt, "'");
4593     strcat(nkt, newKernelTitle);
4594     strcat(nkt, "'");
4595     newLine =
4596        addLine(new, config->cfi, LT_MENUENTRY,
4597        config->primaryIndent, nkt);
4598     free(nkt);
4599     needs &= ~NEED_TITLE;
4600     needs |= NEED_END;
4601     break;
4602     }
4603     case LT_TITLE:
4604     if (useextlinuxmenu != 0) { // We just need useextlinuxmenu to not be zero (set above)
4605     char *templabel;
4606     int x = 0, y = 0;
4607    
4608     templabel = strdup(newKernelTitle);
4609     while (templabel[x]) {
4610     if (templabel[x] == ' ') {
4611     y = x;
4612     while (templabel[y]) {
4613     templabel[y] =
4614        templabel[y + 1];
4615     y++;
4616     }
4617     }
4618     x++;
4619     }
4620     newLine = addLine(new, config->cfi, LT_TITLE,
4621      config->primaryIndent,
4622      templabel);
4623     free(templabel);
4624     } else {
4625     newLine = addLine(new, config->cfi, LT_TITLE,
4626      config->primaryIndent,
4627      newKernelTitle);
4628     }
4629     needs &= ~NEED_TITLE;
4630     break;
4631    
4632     default:
4633     abort();
4634     }
4635   }   }
     }  
4636    
4637      struct singleLine *endLine = NULL;   struct singleLine *endLine = NULL;
4638      endLine = getLineByType(LT_ENTRY_END, new->lines);   endLine = getLineByType(LT_ENTRY_END, new->lines);
4639      if (endLine) {   if (endLine) {
4640      removeLine(new, endLine);   removeLine(new, endLine);
4641      needs |= NEED_END;   needs |= NEED_END;
4642      }   }
4643    
4644      /* add the remainder of the lines, i.e. those that either   /* add the remainder of the lines, i.e. those that either
4645       * weren't present in the template, or in the case of no template,   * weren't present in the template, or in the case of no template,
4646       * all the lines following the entryStart.   * all the lines following the entryStart.
4647       */   */
4648      if (needs & NEED_TITLE) {   if (needs & NEED_TITLE) {
4649   newLine = addLine(new, config->cfi, LT_TITLE,   newLine = addLine(new, config->cfi, LT_TITLE,
4650    config->secondaryIndent,    config->secondaryIndent, newKernelTitle);
4651    newKernelTitle);   needs &= ~NEED_TITLE;
4652   needs &= ~NEED_TITLE;   }
4653      }   if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
4654      if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {   newLine = addLine(new, config->cfi, LT_HYPER,
4655   newLine = addLine(new, config->cfi, LT_HYPER,    config->secondaryIndent,
4656    config->secondaryIndent,    newMBKernel + strlen(prefix));
4657    newMBKernel + strlen(prefix));   needs &= ~NEED_MB;
4658   needs &= ~NEED_MB;   }
4659      }   if (needs & NEED_KERNEL) {
4660      if (needs & NEED_KERNEL) {   newLine = addLine(new, config->cfi,
4661   newLine = addLine(new, config->cfi,    (new->multiboot
4662    (new->multiboot && getKeywordByType(LT_MBMODULE,     && getKeywordByType(LT_MBMODULE,
4663        config->cfi))         config->cfi))
4664     ? LT_MBMODULE    ? LT_MBMODULE : preferredLineType(LT_KERNEL,
4665   : preferredLineType(LT_KERNEL, config->cfi),      config->
4666    config->secondaryIndent,      cfi),
4667    newKernelPath + strlen(prefix));    config->secondaryIndent,
4668   needs &= ~NEED_KERNEL;    newKernelPath + strlen(prefix));
4669      }   needs &= ~NEED_KERNEL;
4670      if (needs & NEED_MB) {   }
4671   newLine = addLine(new, config->cfi, LT_HYPER,   if (needs & NEED_MB) {
4672    config->secondaryIndent,   newLine = addLine(new, config->cfi, LT_HYPER,
4673    newMBKernel + strlen(prefix));    config->secondaryIndent,
4674   needs &= ~NEED_MB;    newMBKernel + strlen(prefix));
4675      }   needs &= ~NEED_MB;
4676      if (needs & NEED_INITRD) {   }
4677   char *initrdVal;   if (needs & NEED_INITRD) {
4678   initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);   char *initrdVal;
4679   newLine = addLine(new, config->cfi,   initrdVal =
4680    (new->multiboot && getKeywordByType(LT_MBMODULE,      getInitrdVal(config, prefix, NULL, newKernelInitrd,
4681        config->cfi))   extraInitrds, extraInitrdCount);
4682     ? LT_MBMODULE   newLine =
4683     : preferredLineType(LT_INITRD, config->cfi),      addLine(new, config->cfi,
4684    config->secondaryIndent,      (new->multiboot
4685    initrdVal);       && getKeywordByType(LT_MBMODULE, config->cfi))
4686   free(initrdVal);      ? LT_MBMODULE : preferredLineType(LT_INITRD,
4687   needs &= ~NEED_INITRD;        config->cfi),
4688      }      config->secondaryIndent, initrdVal);
4689      if (needs & NEED_DEVTREE) {   free(initrdVal);
4690   newLine = addLine(new, config->cfi, LT_DEVTREE,   needs &= ~NEED_INITRD;
4691    config->secondaryIndent,   }
4692    newDevTreePath);   if (needs & NEED_DEVTREE) {
4693   needs &= ~NEED_DEVTREE;   newLine = addLine(new, config->cfi, LT_DEVTREE,
4694      }    config->secondaryIndent, newDevTreePath);
4695     needs &= ~NEED_DEVTREE;
4696     }
4697    
4698      /* NEEDS_END must be last on bootloaders that need it... */   /* NEEDS_END must be last on bootloaders that need it... */
4699      if (needs & NEED_END) {   if (needs & NEED_END) {
4700   newLine = addLine(new, config->cfi, LT_ENTRY_END,   newLine = addLine(new, config->cfi, LT_ENTRY_END,
4701   config->secondaryIndent, NULL);    config->secondaryIndent, NULL);
4702   needs &= ~NEED_END;   needs &= ~NEED_END;
4703      }   }
4704    
4705      if (needs) {   if (needs) {
4706   printf(_("grubby: needs=%d, aborting\n"), needs);   printf(_("grubby: needs=%d, aborting\n"), needs);
4707   abort();   abort();
4708      }   }
4709    
4710      if (updateImage(config, "0", prefix, newKernelArgs, NULL,   if (updateImage(config, "0", prefix, newKernelArgs, NULL,
4711                      newMBKernelArgs, NULL)) return 1;   newMBKernelArgs, NULL))
4712     return 1;
4713    
4714      return 0;   return 0;
4715  }  }
4716    
4717  int main(int argc, const char ** argv) {  int main(int argc, const char **argv)
4718      poptContext optCon;  {
4719      const char * grubConfig = NULL;   poptContext optCon;
4720      char * outputFile = NULL;   const char *grubConfig = NULL;
4721      int arg = 0;   char *outputFile = NULL;
4722      int flags = 0;   int arg = 0;
4723      int badImageOkay = 0;   int flags = 0;
4724      int configureGrub2 = 0;   int badImageOkay = 0;
4725      int configureLilo = 0, configureELilo = 0, configureGrub = 0;   int configureGrub2 = 0;
4726      int configureYaboot = 0, configureSilo = 0, configureZipl = 0;   int configureLilo = 0, configureELilo = 0, configureGrub = 0;
4727      int configureExtLinux = 0;   int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
4728      int bootloaderProbe = 0;   int configureExtLinux = 0;
4729      int extraInitrdCount = 0;   int bootloaderProbe = 0;
4730      char * updateKernelPath = NULL;   int extraInitrdCount = 0;
4731      char * newKernelPath = NULL;   char *updateKernelPath = NULL;
4732      char * removeKernelPath = NULL;   char *newKernelPath = NULL;
4733      char * newKernelArgs = NULL;   char *removeKernelPath = NULL;
4734      char * newKernelInitrd = NULL;   char *newKernelArgs = NULL;
4735      char * newKernelTitle = NULL;   char *newKernelInitrd = NULL;
4736      char * newDevTreePath = NULL;   char *newKernelTitle = NULL;
4737      char * newMBKernel = NULL;   char *newDevTreePath = NULL;
4738      char * newMBKernelArgs = NULL;   char *newMBKernel = NULL;
4739      char * removeMBKernelArgs = NULL;   char *newMBKernelArgs = NULL;
4740      char * removeMBKernel = NULL;   char *removeMBKernelArgs = NULL;
4741      char * bootPrefix = NULL;   char *removeMBKernel = NULL;
4742      char * defaultKernel = NULL;   char *bootPrefix = NULL;
4743      char * removeArgs = NULL;   char *defaultKernel = NULL;
4744      char * kernelInfo = NULL;   char *removeArgs = NULL;
4745      char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };   char *kernelInfo = NULL;
4746      char * envPath = NULL;   char *extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
4747      const char * chptr = NULL;   char *envPath = NULL;
4748      struct configFileInfo * cfi = NULL;   const char *chptr = NULL;
4749      struct grubConfig * config;   struct configFileInfo *cfi = NULL;
4750      struct singleEntry * template = NULL;   struct grubConfig *config;
4751      int copyDefault = 0, makeDefault = 0;   struct singleEntry *template = NULL;
4752      int displayDefault = 0;   int copyDefault = 0, makeDefault = 0;
4753      int displayDefaultIndex = 0;   int displayDefault = 0;
4754      int displayDefaultTitle = 0;   int displayDefaultIndex = 0;
4755      int defaultIndex = -1;   int displayDefaultTitle = 0;
4756      struct poptOption options[] = {   int defaultIndex = -1;
4757   { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,   struct poptOption options[] = {
4758      _("add an entry for the specified kernel"), _("kernel-path") },   {"add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
4759   { "add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,   _("add an entry for the specified kernel"), _("kernel-path")},
4760      _("add an entry for the specified multiboot kernel"), NULL },   {"add-multiboot", 0, POPT_ARG_STRING, &newMBKernel, 0,
4761   { "args", 0, POPT_ARG_STRING, &newKernelArgs, 0,   _("add an entry for the specified multiboot kernel"), NULL},
4762      _("default arguments for the new kernel or new arguments for "   {"args", 0, POPT_ARG_STRING, &newKernelArgs, 0,
4763        "kernel being updated"), _("args") },   _("default arguments for the new kernel or new arguments for "
4764   { "mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,     "kernel being updated"), _("args")},
4765      _("default arguments for the new multiboot kernel or "   {"mbargs", 0, POPT_ARG_STRING, &newMBKernelArgs, 0,
4766                "new arguments for multiboot kernel being updated"), NULL },   _("default arguments for the new multiboot kernel or "
4767   { "bad-image-okay", 0, 0, &badImageOkay, 0,     "new arguments for multiboot kernel being updated"), NULL},
4768      _("don't sanity check images in boot entries (for testing only)"),   {"bad-image-okay", 0, 0, &badImageOkay, 0,
4769      NULL },   _
4770   { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,   ("don't sanity check images in boot entries (for testing only)"),
4771      _("filestystem which contains /boot directory (for testing only)"),   NULL},
4772      _("bootfs") },   {"boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
4773     _
4774     ("filestystem which contains /boot directory (for testing only)"),
4775     _("bootfs")},
4776  #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)  #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
4777   { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,   {"bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
4778      _("check which bootloader is installed on boot sector") },   _("check which bootloader is installed on boot sector")},
4779  #endif  #endif
4780   { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,   {"config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
4781      _("path to grub config file to update (\"-\" for stdin)"),   _("path to grub config file to update (\"-\" for stdin)"),
4782      _("path") },   _("path")},
4783   { "copy-default", 0, 0, &copyDefault, 0,   {"copy-default", 0, 0, &copyDefault, 0,
4784      _("use the default boot entry as a template for the new entry "   _("use the default boot entry as a template for the new entry "
4785        "being added; if the default is not a linux image, or if "     "being added; if the default is not a linux image, or if "
4786        "the kernel referenced by the default image does not exist, "     "the kernel referenced by the default image does not exist, "
4787        "the first linux entry whose kernel does exist is used as the "     "the first linux entry whose kernel does exist is used as the "
4788        "template"), NULL },     "template"), NULL},
4789   { "debug", 0, 0, &debug, 0,   {"debug", 0, 0, &debug, 0,
4790      _("print debugging information for failures") },   _("print debugging information for failures")},
4791   { "default-kernel", 0, 0, &displayDefault, 0,   {"default-kernel", 0, 0, &displayDefault, 0,
4792      _("display the path of the default kernel") },   _("display the path of the default kernel")},
4793   { "default-index", 0, 0, &displayDefaultIndex, 0,   {"default-index", 0, 0, &displayDefaultIndex, 0,
4794      _("display the index of the default kernel") },   _("display the index of the default kernel")},
4795   { "default-title", 0, 0, &displayDefaultTitle, 0,   {"default-title", 0, 0, &displayDefaultTitle, 0,
4796      _("display the title of the default kernel") },   _("display the title of the default kernel")},
4797   { "devtree", 0, POPT_ARG_STRING, &newDevTreePath, 0,   {"devtree", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4798      _("device tree file for new stanza"), _("dtb-path") },   _("device tree file for new stanza"), _("dtb-path")},
4799   { "devtreedir", 0, POPT_ARG_STRING, &newDevTreePath, 0,   {"devtreedir", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4800      _("device tree directory for new stanza"), _("dtb-path") },   _("device tree directory for new stanza"), _("dtb-path")},
4801   { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,   {"elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
4802      _("configure elilo bootloader") },   _("configure elilo bootloader")},
4803   { "efi", 0, POPT_ARG_NONE, &isEfi, 0,   {"efi", 0, POPT_ARG_NONE, &isEfi, 0,
4804      _("force grub2 stanzas to use efi") },   _("force grub2 stanzas to use efi")},
4805   { "env", 0, POPT_ARG_STRING, &envPath, 0,   {"env", 0, POPT_ARG_STRING, &envPath, 0,
4806      _("path for environment data"),   _("path for environment data"),
4807      _("path") },   _("path")},
4808   { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,   {"extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
4809      _("configure extlinux bootloader (from syslinux)") },   _("configure extlinux bootloader (from syslinux)")},
4810   { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,   {"grub", 0, POPT_ARG_NONE, &configureGrub, 0,
4811      _("configure grub bootloader") },   _("configure grub bootloader")},
4812   { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,   {"grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
4813      _("configure grub2 bootloader") },   _("configure grub2 bootloader")},
4814   { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,   {"info", 0, POPT_ARG_STRING, &kernelInfo, 0,
4815      _("display boot information for specified kernel"),   _("display boot information for specified kernel"),
4816      _("kernel-path") },   _("kernel-path")},
4817   { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,   {"initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
4818      _("initrd image for the new kernel"), _("initrd-path") },   _("initrd image for the new kernel"), _("initrd-path")},
4819   { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',   {"extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
4820      _("auxiliary initrd image for things other than the new kernel"), _("initrd-path") },   _
4821   { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,   ("auxiliary initrd image for things other than the new kernel"),
4822      _("configure lilo bootloader") },   _("initrd-path")},
4823   { "make-default", 0, 0, &makeDefault, 0,   {"lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
4824      _("make the newly added entry the default boot entry"), NULL },   _("configure lilo bootloader")},
4825   { "output-file", 'o', POPT_ARG_STRING, &outputFile, 0,   {"make-default", 0, 0, &makeDefault, 0,
4826      _("path to output updated config file (\"-\" for stdout)"),   _("make the newly added entry the default boot entry"), NULL},
4827      _("path") },   {"output-file", 'o', POPT_ARG_STRING, &outputFile, 0,
4828   { "remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,   _("path to output updated config file (\"-\" for stdout)"),
4829              _("remove kernel arguments"), NULL },   _("path")},
4830          { "remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,   {"remove-args", 0, POPT_ARG_STRING, &removeArgs, 0,
4831      _("remove multiboot kernel arguments"), NULL },   _("remove kernel arguments"), NULL},
4832   { "remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,   {"remove-mbargs", 0, POPT_ARG_STRING, &removeMBKernelArgs, 0,
4833      _("remove all entries for the specified kernel"),   _("remove multiboot kernel arguments"), NULL},
4834      _("kernel-path") },   {"remove-kernel", 0, POPT_ARG_STRING, &removeKernelPath, 0,
4835   { "remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,   _("remove all entries for the specified kernel"),
4836              _("remove all entries for the specified multiboot kernel"), NULL },   _("kernel-path")},
4837   { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,   {"remove-multiboot", 0, POPT_ARG_STRING, &removeMBKernel, 0,
4838      _("make the first entry referencing the specified kernel "   _("remove all entries for the specified multiboot kernel"),
4839        "the default"), _("kernel-path") },   NULL},
4840   { "set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,   {"set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
4841      _("make the given entry index the default entry"),   _("make the first entry referencing the specified kernel "
4842      _("entry-index") },     "the default"), _("kernel-path")},
4843   { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,   {"set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
4844      _("configure silo bootloader") },   _("make the given entry index the default entry"),
4845   { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,   _("entry-index")},
4846      _("title to use for the new kernel entry"), _("entry-title") },   {"silo", 0, POPT_ARG_NONE, &configureSilo, 0,
4847   { "update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,   _("configure silo bootloader")},
4848      _("updated information for the specified kernel"),   {"title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
4849      _("kernel-path") },   _("title to use for the new kernel entry"), _("entry-title")},
4850   { "version", 'v', 0, NULL, 'v',   {"update-kernel", 0, POPT_ARG_STRING, &updateKernelPath, 0,
4851      _("print the version of this program and exit"), NULL },   _("updated information for the specified kernel"),
4852   { "yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,   _("kernel-path")},
4853      _("configure yaboot bootloader") },   {"version", 'v', 0, NULL, 'v',
4854   { "zipl", 0, POPT_ARG_NONE, &configureZipl, 0,   _("print the version of this program and exit"), NULL},
4855      _("configure zipl bootloader") },   {"yaboot", 0, POPT_ARG_NONE, &configureYaboot, 0,
4856   POPT_AUTOHELP   _("configure yaboot bootloader")},
4857   { 0, 0, 0, 0, 0 }   {"zipl", 0, POPT_ARG_NONE, &configureZipl, 0,
4858      };   _("configure zipl bootloader")},
4859     POPT_AUTOHELP {0, 0, 0, 0, 0}
4860      useextlinuxmenu=0;   };
4861    
4862      int i = 0;   useextlinuxmenu = 0;
4863      for (int j = 1; j < argc; j++)  
4864   i += strlen(argv[j]) + 1;   int i = 0;
4865      saved_command_line = malloc(i);   for (int j = 1; j < argc; j++)
4866      if (!saved_command_line) {   i += strlen(argv[j]) + 1;
4867   fprintf(stderr, "grubby: %m\n");   saved_command_line = malloc(i);
4868   exit(1);   if (!saved_command_line) {
4869      }   fprintf(stderr, "grubby: %m\n");
4870      saved_command_line[0] = '\0';   exit(1);
4871      for (int j = 1; j < argc; j++) {   }
4872   strcat(saved_command_line, argv[j]);   saved_command_line[0] = '\0';
4873   strncat(saved_command_line, j == argc -1 ? "" : " ", 1);   for (int j = 1; j < argc; j++) {
4874      }   strcat(saved_command_line, argv[j]);
4875     strncat(saved_command_line, j == argc - 1 ? "" : " ", 1);
4876      optCon = poptGetContext("grubby", argc, argv, options, 0);   }
4877      poptReadDefaultConfig(optCon, 1);  
4878     optCon = poptGetContext("grubby", argc, argv, options, 0);
4879     poptReadDefaultConfig(optCon, 1);
4880    
4881     while ((arg = poptGetNextOpt(optCon)) >= 0) {
4882     switch (arg) {
4883     case 'v':
4884     printf("grubby version %s\n", VERSION);
4885     exit(0);
4886     break;
4887     case 'i':
4888     if (extraInitrdCount < MAX_EXTRA_INITRDS) {
4889     extraInitrds[extraInitrdCount++] =
4890        strdup(poptGetOptArg(optCon));
4891     } else {
4892     fprintf(stderr,
4893     _
4894     ("grubby: extra initrd maximum is %d\n"),
4895     extraInitrdCount);
4896     return 1;
4897     }
4898     break;
4899     }
4900     }
4901    
4902      while ((arg = poptGetNextOpt(optCon)) >= 0) {   if (arg < -1) {
4903   switch (arg) {   fprintf(stderr, _("grubby: bad argument %s: %s\n"),
4904    case 'v':   poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
4905      printf("grubby version %s\n", VERSION);   poptStrerror(arg));
     exit(0);  
     break;  
   case 'i':  
     if (extraInitrdCount < MAX_EXTRA_INITRDS) {  
      extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));  
     } else {  
  fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);  
4906   return 1;   return 1;
     }  
     break;  
4907   }   }
     }  
   
     if (arg < -1) {  
  fprintf(stderr, _("grubby: bad argument %s: %s\n"),  
  poptBadOption(optCon, POPT_BADOPTION_NOALIAS),  
  poptStrerror(arg));  
  return 1;  
     }  
   
     if ((chptr = poptGetArg(optCon))) {  
  fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);  
  return 1;  
     }  
4908    
4909      if ((configureLilo + configureGrub2 + configureGrub + configureELilo +   if ((chptr = poptGetArg(optCon))) {
4910   configureYaboot + configureSilo + configureZipl +   fprintf(stderr, _("grubby: unexpected argument %s\n"), chptr);
4911   configureExtLinux ) > 1) {   return 1;
4912   fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));   }
  return 1;  
     } else if (bootloaderProbe && grubConfig) {  
  fprintf(stderr,  
     _("grubby: cannot specify config file with --bootloader-probe\n"));  
  return 1;  
     } else if (configureGrub2) {  
  cfi = &grub2ConfigType;  
  if (envPath)  
     cfi->envFile = envPath;  
     } else if (configureLilo) {  
  cfi = &liloConfigType;  
     } else if (configureGrub) {  
  cfi = &grubConfigType;  
     } else if (configureELilo) {  
  cfi = &eliloConfigType;  
     } else if (configureYaboot) {  
  cfi = &yabootConfigType;  
     } else if (configureSilo) {  
         cfi = &siloConfigType;  
     } else if (configureZipl) {  
         cfi = &ziplConfigType;  
     } else if (configureExtLinux) {  
  cfi = &extlinuxConfigType;  
  useextlinuxmenu=1;  
     }  
4913    
4914      if (!cfi) {   if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
4915          if (grub2FindConfig(&grub2ConfigType)) {       configureYaboot + configureSilo + configureZipl +
4916      cfi = &grub2ConfigType;       configureExtLinux) > 1) {
4917      if (envPath)   fprintf(stderr,
4918   cfi->envFile = envPath;   _("grubby: cannot specify multiple bootloaders\n"));
4919          } else   return 1;
4920        #ifdef __ia64__   } else if (bootloaderProbe && grubConfig) {
4921      cfi = &eliloConfigType;   fprintf(stderr,
4922        #elif __powerpc__   _
4923      cfi = &yabootConfigType;   ("grubby: cannot specify config file with --bootloader-probe\n"));
4924        #elif __sparc__   return 1;
4925              cfi = &siloConfigType;   } else if (configureGrub2) {
4926        #elif __s390__   cfi = &grub2ConfigType;
4927              cfi = &ziplConfigType;   if (envPath)
4928        #elif __s390x__   cfi->envFile = envPath;
4929              cfi = &ziplConfigtype;   } else if (configureLilo) {
4930        #else   cfi = &liloConfigType;
4931      cfi = &grubConfigType;   } else if (configureGrub) {
4932        #endif   cfi = &grubConfigType;
4933      }   } else if (configureELilo) {
4934     cfi = &eliloConfigType;
4935     } else if (configureYaboot) {
4936     cfi = &yabootConfigType;
4937     } else if (configureSilo) {
4938     cfi = &siloConfigType;
4939     } else if (configureZipl) {
4940     cfi = &ziplConfigType;
4941     } else if (configureExtLinux) {
4942     cfi = &extlinuxConfigType;
4943     useextlinuxmenu = 1;
4944     }
4945    
4946     if (!cfi) {
4947     if (grub2FindConfig(&grub2ConfigType)) {
4948     cfi = &grub2ConfigType;
4949     if (envPath)
4950     cfi->envFile = envPath;
4951     } else
4952    #ifdef __ia64__
4953     cfi = &eliloConfigType;
4954    #elif __powerpc__
4955     cfi = &yabootConfigType;
4956    #elif __sparc__
4957     cfi = &siloConfigType;
4958    #elif __s390__
4959     cfi = &ziplConfigType;
4960    #elif __s390x__
4961     cfi = &ziplConfigtype;
4962    #else
4963     cfi = &grubConfigType;
4964    #endif
4965     }
4966    
4967      if (!grubConfig) {   if (!grubConfig) {
4968   if (cfi->findConfig)   if (cfi->findConfig)
4969      grubConfig = cfi->findConfig(cfi);   grubConfig = cfi->findConfig(cfi);
4970   if (!grubConfig)   if (!grubConfig)
4971      grubConfig = cfi->defaultConfig;   grubConfig = cfi->defaultConfig;
4972      }   }
4973    
4974      if (bootloaderProbe && (displayDefault || kernelInfo ||   if (bootloaderProbe && (displayDefault || kernelInfo ||
4975      newKernelPath || removeKernelPath || makeDefault ||   newKernelPath || removeKernelPath || makeDefault
4976      defaultKernel || displayDefaultIndex || displayDefaultTitle ||   || defaultKernel || displayDefaultIndex
4977      (defaultIndex >= 0))) {   || displayDefaultTitle
4978   fprintf(stderr, _("grubby: --bootloader-probe may not be used with "   || (defaultIndex >= 0))) {
4979     fprintf(stderr,
4980     _("grubby: --bootloader-probe may not be used with "
4981    "specified option"));    "specified option"));
4982   return 1;   return 1;
4983      }   }
4984    
4985      if ((displayDefault || kernelInfo) && (newKernelPath ||   if ((displayDefault || kernelInfo) && (newKernelPath ||
4986     removeKernelPath)) {         removeKernelPath)) {
4987   fprintf(stderr, _("grubby: --default-kernel and --info may not "   fprintf(stderr, _("grubby: --default-kernel and --info may not "
4988    "be used when adding or removing kernels\n"));    "be used when adding or removing kernels\n"));
4989   return 1;   return 1;
4990      }   }
4991    
4992      if (newKernelPath && !newKernelTitle) {   if (newKernelPath && !newKernelTitle) {
4993   fprintf(stderr, _("grubby: kernel title must be specified\n"));   fprintf(stderr, _("grubby: kernel title must be specified\n"));
4994   return 1;   return 1;
4995      } else if (!newKernelPath && (copyDefault ||   } else if (!newKernelPath && (copyDefault ||
4996    (newKernelInitrd && !updateKernelPath)||        (newKernelInitrd && !updateKernelPath) ||
4997    makeDefault || extraInitrdCount > 0)) {        makeDefault || extraInitrdCount > 0)) {
4998   fprintf(stderr, _("grubby: kernel path expected\n"));   fprintf(stderr, _("grubby: kernel path expected\n"));
4999   return 1;   return 1;
5000      }   }
5001    
5002      if (newKernelPath && updateKernelPath) {   if (newKernelPath && updateKernelPath) {
5003   fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"   fprintf(stderr, _("grubby: --add-kernel and --update-kernel may"
5004            "not be used together"));    "not be used together"));
5005   return 1;   return 1;
5006      }   }
5007    
5008      if (makeDefault && defaultKernel) {   if (makeDefault && defaultKernel) {
5009   fprintf(stderr, _("grubby: --make-default and --default-kernel "   fprintf(stderr, _("grubby: --make-default and --default-kernel "
5010    "may not be used together\n"));    "may not be used together\n"));
5011   return 1;   return 1;
5012      } else if (defaultKernel && removeKernelPath &&   } else if (defaultKernel && removeKernelPath &&
5013   !strcmp(defaultKernel, removeKernelPath)) {     !strcmp(defaultKernel, removeKernelPath)) {
5014   fprintf(stderr, _("grubby: cannot make removed kernel the default\n"));   fprintf(stderr,
5015   return 1;   _("grubby: cannot make removed kernel the default\n"));
5016      } else if (defaultKernel && newKernelPath &&   return 1;
5017   !strcmp(defaultKernel, newKernelPath)) {   } else if (defaultKernel && newKernelPath &&
5018   makeDefault = 1;     !strcmp(defaultKernel, newKernelPath)) {
5019   defaultKernel = NULL;   makeDefault = 1;
5020      }   defaultKernel = NULL;
5021      else if (defaultKernel && (defaultIndex >= 0)) {   } else if (defaultKernel && (defaultIndex >= 0)) {
5022   fprintf(stderr, _("grubby: --set-default and --set-default-index "   fprintf(stderr,
5023     _("grubby: --set-default and --set-default-index "
5024    "may not be used together\n"));    "may not be used together\n"));
5025   return 1;   return 1;
5026      }   }
5027    
5028      if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {   if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
5029   fprintf(stderr, _("grubby: output file must be specified if stdin "   fprintf(stderr,
5030   "is used\n"));   _("grubby: output file must be specified if stdin "
5031   return 1;    "is used\n"));
5032      }   return 1;
5033     }
5034    
5035      if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel   if (!removeKernelPath && !newKernelPath && !displayDefault
5036   && !kernelInfo && !bootloaderProbe && !updateKernelPath      && !defaultKernel && !kernelInfo && !bootloaderProbe
5037   && !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle      && !updateKernelPath && !removeMBKernel && !displayDefaultIndex
5038   && (defaultIndex == -1)) {      && !displayDefaultTitle && (defaultIndex == -1)) {
5039   fprintf(stderr, _("grubby: no action specified\n"));   fprintf(stderr, _("grubby: no action specified\n"));
5040   return 1;   return 1;
5041      }   }
5042    
5043      flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;   flags |= badImageOkay ? GRUBBY_BADIMAGE_OKAY : 0;
5044    
5045      if (cfi->needsBootPrefix) {   if (cfi->needsBootPrefix) {
5046   if (!bootPrefix) {   if (!bootPrefix) {
5047      bootPrefix = findBootPrefix();   bootPrefix = findBootPrefix();
5048      if (!bootPrefix) return 1;   if (!bootPrefix)
5049     return 1;
5050     } else {
5051     /* this shouldn't end with a / */
5052     if (bootPrefix[strlen(bootPrefix) - 1] == '/')
5053     bootPrefix[strlen(bootPrefix) - 1] = '\0';
5054     }
5055   } else {   } else {
5056      /* this shouldn't end with a / */   bootPrefix = "";
     if (bootPrefix[strlen(bootPrefix) - 1] == '/')  
  bootPrefix[strlen(bootPrefix) - 1] = '\0';  
5057   }   }
     } else {  
  bootPrefix = "";  
     }  
5058    
5059      if (!cfi->mbAllowExtraInitRds &&   if (!cfi->mbAllowExtraInitRds && extraInitrdCount > 0) {
5060   extraInitrdCount > 0) {   fprintf(stderr,
5061   fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);   _("grubby: %s doesn't allow multiple initrds\n"),
5062   return 1;   cfi->defaultConfig);
5063      }   return 1;
5064     }
5065    
5066      if (bootloaderProbe) {   if (bootloaderProbe) {
5067   int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;   int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
5068   struct grubConfig * lconfig, * gconfig, * yconfig, * econfig;   struct grubConfig *lconfig, *gconfig, *yconfig, *econfig;
5069    
5070   const char *grub2config = grub2FindConfig(&grub2ConfigType);   const char *grub2config = grub2FindConfig(&grub2ConfigType);
5071   if (grub2config) {   if (grub2config) {
5072      gconfig = readConfig(grub2config, &grub2ConfigType);   gconfig = readConfig(grub2config, &grub2ConfigType);
5073      if (!gconfig)   if (!gconfig)
5074   gr2c = 1;   gr2c = 1;
5075      else   else
5076   gr2c = checkForGrub2(gconfig);   gr2c = checkForGrub2(gconfig);
5077   }   }
   
  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(eliloConfigType.defaultConfig, F_OK)) {  
     econfig = readConfig(eliloConfigType.defaultConfig,  
  &eliloConfigType);  
     if (!econfig)  
  erc = 1;  
     else  
  erc = checkForElilo(econfig);  
  }  
   
  if (!access(extlinuxConfigType.defaultConfig, F_OK)) {  
     lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);  
     if (!lconfig)  
  extrc = 1;  
     else  
  extrc = checkForExtLinux(lconfig);  
  }  
   
   
  if (!access(yabootConfigType.defaultConfig, F_OK)) {  
     yconfig = readConfig(yabootConfigType.defaultConfig,  
  &yabootConfigType);  
     if (!yconfig)  
  yrc = 1;  
     else  
  yrc = checkForYaboot(yconfig);  
  }  
   
  if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1 ||  
  erc == 1)  
     return 1;  
   
  if (lrc == 2) printf("lilo\n");  
  if (gr2c == 2) printf("grub2\n");  
  if (grc == 2) printf("grub\n");  
  if (extrc == 2) printf("extlinux\n");  
  if (yrc == 2) printf("yaboot\n");  
  if (erc == 2) printf("elilo\n");  
5078    
5079   return 0;   const char *grubconfig = grubFindConfig(&grubConfigType);
5080      }   if (!access(grubconfig, F_OK)) {
5081     gconfig = readConfig(grubconfig, &grubConfigType);
5082     if (!gconfig)
5083     grc = 1;
5084     else
5085     grc = checkForGrub(gconfig);
5086     }
5087    
5088      if (grubConfig == NULL) {   if (!access(liloConfigType.defaultConfig, F_OK)) {
5089   printf("Could not find bootloader configuration file.\n");   lconfig =
5090   exit(1);      readConfig(liloConfigType.defaultConfig,
5091      }         &liloConfigType);
5092     if (!lconfig)
5093     lrc = 1;
5094     else
5095     lrc = checkForLilo(lconfig);
5096     }
5097    
5098     if (!access(eliloConfigType.defaultConfig, F_OK)) {
5099     econfig = readConfig(eliloConfigType.defaultConfig,
5100         &eliloConfigType);
5101     if (!econfig)
5102     erc = 1;
5103     else
5104     erc = checkForElilo(econfig);
5105     }
5106    
5107      config = readConfig(grubConfig, cfi);   if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
5108      if (!config) return 1;   lconfig =
5109        readConfig(extlinuxConfigType.defaultConfig,
5110           &extlinuxConfigType);
5111     if (!lconfig)
5112     extrc = 1;
5113     else
5114     extrc = checkForExtLinux(lconfig);
5115     }
5116    
5117      if (displayDefault) {   if (!access(yabootConfigType.defaultConfig, F_OK)) {
5118   struct singleLine * line;   yconfig = readConfig(yabootConfigType.defaultConfig,
5119   struct singleEntry * entry;       &yabootConfigType);
5120          char * rootspec;   if (!yconfig)
5121     yrc = 1;
5122   if (config->defaultImage == -1) return 0;   else
5123   if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&   yrc = checkForYaboot(yconfig);
5124   cfi->defaultIsSaved)   }
     config->defaultImage = 0;  
  entry = findEntryByIndex(config, config->defaultImage);  
  if (!entry) return 0;  
  if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;  
   
  line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, 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));  
5125    
5126   return 0;   if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1
5127        || erc == 1)
5128     return 1;
5129    
5130     if (lrc == 2)
5131     printf("lilo\n");
5132     if (gr2c == 2)
5133     printf("grub2\n");
5134     if (grc == 2)
5135     printf("grub\n");
5136     if (extrc == 2)
5137     printf("extlinux\n");
5138     if (yrc == 2)
5139     printf("yaboot\n");
5140     if (erc == 2)
5141     printf("elilo\n");
5142    
     } else if (displayDefaultTitle) {  
  struct singleLine * line;  
  struct singleEntry * entry;  
   
  if (config->defaultImage == -1)  
     return 0;  
  if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&  
  cfi->defaultIsSaved)  
     config->defaultImage = 0;  
  entry = findEntryByIndex(config, config->defaultImage);  
  if (!entry)  
5143   return 0;   return 0;
5144     }
5145    
5146     if (grubConfig == NULL) {
5147     printf("Could not find bootloader configuration file.\n");
5148     exit(1);
5149     }
5150    
5151     config = readConfig(grubConfig, cfi);
5152     if (!config)
5153     return 1;
5154    
5155     if (displayDefault) {
5156     struct singleLine *line;
5157     struct singleEntry *entry;
5158     char *rootspec;
5159    
5160     if (config->defaultImage == -1)
5161     return 0;
5162     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5163        cfi->defaultIsSaved)
5164     config->defaultImage = 0;
5165     entry = findEntryByIndex(config, config->defaultImage);
5166     if (!entry)
5167     return 0;
5168     if (!suitableImage(entry, bootPrefix, 0, flags))
5169     return 0;
5170    
5171     line =
5172        getLineByType(LT_KERNEL | LT_HYPER | LT_KERNEL_EFI |
5173      LT_KERNEL_16, entry->lines);
5174     if (!line)
5175     return 0;
5176    
5177     rootspec = getRootSpecifier(line->elements[1].item);
5178     printf("%s%s\n", bootPrefix, line->elements[1].item +
5179           ((rootspec != NULL) ? strlen(rootspec) : 0));
5180    
  if (!configureGrub2) {  
     char *title;  
     line = getLineByType(LT_TITLE, entry->lines);  
     if (!line)  
5181   return 0;   return 0;
5182      title = extractTitle(config, line);  
5183      if (!title)   } else if (displayDefaultTitle) {
5184     struct singleLine *line;
5185     struct singleEntry *entry;
5186    
5187     if (config->defaultImage == -1)
5188     return 0;
5189     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5190        cfi->defaultIsSaved)
5191     config->defaultImage = 0;
5192     entry = findEntryByIndex(config, config->defaultImage);
5193     if (!entry)
5194     return 0;
5195    
5196     if (!configureGrub2) {
5197     char *title;
5198     line = getLineByType(LT_TITLE, entry->lines);
5199     if (!line)
5200     return 0;
5201     title = extractTitle(config, line);
5202     if (!title)
5203     return 0;
5204     printf("%s\n", title);
5205     free(title);
5206     } else {
5207     char *title;
5208    
5209     dbgPrintf
5210        ("This is GRUB2, default title is embeded in menuentry\n");
5211     line = getLineByType(LT_MENUENTRY, entry->lines);
5212     if (!line)
5213     return 0;
5214     title = grub2ExtractTitle(line);
5215     if (title)
5216     printf("%s\n", title);
5217     }
5218   return 0;   return 0;
     printf("%s\n", title);  
     free(title);  
  } else {  
     char * title;  
5219    
5220      dbgPrintf("This is GRUB2, default title is embeded in menuentry\n");   } else if (displayDefaultIndex) {
5221      line = getLineByType(LT_MENUENTRY, entry->lines);   if (config->defaultImage == -1)
5222      if (!line)   return 0;
5223     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
5224        cfi->defaultIsSaved)
5225     config->defaultImage = 0;
5226     printf("%i\n", config->defaultImage);
5227   return 0;   return 0;
     title = grub2ExtractTitle(line);  
     if (title)  
  printf("%s\n", title);  
  }  
  return 0;  
5228    
5229      } else if (displayDefaultIndex) {   } else if (kernelInfo)
5230          if (config->defaultImage == -1) return 0;   return displayInfo(config, kernelInfo, bootPrefix);
  if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&  
  cfi->defaultIsSaved)  
     config->defaultImage = 0;  
         printf("%i\n", config->defaultImage);  
         return 0;  
   
     } else if (kernelInfo)  
  return displayInfo(config, kernelInfo, bootPrefix);  
   
     if (copyDefault) {  
  template = findTemplate(config, bootPrefix, NULL, 0, flags);  
  if (!template) return 1;  
     }  
5231    
5232      markRemovedImage(config, removeKernelPath, bootPrefix);   if (copyDefault) {
5233      markRemovedImage(config, removeMBKernel, bootPrefix);   template = findTemplate(config, bootPrefix, NULL, 0, flags);
5234      setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,   if (!template)
5235      bootPrefix, flags, defaultIndex);   return 1;
5236      setFallbackImage(config, newKernelPath != NULL);   }
5237      if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,  
5238                      removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;   markRemovedImage(config, removeKernelPath, bootPrefix);
5239      if (updateKernelPath && newKernelInitrd) {   markRemovedImage(config, removeMBKernel, bootPrefix);
5240      if (newMBKernel) {   setDefaultImage(config, newKernelPath != NULL, defaultKernel,
5241      if (addMBInitrd(config, newMBKernel, updateKernelPath,   makeDefault, bootPrefix, flags, defaultIndex);
5242     setFallbackImage(config, newKernelPath != NULL);
5243     if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
5244     removeArgs, newMBKernelArgs, removeMBKernelArgs))
5245     return 1;
5246     if (updateKernelPath && newKernelInitrd) {
5247     if (newMBKernel) {
5248     if (addMBInitrd(config, newMBKernel, updateKernelPath,
5249   bootPrefix, newKernelInitrd,   bootPrefix, newKernelInitrd,
5250   newKernelTitle))   newKernelTitle))
5251      return 1;   return 1;
5252      } else {   } else {
5253      if (updateInitrd(config, updateKernelPath, bootPrefix,   if (updateInitrd(config, updateKernelPath, bootPrefix,
5254   newKernelInitrd, newKernelTitle))   newKernelInitrd, newKernelTitle))
5255   return 1;   return 1;
5256      }   }
5257      }   }
5258      if (addNewKernel(config, template, bootPrefix, newKernelPath,   if (addNewKernel(config, template, bootPrefix, newKernelPath,
5259                       newKernelTitle, newKernelArgs, newKernelInitrd,   newKernelTitle, newKernelArgs, newKernelInitrd,
5260                       (const char **)extraInitrds, extraInitrdCount,   (const char **)extraInitrds, extraInitrdCount,
5261                       newMBKernel, newMBKernelArgs, newDevTreePath)) return 1;   newMBKernel, newMBKernelArgs, newDevTreePath))
5262         return 1;
5263    
5264      if (numEntries(config) == 0) {   if (numEntries(config) == 0) {
5265          fprintf(stderr, _("grubby: doing this would leave no kernel entries. "   fprintf(stderr,
5266                            "Not writing out new config.\n"));   _("grubby: doing this would leave no kernel entries. "
5267          return 1;    "Not writing out new config.\n"));
5268      }   return 1;
5269     }
5270    
5271      if (!outputFile)   if (!outputFile)
5272   outputFile = (char *)grubConfig;   outputFile = (char *)grubConfig;
5273    
5274      return writeConfig(config, outputFile, bootPrefix);   return writeConfig(config, outputFile, bootPrefix);
5275  }  }

Legend:
Removed from v.2992  
changed lines
  Added in v.3003