Magellan Linux

Diff of /trunk/grubby/grubby.c

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

trunk/mkinitrd-magellan/grubby/grubby.c revision 532 by niro, Sat Sep 1 22:45:15 2007 UTC trunk/grubby/grubby.c revision 2685 by niro, Wed Jul 16 10:38:09 2014 UTC
# Line 1  Line 1 
1  /* Copyright (C) 2001-2005 Red Hat, Inc.  /*
2     * grubby.c
3     This program is free software; you can redistribute it and/or   *
4     modify it under the terms of the General Public License as published   * Copyright (C) 2001-2008 Red Hat, Inc.
5     by the Free Software Foundation; either version 2 of the License, or   * All rights reserved.
6     (at your option) any later version.   *
7     * This program is free software; you can redistribute it and/or modify
8     This program is distributed in the hope that it will be useful,   * it under the terms of the GNU General Public License as published by
9     but WITHOUT ANY WARRANTY; without even the implied warranty of   * the Free Software Foundation; either version 2 of the License, or
10     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU   * (at your option) any later version.
11     General Public License for more details.   *
12     * This program is distributed in the hope that it will be useful,
13     You should have received a copy of the GNU General Public   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14     License along with this program; if not, write to the Free   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA   * GNU General Public License for more details.
16     02111-1307 USA.  */   *
17     * You should have received a copy of the GNU General Public License
18     * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19     */
20    
21    #ifndef _GNU_SOURCE
22    #define _GNU_SOURCE
23    #endif
24  #include <ctype.h>  #include <ctype.h>
25  #include <errno.h>  #include <errno.h>
26  #include <fcntl.h>  #include <fcntl.h>
# Line 25  Line 31 
31  #include <string.h>  #include <string.h>
32  #include <sys/stat.h>  #include <sys/stat.h>
33  #include <unistd.h>  #include <unistd.h>
34    #include <libgen.h>
35    #include <execinfo.h>
36    #include <signal.h>
37    #include <blkid/blkid.h>
38    
39    #include "log.h"
40    
41    #ifndef DEBUG
42    #define DEBUG 0
43    #endif
44    
45    #if DEBUG
46    #define dbgPrintf(format, args...) fprintf(stderr, format , ## args)
47    #else
48    #define dbgPrintf(format, args...)
49    #endif
50    
51  #include "mount_by_label.h"  int debug = 0; /* Currently just for template debugging */
52    
53  #define _(A) (A)  #define _(A) (A)
54    
55    #define MAX_EXTRA_INITRDS  16 /* code segment checked by --bootloader-probe */
56  #define CODE_SEG_SIZE  128 /* code segment checked by --bootloader-probe */  #define CODE_SEG_SIZE  128 /* code segment checked by --bootloader-probe */
57    
58    #define NOOP_OPCODE 0x90
59    #define JMP_SHORT_OPCODE 0xeb
60    
61    int isEfi = 0;
62    
63    char *saved_command_line = NULL;
64    
65  /* comments get lumped in with indention */  /* comments get lumped in with indention */
66  struct lineElement {  struct lineElement {
67      char * item;      char * item;
68      char * indent;      char * indent;
69  };  };
70    
71  enum lineType_e { LT_WHITESPACE, LT_TITLE, LT_KERNEL, LT_INITRD, LT_DEFAULT,  enum lineType_e {
72         LT_UNKNOWN, LT_ROOT, LT_FALLBACK, LT_KERNELARGS, LT_BOOT,      LT_WHITESPACE   = 1 << 0,
73         LT_BOOTROOT, LT_LBA, LT_MBMODULE, LT_OTHER, LT_GENERIC };      LT_TITLE        = 1 << 1,
74        LT_KERNEL       = 1 << 2,
75        LT_INITRD       = 1 << 3,
76        LT_HYPER        = 1 << 4,
77        LT_DEFAULT      = 1 << 5,
78        LT_MBMODULE     = 1 << 6,
79        LT_ROOT         = 1 << 7,
80        LT_FALLBACK     = 1 << 8,
81        LT_KERNELARGS   = 1 << 9,
82        LT_BOOT         = 1 << 10,
83        LT_BOOTROOT     = 1 << 11,
84        LT_LBA          = 1 << 12,
85        LT_OTHER        = 1 << 13,
86        LT_GENERIC      = 1 << 14,
87        LT_ECHO    = 1 << 16,
88        LT_MENUENTRY    = 1 << 17,
89        LT_ENTRY_END    = 1 << 18,
90        LT_SET_VARIABLE = 1 << 19,
91        LT_KERNEL_EFI   = 1 << 20,
92        LT_INITRD_EFI   = 1 << 21,
93        LT_KERNEL_16    = 1 << 22,
94        LT_INITRD_16    = 1 << 23,
95        LT_DEVTREE      = 1 << 24,
96        LT_UNKNOWN      = 1 << 25,
97    };
98    
99  struct singleLine {  struct singleLine {
100      char * indent;      char * indent;
# Line 61  struct singleEntry { Line 115  struct singleEntry {
115    
116  #define GRUB_CONFIG_NO_DEFAULT    (1 << 0) /* don't write out default=0 */  #define GRUB_CONFIG_NO_DEFAULT    (1 << 0) /* don't write out default=0 */
117    
118  #define KERNEL_KERNEL    (1 << 0)  /* These defines are (only) used in addNewKernel() */
119  #define KERNEL_INITRD    (1 << 2)  #define NEED_KERNEL  (1 << 0)
120  #define KERNEL_TITLE    (1 << 3)  #define NEED_INITRD  (1 << 1)
121  #define KERNEL_ARGS    (1 << 4)  #define NEED_TITLE   (1 << 2)
122  #define KERNEL_MB           (1 << 5)  #define NEED_ARGS    (1 << 3)
123    #define NEED_MB      (1 << 4)
124    #define NEED_END     (1 << 5)
125    #define NEED_DEVTREE (1 << 6)
126    
127  #define MAIN_DEFAULT    (1 << 0)  #define MAIN_DEFAULT    (1 << 0)
128  #define DEFAULT_SAVED       -2  #define DEFAULT_SAVED       -2
129    #define DEFAULT_SAVED_GRUB2 -3
130    
131  struct keywordTypes {  struct keywordTypes {
132      char * key;      char * key;
133      enum lineType_e type;      enum lineType_e type;
134      char nextChar;      char nextChar;
135  } ;      char separatorChar;
136    };
137    
138    struct configFileInfo;
139    
140    typedef const char *(*findConfigFunc)(struct configFileInfo *);
141    typedef const int (*writeLineFunc)(struct configFileInfo *,
142     struct singleLine *line);
143    typedef char *(*getEnvFunc)(struct configFileInfo *, char *name);
144    typedef int (*setEnvFunc)(struct configFileInfo *, char *name, char *value);
145    
146  struct configFileInfo {  struct configFileInfo {
147      char * defaultConfig;      char * defaultConfig;
148        findConfigFunc findConfig;
149        writeLineFunc writeLine;
150        getEnvFunc getEnv;
151        setEnvFunc setEnv;
152      struct keywordTypes * keywords;      struct keywordTypes * keywords;
153        int caseInsensitive;
154      int defaultIsIndex;      int defaultIsIndex;
155        int defaultIsVariable;
156      int defaultSupportSaved;      int defaultSupportSaved;
157      enum lineType_e entrySeparator;      int defaultIsSaved;
158        enum lineType_e entryStart;
159        enum lineType_e entryEnd;
160      int needsBootPrefix;      int needsBootPrefix;
161      int argsInQuotes;      int argsInQuotes;
162      int maxTitleLength;      int maxTitleLength;
163      int titleBracketed;      int titleBracketed;
164        int titlePosition;
165        int mbHyperFirst;
166        int mbInitRdIsModule;
167        int mbConcatArgs;
168        int mbAllowExtraInitRds;
169        char *envFile;
170  };  };
171    
172  struct keywordTypes grubKeywords[] = {  struct keywordTypes grubKeywords[] = {
# Line 94  struct keywordTypes grubKeywords[] = { Line 175  struct keywordTypes grubKeywords[] = {
175      { "default",    LT_DEFAULT,    ' ' },      { "default",    LT_DEFAULT,    ' ' },
176      { "fallback",   LT_FALLBACK,    ' ' },      { "fallback",   LT_FALLBACK,    ' ' },
177      { "kernel",    LT_KERNEL,    ' ' },      { "kernel",    LT_KERNEL,    ' ' },
178      { "initrd",    LT_INITRD,    ' ' },      { "initrd",    LT_INITRD,    ' ', ' ' },
179      { "module",     LT_MBMODULE,    ' ' },      { "module",     LT_MBMODULE,    ' ' },
180        { "kernel",     LT_HYPER,       ' ' },
181      { NULL,    0, 0 },      { NULL,    0, 0 },
182  };  };
183    
184    const char *grubFindConfig(struct configFileInfo *cfi) {
185        static const char *configFiles[] = {
186     "/boot/grub/grub.conf",
187     "/boot/grub/menu.lst",
188     "/etc/grub.conf",
189     "/boot/grub2/grub.cfg",
190     "/boot/grub2-efi/grub.cfg",
191     NULL
192        };
193        static int i = -1;
194    
195        if (i == -1) {
196     for (i = 0; configFiles[i] != NULL; i++) {
197        dbgPrintf("Checking \"%s\": ", configFiles[i]);
198        if (!access(configFiles[i], R_OK)) {
199     dbgPrintf("found\n");
200     return configFiles[i];
201        }
202        dbgPrintf("not found\n");
203     }
204        }
205        return configFiles[i];
206    }
207    
208  struct configFileInfo grubConfigType = {  struct configFileInfo grubConfigType = {
209      "/boot/grub/grub.conf",    /* defaultConfig */      .findConfig = grubFindConfig,
210      grubKeywords,    /* keywords */      .keywords = grubKeywords,
211      1,    /* defaultIsIndex */      .defaultIsIndex = 1,
212      1,    /* defaultSupportSaved */      .defaultSupportSaved = 1,
213      LT_TITLE,    /* entrySeparator */      .entryStart = LT_TITLE,
214      1,    /* needsBootPrefix */      .needsBootPrefix = 1,
215      0,    /* argsInQuotes */      .mbHyperFirst = 1,
216      0,    /* maxTitleLength */      .mbInitRdIsModule = 1,
217      0,                                      /* titleBracketed */      .mbAllowExtraInitRds = 1,
218    };
219    
220    struct keywordTypes grub2Keywords[] = {
221        { "menuentry",  LT_MENUENTRY,   ' ' },
222        { "}",          LT_ENTRY_END,   ' ' },
223        { "echo",       LT_ECHO,        ' ' },
224        { "set",        LT_SET_VARIABLE,' ', '=' },
225        { "root",       LT_BOOTROOT,    ' ' },
226        { "default",    LT_DEFAULT,     ' ' },
227        { "fallback",   LT_FALLBACK,    ' ' },
228        { "linux",      LT_KERNEL,      ' ' },
229        { "linuxefi",   LT_KERNEL_EFI,  ' ' },
230        { "linux16",    LT_KERNEL_16,   ' ' },
231        { "initrd",     LT_INITRD,      ' ', ' ' },
232        { "initrdefi",  LT_INITRD_EFI,  ' ', ' ' },
233        { "initrd16",   LT_INITRD_16,   ' ', ' ' },
234        { "module",     LT_MBMODULE,    ' ' },
235        { "kernel",     LT_HYPER,       ' ' },
236        { "devicetree", LT_DEVTREE,  ' ' },
237        { NULL, 0, 0 },
238    };
239    
240    const char *grub2FindConfig(struct configFileInfo *cfi) {
241        static const char *configFiles[] = {
242     "/boot/grub/grub-efi.cfg",
243     "/boot/grub/grub.cfg",
244     NULL
245        };
246        static int i = -1;
247        static const char *grub_cfg = "/boot/grub/grub.cfg";
248        int rc = -1;
249    
250        if (i == -1) {
251     for (i = 0; configFiles[i] != NULL; i++) {
252        dbgPrintf("Checking \"%s\": ", configFiles[i]);
253        if ((rc = access(configFiles[i], R_OK))) {
254     if (errno == EACCES) {
255        printf("Unable to access bootloader configuration file "
256           "\"%s\": %m\n", configFiles[i]);
257        exit(1);
258     }
259     continue;
260        } else {
261     dbgPrintf("found\n");
262     return configFiles[i];
263        }
264     }
265        }
266    
267        /* Ubuntu renames grub2 to grub, so check for the grub.d directory
268         * that isn't in grub1, and if it exists, return the config file path
269         * that they use. */
270        if (configFiles[i] == NULL && !access("/etc/grub.d/", R_OK)) {
271     dbgPrintf("found\n");
272     return grub_cfg;
273        }
274    
275        dbgPrintf("not found\n");
276        return configFiles[i];
277    }
278    
279    /* kind of hacky.  It'll give the first 1024 bytes, ish. */
280    static char *grub2GetEnv(struct configFileInfo *info, char *name)
281    {
282        static char buf[1025];
283        char *s = NULL;
284        char *ret = NULL;
285        char *envFile = info->envFile ? info->envFile : "/boot/grub/grubenv";
286        int rc = asprintf(&s, "grub-editenv %s list | grep '^%s='", envFile, name);
287    
288        if (rc < 0)
289     return NULL;
290    
291        FILE *f = popen(s, "r");
292        if (!f)
293     goto out;
294    
295        memset(buf, '\0', sizeof (buf));
296        ret = fgets(buf, 1024, f);
297        pclose(f);
298    
299        if (ret) {
300     ret += strlen(name) + 1;
301     ret[strlen(ret) - 1] = '\0';
302        }
303        dbgPrintf("grub2GetEnv(%s): %s\n", name, ret);
304    out:
305        free(s);
306        return ret;
307    }
308    
309    static int sPopCount(const char *s, const char *c)
310    {
311        int ret = 0;
312        if (!s)
313     return -1;
314        for (int i = 0; s[i] != '\0'; i++)
315     for (int j = 0; c[j] != '\0'; j++)
316        if (s[i] == c[j])
317     ret++;
318        return ret;
319    }
320    
321    static char *shellEscape(const char *s)
322    {
323        int l = strlen(s) + sPopCount(s, "'") * 2;
324    
325        char *ret = calloc(l+1, sizeof (*ret));
326        if (!ret)
327     return NULL;
328        for (int i = 0, j = 0; s[i] != '\0'; i++, j++) {
329     if (s[i] == '\'')
330        ret[j++] = '\\';
331     ret[j] = s[i];
332        }
333        return ret;
334    }
335    
336    static void unquote(char *s)
337    {
338        int l = strlen(s);
339    
340        if ((s[l-1] == '\'' && s[0] == '\'') || (s[l-1] == '"' && s[0] == '"')) {
341     memmove(s, s+1, l-2);
342     s[l-2] = '\0';
343        }
344    }
345    
346    static int grub2SetEnv(struct configFileInfo *info, char *name, char *value)
347    {
348        char *s = NULL;
349        int rc = 0;
350        char *envFile = info->envFile ? info->envFile : "/boot/grub/grubenv";
351    
352        unquote(value);
353        value = shellEscape(value);
354        if (!value)
355        return -1;
356    
357        rc = asprintf(&s, "grub-editenv %s set '%s=%s'", envFile, name, value);
358        free(value);
359        if (rc <0)
360     return -1;
361    
362        dbgPrintf("grub2SetEnv(%s): %s\n", name, s);
363        rc = system(s);
364        free(s);
365        return rc;
366    }
367    
368    /* this is a gigantic hack to avoid clobbering grub2 variables... */
369    static int is_special_grub2_variable(const char *name)
370    {
371        if (!strcmp(name,"\"${next_entry}\""))
372     return 1;
373        if (!strcmp(name,"\"${prev_saved_entry}\""))
374     return 1;
375        return 0;
376    }
377    
378    int sizeOfSingleLine(struct singleLine * line) {
379      int count = 0;
380    
381      for (int i = 0; i < line->numElements; i++) {
382        int indentSize = 0;
383    
384        count = count + strlen(line->elements[i].item);
385    
386        indentSize = strlen(line->elements[i].indent);
387        if (indentSize > 0)
388          count = count + indentSize;
389        else
390          /* be extra safe and add room for whitespaces */
391          count = count + 1;
392      }
393    
394      /* room for trailing terminator */
395      count = count + 1;
396    
397      return count;
398    }
399    
400    static int isquote(char q)
401    {
402        if (q == '\'' || q == '\"')
403     return 1;
404        return 0;
405    }
406    
407    static int iskernel(enum lineType_e type) {
408        return (type == LT_KERNEL || type == LT_KERNEL_EFI || type == LT_KERNEL_16);
409    }
410    
411    static int isinitrd(enum lineType_e type) {
412        return (type == LT_INITRD || type == LT_INITRD_EFI || type == LT_INITRD_16);
413    }
414    
415    char *grub2ExtractTitle(struct singleLine * line) {
416        char * current;
417        char * current_indent;
418        int current_len;
419        int current_indent_len;
420        int i;
421    
422        /* bail out if line does not start with menuentry */
423        if (strcmp(line->elements[0].item, "menuentry"))
424          return NULL;
425    
426        i = 1;
427        current = line->elements[i].item;
428        current_len = strlen(current);
429    
430        /* if second word is quoted, strip the quotes and return single word */
431        if (isquote(*current) && isquote(current[current_len - 1])) {
432     char *tmp;
433    
434     tmp = strdup(current);
435     *(tmp + current_len - 1) = '\0';
436     return ++tmp;
437        }
438    
439        /* if no quotes, return second word verbatim */
440        if (!isquote(*current))
441     return current;
442    
443        /* second element start with a quote, so we have to find the element
444         * whose last character is also quote (assuming it's the closing one) */
445        int resultMaxSize;
446        char * result;
447        
448        resultMaxSize = sizeOfSingleLine(line);
449        result = malloc(resultMaxSize);
450        snprintf(result, resultMaxSize, "%s", ++current);
451        
452        i++;
453        for (; i < line->numElements; ++i) {
454     current = line->elements[i].item;
455     current_len = strlen(current);
456     current_indent = line->elements[i].indent;
457     current_indent_len = strlen(current_indent);
458    
459     strncat(result, current_indent, current_indent_len);
460     if (!isquote(current[current_len-1])) {
461        strncat(result, current, current_len);
462     } else {
463        strncat(result, current, current_len - 1);
464        break;
465     }
466        }
467        return result;
468    }
469    
470    struct configFileInfo grub2ConfigType = {
471        .findConfig = grub2FindConfig,
472        .getEnv = grub2GetEnv,
473        .setEnv = grub2SetEnv,
474        .keywords = grub2Keywords,
475        .defaultIsIndex = 1,
476        .defaultSupportSaved = 1,
477        .defaultIsVariable = 1,
478        .entryStart = LT_MENUENTRY,
479        .entryEnd = LT_ENTRY_END,
480        .titlePosition = 1,
481        .needsBootPrefix = 1,
482        .mbHyperFirst = 1,
483        .mbInitRdIsModule = 1,
484        .mbAllowExtraInitRds = 1,
485  };  };
486    
487  struct keywordTypes yabootKeywords[] = {  struct keywordTypes yabootKeywords[] = {
# Line 142  struct keywordTypes yabootKeywords[] = { Line 515  struct keywordTypes yabootKeywords[] = {
515      { "partition",  LT_GENERIC,    '=' },      { "partition",  LT_GENERIC,    '=' },
516      { "device",    LT_GENERIC,    '=' },      { "device",    LT_GENERIC,    '=' },
517      { "fstype",    LT_GENERIC,    '=' },      { "fstype",    LT_GENERIC,    '=' },
518      { "initrd",    LT_INITRD,    '=' },      { "initrd",    LT_INITRD,    '=', ';' },
519      { "append",    LT_KERNELARGS,  '=' },      { "append",    LT_KERNELARGS,  '=' },
520      { "boot",    LT_BOOT,    '=' },      { "boot",    LT_BOOT,    '=' },
521      { "lba",    LT_LBA,    ' ' },      { "lba",    LT_LBA,    ' ' },
# Line 162  struct keywordTypes liloKeywords[] = { Line 535  struct keywordTypes liloKeywords[] = {
535      { NULL,    0, 0 },      { NULL,    0, 0 },
536  };  };
537    
538    struct keywordTypes eliloKeywords[] = {
539        { "label",    LT_TITLE,    '=' },
540        { "root",    LT_ROOT,    '=' },
541        { "default",    LT_DEFAULT,    '=' },
542        { "image",    LT_KERNEL,    '=' },
543        { "initrd",    LT_INITRD,    '=' },
544        { "append",    LT_KERNELARGS,  '=' },
545        { "vmm",    LT_HYPER,       '=' },
546        { NULL,    0, 0 },
547    };
548    
549  struct keywordTypes siloKeywords[] = {  struct keywordTypes siloKeywords[] = {
550      { "label",    LT_TITLE,    '=' },      { "label",    LT_TITLE,    '=' },
551      { "root",    LT_ROOT,    '=' },      { "root",    LT_ROOT,    '=' },
# Line 183  struct keywordTypes ziplKeywords[] = { Line 567  struct keywordTypes ziplKeywords[] = {
567      { NULL,         0, 0 },      { NULL,         0, 0 },
568  };  };
569    
570    struct keywordTypes extlinuxKeywords[] = {
571        { "label",    LT_TITLE,    ' ' },
572        { "root",    LT_ROOT,    ' ' },
573        { "default",    LT_DEFAULT,    ' ' },
574        { "kernel",    LT_KERNEL,    ' ' },
575        { "initrd",    LT_INITRD,      ' ', ',' },
576        { "append",    LT_KERNELARGS,  ' ' },
577        { "prompt",     LT_UNKNOWN,     ' ' },
578        { NULL,    0, 0 },
579    };
580    int useextlinuxmenu;
581  struct configFileInfo eliloConfigType = {  struct configFileInfo eliloConfigType = {
582      "/boot/efi/EFI/redhat/elilo.conf",    /* defaultConfig */      .defaultConfig = "/boot/efi/EFI/redhat/elilo.conf",
583      liloKeywords,    /* keywords */      .keywords = eliloKeywords,
584      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
585      0,    /* defaultSupportSaved */      .needsBootPrefix = 1,
586      LT_KERNEL,    /* entrySeparator */      .argsInQuotes = 1,
587      1,                    /* needsBootPrefix */      .mbConcatArgs = 1,
     1,    /* argsInQuotes */  
     0,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
588  };  };
589    
590  struct configFileInfo liloConfigType = {  struct configFileInfo liloConfigType = {
591      "/etc/lilo.conf",    /* defaultConfig */      .defaultConfig = "/etc/lilo.conf",
592      liloKeywords,    /* keywords */      .keywords = liloKeywords,
593      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
594      0,    /* defaultSupportSaved */      .argsInQuotes = 1,
595      LT_KERNEL,    /* entrySeparator */      .maxTitleLength = 15,
     0,    /* needsBootPrefix */  
     1,    /* argsInQuotes */  
     15,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
596  };  };
597    
598  struct configFileInfo yabootConfigType = {  struct configFileInfo yabootConfigType = {
599      "/etc/yaboot.conf",    /* defaultConfig */      .defaultConfig = "/etc/yaboot.conf",
600      yabootKeywords,    /* keywords */      .keywords = yabootKeywords,
601      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
602      0,    /* defaultSupportSaved */      .needsBootPrefix = 1,
603      LT_KERNEL,    /* entrySeparator */      .argsInQuotes = 1,
604      1,    /* needsBootPrefix */      .maxTitleLength = 15,
605      1,    /* argsInQuotes */      .mbAllowExtraInitRds = 1,
     15,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
606  };  };
607    
608  struct configFileInfo siloConfigType = {  struct configFileInfo siloConfigType = {
609      "/etc/silo.conf",    /* defaultConfig */      .defaultConfig = "/etc/silo.conf",
610      siloKeywords,    /* keywords */      .keywords = siloKeywords,
611      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
612      0,    /* defaultSupportSaved */      .needsBootPrefix = 1,
613      LT_KERNEL,    /* entrySeparator */      .argsInQuotes = 1,
614      1,    /* needsBootPrefix */      .maxTitleLength = 15,
     1,    /* argsInQuotes */  
     15,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
615  };  };
616    
617  struct configFileInfo ziplConfigType = {  struct configFileInfo ziplConfigType = {
618      "/etc/zipl.conf",    /* defaultConfig */      .defaultConfig = "/etc/zipl.conf",
619      ziplKeywords,    /* keywords */      .keywords = ziplKeywords,
620      0,    /* defaultIsIndex */      .entryStart = LT_TITLE,
621      0,    /* defaultSupportSaved */      .argsInQuotes = 1,
622      LT_TITLE,    /* entrySeparator */      .titleBracketed = 1,
623      0,    /* needsBootPrefix */  };
624      1,    /* argsInQuotes */  
625      15,    /* maxTitleLength */  struct configFileInfo extlinuxConfigType = {
626      1,                                      /* titleBracketed */      .defaultConfig = "/boot/extlinux/extlinux.conf",
627        .keywords = extlinuxKeywords,
628        .caseInsensitive = 1,
629        .entryStart = LT_TITLE,
630        .needsBootPrefix = 1,
631        .maxTitleLength = 255,
632        .mbAllowExtraInitRds = 1,
633  };  };
634    
635  struct grubConfig {  struct grubConfig {
# Line 255  struct grubConfig { Line 644  struct grubConfig {
644      struct configFileInfo * cfi;      struct configFileInfo * cfi;
645  };  };
646    
647    blkid_cache blkid;
648    
649  struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index);  struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index);
650  struct singleEntry * findEntryByPath(struct grubConfig * cfg,  struct singleEntry * findEntryByPath(struct grubConfig * cfg,
651       const char * path, const char * prefix,       const char * path, const char * prefix,
652       int * index);       int * index);
653  static char * strndup(char * from, int len);  struct singleEntry * findEntryByTitle(struct grubConfig * cfg, char *title,
654          int * index);
655  static int readFile(int fd, char ** bufPtr);  static int readFile(int fd, char ** bufPtr);
656  static void lineInit(struct singleLine * line);  static void lineInit(struct singleLine * line);
657    struct singleLine * lineDup(struct singleLine * line);
658  static void lineFree(struct singleLine * line);  static void lineFree(struct singleLine * line);
659  static int lineWrite(FILE * out, struct singleLine * line,  static int lineWrite(FILE * out, struct singleLine * line,
660       struct configFileInfo * cfi);       struct configFileInfo * cfi);
661  static int getNextLine(char ** bufPtr, struct singleLine * line,  static int getNextLine(char ** bufPtr, struct singleLine * line,
662         struct configFileInfo * cfi);         struct configFileInfo * cfi);
663  static char * getRootSpecifier(char * str);  static char * getRootSpecifier(char * str);
664    static void requote(struct singleLine *line, struct configFileInfo * cfi);
665  static char * strndup(char * from, int len) {  static void insertElement(struct singleLine * line,
666      char * to;    const char * item, int insertHere,
667      struct configFileInfo * cfi);
668      to = malloc(len + 1);  static void removeElement(struct singleLine * line, int removeHere);
669      strncpy(to, from, len);  static struct keywordTypes * getKeywordByType(enum lineType_e type,
670      to[len] = '\0';        struct configFileInfo * cfi);
671    static enum lineType_e getTypeByKeyword(char * keyword,
672      return to;   struct configFileInfo * cfi);
673  }  static struct singleLine * getLineByType(enum lineType_e type,
674     struct singleLine * line);
675    static int checkForExtLinux(struct grubConfig * config);
676    struct singleLine * addLineTmpl(struct singleEntry * entry,
677                                    struct singleLine * tmplLine,
678                                    struct singleLine * prevLine,
679                                    const char * val,
680     struct configFileInfo * cfi);
681    struct singleLine *  addLine(struct singleEntry * entry,
682                                 struct configFileInfo * cfi,
683                                 enum lineType_e type, char * defaultIndent,
684                                 const char * val);
685    
686  static char * sdupprintf(const char *format, ...)  static char * sdupprintf(const char *format, ...)
687  #ifdef __GNUC__  #ifdef __GNUC__
# Line 313  static char * sdupprintf(const char *for Line 716  static char * sdupprintf(const char *for
716      return buf;      return buf;
717  }  }
718    
719    static enum lineType_e preferredLineType(enum lineType_e type,
720     struct configFileInfo *cfi) {
721        if (isEfi && cfi == &grub2ConfigType) {
722     switch (type) {
723     case LT_KERNEL:
724        return LT_KERNEL_EFI;
725     case LT_INITRD:
726        return LT_INITRD_EFI;
727     default:
728        return type;
729     }
730    #if defined(__i386__) || defined(__x86_64__)
731        } else if (cfi == &grub2ConfigType) {
732     switch (type) {
733     case LT_KERNEL:
734        return LT_KERNEL_16;
735     case LT_INITRD:
736        return LT_INITRD_16;
737     default:
738        return type;
739     }
740    #endif
741        }
742        return type;
743    }
744    
745    static struct keywordTypes * getKeywordByType(enum lineType_e type,
746          struct configFileInfo * cfi) {
747        for (struct keywordTypes *kw = cfi->keywords; kw->key; kw++) {
748     if (kw->type == type)
749        return kw;
750        }
751        return NULL;
752    }
753    
754    static char *getKeyByType(enum lineType_e type, struct configFileInfo * cfi) {
755        struct keywordTypes *kt = getKeywordByType(type, cfi);
756        if (kt)
757     return kt->key;
758        return "unknown";
759    }
760    
761    static char * getpathbyspec(char *device) {
762        if (!blkid)
763            blkid_get_cache(&blkid, NULL);
764    
765        return blkid_get_devname(blkid, device, NULL);
766    }
767    
768    static char * getuuidbydev(char *device) {
769        if (!blkid)
770     blkid_get_cache(&blkid, NULL);
771    
772        return blkid_get_tag_value(blkid, "UUID", device);
773    }
774    
775    static enum lineType_e getTypeByKeyword(char * keyword,
776     struct configFileInfo * cfi) {
777        for (struct keywordTypes *kw = cfi->keywords; kw->key; kw++) {
778     if (cfi->caseInsensitive) {
779        if (!strcasecmp(keyword, kw->key))
780                    return kw->type;
781     } else {
782        if (!strcmp(keyword, kw->key))
783            return kw->type;
784     }
785        }
786        return LT_UNKNOWN;
787    }
788    
789    static struct singleLine * getLineByType(enum lineType_e type,
790     struct singleLine * line) {
791        dbgPrintf("getLineByType(%d): ", type);
792        for (; line; line = line->next) {
793     dbgPrintf("%d:%s ", line->type,
794      line->numElements ? line->elements[0].item : "(empty)");
795     if (line->type & type) break;
796        }
797        dbgPrintf(line ? "\n" : " (failed)\n");
798        return line;
799    }
800    
801  static int isBracketedTitle(struct singleLine * line) {  static int isBracketedTitle(struct singleLine * line) {
802      if ((*line->elements[0].item == '[') && (line->numElements == 1)) {      if (line->numElements == 1 && *line->elements[0].item == '[') {
803          int len = strlen(line->elements[0].item);          int len = strlen(line->elements[0].item);
804          if (*(line->elements[0].item + len - 1) == ']') {          if (*(line->elements[0].item + len - 1) == ']') {
805              /* FIXME: this is a hack... */              /* FIXME: this is a hack... */
# Line 326  static int isBracketedTitle(struct singl Line 811  static int isBracketedTitle(struct singl
811      return 0;      return 0;
812  }  }
813    
814  /* figure out if this is a entry separator */  static int isEntryStart(struct singleLine * line,
 static int isEntrySeparator(struct singleLine * line,  
815                              struct configFileInfo * cfi) {                              struct configFileInfo * cfi) {
816      if (line->type == LT_WHITESPACE)      return line->type == cfi->entryStart || line->type == LT_OTHER ||
817   return 0;   (cfi->titleBracketed && isBracketedTitle(line));
     if (line->type == cfi->entrySeparator)  
         return 1;  
     if (line->type == LT_OTHER)  
         return 1;  
     if (cfi->titleBracketed && isBracketedTitle(line)) {  
         return 1;  
     }  
     return 0;  
818  }  }
819    
820  /* extract the title from within brackets (for zipl) */  /* extract the title from within brackets (for zipl) */
821  static char * extractTitle(struct singleLine * line) {  static char * extractTitle(struct singleLine * line) {
822      /* bracketed title... let's extract it (leaks a byte) */      /* bracketed title... let's extract it (leaks a byte) */
823      char * title;      char * title = NULL;
824      title = strdup(line->elements[0].item);      if (line->type == LT_TITLE) {
825      title++;   title = strdup(line->elements[0].item);
826      *(title + strlen(title) - 1) = '\0';   title++;
827     *(title + strlen(title) - 1) = '\0';
828        } else if (line->type == LT_MENUENTRY)
829     title = strdup(line->elements[1].item);
830        else
831     return NULL;
832      return title;      return title;
833  }  }
834    
# Line 389  static void lineInit(struct singleLine * Line 870  static void lineInit(struct singleLine *
870      line->next = NULL;      line->next = NULL;
871  }  }
872    
873  static void lineFree(struct singleLine * line) {  struct singleLine * lineDup(struct singleLine * line) {
874      int i;      struct singleLine * newLine = malloc(sizeof(*newLine));
875    
876        newLine->indent = strdup(line->indent);
877        newLine->next = NULL;
878        newLine->type = line->type;
879        newLine->numElements = line->numElements;
880        newLine->elements = malloc(sizeof(*newLine->elements) *
881           newLine->numElements);
882    
883        for (int i = 0; i < newLine->numElements; i++) {
884     newLine->elements[i].indent = strdup(line->elements[i].indent);
885     newLine->elements[i].item = strdup(line->elements[i].item);
886        }
887    
888        return newLine;
889    }
890    
891    static void lineFree(struct singleLine * line) {
892      if (line->indent) free(line->indent);      if (line->indent) free(line->indent);
893    
894      for (i = 0; i < line->numElements; i++) {      for (int i = 0; i < line->numElements; i++) {
895   free(line->elements[i].item);   free(line->elements[i].item);
896   free(line->elements[i].indent);   free(line->elements[i].indent);
897      }      }
# Line 405  static void lineFree(struct singleLine * Line 902  static void lineFree(struct singleLine *
902    
903  static int lineWrite(FILE * out, struct singleLine * line,  static int lineWrite(FILE * out, struct singleLine * line,
904       struct configFileInfo * cfi) {       struct configFileInfo * cfi) {
     int i;  
   
905      if (fprintf(out, "%s", line->indent) == -1) return -1;      if (fprintf(out, "%s", line->indent) == -1) return -1;
906    
907      for (i = 0; i < line->numElements; i++) {      for (int i = 0; i < line->numElements; i++) {
908     /* Need to handle this, because we strip the quotes from
909     * menuentry when read it. */
910     if (line->type == LT_MENUENTRY && i == 1) {
911        if(!isquote(*line->elements[i].item))
912     fprintf(out, "\'%s\'", line->elements[i].item);
913        else
914     fprintf(out, "%s", line->elements[i].item);
915        fprintf(out, "%s", line->elements[i].indent);
916    
917        continue;
918     }
919    
920   if (i == 1 && line->type == LT_KERNELARGS && cfi->argsInQuotes)   if (i == 1 && line->type == LT_KERNELARGS && cfi->argsInQuotes)
921      if (fputc('"', out) == EOF) return -1;      if (fputc('"', out) == EOF) return -1;
922    
923   if (fprintf(out, "%s", line->elements[i].item) == -1) return -1;   if (fprintf(out, "%s", line->elements[i].item) == -1) return -1;
924   if (fprintf(out, "%s", line->elements[i].indent) == -1) return -1;   if (i < line->numElements - 1)
925        if (fprintf(out, "%s", line->elements[i].indent) == -1) return -1;
926      }      }
927    
928      if (line->type == LT_KERNELARGS && cfi->argsInQuotes)      if (line->type == LT_KERNELARGS && cfi->argsInQuotes)
# Line 433  static int getNextLine(char ** bufPtr, s Line 941  static int getNextLine(char ** bufPtr, s
941      char * chptr;      char * chptr;
942      int elementsAlloced = 0;      int elementsAlloced = 0;
943      struct lineElement * element;      struct lineElement * element;
     struct keywordTypes * keywords = cfi->keywords;  
944      int first = 1;      int first = 1;
     int i;  
945    
946      lineFree(line);      lineFree(line);
947    
# Line 489  static int getNextLine(char ** bufPtr, s Line 995  static int getNextLine(char ** bufPtr, s
995      if (!line->numElements)      if (!line->numElements)
996   line->type = LT_WHITESPACE;   line->type = LT_WHITESPACE;
997      else {      else {
998   for (i = 0; keywords[i].key; i++)   line->type = getTypeByKeyword(line->elements[0].item, cfi);
999      if (!strcmp(line->elements[0].item, keywords[i].key)) break;   if (line->type == LT_UNKNOWN) {
   
  if (keywords[i].key) {  
     line->type = keywords[i].type;  
  } else {  
     line->type = LT_UNKNOWN;  
               
1000              /* zipl does [title] instead of something reasonable like all              /* zipl does [title] instead of something reasonable like all
1001               * the other boot loaders.  kind of ugly */               * the other boot loaders.  kind of ugly */
1002              if (cfi->titleBracketed && isBracketedTitle(line)) {              if (cfi->titleBracketed && isBracketedTitle(line)) {
# Line 510  static int getNextLine(char ** bufPtr, s Line 1010  static int getNextLine(char ** bufPtr, s
1010      if (*line->elements[0].item == '#') {      if (*line->elements[0].item == '#') {
1011   char * fullLine;   char * fullLine;
1012   int len;   int len;
  int i;  
1013    
1014   len = strlen(line->indent);   len = strlen(line->indent);
1015   for (i = 0; i < line->numElements; i++)   for (int i = 0; i < line->numElements; i++)
1016      len += strlen(line->elements[i].item) +      len += strlen(line->elements[i].item) +
1017     strlen(line->elements[i].indent);     strlen(line->elements[i].indent);
1018    
# Line 522  static int getNextLine(char ** bufPtr, s Line 1021  static int getNextLine(char ** bufPtr, s
1021   free(line->indent);   free(line->indent);
1022   line->indent = fullLine;   line->indent = fullLine;
1023    
1024   for (i = 0; i < line->numElements; i++) {   for (int i = 0; i < line->numElements; i++) {
1025      strcat(fullLine, line->elements[i].item);      strcat(fullLine, line->elements[i].item);
1026      strcat(fullLine, line->elements[i].indent);      strcat(fullLine, line->elements[i].indent);
1027      free(line->elements[i].item);      free(line->elements[i].item);
# Line 532  static int getNextLine(char ** bufPtr, s Line 1031  static int getNextLine(char ** bufPtr, s
1031   line->type = LT_WHITESPACE;   line->type = LT_WHITESPACE;
1032   line->numElements = 0;   line->numElements = 0;
1033      }      }
1034     } else {
1035     struct keywordTypes *kw;
1036    
1037     kw = getKeywordByType(line->type, cfi);
1038    
1039     /* space isn't the only separator, we need to split
1040     * elements up more
1041     */
1042     if (!isspace(kw->separatorChar)) {
1043        char indent[2] = "";
1044        indent[0] = kw->separatorChar;
1045        for (int i = 1; i < line->numElements; i++) {
1046     char *p;
1047     int numNewElements;
1048    
1049     numNewElements = 0;
1050     p = line->elements[i].item;
1051     while (*p != '\0') {
1052     if (*p == kw->separatorChar)
1053     numNewElements++;
1054     p++;
1055     }
1056     if (line->numElements + numNewElements >= elementsAlloced) {
1057     elementsAlloced += numNewElements + 5;
1058     line->elements = realloc(line->elements,
1059        sizeof(*line->elements) * elementsAlloced);
1060     }
1061    
1062     for (int j = line->numElements; j > i; j--) {
1063     line->elements[j + numNewElements] = line->elements[j];
1064     }
1065     line->numElements += numNewElements;
1066    
1067     p = line->elements[i].item;
1068     while (*p != '\0') {
1069    
1070     while (*p != kw->separatorChar && *p != '\0') p++;
1071     if (*p == '\0') {
1072     break;
1073     }
1074    
1075     line->elements[i + 1].indent = line->elements[i].indent;
1076     line->elements[i].indent = strdup(indent);
1077     *p++ = '\0';
1078     i++;
1079     line->elements[i].item = strdup(p);
1080     }
1081        }
1082     }
1083   }   }
1084      }      }
1085    
1086      return 0;      return 0;
1087  }  }
1088    
1089    static int isnumber(const char *s)
1090    {
1091        int i;
1092        for (i = 0; s[i] != '\0'; i++)
1093     if (s[i] < '0' || s[i] > '9')
1094        return 0;
1095        return i;
1096    }
1097    
1098  static struct grubConfig * readConfig(const char * inName,  static struct grubConfig * readConfig(const char * inName,
1099        struct configFileInfo * cfi) {        struct configFileInfo * cfi) {
1100      int in;      int in;
# Line 549  static struct grubConfig * readConfig(co Line 1106  static struct grubConfig * readConfig(co
1106      struct singleLine * last = NULL, * line, * defaultLine = NULL;      struct singleLine * last = NULL, * line, * defaultLine = NULL;
1107      char * end;      char * end;
1108      struct singleEntry * entry = NULL;      struct singleEntry * entry = NULL;
1109      int i, len;      int len;
1110      char * buf;      char * buf;
1111    
1112      if (!strcmp(inName, "-")) {      if (inName == NULL) {
1113            printf("Could not find bootloader configuration\n");
1114            exit(1);
1115        } else if (!strcmp(inName, "-")) {
1116   in = 0;   in = 0;
1117      } else {      } else {
1118   if ((in = open(inName, O_RDONLY)) < 0) {   if ((in = open(inName, O_RDONLY)) < 0) {
# Line 595  static struct grubConfig * readConfig(co Line 1155  static struct grubConfig * readConfig(co
1155      cfg->secondaryIndent = strdup(line->indent);      cfg->secondaryIndent = strdup(line->indent);
1156   }   }
1157    
1158   if (isEntrySeparator(line, cfi)) {   if (isEntryStart(line, cfi) || (cfg->entries && !sawEntry)) {
1159      sawEntry = 1;      sawEntry = 1;
1160      if (!entry) {      if (!entry) {
1161   cfg->entries = malloc(sizeof(*entry));   cfg->entries = malloc(sizeof(*entry));
# Line 611  static struct grubConfig * readConfig(co Line 1171  static struct grubConfig * readConfig(co
1171      entry->next = NULL;      entry->next = NULL;
1172   }   }
1173    
1174   if (line->type == LT_DEFAULT && line->numElements == 2) {   if (line->type == LT_SET_VARIABLE) {
1175        dbgPrintf("found 'set' command (%d elements): ", line->numElements);
1176        dbgPrintf("%s", line->indent);
1177        for (int i = 0; i < line->numElements; i++)
1178     dbgPrintf("\"%s\"%s", line->elements[i].item, line->elements[i].indent);
1179        dbgPrintf("\n");
1180        struct keywordTypes *kwType = getKeywordByType(LT_DEFAULT, cfi);
1181        if (kwType && line->numElements == 3 &&
1182        !strcmp(line->elements[1].item, kwType->key) &&
1183        !is_special_grub2_variable(line->elements[2].item)) {
1184     dbgPrintf("Line sets default config\n");
1185     cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
1186     defaultLine = line;
1187        }
1188     } else if (line->type == LT_DEFAULT && line->numElements == 2) {
1189      cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;      cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
1190      defaultLine = line;      defaultLine = line;
1191    
1192            } else if (iskernel(line->type)) {
1193        /* if by some freak chance this is multiboot and the "module"
1194         * lines came earlier in the template, make sure to use LT_HYPER
1195         * instead of LT_KERNEL now
1196         */
1197        if (entry->multiboot)
1198     line->type = LT_HYPER;
1199    
1200          } else if (line->type == LT_MBMODULE) {          } else if (line->type == LT_MBMODULE) {
1201        /* go back and fix the LT_KERNEL line to indicate LT_HYPER
1202         * instead, now that we know this is a multiboot entry.
1203         * This only applies to grub, but that's the only place we
1204         * should find LT_MBMODULE lines anyway.
1205         */
1206        for (struct singleLine *l = entry->lines; l; l = l->next) {
1207     if (l->type == LT_HYPER)
1208        break;
1209     else if (iskernel(l->type)) {
1210        l->type = LT_HYPER;
1211        break;
1212     }
1213        }
1214              entry->multiboot = 1;              entry->multiboot = 1;
1215    
1216     } else if (line->type == LT_HYPER) {
1217        entry->multiboot = 1;
1218    
1219   } else if (line->type == LT_FALLBACK && line->numElements == 2) {   } else if (line->type == LT_FALLBACK && line->numElements == 2) {
1220      cfg->fallbackImage = strtol(line->elements[1].item, &end, 10);      cfg->fallbackImage = strtol(line->elements[1].item, &end, 10);
1221      if (*end) cfg->fallbackImage = -1;      if (*end) cfg->fallbackImage = -1;
1222    
1223   } else if (line->type == LT_TITLE && line->numElements > 1) {   } else if (line->type == LT_TITLE && line->numElements > 1) {
1224      /* make the title a single argument (undoing our parsing) */      /* make the title a single argument (undoing our parsing) */
1225      len = 0;      len = 0;
1226      for (i = 1; i < line->numElements; i++) {      for (int i = 1; i < line->numElements; i++) {
1227   len += strlen(line->elements[i].item);   len += strlen(line->elements[i].item);
1228   len += strlen(line->elements[i].indent);   len += strlen(line->elements[i].indent);
1229      }      }
1230      buf = malloc(len + 1);      buf = malloc(len + 1);
1231      *buf = '\0';      *buf = '\0';
1232    
1233      for (i = 1; i < line->numElements; i++) {      for (int i = 1; i < line->numElements; i++) {
1234   strcat(buf, line->elements[i].item);   strcat(buf, line->elements[i].item);
1235   free(line->elements[i].item);   free(line->elements[i].item);
1236    
# Line 643  static struct grubConfig * readConfig(co Line 1244  static struct grubConfig * readConfig(co
1244      line->elements[line->numElements - 1].indent;      line->elements[line->numElements - 1].indent;
1245      line->elements[1].item = buf;      line->elements[1].item = buf;
1246      line->numElements = 2;      line->numElements = 2;
1247     } else if (line->type == LT_MENUENTRY && line->numElements > 3) {
1248        /* let --remove-kernel="TITLE=what" work */
1249        len = 0;
1250        char *extras;
1251        char *title;
1252    
1253        for (int i = 1; i < line->numElements; i++) {
1254     len += strlen(line->elements[i].item);
1255     len += strlen(line->elements[i].indent);
1256        }
1257        buf = malloc(len + 1);
1258        *buf = '\0';
1259    
1260        /* allocate mem for extra flags. */
1261        extras = malloc(len + 1);
1262        *extras = '\0';
1263    
1264        /* get title. */
1265        for (int i = 0; i < line->numElements; i++) {
1266     if (!strcmp(line->elements[i].item, "menuentry"))
1267        continue;
1268     if (isquote(*line->elements[i].item))
1269        title = line->elements[i].item + 1;
1270     else
1271        title = line->elements[i].item;
1272    
1273     len = strlen(title);
1274            if (isquote(title[len-1])) {
1275        strncat(buf, title,len-1);
1276        break;
1277     } else {
1278        strcat(buf, title);
1279        strcat(buf, line->elements[i].indent);
1280     }
1281        }
1282    
1283        /* get extras */
1284        int count = 0;
1285        for (int i = 0; i < line->numElements; i++) {
1286     if (count >= 2) {
1287        strcat(extras, line->elements[i].item);
1288        strcat(extras, line->elements[i].indent);
1289     }
1290    
1291     if (!strcmp(line->elements[i].item, "menuentry"))
1292        continue;
1293    
1294     /* count ' or ", there should be two in menuentry line. */
1295     if (isquote(*line->elements[i].item))
1296        count++;
1297    
1298     len = strlen(line->elements[i].item);
1299    
1300     if (isquote(line->elements[i].item[len -1]))
1301        count++;
1302    
1303     /* ok, we get the final ' or ", others are extras. */
1304                }
1305        line->elements[1].indent =
1306     line->elements[line->numElements - 2].indent;
1307        line->elements[1].item = buf;
1308        line->elements[2].indent =
1309     line->elements[line->numElements - 2].indent;
1310        line->elements[2].item = extras;
1311        line->numElements = 3;
1312   } else if (line->type == LT_KERNELARGS && cfi->argsInQuotes) {   } else if (line->type == LT_KERNELARGS && cfi->argsInQuotes) {
1313      /* Strip off any " which may be present; they'll be put back      /* Strip off any " which may be present; they'll be put back
1314         on write. This is one of the few (the only?) places that grubby         on write. This is one of the few (the only?) places that grubby
# Line 651  static struct grubConfig * readConfig(co Line 1317  static struct grubConfig * readConfig(co
1317      if (line->numElements >= 2) {      if (line->numElements >= 2) {
1318   int last, len;   int last, len;
1319    
1320   if (*line->elements[1].item == '"')   if (isquote(*line->elements[1].item))
1321      memcpy(line->elements[1].item, line->elements[1].item + 1,      memmove(line->elements[1].item, line->elements[1].item + 1,
1322     strlen(line->elements[1].item + 1) + 1);      strlen(line->elements[1].item + 1) + 1);
1323    
1324   last = line->numElements - 1;   last = line->numElements - 1;
1325   len = strlen(line->elements[last].item) - 1;   len = strlen(line->elements[last].item) - 1;
1326   if (line->elements[last].item[len] == '"')   if (isquote(line->elements[last].item[len]))
1327      line->elements[last].item[len] = '\0';      line->elements[last].item[len] = '\0';
1328      }      }
   
1329   }   }
1330    
1331   /* If we find a generic config option which should live at the   /* If we find a generic config option which should live at the
# Line 680  static struct grubConfig * readConfig(co Line 1345  static struct grubConfig * readConfig(co
1345   movedLine = 1;   movedLine = 1;
1346   continue; /* without setting 'last' */   continue; /* without setting 'last' */
1347   }   }
1348    
1349   /* If a second line of whitespace happens after a generic option   /* If a second line of whitespace happens after a generic option
1350     which was moved, drop it. */     which was moved, drop it. */
1351   if (movedLine && line->type == LT_WHITESPACE && last->type == LT_WHITESPACE) {   if (movedLine && line->type == LT_WHITESPACE && last->type == LT_WHITESPACE) {
# Line 695  static struct grubConfig * readConfig(co Line 1361  static struct grubConfig * readConfig(co
1361   entry->lines = line;   entry->lines = line;
1362      else      else
1363   last->next = line;   last->next = line;
1364        dbgPrintf("readConfig added %s to %p\n", getKeyByType(line->type, cfi), entry);
1365    
1366        /* we could have seen this outside of an entry... if so, we
1367         * ignore it like any other line we don't grok */
1368        if (line->type == LT_ENTRY_END && sawEntry)
1369     sawEntry = 0;
1370   } else {   } else {
1371      if (!cfg->theLines)      if (!cfg->theLines)
1372   cfg->theLines = line;   cfg->theLines = line;
1373      else {      else
1374   last->next = line;   last->next = line;
1375      }      dbgPrintf("readConfig added %s to cfg\n", getKeyByType(line->type, cfi));
1376   }   }
1377    
1378   last = line;   last = line;
# Line 708  static struct grubConfig * readConfig(co Line 1380  static struct grubConfig * readConfig(co
1380    
1381      free(incoming);      free(incoming);
1382    
1383        dbgPrintf("defaultLine is %s\n", defaultLine ? "set" : "unset");
1384      if (defaultLine) {      if (defaultLine) {
1385   if (cfi->defaultSupportSaved &&          if (defaultLine->numElements > 2 &&
1386        cfi->defaultSupportSaved &&
1387        !strncmp(defaultLine->elements[2].item,"\"${saved_entry}\"", 16)) {
1388     cfg->cfi->defaultIsSaved = 1;
1389     cfg->defaultImage = DEFAULT_SAVED_GRUB2;
1390     if (cfg->cfi->getEnv) {
1391        char *defTitle = cfi->getEnv(cfg->cfi, "saved_entry");
1392        if (defTitle) {
1393     int index = 0;
1394     if (isnumber(defTitle)) {
1395        index = atoi(defTitle);
1396        entry = findEntryByIndex(cfg, index);
1397     } else {
1398        entry = findEntryByTitle(cfg, defTitle, &index);
1399     }
1400     if (entry)
1401        cfg->defaultImage = index;
1402        }
1403     }
1404     } else if (cfi->defaultIsVariable) {
1405        char *value = defaultLine->elements[2].item;
1406        while (*value && (*value == '"' || *value == '\'' ||
1407        *value == ' ' || *value == '\t'))
1408     value++;
1409        cfg->defaultImage = strtol(value, &end, 10);
1410        while (*end && (*end == '"' || *end == '\'' ||
1411        *end == ' ' || *end == '\t'))
1412     end++;
1413        if (*end) cfg->defaultImage = -1;
1414     } else if (cfi->defaultSupportSaved &&
1415   !strncmp(defaultLine->elements[1].item, "saved", 5)) {   !strncmp(defaultLine->elements[1].item, "saved", 5)) {
1416      cfg->defaultImage = DEFAULT_SAVED;      cfg->defaultImage = DEFAULT_SAVED;
1417   } else if (cfi->defaultIsIndex) {   } else if (cfi->defaultIsIndex) {
1418      cfg->defaultImage = strtol(defaultLine->elements[1].item, &end, 10);      cfg->defaultImage = strtol(defaultLine->elements[1].item, &end, 10);
1419      if (*end) cfg->defaultImage = -1;      if (*end) cfg->defaultImage = -1;
1420   } else if (defaultLine->numElements >= 2) {   } else if (defaultLine->numElements >= 2) {
1421      i = 0;      int i = 0;
1422      while ((entry = findEntryByIndex(cfg, i))) {      while ((entry = findEntryByIndex(cfg, i))) {
1423   for (line = entry->lines; line; line = line->next)   for (line = entry->lines; line; line = line->next)
1424      if (line->type == LT_TITLE) break;      if (line->type == LT_TITLE) break;
# Line 730  static struct grubConfig * readConfig(co Line 1432  static struct grubConfig * readConfig(co
1432                                  extractTitle(line))) break;                                  extractTitle(line))) break;
1433                  }                  }
1434   i++;   i++;
1435     entry = NULL;
1436      }      }
1437    
1438      if (entry) cfg->defaultImage = i;      if (entry){
1439            cfg->defaultImage = i;
1440        }else{
1441            cfg->defaultImage = -1;
1442        }
1443     }
1444        } else if (cfg->cfi->defaultIsSaved && cfg->cfi->getEnv) {
1445     char *defTitle = cfi->getEnv(cfg->cfi, "saved_entry");
1446     if (defTitle) {
1447        int index = 0;
1448        if (isnumber(defTitle)) {
1449     index = atoi(defTitle);
1450     entry = findEntryByIndex(cfg, index);
1451        } else {
1452     entry = findEntryByTitle(cfg, defTitle, &index);
1453        }
1454        if (entry)
1455     cfg->defaultImage = index;
1456   }   }
1457        } else {
1458            cfg->defaultImage = 0;
1459      }      }
1460    
1461      return cfg;      return cfg;
# Line 749  static void writeDefault(FILE * out, cha Line 1471  static void writeDefault(FILE * out, cha
1471    
1472      if (cfg->defaultImage == DEFAULT_SAVED)      if (cfg->defaultImage == DEFAULT_SAVED)
1473   fprintf(out, "%sdefault%ssaved\n", indent, separator);   fprintf(out, "%sdefault%ssaved\n", indent, separator);
1474      else if (cfg->defaultImage > -1) {      else if (cfg->cfi->defaultIsSaved) {
1475     fprintf(out, "%sset default=\"${saved_entry}\"\n", indent);
1476     if (cfg->defaultImage >= 0 && cfg->cfi->setEnv) {
1477        char *title;
1478        entry = findEntryByIndex(cfg, cfg->defaultImage);
1479        line = getLineByType(LT_MENUENTRY, entry->lines);
1480        if (!line)
1481     line = getLineByType(LT_TITLE, entry->lines);
1482        if (line) {
1483     title = extractTitle(line);
1484     if (title)
1485        cfg->cfi->setEnv(cfg->cfi, "saved_entry", title);
1486        }
1487     }
1488        } else if (cfg->defaultImage > -1) {
1489   if (cfg->cfi->defaultIsIndex) {   if (cfg->cfi->defaultIsIndex) {
1490      fprintf(out, "%sdefault%s%d\n", indent, separator,      if (cfg->cfi->defaultIsVariable) {
1491      cfg->defaultImage);          fprintf(out, "%sset default=\"%d\"\n", indent,
1492     cfg->defaultImage);
1493        } else {
1494     fprintf(out, "%sdefault%s%d\n", indent, separator,
1495     cfg->defaultImage);
1496        }
1497   } else {   } else {
1498      int image = cfg->defaultImage;      int image = cfg->defaultImage;
1499    
# Line 769  static void writeDefault(FILE * out, cha Line 1510  static void writeDefault(FILE * out, cha
1510    
1511      if (!entry) return;      if (!entry) return;
1512    
1513      line = entry->lines;      line = getLineByType(LT_TITLE, entry->lines);
     while (line && line->type != LT_TITLE) line = line->next;  
1514    
1515      if (line && line->numElements >= 2)      if (line && line->numElements >= 2)
1516   fprintf(out, "%sdefault%s%s\n", indent, separator,   fprintf(out, "%sdefault%s%s\n", indent, separator,
# Line 804  static int writeConfig(struct grubConfig Line 1544  static int writeConfig(struct grubConfig
1544      int rc;      int rc;
1545    
1546      /* most likely the symlink is relative, so change our      /* most likely the symlink is relative, so change our
1547         directory to / */         directory to the dir of the symlink */
1548      rc = chdir("/");      char *dir = strdupa(outName);
1549        rc = chdir(dirname(dir));
1550      do {      do {
1551   buf = alloca(len + 1);   buf = alloca(len + 1);
1552   rc = readlink(outName, buf, len);   rc = readlink(basename(outName), buf, len);
1553   if (rc == len) len += 256;   if (rc == len) len += 256;
1554      } while (rc == len);      } while (rc == len);
1555            
# Line 843  static int writeConfig(struct grubConfig Line 1584  static int writeConfig(struct grubConfig
1584      }      }
1585    
1586      line = cfg->theLines;      line = cfg->theLines;
1587        struct keywordTypes *defaultKw = getKeywordByType(LT_DEFAULT, cfg->cfi);
1588      while (line) {      while (line) {
1589   if (line->type == LT_DEFAULT) {          if (line->type == LT_SET_VARIABLE && defaultKw &&
1590     line->numElements == 3 &&
1591     !strcmp(line->elements[1].item, defaultKw->key) &&
1592     !is_special_grub2_variable(line->elements[2].item)) {
1593        writeDefault(out, line->indent, line->elements[0].indent, cfg);
1594        needs &= ~MAIN_DEFAULT;
1595     } else if (line->type == LT_DEFAULT) {
1596      writeDefault(out, line->indent, line->elements[0].indent, cfg);      writeDefault(out, line->indent, line->elements[0].indent, cfg);
1597      needs &= ~MAIN_DEFAULT;      needs &= ~MAIN_DEFAULT;
1598   } else if (line->type == LT_FALLBACK) {   } else if (line->type == LT_FALLBACK) {
# Line 912  static int numEntries(struct grubConfig Line 1660  static int numEntries(struct grubConfig
1660      return i;      return i;
1661  }  }
1662    
1663  int suitableImage(struct singleEntry * entry, const char * bootPrefix,  static char *findDiskForRoot()
1664    int skipRemoved, int flags) {  {
1665      struct singleLine * line;      int fd;
1666      char * fullName;      char buf[65536];
1667      int i;      char *devname;
1668      struct stat sb, sb2;      char *chptr;
1669      char * dev;      int rc;
     char * end;  
     char * rootspec;  
1670    
1671      line = entry->lines;      if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) {
1672      while (line && line->type != LT_KERNEL) line = line->next;          fprintf(stderr, "grubby: failed to open %s: %s\n",
1673                        _PATH_MOUNTED, strerror(errno));
1674      if (!line) return 0;          return NULL;
1675      if (skipRemoved && entry->skip) return 0;      }
     if (line->numElements < 2) return 0;  
1676    
1677      if (flags & GRUBBY_BADIMAGE_OKAY) return 1;      rc = read(fd, buf, sizeof(buf) - 1);
1678        if (rc <= 0) {
1679            fprintf(stderr, "grubby: failed to read %s: %s\n",
1680                    _PATH_MOUNTED, strerror(errno));
1681            close(fd);
1682            return NULL;
1683        }
1684        close(fd);
1685        buf[rc] = '\0';
1686        chptr = buf;
1687    
1688      fullName = alloca(strlen(bootPrefix) +      char *foundanswer = NULL;
       strlen(line->elements[1].item) + 1);  
     rootspec = getRootSpecifier(line->elements[1].item);  
     sprintf(fullName, "%s%s", bootPrefix,  
             line->elements[1].item + ((rootspec != NULL) ?  
                                       strlen(rootspec) : 0));  
     if (access(fullName, R_OK)) return 0;  
1689    
1690      for (i = 2; i < line->numElements; i++)      while (chptr && chptr != buf+rc) {
1691            devname = chptr;
1692    
1693            /*
1694             * The first column of a mtab entry is the device, but if the entry is a
1695             * special device it won't start with /, so move on to the next line.
1696             */
1697            if (*devname != '/') {
1698                chptr = strchr(chptr, '\n');
1699                if (chptr)
1700                    chptr++;
1701                continue;
1702            }
1703    
1704            /* Seek to the next space */
1705            chptr = strchr(chptr, ' ');
1706            if (!chptr) {
1707                fprintf(stderr, "grubby: error parsing %s: %s\n",
1708                        _PATH_MOUNTED, strerror(errno));
1709                return NULL;
1710            }
1711    
1712            /*
1713             * The second column of a mtab entry is the mount point, we are looking
1714             * for '/' obviously.
1715             */
1716            if (*(++chptr) == '/' && *(++chptr) == ' ') {
1717                /* remember the last / entry in mtab */
1718               foundanswer = devname;
1719            }
1720    
1721            /* Next line */
1722            chptr = strchr(chptr, '\n');
1723            if (chptr)
1724                chptr++;
1725        }
1726    
1727        /* Return the last / entry found */
1728        if (foundanswer) {
1729            chptr = strchr(foundanswer, ' ');
1730            *chptr = '\0';
1731            return strdup(foundanswer);
1732        }
1733    
1734        return NULL;
1735    }
1736    
1737    void printEntry(struct singleEntry * entry, FILE *f) {
1738        int i;
1739        struct singleLine * line;
1740    
1741        for (line = entry->lines; line; line = line->next) {
1742     log_message(f, "DBG: %s", line->indent);
1743     for (i = 0; i < line->numElements; i++) {
1744        /* Need to handle this, because we strip the quotes from
1745         * menuentry when read it. */
1746        if (line->type == LT_MENUENTRY && i == 1) {
1747     if(!isquote(*line->elements[i].item))
1748        log_message(f, "\'%s\'", line->elements[i].item);
1749     else
1750        log_message(f, "%s", line->elements[i].item);
1751     log_message(f, "%s", line->elements[i].indent);
1752    
1753     continue;
1754        }
1755        
1756        log_message(f, "%s%s",
1757        line->elements[i].item, line->elements[i].indent);
1758     }
1759     log_message(f, "\n");
1760        }
1761    }
1762    
1763    void notSuitablePrintf(struct singleEntry * entry, int okay, const char *fmt, ...)
1764    {
1765        static int once;
1766        va_list argp, argq;
1767    
1768        va_start(argp, fmt);
1769    
1770        va_copy(argq, argp);
1771        if (!once) {
1772     log_time(NULL);
1773     log_message(NULL, "command line: %s\n", saved_command_line);
1774        }
1775        log_message(NULL, "DBG: Image entry %s: ", okay ? "succeeded" : "failed");
1776        log_vmessage(NULL, fmt, argq);
1777    
1778        printEntry(entry, NULL);
1779        va_end(argq);
1780    
1781        if (!debug) {
1782     once = 1;
1783         va_end(argp);
1784     return;
1785        }
1786    
1787        if (okay) {
1788     va_end(argp);
1789     return;
1790        }
1791    
1792        if (!once)
1793     log_message(stderr, "DBG: command line: %s\n", saved_command_line);
1794        once = 1;
1795        fprintf(stderr, "DBG: Image entry failed: ");
1796        vfprintf(stderr, fmt, argp);
1797        printEntry(entry, stderr);
1798        va_end(argp);
1799    }
1800    
1801    #define beginswith(s, c) ((s) && (s)[0] == (c))
1802    
1803    static int endswith(const char *s, char c)
1804    {
1805     int slen;
1806    
1807     if (!s || !s[0])
1808     return 0;
1809     slen = strlen(s) - 1;
1810    
1811     return s[slen] == c;
1812    }
1813    
1814    int suitableImage(struct singleEntry * entry, const char * bootPrefix,
1815      int skipRemoved, int flags) {
1816        struct singleLine * line;
1817        char * fullName;
1818        int i;
1819        char * dev;
1820        char * rootspec;
1821        char * rootdev;
1822    
1823        if (skipRemoved && entry->skip) {
1824     notSuitablePrintf(entry, 0, "marked to skip\n");
1825     return 0;
1826        }
1827    
1828        line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
1829        if (!line) {
1830     notSuitablePrintf(entry, 0, "no line found\n");
1831     return 0;
1832        }
1833        if (line->numElements < 2) {
1834     notSuitablePrintf(entry, 0, "line has only %d elements\n",
1835        line->numElements);
1836     return 0;
1837        }
1838    
1839        if (flags & GRUBBY_BADIMAGE_OKAY) {
1840        notSuitablePrintf(entry, 1, "\n");
1841        return 1;
1842        }
1843    
1844        fullName = alloca(strlen(bootPrefix) +
1845          strlen(line->elements[1].item) + 1);
1846        rootspec = getRootSpecifier(line->elements[1].item);
1847        int rootspec_offset = rootspec ? strlen(rootspec) : 0;
1848        int hasslash = endswith(bootPrefix, '/') ||
1849         beginswith(line->elements[1].item + rootspec_offset, '/');
1850        sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/",
1851                line->elements[1].item + rootspec_offset);
1852        if (access(fullName, R_OK)) {
1853     notSuitablePrintf(entry, 0, "access to %s failed\n", fullName);
1854     return 0;
1855        }
1856        for (i = 2; i < line->numElements; i++)
1857   if (!strncasecmp(line->elements[i].item, "root=", 5)) break;   if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1858      if (i < line->numElements) {      if (i < line->numElements) {
1859   dev = line->elements[i].item + 5;   dev = line->elements[i].item + 5;
1860      } else {      } else {
1861   /* look for a lilo style LT_ROOT line */   /* look for a lilo style LT_ROOT line */
1862   line = entry->lines;   line = getLineByType(LT_ROOT, entry->lines);
  while (line && line->type != LT_ROOT) line = line->next;  
1863    
1864   if (line && line->numElements >= 2) {   if (line && line->numElements >= 2) {
1865      dev = line->elements[1].item;      dev = line->elements[1].item;
1866   } else {   } else {
1867              int type;      /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS.
1868      /* 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.
1869      line = entry->lines;       */
1870        line = getLineByType(LT_KERNELARGS|LT_MBMODULE, entry->lines);
             type = ((entry->multiboot) ? LT_MBMODULE : LT_KERNELARGS);  
   
     while (line && line->type != type) line = line->next;  
1871    
1872              /* failed to find one */              /* failed to find one */
1873              if (!line) return 0;              if (!line) {
1874     notSuitablePrintf(entry, 0, "no line found\n");
1875     return 0;
1876                }
1877    
1878      for (i = 1; i < line->numElements; i++)      for (i = 1; i < line->numElements; i++)
1879          if (!strncasecmp(line->elements[i].item, "root=", 5)) break;          if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1880      if (i < line->numElements)      if (i < line->numElements)
1881          dev = line->elements[i].item + 5;          dev = line->elements[i].item + 5;
1882      else {      else {
1883     notSuitablePrintf(entry, 0, "no root= entry found\n");
1884   /* it failed too...  can't find root= */   /* it failed too...  can't find root= */
1885          return 0;          return 0;
1886              }              }
1887   }   }
1888      }      }
1889    
1890      if (!strncmp(dev, "LABEL=", 6)) {      dev = getpathbyspec(dev);
1891   dev += 6;      if (!getpathbyspec(dev)) {
1892            notSuitablePrintf(entry, 0, "can't find blkid entry for %s\n", dev);
1893   /* check which device has this label */          return 0;
1894   dev = get_spec_by_volume_label(dev, &i, &i);      } else
1895   if (!dev) return 0;   dev = getpathbyspec(dev);
1896    
1897        rootdev = findDiskForRoot();
1898        if (!rootdev) {
1899            notSuitablePrintf(entry, 0, "can't find root device\n");
1900     return 0;
1901      }      }
1902    
1903      if (*dev == '/') {      if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) {
1904   if (stat(dev, &sb))          notSuitablePrintf(entry, 0, "uuid missing: rootdev %s, dev %s\n",
1905      return 0;   getuuidbydev(rootdev), getuuidbydev(dev));
1906      } else {          free(rootdev);
1907   sb.st_rdev = strtol(dev, &end, 16);          return 0;
1908   if (*end) return 0;      }
1909    
1910        if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) {
1911            notSuitablePrintf(entry, 0, "uuid mismatch: rootdev %s, dev %s\n",
1912     getuuidbydev(rootdev), getuuidbydev(dev));
1913     free(rootdev);
1914            return 0;
1915      }      }
     stat("/", &sb2);  
1916    
1917      if (sb.st_rdev != sb2.st_dev) return 0;      free(rootdev);
1918        notSuitablePrintf(entry, 1, "\n");
1919    
1920      return 1;      return 1;
1921  }  }
# Line 1034  struct singleEntry * findEntryByPath(str Line 1959  struct singleEntry * findEntryByPath(str
1959   entry = findEntryByIndex(config, indexVars[i]);   entry = findEntryByIndex(config, indexVars[i]);
1960   if (!entry) return NULL;   if (!entry) return NULL;
1961    
1962   line = entry->lines;   line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
  while (line && line->type != LT_KERNEL)  
     line = line->next;  
   
1963   if (!line) return NULL;   if (!line) return NULL;
1964    
1965   if (index) *index = indexVars[i];   if (index) *index = indexVars[i];
# Line 1075  struct singleEntry * findEntryByPath(str Line 1997  struct singleEntry * findEntryByPath(str
1997    
1998   if (!strncmp(kernel, "TITLE=", 6)) {   if (!strncmp(kernel, "TITLE=", 6)) {
1999      prefix = "";      prefix = "";
2000      checkType = LT_TITLE;      checkType = LT_TITLE|LT_MENUENTRY;
2001      kernel += 6;      kernel += 6;
2002   }   }
2003    
2004   while ((entry = findEntryByIndex(config, i))) {   for (entry = findEntryByIndex(config, i); entry; entry = entry->next, i++) {
2005      line = entry->lines;      if (entry->skip) continue;
     while (line && line->type != checkType) line=line->next;  
2006    
2007        dbgPrintf("findEntryByPath looking for %d %s in %p\n", checkType, kernel, entry);
2008    
2009      if (line && line->numElements >= 2 && !entry->skip) {      /* check all the lines matching checkType */
2010                  rootspec = getRootSpecifier(line->elements[1].item);      for (line = entry->lines; line; line = line->next) {
2011          if (!strcmp(line->elements[1].item  +   enum lineType_e ct = checkType;
2012                              ((rootspec != NULL) ? strlen(rootspec) : 0),   if (entry->multiboot && checkType == LT_KERNEL)
2013                              kernel + strlen(prefix)))      ct = LT_KERNEL|LT_KERNEL_EFI|LT_MBMODULE|LT_HYPER|LT_KERNEL_16;
2014                      break;   else if (checkType & LT_KERNEL)
2015              }      ct = checkType | LT_KERNEL_EFI | LT_KERNEL_16;
2016                 line = getLineByType(ct, line);
2017              /* have to check multiboot lines too */   if (!line)
2018              if (entry->multiboot) {      break;  /* not found in this entry */
2019                  while (line && line->type != LT_MBMODULE) line = line->next;  
2020                  if (line && line->numElements >= 2 && !entry->skip) {   if (line && line->type != LT_MENUENTRY &&
2021                      rootspec = getRootSpecifier(line->elements[1].item);   line->numElements >= 2) {
2022                      if (!strcmp(line->elements[1].item  +      rootspec = getRootSpecifier(line->elements[1].item);
2023                                  ((rootspec != NULL) ? strlen(rootspec) : 0),      if (!strcmp(line->elements[1].item +
2024                                  kernel + strlen(prefix)))   ((rootspec != NULL) ? strlen(rootspec) : 0),
2025                          break;   kernel + strlen(prefix)))
2026                  }   break;
2027              }   }
2028     if(line->type == LT_MENUENTRY &&
2029     !strcmp(line->elements[1].item, kernel))
2030        break;
2031        }
2032    
2033      i++;      /* make sure this entry has a kernel identifier; this skips
2034         * non-Linux boot entries (could find netbsd etc, though, which is
2035         * unfortunate)
2036         */
2037        if (line && getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines))
2038     break; /* found 'im! */
2039   }   }
2040    
2041   if (index) *index = i;   if (index) *index = i;
2042      }      }
2043    
2044      if (!entry) return NULL;      return entry;
2045    }
2046    
2047      /* make sure this entry has a kernel identifier; this skips non-Linux  struct singleEntry * findEntryByTitle(struct grubConfig * cfg, char *title,
2048         boot entries (could find netbsd etc, though, which is unfortunate) */        int * index) {
2049      line = entry->lines;      struct singleEntry * entry;
2050      while (line && line->type != LT_KERNEL) line = line->next;      struct singleLine * line;
2051      if (!line) {      int i;
2052   if (!index) index = &i;      char * newtitle;
2053   (*index)++;  
2054   return findEntryByPath(config, kernel, prefix, index);      for (i = 0, entry = cfg->entries; entry; entry = entry->next, i++) {
2055     if (index && i < *index)
2056        continue;
2057     line = getLineByType(LT_TITLE, entry->lines);
2058     if (!line)
2059        line = getLineByType(LT_MENUENTRY, entry->lines);
2060     if (!line)
2061        continue;
2062     newtitle = grub2ExtractTitle(line);
2063     if (!newtitle)
2064        continue;
2065     if (!strcmp(title, newtitle))
2066        break;
2067      }      }
2068    
2069        if (!entry)
2070     return NULL;
2071    
2072        if (index)
2073     *index = i;
2074      return entry;      return entry;
2075  }  }
2076    
# Line 1147  struct singleEntry * findTemplate(struct Line 2096  struct singleEntry * findTemplate(struct
2096      struct singleEntry * entry, * entry2;      struct singleEntry * entry, * entry2;
2097      int index;      int index;
2098    
2099      if (cfg->defaultImage > -1) {      if (cfg->cfi->defaultIsSaved) {
2100     if (cfg->cfi->getEnv) {
2101        char *defTitle = cfg->cfi->getEnv(cfg->cfi, "saved_entry");
2102        if (defTitle) {
2103     int index = 0;
2104     if (isnumber(defTitle)) {
2105        index = atoi(defTitle);
2106        entry = findEntryByIndex(cfg, index);
2107     } else {
2108        entry = findEntryByTitle(cfg, defTitle, &index);
2109     }
2110     if (entry)
2111        cfg->defaultImage = index;
2112        }
2113     }
2114        } else if (cfg->defaultImage > -1) {
2115   entry = findEntryByIndex(cfg, cfg->defaultImage);   entry = findEntryByIndex(cfg, cfg->defaultImage);
2116   if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {   if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
2117      if (indexPtr) *indexPtr = cfg->defaultImage;      if (indexPtr) *indexPtr = cfg->defaultImage;
# Line 1200  void markRemovedImage(struct grubConfig Line 2164  void markRemovedImage(struct grubConfig
2164        const char * prefix) {        const char * prefix) {
2165      struct singleEntry * entry;      struct singleEntry * entry;
2166    
2167      if (!image) return;      if (!image)
2168     return;
2169    
2170        /* check and see if we're removing the default image */
2171        if (isdigit(*image)) {
2172     entry = findEntryByPath(cfg, image, prefix, NULL);
2173     if(entry)
2174        entry->skip = 1;
2175     return;
2176        }
2177    
2178      while ((entry = findEntryByPath(cfg, image, prefix, NULL)))      while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
2179   entry->skip = 1;   entry->skip = 1;
# Line 1208  void markRemovedImage(struct grubConfig Line 2181  void markRemovedImage(struct grubConfig
2181    
2182  void setDefaultImage(struct grubConfig * config, int hasNew,  void setDefaultImage(struct grubConfig * config, int hasNew,
2183       const char * defaultKernelPath, int newIsDefault,       const char * defaultKernelPath, int newIsDefault,
2184       const char * prefix, int flags) {       const char * prefix, int flags, int index) {
2185      struct singleEntry * entry, * entry2, * newDefault;      struct singleEntry * entry, * entry2, * newDefault;
2186      int i, j;      int i, j;
2187    
2188      if (newIsDefault) {      if (newIsDefault) {
2189   config->defaultImage = 0;   config->defaultImage = 0;
2190   return;   return;
2191        } else if ((index >= 0) && config->cfi->defaultIsIndex) {
2192     if (findEntryByIndex(config, index))
2193        config->defaultImage = index;
2194     else
2195        config->defaultImage = -1;
2196     return;
2197      } else if (defaultKernelPath) {      } else if (defaultKernelPath) {
2198   i = 0;   i = 0;
2199   if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {   if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
# Line 1227  void setDefaultImage(struct grubConfig * Line 2206  void setDefaultImage(struct grubConfig *
2206    
2207      /* defaultImage now points to what we'd like to use, but before any order      /* defaultImage now points to what we'd like to use, but before any order
2208         changes */         changes */
2209      if (config->defaultImage == DEFAULT_SAVED)      if ((config->defaultImage == DEFAULT_SAVED) ||
2210     (config->defaultImage == DEFAULT_SAVED_GRUB2))
2211        /* default is set to saved, we don't want to change it */        /* default is set to saved, we don't want to change it */
2212        return;        return;
2213    
# Line 1286  void displayEntry(struct singleEntry * e Line 2266  void displayEntry(struct singleEntry * e
2266      char * root = NULL;      char * root = NULL;
2267      int i;      int i;
2268    
     line = entry->lines;  
     while (line && line->type != LT_KERNEL) line = line->next;  
   
2269      printf("index=%d\n", index);      printf("index=%d\n", index);
2270    
2271      printf("kernel=%s\n", line->elements[1].item);      line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
2272        if (!line) {
2273            printf("non linux entry\n");
2274            return;
2275        }
2276    
2277        if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2278     printf("kernel=%s\n", line->elements[1].item);
2279        else
2280     printf("kernel=%s%s\n", prefix, line->elements[1].item);
2281    
2282      if (line->numElements >= 3) {      if (line->numElements >= 3) {
2283   printf("args=\"");   printf("args=\"");
# Line 1308  void displayEntry(struct singleEntry * e Line 2294  void displayEntry(struct singleEntry * e
2294   }   }
2295   printf("\"\n");   printf("\"\n");
2296      } else {      } else {
2297   line = entry->lines;   line = getLineByType(LT_KERNELARGS, entry->lines);
  while (line && line->type != LT_KERNELARGS) line=line->next;  
   
2298   if (line) {   if (line) {
2299      char * s;      char * s;
2300    
# Line 1334  void displayEntry(struct singleEntry * e Line 2318  void displayEntry(struct singleEntry * e
2318      }      }
2319    
2320      if (!root) {      if (!root) {
2321   line = entry->lines;   line = getLineByType(LT_ROOT, entry->lines);
  while (line && line->type != LT_ROOT) line = line->next;  
   
2322   if (line && line->numElements >= 2)   if (line && line->numElements >= 2)
2323      root=line->elements[1].item;      root=line->elements[1].item;
2324      }      }
# Line 1351  void displayEntry(struct singleEntry * e Line 2333  void displayEntry(struct singleEntry * e
2333   printf("root=%s\n", s);   printf("root=%s\n", s);
2334      }      }
2335    
2336      line = entry->lines;      line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);
     while (line && line->type != LT_INITRD) line = line->next;  
2337    
2338      if (line && line->numElements >= 2) {      if (line && line->numElements >= 2) {
2339   printf("initrd=%s", prefix);   if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2340        printf("initrd=");
2341     else
2342        printf("initrd=%s", prefix);
2343    
2344   for (i = 1; i < line->numElements; i++)   for (i = 1; i < line->numElements; i++)
2345      printf("%s%s", line->elements[i].item, line->elements[i].indent);      printf("%s%s", line->elements[i].item, line->elements[i].indent);
2346   printf("\n");   printf("\n");
2347      }      }
2348    
2349        line = getLineByType(LT_TITLE, entry->lines);
2350        if (line) {
2351     printf("title=%s\n", line->elements[1].item);
2352        } else {
2353     char * title;
2354     line = getLineByType(LT_MENUENTRY, entry->lines);
2355     title = grub2ExtractTitle(line);
2356     if (title)
2357        printf("title=%s\n", title);
2358        }
2359    }
2360    
2361    int isSuseSystem(void) {
2362        const char * path;
2363        const static char default_path[] = "/etc/SuSE-release";
2364    
2365        if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2366     path = default_path;
2367    
2368        if (!access(path, R_OK))
2369     return 1;
2370        return 0;
2371    }
2372    
2373    int isSuseGrubConf(const char * path) {
2374        FILE * grubConf;
2375        char * line = NULL;
2376        size_t len = 0, res = 0;
2377    
2378        grubConf = fopen(path, "r");
2379        if (!grubConf) {
2380            dbgPrintf("Could not open SuSE configuration file '%s'\n", path);
2381     return 0;
2382        }
2383    
2384        while ((res = getline(&line, &len, grubConf)) != -1) {
2385     if (!strncmp(line, "setup", 5)) {
2386        fclose(grubConf);
2387        free(line);
2388        return 1;
2389     }
2390        }
2391    
2392        dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2393          path);
2394    
2395        fclose(grubConf);
2396        free(line);
2397        return 0;
2398    }
2399    
2400    int suseGrubConfGetLba(const char * path, int * lbaPtr) {
2401        FILE * grubConf;
2402        char * line = NULL;
2403        size_t res = 0, len = 0;
2404    
2405        if (!path) return 1;
2406        if (!lbaPtr) return 1;
2407    
2408        grubConf = fopen(path, "r");
2409        if (!grubConf) return 1;
2410    
2411        while ((res = getline(&line, &len, grubConf)) != -1) {
2412     if (line[res - 1] == '\n')
2413        line[res - 1] = '\0';
2414     else if (len > res)
2415        line[res] = '\0';
2416     else {
2417        line = realloc(line, res + 1);
2418        line[res] = '\0';
2419     }
2420    
2421     if (!strncmp(line, "setup", 5)) {
2422        if (strstr(line, "--force-lba")) {
2423            *lbaPtr = 1;
2424        } else {
2425            *lbaPtr = 0;
2426        }
2427        dbgPrintf("lba: %i\n", *lbaPtr);
2428        break;
2429     }
2430        }
2431    
2432        free(line);
2433        fclose(grubConf);
2434        return 0;
2435    }
2436    
2437    int suseGrubConfGetInstallDevice(const char * path, char ** devicePtr) {
2438        FILE * grubConf;
2439        char * line = NULL;
2440        size_t res = 0, len = 0;
2441        char * lastParamPtr = NULL;
2442        char * secLastParamPtr = NULL;
2443        char installDeviceNumber = '\0';
2444        char * bounds = NULL;
2445    
2446        if (!path) return 1;
2447        if (!devicePtr) return 1;
2448    
2449        grubConf = fopen(path, "r");
2450        if (!grubConf) return 1;
2451    
2452        while ((res = getline(&line, &len, grubConf)) != -1) {
2453     if (strncmp(line, "setup", 5))
2454        continue;
2455    
2456     if (line[res - 1] == '\n')
2457        line[res - 1] = '\0';
2458     else if (len > res)
2459        line[res] = '\0';
2460     else {
2461        line = realloc(line, res + 1);
2462        line[res] = '\0';
2463     }
2464    
2465     lastParamPtr = bounds = line + res;
2466    
2467     /* Last parameter in grub may be an optional IMAGE_DEVICE */
2468     while (!isspace(*lastParamPtr))
2469        lastParamPtr--;
2470     lastParamPtr++;
2471    
2472     secLastParamPtr = lastParamPtr - 2;
2473     dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2474    
2475     if (lastParamPtr + 3 > bounds) {
2476        dbgPrintf("lastParamPtr going over boundary");
2477        fclose(grubConf);
2478        free(line);
2479        return 1;
2480     }
2481     if (!strncmp(lastParamPtr, "(hd", 3))
2482        lastParamPtr += 3;
2483     dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);
2484    
2485     /*
2486     * Second last parameter will decide wether last parameter is
2487     * an IMAGE_DEVICE or INSTALL_DEVICE
2488     */
2489     while (!isspace(*secLastParamPtr))
2490        secLastParamPtr--;
2491     secLastParamPtr++;
2492    
2493     if (secLastParamPtr + 3 > bounds) {
2494        dbgPrintf("secLastParamPtr going over boundary");
2495        fclose(grubConf);
2496        free(line);
2497        return 1;
2498     }
2499     dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);
2500     if (!strncmp(secLastParamPtr, "(hd", 3)) {
2501        secLastParamPtr += 3;
2502        dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);
2503        installDeviceNumber = *secLastParamPtr;
2504     } else {
2505        installDeviceNumber = *lastParamPtr;
2506     }
2507    
2508     *devicePtr = malloc(6);
2509     snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);
2510     dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);
2511     fclose(grubConf);
2512     free(line);
2513     return 0;
2514        }
2515    
2516        free(line);
2517        fclose(grubConf);
2518        return 1;
2519    }
2520    
2521    int grubGetBootFromDeviceMap(const char * device,
2522         char ** bootPtr) {
2523        FILE * deviceMap;
2524        char * line = NULL;
2525        size_t res = 0, len = 0;
2526        char * devicePtr;
2527        char * bounds = NULL;
2528        const char * path;
2529        const static char default_path[] = "/boot/grub/device.map";
2530    
2531        if (!device) return 1;
2532        if (!bootPtr) return 1;
2533    
2534        if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
2535     path = default_path;
2536    
2537        dbgPrintf("opening grub device.map file from: %s\n", path);
2538        deviceMap = fopen(path, "r");
2539        if (!deviceMap)
2540     return 1;
2541    
2542        while ((res = getline(&line, &len, deviceMap)) != -1) {
2543            if (!strncmp(line, "#", 1))
2544        continue;
2545    
2546     if (line[res - 1] == '\n')
2547        line[res - 1] = '\0';
2548     else if (len > res)
2549        line[res] = '\0';
2550     else {
2551        line = realloc(line, res + 1);
2552        line[res] = '\0';
2553     }
2554    
2555     devicePtr = line;
2556     bounds = line + res;
2557    
2558     while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
2559        devicePtr++;
2560     dbgPrintf("device: %s\n", devicePtr);
2561    
2562     if (!strncmp(devicePtr, device, strlen(device))) {
2563        devicePtr += strlen(device);
2564        while (isspace(*devicePtr) && ((devicePtr + 1) <= bounds))
2565            devicePtr++;
2566    
2567        *bootPtr = strdup(devicePtr);
2568        break;
2569     }
2570        }
2571    
2572        free(line);
2573        fclose(deviceMap);
2574        return 0;
2575    }
2576    
2577    int suseGrubConfGetBoot(const char * path, char ** bootPtr) {
2578        char * grubDevice;
2579    
2580        if (suseGrubConfGetInstallDevice(path, &grubDevice))
2581     dbgPrintf("error looking for grub installation device\n");
2582        else
2583     dbgPrintf("grubby installation device: %s\n", grubDevice);
2584    
2585        if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
2586     dbgPrintf("error looking for grub boot device\n");
2587        else
2588     dbgPrintf("grubby boot device: %s\n", *bootPtr);
2589    
2590        free(grubDevice);
2591        return 0;
2592    }
2593    
2594    int parseSuseGrubConf(int * lbaPtr, char ** bootPtr) {
2595        /*
2596         * This SuSE grub configuration file at this location is not your average
2597         * grub configuration file, but instead the grub commands used to setup
2598         * grub on that system.
2599         */
2600        const char * path;
2601        const static char default_path[] = "/etc/grub.conf";
2602    
2603        if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
2604     path = default_path;
2605    
2606        if (!isSuseGrubConf(path)) return 1;
2607    
2608        if (lbaPtr) {
2609            *lbaPtr = 0;
2610            if (suseGrubConfGetLba(path, lbaPtr))
2611                return 1;
2612        }
2613    
2614        if (bootPtr) {
2615            *bootPtr = NULL;
2616            suseGrubConfGetBoot(path, bootPtr);
2617        }
2618    
2619        return 0;
2620  }  }
2621    
2622  int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {  int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {
# Line 1369  int parseSysconfigGrub(int * lbaPtr, cha Line 2626  int parseSysconfigGrub(int * lbaPtr, cha
2626      char * start;      char * start;
2627      char * param;      char * param;
2628    
2629      in = fopen("/etc/sysconfig/grub", "r");      in = fopen("/etc/conf.d/grub", "r");
2630      if (!in) return 1;      if (!in) return 1;
2631    
2632      if (lbaPtr) *lbaPtr = 0;      if (lbaPtr) *lbaPtr = 0;
# Line 1410  int parseSysconfigGrub(int * lbaPtr, cha Line 2667  int parseSysconfigGrub(int * lbaPtr, cha
2667  }  }
2668    
2669  void dumpSysconfigGrub(void) {  void dumpSysconfigGrub(void) {
2670      char * boot;      char * boot = NULL;
2671      int lba;      int lba;
2672    
2673      if (!parseSysconfigGrub(&lba, &boot)) {      if (isSuseSystem()) {
2674   if (lba) printf("lba\n");          if (parseSuseGrubConf(&lba, &boot)) {
2675   if (boot) printf("boot=%s\n", boot);      free(boot);
2676        return;
2677     }
2678        } else {
2679            if (parseSysconfigGrub(&lba, &boot)) {
2680        free(boot);
2681        return;
2682     }
2683        }
2684    
2685        if (lba) printf("lba\n");
2686        if (boot) {
2687     printf("boot=%s\n", boot);
2688     free(boot);
2689      }      }
2690  }  }
2691    
# Line 1431  int displayInfo(struct grubConfig * conf Line 2701  int displayInfo(struct grubConfig * conf
2701   return 1;   return 1;
2702      }      }
2703    
2704      /* this is a horrible hack to support /etc/sysconfig/grub; there must      /* this is a horrible hack to support /etc/conf.d/grub; there must
2705         be a better way */         be a better way */
2706      if (config->cfi == &grubConfigType) {      if (config->cfi == &grubConfigType) {
2707   dumpSysconfigGrub();   dumpSysconfigGrub();
2708      } else {      } else {
2709   line = config->theLines;   line = getLineByType(LT_BOOT, config->theLines);
  while (line && line->type != LT_BOOT) line = line->next;  
2710   if (line && line->numElements >= 1) {   if (line && line->numElements >= 1) {
2711      printf("boot=%s\n", line->elements[1].item);      printf("boot=%s\n", line->elements[1].item);
2712   }   }
2713    
2714   line = config->theLines;   line = getLineByType(LT_LBA, config->theLines);
  while (line && line->type != LT_LBA) line = line->next;  
2715   if (line) printf("lba\n");   if (line) printf("lba\n");
2716      }      }
2717    
# Line 1458  int displayInfo(struct grubConfig * conf Line 2726  int displayInfo(struct grubConfig * conf
2726      return 0;      return 0;
2727  }  }
2728    
2729    struct singleLine * addLineTmpl(struct singleEntry * entry,
2730     struct singleLine * tmplLine,
2731     struct singleLine * prevLine,
2732     const char * val,
2733     struct configFileInfo * cfi)
2734    {
2735        struct singleLine * newLine = lineDup(tmplLine);
2736    
2737        if (isEfi && cfi == &grub2ConfigType) {
2738     enum lineType_e old = newLine->type;
2739     newLine->type = preferredLineType(newLine->type, cfi);
2740     if (old != newLine->type)
2741        newLine->elements[0].item = getKeyByType(newLine->type, cfi);
2742        }
2743    
2744        if (val) {
2745     /* override the inherited value with our own.
2746     * This is a little weak because it only applies to elements[1]
2747     */
2748     if (newLine->numElements > 1)
2749        removeElement(newLine, 1);
2750     insertElement(newLine, val, 1, cfi);
2751    
2752     /* but try to keep the rootspec from the template... sigh */
2753     if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD|LT_KERNEL_EFI|LT_INITRD_EFI|LT_KERNEL_16|LT_INITRD_16)) {
2754        char * rootspec = getRootSpecifier(tmplLine->elements[1].item);
2755        if (rootspec != NULL) {
2756     free(newLine->elements[1].item);
2757     newLine->elements[1].item =
2758        sdupprintf("%s%s", rootspec, val);
2759        }
2760     }
2761        }
2762    
2763        dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
2764          newLine->elements[0].item : "");
2765    
2766        if (!entry->lines) {
2767     /* first one on the list */
2768     entry->lines = newLine;
2769        } else if (prevLine) {
2770     /* add after prevLine */
2771     newLine->next = prevLine->next;
2772     prevLine->next = newLine;
2773        }
2774    
2775        return newLine;
2776    }
2777    
2778  /* val may be NULL */  /* val may be NULL */
2779  struct singleLine *  addLine(struct singleEntry * entry,  struct singleLine *  addLine(struct singleEntry * entry,
2780       struct configFileInfo * cfi,       struct configFileInfo * cfi,
2781       enum lineType_e type, const char * defaultIndent,       enum lineType_e type, char * defaultIndent,
2782       char * val) {       const char * val) {
2783      struct singleLine * line, * prev;      struct singleLine * line, * prev;
2784      int i;      struct keywordTypes * kw;
2785        struct singleLine tmpl;
2786    
2787      for (i = 0; cfi->keywords[i].key; i++)      /* NB: This function shouldn't allocate items on the heap, rather on the
2788   if (cfi->keywords[i].type == type) break;       * stack since it calls addLineTmpl which will make copies.
2789      if (type != LT_TITLE || !cfi->titleBracketed)       */
2790          if (!cfi->keywords[i].key) abort();      if (type == LT_TITLE && cfi->titleBracketed) {
2791     /* we're doing a bracketed title (zipl) */
2792     tmpl.type = type;
2793     tmpl.numElements = 1;
2794     tmpl.elements = alloca(sizeof(*tmpl.elements));
2795     tmpl.elements[0].item = alloca(strlen(val)+3);
2796     sprintf(tmpl.elements[0].item, "[%s]", val);
2797     tmpl.elements[0].indent = "";
2798     val = NULL;
2799        } else if (type == LT_MENUENTRY) {
2800     char *lineend = "--class gnu-linux --class gnu --class os {";
2801     if (!val) {
2802        fprintf(stderr, "Line type LT_MENUENTRY requires a value\n");
2803        abort();
2804     }
2805     kw = getKeywordByType(type, cfi);
2806     if (!kw) {
2807        fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2808        abort();
2809     }
2810     tmpl.indent = "";
2811     tmpl.type = type;
2812     tmpl.numElements = 3;
2813     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2814     tmpl.elements[0].item = kw->key;
2815     tmpl.elements[0].indent = alloca(2);
2816     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2817     tmpl.elements[1].item = (char *)val;
2818     tmpl.elements[1].indent = alloca(2);
2819     sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
2820     tmpl.elements[2].item = alloca(strlen(lineend)+1);
2821     strcpy(tmpl.elements[2].item, lineend);
2822     tmpl.elements[2].indent = "";
2823        } else {
2824     kw = getKeywordByType(type, cfi);
2825     if (!kw) {
2826        fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2827        abort();
2828     }
2829     tmpl.type = type;
2830     tmpl.numElements = val ? 2 : 1;
2831     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2832     tmpl.elements[0].item = kw->key;
2833     tmpl.elements[0].indent = alloca(2);
2834     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2835     if (val) {
2836        tmpl.elements[1].item = (char *)val;
2837        tmpl.elements[1].indent = "";
2838     }
2839        }
2840    
2841      /* The last non-empty line gives us the indention to us and the line      /* The last non-empty line gives us the indention to us and the line
2842         to insert after. Note that comments are considered empty lines, which         to insert after. Note that comments are considered empty lines, which
2843         may not be ideal? If there are no lines or we are looking at the         may not be ideal? If there are no lines or we are looking at the
2844         first line, we use defaultIndent (the first line is normally indented         first line, we use defaultIndent (the first line is normally indented
2845         differently from the rest) */         differently from the rest) */
2846      if (entry->lines) {      for (line = entry->lines, prev = NULL; line; line = line->next) {
2847   line = entry->lines;   if (line->numElements) prev = line;
2848   prev = NULL;   /* fall back on the last line if prev isn't otherwise set */
2849   while (line) {   if (!line->next && !prev) prev = line;
     if (line->numElements) prev = line;  
     line = line->next;  
  }  
  if (!prev) {  
     /* just use the last line */  
     prev = entry->lines;  
     while (prev->next) prev = prev->next;  
  }  
   
  line = prev->next;  
  prev->next = malloc(sizeof(*line));  
  prev->next->next = line;  
  line = prev->next;  
   
  if (prev == entry->lines)  
     line->indent = strdup(defaultIndent);  
  else  
     line->indent = strdup(prev->indent);  
     } else {  
  line = malloc(sizeof(*line));  
  line->indent = strdup(defaultIndent);  
  line->next = NULL;  
2850      }      }
2851    
2852      if (type != LT_TITLE || !cfi->titleBracketed) {      struct singleLine *menuEntry;
2853          line->type = type;      menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
2854          line->numElements = val ? 2 : 1;      if (tmpl.type == LT_ENTRY_END) {
2855          line->elements = malloc(sizeof(*line->elements) * line->numElements);   if (menuEntry)
2856          line->elements[0].item = strdup(cfi->keywords[i].key);      tmpl.indent = menuEntry->indent;
2857          line->elements[0].indent = malloc(2);   else
2858          line->elements[0].indent[0] = cfi->keywords[i].nextChar;      tmpl.indent = defaultIndent ?: "";
2859          line->elements[0].indent[1] = '\0';      } else if (tmpl.type != LT_MENUENTRY) {
2860             if (menuEntry)
2861          if (val) {      tmpl.indent = "\t";
2862              line->elements[1].item = val;   else if (prev == entry->lines)
2863              line->elements[1].indent = strdup("");      tmpl.indent = defaultIndent ?: "";
2864          }   else
2865      } else {      tmpl.indent = prev->indent;
         /* we're doing the title of a bracketed title (zipl) */  
         line->type = type;  
         line->numElements = 1;  
         line->elements = malloc(sizeof(*line->elements) * line->numElements);  
   
         line->elements[0].item = malloc(strlen(val) + 3);  
         sprintf(line->elements[0].item, "[%s]", val);  
         line->elements[0].indent = strdup("");  
2866      }      }
2867    
2868      return line;      return addLineTmpl(entry, &tmpl, prev, val, cfi);
2869  }  }
2870    
2871  void removeLine(struct singleEntry * entry, struct singleLine * line) {  void removeLine(struct singleEntry * entry, struct singleLine * line) {
# Line 1553  void removeLine(struct singleEntry * ent Line 2890  void removeLine(struct singleEntry * ent
2890      free(line);      free(line);
2891  }  }
2892    
2893    static void requote(struct singleLine *tmplLine, struct configFileInfo * cfi)
2894    {
2895        struct singleLine newLine = {
2896     .indent = tmplLine->indent,
2897     .type = tmplLine->type,
2898     .next = tmplLine->next,
2899        };
2900        int firstQuotedItem = -1;
2901        int quoteLen = 0;
2902        int j;
2903        int element = 0;
2904        char *c;
2905    
2906        c = malloc(strlen(tmplLine->elements[0].item) + 1);
2907        strcpy(c, tmplLine->elements[0].item);
2908        insertElement(&newLine, c, element++, cfi);
2909        free(c);
2910        c = NULL;
2911    
2912        for (j = 1; j < tmplLine->numElements; j++) {
2913     if (firstQuotedItem == -1) {
2914        quoteLen += strlen(tmplLine->elements[j].item);
2915        
2916        if (isquote(tmplLine->elements[j].item[0])) {
2917     firstQuotedItem = j;
2918            quoteLen += strlen(tmplLine->elements[j].indent);
2919        } else {
2920     c = malloc(quoteLen + 1);
2921     strcpy(c, tmplLine->elements[j].item);
2922     insertElement(&newLine, c, element++, cfi);
2923     free(c);
2924     quoteLen = 0;
2925        }
2926     } else {
2927        int itemlen = strlen(tmplLine->elements[j].item);
2928        quoteLen += itemlen;
2929        quoteLen += strlen(tmplLine->elements[j].indent);
2930        
2931        if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
2932     c = malloc(quoteLen + 1);
2933     c[0] = '\0';
2934     for (int i = firstQuotedItem; i < j+1; i++) {
2935        strcat(c, tmplLine->elements[i].item);
2936        strcat(c, tmplLine->elements[i].indent);
2937     }
2938     insertElement(&newLine, c, element++, cfi);
2939     free(c);
2940    
2941     firstQuotedItem = -1;
2942     quoteLen = 0;
2943        }
2944     }
2945        }
2946        while (tmplLine->numElements)
2947     removeElement(tmplLine, 0);
2948        if (tmplLine->elements)
2949     free(tmplLine->elements);
2950    
2951        tmplLine->numElements = newLine.numElements;
2952        tmplLine->elements = newLine.elements;
2953    }
2954    
2955    static void insertElement(struct singleLine * line,
2956      const char * item, int insertHere,
2957      struct configFileInfo * cfi)
2958    {
2959        struct keywordTypes * kw;
2960        char indent[2] = "";
2961    
2962        /* sanity check */
2963        if (insertHere > line->numElements) {
2964     dbgPrintf("insertElement() adjusting insertHere from %d to %d\n",
2965      insertHere, line->numElements);
2966     insertHere = line->numElements;
2967        }
2968    
2969        line->elements = realloc(line->elements, (line->numElements + 1) *
2970         sizeof(*line->elements));
2971        memmove(&line->elements[insertHere+1],
2972        &line->elements[insertHere],
2973        (line->numElements - insertHere) *
2974        sizeof(*line->elements));
2975        line->elements[insertHere].item = strdup(item);
2976    
2977        kw = getKeywordByType(line->type, cfi);
2978    
2979        if (line->numElements == 0) {
2980     indent[0] = '\0';
2981        } else if (insertHere == 0) {
2982     indent[0] = kw->nextChar;
2983        } else if (kw->separatorChar != '\0') {
2984     indent[0] = kw->separatorChar;
2985        } else {
2986     indent[0] = ' ';
2987        }
2988    
2989        if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') {
2990     /* move the end-of-line forward */
2991     line->elements[insertHere].indent =
2992        line->elements[insertHere-1].indent;
2993     line->elements[insertHere-1].indent = strdup(indent);
2994        } else {
2995     line->elements[insertHere].indent = strdup(indent);
2996        }
2997    
2998        line->numElements++;
2999    
3000        dbgPrintf("insertElement(%s, '%s%s', %d)\n",
3001          line->elements[0].item,
3002          line->elements[insertHere].item,
3003          line->elements[insertHere].indent,
3004          insertHere);
3005    }
3006    
3007    static void removeElement(struct singleLine * line, int removeHere) {
3008        int i;
3009    
3010        /* sanity check */
3011        if (removeHere >= line->numElements) return;
3012    
3013        dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
3014          removeHere, line->elements[removeHere].item);
3015    
3016        free(line->elements[removeHere].item);
3017    
3018        if (removeHere > 1) {
3019     /* previous argument gets this argument's post-indentation */
3020     free(line->elements[removeHere-1].indent);
3021     line->elements[removeHere-1].indent =
3022        line->elements[removeHere].indent;
3023        } else {
3024     free(line->elements[removeHere].indent);
3025        }
3026    
3027        /* now collapse the array, but don't bother to realloc smaller */
3028        for (i = removeHere; i < line->numElements - 1; i++)
3029     line->elements[i] = line->elements[i + 1];
3030    
3031        line->numElements--;
3032    }
3033    
3034  int argMatch(const char * one, const char * two) {  int argMatch(const char * one, const char * two) {
3035      char * first, * second;      char * first, * second;
3036      char * chptr;      char * chptr;
# Line 1575  int updateActualImage(struct grubConfig Line 3053  int updateActualImage(struct grubConfig
3053      struct singleEntry * entry;      struct singleEntry * entry;
3054      struct singleLine * line, * rootLine;      struct singleLine * line, * rootLine;
3055      int index = 0;      int index = 0;
3056      int i, j, k;      int i, k;
3057      const char ** newArgs, ** oldArgs;      const char ** newArgs, ** oldArgs;
3058      const char ** arg;      const char ** arg;
3059      const char * chptr;      int useKernelArgs, useRoot;
     int useKernelArgs = 0;  
     int useRoot = 0;  
3060      int firstElement;      int firstElement;
3061      int *usedElements, *usedArgs;      int *usedElements;
3062        int doreplace;
3063    
3064      if (!image) return 0;      if (!image) return 0;
3065    
# Line 1609  int updateActualImage(struct grubConfig Line 3086  int updateActualImage(struct grubConfig
3086   }   }
3087      }      }
3088    
     for (i = 0; cfg->cfi->keywords[i].key; i++)  
  if (cfg->cfi->keywords[i].type == LT_KERNELARGS) break;  
