Magellan Linux

Diff of /trunk/grubby/grubby.c

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

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

Legend:
Removed from v.1850  
changed lines
  Added in v.3151