Magellan Linux

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

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