Magellan Linux

Diff of /trunk/grubby/grubby.c

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

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

Legend:
Removed from v.1854  
changed lines
  Added in v.3139