3089    
3090      if (cfg->cfi->keywords[i].key)      useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
3091   useKernelArgs = 1;       && (!multibootArgs || cfg->cfi->mbConcatArgs));
3092    
3093      for (i = 0; cfg->cfi->keywords[i].key; i++)      useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
3094   if (cfg->cfi->keywords[i].type == LT_ROOT) break;         && !multibootArgs);
3095    
3096      if (cfg->cfi->keywords[i].key)      for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
  useRoot = 1;  
3097    
3098      k = 0;   if (multibootArgs && !entry->multiboot)
3099      for (arg = newArgs; *arg; arg++)      continue;
3100          k++;  
3101      usedArgs = calloc(k, sizeof(int));   /* Determine where to put the args.  If this config supports
3102     * LT_KERNELARGS, use that.  Otherwise use
3103     * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
3104     */
3105     if (useKernelArgs) {
3106        line = getLineByType(LT_KERNELARGS, entry->lines);
3107        if (!line) {
3108     /* no LT_KERNELARGS, need to add it */
3109     line = addLine(entry, cfg->cfi, LT_KERNELARGS,
3110           cfg->secondaryIndent, NULL);
3111        }
3112        firstElement = 1;
3113    
3114      while ((entry = findEntryByPath(cfg, image, prefix, &index))) {   } else if (multibootArgs) {
3115   index++;      line = getLineByType(LT_HYPER, entry->lines);
3116        if (!line) {
3117     /* a multiboot entry without LT_HYPER? */
3118     continue;
3119        }
3120        firstElement = 2;
3121    
3122   line = entry->lines;   } else {
3123   while (line && line->type != LT_KERNEL) line = line->next;      line = getLineByType(LT_KERNEL|LT_MBMODULE|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
3124   if (!line) continue;      if (!line) {
3125   firstElement = 2;   /* no LT_KERNEL or LT_MBMODULE in this entry? */
3126     continue;
3127          if (entry->multiboot && !multibootArgs) {      }
3128              /* first mb module line is the real kernel */      firstElement = 2;
             while (line && line->type != LT_MBMODULE) line = line->next;  
             firstElement = 2;  
         } else if (useKernelArgs) {  
     while (line && line->type != LT_KERNELARGS) line = line->next;  
     firstElement = 1;  
3129   }   }
3130    
3131   if (!line && useKernelArgs) {   /* handle the elilo case which does:
3132      /* no append in there, need to add it */   *   append="hypervisor args -- kernel args"
3133      line = addLine(entry, cfg->cfi, LT_KERNELARGS, NULL, NULL);   */
3134     if (entry->multiboot && cfg->cfi->mbConcatArgs) {
3135        /* this is a multiboot entry, make sure there's
3136         * -- on the args line
3137         */
3138        for (i = firstElement; i < line->numElements; i++) {
3139     if (!strcmp(line->elements[i].item, "--"))
3140        break;
3141        }
3142        if (i == line->numElements) {
3143     /* assume all existing args are kernel args,
3144     * prepend -- to make it official
3145     */
3146     insertElement(line, "--", firstElement, cfg->cfi);
3147     i = firstElement;
3148        }
3149        if (!multibootArgs) {
3150     /* kernel args start after the -- */
3151     firstElement = i + 1;
3152        }
3153     } else if (cfg->cfi->mbConcatArgs) {
3154        /* this is a non-multiboot entry, remove hyper args */
3155        for (i = firstElement; i < line->numElements; i++) {
3156     if (!strcmp(line->elements[i].item, "--"))
3157        break;
3158        }
3159        if (i < line->numElements) {
3160     /* remove args up to -- */
3161     while (strcmp(line->elements[firstElement].item, "--"))
3162        removeElement(line, firstElement);
3163     /* remove -- */
3164     removeElement(line, firstElement);
3165        }
3166   }   }
3167    
3168          usedElements = calloc(line->numElements, sizeof(int));          usedElements = calloc(line->numElements, sizeof(*usedElements));
3169    
3170          k = 0;   for (k = 0, arg = newArgs; *arg; arg++, k++) {
3171   for (arg = newArgs; *arg; arg++) {  
3172              if (usedArgs[k]) {      doreplace = 1;
                 k++;  
                 continue;  
             }  
3173      for (i = firstElement; i < line->numElements; i++) {      for (i = firstElement; i < line->numElements; i++) {
3174     if (multibootArgs && cfg->cfi->mbConcatArgs &&
3175        !strcmp(line->elements[i].item, "--"))
3176     {
3177        /* reached the end of hyper args, insert here */
3178        doreplace = 0;
3179        break;  
3180     }
3181                  if (usedElements[i])                  if (usedElements[i])
3182                      continue;                      continue;
3183   if (!argMatch(line->elements[i].item, *arg)) {   if (!argMatch(line->elements[i].item, *arg)) {
3184                      usedElements[i]=1;                      usedElements[i]=1;
                     usedArgs[k]=1;  
3185      break;      break;
3186                  }                  }
3187              }              }
     chptr = strchr(*arg, '=');  
3188    
3189      if (i < line->numElements) {      if (i < line->numElements && doreplace) {
3190   /* replace */   /* direct replacement */
3191   free(line->elements[i].item);   free(line->elements[i].item);
3192   line->elements[i].item = strdup(*arg);   line->elements[i].item = strdup(*arg);
     } else if (useRoot && !strncmp(*arg, "root=/dev/", 10) && *chptr) {  
  rootLine = entry->lines;  
  while (rootLine && rootLine->type != LT_ROOT)  
     rootLine = rootLine->next;  
  if (!rootLine) {  
     rootLine = addLine(entry, cfg->cfi, LT_ROOT, NULL, NULL);  
     rootLine->elements = realloc(rootLine->elements,  
     2 * sizeof(*rootLine->elements));  
     rootLine->numElements++;  
     rootLine->elements[1].indent = strdup("");  
     rootLine->elements[1].item = strdup("");  
  }  
3193    
3194   free(rootLine->elements[1].item);      } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
3195   rootLine->elements[1].item = strdup(chptr + 1);   /* root= replacement */
3196      } else {   rootLine = getLineByType(LT_ROOT, entry->lines);
3197   /* append */   if (rootLine) {
3198   line->elements = realloc(line->elements,      free(rootLine->elements[1].item);
3199   (line->numElements + 1) * sizeof(*line->elements));      rootLine->elements[1].item = strdup(*arg + 5);
  line->elements[line->numElements].item = strdup(*arg);  
  usedElements = realloc(usedElements,  
  (line->numElements + 1) * sizeof(int));  
  usedElements[line->numElements] = 1;  
   
  if (line->numElements > 1) {  
     /* add to existing list of arguments */  
     line->elements[line->numElements].indent =  
  line->elements[line->numElements - 1].indent;  
     line->elements[line->numElements - 1].indent = strdup(" ");  
3200   } else {   } else {
3201      /* First thing on this line; treat a bit differently. Note      rootLine = addLine(entry, cfg->cfi, LT_ROOT,
3202         this is only possible if we've added a LT_KERNELARGS         cfg->secondaryIndent, *arg + 5);
        entry */  
     line->elements[line->numElements].indent = strdup("");  
3203   }   }
3204        }
3205    
3206   line->numElements++;      else {
3207     /* insert/append */
3208     insertElement(line, *arg, i, cfg->cfi);
3209     usedElements = realloc(usedElements, line->numElements *
3210           sizeof(*usedElements));
3211     memmove(&usedElements[i + 1], &usedElements[i],
3212     line->numElements - i - 1);
3213     usedElements[i] = 1;
3214    
3215   /* if we updated a root= here even though there is a   /* if we updated a root= here even though there is a
3216     LT_ROOT available we need to remove the LT_ROOT entry     LT_ROOT available we need to remove the LT_ROOT entry
3217     (this will happen if we switch from a device to a label) */     (this will happen if we switch from a device to a label) */
3218   if (useRoot && !strncmp(*arg, "root=", 5)) {   if (useRoot && !strncmp(*arg, "root=", 5)) {
3219      rootLine = entry->lines;      rootLine = getLineByType(LT_ROOT, entry->lines);
3220      while (rootLine && rootLine->type != LT_ROOT)      if (rootLine)
  rootLine = rootLine->next;  
     if (rootLine) {  
3221   removeLine(entry, rootLine);   removeLine(entry, rootLine);
     }  
3222   }   }
3223      }      }
             k++;  
3224   }   }
3225    
3226          free(usedElements);          free(usedElements);
3227    
  /* no arguments to remove (i.e. no append line) */  
  if (!line) continue;  
   
  /* this won't remove an LT_ROOT item properly (but then again,  
    who cares? */  
3228   for (arg = oldArgs; *arg; arg++) {   for (arg = oldArgs; *arg; arg++) {
3229      for (i = firstElement; i < line->numElements; i++)      for (i = firstElement; i < line->numElements; i++) {
3230   if (!argMatch(line->elements[i].item, *arg))   if (multibootArgs && cfg->cfi->mbConcatArgs &&
3231        !strcmp(line->elements[i].item, "--"))
3232        /* reached the end of hyper args, stop here */
3233        break;
3234     if (!argMatch(line->elements[i].item, *arg)) {
3235        removeElement(line, i);
3236      break;      break;
   
     if (i < line->numElements) {  
  /* if this isn't the first argument the previous argument  
    gets this arguments post-indention */  
  if (i > firstElement) {  
     free(line->elements[i - 1].indent);  
     line->elements[i - 1].indent = line->elements[i].indent;  
3237   }   }
3238        }
3239   free(line->elements[i].item);      /* handle removing LT_ROOT line too */
3240        if (useRoot && !strncmp(*arg, "root=", 5)) {
3241   for (j = i + 1; j < line->numElements; j++)   rootLine = getLineByType(LT_ROOT, entry->lines);
3242      line->elements[j - 1] = line->elements[j];   if (rootLine)
3243        removeLine(entry, rootLine);
  line->numElements--;  
3244      }      }
3245   }   }
3246    
# Line 1760  int updateActualImage(struct grubConfig Line 3251  int updateActualImage(struct grubConfig
3251   }   }
3252      }      }
3253    
     free(usedArgs);  
