Magellan Linux

Diff of /trunk/grubby/grubby.c

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

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

Legend:
Removed from v.2957  
changed lines
  Added in v.3154