3254      free(newArgs);      free(newArgs);
3255      free(oldArgs);      free(oldArgs);
3256    
# Line 1786  int updateImage(struct grubConfig * cfg, Line 3276  int updateImage(struct grubConfig * cfg,
3276      return rc;      return rc;
3277  }  }
3278    
3279    int addMBInitrd(struct grubConfig * cfg, const char *newMBKernel,
3280     const char * image, const char * prefix, const char * initrd) {
3281        struct singleEntry * entry;
3282        struct singleLine * line, * kernelLine, *endLine = NULL;
3283        int index = 0;
3284    
3285        if (!image) return 0;
3286    
3287        for (; (entry = findEntryByPath(cfg, newMBKernel, prefix, &index)); index++) {
3288            kernelLine = getLineByType(LT_MBMODULE, entry->lines);
3289            if (!kernelLine) continue;
3290    
3291            if (prefix) {
3292                int prefixLen = strlen(prefix);
3293                if (!strncmp(initrd, prefix, prefixLen))
3294                    initrd += prefixLen;
3295            }
3296     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3297     if (endLine)
3298        removeLine(entry, endLine);
3299            line = addLine(entry, cfg->cfi, preferredLineType(LT_MBMODULE,cfg->cfi),
3300     kernelLine->indent, initrd);
3301            if (!line)
3302        return 1;
3303     if (endLine) {
3304        line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3305                if (!line)
3306     return 1;
3307     }
3308    
3309            break;
3310        }
3311    
3312        return 0;
3313    }
3314    
3315    int updateInitrd(struct grubConfig * cfg, const char * image,
3316                     const char * prefix, const char * initrd) {
3317        struct singleEntry * entry;
3318        struct singleLine * line, * kernelLine, *endLine = NULL;
3319        int index = 0;
3320    
3321        if (!image) return 0;
3322    
3323        for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3324            kernelLine = getLineByType(LT_KERNEL|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
3325            if (!kernelLine) continue;
3326    
3327            line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);
3328            if (line)
3329                removeLine(entry, line);
3330            if (prefix) {
3331                int prefixLen = strlen(prefix);
3332                if (!strncmp(initrd, prefix, prefixLen))
3333                    initrd += prefixLen;
3334            }
3335     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3336     if (endLine)
3337        removeLine(entry, endLine);
3338     enum lineType_e lt;
3339     switch(kernelLine->type) {
3340        case LT_KERNEL:
3341            lt = LT_INITRD;
3342     break;
3343        case LT_KERNEL_EFI:
3344            lt = LT_INITRD_EFI;
3345     break;
3346        case LT_KERNEL_16:
3347            lt = LT_INITRD_16;
3348     break;
3349        default:
3350            lt = preferredLineType(LT_INITRD, cfg->cfi);
3351     }
3352            line = addLine(entry, cfg->cfi, lt, kernelLine->indent, initrd);
3353            if (!line)
3354        return 1;
3355     if (endLine) {
3356        line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3357                if (!line)
3358     return 1;
3359     }
3360    
3361            break;
3362        }
3363    
3364        return 0;
3365    }
3366    
3367  int checkDeviceBootloader(const char * device, const unsigned char * boot) {  int checkDeviceBootloader(const char * device, const unsigned char * boot) {
3368      int fd;      int fd;
3369      unsigned char bootSect[512];      unsigned char bootSect[512];
# Line 1809  int checkDeviceBootloader(const char * d Line 3387  int checkDeviceBootloader(const char * d
3387      if (memcmp(boot, bootSect, 3))      if (memcmp(boot, bootSect, 3))
3388   return 0;   return 0;
3389    
3390      if (boot[1] == 0xeb) {      if (boot[1] == JMP_SHORT_OPCODE) {
3391   offset = boot[2] + 2;   offset = boot[2] + 2;
3392      } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {      } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
3393   offset = (boot[3] << 8) + boot[2] + 2;   offset = (boot[3] << 8) + boot[2] + 2;
3394      } else if (boot[0] == 0xeb) {      } else if (boot[0] == JMP_SHORT_OPCODE) {
3395   offset = boot[1] + 2;        offset = boot[1] + 2;
3396            /*
3397     * it looks like grub, when copying stage1 into the mbr, patches stage1
3398     * right after the JMP location, replacing other instructions such as
3399     * JMPs for NOOPs. So, relax the check a little bit by skipping those
3400     * different bytes.
3401     */
3402          if ((bootSect[offset + 1] == NOOP_OPCODE)
3403      && (bootSect[offset + 2] == NOOP_OPCODE)) {
3404     offset = offset + 3;
3405          }
3406      } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {      } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
3407   offset = (boot[2] << 8) + boot[1] + 2;   offset = (boot[2] << 8) + boot[1] + 2;
3408      } else {      } else {
# Line 1956  int checkForLilo(struct grubConfig * con Line 3544  int checkForLilo(struct grubConfig * con
3544      return checkDeviceBootloader(line->elements[1].item, boot);      return checkDeviceBootloader(line->elements[1].item, boot);
3545  }  }
3546    
3547    int checkForGrub2(struct grubConfig * config) {
3548        if (!access("/etc/grub.d/", R_OK))
3549     return 2;
3550    
3551        return 1;
3552    }
3553    
3554  int checkForGrub(struct grubConfig * config) {  int checkForGrub(struct grubConfig * config) {
3555      int fd;      int fd;
3556      unsigned char bootSect[512];      unsigned char bootSect[512];
3557      char * boot;      char * boot;
3558        int onSuse = isSuseSystem();
3559    
3560      if (parseSysconfigGrub(NULL, &boot))  
3561   return 0;      if (onSuse) {
3562     if (parseSuseGrubConf(NULL, &boot))
3563        return 0;
3564        } else {
3565     if (parseSysconfigGrub(NULL, &boot))
3566        return 0;
3567        }
3568    
3569      /* assume grub is not installed -- not an error condition */      /* assume grub is not installed -- not an error condition */
3570      if (!boot)      if (!boot)
# Line 1976  int checkForGrub(struct grubConfig * con Line 3578  int checkForGrub(struct grubConfig * con
3578      if (read(fd, bootSect, 512) != 512) {      if (read(fd, bootSect, 512) != 512) {
3579   fprintf(stderr, _("grubby: unable to read %s: %s\n"),   fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3580   "/boot/grub/stage1", strerror(errno));   "/boot/grub/stage1", strerror(errno));
3581     close(fd);
3582     return 1;
3583        }
3584        close(fd);
3585    
3586        /* The more elaborate checks do not work on SuSE. The checks done
3587         * seem to be reasonble (at least for now), so just return success
3588         */
3589        if (onSuse)
3590     return 2;
3591    
3592        return checkDeviceBootloader(boot, bootSect);
3593    }
3594    
3595    int checkForExtLinux(struct grubConfig * config) {
3596        int fd;
3597        unsigned char bootSect[512];
3598        char * boot;
3599        char executable[] = "/boot/extlinux/extlinux";
3600    
3601        printf("entered: checkForExtLinux()\n");
3602    
3603        if (parseSysconfigGrub(NULL, &boot))
3604     return 0;
3605    
3606        /* assume grub is not installed -- not an error condition */
3607        if (!boot)
3608     return 0;
3609    
3610        fd = open(executable, O_RDONLY);
3611        if (fd < 0)
3612     /* this doesn't exist if grub hasn't been installed */
3613     return 0;
3614    
3615        if (read(fd, bootSect, 512) != 512) {
3616     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3617     executable, strerror(errno));
3618   return 1;   return 1;
3619      }      }
3620      close(fd);      close(fd);
# Line 1983  int checkForGrub(struct grubConfig * con Line 3622  int checkForGrub(struct grubConfig * con
3622      return checkDeviceBootloader(boot, bootSect);      return checkDeviceBootloader(boot, bootSect);
3623  }  }
3624    
3625    int checkForYaboot(struct grubConfig * config) {
3626        /*
3627         * This is a simplistic check that we consider good enough for own puporses
3628         *
3629         * If we were to properly check if yaboot is *installed* we'd need to:
3630         * 1) get the system boot device (LT_BOOT)
3631         * 2) considering it's a raw filesystem, check if the yaboot binary matches
3632         *    the content on the boot device
3633         * 3) if not, copy the binary to a temporary file and run "addnote" on it
3634         * 4) check again if binary and boot device contents match
3635         */
3636        if (!access("/etc/yaboot.conf", R_OK))
3637     return 2;
3638    
3639        return 1;
3640    }
3641    
3642    int checkForElilo(struct grubConfig * config) {
3643        if (!access("/etc/elilo.conf", R_OK))
3644     return 2;
3645    
3646        return 1;
3647    }
3648    
3649  static char * getRootSpecifier(char * str) {  static char * getRootSpecifier(char * str) {
3650      char * idx, * rootspec = NULL;      char * idx, * rootspec = NULL;
3651    
# Line 1994  static char * getRootSpecifier(char * st Line 3657  static char * getRootSpecifier(char * st
3657      return rootspec;      return rootspec;
3658  }  }
3659    
3660    static char * getInitrdVal(struct grubConfig * config,
3661       const char * prefix, struct singleLine *tmplLine,
3662       const char * newKernelInitrd,
3663       const char ** extraInitrds, int extraInitrdCount)
3664    {
3665        char *initrdVal, *end;
3666        int i;
3667        size_t totalSize;
3668        size_t prefixLen;
3669        char separatorChar;
3670    
3671        prefixLen = strlen(prefix);
3672        totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
3673    
3674        for (i = 0; i < extraInitrdCount; i++) {
3675     totalSize += sizeof(separatorChar);
3676     totalSize += strlen(extraInitrds[i]) - prefixLen;
3677        }
3678    
3679        initrdVal = end = malloc(totalSize);
3680    
3681        end = stpcpy (end, newKernelInitrd + prefixLen);
3682    
3683        separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
3684        for (i = 0; i < extraInitrdCount; i++) {
3685     const char *extraInitrd;
3686     int j;
3687    
3688     extraInitrd = extraInitrds[i] + prefixLen;
3689     /* Don't add entries that are already there */
3690     if (tmplLine != NULL) {
3691        for (j = 2; j < tmplLine->numElements; j++)
3692     if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
3693        break;
3694    
3695        if (j != tmplLine->numElements)
3696     continue;
3697     }
3698    
3699     *end++ = separatorChar;
3700     end = stpcpy(end, extraInitrd);
3701        }
3702    
3703        return initrdVal;
3704    }
3705    
3706  int addNewKernel(struct grubConfig * config, struct singleEntry * template,  int addNewKernel(struct grubConfig * config, struct singleEntry * template,
3707           const char * prefix,           const char * prefix,
3708   char * newKernelPath, char * newKernelTitle,   const char * newKernelPath, const char * newKernelTitle,
3709   char * newKernelArgs, char * newKernelInitrd,   const char * newKernelArgs, const char * newKernelInitrd,
3710                   char * newMBKernel, char * newMBKernelArgs) {   const char ** extraInitrds, int extraInitrdCount,
3711                     const char * newMBKernel, const char * newMBKernelArgs,
3712     const char * newDevTreePath) {
3713      struct singleEntry * new;      struct singleEntry * new;
3714      struct singleLine * newLine = NULL, * tmplLine = NULL, * lastLine = NULL;      struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
3715      int needs;      int needs;
     char * indent = NULL;  
     char * rootspec = NULL;  
3716      char * chptr;      char * chptr;
     int i;  
     enum lineType_e type;  
3717    
3718      if (!newKernelPath) return 0;      if (!newKernelPath) return 0;
3719    
# Line 2036  int addNewKernel(struct grubConfig * con Line 3743  int addNewKernel(struct grubConfig * con
3743      config->entries = new;      config->entries = new;
3744    
3745      /* copy/update from the template */      /* copy/update from the template */
3746      needs = KERNEL_KERNEL | KERNEL_INITRD | KERNEL_TITLE;      needs = NEED_KERNEL | NEED_TITLE;
3747        if (newKernelInitrd)
3748     needs |= NEED_INITRD;
3749      if (newMBKernel) {      if (newMBKernel) {
3750          needs |= KERNEL_MB;          needs |= NEED_MB;
3751          new->multiboot = 1;          new->multiboot = 1;
3752      }      }
3753        if (newDevTreePath && getKeywordByType(LT_DEVTREE, config->cfi))
3754     needs |= NEED_DEVTREE;
3755    
3756      if (template) {      if (template) {
3757   for (tmplLine = template->lines; tmplLine; tmplLine = tmplLine->next) {   for (masterLine = template->lines;
3758      /* remember the indention level; we may need it for new lines */       masterLine && (tmplLine = lineDup(masterLine));
3759      if (tmplLine->numElements)       lineFree(tmplLine), masterLine = masterLine->next)
3760   indent = tmplLine->indent;   {
3761        dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
3762    
3763      /* skip comments */      /* skip comments */
3764      chptr = tmplLine->indent;      chptr = tmplLine->indent;
3765      while (*chptr && isspace(*chptr)) chptr++;      while (*chptr && isspace(*chptr)) chptr++;
3766      if (*chptr == '#') continue;      if (*chptr == '#') continue;
3767    
3768      /* we don't need an initrd here */      if (iskernel(tmplLine->type) && tmplLine->numElements >= 2) {
3769      if (tmplLine->type == LT_INITRD && !newKernelInitrd) continue;   if (!template->multiboot && (needs & NEED_MB)) {
3770        /* it's not a multiboot template and this is the kernel
3771              if (tmplLine->type == LT_KERNEL &&       * line.  Try to be intelligent about inserting the
3772                  !template->multiboot && (needs & KERNEL_MB)) {       * hypervisor at the same time.
3773                  struct singleLine *l;       */
3774                  needs &= ~ KERNEL_MB;      if (config->cfi->mbHyperFirst) {
3775     /* insert the hypervisor first */
3776                  l = addLine(new, config->cfi, LT_KERNEL,   newLine = addLine(new, config->cfi, LT_HYPER,
3777                                    config->secondaryIndent,    tmplLine->indent,
3778                                    newMBKernel + strlen(prefix));    newMBKernel + strlen(prefix));
3779                     /* set up for adding the kernel line */
3780                  tmplLine = lastLine;   free(tmplLine->indent);
3781                  if (!new->lines) {   tmplLine->indent = strdup(config->secondaryIndent);
3782                      new->lines = l;   needs &= ~NEED_MB;
3783                  } else {      }
3784                      newLine->next = l;      if (needs & NEED_KERNEL) {
3785                      newLine = l;   /* use addLineTmpl to preserve line elements,
3786                  }   * otherwise we could just call addLine.  Unfortunately
3787                  continue;   * this means making some changes to the template
3788              } else if (tmplLine->type == LT_KERNEL &&   * such as the indent change above and the type
3789                         template->multiboot && !new->multiboot) {   * change below.
3790                  continue; /* don't need multiboot kernel here */   */
3791              }   struct keywordTypes * mbm_kw =
3792        getKeywordByType(LT_MBMODULE, config->cfi);
3793      if (!new->lines) {   if (mbm_kw) {
3794   newLine = malloc(sizeof(*newLine));      tmplLine->type = LT_MBMODULE;
3795   new->lines = newLine;      free(tmplLine->elements[0].item);
3796      } else {      tmplLine->elements[0].item = strdup(mbm_kw->key);
3797   newLine->next = malloc(sizeof(*newLine));   }
3798   newLine = newLine->next;   newLine = addLineTmpl(new, tmplLine, newLine,
3799      }        newKernelPath + strlen(prefix), config->cfi);
3800     needs &= ~NEED_KERNEL;
3801        }
3802        if (needs & NEED_MB) { /* !mbHyperFirst */
3803     newLine = addLine(new, config->cfi, LT_HYPER,
3804      config->secondaryIndent,
3805      newMBKernel + strlen(prefix));
3806     needs &= ~NEED_MB;
3807        }
3808     } else if (needs & NEED_KERNEL) {
3809        newLine = addLineTmpl(new, tmplLine, newLine,
3810      newKernelPath + strlen(prefix), config->cfi);
3811        needs &= ~NEED_KERNEL;
3812     }
3813    
3814        } else if (tmplLine->type == LT_HYPER &&
3815           tmplLine->numElements >= 2) {
3816     if (needs & NEED_MB) {
3817        newLine = addLineTmpl(new, tmplLine, newLine,
3818      newMBKernel + strlen(prefix), config->cfi);
3819        needs &= ~NEED_MB;
3820     }
3821    
3822      newLine->indent = strdup(tmplLine->indent);      } else if (tmplLine->type == LT_MBMODULE &&
3823      newLine->next = NULL;         tmplLine->numElements >= 2) {
3824      newLine->type = tmplLine->type;   if (new->multiboot) {
3825      newLine->numElements = tmplLine->numElements;      if (needs & NEED_KERNEL) {
3826      newLine->elements = malloc(sizeof(*newLine->elements) *   newLine = addLineTmpl(new, tmplLine, newLine,
3827      newLine->numElements);        newKernelPath +
3828      for (i = 0; i < newLine->numElements; i++) {        strlen(prefix), config->cfi);
3829   newLine->elements[i].item = strdup(tmplLine->elements[i].item);   needs &= ~NEED_KERNEL;
3830   newLine->elements[i].indent =      } else if (config->cfi->mbInitRdIsModule &&
3831   strdup(tmplLine->elements[i].indent);         (needs & NEED_INITRD)) {
3832      }   char *initrdVal;
3833     initrdVal = getInitrdVal(config, prefix, tmplLine,
3834              lastLine = tmplLine;   newKernelInitrd, extraInitrds,
3835      if (tmplLine->type == LT_KERNEL && tmplLine->numElements >= 2) {   extraInitrdCount);
3836                  char * repl;   newLine = addLineTmpl(new, tmplLine, newLine,
3837                  if (!template->multiboot) {        initrdVal, config->cfi);
3838                      needs &= ~KERNEL_KERNEL;   free(initrdVal);
3839                      repl = newKernelPath;   needs &= ~NEED_INITRD;
3840                  } else {      }
3841                      needs &= ~KERNEL_MB;   } else if (needs & NEED_KERNEL) {
3842                      repl = newMBKernel;      /* template is multi but new is not,
3843                  }       * insert the kernel in the first module slot
3844                  if (new->multiboot && !template->multiboot) {       */
3845                      free(newLine->elements[0].item);      tmplLine->type = preferredLineType(LT_KERNEL, config->cfi);
3846                      newLine->elements[0].item = strdup("module");      free(tmplLine->elements[0].item);
3847                      newLine->type = LT_MBMODULE;      tmplLine->elements[0].item =
3848                  }   strdup(getKeywordByType(tmplLine->type,
3849   free(newLine->elements[1].item);   config->cfi)->key);
3850                  rootspec = getRootSpecifier(tmplLine->elements[1].item);      newLine = addLineTmpl(new, tmplLine, newLine,
3851                  if (rootspec != NULL) {    newKernelPath + strlen(prefix),
3852                      newLine->elements[1].item = sdupprintf("%s%s",    config->cfi);
3853                                                             rootspec,      needs &= ~NEED_KERNEL;
3854                                                             repl +   } else if (needs & NEED_INITRD) {
3855                                                             strlen(prefix));      char *initrdVal;
3856                  } else {      /* template is multi but new is not,
3857                      newLine->elements[1].item = strdup(repl +       * insert the initrd in the second module slot
3858                                                         strlen(prefix));       */
3859                  }      tmplLine->type = preferredLineType(LT_INITRD, config->cfi);
3860              } else if (tmplLine->type == LT_MBMODULE &&      free(tmplLine->elements[0].item);
3861                         tmplLine->numElements >= 2 && (needs & KERNEL_KERNEL)) {      tmplLine->elements[0].item =
3862                  needs &= ~KERNEL_KERNEL;   strdup(getKeywordByType(tmplLine->type,
3863                  if (!new->multiboot && template->multiboot) {   config->cfi)->key);
3864                      free(newLine->elements[0].item);      initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3865                      newLine->elements[0].item = strdup("kernel");      newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3866                      newLine->type = LT_KERNEL;      free(initrdVal);
3867                  }      needs &= ~NEED_INITRD;
3868   free(newLine->elements[1].item);   }
                 rootspec = getRootSpecifier(tmplLine->elements[1].item);  
                 if (rootspec != NULL) {  
                     newLine->elements[1].item = sdupprintf("%s%s",  
                                                            rootspec,  
                                                            newKernelPath +  
                                                            strlen(prefix));  
                 } else {  
                     newLine->elements[1].item = strdup(newKernelPath +  
                                                        strlen(prefix));  
                 }  
     } else if (tmplLine->type == LT_INITRD &&  
     tmplLine->numElements >= 2) {  
  needs &= ~KERNEL_INITRD;  
  free(newLine->elements[1].item);  
                 if (new->multiboot && !template->multiboot) {  
                     free(newLine->elements[0].item);  
                     newLine->elements[0].item = strdup("module");  
                     newLine->type = LT_MBMODULE;  
                 }  
                 rootspec = getRootSpecifier(tmplLine->elements[1].item);  
                 if (rootspec != NULL) {  
                     newLine->elements[1].item = sdupprintf("%s%s",  
                                                            rootspec,  
                                                            newKernelInitrd +  
                                                            strlen(prefix));  
                 } else {  
                     newLine->elements[1].item = strdup(newKernelInitrd +  
                                                        strlen(prefix));  
                 }  
             } else if (tmplLine->type == LT_MBMODULE &&  
                        tmplLine->numElements >= 2 && (needs & KERNEL_INITRD)) {  
  needs &= ~KERNEL_INITRD;  
                 if (!new->multiboot && template->multiboot) {  
                     free(newLine->elements[0].item);  
                     newLine->elements[0].item = strdup("initrd");  
                     newLine->type = LT_INITRD;  
                 }  
  free(newLine->elements[1].item);  
                 rootspec = getRootSpecifier(tmplLine->elements[1].item);  
                 if (rootspec != NULL) {  
                     newLine->elements[1].item = sdupprintf("%s%s",  
                                                            rootspec,  
                                                            newKernelInitrd +  
                                                            strlen(prefix));  
                 } else {  
                     newLine->elements[1].item = strdup(newKernelInitrd +  
                                                        strlen(prefix));  
                 }  
     } else if (tmplLine->type == LT_TITLE &&  
     tmplLine->numElements >= 2) {  
  needs &= ~KERNEL_TITLE;  
3869    
3870   for (i = 1; i < newLine->numElements; i++) {      } else if (isinitrd(tmplLine->type) && tmplLine->numElements >= 2) {
3871      free(newLine->elements[i].item);   if (needs & NEED_INITRD &&
3872      free(newLine->elements[i].indent);      new->multiboot && !template->multiboot &&
3873        config->cfi->mbInitRdIsModule) {
3874        /* make sure we don't insert the module initrd
3875         * before the module kernel... if we don't do it here,
3876         * it will be inserted following the template.
3877         */
3878        if (!needs & NEED_KERNEL) {
3879     char *initrdVal;
3880    
3881     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3882     newLine = addLine(new, config->cfi, LT_MBMODULE,
3883      config->secondaryIndent,
3884      initrdVal);
3885     free(initrdVal);
3886     needs &= ~NEED_INITRD;
3887        }
3888     } else if (needs & NEED_INITRD) {
3889        char *initrdVal;
3890        initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3891        newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3892        free(initrdVal);
3893        needs &= ~NEED_INITRD;
3894   }   }
3895    
3896   newLine->elements[1].item = strdup(newKernelTitle);      } else if (tmplLine->type == LT_MENUENTRY &&
3897   newLine->elements[1].indent = strdup("");         (needs & NEED_TITLE)) {
3898   newLine->numElements = 2;   requote(tmplLine, config->cfi);
3899     char *nkt = malloc(strlen(newKernelTitle)+3);
3900     strcpy(nkt, "'");
3901     strcat(nkt, newKernelTitle);
3902     strcat(nkt, "'");
3903     newLine = addLineTmpl(new, tmplLine, newLine, nkt, config->cfi);
3904     free(nkt);
3905     needs &= ~NEED_TITLE;
3906      } else if (tmplLine->type == LT_TITLE &&      } else if (tmplLine->type == LT_TITLE &&
3907                         config->cfi->titleBracketed &&         (needs & NEED_TITLE)) {
3908                         tmplLine->numElements == 1) {   if (tmplLine->numElements >= 2) {
3909                  needs &= ~KERNEL_TITLE;      newLine = addLineTmpl(new, tmplLine, newLine,
3910                  free(newLine->elements[0].item);    newKernelTitle, config->cfi);
3911                  free(newLine->elements[0].indent);      needs &= ~NEED_TITLE;
3912                  newLine->elements = malloc(sizeof(*newLine->elements) *   } else if (tmplLine->numElements == 1 &&
3913                                             newLine->numElements);     config->cfi->titleBracketed) {
3914        /* addLineTmpl doesn't handle titleBracketed */
3915                  newLine->elements[0].item = malloc(strlen(newKernelTitle) + 3);      newLine = addLine(new, config->cfi, LT_TITLE,
3916                  sprintf(newLine->elements[0].item, "[%s]", newKernelTitle);        tmplLine->indent, newKernelTitle);
3917                  newLine->elements[0].indent = strdup("");      needs &= ~NEED_TITLE;
3918                  newLine->numElements = 1;   }
3919              }      } else if (tmplLine->type == LT_ECHO) {
3920        requote(tmplLine, config->cfi);
3921        static const char *prefix = "'Loading ";
3922        if (tmplLine->numElements > 1 &&
3923        strstr(tmplLine->elements[1].item, prefix) &&
3924        masterLine->next &&
3925        iskernel(masterLine->next->type)) {
3926     char *newTitle = malloc(strlen(prefix) +
3927     strlen(newKernelTitle) + 2);
3928    
3929     strcpy(newTitle, prefix);
3930     strcat(newTitle, newKernelTitle);
3931     strcat(newTitle, "'");
3932     newLine = addLine(new, config->cfi, LT_ECHO,
3933     tmplLine->indent, newTitle);
3934     free(newTitle);
3935        } else {
3936     /* pass through other lines from the template */
3937     newLine = addLineTmpl(new, tmplLine, newLine, NULL,
3938     config->cfi);
3939        }
3940        } else if (tmplLine->type == LT_DEVTREE &&
3941           tmplLine->numElements == 2 && newDevTreePath) {
3942            newLine = addLineTmpl(new, tmplLine, newLine,
3943          newDevTreePath + strlen(prefix),
3944          config->cfi);
3945     needs &= ~NEED_DEVTREE;
3946        } else if (tmplLine->type == LT_ENTRY_END && needs & NEED_DEVTREE) {
3947     const char *ndtp = newDevTreePath;
3948     if (!strncmp(newDevTreePath, prefix, strlen(prefix)))
3949        ndtp += strlen(prefix);
3950     newLine = addLine(new, config->cfi, LT_DEVTREE,
3951      config->secondaryIndent,
3952      ndtp);
3953     needs &= ~NEED_DEVTREE;
3954     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
3955        } else {
3956     /* pass through other lines from the template */
3957     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
3958        }
3959   }   }
3960    
3961      } else {      } else {
3962   for (i = 0; config->cfi->keywords[i].key; i++) {   /* don't have a template, so start the entry with the
3963      if ((config->cfi->keywords[i].type == config->cfi->entrySeparator) || (config->cfi->keywords[i].type == LT_OTHER))   * appropriate starting line
3964     */
3965     switch (config->cfi->entryStart) {
3966        case LT_KERNEL:
3967        case LT_KERNEL_EFI:
3968        case LT_KERNEL_16:
3969     if (new->multiboot && config->cfi->mbHyperFirst) {
3970        /* fall through to LT_HYPER */
3971     } else {
3972        newLine = addLine(new, config->cfi,
3973              preferredLineType(LT_KERNEL, config->cfi),
3974          config->primaryIndent,
3975          newKernelPath + strlen(prefix));
3976        needs &= ~NEED_KERNEL;
3977        break;
3978     }
3979    
3980        case LT_HYPER:
3981     newLine = addLine(new, config->cfi, LT_HYPER,
3982      config->primaryIndent,
3983      newMBKernel + strlen(prefix));
3984     needs &= ~NEED_MB;
3985   break;   break;
         }  
3986    
3987   switch (config->cfi->keywords[i].type) {      case LT_MENUENTRY: {
3988      case LT_KERNEL:  needs &= ~KERNEL_KERNEL,   char *nkt = malloc(strlen(newKernelTitle)+3);
3989       chptr = newKernelPath + strlen(prefix);   strcpy(nkt, "'");
3990       type = LT_KERNEL; break;   strcat(nkt, newKernelTitle);
3991      case LT_TITLE:   needs &= ~KERNEL_TITLE, chptr = newKernelTitle;   strcat(nkt, "'");
3992       type = LT_TITLE; break;          newLine = addLine(new, config->cfi, LT_MENUENTRY,
3993      default:        config->primaryIndent, nkt);
3994                  /* zipl strikes again */   free(nkt);
3995                  if (config->cfi->titleBracketed) {   needs &= ~NEED_TITLE;
3996                      needs &= ~KERNEL_TITLE;   needs |= NEED_END;
3997                      chptr = newKernelTitle;   break;
3998                      type = LT_TITLE;      }
3999                      break;      case LT_TITLE:
4000                  } else {   if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
4001                      abort();   char * templabel;
4002                  }   int x = 0, y = 0;
4003   }  
4004     templabel = strdup(newKernelTitle);
4005     while( templabel[x]){
4006     if( templabel[x] == ' ' ){
4007     y = x;
4008     while( templabel[y] ){
4009     templabel[y] = templabel[y+1];
4010     y++;
4011     }
4012     }
4013     x++;
4014     }
4015     newLine = addLine(new, config->cfi, LT_TITLE,
4016      config->primaryIndent, templabel);
4017     free(templabel);
4018     }else{
4019     newLine = addLine(new, config->cfi, LT_TITLE,
4020      config->primaryIndent, newKernelTitle);
4021     }
4022     needs &= ~NEED_TITLE;
4023     break;
4024    
4025   newLine = addLine(new, config->cfi, type, config->primaryIndent, chptr);      default:
4026   new->lines = newLine;   abort();
4027     }
4028      }      }
4029    
4030      if (new->multiboot) {      /* add the remainder of the lines, i.e. those that either
4031          if (needs & KERNEL_MB)       * weren't present in the template, or in the case of no template,
4032              newLine = addLine(new, config->cfi, LT_KERNEL,       * all the lines following the entryStart.
4033                                config->secondaryIndent,       */
4034                                newMBKernel + strlen(prefix));      if (needs & NEED_TITLE) {
4035          if (needs & KERNEL_KERNEL)   newLine = addLine(new, config->cfi, LT_TITLE,
4036              newLine = addLine(new, config->cfi, LT_MBMODULE,    config->secondaryIndent,
4037                                config->secondaryIndent,    newKernelTitle);
4038                                newKernelPath + strlen(prefix));   needs &= ~NEED_TITLE;
4039          /* don't need to check for title as it's guaranteed to have been      }
4040           * done as we only do multiboot with grub which uses title as      if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
4041           * a separator */   newLine = addLine(new, config->cfi, LT_HYPER,
4042          if (needs & KERNEL_INITRD && newKernelInitrd)    config->secondaryIndent,
4043              newLine = addLine(new, config->cfi, LT_MBMODULE,    newMBKernel + strlen(prefix));
4044                                config->secondaryIndent,   needs &= ~NEED_MB;
4045                                newKernelInitrd + strlen(prefix));      }
4046      } else {      if (needs & NEED_KERNEL) {
4047          if (needs & KERNEL_KERNEL)   newLine = addLine(new, config->cfi,
4048              newLine = addLine(new, config->cfi, LT_KERNEL,    (new->multiboot && getKeywordByType(LT_MBMODULE,
4049                                config->secondaryIndent,        config->cfi))
4050                                newKernelPath + strlen(prefix));     ? LT_MBMODULE
4051          if (needs & KERNEL_TITLE)   : preferredLineType(LT_KERNEL, config->cfi),
4052              newLine = addLine(new, config->cfi, LT_TITLE,    config->secondaryIndent,
4053                                config->secondaryIndent,    newKernelPath + strlen(prefix));
4054                                newKernelTitle);   needs &= ~NEED_KERNEL;
4055          if (needs & KERNEL_INITRD && newKernelInitrd)      }
4056              newLine = addLine(new, config->cfi, LT_INITRD,      if (needs & NEED_MB) {
4057                                config->secondaryIndent,   newLine = addLine(new, config->cfi, LT_HYPER,
4058                                newKernelInitrd + strlen(prefix));    config->secondaryIndent,
4059      newMBKernel + strlen(prefix));
4060     needs &= ~NEED_MB;
4061        }
4062        if (needs & NEED_INITRD) {
4063     char *initrdVal;
4064     initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
4065     newLine = addLine(new, config->cfi,
4066      (new->multiboot && getKeywordByType(LT_MBMODULE,
4067          config->cfi))
4068       ? LT_MBMODULE
4069       : preferredLineType(LT_INITRD, config->cfi),
4070      config->secondaryIndent,
4071      initrdVal);
4072     free(initrdVal);
4073     needs &= ~NEED_INITRD;
4074        }
4075        if (needs & NEED_DEVTREE) {
4076     newLine = addLine(new, config->cfi, LT_DEVTREE,
4077      config->secondaryIndent,
4078      newDevTreePath);
4079     needs &= ~NEED_DEVTREE;
4080        }
4081    
4082        /* NEEDS_END must be last on bootloaders that need it... */
4083        if (needs & NEED_END) {
4084     newLine = addLine(new, config->cfi, LT_ENTRY_END,
4085     config->secondaryIndent, NULL);
4086     needs &= ~NEED_END;
4087        }
4088        if (needs) {
4089     printf(_("grubby: needs=%d, aborting\n"), needs);
4090     abort();
4091      }      }
4092    
4093      if (updateImage(config, "0", prefix, newKernelArgs, NULL,      if (updateImage(config, "0", prefix, newKernelArgs, NULL,
# Line 2274  int addNewKernel(struct grubConfig * con Line 4096  int addNewKernel(struct grubConfig * con
4096      return 0;      return 0;
4097  }  }
4098    
4099    static void traceback(int signum)
4100    {
4101        void *array[40];
4102        size_t size;
4103    
4104        signal(SIGSEGV, SIG_DFL);
4105        memset(array, '\0', sizeof (array));
4106        size = backtrace(array, 40);
4107    
4108        fprintf(stderr, "grubby received SIGSEGV!  Backtrace (%ld):\n",
4109                (unsigned long)size);
4110        backtrace_symbols_fd(array, size, STDERR_FILENO);
4111        exit(1);
4112    }
4113    
4114  int main(int argc, const char ** argv) {  int main(int argc, const char ** argv) {
4115      poptContext optCon;      poptContext optCon;
4116      char * grubConfig = NULL;      const char * grubConfig = NULL;
4117      char * outputFile = NULL;      char * outputFile = NULL;
4118      int arg = 0;      int arg = 0;
4119      int flags = 0;      int flags = 0;
4120      int badImageOkay = 0;      int badImageOkay = 0;
4121        int configureGrub2 = 0;
4122      int configureLilo = 0, configureELilo = 0, configureGrub = 0;      int configureLilo = 0, configureELilo = 0, configureGrub = 0;
4123      int configureYaboot = 0, configureSilo = 0, configureZipl = 0;      int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
4124        int configureExtLinux = 0;
4125      int bootloaderProbe = 0;      int bootloaderProbe = 0;
4126        int extraInitrdCount = 0;
4127      char * updateKernelPath = NULL;      char * updateKernelPath = NULL;
4128      char * newKernelPath = NULL;      char * newKernelPath = NULL;
4129      char * removeKernelPath = NULL;      char * removeKernelPath = NULL;
4130      char * newKernelArgs = NULL;      char * newKernelArgs = NULL;
4131      char * newKernelInitrd = NULL;      char * newKernelInitrd = NULL;
4132      char * newKernelTitle = NULL;      char * newKernelTitle = NULL;
4133      char * newKernelVersion = NULL;      char * newDevTreePath = NULL;
4134      char * newMBKernel = NULL;      char * newMBKernel = NULL;
4135      char * newMBKernelArgs = NULL;      char * newMBKernelArgs = NULL;
4136      char * removeMBKernelArgs = NULL;      char * removeMBKernelArgs = NULL;
# Line 2299  int main(int argc, const char ** argv) { Line 4139  int main(int argc, const char ** argv) {
4139      char * defaultKernel = NULL;      char * defaultKernel = NULL;
4140      char * removeArgs = NULL;      char * removeArgs = NULL;
4141      char * kernelInfo = NULL;      char * kernelInfo = NULL;
4142        char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
4143        char * envPath = NULL;
4144      const char * chptr = NULL;      const char * chptr = NULL;
4145      struct configFileInfo * cfi = NULL;      struct configFileInfo * cfi = NULL;
4146      struct grubConfig * config;      struct grubConfig * config;
4147      struct singleEntry * template = NULL;      struct singleEntry * template = NULL;
4148      int copyDefault = 0, makeDefault = 0;      int copyDefault = 0, makeDefault = 0;
4149      int displayDefault = 0;      int displayDefault = 0;
4150        int displayDefaultIndex = 0;
4151        int displayDefaultTitle = 0;
4152        int defaultIndex = -1;
4153      struct poptOption options[] = {      struct poptOption options[] = {
4154   { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,   { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
4155      _("add an entry for the specified kernel"), _("kernel-path") },      _("add an entry for the specified kernel"), _("kernel-path") },
# Line 2322  int main(int argc, const char ** argv) { Line 4167  int main(int argc, const char ** argv) {
4167   { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,   { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
4168      _("filestystem which contains /boot directory (for testing only)"),      _("filestystem which contains /boot directory (for testing only)"),
4169      _("bootfs") },      _("bootfs") },
4170  #if defined(__i386__) || defined(__x86_64__)  #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
4171   { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,   { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
4172      _("check if lilo is installed on lilo.conf boot sector") },      _("check which bootloader is installed on boot sector") },
4173  #endif  #endif
4174   { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,   { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
4175      _("path to grub config file to update (\"-\" for stdin)"),      _("path to grub config file to update (\"-\" for stdin)"),
# Line 2335  int main(int argc, const char ** argv) { Line 4180  int main(int argc, const char ** argv) {
4180        "the kernel referenced by the default image does not exist, "        "the kernel referenced by the default image does not exist, "
4181        "the first linux entry whose kernel does exist is used as the "        "the first linux entry whose kernel does exist is used as the "
4182        "template"), NULL },        "template"), NULL },
4183     { "debug", 0, 0, &debug, 0,
4184        _("print debugging information for failures") },
4185   { "default-kernel", 0, 0, &displayDefault, 0,   { "default-kernel", 0, 0, &displayDefault, 0,
4186      _("display the path of the default kernel") },      _("display the path of the default kernel") },
4187     { "default-index", 0, 0, &displayDefaultIndex, 0,
4188        _("display the index of the default kernel") },
4189     { "default-title", 0, 0, &displayDefaultTitle, 0,
4190        _("display the title of the default kernel") },
4191     { "devtree", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4192        _("device tree file for new stanza"), _("dtb-path") },
4193   { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,   { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
4194      _("configure elilo bootloader") },      _("configure elilo bootloader") },
4195     { "efi", 0, POPT_ARG_NONE, &isEfi, 0,
4196        _("force grub2 stanzas to use efi") },
4197     { "env", 0, POPT_ARG_STRING, &envPath, 0,
4198        _("path for environment data"),
4199        _("path") },
4200     { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
4201        _("configure extlinux bootloader (from syslinux)") },
4202   { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,   { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
4203      _("configure grub bootloader") },      _("configure grub bootloader") },
4204     { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
4205        _("configure grub2 bootloader") },
4206   { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,   { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
4207      _("display boot information for specified kernel"),      _("display boot information for specified kernel"),
4208      _("kernel-path") },      _("kernel-path") },
4209   { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,   { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
4210      _("initrd image for the new kernel"), _("initrd-path") },      _("initrd image for the new kernel"), _("initrd-path") },
4211     { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
4212        _("auxiliary initrd image for things other than the new kernel"), _("initrd-path") },
4213   { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,   { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
4214      _("configure lilo bootloader") },      _("configure lilo bootloader") },
4215   { "make-default", 0, 0, &makeDefault, 0,   { "make-default", 0, 0, &makeDefault, 0,
# Line 2365  int main(int argc, const char ** argv) { Line 4229  int main(int argc, const char ** argv) {
4229   { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,   { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
4230      _("make the first entry referencing the specified kernel "      _("make the first entry referencing the specified kernel "
4231        "the default"), _("kernel-path") },        "the default"), _("kernel-path") },
4232     { "set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
4233        _("make the given entry index the default entry"),
4234        _("entry-index") },
4235   { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,   { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
4236      _("configure silo bootloader") },      _("configure silo bootloader") },
4237   { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,   { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
# Line 2382  int main(int argc, const char ** argv) { Line 4249  int main(int argc, const char ** argv) {
4249   { 0, 0, 0, 0, 0 }   { 0, 0, 0, 0, 0 }
4250      };      };
4251    
4252        useextlinuxmenu=0;
4253    
4254        signal(SIGSEGV, traceback);
4255    
4256        int i = 0;
4257        for (int j = 1; j < argc; j++)
4258     i += strlen(argv[j]) + 1;
4259        saved_command_line = malloc(i);
4260        if (!saved_command_line) {
4261     fprintf(stderr, "grubby: %m\n");
4262     exit(1);
4263        }
4264        saved_command_line[0] = '\0';
4265        for (int j = 1; j < argc; j++) {
4266     strcat(saved_command_line, argv[j]);
4267     strncat(saved_command_line, j == argc -1 ? "" : " ", 1);
4268        }
4269    
4270      optCon = poptGetContext("grubby", argc, argv, options, 0);      optCon = poptGetContext("grubby", argc, argv, options, 0);
4271      poptReadDefaultConfig(optCon, 1);      poptReadDefaultConfig(optCon, 1);
4272    
# Line 2391  int main(int argc, const char ** argv) { Line 4276  int main(int argc, const char ** argv) {
4276      printf("grubby version %s\n", VERSION);      printf("grubby version %s\n", VERSION);
4277      exit(0);      exit(0);
4278      break;      break;
4279      case 'i':
4280        if (extraInitrdCount < MAX_EXTRA_INITRDS) {
4281         extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
4282        } else {
4283     fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
4284     return 1;
4285        }
4286        break;
4287   }   }
4288      }      }
4289    
# Line 2406  int main(int argc, const char ** argv) { Line 4299  int main(int argc, const char ** argv) {
4299   return 1;   return 1;
4300      }      }
4301    
4302      if ((configureLilo + configureGrub + configureELilo +      if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
4303   configureYaboot + configureSilo + configureZipl) > 1) {   configureYaboot + configureSilo + configureZipl +
4304     configureExtLinux ) > 1) {
4305   fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));   fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
4306   return 1;   return 1;
4307      } else if (bootloaderProbe && grubConfig) {      } else if (bootloaderProbe && grubConfig) {
4308   fprintf(stderr,   fprintf(stderr,
4309      _("grubby: cannot specify config file with --bootloader-probe\n"));      _("grubby: cannot specify config file with --bootloader-probe\n"));
4310   return 1;   return 1;
4311        } else if (configureGrub2) {
4312     cfi = &grub2ConfigType;
4313     if (envPath)
4314        cfi->envFile = envPath;
4315      } else if (configureLilo) {      } else if (configureLilo) {
4316   cfi = &liloConfigType;   cfi = &liloConfigType;
4317      } else if (configureGrub) {      } else if (configureGrub) {
# Line 2426  int main(int argc, const char ** argv) { Line 4324  int main(int argc, const char ** argv) {
4324          cfi = &siloConfigType;          cfi = &siloConfigType;
4325      } else if (configureZipl) {      } else if (configureZipl) {
4326          cfi = &ziplConfigType;          cfi = &ziplConfigType;
4327        } else if (configureExtLinux) {
4328     cfi = &extlinuxConfigType;
4329     useextlinuxmenu=1;
4330      }      }
4331    
4332      if (!cfi) {      if (!cfi) {
4333            if (grub2FindConfig(&grub2ConfigType))
4334        cfi = &grub2ConfigType;
4335     else
4336        #ifdef __ia64__        #ifdef __ia64__
4337   cfi = &eliloConfigType;      cfi = &eliloConfigType;
4338        #elif __powerpc__        #elif __powerpc__
4339   cfi = &yabootConfigType;      cfi = &yabootConfigType;
4340        #elif __sparc__        #elif __sparc__
4341          cfi = &siloConfigType;              cfi = &siloConfigType;
4342        #elif __s390__        #elif __s390__
4343          cfi = &ziplConfigType;              cfi = &ziplConfigType;
4344        #elif __s390x__        #elif __s390x__
4345          cfi = &ziplConfigtype;              cfi = &ziplConfigtype;
4346        #else        #else
4347   cfi = &grubConfigType;      cfi = &grubConfigType;
4348        #endif        #endif
4349      }      }
4350    
4351      if (!grubConfig)      if (!grubConfig) {
4352   grubConfig = cfi->defaultConfig;   if (cfi->findConfig)
4353        grubConfig = cfi->findConfig(cfi);
4354     if (!grubConfig)
4355        grubConfig = cfi->defaultConfig;
4356        }
4357    
4358      if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion ||      if (bootloaderProbe && (displayDefault || kernelInfo ||
4359    newKernelPath || removeKernelPath || makeDefault ||      newKernelPath || removeKernelPath || makeDefault ||
4360    defaultKernel)) {      defaultKernel || displayDefaultIndex || displayDefaultTitle ||
4361        (defaultIndex >= 0))) {
4362   fprintf(stderr, _("grubby: --bootloader-probe may not be used with "   fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
4363    "specified option"));    "specified option"));
4364   return 1;   return 1;
4365      }      }
4366    
4367      if ((displayDefault || kernelInfo) && (newKernelVersion || newKernelPath ||      if ((displayDefault || kernelInfo) && (newKernelPath ||
4368     removeKernelPath)) {     removeKernelPath)) {
4369   fprintf(stderr, _("grubby: --default-kernel and --info may not "   fprintf(stderr, _("grubby: --default-kernel and --info may not "
4370    "be used when adding or removing kernels\n"));    "be used when adding or removing kernels\n"));
# Line 2465  int main(int argc, const char ** argv) { Line 4374  int main(int argc, const char ** argv) {
4374      if (newKernelPath && !newKernelTitle) {      if (newKernelPath && !newKernelTitle) {
4375   fprintf(stderr, _("grubby: kernel title must be specified\n"));   fprintf(stderr, _("grubby: kernel title must be specified\n"));
4376   return 1;   return 1;
4377      } else if (!newKernelPath && (newKernelTitle  || newKernelInitrd ||      } else if (!newKernelPath && (newKernelTitle  || copyDefault ||
4378    newKernelInitrd || copyDefault     ||    (newKernelInitrd && !updateKernelPath)||
4379    makeDefault)) {    makeDefault || extraInitrdCount > 0)) {
4380   fprintf(stderr, _("grubby: kernel path expected\n"));   fprintf(stderr, _("grubby: kernel path expected\n"));
4381   return 1;   return 1;
4382      }      }
# Line 2491  int main(int argc, const char ** argv) { Line 4400  int main(int argc, const char ** argv) {
4400   makeDefault = 1;   makeDefault = 1;
4401   defaultKernel = NULL;   defaultKernel = NULL;
4402      }      }
4403        else if (defaultKernel && (defaultIndex >= 0)) {
4404     fprintf(stderr, _("grubby: --set-default and --set-default-index "
4405      "may not be used together\n"));
4406     return 1;
4407        }
4408    
4409      if (!strcmp(grubConfig, "-") && !outputFile) {      if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
4410   fprintf(stderr, _("grubby: output file must be specified if stdin "   fprintf(stderr, _("grubby: output file must be specified if stdin "
4411   "is used\n"));   "is used\n"));
4412   return 1;   return 1;
4413      }      }
4414    
4415      if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel      if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
4416   && !kernelInfo && !bootloaderProbe && !updateKernelPath   && !kernelInfo && !bootloaderProbe && !updateKernelPath
4417          && !removeMBKernel) {   && !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle
4418     && (defaultIndex == -1)) {
4419   fprintf(stderr, _("grubby: no action specified\n"));   fprintf(stderr, _("grubby: no action specified\n"));
4420   return 1;   return 1;
4421      }      }
# Line 2520  int main(int argc, const char ** argv) { Line 4435  int main(int argc, const char ** argv) {
4435   bootPrefix = "";   bootPrefix = "";
4436      }      }
4437    
4438        if (!cfi->mbAllowExtraInitRds &&
4439     extraInitrdCount > 0) {
4440     fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
4441     return 1;
4442        }
4443    
4444      if (bootloaderProbe) {      if (bootloaderProbe) {
4445   int lrc = 0, grc = 0;   int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
4446   struct grubConfig * lconfig, * gconfig;   struct grubConfig * lconfig, * gconfig, * yconfig, * econfig;
4447    
4448     const char *grub2config = grub2FindConfig(&grub2ConfigType);
4449     if (grub2config) {
4450        gconfig = readConfig(grub2config, &grub2ConfigType);
4451        if (!gconfig)
4452     gr2c = 1;
4453        else
4454     gr2c = checkForGrub2(gconfig);
4455     }
4456    
4457   if (!access(grubConfigType.defaultConfig, F_OK)) {   const char *grubconfig = grubFindConfig(&grubConfigType);
4458      gconfig = readConfig(grubConfigType.defaultConfig, &grubConfigType);   if (!access(grubconfig, F_OK)) {
4459        gconfig = readConfig(grubconfig, &grubConfigType);
4460      if (!gconfig)      if (!gconfig)
4461   grc = 1;   grc = 1;
4462      else      else
# Line 2540  int main(int argc, const char ** argv) { Line 4471  int main(int argc, const char ** argv) {
4471   lrc = checkForLilo(lconfig);   lrc = checkForLilo(lconfig);
4472   }   }
4473    
4474   if (lrc == 1 || grc == 1) return 1;   if (!access(eliloConfigType.defaultConfig, F_OK)) {
4475        econfig = readConfig(eliloConfigType.defaultConfig,
4476     &eliloConfigType);
4477        if (!econfig)
4478     erc = 1;
4479        else
4480     erc = checkForElilo(econfig);
4481     }
4482    
4483     if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
4484        lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
4485        if (!lconfig)
4486     extrc = 1;
4487        else
4488     extrc = checkForExtLinux(lconfig);
4489     }
4490    
4491    
4492     if (!access(yabootConfigType.defaultConfig, F_OK)) {
4493        yconfig = readConfig(yabootConfigType.defaultConfig,
4494     &yabootConfigType);
4495        if (!yconfig)
4496     yrc = 1;
4497        else
4498     yrc = checkForYaboot(yconfig);
4499     }
4500    
4501     if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1 ||
4502     erc == 1)
4503        return 1;
4504    
4505   if (lrc == 2) printf("lilo\n");   if (lrc == 2) printf("lilo\n");
4506     if (gr2c == 2) printf("grub2\n");
4507   if (grc == 2) printf("grub\n");   if (grc == 2) printf("grub\n");
4508     if (extrc == 2) printf("extlinux\n");
4509     if (yrc == 2) printf("yaboot\n");
4510     if (erc == 2) printf("elilo\n");
4511    
4512   return 0;   return 0;
4513      }      }
4514    
4515        if (grubConfig == NULL) {
4516     printf("Could not find bootloader configuration file.\n");
4517     exit(1);
4518        }
4519    
4520      config = readConfig(grubConfig, cfi);      config = readConfig(grubConfig, cfi);
4521      if (!config) return 1;      if (!config) return 1;
4522    
# Line 2557  int main(int argc, const char ** argv) { Line 4526  int main(int argc, const char ** argv) {
4526          char * rootspec;          char * rootspec;
4527    
4528   if (config->defaultImage == -1) return 0;   if (config->defaultImage == -1) return 0;
4529     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4530     cfi->defaultIsSaved)
4531        config->defaultImage = 0;
4532   entry = findEntryByIndex(config, config->defaultImage);   entry = findEntryByIndex(config, config->defaultImage);
4533   if (!entry) return 0;   if (!entry) return 0;
4534   if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;   if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
4535    
4536   line = entry->lines;   line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
  while (line && line->type != LT_KERNEL) line = line->next;  
4537   if (!line) return 0;   if (!line) return 0;
4538    
4539          rootspec = getRootSpecifier(line->elements[1].item);          rootspec = getRootSpecifier(line->elements[1].item);
# Line 2570  int main(int argc, const char ** argv) { Line 4541  int main(int argc, const char ** argv) {
4541                 ((rootspec != NULL) ? strlen(rootspec) : 0));                 ((rootspec != NULL) ? strlen(rootspec) : 0));
4542    
4543   return 0;   return 0;
4544    
4545        } else if (displayDefaultTitle) {
4546     struct singleLine * line;
4547     struct singleEntry * entry;
4548    
4549     if (config->defaultImage == -1) return 0;
4550     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4551     cfi->defaultIsSaved)
4552        config->defaultImage = 0;
4553     entry = findEntryByIndex(config, config->defaultImage);
4554     if (!entry) return 0;
4555    
4556     if (!configureGrub2) {
4557      line = getLineByType(LT_TITLE, entry->lines);
4558      if (!line) return 0;
4559      printf("%s\n", line->elements[1].item);
4560    
4561     } else {
4562      char * title;
4563    
4564      dbgPrintf("This is GRUB2, default title is embeded in menuentry\n");
4565      line = getLineByType(LT_MENUENTRY, entry->lines);
4566      if (!line) return 0;
4567      title = grub2ExtractTitle(line);
4568      if (title)
4569        printf("%s\n", title);
4570     }
4571     return 0;
4572    
4573        } else if (displayDefaultIndex) {
4574            if (config->defaultImage == -1) return 0;
4575     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4576     cfi->defaultIsSaved)
4577        config->defaultImage = 0;
4578            printf("%i\n", config->defaultImage);
4579            return 0;
4580    
4581      } else if (kernelInfo)      } else if (kernelInfo)
4582   return displayInfo(config, kernelInfo, bootPrefix);   return displayInfo(config, kernelInfo, bootPrefix);
4583    
# Line 2581  int main(int argc, const char ** argv) { Line 4589  int main(int argc, const char ** argv) {
4589      markRemovedImage(config, removeKernelPath, bootPrefix);      markRemovedImage(config, removeKernelPath, bootPrefix);
4590      markRemovedImage(config, removeMBKernel, bootPrefix);      markRemovedImage(config, removeMBKernel, bootPrefix);
4591      setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,      setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
4592      bootPrefix, flags);      bootPrefix, flags, defaultIndex);
4593      setFallbackImage(config, newKernelPath != NULL);      setFallbackImage(config, newKernelPath != NULL);
4594      if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,      if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
4595                      removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;                      removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
4596        if (updateKernelPath && newKernelInitrd) {
4597        if (newMBKernel) {
4598        if (addMBInitrd(config, newMBKernel, updateKernelPath,
4599     bootPrefix, newKernelInitrd))
4600        return 1;
4601        } else {
4602        if (updateInitrd(config, updateKernelPath, bootPrefix,
4603     newKernelInitrd))
4604     return 1;
4605        }
4606        }
4607      if (addNewKernel(config, template, bootPrefix, newKernelPath,      if (addNewKernel(config, template, bootPrefix, newKernelPath,
4608                       newKernelTitle, newKernelArgs, newKernelInitrd,                       newKernelTitle, newKernelArgs, newKernelInitrd,
4609                       newMBKernel, newMBKernelArgs)) return 1;                       (const char **)extraInitrds, extraInitrdCount,
4610                         newMBKernel, newMBKernelArgs, newDevTreePath)) return 1;
4611            
4612    
4613      if (numEntries(config) == 0) {      if (numEntries(config) == 0) {
# Line 2597  int main(int argc, const char ** argv) { Line 4617  int main(int argc, const char ** argv) {
4617      }      }
4618    
4619      if (!outputFile)      if (!outputFile)
4620   outputFile = grubConfig;   outputFile = (char *)grubConfig;
4621    
4622      return writeConfig(config, outputFile, bootPrefix);      return writeConfig(config, outputFile, bootPrefix);
4623  }  }

Legend:
Removed from v.532  
changed lines
  Added in v.2685