Magellan Linux

Diff of /tags/grubby-8_32/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 2694 by niro, Wed Jul 16 10:48:13 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    #if defined(__arch64__)
64    #define isEfiOnly 1
65    #else
66    #define isEfiOnly 0
67    #endif
68    
69    char *saved_command_line = NULL;
70    
71  /* comments get lumped in with indention */  /* comments get lumped in with indention */
72  struct lineElement {  struct lineElement {
73      char * item;      char * item;
74      char * indent;      char * indent;
75  };  };
76    
77  enum lineType_e { LT_WHITESPACE, LT_TITLE, LT_KERNEL, LT_INITRD, LT_DEFAULT,  enum lineType_e {
78         LT_UNKNOWN, LT_ROOT, LT_FALLBACK, LT_KERNELARGS, LT_BOOT,      LT_WHITESPACE   = 1 << 0,
79         LT_BOOTROOT, LT_LBA, LT_MBMODULE, LT_OTHER, LT_GENERIC };      LT_TITLE        = 1 << 1,
80        LT_KERNEL       = 1 << 2,
81        LT_INITRD       = 1 << 3,
82        LT_HYPER        = 1 << 4,
83        LT_DEFAULT      = 1 << 5,
84        LT_MBMODULE     = 1 << 6,
85        LT_ROOT         = 1 << 7,
86        LT_FALLBACK     = 1 << 8,
87        LT_KERNELARGS   = 1 << 9,
88        LT_BOOT         = 1 << 10,
89        LT_BOOTROOT     = 1 << 11,
90        LT_LBA          = 1 << 12,
91        LT_OTHER        = 1 << 13,
92        LT_GENERIC      = 1 << 14,
93        LT_ECHO    = 1 << 16,
94        LT_MENUENTRY    = 1 << 17,
95        LT_ENTRY_END    = 1 << 18,
96        LT_SET_VARIABLE = 1 << 19,
97        LT_KERNEL_EFI   = 1 << 20,
98        LT_INITRD_EFI   = 1 << 21,
99        LT_KERNEL_16    = 1 << 22,
100        LT_INITRD_16    = 1 << 23,
101        LT_DEVTREE      = 1 << 24,
102        LT_UNKNOWN      = 1 << 25,
103    };
104    
105  struct singleLine {  struct singleLine {
106      char * indent;      char * indent;
# Line 61  struct singleEntry { Line 121  struct singleEntry {
121    
122  #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 */
123    
124  #define KERNEL_KERNEL    (1 << 0)  /* These defines are (only) used in addNewKernel() */
125  #define KERNEL_INITRD    (1 << 2)  #define NEED_KERNEL  (1 << 0)
126  #define KERNEL_TITLE    (1 << 3)  #define NEED_INITRD  (1 << 1)
127  #define KERNEL_ARGS    (1 << 4)  #define NEED_TITLE   (1 << 2)
128  #define KERNEL_MB           (1 << 5)  #define NEED_ARGS    (1 << 3)
129    #define NEED_MB      (1 << 4)
130    #define NEED_END     (1 << 5)
131    #define NEED_DEVTREE (1 << 6)
132    
133  #define MAIN_DEFAULT    (1 << 0)  #define MAIN_DEFAULT    (1 << 0)
134  #define DEFAULT_SAVED       -2  #define DEFAULT_SAVED       -2
135    #define DEFAULT_SAVED_GRUB2 -3
136    
137  struct keywordTypes {  struct keywordTypes {
138      char * key;      char * key;
139      enum lineType_e type;      enum lineType_e type;
140      char nextChar;      char nextChar;
141  } ;      char separatorChar;
142    };
143    
144    struct configFileInfo;
145    
146    typedef const char *(*findConfigFunc)(struct configFileInfo *);
147    typedef const int (*writeLineFunc)(struct configFileInfo *,
148     struct singleLine *line);
149    typedef char *(*getEnvFunc)(struct configFileInfo *, char *name);
150    typedef int (*setEnvFunc)(struct configFileInfo *, char *name, char *value);
151    
152  struct configFileInfo {  struct configFileInfo {
153      char * defaultConfig;      char * defaultConfig;
154        findConfigFunc findConfig;
155        writeLineFunc writeLine;
156        getEnvFunc getEnv;
157        setEnvFunc setEnv;
158      struct keywordTypes * keywords;      struct keywordTypes * keywords;
159        int caseInsensitive;
160      int defaultIsIndex;      int defaultIsIndex;
161        int defaultIsVariable;
162      int defaultSupportSaved;      int defaultSupportSaved;
163      enum lineType_e entrySeparator;      int defaultIsSaved;
164        int defaultIsUnquoted;
165        enum lineType_e entryStart;
166        enum lineType_e entryEnd;
167      int needsBootPrefix;      int needsBootPrefix;
168      int argsInQuotes;      int argsInQuotes;
169      int maxTitleLength;      int maxTitleLength;
170      int titleBracketed;      int titleBracketed;
171        int titlePosition;
172        int mbHyperFirst;
173        int mbInitRdIsModule;
174        int mbConcatArgs;
175        int mbAllowExtraInitRds;
176        char *envFile;
177  };  };
178    
179  struct keywordTypes grubKeywords[] = {  struct keywordTypes grubKeywords[] = {
# Line 94  struct keywordTypes grubKeywords[] = { Line 182  struct keywordTypes grubKeywords[] = {
182      { "default",    LT_DEFAULT,    ' ' },      { "default",    LT_DEFAULT,    ' ' },
183      { "fallback",   LT_FALLBACK,    ' ' },      { "fallback",   LT_FALLBACK,    ' ' },
184      { "kernel",    LT_KERNEL,    ' ' },      { "kernel",    LT_KERNEL,    ' ' },
185      { "initrd",    LT_INITRD,    ' ' },      { "initrd",    LT_INITRD,    ' ', ' ' },
186      { "module",     LT_MBMODULE,    ' ' },      { "module",     LT_MBMODULE,    ' ' },
187        { "kernel",     LT_HYPER,       ' ' },
188      { NULL,    0, 0 },      { NULL,    0, 0 },
189  };  };
190    
191    const char *grubFindConfig(struct configFileInfo *cfi) {
192        static const char *configFiles[] = {
193     "/boot/grub/grub.conf",
194     "/boot/grub/menu.lst",
195     "/etc/grub.conf",
196     "/boot/grub2/grub.cfg",
197     "/boot/grub2-efi/grub.cfg",
198     NULL
199        };
200        static int i = -1;
201    
202        if (i == -1) {
203     for (i = 0; configFiles[i] != NULL; i++) {
204        dbgPrintf("Checking \"%s\": ", configFiles[i]);
205        if (!access(configFiles[i], R_OK)) {
206     dbgPrintf("found\n");
207     return configFiles[i];
208        }
209        dbgPrintf("not found\n");
210     }
211        }
212        return configFiles[i];
213    }
214    
215  struct configFileInfo grubConfigType = {  struct configFileInfo grubConfigType = {
216      "/boot/grub/grub.conf",    /* defaultConfig */      .findConfig = grubFindConfig,
217      grubKeywords,    /* keywords */      .keywords = grubKeywords,
218      1,    /* defaultIsIndex */      .defaultIsIndex = 1,
219      1,    /* defaultSupportSaved */      .defaultSupportSaved = 1,
220      LT_TITLE,    /* entrySeparator */      .entryStart = LT_TITLE,
221      1,    /* needsBootPrefix */      .needsBootPrefix = 1,
222      0,    /* argsInQuotes */      .mbHyperFirst = 1,
223      0,    /* maxTitleLength */      .mbInitRdIsModule = 1,
224      0,                                      /* titleBracketed */      .mbAllowExtraInitRds = 1,
225    };
226    
227    struct keywordTypes grub2Keywords[] = {
228        { "menuentry",  LT_MENUENTRY,   ' ' },
229        { "}",          LT_ENTRY_END,   ' ' },
230        { "echo",       LT_ECHO,        ' ' },
231        { "set",        LT_SET_VARIABLE,' ', '=' },
232        { "root",       LT_BOOTROOT,    ' ' },
233        { "default",    LT_DEFAULT,     ' ' },
234        { "fallback",   LT_FALLBACK,    ' ' },
235        { "linux",      LT_KERNEL,      ' ' },
236        { "linuxefi",   LT_KERNEL_EFI,  ' ' },
237        { "linux16",    LT_KERNEL_16,   ' ' },
238        { "initrd",     LT_INITRD,      ' ', ' ' },
239        { "initrdefi",  LT_INITRD_EFI,  ' ', ' ' },
240        { "initrd16",   LT_INITRD_16,   ' ', ' ' },
241        { "module",     LT_MBMODULE,    ' ' },
242        { "kernel",     LT_HYPER,       ' ' },
243        { "devicetree", LT_DEVTREE,  ' ' },
244        { NULL, 0, 0 },
245    };
246    
247    const char *grub2FindConfig(struct configFileInfo *cfi) {
248        static const char *configFiles[] = {
249     "/boot/grub/grub-efi.cfg",
250     "/boot/grub/grub.cfg",
251     NULL
252        };
253        static int i = -1;
254        static const char *grub_cfg = "/boot/grub/grub.cfg";
255        int rc = -1;
256    
257        if (i == -1) {
258     for (i = 0; configFiles[i] != NULL; i++) {
259        dbgPrintf("Checking \"%s\": ", configFiles[i]);
260        if ((rc = access(configFiles[i], R_OK))) {
261     if (errno == EACCES) {
262        printf("Unable to access bootloader configuration file "
263           "\"%s\": %m\n", configFiles[i]);
264        exit(1);
265     }
266     continue;
267        } else {
268     dbgPrintf("found\n");
269     return configFiles[i];
270        }
271     }
272        }
273    
274        /* Ubuntu renames grub2 to grub, so check for the grub.d directory
275         * that isn't in grub1, and if it exists, return the config file path
276         * that they use. */
277        if (configFiles[i] == NULL && !access("/etc/grub.d/", R_OK)) {
278     dbgPrintf("found\n");
279     return grub_cfg;
280        }
281    
282        dbgPrintf("not found\n");
283        return configFiles[i];
284    }
285    
286    /* kind of hacky.  It'll give the first 1024 bytes, ish. */
287    static char *grub2GetEnv(struct configFileInfo *info, char *name)
288    {
289        static char buf[1025];
290        char *s = NULL;
291        char *ret = NULL;
292        char *envFile = info->envFile ? info->envFile : "/boot/grub/grubenv";
293        int rc = asprintf(&s, "grub-editenv %s list | grep '^%s='", envFile, name);
294    
295        if (rc < 0)
296     return NULL;
297    
298        FILE *f = popen(s, "r");
299        if (!f)
300     goto out;
301    
302        memset(buf, '\0', sizeof (buf));
303        ret = fgets(buf, 1024, f);
304        pclose(f);
305    
306        if (ret) {
307     ret += strlen(name) + 1;
308     ret[strlen(ret) - 1] = '\0';
309        }
310        dbgPrintf("grub2GetEnv(%s): %s\n", name, ret);
311    out:
312        free(s);
313        return ret;
314    }
315    
316    static int sPopCount(const char *s, const char *c)
317    {
318        int ret = 0;
319        if (!s)
320     return -1;
321        for (int i = 0; s[i] != '\0'; i++)
322     for (int j = 0; c[j] != '\0'; j++)
323        if (s[i] == c[j])
324     ret++;
325        return ret;
326    }
327    
328    static char *shellEscape(const char *s)
329    {
330        int l = strlen(s) + sPopCount(s, "'") * 2;
331    
332        char *ret = calloc(l+1, sizeof (*ret));
333        if (!ret)
334     return NULL;
335        for (int i = 0, j = 0; s[i] != '\0'; i++, j++) {
336     if (s[i] == '\'')
337        ret[j++] = '\\';
338     ret[j] = s[i];
339        }
340        return ret;
341    }
342    
343    static void unquote(char *s)
344    {
345        int l = strlen(s);
346    
347        if ((s[l-1] == '\'' && s[0] == '\'') || (s[l-1] == '"' && s[0] == '"')) {
348     memmove(s, s+1, l-2);
349     s[l-2] = '\0';
350        }
351    }
352    
353    static int grub2SetEnv(struct configFileInfo *info, char *name, char *value)
354    {
355        char *s = NULL;
356        int rc = 0;
357        char *envFile = info->envFile ? info->envFile : "/boot/grub/grubenv";
358    
359        unquote(value);
360        value = shellEscape(value);
361        if (!value)
362        return -1;
363    
364        rc = asprintf(&s, "grub-editenv %s set '%s=%s'", envFile, name, value);
365        free(value);
366        if (rc <0)
367     return -1;
368    
369        dbgPrintf("grub2SetEnv(%s): %s\n", name, s);
370        rc = system(s);
371        free(s);
372        return rc;
373    }
374    
375    /* this is a gigantic hack to avoid clobbering grub2 variables... */
376    static int is_special_grub2_variable(const char *name)
377    {
378        if (!strcmp(name,"\"${next_entry}\""))
379     return 1;
380        if (!strcmp(name,"\"${prev_saved_entry}\""))
381     return 1;
382        return 0;
383    }
384    
385    int sizeOfSingleLine(struct singleLine * line) {
386      int count = 0;
387    
388      for (int i = 0; i < line->numElements; i++) {
389        int indentSize = 0;
390    
391        count = count + strlen(line->elements[i].item);
392    
393        indentSize = strlen(line->elements[i].indent);
394        if (indentSize > 0)
395          count = count + indentSize;
396        else
397          /* be extra safe and add room for whitespaces */
398          count = count + 1;
399      }
400    
401      /* room for trailing terminator */
402      count = count + 1;
403    
404      return count;
405    }
406    
407    static int isquote(char q)
408    {
409        if (q == '\'' || q == '\"')
410     return 1;
411        return 0;
412    }
413    
414    static int iskernel(enum lineType_e type) {
415        return (type == LT_KERNEL || type == LT_KERNEL_EFI || type == LT_KERNEL_16);
416    }
417    
418    static int isinitrd(enum lineType_e type) {
419        return (type == LT_INITRD || type == LT_INITRD_EFI || type == LT_INITRD_16);
420    }
421    
422    char *grub2ExtractTitle(struct singleLine * line) {
423        char * current;
424        char * current_indent;
425        int current_len;
426        int current_indent_len;
427        int i;
428    
429        /* bail out if line does not start with menuentry */
430        if (strcmp(line->elements[0].item, "menuentry"))
431          return NULL;
432    
433        i = 1;
434        current = line->elements[i].item;
435        current_len = strlen(current);
436    
437        /* if second word is quoted, strip the quotes and return single word */
438        if (isquote(*current) && isquote(current[current_len - 1])) {
439     char *tmp;
440    
441     tmp = strdup(current);
442     *(tmp + current_len - 1) = '\0';
443     return ++tmp;
444        }
445    
446        /* if no quotes, return second word verbatim */
447        if (!isquote(*current))
448     return current;
449    
450        /* second element start with a quote, so we have to find the element
451         * whose last character is also quote (assuming it's the closing one) */
452        int resultMaxSize;
453        char * result;
454        
455        resultMaxSize = sizeOfSingleLine(line);
456        result = malloc(resultMaxSize);
457        snprintf(result, resultMaxSize, "%s", ++current);
458        
459        i++;
460        for (; i < line->numElements; ++i) {
461     current = line->elements[i].item;
462     current_len = strlen(current);
463     current_indent = line->elements[i].indent;
464     current_indent_len = strlen(current_indent);
465    
466     strncat(result, current_indent, current_indent_len);
467     if (!isquote(current[current_len-1])) {
468        strncat(result, current, current_len);
469     } else {
470        strncat(result, current, current_len - 1);
471        break;
472     }
473        }
474        return result;
475    }
476    
477    struct configFileInfo grub2ConfigType = {
478        .findConfig = grub2FindConfig,
479        .getEnv = grub2GetEnv,
480        .setEnv = grub2SetEnv,
481        .keywords = grub2Keywords,
482        .defaultIsIndex = 1,
483        .defaultSupportSaved = 1,
484        .defaultIsVariable = 1,
485        .entryStart = LT_MENUENTRY,
486        .entryEnd = LT_ENTRY_END,
487        .titlePosition = 1,
488        .needsBootPrefix = 1,
489        .mbHyperFirst = 1,
490        .mbInitRdIsModule = 1,
491        .mbAllowExtraInitRds = 1,
492  };  };
493    
494  struct keywordTypes yabootKeywords[] = {  struct keywordTypes yabootKeywords[] = {
# Line 142  struct keywordTypes yabootKeywords[] = { Line 522  struct keywordTypes yabootKeywords[] = {
522      { "partition",  LT_GENERIC,    '=' },      { "partition",  LT_GENERIC,    '=' },
523      { "device",    LT_GENERIC,    '=' },      { "device",    LT_GENERIC,    '=' },
524      { "fstype",    LT_GENERIC,    '=' },      { "fstype",    LT_GENERIC,    '=' },
525      { "initrd",    LT_INITRD,    '=' },      { "initrd",    LT_INITRD,    '=', ';' },
526      { "append",    LT_KERNELARGS,  '=' },      { "append",    LT_KERNELARGS,  '=' },
527      { "boot",    LT_BOOT,    '=' },      { "boot",    LT_BOOT,    '=' },
528      { "lba",    LT_LBA,    ' ' },      { "lba",    LT_LBA,    ' ' },
# Line 162  struct keywordTypes liloKeywords[] = { Line 542  struct keywordTypes liloKeywords[] = {
542      { NULL,    0, 0 },      { NULL,    0, 0 },
543  };  };
544    
545    struct keywordTypes eliloKeywords[] = {
546        { "label",    LT_TITLE,    '=' },
547        { "root",    LT_ROOT,    '=' },
548        { "default",    LT_DEFAULT,    '=' },
549        { "image",    LT_KERNEL,    '=' },
550        { "initrd",    LT_INITRD,    '=' },
551        { "append",    LT_KERNELARGS,  '=' },
552        { "vmm",    LT_HYPER,       '=' },
553        { NULL,    0, 0 },
554    };
555    
556  struct keywordTypes siloKeywords[] = {  struct keywordTypes siloKeywords[] = {
557      { "label",    LT_TITLE,    '=' },      { "label",    LT_TITLE,    '=' },
558      { "root",    LT_ROOT,    '=' },      { "root",    LT_ROOT,    '=' },
# Line 183  struct keywordTypes ziplKeywords[] = { Line 574  struct keywordTypes ziplKeywords[] = {
574      { NULL,         0, 0 },      { NULL,         0, 0 },
575  };  };
576    
577    struct keywordTypes extlinuxKeywords[] = {
578        { "label",    LT_TITLE,    ' ' },
579        { "root",    LT_ROOT,    ' ' },
580        { "default",    LT_DEFAULT,    ' ' },
581        { "kernel",    LT_KERNEL,    ' ' },
582        { "initrd",    LT_INITRD,      ' ', ',' },
583        { "append",    LT_KERNELARGS,  ' ' },
584        { "prompt",     LT_UNKNOWN,     ' ' },
585        { NULL,    0, 0 },
586    };
587    int useextlinuxmenu;
588  struct configFileInfo eliloConfigType = {  struct configFileInfo eliloConfigType = {
589      "/boot/efi/EFI/redhat/elilo.conf",    /* defaultConfig */      .defaultConfig = "/boot/efi/EFI/redhat/elilo.conf",
590      liloKeywords,    /* keywords */      .keywords = eliloKeywords,
591      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
592      0,    /* defaultSupportSaved */      .needsBootPrefix = 1,
593      LT_KERNEL,    /* entrySeparator */      .argsInQuotes = 1,
594      1,                    /* needsBootPrefix */      .mbConcatArgs = 1,
     1,    /* argsInQuotes */  
     0,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
595  };  };
596    
597  struct configFileInfo liloConfigType = {  struct configFileInfo liloConfigType = {
598      "/etc/lilo.conf",    /* defaultConfig */      .defaultConfig = "/etc/lilo.conf",
599      liloKeywords,    /* keywords */      .keywords = liloKeywords,
600      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
601      0,    /* defaultSupportSaved */      .argsInQuotes = 1,
602      LT_KERNEL,    /* entrySeparator */      .maxTitleLength = 15,
     0,    /* needsBootPrefix */  
     1,    /* argsInQuotes */  
     15,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
603  };  };
604    
605  struct configFileInfo yabootConfigType = {  struct configFileInfo yabootConfigType = {
606      "/etc/yaboot.conf",    /* defaultConfig */      .defaultConfig = "/etc/yaboot.conf",
607      yabootKeywords,    /* keywords */      .keywords = yabootKeywords,
608      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
609      0,    /* defaultSupportSaved */      .needsBootPrefix = 1,
610      LT_KERNEL,    /* entrySeparator */      .argsInQuotes = 1,
611      1,    /* needsBootPrefix */      .maxTitleLength = 15,
612      1,    /* argsInQuotes */      .mbAllowExtraInitRds = 1,
     15,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
613  };  };
614    
615  struct configFileInfo siloConfigType = {  struct configFileInfo siloConfigType = {
616      "/etc/silo.conf",    /* defaultConfig */      .defaultConfig = "/etc/silo.conf",
617      siloKeywords,    /* keywords */      .keywords = siloKeywords,
618      0,    /* defaultIsIndex */      .entryStart = LT_KERNEL,
619      0,    /* defaultSupportSaved */      .needsBootPrefix = 1,
620      LT_KERNEL,    /* entrySeparator */      .argsInQuotes = 1,
621      1,    /* needsBootPrefix */      .maxTitleLength = 15,
     1,    /* argsInQuotes */  
     15,    /* maxTitleLength */  
     0,                                      /* titleBracketed */  
622  };  };
623    
624  struct configFileInfo ziplConfigType = {  struct configFileInfo ziplConfigType = {
625      "/etc/zipl.conf",    /* defaultConfig */      .defaultConfig = "/etc/zipl.conf",
626      ziplKeywords,    /* keywords */      .keywords = ziplKeywords,
627      0,    /* defaultIsIndex */      .entryStart = LT_TITLE,
628      0,    /* defaultSupportSaved */      .argsInQuotes = 1,
629      LT_TITLE,    /* entrySeparator */      .titleBracketed = 1,
630      0,    /* needsBootPrefix */  };
631      1,    /* argsInQuotes */  
632      15,    /* maxTitleLength */  struct configFileInfo extlinuxConfigType = {
633      1,                                      /* titleBracketed */      .defaultConfig = "/boot/extlinux/extlinux.conf",
634        .keywords = extlinuxKeywords,
635        .caseInsensitive = 1,
636        .entryStart = LT_TITLE,
637        .needsBootPrefix = 1,
638        .maxTitleLength = 255,
639        .mbAllowExtraInitRds = 1,
640        .defaultIsUnquoted = 1,
641  };  };
642    
643  struct grubConfig {  struct grubConfig {
# Line 255  struct grubConfig { Line 652  struct grubConfig {
652      struct configFileInfo * cfi;      struct configFileInfo * cfi;
653  };  };
654    
655    blkid_cache blkid;
656    
657  struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index);  struct singleEntry * findEntryByIndex(struct grubConfig * cfg, int index);
658  struct singleEntry * findEntryByPath(struct grubConfig * cfg,  struct singleEntry * findEntryByPath(struct grubConfig * cfg,
659       const char * path, const char * prefix,       const char * path, const char * prefix,
660       int * index);       int * index);
661  static char * strndup(char * from, int len);  struct singleEntry * findEntryByTitle(struct grubConfig * cfg, char *title,
662          int * index);
663  static int readFile(int fd, char ** bufPtr);  static int readFile(int fd, char ** bufPtr);
664  static void lineInit(struct singleLine * line);  static void lineInit(struct singleLine * line);
665    struct singleLine * lineDup(struct singleLine * line);
666  static void lineFree(struct singleLine * line);  static void lineFree(struct singleLine * line);
667  static int lineWrite(FILE * out, struct singleLine * line,  static int lineWrite(FILE * out, struct singleLine * line,
668       struct configFileInfo * cfi);       struct configFileInfo * cfi);
669  static int getNextLine(char ** bufPtr, struct singleLine * line,  static int getNextLine(char ** bufPtr, struct singleLine * line,
670         struct configFileInfo * cfi);         struct configFileInfo * cfi);
671  static char * getRootSpecifier(char * str);  static char * getRootSpecifier(char * str);
672    static void requote(struct singleLine *line, struct configFileInfo * cfi);
673  static char * strndup(char * from, int len) {  static void insertElement(struct singleLine * line,
674      char * to;    const char * item, int insertHere,
675      struct configFileInfo * cfi);
676      to = malloc(len + 1);  static void removeElement(struct singleLine * line, int removeHere);
677      strncpy(to, from, len);  static struct keywordTypes * getKeywordByType(enum lineType_e type,
678      to[len] = '\0';        struct configFileInfo * cfi);
679    static enum lineType_e getTypeByKeyword(char * keyword,
680      return to;   struct configFileInfo * cfi);
681  }  static struct singleLine * getLineByType(enum lineType_e type,
682     struct singleLine * line);
683    static int checkForExtLinux(struct grubConfig * config);
684    struct singleLine * addLineTmpl(struct singleEntry * entry,
685                                    struct singleLine * tmplLine,
686                                    struct singleLine * prevLine,
687                                    const char * val,
688     struct configFileInfo * cfi);
689    struct singleLine *  addLine(struct singleEntry * entry,
690                                 struct configFileInfo * cfi,
691                                 enum lineType_e type, char * defaultIndent,
692                                 const char * val);
693    
694  static char * sdupprintf(const char *format, ...)  static char * sdupprintf(const char *format, ...)
695  #ifdef __GNUC__  #ifdef __GNUC__
# Line 313  static char * sdupprintf(const char *for Line 724  static char * sdupprintf(const char *for
724      return buf;      return buf;
725  }  }
726    
727    static enum lineType_e preferredLineType(enum lineType_e type,
728     struct configFileInfo *cfi) {
729        if (isEfi && cfi == &grub2ConfigType) {
730     switch (type) {
731     case LT_KERNEL:
732        return isEfiOnly ? LT_KERNEL : LT_KERNEL_EFI;
733     case LT_INITRD:
734        return isEfiOnly ? LT_INITRD : LT_INITRD_EFI;
735     default:
736        return type;
737     }
738    #if defined(__i386__) || defined(__x86_64__)
739        } else if (cfi == &grub2ConfigType) {
740     switch (type) {
741     case LT_KERNEL:
742        return LT_KERNEL_16;
743     case LT_INITRD:
744        return LT_INITRD_16;
745     default:
746        return type;
747     }
748    #endif
749        }
750        return type;
751    }
752    
753    static struct keywordTypes * getKeywordByType(enum lineType_e type,
754          struct configFileInfo * cfi) {
755        for (struct keywordTypes *kw = cfi->keywords; kw->key; kw++) {
756     if (kw->type == type)
757        return kw;
758        }
759        return NULL;
760    }
761    
762    static char *getKeyByType(enum lineType_e type, struct configFileInfo * cfi) {
763        struct keywordTypes *kt = getKeywordByType(type, cfi);
764        if (kt)
765     return kt->key;
766        return "unknown";
767    }
768    
769    static char * getpathbyspec(char *device) {
770        if (!blkid)
771            blkid_get_cache(&blkid, NULL);
772    
773        return blkid_get_devname(blkid, device, NULL);
774    }
775    
776    static char * getuuidbydev(char *device) {
777        if (!blkid)
778     blkid_get_cache(&blkid, NULL);
779    
780        return blkid_get_tag_value(blkid, "UUID", device);
781    }
782    
783    static enum lineType_e getTypeByKeyword(char * keyword,
784     struct configFileInfo * cfi) {
785        for (struct keywordTypes *kw = cfi->keywords; kw->key; kw++) {
786     if (cfi->caseInsensitive) {
787        if (!strcasecmp(keyword, kw->key))
788                    return kw->type;
789     } else {
790        if (!strcmp(keyword, kw->key))
791            return kw->type;
792     }
793        }
794        return LT_UNKNOWN;
795    }
796    
797    static struct singleLine * getLineByType(enum lineType_e type,
798     struct singleLine * line) {
799        dbgPrintf("getLineByType(%d): ", type);
800        for (; line; line = line->next) {
801     dbgPrintf("%d:%s ", line->type,
802      line->numElements ? line->elements[0].item : "(empty)");
803     if (line->type & type) break;
804        }
805        dbgPrintf(line ? "\n" : " (failed)\n");
806        return line;
807    }
808    
809  static int isBracketedTitle(struct singleLine * line) {  static int isBracketedTitle(struct singleLine * line) {
810      if ((*line->elements[0].item == '[') && (line->numElements == 1)) {      if (line->numElements == 1 && *line->elements[0].item == '[') {
811          int len = strlen(line->elements[0].item);          int len = strlen(line->elements[0].item);
812          if (*(line->elements[0].item + len - 1) == ']') {          if (*(line->elements[0].item + len - 1) == ']') {
813              /* FIXME: this is a hack... */              /* FIXME: this is a hack... */
# Line 326  static int isBracketedTitle(struct singl Line 819  static int isBracketedTitle(struct singl
819      return 0;      return 0;
820  }  }
821    
822  /* figure out if this is a entry separator */  static int isEntryStart(struct singleLine * line,
 static int isEntrySeparator(struct singleLine * line,  
823                              struct configFileInfo * cfi) {                              struct configFileInfo * cfi) {
824      if (line->type == LT_WHITESPACE)      return line->type == cfi->entryStart || line->type == LT_OTHER ||
825   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;  
826  }  }
827    
828  /* extract the title from within brackets (for zipl) */  /* extract the title from within brackets (for zipl) */
829  static char * extractTitle(struct singleLine * line) {  static char * extractTitle(struct singleLine * line) {
830      /* bracketed title... let's extract it (leaks a byte) */      /* bracketed title... let's extract it (leaks a byte) */
831      char * title;      char * title = NULL;
832      title = strdup(line->elements[0].item);      if (line->type == LT_TITLE) {
833      title++;   title = strdup(line->elements[0].item);
834      *(title + strlen(title) - 1) = '\0';   title++;
835     *(title + strlen(title) - 1) = '\0';
836        } else if (line->type == LT_MENUENTRY)
837     title = strdup(line->elements[1].item);
838        else
839     return NULL;
840      return title;      return title;
841  }  }
842    
# Line 389  static void lineInit(struct singleLine * Line 878  static void lineInit(struct singleLine *
878      line->next = NULL;      line->next = NULL;
879  }  }
880    
881  static void lineFree(struct singleLine * line) {  struct singleLine * lineDup(struct singleLine * line) {
882      int i;      struct singleLine * newLine = malloc(sizeof(*newLine));
883    
884        newLine->indent = strdup(line->indent);
885        newLine->next = NULL;
886        newLine->type = line->type;
887        newLine->numElements = line->numElements;
888        newLine->elements = malloc(sizeof(*newLine->elements) *
889           newLine->numElements);
890    
891        for (int i = 0; i < newLine->numElements; i++) {
892     newLine->elements[i].indent = strdup(line->elements[i].indent);
893     newLine->elements[i].item = strdup(line->elements[i].item);
894        }
895    
896        return newLine;
897    }
898    
899    static void lineFree(struct singleLine * line) {
900      if (line->indent) free(line->indent);      if (line->indent) free(line->indent);
901    
902      for (i = 0; i < line->numElements; i++) {      for (int i = 0; i < line->numElements; i++) {
903   free(line->elements[i].item);   free(line->elements[i].item);
904   free(line->elements[i].indent);   free(line->elements[i].indent);
905      }      }
# Line 405  static void lineFree(struct singleLine * Line 910  static void lineFree(struct singleLine *
910    
911  static int lineWrite(FILE * out, struct singleLine * line,  static int lineWrite(FILE * out, struct singleLine * line,
912       struct configFileInfo * cfi) {       struct configFileInfo * cfi) {
     int i;  
   
913      if (fprintf(out, "%s", line->indent) == -1) return -1;      if (fprintf(out, "%s", line->indent) == -1) return -1;
914    
915      for (i = 0; i < line->numElements; i++) {      for (int i = 0; i < line->numElements; i++) {
916     /* Need to handle this, because we strip the quotes from
917     * menuentry when read it. */
918     if (line->type == LT_MENUENTRY && i == 1) {
919        if(!isquote(*line->elements[i].item))
920     fprintf(out, "\'%s\'", line->elements[i].item);
921        else
922     fprintf(out, "%s", line->elements[i].item);
923        fprintf(out, "%s", line->elements[i].indent);
924    
925        continue;
926     }
927    
928   if (i == 1 && line->type == LT_KERNELARGS && cfi->argsInQuotes)   if (i == 1 && line->type == LT_KERNELARGS && cfi->argsInQuotes)
929      if (fputc('"', out) == EOF) return -1;      if (fputc('"', out) == EOF) return -1;
930    
931   if (fprintf(out, "%s", line->elements[i].item) == -1) return -1;   if (fprintf(out, "%s", line->elements[i].item) == -1) return -1;
932   if (fprintf(out, "%s", line->elements[i].indent) == -1) return -1;   if (i < line->numElements - 1)
933        if (fprintf(out, "%s", line->elements[i].indent) == -1) return -1;
934      }      }
935    
936      if (line->type == LT_KERNELARGS && cfi->argsInQuotes)      if (line->type == LT_KERNELARGS && cfi->argsInQuotes)
# Line 433  static int getNextLine(char ** bufPtr, s Line 949  static int getNextLine(char ** bufPtr, s
949      char * chptr;      char * chptr;
950      int elementsAlloced = 0;      int elementsAlloced = 0;
951      struct lineElement * element;      struct lineElement * element;
     struct keywordTypes * keywords = cfi->keywords;  
952      int first = 1;      int first = 1;
     int i;  
953    
954      lineFree(line);      lineFree(line);
955    
# Line 489  static int getNextLine(char ** bufPtr, s Line 1003  static int getNextLine(char ** bufPtr, s
1003      if (!line->numElements)      if (!line->numElements)
1004   line->type = LT_WHITESPACE;   line->type = LT_WHITESPACE;
1005      else {      else {
1006   for (i = 0; keywords[i].key; i++)   line->type = getTypeByKeyword(line->elements[0].item, cfi);
1007      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;  
               
1008              /* zipl does [title] instead of something reasonable like all              /* zipl does [title] instead of something reasonable like all
1009               * the other boot loaders.  kind of ugly */               * the other boot loaders.  kind of ugly */
1010              if (cfi->titleBracketed && isBracketedTitle(line)) {              if (cfi->titleBracketed && isBracketedTitle(line)) {
# Line 510  static int getNextLine(char ** bufPtr, s Line 1018  static int getNextLine(char ** bufPtr, s
1018      if (*line->elements[0].item == '#') {      if (*line->elements[0].item == '#') {
1019   char * fullLine;   char * fullLine;
1020   int len;   int len;
  int i;  
1021    
1022   len = strlen(line->indent);   len = strlen(line->indent);
1023   for (i = 0; i < line->numElements; i++)   for (int i = 0; i < line->numElements; i++)
1024      len += strlen(line->elements[i].item) +      len += strlen(line->elements[i].item) +
1025     strlen(line->elements[i].indent);     strlen(line->elements[i].indent);
1026    
# Line 522  static int getNextLine(char ** bufPtr, s Line 1029  static int getNextLine(char ** bufPtr, s
1029   free(line->indent);   free(line->indent);
1030   line->indent = fullLine;   line->indent = fullLine;
1031    
1032   for (i = 0; i < line->numElements; i++) {   for (int i = 0; i < line->numElements; i++) {
1033      strcat(fullLine, line->elements[i].item);      strcat(fullLine, line->elements[i].item);
1034      strcat(fullLine, line->elements[i].indent);      strcat(fullLine, line->elements[i].indent);
1035      free(line->elements[i].item);      free(line->elements[i].item);
# Line 532  static int getNextLine(char ** bufPtr, s Line 1039  static int getNextLine(char ** bufPtr, s
1039   line->type = LT_WHITESPACE;   line->type = LT_WHITESPACE;
1040   line->numElements = 0;   line->numElements = 0;
1041      }      }
1042     } else {
1043     struct keywordTypes *kw;
1044    
1045     kw = getKeywordByType(line->type, cfi);
1046    
1047     /* space isn't the only separator, we need to split
1048     * elements up more
1049     */
1050     if (!isspace(kw->separatorChar)) {
1051        char indent[2] = "";
1052        indent[0] = kw->separatorChar;
1053        for (int i = 1; i < line->numElements; i++) {
1054     char *p;
1055     int numNewElements;
1056    
1057     numNewElements = 0;
1058     p = line->elements[i].item;
1059     while (*p != '\0') {
1060     if (*p == kw->separatorChar)
1061     numNewElements++;
1062     p++;
1063     }
1064     if (line->numElements + numNewElements >= elementsAlloced) {
1065     elementsAlloced += numNewElements + 5;
1066     line->elements = realloc(line->elements,
1067        sizeof(*line->elements) * elementsAlloced);
1068     }
1069    
1070     for (int j = line->numElements; j > i; j--) {
1071     line->elements[j + numNewElements] = line->elements[j];
1072     }
1073     line->numElements += numNewElements;
1074    
1075     p = line->elements[i].item;
1076     while (*p != '\0') {
1077    
1078     while (*p != kw->separatorChar && *p != '\0') p++;
1079     if (*p == '\0') {
1080     break;
1081     }
1082    
1083     line->elements[i + 1].indent = line->elements[i].indent;
1084     line->elements[i].indent = strdup(indent);
1085     *p++ = '\0';
1086     i++;
1087     line->elements[i].item = strdup(p);
1088     }
1089        }
1090     }
1091   }   }
1092      }      }
1093    
1094      return 0;      return 0;
1095  }  }
1096    
1097    static int isnumber(const char *s)
1098    {
1099        int i;
1100        for (i = 0; s[i] != '\0'; i++)
1101     if (s[i] < '0' || s[i] > '9')
1102        return 0;
1103        return i;
1104    }
1105    
1106  static struct grubConfig * readConfig(const char * inName,  static struct grubConfig * readConfig(const char * inName,
1107        struct configFileInfo * cfi) {        struct configFileInfo * cfi) {
1108      int in;      int in;
# Line 549  static struct grubConfig * readConfig(co Line 1114  static struct grubConfig * readConfig(co
1114      struct singleLine * last = NULL, * line, * defaultLine = NULL;      struct singleLine * last = NULL, * line, * defaultLine = NULL;
1115      char * end;      char * end;
1116      struct singleEntry * entry = NULL;      struct singleEntry * entry = NULL;
1117      int i, len;      int len;
1118      char * buf;      char * buf;
1119    
1120      if (!strcmp(inName, "-")) {      if (inName == NULL) {
1121            printf("Could not find bootloader configuration\n");
1122            exit(1);
1123        } else if (!strcmp(inName, "-")) {
1124   in = 0;   in = 0;
1125      } else {      } else {
1126   if ((in = open(inName, O_RDONLY)) < 0) {   if ((in = open(inName, O_RDONLY)) < 0) {
# Line 595  static struct grubConfig * readConfig(co Line 1163  static struct grubConfig * readConfig(co
1163      cfg->secondaryIndent = strdup(line->indent);      cfg->secondaryIndent = strdup(line->indent);
1164   }   }
1165    
1166   if (isEntrySeparator(line, cfi)) {   if (isEntryStart(line, cfi) || (cfg->entries && !sawEntry)) {
1167      sawEntry = 1;      sawEntry = 1;
1168      if (!entry) {      if (!entry) {
1169   cfg->entries = malloc(sizeof(*entry));   cfg->entries = malloc(sizeof(*entry));
# Line 611  static struct grubConfig * readConfig(co Line 1179  static struct grubConfig * readConfig(co
1179      entry->next = NULL;      entry->next = NULL;
1180   }   }
1181    
1182   if (line->type == LT_DEFAULT && line->numElements == 2) {   if (line->type == LT_SET_VARIABLE) {
1183      cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;      dbgPrintf("found 'set' command (%d elements): ", line->numElements);
1184      defaultLine = line;      dbgPrintf("%s", line->indent);
1185        for (int i = 0; i < line->numElements; i++)
1186     dbgPrintf("\"%s\"%s", line->elements[i].item, line->elements[i].indent);
1187        dbgPrintf("\n");
1188        struct keywordTypes *kwType = getKeywordByType(LT_DEFAULT, cfi);
1189        if (kwType && line->numElements == 3 &&
1190        !strcmp(line->elements[1].item, kwType->key) &&
1191        !is_special_grub2_variable(line->elements[2].item)) {
1192     dbgPrintf("Line sets default config\n");
1193     cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
1194     defaultLine = line;
1195        }
1196    
1197            } else if (iskernel(line->type)) {
1198        /* if by some freak chance this is multiboot and the "module"
1199         * lines came earlier in the template, make sure to use LT_HYPER
1200         * instead of LT_KERNEL now
1201         */
1202        if (entry && entry->multiboot)
1203     line->type = LT_HYPER;
1204    
1205          } else if (line->type == LT_MBMODULE) {          } else if (line->type == LT_MBMODULE) {
1206        /* go back and fix the LT_KERNEL line to indicate LT_HYPER
1207         * instead, now that we know this is a multiboot entry.
1208         * This only applies to grub, but that's the only place we
1209         * should find LT_MBMODULE lines anyway.
1210         */
1211        for (struct singleLine *l = entry->lines; l; l = l->next) {
1212     if (l->type == LT_HYPER)
1213        break;
1214     else if (iskernel(l->type)) {
1215        l->type = LT_HYPER;
1216        break;
1217     }
1218        }
1219              entry->multiboot = 1;              entry->multiboot = 1;
1220    
1221     } else if (line->type == LT_HYPER) {
1222        entry->multiboot = 1;
1223    
1224   } else if (line->type == LT_FALLBACK && line->numElements == 2) {   } else if (line->type == LT_FALLBACK && line->numElements == 2) {
1225      cfg->fallbackImage = strtol(line->elements[1].item, &end, 10);      cfg->fallbackImage = strtol(line->elements[1].item, &end, 10);
1226      if (*end) cfg->fallbackImage = -1;      if (*end) cfg->fallbackImage = -1;
1227   } else if (line->type == LT_TITLE && line->numElements > 1) {  
1228      /* make the title a single argument (undoing our parsing) */   } else if ((line->type == LT_DEFAULT && cfi->defaultIsUnquoted) ||
1229                    (line->type == LT_TITLE && line->numElements > 1)) {
1230        /* make the title/default a single argument (undoing our parsing) */
1231      len = 0;      len = 0;
1232      for (i = 1; i < line->numElements; i++) {      for (int i = 1; i < line->numElements; i++) {
1233   len += strlen(line->elements[i].item);   len += strlen(line->elements[i].item);
1234   len += strlen(line->elements[i].indent);   len += strlen(line->elements[i].indent);
1235      }      }
1236      buf = malloc(len + 1);      buf = malloc(len + 1);
1237      *buf = '\0';      *buf = '\0';
1238    
1239      for (i = 1; i < line->numElements; i++) {      for (int i = 1; i < line->numElements; i++) {
1240   strcat(buf, line->elements[i].item);   strcat(buf, line->elements[i].item);
1241   free(line->elements[i].item);   free(line->elements[i].item);
1242    
# Line 643  static struct grubConfig * readConfig(co Line 1250  static struct grubConfig * readConfig(co
1250      line->elements[line->numElements - 1].indent;      line->elements[line->numElements - 1].indent;
1251      line->elements[1].item = buf;      line->elements[1].item = buf;
1252      line->numElements = 2;      line->numElements = 2;
1253     } else if (line->type == LT_MENUENTRY && line->numElements > 3) {
1254        /* let --remove-kernel="TITLE=what" work */
1255        len = 0;
1256        char *extras;
1257        char *title;
1258    
1259        for (int i = 1; i < line->numElements; i++) {
1260     len += strlen(line->elements[i].item);
1261     len += strlen(line->elements[i].indent);
1262        }
1263        buf = malloc(len + 1);
1264        *buf = '\0';
1265    
1266        /* allocate mem for extra flags. */
1267        extras = malloc(len + 1);
1268        *extras = '\0';
1269    
1270        /* get title. */
1271        for (int i = 0; i < line->numElements; i++) {
1272     if (!strcmp(line->elements[i].item, "menuentry"))
1273        continue;
1274     if (isquote(*line->elements[i].item))
1275        title = line->elements[i].item + 1;
1276     else
1277        title = line->elements[i].item;
1278    
1279     len = strlen(title);
1280            if (isquote(title[len-1])) {
1281        strncat(buf, title,len-1);
1282        break;
1283     } else {
1284        strcat(buf, title);
1285        strcat(buf, line->elements[i].indent);
1286     }
1287        }
1288    
1289        /* get extras */
1290        int count = 0;
1291        for (int i = 0; i < line->numElements; i++) {
1292     if (count >= 2) {
1293        strcat(extras, line->elements[i].item);
1294        strcat(extras, line->elements[i].indent);
1295     }
1296    
1297     if (!strcmp(line->elements[i].item, "menuentry"))
1298        continue;
1299    
1300     /* count ' or ", there should be two in menuentry line. */
1301     if (isquote(*line->elements[i].item))
1302        count++;
1303    
1304     len = strlen(line->elements[i].item);
1305    
1306     if (isquote(line->elements[i].item[len -1]))
1307        count++;
1308    
1309     /* ok, we get the final ' or ", others are extras. */
1310                }
1311        line->elements[1].indent =
1312     line->elements[line->numElements - 2].indent;
1313        line->elements[1].item = buf;
1314        line->elements[2].indent =
1315     line->elements[line->numElements - 2].indent;
1316        line->elements[2].item = extras;
1317        line->numElements = 3;
1318   } else if (line->type == LT_KERNELARGS && cfi->argsInQuotes) {   } else if (line->type == LT_KERNELARGS && cfi->argsInQuotes) {
1319      /* Strip off any " which may be present; they'll be put back      /* Strip off any " which may be present; they'll be put back
1320         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 1323  static struct grubConfig * readConfig(co
1323      if (line->numElements >= 2) {      if (line->numElements >= 2) {
1324   int last, len;   int last, len;
1325    
1326   if (*line->elements[1].item == '"')   if (isquote(*line->elements[1].item))
1327      memcpy(line->elements[1].item, line->elements[1].item + 1,      memmove(line->elements[1].item, line->elements[1].item + 1,
1328     strlen(line->elements[1].item + 1) + 1);      strlen(line->elements[1].item + 1) + 1);
1329    
1330   last = line->numElements - 1;   last = line->numElements - 1;
1331   len = strlen(line->elements[last].item) - 1;   len = strlen(line->elements[last].item) - 1;
1332   if (line->elements[last].item[len] == '"')   if (isquote(line->elements[last].item[len]))
1333      line->elements[last].item[len] = '\0';      line->elements[last].item[len] = '\0';
1334      }      }
1335     }
1336    
1337     if (line->type == LT_DEFAULT && line->numElements == 2) {
1338        cfg->flags &= ~GRUB_CONFIG_NO_DEFAULT;
1339        defaultLine = line;
1340   }   }
1341    
1342   /* 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 1356  static struct grubConfig * readConfig(co
1356   movedLine = 1;   movedLine = 1;
1357   continue; /* without setting 'last' */   continue; /* without setting 'last' */
1358   }   }
1359    
1360   /* If a second line of whitespace happens after a generic option   /* If a second line of whitespace happens after a generic option
1361     which was moved, drop it. */     which was moved, drop it. */
1362   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 1372  static struct grubConfig * readConfig(co
1372   entry->lines = line;   entry->lines = line;
1373      else      else
1374   last->next = line;   last->next = line;
1375        dbgPrintf("readConfig added %s to %p\n", getKeyByType(line->type, cfi), entry);
1376    
1377        /* we could have seen this outside of an entry... if so, we
1378         * ignore it like any other line we don't grok */
1379        if (line->type == LT_ENTRY_END && sawEntry)
1380     sawEntry = 0;
1381   } else {   } else {
1382      if (!cfg->theLines)      if (!cfg->theLines)
1383   cfg->theLines = line;   cfg->theLines = line;
1384      else {      else
1385   last->next = line;   last->next = line;
1386      }      dbgPrintf("readConfig added %s to cfg\n", getKeyByType(line->type, cfi));
1387   }   }
1388    
1389   last = line;   last = line;
# Line 708  static struct grubConfig * readConfig(co Line 1391  static struct grubConfig * readConfig(co
1391    
1392      free(incoming);      free(incoming);
1393    
1394        dbgPrintf("defaultLine is %s\n", defaultLine ? "set" : "unset");
1395      if (defaultLine) {      if (defaultLine) {
1396   if (cfi->defaultSupportSaved &&          if (defaultLine->numElements > 2 &&
1397        cfi->defaultSupportSaved &&
1398        !strncmp(defaultLine->elements[2].item,"\"${saved_entry}\"", 16)) {
1399     cfg->cfi->defaultIsSaved = 1;
1400     cfg->defaultImage = DEFAULT_SAVED_GRUB2;
1401     if (cfg->cfi->getEnv) {
1402        char *defTitle = cfi->getEnv(cfg->cfi, "saved_entry");
1403        if (defTitle) {
1404     int index = 0;
1405     if (isnumber(defTitle)) {
1406        index = atoi(defTitle);
1407        entry = findEntryByIndex(cfg, index);
1408     } else {
1409        entry = findEntryByTitle(cfg, defTitle, &index);
1410     }
1411     if (entry)
1412        cfg->defaultImage = index;
1413        }
1414     }
1415     } else if (cfi->defaultIsVariable) {
1416        char *value = defaultLine->elements[2].item;
1417        while (*value && (*value == '"' || *value == '\'' ||
1418        *value == ' ' || *value == '\t'))
1419     value++;
1420        cfg->defaultImage = strtol(value, &end, 10);
1421        while (*end && (*end == '"' || *end == '\'' ||
1422        *end == ' ' || *end == '\t'))
1423     end++;
1424        if (*end) cfg->defaultImage = -1;
1425     } else if (cfi->defaultSupportSaved &&
1426   !strncmp(defaultLine->elements[1].item, "saved", 5)) {   !strncmp(defaultLine->elements[1].item, "saved", 5)) {
1427      cfg->defaultImage = DEFAULT_SAVED;      cfg->defaultImage = DEFAULT_SAVED;
1428   } else if (cfi->defaultIsIndex) {   } else if (cfi->defaultIsIndex) {
1429      cfg->defaultImage = strtol(defaultLine->elements[1].item, &end, 10);      cfg->defaultImage = strtol(defaultLine->elements[1].item, &end, 10);
1430      if (*end) cfg->defaultImage = -1;      if (*end) cfg->defaultImage = -1;
1431   } else if (defaultLine->numElements >= 2) {   } else if (defaultLine->numElements >= 2) {
1432      i = 0;      int i = 0;
1433      while ((entry = findEntryByIndex(cfg, i))) {      while ((entry = findEntryByIndex(cfg, i))) {
1434   for (line = entry->lines; line; line = line->next)   for (line = entry->lines; line; line = line->next)
1435      if (line->type == LT_TITLE) break;      if (line->type == LT_TITLE) break;
# Line 730  static struct grubConfig * readConfig(co Line 1443  static struct grubConfig * readConfig(co
1443                                  extractTitle(line))) break;                                  extractTitle(line))) break;
1444                  }                  }
1445   i++;   i++;
1446     entry = NULL;
1447      }      }
1448    
1449      if (entry) cfg->defaultImage = i;      if (entry){
1450            cfg->defaultImage = i;
1451        }else{
1452            cfg->defaultImage = -1;
1453        }
1454   }   }
1455        } else if (cfg->cfi->defaultIsSaved && cfg->cfi->getEnv) {
1456     char *defTitle = cfi->getEnv(cfg->cfi, "saved_entry");
1457     if (defTitle) {
1458        int index = 0;
1459        if (isnumber(defTitle)) {
1460     index = atoi(defTitle);
1461     entry = findEntryByIndex(cfg, index);
1462        } else {
1463     entry = findEntryByTitle(cfg, defTitle, &index);
1464        }
1465        if (entry)
1466     cfg->defaultImage = index;
1467     }
1468        } else {
1469            cfg->defaultImage = 0;
1470      }      }
1471    
1472      return cfg;      return cfg;
# Line 749  static void writeDefault(FILE * out, cha Line 1482  static void writeDefault(FILE * out, cha
1482    
1483      if (cfg->defaultImage == DEFAULT_SAVED)      if (cfg->defaultImage == DEFAULT_SAVED)
1484   fprintf(out, "%sdefault%ssaved\n", indent, separator);   fprintf(out, "%sdefault%ssaved\n", indent, separator);
1485      else if (cfg->defaultImage > -1) {      else if (cfg->cfi->defaultIsSaved) {
1486     fprintf(out, "%sset default=\"${saved_entry}\"\n", indent);
1487     if (cfg->defaultImage >= 0 && cfg->cfi->setEnv) {
1488        char *title;
1489        entry = findEntryByIndex(cfg, cfg->defaultImage);
1490        line = getLineByType(LT_MENUENTRY, entry->lines);
1491        if (!line)
1492     line = getLineByType(LT_TITLE, entry->lines);
1493        if (line) {
1494     title = extractTitle(line);
1495     if (title)
1496        cfg->cfi->setEnv(cfg->cfi, "saved_entry", title);
1497        }
1498     }
1499        } else if (cfg->defaultImage > -1) {
1500   if (cfg->cfi->defaultIsIndex) {   if (cfg->cfi->defaultIsIndex) {
1501      fprintf(out, "%sdefault%s%d\n", indent, separator,      if (cfg->cfi->defaultIsVariable) {
1502      cfg->defaultImage);          fprintf(out, "%sset default=\"%d\"\n", indent,
1503     cfg->defaultImage);
1504        } else {
1505     fprintf(out, "%sdefault%s%d\n", indent, separator,
1506     cfg->defaultImage);
1507        }
1508   } else {   } else {
1509      int image = cfg->defaultImage;      int image = cfg->defaultImage;
1510    
# Line 769  static void writeDefault(FILE * out, cha Line 1521  static void writeDefault(FILE * out, cha
1521    
1522      if (!entry) return;      if (!entry) return;
1523    
1524      line = entry->lines;      line = getLineByType(LT_TITLE, entry->lines);
     while (line && line->type != LT_TITLE) line = line->next;  
1525    
1526      if (line && line->numElements >= 2)      if (line && line->numElements >= 2)
1527   fprintf(out, "%sdefault%s%s\n", indent, separator,   fprintf(out, "%sdefault%s%s\n", indent, separator,
# Line 804  static int writeConfig(struct grubConfig Line 1555  static int writeConfig(struct grubConfig
1555      int rc;      int rc;
1556    
1557      /* most likely the symlink is relative, so change our      /* most likely the symlink is relative, so change our
1558         directory to / */         directory to the dir of the symlink */
1559      rc = chdir("/");      char *dir = strdupa(outName);
1560        rc = chdir(dirname(dir));
1561      do {      do {
1562   buf = alloca(len + 1);   buf = alloca(len + 1);
1563   rc = readlink(outName, buf, len);   rc = readlink(basename(outName), buf, len);
1564   if (rc == len) len += 256;   if (rc == len) len += 256;
1565      } while (rc == len);      } while (rc == len);
1566            
# Line 843  static int writeConfig(struct grubConfig Line 1595  static int writeConfig(struct grubConfig
1595      }      }
1596    
1597      line = cfg->theLines;      line = cfg->theLines;
1598        struct keywordTypes *defaultKw = getKeywordByType(LT_DEFAULT, cfg->cfi);
1599      while (line) {      while (line) {
1600   if (line->type == LT_DEFAULT) {          if (line->type == LT_SET_VARIABLE && defaultKw &&
1601     line->numElements == 3 &&
1602     !strcmp(line->elements[1].item, defaultKw->key) &&
1603     !is_special_grub2_variable(line->elements[2].item)) {
1604        writeDefault(out, line->indent, line->elements[0].indent, cfg);
1605        needs &= ~MAIN_DEFAULT;
1606     } else if (line->type == LT_DEFAULT) {
1607      writeDefault(out, line->indent, line->elements[0].indent, cfg);      writeDefault(out, line->indent, line->elements[0].indent, cfg);
1608      needs &= ~MAIN_DEFAULT;      needs &= ~MAIN_DEFAULT;
1609   } else if (line->type == LT_FALLBACK) {   } else if (line->type == LT_FALLBACK) {
# Line 912  static int numEntries(struct grubConfig Line 1671  static int numEntries(struct grubConfig
1671      return i;      return i;
1672  }  }
1673    
1674  int suitableImage(struct singleEntry * entry, const char * bootPrefix,  static char *findDiskForRoot()
1675    int skipRemoved, int flags) {  {
1676      struct singleLine * line;      int fd;
1677      char * fullName;      char buf[65536];
1678      int i;      char *devname;
1679      struct stat sb, sb2;      char *chptr;
1680      char * dev;      int rc;
     char * end;  
     char * rootspec;  
1681    
1682      line = entry->lines;      if ((fd = open(_PATH_MOUNTED, O_RDONLY)) < 0) {
1683      while (line && line->type != LT_KERNEL) line = line->next;          fprintf(stderr, "grubby: failed to open %s: %s\n",
1684                        _PATH_MOUNTED, strerror(errno));
1685      if (!line) return 0;          return NULL;
1686      if (skipRemoved && entry->skip) return 0;      }
     if (line->numElements < 2) return 0;  
1687    
1688      if (flags & GRUBBY_BADIMAGE_OKAY) return 1;      rc = read(fd, buf, sizeof(buf) - 1);
1689        if (rc <= 0) {
1690            fprintf(stderr, "grubby: failed to read %s: %s\n",
1691                    _PATH_MOUNTED, strerror(errno));
1692            close(fd);
1693            return NULL;
1694        }
1695        close(fd);
1696        buf[rc] = '\0';
1697        chptr = buf;
1698    
1699        char *foundanswer = NULL;
1700    
1701        while (chptr && chptr != buf+rc) {
1702            devname = chptr;
1703    
1704            /*
1705             * The first column of a mtab entry is the device, but if the entry is a
1706             * special device it won't start with /, so move on to the next line.
1707             */
1708            if (*devname != '/') {
1709                chptr = strchr(chptr, '\n');
1710                if (chptr)
1711                    chptr++;
1712                continue;
1713            }
1714    
1715            /* Seek to the next space */
1716            chptr = strchr(chptr, ' ');
1717            if (!chptr) {
1718                fprintf(stderr, "grubby: error parsing %s: %s\n",
1719                        _PATH_MOUNTED, strerror(errno));
1720                return NULL;
1721            }
1722    
1723            /*
1724             * The second column of a mtab entry is the mount point, we are looking
1725             * for '/' obviously.
1726             */
1727            if (*(++chptr) == '/' && *(++chptr) == ' ') {
1728                /* remember the last / entry in mtab */
1729               foundanswer = devname;
1730            }
1731    
1732            /* Next line */
1733            chptr = strchr(chptr, '\n');
1734            if (chptr)
1735                chptr++;
1736        }
1737    
1738        /* Return the last / entry found */
1739        if (foundanswer) {
1740            chptr = strchr(foundanswer, ' ');
1741            *chptr = '\0';
1742            return strdup(foundanswer);
1743        }
1744    
1745        return NULL;
1746    }
1747    
1748    void printEntry(struct singleEntry * entry, FILE *f) {
1749        int i;
1750        struct singleLine * line;
1751    
1752        for (line = entry->lines; line; line = line->next) {
1753     log_message(f, "DBG: %s", line->indent);
1754     for (i = 0; i < line->numElements; i++) {
1755        /* Need to handle this, because we strip the quotes from
1756         * menuentry when read it. */
1757        if (line->type == LT_MENUENTRY && i == 1) {
1758     if(!isquote(*line->elements[i].item))
1759        log_message(f, "\'%s\'", line->elements[i].item);
1760     else
1761        log_message(f, "%s", line->elements[i].item);
1762     log_message(f, "%s", line->elements[i].indent);
1763    
1764     continue;
1765        }
1766        
1767        log_message(f, "%s%s",
1768        line->elements[i].item, line->elements[i].indent);
1769     }
1770     log_message(f, "\n");
1771        }
1772    }
1773    
1774    void notSuitablePrintf(struct singleEntry * entry, int okay, const char *fmt, ...)
1775    {
1776        static int once;
1777        va_list argp, argq;
1778    
1779        va_start(argp, fmt);
1780    
1781        va_copy(argq, argp);
1782        if (!once) {
1783     log_time(NULL);
1784     log_message(NULL, "command line: %s\n", saved_command_line);
1785        }
1786        log_message(NULL, "DBG: Image entry %s: ", okay ? "succeeded" : "failed");
1787        log_vmessage(NULL, fmt, argq);
1788    
1789        printEntry(entry, NULL);
1790        va_end(argq);
1791    
1792        if (!debug) {
1793     once = 1;
1794         va_end(argp);
1795     return;
1796        }
1797    
1798        if (okay) {
1799     va_end(argp);
1800     return;
1801        }
1802    
1803        if (!once)
1804     log_message(stderr, "DBG: command line: %s\n", saved_command_line);
1805        once = 1;
1806        fprintf(stderr, "DBG: Image entry failed: ");
1807        vfprintf(stderr, fmt, argp);
1808        printEntry(entry, stderr);
1809        va_end(argp);
1810    }
1811    
1812    #define beginswith(s, c) ((s) && (s)[0] == (c))
1813    
1814    static int endswith(const char *s, char c)
1815    {
1816     int slen;
1817    
1818     if (!s || !s[0])
1819     return 0;
1820     slen = strlen(s) - 1;
1821    
1822     return s[slen] == c;
1823    }
1824    
1825    int suitableImage(struct singleEntry * entry, const char * bootPrefix,
1826      int skipRemoved, int flags) {
1827        struct singleLine * line;
1828        char * fullName;
1829        int i;
1830        char * dev;
1831        char * rootspec;
1832        char * rootdev;
1833    
1834        if (skipRemoved && entry->skip) {
1835     notSuitablePrintf(entry, 0, "marked to skip\n");
1836     return 0;
1837        }
1838    
1839        line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
1840        if (!line) {
1841     notSuitablePrintf(entry, 0, "no line found\n");
1842     return 0;
1843        }
1844        if (line->numElements < 2) {
1845     notSuitablePrintf(entry, 0, "line has only %d elements\n",
1846        line->numElements);
1847     return 0;
1848        }
1849    
1850        if (flags & GRUBBY_BADIMAGE_OKAY) {
1851        notSuitablePrintf(entry, 1, "\n");
1852        return 1;
1853        }
1854    
1855      fullName = alloca(strlen(bootPrefix) +      fullName = alloca(strlen(bootPrefix) +
1856        strlen(line->elements[1].item) + 1);        strlen(line->elements[1].item) + 1);
1857      rootspec = getRootSpecifier(line->elements[1].item);      rootspec = getRootSpecifier(line->elements[1].item);
1858      sprintf(fullName, "%s%s", bootPrefix,      int rootspec_offset = rootspec ? strlen(rootspec) : 0;
1859              line->elements[1].item + ((rootspec != NULL) ?      int hasslash = endswith(bootPrefix, '/') ||
1860                                        strlen(rootspec) : 0));       beginswith(line->elements[1].item + rootspec_offset, '/');
1861      if (access(fullName, R_OK)) return 0;      sprintf(fullName, "%s%s%s", bootPrefix, hasslash ? "" : "/",
1862                line->elements[1].item + rootspec_offset);
1863        if (access(fullName, R_OK)) {
1864     notSuitablePrintf(entry, 0, "access to %s failed\n", fullName);
1865     return 0;
1866        }
1867      for (i = 2; i < line->numElements; i++)      for (i = 2; i < line->numElements; i++)
1868   if (!strncasecmp(line->elements[i].item, "root=", 5)) break;   if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1869      if (i < line->numElements) {      if (i < line->numElements) {
1870   dev = line->elements[i].item + 5;   dev = line->elements[i].item + 5;
1871      } else {      } else {
1872   /* look for a lilo style LT_ROOT line */   /* look for a lilo style LT_ROOT line */
1873   line = entry->lines;   line = getLineByType(LT_ROOT, entry->lines);
  while (line && line->type != LT_ROOT) line = line->next;  
1874    
1875   if (line && line->numElements >= 2) {   if (line && line->numElements >= 2) {
1876      dev = line->elements[1].item;      dev = line->elements[1].item;
1877   } else {   } else {
1878              int type;      /* didn't succeed in finding a LT_ROOT, let's try LT_KERNELARGS.
1879      /* 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.
1880      line = entry->lines;       */
1881        line = getLineByType(LT_KERNELARGS|LT_MBMODULE, entry->lines);
             type = ((entry->multiboot) ? LT_MBMODULE : LT_KERNELARGS);  
   
     while (line && line->type != type) line = line->next;  
1882    
1883              /* failed to find one */              /* failed to find one */
1884              if (!line) return 0;              if (!line) {
1885     notSuitablePrintf(entry, 0, "no line found\n");
1886     return 0;
1887                }
1888    
1889      for (i = 1; i < line->numElements; i++)      for (i = 1; i < line->numElements; i++)
1890          if (!strncasecmp(line->elements[i].item, "root=", 5)) break;          if (!strncasecmp(line->elements[i].item, "root=", 5)) break;
1891      if (i < line->numElements)      if (i < line->numElements)
1892          dev = line->elements[i].item + 5;          dev = line->elements[i].item + 5;
1893      else {      else {
1894     notSuitablePrintf(entry, 0, "no root= entry found\n");
1895   /* it failed too...  can't find root= */   /* it failed too...  can't find root= */
1896          return 0;          return 0;
1897              }              }
1898   }   }
1899      }      }
1900    
1901      if (!strncmp(dev, "LABEL=", 6)) {      dev = getpathbyspec(dev);
1902   dev += 6;      if (!getpathbyspec(dev)) {
1903            notSuitablePrintf(entry, 0, "can't find blkid entry for %s\n", dev);
1904   /* check which device has this label */          return 0;
1905   dev = get_spec_by_volume_label(dev, &i, &i);      } else
1906   if (!dev) return 0;   dev = getpathbyspec(dev);
1907    
1908        rootdev = findDiskForRoot();
1909        if (!rootdev) {
1910            notSuitablePrintf(entry, 0, "can't find root device\n");
1911     return 0;
1912      }      }
1913    
1914      if (*dev == '/') {      if (!getuuidbydev(rootdev) || !getuuidbydev(dev)) {
1915   if (stat(dev, &sb))          notSuitablePrintf(entry, 0, "uuid missing: rootdev %s, dev %s\n",
1916      return 0;   getuuidbydev(rootdev), getuuidbydev(dev));
1917      } else {          free(rootdev);
1918   sb.st_rdev = strtol(dev, &end, 16);          return 0;
1919   if (*end) return 0;      }
1920    
1921        if (strcmp(getuuidbydev(rootdev), getuuidbydev(dev))) {
1922            notSuitablePrintf(entry, 0, "uuid mismatch: rootdev %s, dev %s\n",
1923     getuuidbydev(rootdev), getuuidbydev(dev));
1924     free(rootdev);
1925            return 0;
1926      }      }
     stat("/", &sb2);  
1927    
1928      if (sb.st_rdev != sb2.st_dev) return 0;      free(rootdev);
1929        notSuitablePrintf(entry, 1, "\n");
1930    
1931      return 1;      return 1;
1932  }  }
# Line 1034  struct singleEntry * findEntryByPath(str Line 1970  struct singleEntry * findEntryByPath(str
1970   entry = findEntryByIndex(config, indexVars[i]);   entry = findEntryByIndex(config, indexVars[i]);
1971   if (!entry) return NULL;   if (!entry) return NULL;
1972    
1973   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;  
   
1974   if (!line) return NULL;   if (!line) return NULL;
1975    
1976   if (index) *index = indexVars[i];   if (index) *index = indexVars[i];
# Line 1075  struct singleEntry * findEntryByPath(str Line 2008  struct singleEntry * findEntryByPath(str
2008    
2009   if (!strncmp(kernel, "TITLE=", 6)) {   if (!strncmp(kernel, "TITLE=", 6)) {
2010      prefix = "";      prefix = "";
2011      checkType = LT_TITLE;      checkType = LT_TITLE|LT_MENUENTRY;
2012      kernel += 6;      kernel += 6;
2013   }   }
2014    
2015   while ((entry = findEntryByIndex(config, i))) {   for (entry = findEntryByIndex(config, i); entry; entry = entry->next, i++) {
2016      line = entry->lines;      if (entry->skip) continue;
     while (line && line->type != checkType) line=line->next;  
2017    
2018        dbgPrintf("findEntryByPath looking for %d %s in %p\n", checkType, kernel, entry);
2019    
2020      if (line && line->numElements >= 2 && !entry->skip) {      /* check all the lines matching checkType */
2021                  rootspec = getRootSpecifier(line->elements[1].item);      for (line = entry->lines; line; line = line->next) {
2022          if (!strcmp(line->elements[1].item  +   enum lineType_e ct = checkType;
2023                              ((rootspec != NULL) ? strlen(rootspec) : 0),   if (entry->multiboot && checkType == LT_KERNEL)
2024                              kernel + strlen(prefix)))      ct = LT_KERNEL|LT_KERNEL_EFI|LT_MBMODULE|LT_HYPER|LT_KERNEL_16;
2025                      break;   else if (checkType & LT_KERNEL)
2026              }      ct = checkType | LT_KERNEL_EFI | LT_KERNEL_16;
2027                 line = getLineByType(ct, line);
2028              /* have to check multiboot lines too */   if (!line)
2029              if (entry->multiboot) {      break;  /* not found in this entry */
2030                  while (line && line->type != LT_MBMODULE) line = line->next;  
2031                  if (line && line->numElements >= 2 && !entry->skip) {   if (line && line->type != LT_MENUENTRY &&
2032                      rootspec = getRootSpecifier(line->elements[1].item);   line->numElements >= 2) {
2033                      if (!strcmp(line->elements[1].item  +      rootspec = getRootSpecifier(line->elements[1].item);
2034                                  ((rootspec != NULL) ? strlen(rootspec) : 0),      if (!strcmp(line->elements[1].item +
2035                                  kernel + strlen(prefix)))   ((rootspec != NULL) ? strlen(rootspec) : 0),
2036                          break;   kernel + strlen(prefix)))
2037                  }   break;
2038              }   }
2039     if(line->type == LT_MENUENTRY &&
2040     !strcmp(line->elements[1].item, kernel))
2041        break;
2042        }
2043    
2044      i++;      /* make sure this entry has a kernel identifier; this skips
2045         * non-Linux boot entries (could find netbsd etc, though, which is
2046         * unfortunate)
2047         */
2048        if (line && getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines))
2049     break; /* found 'im! */
2050   }   }
2051    
2052   if (index) *index = i;   if (index) *index = i;
2053      }      }
2054    
2055      if (!entry) return NULL;      return entry;
2056    }
2057    
2058      /* make sure this entry has a kernel identifier; this skips non-Linux  struct singleEntry * findEntryByTitle(struct grubConfig * cfg, char *title,
2059         boot entries (could find netbsd etc, though, which is unfortunate) */        int * index) {
2060      line = entry->lines;      struct singleEntry * entry;
2061      while (line && line->type != LT_KERNEL) line = line->next;      struct singleLine * line;
2062      if (!line) {      int i;
2063   if (!index) index = &i;      char * newtitle;
2064   (*index)++;  
2065   return findEntryByPath(config, kernel, prefix, index);      for (i = 0, entry = cfg->entries; entry; entry = entry->next, i++) {
2066     if (index && i < *index)
2067        continue;
2068     line = getLineByType(LT_TITLE, entry->lines);
2069     if (!line)
2070        line = getLineByType(LT_MENUENTRY, entry->lines);
2071     if (!line)
2072        continue;
2073     newtitle = grub2ExtractTitle(line);
2074     if (!newtitle)
2075        continue;
2076     if (!strcmp(title, newtitle))
2077        break;
2078      }      }
2079    
2080        if (!entry)
2081     return NULL;
2082    
2083        if (index)
2084     *index = i;
2085      return entry;      return entry;
2086  }  }
2087    
# Line 1147  struct singleEntry * findTemplate(struct Line 2107  struct singleEntry * findTemplate(struct
2107      struct singleEntry * entry, * entry2;      struct singleEntry * entry, * entry2;
2108      int index;      int index;
2109    
2110      if (cfg->defaultImage > -1) {      if (cfg->cfi->defaultIsSaved) {
2111     if (cfg->cfi->getEnv) {
2112        char *defTitle = cfg->cfi->getEnv(cfg->cfi, "saved_entry");
2113        if (defTitle) {
2114     int index = 0;
2115     if (isnumber(defTitle)) {
2116        index = atoi(defTitle);
2117        entry = findEntryByIndex(cfg, index);
2118     } else {
2119        entry = findEntryByTitle(cfg, defTitle, &index);
2120     }
2121     if (entry)
2122        cfg->defaultImage = index;
2123        }
2124     }
2125        } else if (cfg->defaultImage > -1) {
2126   entry = findEntryByIndex(cfg, cfg->defaultImage);   entry = findEntryByIndex(cfg, cfg->defaultImage);
2127   if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {   if (entry && suitableImage(entry, prefix, skipRemoved, flags)) {
2128      if (indexPtr) *indexPtr = cfg->defaultImage;      if (indexPtr) *indexPtr = cfg->defaultImage;
# Line 1200  void markRemovedImage(struct grubConfig Line 2175  void markRemovedImage(struct grubConfig
2175        const char * prefix) {        const char * prefix) {
2176      struct singleEntry * entry;      struct singleEntry * entry;
2177    
2178      if (!image) return;      if (!image)
2179     return;
2180    
2181        /* check and see if we're removing the default image */
2182        if (isdigit(*image)) {
2183     entry = findEntryByPath(cfg, image, prefix, NULL);
2184     if(entry)
2185        entry->skip = 1;
2186     return;
2187        }
2188    
2189      while ((entry = findEntryByPath(cfg, image, prefix, NULL)))      while ((entry = findEntryByPath(cfg, image, prefix, NULL)))
2190   entry->skip = 1;   entry->skip = 1;
# Line 1208  void markRemovedImage(struct grubConfig Line 2192  void markRemovedImage(struct grubConfig
2192    
2193  void setDefaultImage(struct grubConfig * config, int hasNew,  void setDefaultImage(struct grubConfig * config, int hasNew,
2194       const char * defaultKernelPath, int newIsDefault,       const char * defaultKernelPath, int newIsDefault,
2195       const char * prefix, int flags) {       const char * prefix, int flags, int index) {
2196      struct singleEntry * entry, * entry2, * newDefault;      struct singleEntry * entry, * entry2, * newDefault;
2197      int i, j;      int i, j;
2198    
2199      if (newIsDefault) {      if (newIsDefault) {
2200   config->defaultImage = 0;   config->defaultImage = 0;
2201   return;   return;
2202        } else if ((index >= 0) && config->cfi->defaultIsIndex) {
2203     if (findEntryByIndex(config, index))
2204        config->defaultImage = index;
2205     else
2206        config->defaultImage = -1;
2207     return;
2208      } else if (defaultKernelPath) {      } else if (defaultKernelPath) {
2209   i = 0;   i = 0;
2210   if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {   if (findEntryByPath(config, defaultKernelPath, prefix, &i)) {
# Line 1227  void setDefaultImage(struct grubConfig * Line 2217  void setDefaultImage(struct grubConfig *
2217    
2218      /* 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
2219         changes */         changes */
2220      if (config->defaultImage == DEFAULT_SAVED)      if ((config->defaultImage == DEFAULT_SAVED) ||
2221     (config->defaultImage == DEFAULT_SAVED_GRUB2))
2222        /* default is set to saved, we don't want to change it */        /* default is set to saved, we don't want to change it */
2223        return;        return;
2224    
# Line 1286  void displayEntry(struct singleEntry * e Line 2277  void displayEntry(struct singleEntry * e
2277      char * root = NULL;      char * root = NULL;
2278      int i;      int i;
2279    
     line = entry->lines;  
     while (line && line->type != LT_KERNEL) line = line->next;  
   
2280      printf("index=%d\n", index);      printf("index=%d\n", index);
2281    
2282      printf("kernel=%s\n", line->elements[1].item);      line = getLineByType(LT_KERNEL|LT_HYPER|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
2283        if (!line) {
2284            printf("non linux entry\n");
2285            return;
2286        }
2287    
2288        if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2289     printf("kernel=%s\n", line->elements[1].item);
2290        else
2291     printf("kernel=%s%s\n", prefix, line->elements[1].item);
2292    
2293      if (line->numElements >= 3) {      if (line->numElements >= 3) {
2294   printf("args=\"");   printf("args=\"");
# Line 1308  void displayEntry(struct singleEntry * e Line 2305  void displayEntry(struct singleEntry * e
2305   }   }
2306   printf("\"\n");   printf("\"\n");
2307      } else {      } else {
2308   line = entry->lines;   line = getLineByType(LT_KERNELARGS, entry->lines);
  while (line && line->type != LT_KERNELARGS) line=line->next;  
   
2309   if (line) {   if (line) {
2310      char * s;      char * s;
2311    
# Line 1334  void displayEntry(struct singleEntry * e Line 2329  void displayEntry(struct singleEntry * e
2329      }      }
2330    
2331      if (!root) {      if (!root) {
2332   line = entry->lines;   line = getLineByType(LT_ROOT, entry->lines);
  while (line && line->type != LT_ROOT) line = line->next;  
   
2333   if (line && line->numElements >= 2)   if (line && line->numElements >= 2)
2334      root=line->elements[1].item;      root=line->elements[1].item;
2335      }      }
# Line 1351  void displayEntry(struct singleEntry * e Line 2344  void displayEntry(struct singleEntry * e
2344   printf("root=%s\n", s);   printf("root=%s\n", s);
2345      }      }
2346    
2347      line = entry->lines;      line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);
     while (line && line->type != LT_INITRD) line = line->next;  
2348    
2349      if (line && line->numElements >= 2) {      if (line && line->numElements >= 2) {
2350   printf("initrd=%s", prefix);   if (!strncmp(prefix, line->elements[1].item, strlen(prefix)))
2351        printf("initrd=");
2352     else
2353        printf("initrd=%s", prefix);
2354    
2355   for (i = 1; i < line->numElements; i++)   for (i = 1; i < line->numElements; i++)
2356      printf("%s%s", line->elements[i].item, line->elements[i].indent);      printf("%s%s", line->elements[i].item, line->elements[i].indent);
2357   printf("\n");   printf("\n");
2358      }      }
2359    
2360        line = getLineByType(LT_TITLE, entry->lines);
2361        if (line) {
2362     printf("title=%s\n", line->elements[1].item);
2363        } else {
2364     char * title;
2365     line = getLineByType(LT_MENUENTRY, entry->lines);
2366     title = grub2ExtractTitle(line);
2367     if (title)
2368        printf("title=%s\n", title);
2369        }
2370    }
2371    
2372    int isSuseSystem(void) {
2373        const char * path;
2374        const static char default_path[] = "/etc/SuSE-release";
2375    
2376        if ((path = getenv("GRUBBY_SUSE_RELEASE")) == NULL)
2377     path = default_path;
2378    
2379        if (!access(path, R_OK))
2380     return 1;
2381        return 0;
2382    }
2383    
2384    int isSuseGrubConf(const char * path) {
2385        FILE * grubConf;
2386        char * line = NULL;
2387        size_t len = 0, res = 0;
2388    
2389        grubConf = fopen(path, "r");
2390        if (!grubConf) {
2391            dbgPrintf("Could not open SuSE configuration file '%s'\n", path);
2392     return 0;
2393        }
2394    
2395        while ((res = getline(&line, &len, grubConf)) != -1) {
2396     if (!strncmp(line, "setup", 5)) {
2397        fclose(grubConf);
2398        free(line);
2399        return 1;
2400     }
2401        }
2402    
2403        dbgPrintf("SuSE configuration file '%s' does not appear to be valid\n",
2404          path);
2405    
2406        fclose(grubConf);
2407        free(line);
2408        return 0;
2409    }
2410    
2411    int suseGrubConfGetLba(const char * path, int * lbaPtr) {
2412        FILE * grubConf;
2413        char * line = NULL;
2414        size_t res = 0, len = 0;
2415    
2416        if (!path) return 1;
2417        if (!lbaPtr) return 1;
2418    
2419        grubConf = fopen(path, "r");
2420        if (!grubConf) return 1;
2421    
2422        while ((res = getline(&line, &len, grubConf)) != -1) {
2423     if (line[res - 1] == '\n')
2424        line[res - 1] = '\0';
2425     else if (len > res)
2426        line[res] = '\0';
2427     else {
2428        line = realloc(line, res + 1);
2429        line[res] = '\0';
2430     }
2431    
2432     if (!strncmp(line, "setup", 5)) {
2433        if (strstr(line, "--force-lba")) {
2434            *lbaPtr = 1;
2435        } else {
2436            *lbaPtr = 0;
2437        }
2438        dbgPrintf("lba: %i\n", *lbaPtr);
2439        break;
2440     }
2441        }
2442    
2443        free(line);
2444        fclose(grubConf);
2445        return 0;
2446    }
2447    
2448    int suseGrubConfGetInstallDevice(const char * path, char ** devicePtr) {
2449        FILE * grubConf;
2450        char * line = NULL;
2451        size_t res = 0, len = 0;
2452        char * lastParamPtr = NULL;
2453        char * secLastParamPtr = NULL;
2454        char installDeviceNumber = '\0';
2455        char * bounds = NULL;
2456    
2457        if (!path) return 1;
2458        if (!devicePtr) return 1;
2459    
2460        grubConf = fopen(path, "r");
2461        if (!grubConf) return 1;
2462    
2463        while ((res = getline(&line, &len, grubConf)) != -1) {
2464     if (strncmp(line, "setup", 5))
2465        continue;
2466    
2467     if (line[res - 1] == '\n')
2468        line[res - 1] = '\0';
2469     else if (len > res)
2470        line[res] = '\0';
2471     else {
2472        line = realloc(line, res + 1);
2473        line[res] = '\0';
2474     }
2475    
2476     lastParamPtr = bounds = line + res;
2477    
2478     /* Last parameter in grub may be an optional IMAGE_DEVICE */
2479     while (!isspace(*lastParamPtr))
2480        lastParamPtr--;
2481     lastParamPtr++;
2482    
2483     secLastParamPtr = lastParamPtr - 2;
2484     dbgPrintf("lastParamPtr: %s\n", lastParamPtr);
2485    
2486     if (lastParamPtr + 3 > bounds) {
2487        dbgPrintf("lastParamPtr going over boundary");
2488        fclose(grubConf);
2489        free(line);
2490        return 1;
2491     }
2492     if (!strncmp(lastParamPtr, "(hd", 3))
2493        lastParamPtr += 3;
2494     dbgPrintf("lastParamPtr: %c\n", *lastParamPtr);
2495    
2496     /*
2497     * Second last parameter will decide wether last parameter is
2498     * an IMAGE_DEVICE or INSTALL_DEVICE
2499     */
2500     while (!isspace(*secLastParamPtr))
2501        secLastParamPtr--;
2502     secLastParamPtr++;
2503    
2504     if (secLastParamPtr + 3 > bounds) {
2505        dbgPrintf("secLastParamPtr going over boundary");
2506        fclose(grubConf);
2507        free(line);
2508        return 1;
2509     }
2510     dbgPrintf("secLastParamPtr: %s\n", secLastParamPtr);
2511     if (!strncmp(secLastParamPtr, "(hd", 3)) {
2512        secLastParamPtr += 3;
2513        dbgPrintf("secLastParamPtr: %c\n", *secLastParamPtr);
2514        installDeviceNumber = *secLastParamPtr;
2515     } else {
2516        installDeviceNumber = *lastParamPtr;
2517     }
2518    
2519     *devicePtr = malloc(6);
2520     snprintf(*devicePtr, 6, "(hd%c)", installDeviceNumber);
2521     dbgPrintf("installDeviceNumber: %c\n", installDeviceNumber);
2522     fclose(grubConf);
2523     free(line);
2524     return 0;
2525        }
2526    
2527        free(line);
2528        fclose(grubConf);
2529        return 1;
2530    }
2531    
2532    int grubGetBootFromDeviceMap(const char * device,
2533         char ** bootPtr) {
2534        FILE * deviceMap;
2535        char * line = NULL;
2536        size_t res = 0, len = 0;
2537        char * devicePtr;
2538        char * bounds = NULL;
2539        const char * path;
2540        const static char default_path[] = "/boot/grub/device.map";
2541    
2542        if (!device) return 1;
2543        if (!bootPtr) return 1;
2544    
2545        if ((path = getenv("GRUBBY_GRUB_DEVICE_MAP")) == NULL)
2546     path = default_path;
2547    
2548        dbgPrintf("opening grub device.map file from: %s\n", path);
2549        deviceMap = fopen(path, "r");
2550        if (!deviceMap)
2551     return 1;
2552    
2553        while ((res = getline(&line, &len, deviceMap)) != -1) {
2554            if (!strncmp(line, "#", 1))
2555        continue;
2556    
2557     if (line[res - 1] == '\n')
2558        line[res - 1] = '\0';
2559     else if (len > res)
2560        line[res] = '\0';
2561     else {
2562        line = realloc(line, res + 1);
2563        line[res] = '\0';
2564     }
2565    
2566     devicePtr = line;
2567     bounds = line + res;
2568    
2569     while ((isspace(*line) && ((devicePtr + 1) <= bounds)))
2570        devicePtr++;
2571     dbgPrintf("device: %s\n", devicePtr);
2572    
2573     if (!strncmp(devicePtr, device, strlen(device))) {
2574        devicePtr += strlen(device);
2575        while (isspace(*devicePtr) && ((devicePtr + 1) <= bounds))
2576            devicePtr++;
2577    
2578        *bootPtr = strdup(devicePtr);
2579        break;
2580     }
2581        }
2582    
2583        free(line);
2584        fclose(deviceMap);
2585        return 0;
2586    }
2587    
2588    int suseGrubConfGetBoot(const char * path, char ** bootPtr) {
2589        char * grubDevice;
2590    
2591        if (suseGrubConfGetInstallDevice(path, &grubDevice))
2592     dbgPrintf("error looking for grub installation device\n");
2593        else
2594     dbgPrintf("grubby installation device: %s\n", grubDevice);
2595    
2596        if (grubGetBootFromDeviceMap(grubDevice, bootPtr))
2597     dbgPrintf("error looking for grub boot device\n");
2598        else
2599     dbgPrintf("grubby boot device: %s\n", *bootPtr);
2600    
2601        free(grubDevice);
2602        return 0;
2603    }
2604    
2605    int parseSuseGrubConf(int * lbaPtr, char ** bootPtr) {
2606        /*
2607         * This SuSE grub configuration file at this location is not your average
2608         * grub configuration file, but instead the grub commands used to setup
2609         * grub on that system.
2610         */
2611        const char * path;
2612        const static char default_path[] = "/etc/grub.conf";
2613    
2614        if ((path = getenv("GRUBBY_SUSE_GRUB_CONF")) == NULL)
2615     path = default_path;
2616    
2617        if (!isSuseGrubConf(path)) return 1;
2618    
2619        if (lbaPtr) {
2620            *lbaPtr = 0;
2621            if (suseGrubConfGetLba(path, lbaPtr))
2622                return 1;
2623        }
2624    
2625        if (bootPtr) {
2626            *bootPtr = NULL;
2627            suseGrubConfGetBoot(path, bootPtr);
2628        }
2629    
2630        return 0;
2631  }  }
2632    
2633  int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {  int parseSysconfigGrub(int * lbaPtr, char ** bootPtr) {
# Line 1369  int parseSysconfigGrub(int * lbaPtr, cha Line 2637  int parseSysconfigGrub(int * lbaPtr, cha
2637      char * start;      char * start;
2638      char * param;      char * param;
2639    
2640      in = fopen("/etc/sysconfig/grub", "r");      in = fopen("/etc/conf.d/grub", "r");
2641      if (!in) return 1;      if (!in) return 1;
2642    
2643      if (lbaPtr) *lbaPtr = 0;      if (lbaPtr) *lbaPtr = 0;
# Line 1410  int parseSysconfigGrub(int * lbaPtr, cha Line 2678  int parseSysconfigGrub(int * lbaPtr, cha
2678  }  }
2679    
2680  void dumpSysconfigGrub(void) {  void dumpSysconfigGrub(void) {
2681      char * boot;      char * boot = NULL;
2682      int lba;      int lba;
2683    
2684      if (!parseSysconfigGrub(&lba, &boot)) {      if (isSuseSystem()) {
2685   if (lba) printf("lba\n");          if (parseSuseGrubConf(&lba, &boot)) {
2686   if (boot) printf("boot=%s\n", boot);      free(boot);
2687        return;
2688     }
2689        } else {
2690            if (parseSysconfigGrub(&lba, &boot)) {
2691        free(boot);
2692        return;
2693     }
2694        }
2695    
2696        if (lba) printf("lba\n");
2697        if (boot) {
2698     printf("boot=%s\n", boot);
2699     free(boot);
2700      }      }
2701  }  }
2702    
# Line 1431  int displayInfo(struct grubConfig * conf Line 2712  int displayInfo(struct grubConfig * conf
2712   return 1;   return 1;
2713      }      }
2714    
2715      /* 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
2716         be a better way */         be a better way */
2717      if (config->cfi == &grubConfigType) {      if (config->cfi == &grubConfigType) {
2718   dumpSysconfigGrub();   dumpSysconfigGrub();
2719      } else {      } else {
2720   line = config->theLines;   line = getLineByType(LT_BOOT, config->theLines);
  while (line && line->type != LT_BOOT) line = line->next;  
2721   if (line && line->numElements >= 1) {   if (line && line->numElements >= 1) {
2722      printf("boot=%s\n", line->elements[1].item);      printf("boot=%s\n", line->elements[1].item);
2723   }   }
2724    
2725   line = config->theLines;   line = getLineByType(LT_LBA, config->theLines);
  while (line && line->type != LT_LBA) line = line->next;  
2726   if (line) printf("lba\n");   if (line) printf("lba\n");
2727      }      }
2728    
# Line 1458  int displayInfo(struct grubConfig * conf Line 2737  int displayInfo(struct grubConfig * conf
2737      return 0;      return 0;
2738  }  }
2739    
2740    struct singleLine * addLineTmpl(struct singleEntry * entry,
2741     struct singleLine * tmplLine,
2742     struct singleLine * prevLine,
2743     const char * val,
2744     struct configFileInfo * cfi)
2745    {
2746        struct singleLine * newLine = lineDup(tmplLine);
2747    
2748        if (isEfi && cfi == &grub2ConfigType) {
2749     enum lineType_e old = newLine->type;
2750     newLine->type = preferredLineType(newLine->type, cfi);
2751     if (old != newLine->type)
2752        newLine->elements[0].item = getKeyByType(newLine->type, cfi);
2753        }
2754    
2755        if (val) {
2756     /* override the inherited value with our own.
2757     * This is a little weak because it only applies to elements[1]
2758     */
2759     if (newLine->numElements > 1)
2760        removeElement(newLine, 1);
2761     insertElement(newLine, val, 1, cfi);
2762    
2763     /* but try to keep the rootspec from the template... sigh */
2764     if (tmplLine->type & (LT_HYPER|LT_KERNEL|LT_MBMODULE|LT_INITRD|LT_KERNEL_EFI|LT_INITRD_EFI|LT_KERNEL_16|LT_INITRD_16)) {
2765        char * rootspec = getRootSpecifier(tmplLine->elements[1].item);
2766        if (rootspec != NULL) {
2767     free(newLine->elements[1].item);
2768     newLine->elements[1].item =
2769        sdupprintf("%s%s", rootspec, val);
2770        }
2771     }
2772        }
2773    
2774        dbgPrintf("addLineTmpl(%s)\n", newLine->numElements ?
2775          newLine->elements[0].item : "");
2776    
2777        if (!entry->lines) {
2778     /* first one on the list */
2779     entry->lines = newLine;
2780        } else if (prevLine) {
2781     /* add after prevLine */
2782     newLine->next = prevLine->next;
2783     prevLine->next = newLine;
2784        }
2785    
2786        return newLine;
2787    }
2788    
2789  /* val may be NULL */  /* val may be NULL */
2790  struct singleLine *  addLine(struct singleEntry * entry,  struct singleLine *  addLine(struct singleEntry * entry,
2791       struct configFileInfo * cfi,       struct configFileInfo * cfi,
2792       enum lineType_e type, const char * defaultIndent,       enum lineType_e type, char * defaultIndent,
2793       char * val) {       const char * val) {
2794      struct singleLine * line, * prev;      struct singleLine * line, * prev;
2795      int i;      struct keywordTypes * kw;
2796        struct singleLine tmpl;
2797    
2798      for (i = 0; cfi->keywords[i].key; i++)      /* NB: This function shouldn't allocate items on the heap, rather on the
2799   if (cfi->keywords[i].type == type) break;       * stack since it calls addLineTmpl which will make copies.
2800      if (type != LT_TITLE || !cfi->titleBracketed)       */
2801          if (!cfi->keywords[i].key) abort();      if (type == LT_TITLE && cfi->titleBracketed) {
2802     /* we're doing a bracketed title (zipl) */
2803     tmpl.type = type;
2804     tmpl.numElements = 1;
2805     tmpl.elements = alloca(sizeof(*tmpl.elements));
2806     tmpl.elements[0].item = alloca(strlen(val)+3);
2807     sprintf(tmpl.elements[0].item, "[%s]", val);
2808     tmpl.elements[0].indent = "";
2809     val = NULL;
2810        } else if (type == LT_MENUENTRY) {
2811     char *lineend = "--class gnu-linux --class gnu --class os {";
2812     if (!val) {
2813        fprintf(stderr, "Line type LT_MENUENTRY requires a value\n");
2814        abort();
2815     }
2816     kw = getKeywordByType(type, cfi);
2817     if (!kw) {
2818        fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2819        abort();
2820     }
2821     tmpl.indent = "";
2822     tmpl.type = type;
2823     tmpl.numElements = 3;
2824     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2825     tmpl.elements[0].item = kw->key;
2826     tmpl.elements[0].indent = alloca(2);
2827     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2828     tmpl.elements[1].item = (char *)val;
2829     tmpl.elements[1].indent = alloca(2);
2830     sprintf(tmpl.elements[1].indent, "%c", kw->nextChar);
2831     tmpl.elements[2].item = alloca(strlen(lineend)+1);
2832     strcpy(tmpl.elements[2].item, lineend);
2833     tmpl.elements[2].indent = "";
2834        } else {
2835     kw = getKeywordByType(type, cfi);
2836     if (!kw) {
2837        fprintf(stderr, "Looking up keyword for unknown type %d\n", type);
2838        abort();
2839     }
2840     tmpl.type = type;
2841     tmpl.numElements = val ? 2 : 1;
2842     tmpl.elements = alloca(sizeof(*tmpl.elements) * tmpl.numElements);
2843     tmpl.elements[0].item = kw->key;
2844     tmpl.elements[0].indent = alloca(2);
2845     sprintf(tmpl.elements[0].indent, "%c", kw->nextChar);
2846     if (val) {
2847        tmpl.elements[1].item = (char *)val;
2848        tmpl.elements[1].indent = "";
2849     }
2850        }
2851    
2852      /* 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
2853         to insert after. Note that comments are considered empty lines, which         to insert after. Note that comments are considered empty lines, which
2854         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
2855         first line, we use defaultIndent (the first line is normally indented         first line, we use defaultIndent (the first line is normally indented
2856         differently from the rest) */         differently from the rest) */
2857      if (entry->lines) {      for (line = entry->lines, prev = NULL; line; line = line->next) {
2858   line = entry->lines;   if (line->numElements) prev = line;
2859   prev = NULL;   /* fall back on the last line if prev isn't otherwise set */
2860   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;  
2861      }      }
2862    
2863      if (type != LT_TITLE || !cfi->titleBracketed) {      struct singleLine *menuEntry;
2864          line->type = type;      menuEntry = getLineByType(LT_MENUENTRY, entry->lines);
2865          line->numElements = val ? 2 : 1;      if (tmpl.type == LT_ENTRY_END) {
2866          line->elements = malloc(sizeof(*line->elements) * line->numElements);   if (menuEntry)
2867          line->elements[0].item = strdup(cfi->keywords[i].key);      tmpl.indent = menuEntry->indent;
2868          line->elements[0].indent = malloc(2);   else
2869          line->elements[0].indent[0] = cfi->keywords[i].nextChar;      tmpl.indent = defaultIndent ?: "";
2870          line->elements[0].indent[1] = '\0';      } else if (tmpl.type != LT_MENUENTRY) {
2871             if (menuEntry)
2872          if (val) {      tmpl.indent = "\t";
2873              line->elements[1].item = val;   else if (prev == entry->lines)
2874              line->elements[1].indent = strdup("");      tmpl.indent = defaultIndent ?: "";
2875          }   else
2876      } 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("");  
2877      }      }
2878    
2879      return line;      return addLineTmpl(entry, &tmpl, prev, val, cfi);
2880  }  }
2881    
2882  void removeLine(struct singleEntry * entry, struct singleLine * line) {  void removeLine(struct singleEntry * entry, struct singleLine * line) {
# Line 1553  void removeLine(struct singleEntry * ent Line 2901  void removeLine(struct singleEntry * ent
2901      free(line);      free(line);
2902  }  }
2903    
2904    static void requote(struct singleLine *tmplLine, struct configFileInfo * cfi)
2905    {
2906        struct singleLine newLine = {
2907     .indent = tmplLine->indent,
2908     .type = tmplLine->type,
2909     .next = tmplLine->next,
2910        };
2911        int firstQuotedItem = -1;
2912        int quoteLen = 0;
2913        int j;
2914        int element = 0;
2915        char *c;
2916    
2917        c = malloc(strlen(tmplLine->elements[0].item) + 1);
2918        strcpy(c, tmplLine->elements[0].item);
2919        insertElement(&newLine, c, element++, cfi);
2920        free(c);
2921        c = NULL;
2922    
2923        for (j = 1; j < tmplLine->numElements; j++) {
2924     if (firstQuotedItem == -1) {
2925        quoteLen += strlen(tmplLine->elements[j].item);
2926        
2927        if (isquote(tmplLine->elements[j].item[0])) {
2928     firstQuotedItem = j;
2929            quoteLen += strlen(tmplLine->elements[j].indent);
2930        } else {
2931     c = malloc(quoteLen + 1);
2932     strcpy(c, tmplLine->elements[j].item);
2933     insertElement(&newLine, c, element++, cfi);
2934     free(c);
2935     quoteLen = 0;
2936        }
2937     } else {
2938        int itemlen = strlen(tmplLine->elements[j].item);
2939        quoteLen += itemlen;
2940        quoteLen += strlen(tmplLine->elements[j].indent);
2941        
2942        if (isquote(tmplLine->elements[j].item[itemlen - 1])) {
2943     c = malloc(quoteLen + 1);
2944     c[0] = '\0';
2945     for (int i = firstQuotedItem; i < j+1; i++) {
2946        strcat(c, tmplLine->elements[i].item);
2947        strcat(c, tmplLine->elements[i].indent);
2948     }
2949     insertElement(&newLine, c, element++, cfi);
2950     free(c);
2951    
2952     firstQuotedItem = -1;
2953     quoteLen = 0;
2954        }
2955     }
2956        }
2957        while (tmplLine->numElements)
2958     removeElement(tmplLine, 0);
2959        if (tmplLine->elements)
2960     free(tmplLine->elements);
2961    
2962        tmplLine->numElements = newLine.numElements;
2963        tmplLine->elements = newLine.elements;
2964    }
2965    
2966    static void insertElement(struct singleLine * line,
2967      const char * item, int insertHere,
2968      struct configFileInfo * cfi)
2969    {
2970        struct keywordTypes * kw;
2971        char indent[2] = "";
2972    
2973        /* sanity check */
2974        if (insertHere > line->numElements) {
2975     dbgPrintf("insertElement() adjusting insertHere from %d to %d\n",
2976      insertHere, line->numElements);
2977     insertHere = line->numElements;
2978        }
2979    
2980        line->elements = realloc(line->elements, (line->numElements + 1) *
2981         sizeof(*line->elements));
2982        memmove(&line->elements[insertHere+1],
2983        &line->elements[insertHere],
2984        (line->numElements - insertHere) *
2985        sizeof(*line->elements));
2986        line->elements[insertHere].item = strdup(item);
2987    
2988        kw = getKeywordByType(line->type, cfi);
2989    
2990        if (line->numElements == 0) {
2991     indent[0] = '\0';
2992        } else if (insertHere == 0) {
2993     indent[0] = kw->nextChar;
2994        } else if (kw->separatorChar != '\0') {
2995     indent[0] = kw->separatorChar;
2996        } else {
2997     indent[0] = ' ';
2998        }
2999    
3000        if (insertHere > 0 && line->elements[insertHere-1].indent[0] == '\0') {
3001     /* move the end-of-line forward */
3002     line->elements[insertHere].indent =
3003        line->elements[insertHere-1].indent;
3004     line->elements[insertHere-1].indent = strdup(indent);
3005        } else {
3006     line->elements[insertHere].indent = strdup(indent);
3007        }
3008    
3009        line->numElements++;
3010    
3011        dbgPrintf("insertElement(%s, '%s%s', %d)\n",
3012          line->elements[0].item,
3013          line->elements[insertHere].item,
3014          line->elements[insertHere].indent,
3015          insertHere);
3016    }
3017    
3018    static void removeElement(struct singleLine * line, int removeHere) {
3019        int i;
3020    
3021        /* sanity check */
3022        if (removeHere >= line->numElements) return;
3023    
3024        dbgPrintf("removeElement(%s, %d:%s)\n", line->elements[0].item,
3025          removeHere, line->elements[removeHere].item);
3026    
3027        free(line->elements[removeHere].item);
3028    
3029        if (removeHere > 1) {
3030     /* previous argument gets this argument's post-indentation */
3031     free(line->elements[removeHere-1].indent);
3032     line->elements[removeHere-1].indent =
3033        line->elements[removeHere].indent;
3034        } else {
3035     free(line->elements[removeHere].indent);
3036        }
3037    
3038        /* now collapse the array, but don't bother to realloc smaller */
3039        for (i = removeHere; i < line->numElements - 1; i++)
3040     line->elements[i] = line->elements[i + 1];
3041    
3042        line->numElements--;
3043    }
3044    
3045  int argMatch(const char * one, const char * two) {  int argMatch(const char * one, const char * two) {
3046      char * first, * second;      char * first, * second;
3047      char * chptr;      char * chptr;
# Line 1575  int updateActualImage(struct grubConfig Line 3064  int updateActualImage(struct grubConfig
3064      struct singleEntry * entry;      struct singleEntry * entry;
3065      struct singleLine * line, * rootLine;      struct singleLine * line, * rootLine;
3066      int index = 0;      int index = 0;
3067      int i, j, k;      int i, k;
3068      const char ** newArgs, ** oldArgs;      const char ** newArgs, ** oldArgs;
3069      const char ** arg;      const char ** arg;
3070      const char * chptr;      int useKernelArgs, useRoot;
     int useKernelArgs = 0;  
     int useRoot = 0;  
3071      int firstElement;      int firstElement;
3072      int *usedElements, *usedArgs;      int *usedElements;
3073        int doreplace;
3074    
3075      if (!image) return 0;      if (!image) return 0;
3076    
# Line 1609  int updateActualImage(struct grubConfig Line 3097  int updateActualImage(struct grubConfig
3097   }   }
3098      }      }
3099    
     for (i = 0; cfg->cfi->keywords[i].key; i++)  
  if (cfg->cfi->keywords[i].type == LT_KERNELARGS) break;  
3100    
3101      if (cfg->cfi->keywords[i].key)      useKernelArgs = (getKeywordByType(LT_KERNELARGS, cfg->cfi)
3102   useKernelArgs = 1;       && (!multibootArgs || cfg->cfi->mbConcatArgs));
3103    
3104      for (i = 0; cfg->cfi->keywords[i].key; i++)      useRoot = (getKeywordByType(LT_ROOT, cfg->cfi)
3105   if (cfg->cfi->keywords[i].type == LT_ROOT) break;         && !multibootArgs);
3106    
3107      if (cfg->cfi->keywords[i].key)      for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
  useRoot = 1;  
3108    
3109      k = 0;   if (multibootArgs && !entry->multiboot)
3110      for (arg = newArgs; *arg; arg++)      continue;
3111          k++;  
3112      usedArgs = calloc(k, sizeof(int));   /* Determine where to put the args.  If this config supports
3113     * LT_KERNELARGS, use that.  Otherwise use
3114     * LT_HYPER/LT_KERNEL/LT_MBMODULE lines.
3115     */
3116     if (useKernelArgs) {
3117        line = getLineByType(LT_KERNELARGS, entry->lines);
3118        if (!line) {
3119     /* no LT_KERNELARGS, need to add it */
3120     line = addLine(entry, cfg->cfi, LT_KERNELARGS,
3121           cfg->secondaryIndent, NULL);
3122        }
3123        firstElement = 1;
3124    
3125      while ((entry = findEntryByPath(cfg, image, prefix, &index))) {   } else if (multibootArgs) {
3126   index++;      line = getLineByType(LT_HYPER, entry->lines);
3127        if (!line) {
3128     /* a multiboot entry without LT_HYPER? */
3129     continue;
3130        }
3131        firstElement = 2;
3132    
3133   line = entry->lines;   } else {
3134   while (line && line->type != LT_KERNEL) line = line->next;      line = getLineByType(LT_KERNEL|LT_MBMODULE|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
3135   if (!line) continue;      if (!line) {
3136   firstElement = 2;   /* no LT_KERNEL or LT_MBMODULE in this entry? */
3137     continue;
3138          if (entry->multiboot && !multibootArgs) {      }
3139              /* 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;  
3140   }   }
3141    
3142   if (!line && useKernelArgs) {   /* handle the elilo case which does:
3143      /* no append in there, need to add it */   *   append="hypervisor args -- kernel args"
3144      line = addLine(entry, cfg->cfi, LT_KERNELARGS, NULL, NULL);   */
3145     if (entry->multiboot && cfg->cfi->mbConcatArgs) {
3146        /* this is a multiboot entry, make sure there's
3147         * -- on the args line
3148         */
3149        for (i = firstElement; i < line->numElements; i++) {
3150     if (!strcmp(line->elements[i].item, "--"))
3151        break;
3152        }
3153        if (i == line->numElements) {
3154     /* assume all existing args are kernel args,
3155     * prepend -- to make it official
3156     */
3157     insertElement(line, "--", firstElement, cfg->cfi);
3158     i = firstElement;
3159        }
3160        if (!multibootArgs) {
3161     /* kernel args start after the -- */
3162     firstElement = i + 1;
3163        }
3164     } else if (cfg->cfi->mbConcatArgs) {
3165        /* this is a non-multiboot entry, remove hyper args */
3166        for (i = firstElement; i < line->numElements; i++) {
3167     if (!strcmp(line->elements[i].item, "--"))
3168        break;
3169        }
3170        if (i < line->numElements) {
3171     /* remove args up to -- */
3172     while (strcmp(line->elements[firstElement].item, "--"))
3173        removeElement(line, firstElement);
3174     /* remove -- */
3175     removeElement(line, firstElement);
3176        }
3177   }   }
3178    
3179          usedElements = calloc(line->numElements, sizeof(int));          usedElements = calloc(line->numElements, sizeof(*usedElements));
3180    
3181          k = 0;   for (k = 0, arg = newArgs; *arg; arg++, k++) {
3182   for (arg = newArgs; *arg; arg++) {  
3183              if (usedArgs[k]) {      doreplace = 1;
                 k++;  
                 continue;  
             }  
3184      for (i = firstElement; i < line->numElements; i++) {      for (i = firstElement; i < line->numElements; i++) {
3185     if (multibootArgs && cfg->cfi->mbConcatArgs &&
3186        !strcmp(line->elements[i].item, "--"))
3187     {
3188        /* reached the end of hyper args, insert here */
3189        doreplace = 0;
3190        break;  
3191     }
3192                  if (usedElements[i])                  if (usedElements[i])
3193                      continue;                      continue;
3194   if (!argMatch(line->elements[i].item, *arg)) {   if (!argMatch(line->elements[i].item, *arg)) {
3195                      usedElements[i]=1;                      usedElements[i]=1;
                     usedArgs[k]=1;  
3196      break;      break;
3197                  }                  }
3198              }              }
     chptr = strchr(*arg, '=');  
3199    
3200      if (i < line->numElements) {      if (i < line->numElements && doreplace) {
3201   /* replace */   /* direct replacement */
3202   free(line->elements[i].item);   free(line->elements[i].item);
3203   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("");  
  }  
3204    
3205   free(rootLine->elements[1].item);      } else if (useRoot && !strncmp(*arg, "root=/dev/", 10)) {
3206   rootLine->elements[1].item = strdup(chptr + 1);   /* root= replacement */
3207      } else {   rootLine = getLineByType(LT_ROOT, entry->lines);
3208   /* append */   if (rootLine) {
3209   line->elements = realloc(line->elements,      free(rootLine->elements[1].item);
3210   (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(" ");  
3211   } else {   } else {
3212      /* First thing on this line; treat a bit differently. Note      rootLine = addLine(entry, cfg->cfi, LT_ROOT,
3213         this is only possible if we've added a LT_KERNELARGS         cfg->secondaryIndent, *arg + 5);
        entry */  
     line->elements[line->numElements].indent = strdup("");  
3214   }   }
3215        }
3216    
3217   line->numElements++;      else {
3218     /* insert/append */
3219     insertElement(line, *arg, i, cfg->cfi);
3220     usedElements = realloc(usedElements, line->numElements *
3221           sizeof(*usedElements));
3222     memmove(&usedElements[i + 1], &usedElements[i],
3223     line->numElements - i - 1);
3224     usedElements[i] = 1;
3225    
3226   /* if we updated a root= here even though there is a   /* if we updated a root= here even though there is a
3227     LT_ROOT available we need to remove the LT_ROOT entry     LT_ROOT available we need to remove the LT_ROOT entry
3228     (this will happen if we switch from a device to a label) */     (this will happen if we switch from a device to a label) */
3229   if (useRoot && !strncmp(*arg, "root=", 5)) {   if (useRoot && !strncmp(*arg, "root=", 5)) {
3230      rootLine = entry->lines;      rootLine = getLineByType(LT_ROOT, entry->lines);
3231      while (rootLine && rootLine->type != LT_ROOT)      if (rootLine)
  rootLine = rootLine->next;  
     if (rootLine) {  
3232   removeLine(entry, rootLine);   removeLine(entry, rootLine);
     }  
3233   }   }
3234      }      }
             k++;  
3235   }   }
3236    
3237          free(usedElements);          free(usedElements);
3238    
  /* 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? */  
3239   for (arg = oldArgs; *arg; arg++) {   for (arg = oldArgs; *arg; arg++) {
3240      for (i = firstElement; i < line->numElements; i++)      for (i = firstElement; i < line->numElements; i++) {
3241   if (!argMatch(line->elements[i].item, *arg))   if (multibootArgs && cfg->cfi->mbConcatArgs &&
3242        !strcmp(line->elements[i].item, "--"))
3243        /* reached the end of hyper args, stop here */
3244        break;
3245     if (!argMatch(line->elements[i].item, *arg)) {
3246        removeElement(line, i);
3247      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;  
3248   }   }
3249        }
3250   free(line->elements[i].item);      /* handle removing LT_ROOT line too */
3251        if (useRoot && !strncmp(*arg, "root=", 5)) {
3252   for (j = i + 1; j < line->numElements; j++)   rootLine = getLineByType(LT_ROOT, entry->lines);
3253      line->elements[j - 1] = line->elements[j];   if (rootLine)
3254        removeLine(entry, rootLine);
  line->numElements--;  
3255      }      }
3256   }   }
3257    
# Line 1760  int updateActualImage(struct grubConfig Line 3262  int updateActualImage(struct grubConfig
3262   }   }
3263      }      }
3264    
     free(usedArgs);  
3265      free(newArgs);      free(newArgs);
3266      free(oldArgs);      free(oldArgs);
3267    
# Line 1786  int updateImage(struct grubConfig * cfg, Line 3287  int updateImage(struct grubConfig * cfg,
3287      return rc;      return rc;
3288  }  }
3289    
3290    int addMBInitrd(struct grubConfig * cfg, const char *newMBKernel,
3291     const char * image, const char * prefix, const char * initrd) {
3292        struct singleEntry * entry;
3293        struct singleLine * line, * kernelLine, *endLine = NULL;
3294        int index = 0;
3295    
3296        if (!image) return 0;
3297    
3298        for (; (entry = findEntryByPath(cfg, newMBKernel, prefix, &index)); index++) {
3299            kernelLine = getLineByType(LT_MBMODULE, entry->lines);
3300            if (!kernelLine) continue;
3301    
3302            if (prefix) {
3303                int prefixLen = strlen(prefix);
3304                if (!strncmp(initrd, prefix, prefixLen))
3305                    initrd += prefixLen;
3306            }
3307     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3308     if (endLine)
3309        removeLine(entry, endLine);
3310            line = addLine(entry, cfg->cfi, preferredLineType(LT_MBMODULE,cfg->cfi),
3311     kernelLine->indent, initrd);
3312            if (!line)
3313        return 1;
3314     if (endLine) {
3315        line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3316                if (!line)
3317     return 1;
3318     }
3319    
3320            break;
3321        }
3322    
3323        return 0;
3324    }
3325    
3326    int updateInitrd(struct grubConfig * cfg, const char * image,
3327                     const char * prefix, const char * initrd) {
3328        struct singleEntry * entry;
3329        struct singleLine * line, * kernelLine, *endLine = NULL;
3330        int index = 0;
3331    
3332        if (!image) return 0;
3333    
3334        for (; (entry = findEntryByPath(cfg, image, prefix, &index)); index++) {
3335            kernelLine = getLineByType(LT_KERNEL|LT_KERNEL_EFI|LT_KERNEL_16, entry->lines);
3336            if (!kernelLine) continue;
3337    
3338            line = getLineByType(LT_INITRD|LT_INITRD_EFI|LT_INITRD_16, entry->lines);
3339            if (line)
3340                removeLine(entry, line);
3341            if (prefix) {
3342                int prefixLen = strlen(prefix);
3343                if (!strncmp(initrd, prefix, prefixLen))
3344                    initrd += prefixLen;
3345            }
3346     endLine = getLineByType(LT_ENTRY_END, entry->lines);
3347     if (endLine)
3348        removeLine(entry, endLine);
3349     enum lineType_e lt;
3350     switch(kernelLine->type) {
3351        case LT_KERNEL:
3352            lt = LT_INITRD;
3353     break;
3354        case LT_KERNEL_EFI:
3355            lt = LT_INITRD_EFI;
3356     break;
3357        case LT_KERNEL_16:
3358            lt = LT_INITRD_16;
3359     break;
3360        default:
3361            lt = preferredLineType(LT_INITRD, cfg->cfi);
3362     }
3363            line = addLine(entry, cfg->cfi, lt, kernelLine->indent, initrd);
3364            if (!line)
3365        return 1;
3366     if (endLine) {
3367        line = addLine(entry, cfg->cfi, LT_ENTRY_END, "", NULL);
3368                if (!line)
3369     return 1;
3370     }
3371    
3372            break;
3373        }
3374    
3375        return 0;
3376    }
3377    
3378  int checkDeviceBootloader(const char * device, const unsigned char * boot) {  int checkDeviceBootloader(const char * device, const unsigned char * boot) {
3379      int fd;      int fd;
3380      unsigned char bootSect[512];      unsigned char bootSect[512];
# Line 1809  int checkDeviceBootloader(const char * d Line 3398  int checkDeviceBootloader(const char * d
3398      if (memcmp(boot, bootSect, 3))      if (memcmp(boot, bootSect, 3))
3399   return 0;   return 0;
3400    
3401      if (boot[1] == 0xeb) {      if (boot[1] == JMP_SHORT_OPCODE) {
3402   offset = boot[2] + 2;   offset = boot[2] + 2;
3403      } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {      } else if (boot[1] == 0xe8 || boot[1] == 0xe9) {
3404   offset = (boot[3] << 8) + boot[2] + 2;   offset = (boot[3] << 8) + boot[2] + 2;
3405      } else if (boot[0] == 0xeb) {      } else if (boot[0] == JMP_SHORT_OPCODE) {
3406   offset = boot[1] + 2;        offset = boot[1] + 2;
3407            /*
3408     * it looks like grub, when copying stage1 into the mbr, patches stage1
3409     * right after the JMP location, replacing other instructions such as
3410     * JMPs for NOOPs. So, relax the check a little bit by skipping those
3411     * different bytes.
3412     */
3413          if ((bootSect[offset + 1] == NOOP_OPCODE)
3414      && (bootSect[offset + 2] == NOOP_OPCODE)) {
3415     offset = offset + 3;
3416          }
3417      } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {      } else if (boot[0] == 0xe8 || boot[0] == 0xe9) {
3418   offset = (boot[2] << 8) + boot[1] + 2;   offset = (boot[2] << 8) + boot[1] + 2;
3419      } else {      } else {
# Line 1956  int checkForLilo(struct grubConfig * con Line 3555  int checkForLilo(struct grubConfig * con
3555      return checkDeviceBootloader(line->elements[1].item, boot);      return checkDeviceBootloader(line->elements[1].item, boot);
3556  }  }
3557    
3558    int checkForGrub2(struct grubConfig * config) {
3559        if (!access("/etc/grub.d/", R_OK))
3560     return 2;
3561    
3562        return 1;
3563    }
3564    
3565  int checkForGrub(struct grubConfig * config) {  int checkForGrub(struct grubConfig * config) {
3566      int fd;      int fd;
3567      unsigned char bootSect[512];      unsigned char bootSect[512];
3568      char * boot;      char * boot;
3569        int onSuse = isSuseSystem();
3570    
3571      if (parseSysconfigGrub(NULL, &boot))  
3572   return 0;      if (onSuse) {
3573     if (parseSuseGrubConf(NULL, &boot))
3574        return 0;
3575        } else {
3576     if (parseSysconfigGrub(NULL, &boot))
3577        return 0;
3578        }
3579    
3580      /* assume grub is not installed -- not an error condition */      /* assume grub is not installed -- not an error condition */
3581      if (!boot)      if (!boot)
# Line 1976  int checkForGrub(struct grubConfig * con Line 3589  int checkForGrub(struct grubConfig * con
3589      if (read(fd, bootSect, 512) != 512) {      if (read(fd, bootSect, 512) != 512) {
3590   fprintf(stderr, _("grubby: unable to read %s: %s\n"),   fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3591   "/boot/grub/stage1", strerror(errno));   "/boot/grub/stage1", strerror(errno));
3592     close(fd);
3593     return 1;
3594        }
3595        close(fd);
3596    
3597        /* The more elaborate checks do not work on SuSE. The checks done
3598         * seem to be reasonble (at least for now), so just return success
3599         */
3600        if (onSuse)
3601     return 2;
3602    
3603        return checkDeviceBootloader(boot, bootSect);
3604    }
3605    
3606    int checkForExtLinux(struct grubConfig * config) {
3607        int fd;
3608        unsigned char bootSect[512];
3609        char * boot;
3610        char executable[] = "/boot/extlinux/extlinux";
3611    
3612        printf("entered: checkForExtLinux()\n");
3613    
3614        if (parseSysconfigGrub(NULL, &boot))
3615     return 0;
3616    
3617        /* assume grub is not installed -- not an error condition */
3618        if (!boot)
3619     return 0;
3620    
3621        fd = open(executable, O_RDONLY);
3622        if (fd < 0)
3623     /* this doesn't exist if grub hasn't been installed */
3624     return 0;
3625    
3626        if (read(fd, bootSect, 512) != 512) {
3627     fprintf(stderr, _("grubby: unable to read %s: %s\n"),
3628     executable, strerror(errno));
3629   return 1;   return 1;
3630      }      }
3631      close(fd);      close(fd);
# Line 1983  int checkForGrub(struct grubConfig * con Line 3633  int checkForGrub(struct grubConfig * con
3633      return checkDeviceBootloader(boot, bootSect);      return checkDeviceBootloader(boot, bootSect);
3634  }  }
3635    
3636    int checkForYaboot(struct grubConfig * config) {
3637        /*
3638         * This is a simplistic check that we consider good enough for own puporses
3639         *
3640         * If we were to properly check if yaboot is *installed* we'd need to:
3641         * 1) get the system boot device (LT_BOOT)
3642         * 2) considering it's a raw filesystem, check if the yaboot binary matches
3643         *    the content on the boot device
3644         * 3) if not, copy the binary to a temporary file and run "addnote" on it
3645         * 4) check again if binary and boot device contents match
3646         */
3647        if (!access("/etc/yaboot.conf", R_OK))
3648     return 2;
3649    
3650        return 1;
3651    }
3652    
3653    int checkForElilo(struct grubConfig * config) {
3654        if (!access("/etc/elilo.conf", R_OK))
3655     return 2;
3656    
3657        return 1;
3658    }
3659    
3660  static char * getRootSpecifier(char * str) {  static char * getRootSpecifier(char * str) {
3661      char * idx, * rootspec = NULL;      char * idx, * rootspec = NULL;
3662    
# Line 1994  static char * getRootSpecifier(char * st Line 3668  static char * getRootSpecifier(char * st
3668      return rootspec;      return rootspec;
3669  }  }
3670    
3671    static char * getInitrdVal(struct grubConfig * config,
3672       const char * prefix, struct singleLine *tmplLine,
3673       const char * newKernelInitrd,
3674       const char ** extraInitrds, int extraInitrdCount)
3675    {
3676        char *initrdVal, *end;
3677        int i;
3678        size_t totalSize;
3679        size_t prefixLen;
3680        char separatorChar;
3681    
3682        prefixLen = strlen(prefix);
3683        totalSize = strlen(newKernelInitrd) - prefixLen + 1 /* \0 */;
3684    
3685        for (i = 0; i < extraInitrdCount; i++) {
3686     totalSize += sizeof(separatorChar);
3687     totalSize += strlen(extraInitrds[i]) - prefixLen;
3688        }
3689    
3690        initrdVal = end = malloc(totalSize);
3691    
3692        end = stpcpy (end, newKernelInitrd + prefixLen);
3693    
3694        separatorChar = getKeywordByType(LT_INITRD, config->cfi)->separatorChar;
3695        for (i = 0; i < extraInitrdCount; i++) {
3696     const char *extraInitrd;
3697     int j;
3698    
3699     extraInitrd = extraInitrds[i] + prefixLen;
3700     /* Don't add entries that are already there */
3701     if (tmplLine != NULL) {
3702        for (j = 2; j < tmplLine->numElements; j++)
3703     if (strcmp(extraInitrd, tmplLine->elements[j].item) == 0)
3704        break;
3705    
3706        if (j != tmplLine->numElements)
3707     continue;
3708     }
3709    
3710     *end++ = separatorChar;
3711     end = stpcpy(end, extraInitrd);
3712        }
3713    
3714        return initrdVal;
3715    }
3716    
3717  int addNewKernel(struct grubConfig * config, struct singleEntry * template,  int addNewKernel(struct grubConfig * config, struct singleEntry * template,
3718           const char * prefix,           const char * prefix,
3719   char * newKernelPath, char * newKernelTitle,   const char * newKernelPath, const char * newKernelTitle,
3720   char * newKernelArgs, char * newKernelInitrd,   const char * newKernelArgs, const char * newKernelInitrd,
3721                   char * newMBKernel, char * newMBKernelArgs) {   const char ** extraInitrds, int extraInitrdCount,
3722                     const char * newMBKernel, const char * newMBKernelArgs,
3723     const char * newDevTreePath) {
3724      struct singleEntry * new;      struct singleEntry * new;
3725      struct singleLine * newLine = NULL, * tmplLine = NULL, * lastLine = NULL;      struct singleLine * newLine = NULL, * tmplLine = NULL, * masterLine = NULL;
3726      int needs;      int needs;
     char * indent = NULL;  
     char * rootspec = NULL;  
3727      char * chptr;      char * chptr;
     int i;  
     enum lineType_e type;  
3728    
3729      if (!newKernelPath) return 0;      if (!newKernelPath) return 0;
3730    
# Line 2036  int addNewKernel(struct grubConfig * con Line 3754  int addNewKernel(struct grubConfig * con
3754      config->entries = new;      config->entries = new;
3755    
3756      /* copy/update from the template */      /* copy/update from the template */
3757      needs = KERNEL_KERNEL | KERNEL_INITRD | KERNEL_TITLE;      needs = NEED_KERNEL | NEED_TITLE;
3758        if (newKernelInitrd)
3759     needs |= NEED_INITRD;
3760      if (newMBKernel) {      if (newMBKernel) {
3761          needs |= KERNEL_MB;          needs |= NEED_MB;
3762          new->multiboot = 1;          new->multiboot = 1;
3763      }      }
3764        if (newDevTreePath && getKeywordByType(LT_DEVTREE, config->cfi))
3765     needs |= NEED_DEVTREE;
3766    
3767      if (template) {      if (template) {
3768   for (tmplLine = template->lines; tmplLine; tmplLine = tmplLine->next) {   for (masterLine = template->lines;
3769      /* remember the indention level; we may need it for new lines */       masterLine && (tmplLine = lineDup(masterLine));
3770      if (tmplLine->numElements)       lineFree(tmplLine), masterLine = masterLine->next)
3771   indent = tmplLine->indent;   {
3772        dbgPrintf("addNewKernel processing %d\n", tmplLine->type);
3773    
3774      /* skip comments */      /* skip comments */
3775      chptr = tmplLine->indent;      chptr = tmplLine->indent;
3776      while (*chptr && isspace(*chptr)) chptr++;      while (*chptr && isspace(*chptr)) chptr++;
3777      if (*chptr == '#') continue;      if (*chptr == '#') continue;
3778    
3779      /* we don't need an initrd here */      if (iskernel(tmplLine->type) && tmplLine->numElements >= 2) {
3780      if (tmplLine->type == LT_INITRD && !newKernelInitrd) continue;   if (!template->multiboot && (needs & NEED_MB)) {
3781        /* it's not a multiboot template and this is the kernel
3782              if (tmplLine->type == LT_KERNEL &&       * line.  Try to be intelligent about inserting the
3783                  !template->multiboot && (needs & KERNEL_MB)) {       * hypervisor at the same time.
3784                  struct singleLine *l;       */
3785                  needs &= ~ KERNEL_MB;      if (config->cfi->mbHyperFirst) {
3786     /* insert the hypervisor first */
3787                  l = addLine(new, config->cfi, LT_KERNEL,   newLine = addLine(new, config->cfi, LT_HYPER,
3788                                    config->secondaryIndent,    tmplLine->indent,
3789                                    newMBKernel + strlen(prefix));    newMBKernel + strlen(prefix));
3790                     /* set up for adding the kernel line */
3791                  tmplLine = lastLine;   free(tmplLine->indent);
3792                  if (!new->lines) {   tmplLine->indent = strdup(config->secondaryIndent);
3793                      new->lines = l;   needs &= ~NEED_MB;
3794                  } else {      }
3795                      newLine->next = l;      if (needs & NEED_KERNEL) {
3796                      newLine = l;   /* use addLineTmpl to preserve line elements,
3797                  }   * otherwise we could just call addLine.  Unfortunately
3798                  continue;   * this means making some changes to the template
3799              } else if (tmplLine->type == LT_KERNEL &&   * such as the indent change above and the type
3800                         template->multiboot && !new->multiboot) {   * change below.
3801                  continue; /* don't need multiboot kernel here */   */
3802              }   struct keywordTypes * mbm_kw =
3803        getKeywordByType(LT_MBMODULE, config->cfi);
3804      if (!new->lines) {   if (mbm_kw) {
3805   newLine = malloc(sizeof(*newLine));      tmplLine->type = LT_MBMODULE;
3806   new->lines = newLine;      free(tmplLine->elements[0].item);
3807      } else {      tmplLine->elements[0].item = strdup(mbm_kw->key);
3808   newLine->next = malloc(sizeof(*newLine));   }
3809   newLine = newLine->next;   newLine = addLineTmpl(new, tmplLine, newLine,
3810      }        newKernelPath + strlen(prefix), config->cfi);
3811     needs &= ~NEED_KERNEL;
3812        }
3813        if (needs & NEED_MB) { /* !mbHyperFirst */
3814     newLine = addLine(new, config->cfi, LT_HYPER,
3815      config->secondaryIndent,
3816      newMBKernel + strlen(prefix));
3817     needs &= ~NEED_MB;
3818        }
3819     } else if (needs & NEED_KERNEL) {
3820        newLine = addLineTmpl(new, tmplLine, newLine,
3821      newKernelPath + strlen(prefix), config->cfi);
3822        needs &= ~NEED_KERNEL;
3823     }
3824    
3825        } else if (tmplLine->type == LT_HYPER &&
3826           tmplLine->numElements >= 2) {
3827     if (needs & NEED_MB) {
3828        newLine = addLineTmpl(new, tmplLine, newLine,
3829      newMBKernel + strlen(prefix), config->cfi);
3830        needs &= ~NEED_MB;
3831     }
3832    
3833      newLine->indent = strdup(tmplLine->indent);      } else if (tmplLine->type == LT_MBMODULE &&
3834      newLine->next = NULL;         tmplLine->numElements >= 2) {
3835      newLine->type = tmplLine->type;   if (new->multiboot) {
3836      newLine->numElements = tmplLine->numElements;      if (needs & NEED_KERNEL) {
3837      newLine->elements = malloc(sizeof(*newLine->elements) *   newLine = addLineTmpl(new, tmplLine, newLine,
3838      newLine->numElements);        newKernelPath +
3839      for (i = 0; i < newLine->numElements; i++) {        strlen(prefix), config->cfi);
3840   newLine->elements[i].item = strdup(tmplLine->elements[i].item);   needs &= ~NEED_KERNEL;
3841   newLine->elements[i].indent =      } else if (config->cfi->mbInitRdIsModule &&
3842   strdup(tmplLine->elements[i].indent);         (needs & NEED_INITRD)) {
3843      }   char *initrdVal;
3844     initrdVal = getInitrdVal(config, prefix, tmplLine,
3845              lastLine = tmplLine;   newKernelInitrd, extraInitrds,
3846      if (tmplLine->type == LT_KERNEL && tmplLine->numElements >= 2) {   extraInitrdCount);
3847                  char * repl;   newLine = addLineTmpl(new, tmplLine, newLine,
3848                  if (!template->multiboot) {        initrdVal, config->cfi);
3849                      needs &= ~KERNEL_KERNEL;   free(initrdVal);
3850                      repl = newKernelPath;   needs &= ~NEED_INITRD;
3851                  } else {      }
3852                      needs &= ~KERNEL_MB;   } else if (needs & NEED_KERNEL) {
3853                      repl = newMBKernel;      /* template is multi but new is not,
3854                  }       * insert the kernel in the first module slot
3855                  if (new->multiboot && !template->multiboot) {       */
3856                      free(newLine->elements[0].item);      tmplLine->type = preferredLineType(LT_KERNEL, config->cfi);
3857                      newLine->elements[0].item = strdup("module");      free(tmplLine->elements[0].item);
3858                      newLine->type = LT_MBMODULE;      tmplLine->elements[0].item =
3859                  }   strdup(getKeywordByType(tmplLine->type,
3860   free(newLine->elements[1].item);   config->cfi)->key);
3861                  rootspec = getRootSpecifier(tmplLine->elements[1].item);      newLine = addLineTmpl(new, tmplLine, newLine,
3862                  if (rootspec != NULL) {    newKernelPath + strlen(prefix),
3863                      newLine->elements[1].item = sdupprintf("%s%s",    config->cfi);
3864                                                             rootspec,      needs &= ~NEED_KERNEL;
3865                                                             repl +   } else if (needs & NEED_INITRD) {
3866                                                             strlen(prefix));      char *initrdVal;
3867                  } else {      /* template is multi but new is not,
3868                      newLine->elements[1].item = strdup(repl +       * insert the initrd in the second module slot
3869                                                         strlen(prefix));       */
3870                  }      tmplLine->type = preferredLineType(LT_INITRD, config->cfi);
3871              } else if (tmplLine->type == LT_MBMODULE &&      free(tmplLine->elements[0].item);
3872                         tmplLine->numElements >= 2 && (needs & KERNEL_KERNEL)) {      tmplLine->elements[0].item =
3873                  needs &= ~KERNEL_KERNEL;   strdup(getKeywordByType(tmplLine->type,
3874                  if (!new->multiboot && template->multiboot) {   config->cfi)->key);
3875                      free(newLine->elements[0].item);      initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3876                      newLine->elements[0].item = strdup("kernel");      newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3877                      newLine->type = LT_KERNEL;      free(initrdVal);
3878                  }      needs &= ~NEED_INITRD;
3879   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;  
3880    
3881   for (i = 1; i < newLine->numElements; i++) {      } else if (isinitrd(tmplLine->type) && tmplLine->numElements >= 2) {
3882      free(newLine->elements[i].item);   if (needs & NEED_INITRD &&
3883      free(newLine->elements[i].indent);      new->multiboot && !template->multiboot &&
3884        config->cfi->mbInitRdIsModule) {
3885        /* make sure we don't insert the module initrd
3886         * before the module kernel... if we don't do it here,
3887         * it will be inserted following the template.
3888         */
3889        if (!needs & NEED_KERNEL) {
3890     char *initrdVal;
3891    
3892     initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3893     newLine = addLine(new, config->cfi, LT_MBMODULE,
3894      config->secondaryIndent,
3895      initrdVal);
3896     free(initrdVal);
3897     needs &= ~NEED_INITRD;
3898        }
3899     } else if (needs & NEED_INITRD) {
3900        char *initrdVal;
3901        initrdVal = getInitrdVal(config, prefix, tmplLine, newKernelInitrd, extraInitrds, extraInitrdCount);
3902        newLine = addLineTmpl(new, tmplLine, newLine, initrdVal, config->cfi);
3903        free(initrdVal);
3904        needs &= ~NEED_INITRD;
3905   }   }
3906    
3907   newLine->elements[1].item = strdup(newKernelTitle);      } else if (tmplLine->type == LT_MENUENTRY &&
3908   newLine->elements[1].indent = strdup("");         (needs & NEED_TITLE)) {
3909   newLine->numElements = 2;   requote(tmplLine, config->cfi);
3910     char *nkt = malloc(strlen(newKernelTitle)+3);
3911     strcpy(nkt, "'");
3912     strcat(nkt, newKernelTitle);
3913     strcat(nkt, "'");
3914     newLine = addLineTmpl(new, tmplLine, newLine, nkt, config->cfi);
3915     free(nkt);
3916     needs &= ~NEED_TITLE;
3917      } else if (tmplLine->type == LT_TITLE &&      } else if (tmplLine->type == LT_TITLE &&
3918                         config->cfi->titleBracketed &&         (needs & NEED_TITLE)) {
3919                         tmplLine->numElements == 1) {   if (tmplLine->numElements >= 2) {
3920                  needs &= ~KERNEL_TITLE;      newLine = addLineTmpl(new, tmplLine, newLine,
3921                  free(newLine->elements[0].item);    newKernelTitle, config->cfi);
3922                  free(newLine->elements[0].indent);      needs &= ~NEED_TITLE;
3923                  newLine->elements = malloc(sizeof(*newLine->elements) *   } else if (tmplLine->numElements == 1 &&
3924                                             newLine->numElements);     config->cfi->titleBracketed) {
3925        /* addLineTmpl doesn't handle titleBracketed */
3926                  newLine->elements[0].item = malloc(strlen(newKernelTitle) + 3);      newLine = addLine(new, config->cfi, LT_TITLE,
3927                  sprintf(newLine->elements[0].item, "[%s]", newKernelTitle);        tmplLine->indent, newKernelTitle);
3928                  newLine->elements[0].indent = strdup("");      needs &= ~NEED_TITLE;
3929                  newLine->numElements = 1;   }
3930              }      } else if (tmplLine->type == LT_ECHO) {
3931        requote(tmplLine, config->cfi);
3932        static const char *prefix = "'Loading ";
3933        if (tmplLine->numElements > 1 &&
3934        strstr(tmplLine->elements[1].item, prefix) &&
3935        masterLine->next &&
3936        iskernel(masterLine->next->type)) {
3937     char *newTitle = malloc(strlen(prefix) +
3938     strlen(newKernelTitle) + 2);
3939    
3940     strcpy(newTitle, prefix);
3941     strcat(newTitle, newKernelTitle);
3942     strcat(newTitle, "'");
3943     newLine = addLine(new, config->cfi, LT_ECHO,
3944     tmplLine->indent, newTitle);
3945     free(newTitle);
3946        } else {
3947     /* pass through other lines from the template */
3948     newLine = addLineTmpl(new, tmplLine, newLine, NULL,
3949     config->cfi);
3950        }
3951        } else if (tmplLine->type == LT_DEVTREE &&
3952           tmplLine->numElements == 2 && newDevTreePath) {
3953            newLine = addLineTmpl(new, tmplLine, newLine,
3954          newDevTreePath + strlen(prefix),
3955          config->cfi);
3956     needs &= ~NEED_DEVTREE;
3957        } else if (tmplLine->type == LT_ENTRY_END && needs & NEED_DEVTREE) {
3958     const char *ndtp = newDevTreePath;
3959     if (!strncmp(newDevTreePath, prefix, strlen(prefix)))
3960        ndtp += strlen(prefix);
3961     newLine = addLine(new, config->cfi, LT_DEVTREE,
3962      config->secondaryIndent,
3963      ndtp);
3964     needs &= ~NEED_DEVTREE;
3965     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
3966        } else {
3967     /* pass through other lines from the template */
3968     newLine = addLineTmpl(new, tmplLine, newLine, NULL, config->cfi);
3969        }
3970   }   }
3971    
3972      } else {      } else {
3973   for (i = 0; config->cfi->keywords[i].key; i++) {   /* don't have a template, so start the entry with the
3974      if ((config->cfi->keywords[i].type == config->cfi->entrySeparator) || (config->cfi->keywords[i].type == LT_OTHER))   * appropriate starting line
3975     */
3976     switch (config->cfi->entryStart) {
3977        case LT_KERNEL:
3978        case LT_KERNEL_EFI:
3979        case LT_KERNEL_16:
3980     if (new->multiboot && config->cfi->mbHyperFirst) {
3981        /* fall through to LT_HYPER */
3982     } else {
3983        newLine = addLine(new, config->cfi,
3984              preferredLineType(LT_KERNEL, config->cfi),
3985          config->primaryIndent,
3986          newKernelPath + strlen(prefix));
3987        needs &= ~NEED_KERNEL;
3988        break;
3989     }
3990    
3991        case LT_HYPER:
3992     newLine = addLine(new, config->cfi, LT_HYPER,
3993      config->primaryIndent,
3994      newMBKernel + strlen(prefix));
3995     needs &= ~NEED_MB;
3996   break;   break;
         }  
3997    
3998   switch (config->cfi->keywords[i].type) {      case LT_MENUENTRY: {
3999      case LT_KERNEL:  needs &= ~KERNEL_KERNEL,   char *nkt = malloc(strlen(newKernelTitle)+3);
4000       chptr = newKernelPath + strlen(prefix);   strcpy(nkt, "'");
4001       type = LT_KERNEL; break;   strcat(nkt, newKernelTitle);
4002      case LT_TITLE:   needs &= ~KERNEL_TITLE, chptr = newKernelTitle;   strcat(nkt, "'");
4003       type = LT_TITLE; break;          newLine = addLine(new, config->cfi, LT_MENUENTRY,
4004      default:        config->primaryIndent, nkt);
4005                  /* zipl strikes again */   free(nkt);
4006                  if (config->cfi->titleBracketed) {   needs &= ~NEED_TITLE;
4007                      needs &= ~KERNEL_TITLE;   needs |= NEED_END;
4008                      chptr = newKernelTitle;   break;
4009                      type = LT_TITLE;      }
4010                      break;      case LT_TITLE:
4011                  } else {   if( useextlinuxmenu != 0 ){ // We just need useextlinuxmenu to not be zero (set above)
4012                      abort();   char * templabel;
4013                  }   int x = 0, y = 0;
4014   }  
4015     templabel = strdup(newKernelTitle);
4016     while( templabel[x]){
4017     if( templabel[x] == ' ' ){
4018     y = x;
4019     while( templabel[y] ){
4020     templabel[y] = templabel[y+1];
4021     y++;
4022     }
4023     }
4024     x++;
4025     }
4026     newLine = addLine(new, config->cfi, LT_TITLE,
4027      config->primaryIndent, templabel);
4028     free(templabel);
4029     }else{
4030     newLine = addLine(new, config->cfi, LT_TITLE,
4031      config->primaryIndent, newKernelTitle);
4032     }
4033     needs &= ~NEED_TITLE;
4034     break;
4035    
4036   newLine = addLine(new, config->cfi, type, config->primaryIndent, chptr);      default:
4037   new->lines = newLine;   abort();
4038     }
4039      }      }
4040    
4041      if (new->multiboot) {      /* add the remainder of the lines, i.e. those that either
4042          if (needs & KERNEL_MB)       * weren't present in the template, or in the case of no template,
4043              newLine = addLine(new, config->cfi, LT_KERNEL,       * all the lines following the entryStart.
4044                                config->secondaryIndent,       */
4045                                newMBKernel + strlen(prefix));      if (needs & NEED_TITLE) {
4046          if (needs & KERNEL_KERNEL)   newLine = addLine(new, config->cfi, LT_TITLE,
4047              newLine = addLine(new, config->cfi, LT_MBMODULE,    config->secondaryIndent,
4048                                config->secondaryIndent,    newKernelTitle);
4049                                newKernelPath + strlen(prefix));   needs &= ~NEED_TITLE;
4050          /* don't need to check for title as it's guaranteed to have been      }
4051           * done as we only do multiboot with grub which uses title as      if ((needs & NEED_MB) && config->cfi->mbHyperFirst) {
4052           * a separator */   newLine = addLine(new, config->cfi, LT_HYPER,
4053          if (needs & KERNEL_INITRD && newKernelInitrd)    config->secondaryIndent,
4054              newLine = addLine(new, config->cfi, LT_MBMODULE,    newMBKernel + strlen(prefix));
4055                                config->secondaryIndent,   needs &= ~NEED_MB;
4056                                newKernelInitrd + strlen(prefix));      }
4057      } else {      if (needs & NEED_KERNEL) {
4058          if (needs & KERNEL_KERNEL)   newLine = addLine(new, config->cfi,
4059              newLine = addLine(new, config->cfi, LT_KERNEL,    (new->multiboot && getKeywordByType(LT_MBMODULE,
4060                                config->secondaryIndent,        config->cfi))
4061                                newKernelPath + strlen(prefix));     ? LT_MBMODULE
4062          if (needs & KERNEL_TITLE)   : preferredLineType(LT_KERNEL, config->cfi),
4063              newLine = addLine(new, config->cfi, LT_TITLE,    config->secondaryIndent,
4064                                config->secondaryIndent,    newKernelPath + strlen(prefix));
4065                                newKernelTitle);   needs &= ~NEED_KERNEL;
4066          if (needs & KERNEL_INITRD && newKernelInitrd)      }
4067              newLine = addLine(new, config->cfi, LT_INITRD,      if (needs & NEED_MB) {
4068                                config->secondaryIndent,   newLine = addLine(new, config->cfi, LT_HYPER,
4069                                newKernelInitrd + strlen(prefix));    config->secondaryIndent,
4070      newMBKernel + strlen(prefix));
4071     needs &= ~NEED_MB;
4072        }
4073        if (needs & NEED_INITRD) {
4074     char *initrdVal;
4075     initrdVal = getInitrdVal(config, prefix, NULL, newKernelInitrd, extraInitrds, extraInitrdCount);
4076     newLine = addLine(new, config->cfi,
4077      (new->multiboot && getKeywordByType(LT_MBMODULE,
4078          config->cfi))
4079       ? LT_MBMODULE
4080       : preferredLineType(LT_INITRD, config->cfi),
4081      config->secondaryIndent,
4082      initrdVal);
4083     free(initrdVal);
4084     needs &= ~NEED_INITRD;
4085        }
4086        if (needs & NEED_DEVTREE) {
4087     newLine = addLine(new, config->cfi, LT_DEVTREE,
4088      config->secondaryIndent,
4089      newDevTreePath);
4090     needs &= ~NEED_DEVTREE;
4091        }
4092    
4093        /* NEEDS_END must be last on bootloaders that need it... */
4094        if (needs & NEED_END) {
4095     newLine = addLine(new, config->cfi, LT_ENTRY_END,
4096     config->secondaryIndent, NULL);
4097     needs &= ~NEED_END;
4098        }
4099        if (needs) {
4100     printf(_("grubby: needs=%d, aborting\n"), needs);
4101     abort();
4102      }      }
4103    
4104      if (updateImage(config, "0", prefix, newKernelArgs, NULL,      if (updateImage(config, "0", prefix, newKernelArgs, NULL,
# Line 2274  int addNewKernel(struct grubConfig * con Line 4107  int addNewKernel(struct grubConfig * con
4107      return 0;      return 0;
4108  }  }
4109    
4110    static void traceback(int signum)
4111    {
4112        void *array[40];
4113        size_t size;
4114    
4115        signal(SIGSEGV, SIG_DFL);
4116        memset(array, '\0', sizeof (array));
4117        size = backtrace(array, 40);
4118    
4119        fprintf(stderr, "grubby received SIGSEGV!  Backtrace (%ld):\n",
4120                (unsigned long)size);
4121        backtrace_symbols_fd(array, size, STDERR_FILENO);
4122        exit(1);
4123    }
4124    
4125  int main(int argc, const char ** argv) {  int main(int argc, const char ** argv) {
4126      poptContext optCon;      poptContext optCon;
4127      char * grubConfig = NULL;      const char * grubConfig = NULL;
4128      char * outputFile = NULL;      char * outputFile = NULL;
4129      int arg = 0;      int arg = 0;
4130      int flags = 0;      int flags = 0;
4131      int badImageOkay = 0;      int badImageOkay = 0;
4132        int configureGrub2 = 0;
4133      int configureLilo = 0, configureELilo = 0, configureGrub = 0;      int configureLilo = 0, configureELilo = 0, configureGrub = 0;
4134      int configureYaboot = 0, configureSilo = 0, configureZipl = 0;      int configureYaboot = 0, configureSilo = 0, configureZipl = 0;
4135        int configureExtLinux = 0;
4136      int bootloaderProbe = 0;      int bootloaderProbe = 0;
4137        int extraInitrdCount = 0;
4138      char * updateKernelPath = NULL;      char * updateKernelPath = NULL;
4139      char * newKernelPath = NULL;      char * newKernelPath = NULL;
4140      char * removeKernelPath = NULL;      char * removeKernelPath = NULL;
4141      char * newKernelArgs = NULL;      char * newKernelArgs = NULL;
4142      char * newKernelInitrd = NULL;      char * newKernelInitrd = NULL;
4143      char * newKernelTitle = NULL;      char * newKernelTitle = NULL;
4144      char * newKernelVersion = NULL;      char * newDevTreePath = NULL;
4145      char * newMBKernel = NULL;      char * newMBKernel = NULL;
4146      char * newMBKernelArgs = NULL;      char * newMBKernelArgs = NULL;
4147      char * removeMBKernelArgs = NULL;      char * removeMBKernelArgs = NULL;
# Line 2299  int main(int argc, const char ** argv) { Line 4150  int main(int argc, const char ** argv) {
4150      char * defaultKernel = NULL;      char * defaultKernel = NULL;
4151      char * removeArgs = NULL;      char * removeArgs = NULL;
4152      char * kernelInfo = NULL;      char * kernelInfo = NULL;
4153        char * extraInitrds[MAX_EXTRA_INITRDS] = { NULL };
4154        char * envPath = NULL;
4155      const char * chptr = NULL;      const char * chptr = NULL;
4156      struct configFileInfo * cfi = NULL;      struct configFileInfo * cfi = NULL;
4157      struct grubConfig * config;      struct grubConfig * config;
4158      struct singleEntry * template = NULL;      struct singleEntry * template = NULL;
4159      int copyDefault = 0, makeDefault = 0;      int copyDefault = 0, makeDefault = 0;
4160      int displayDefault = 0;      int displayDefault = 0;
4161        int displayDefaultIndex = 0;
4162        int displayDefaultTitle = 0;
4163        int defaultIndex = -1;
4164      struct poptOption options[] = {      struct poptOption options[] = {
4165   { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,   { "add-kernel", 0, POPT_ARG_STRING, &newKernelPath, 0,
4166      _("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 4178  int main(int argc, const char ** argv) {
4178   { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,   { "boot-filesystem", 0, POPT_ARG_STRING, &bootPrefix, 0,
4179      _("filestystem which contains /boot directory (for testing only)"),      _("filestystem which contains /boot directory (for testing only)"),
4180      _("bootfs") },      _("bootfs") },
4181  #if defined(__i386__) || defined(__x86_64__)  #if defined(__i386__) || defined(__x86_64__) || defined (__powerpc64__) || defined (__ia64__)
4182   { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,   { "bootloader-probe", 0, POPT_ARG_NONE, &bootloaderProbe, 0,
4183      _("check if lilo is installed on lilo.conf boot sector") },      _("check which bootloader is installed on boot sector") },
4184  #endif  #endif
4185   { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,   { "config-file", 'c', POPT_ARG_STRING, &grubConfig, 0,
4186      _("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 4191  int main(int argc, const char ** argv) {
4191        "the kernel referenced by the default image does not exist, "        "the kernel referenced by the default image does not exist, "
4192        "the first linux entry whose kernel does exist is used as the "        "the first linux entry whose kernel does exist is used as the "
4193        "template"), NULL },        "template"), NULL },
4194     { "debug", 0, 0, &debug, 0,
4195        _("print debugging information for failures") },
4196   { "default-kernel", 0, 0, &displayDefault, 0,   { "default-kernel", 0, 0, &displayDefault, 0,
4197      _("display the path of the default kernel") },      _("display the path of the default kernel") },
4198     { "default-index", 0, 0, &displayDefaultIndex, 0,
4199        _("display the index of the default kernel") },
4200     { "default-title", 0, 0, &displayDefaultTitle, 0,
4201        _("display the title of the default kernel") },
4202     { "devtree", 0, POPT_ARG_STRING, &newDevTreePath, 0,
4203        _("device tree file for new stanza"), _("dtb-path") },
4204   { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,   { "elilo", 0, POPT_ARG_NONE, &configureELilo, 0,
4205      _("configure elilo bootloader") },      _("configure elilo bootloader") },
4206     { "efi", 0, POPT_ARG_NONE, &isEfi, 0,
4207        _("force grub2 stanzas to use efi") },
4208     { "env", 0, POPT_ARG_STRING, &envPath, 0,
4209        _("path for environment data"),
4210        _("path") },
4211     { "extlinux", 0, POPT_ARG_NONE, &configureExtLinux, 0,
4212        _("configure extlinux bootloader (from syslinux)") },
4213   { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,   { "grub", 0, POPT_ARG_NONE, &configureGrub, 0,
4214      _("configure grub bootloader") },      _("configure grub bootloader") },
4215     { "grub2", 0, POPT_ARG_NONE, &configureGrub2, 0,
4216        _("configure grub2 bootloader") },
4217   { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,   { "info", 0, POPT_ARG_STRING, &kernelInfo, 0,
4218      _("display boot information for specified kernel"),      _("display boot information for specified kernel"),
4219      _("kernel-path") },      _("kernel-path") },
4220   { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,   { "initrd", 0, POPT_ARG_STRING, &newKernelInitrd, 0,
4221      _("initrd image for the new kernel"), _("initrd-path") },      _("initrd image for the new kernel"), _("initrd-path") },
4222     { "extra-initrd", 'i', POPT_ARG_STRING, NULL, 'i',
4223        _("auxiliary initrd image for things other than the new kernel"), _("initrd-path") },
4224   { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,   { "lilo", 0, POPT_ARG_NONE, &configureLilo, 0,
4225      _("configure lilo bootloader") },      _("configure lilo bootloader") },
4226   { "make-default", 0, 0, &makeDefault, 0,   { "make-default", 0, 0, &makeDefault, 0,
# Line 2365  int main(int argc, const char ** argv) { Line 4240  int main(int argc, const char ** argv) {
4240   { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,   { "set-default", 0, POPT_ARG_STRING, &defaultKernel, 0,
4241      _("make the first entry referencing the specified kernel "      _("make the first entry referencing the specified kernel "
4242        "the default"), _("kernel-path") },        "the default"), _("kernel-path") },
4243     { "set-default-index", 0, POPT_ARG_INT, &defaultIndex, 0,
4244        _("make the given entry index the default entry"),
4245        _("entry-index") },
4246   { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,   { "silo", 0, POPT_ARG_NONE, &configureSilo, 0,
4247      _("configure silo bootloader") },      _("configure silo bootloader") },
4248   { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,   { "title", 0, POPT_ARG_STRING, &newKernelTitle, 0,
# Line 2382  int main(int argc, const char ** argv) { Line 4260  int main(int argc, const char ** argv) {
4260   { 0, 0, 0, 0, 0 }   { 0, 0, 0, 0, 0 }
4261      };      };
4262    
4263        useextlinuxmenu=0;
4264    
4265        signal(SIGSEGV, traceback);
4266    
4267        int i = 0;
4268        for (int j = 1; j < argc; j++)
4269     i += strlen(argv[j]) + 1;
4270        saved_command_line = malloc(i);
4271        if (!saved_command_line) {
4272     fprintf(stderr, "grubby: %m\n");
4273     exit(1);
4274        }
4275        saved_command_line[0] = '\0';
4276        for (int j = 1; j < argc; j++) {
4277     strcat(saved_command_line, argv[j]);
4278     strncat(saved_command_line, j == argc -1 ? "" : " ", 1);
4279        }
4280    
4281      optCon = poptGetContext("grubby", argc, argv, options, 0);      optCon = poptGetContext("grubby", argc, argv, options, 0);
4282      poptReadDefaultConfig(optCon, 1);      poptReadDefaultConfig(optCon, 1);
4283    
# Line 2391  int main(int argc, const char ** argv) { Line 4287  int main(int argc, const char ** argv) {
4287      printf("grubby version %s\n", VERSION);      printf("grubby version %s\n", VERSION);
4288      exit(0);      exit(0);
4289      break;      break;
4290      case 'i':
4291        if (extraInitrdCount < MAX_EXTRA_INITRDS) {
4292         extraInitrds[extraInitrdCount++] = strdup(poptGetOptArg(optCon));
4293        } else {
4294     fprintf(stderr, _("grubby: extra initrd maximum is %d\n"), extraInitrdCount);
4295     return 1;
4296        }
4297        break;
4298   }   }
4299      }      }
4300    
# Line 2406  int main(int argc, const char ** argv) { Line 4310  int main(int argc, const char ** argv) {
4310   return 1;   return 1;
4311      }      }
4312    
4313      if ((configureLilo + configureGrub + configureELilo +      if ((configureLilo + configureGrub2 + configureGrub + configureELilo +
4314   configureYaboot + configureSilo + configureZipl) > 1) {   configureYaboot + configureSilo + configureZipl +
4315     configureExtLinux ) > 1) {
4316   fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));   fprintf(stderr, _("grubby: cannot specify multiple bootloaders\n"));
4317   return 1;   return 1;
4318      } else if (bootloaderProbe && grubConfig) {      } else if (bootloaderProbe && grubConfig) {
4319   fprintf(stderr,   fprintf(stderr,
4320      _("grubby: cannot specify config file with --bootloader-probe\n"));      _("grubby: cannot specify config file with --bootloader-probe\n"));
4321   return 1;   return 1;
4322        } else if (configureGrub2) {
4323     cfi = &grub2ConfigType;
4324     if (envPath)
4325        cfi->envFile = envPath;
4326      } else if (configureLilo) {      } else if (configureLilo) {
4327   cfi = &liloConfigType;   cfi = &liloConfigType;
4328      } else if (configureGrub) {      } else if (configureGrub) {
# Line 2426  int main(int argc, const char ** argv) { Line 4335  int main(int argc, const char ** argv) {
4335          cfi = &siloConfigType;          cfi = &siloConfigType;
4336      } else if (configureZipl) {      } else if (configureZipl) {
4337          cfi = &ziplConfigType;          cfi = &ziplConfigType;
4338        } else if (configureExtLinux) {
4339     cfi = &extlinuxConfigType;
4340     useextlinuxmenu=1;
4341      }      }
4342    
4343      if (!cfi) {      if (!cfi) {
4344            if (grub2FindConfig(&grub2ConfigType))
4345        cfi = &grub2ConfigType;
4346     else
4347        #ifdef __ia64__        #ifdef __ia64__
4348   cfi = &eliloConfigType;      cfi = &eliloConfigType;
4349        #elif __powerpc__        #elif __powerpc__
4350   cfi = &yabootConfigType;      cfi = &yabootConfigType;
4351        #elif __sparc__        #elif __sparc__
4352          cfi = &siloConfigType;              cfi = &siloConfigType;
4353        #elif __s390__        #elif __s390__
4354          cfi = &ziplConfigType;              cfi = &ziplConfigType;
4355        #elif __s390x__        #elif __s390x__
4356          cfi = &ziplConfigtype;              cfi = &ziplConfigtype;
4357        #else        #else
4358   cfi = &grubConfigType;      cfi = &grubConfigType;
4359        #endif        #endif
4360      }      }
4361    
4362      if (!grubConfig)      if (!grubConfig) {
4363   grubConfig = cfi->defaultConfig;   if (cfi->findConfig)
4364        grubConfig = cfi->findConfig(cfi);
4365     if (!grubConfig)
4366        grubConfig = cfi->defaultConfig;
4367        }
4368    
4369      if (bootloaderProbe && (displayDefault || kernelInfo || newKernelVersion ||      if (bootloaderProbe && (displayDefault || kernelInfo ||
4370    newKernelPath || removeKernelPath || makeDefault ||      newKernelPath || removeKernelPath || makeDefault ||
4371    defaultKernel)) {      defaultKernel || displayDefaultIndex || displayDefaultTitle ||
4372        (defaultIndex >= 0))) {
4373   fprintf(stderr, _("grubby: --bootloader-probe may not be used with "   fprintf(stderr, _("grubby: --bootloader-probe may not be used with "
4374    "specified option"));    "specified option"));
4375   return 1;   return 1;
4376      }      }
4377    
4378      if ((displayDefault || kernelInfo) && (newKernelVersion || newKernelPath ||      if ((displayDefault || kernelInfo) && (newKernelPath ||
4379     removeKernelPath)) {     removeKernelPath)) {
4380   fprintf(stderr, _("grubby: --default-kernel and --info may not "   fprintf(stderr, _("grubby: --default-kernel and --info may not "
4381    "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 4385  int main(int argc, const char ** argv) {
4385      if (newKernelPath && !newKernelTitle) {      if (newKernelPath && !newKernelTitle) {
4386   fprintf(stderr, _("grubby: kernel title must be specified\n"));   fprintf(stderr, _("grubby: kernel title must be specified\n"));
4387   return 1;   return 1;
4388      } else if (!newKernelPath && (newKernelTitle  || newKernelInitrd ||      } else if (!newKernelPath && (newKernelTitle  || copyDefault ||
4389    newKernelInitrd || copyDefault     ||    (newKernelInitrd && !updateKernelPath)||
4390    makeDefault)) {    makeDefault || extraInitrdCount > 0)) {
4391   fprintf(stderr, _("grubby: kernel path expected\n"));   fprintf(stderr, _("grubby: kernel path expected\n"));
4392   return 1;   return 1;
4393      }      }
# Line 2491  int main(int argc, const char ** argv) { Line 4411  int main(int argc, const char ** argv) {
4411   makeDefault = 1;   makeDefault = 1;
4412   defaultKernel = NULL;   defaultKernel = NULL;
4413      }      }
4414        else if (defaultKernel && (defaultIndex >= 0)) {
4415     fprintf(stderr, _("grubby: --set-default and --set-default-index "
4416      "may not be used together\n"));
4417     return 1;
4418        }
4419    
4420      if (!strcmp(grubConfig, "-") && !outputFile) {      if (grubConfig && !strcmp(grubConfig, "-") && !outputFile) {
4421   fprintf(stderr, _("grubby: output file must be specified if stdin "   fprintf(stderr, _("grubby: output file must be specified if stdin "
4422   "is used\n"));   "is used\n"));
4423   return 1;   return 1;
4424      }      }
4425    
4426      if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel      if (!removeKernelPath && !newKernelPath && !displayDefault && !defaultKernel
4427   && !kernelInfo && !bootloaderProbe && !updateKernelPath   && !kernelInfo && !bootloaderProbe && !updateKernelPath
4428          && !removeMBKernel) {   && !removeMBKernel && !displayDefaultIndex && !displayDefaultTitle
4429     && (defaultIndex == -1)) {
4430   fprintf(stderr, _("grubby: no action specified\n"));   fprintf(stderr, _("grubby: no action specified\n"));
4431   return 1;   return 1;
4432      }      }
# Line 2520  int main(int argc, const char ** argv) { Line 4446  int main(int argc, const char ** argv) {
4446   bootPrefix = "";   bootPrefix = "";
4447      }      }
4448    
4449        if (!cfi->mbAllowExtraInitRds &&
4450     extraInitrdCount > 0) {
4451     fprintf(stderr, _("grubby: %s doesn't allow multiple initrds\n"), cfi->defaultConfig);
4452     return 1;
4453        }
4454    
4455      if (bootloaderProbe) {      if (bootloaderProbe) {
4456   int lrc = 0, grc = 0;   int lrc = 0, grc = 0, gr2c = 0, extrc = 0, yrc = 0, erc = 0;
4457   struct grubConfig * lconfig, * gconfig;   struct grubConfig * lconfig, * gconfig, * yconfig, * econfig;
4458    
4459     const char *grub2config = grub2FindConfig(&grub2ConfigType);
4460     if (grub2config) {
4461        gconfig = readConfig(grub2config, &grub2ConfigType);
4462        if (!gconfig)
4463     gr2c = 1;
4464        else
4465     gr2c = checkForGrub2(gconfig);
4466     }
4467    
4468   if (!access(grubConfigType.defaultConfig, F_OK)) {   const char *grubconfig = grubFindConfig(&grubConfigType);
4469      gconfig = readConfig(grubConfigType.defaultConfig, &grubConfigType);   if (!access(grubconfig, F_OK)) {
4470        gconfig = readConfig(grubconfig, &grubConfigType);
4471      if (!gconfig)      if (!gconfig)
4472   grc = 1;   grc = 1;
4473      else      else
# Line 2540  int main(int argc, const char ** argv) { Line 4482  int main(int argc, const char ** argv) {
4482   lrc = checkForLilo(lconfig);   lrc = checkForLilo(lconfig);
4483   }   }
4484    
4485   if (lrc == 1 || grc == 1) return 1;   if (!access(eliloConfigType.defaultConfig, F_OK)) {
4486        econfig = readConfig(eliloConfigType.defaultConfig,
4487     &eliloConfigType);
4488        if (!econfig)
4489     erc = 1;
4490        else
4491     erc = checkForElilo(econfig);
4492     }
4493    
4494     if (!access(extlinuxConfigType.defaultConfig, F_OK)) {
4495        lconfig = readConfig(extlinuxConfigType.defaultConfig, &extlinuxConfigType);
4496        if (!lconfig)
4497     extrc = 1;
4498        else
4499     extrc = checkForExtLinux(lconfig);
4500     }
4501    
4502    
4503     if (!access(yabootConfigType.defaultConfig, F_OK)) {
4504        yconfig = readConfig(yabootConfigType.defaultConfig,
4505     &yabootConfigType);
4506        if (!yconfig)
4507     yrc = 1;
4508        else
4509     yrc = checkForYaboot(yconfig);
4510     }
4511    
4512     if (lrc == 1 || grc == 1 || gr2c == 1 || extrc == 1 || yrc == 1 ||
4513     erc == 1)
4514        return 1;
4515    
4516   if (lrc == 2) printf("lilo\n");   if (lrc == 2) printf("lilo\n");
4517     if (gr2c == 2) printf("grub2\n");
4518   if (grc == 2) printf("grub\n");   if (grc == 2) printf("grub\n");
4519     if (extrc == 2) printf("extlinux\n");
4520     if (yrc == 2) printf("yaboot\n");
4521     if (erc == 2) printf("elilo\n");
4522    
4523   return 0;   return 0;
4524      }      }
4525    
4526        if (grubConfig == NULL) {
4527     printf("Could not find bootloader configuration file.\n");
4528     exit(1);
4529        }
4530    
4531      config = readConfig(grubConfig, cfi);      config = readConfig(grubConfig, cfi);
4532      if (!config) return 1;      if (!config) return 1;
4533    
# Line 2557  int main(int argc, const char ** argv) { Line 4537  int main(int argc, const char ** argv) {
4537          char * rootspec;          char * rootspec;
4538    
4539   if (config->defaultImage == -1) return 0;   if (config->defaultImage == -1) return 0;
4540     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4541     cfi->defaultIsSaved)
4542        config->defaultImage = 0;
4543   entry = findEntryByIndex(config, config->defaultImage);   entry = findEntryByIndex(config, config->defaultImage);
4544   if (!entry) return 0;   if (!entry) return 0;
4545   if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;   if (!suitableImage(entry, bootPrefix, 0, flags)) return 0;
4546    
4547   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;  
4548   if (!line) return 0;   if (!line) return 0;
4549    
4550          rootspec = getRootSpecifier(line->elements[1].item);          rootspec = getRootSpecifier(line->elements[1].item);
# Line 2570  int main(int argc, const char ** argv) { Line 4552  int main(int argc, const char ** argv) {
4552                 ((rootspec != NULL) ? strlen(rootspec) : 0));                 ((rootspec != NULL) ? strlen(rootspec) : 0));
4553    
4554   return 0;   return 0;
4555    
4556        } else if (displayDefaultTitle) {
4557     struct singleLine * line;
4558     struct singleEntry * entry;
4559    
4560     if (config->defaultImage == -1) return 0;
4561     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4562     cfi->defaultIsSaved)
4563        config->defaultImage = 0;
4564     entry = findEntryByIndex(config, config->defaultImage);
4565     if (!entry) return 0;
4566    
4567     if (!configureGrub2) {
4568      line = getLineByType(LT_TITLE, entry->lines);
4569      if (!line) return 0;
4570      printf("%s\n", line->elements[1].item);
4571    
4572     } else {
4573      char * title;
4574    
4575      dbgPrintf("This is GRUB2, default title is embeded in menuentry\n");
4576      line = getLineByType(LT_MENUENTRY, entry->lines);
4577      if (!line) return 0;
4578      title = grub2ExtractTitle(line);
4579      if (title)
4580        printf("%s\n", title);
4581     }
4582     return 0;
4583    
4584        } else if (displayDefaultIndex) {
4585            if (config->defaultImage == -1) return 0;
4586     if (config->defaultImage == DEFAULT_SAVED_GRUB2 &&
4587     cfi->defaultIsSaved)
4588        config->defaultImage = 0;
4589            printf("%i\n", config->defaultImage);
4590            return 0;
4591    
4592      } else if (kernelInfo)      } else if (kernelInfo)
4593   return displayInfo(config, kernelInfo, bootPrefix);   return displayInfo(config, kernelInfo, bootPrefix);
4594    
# Line 2581  int main(int argc, const char ** argv) { Line 4600  int main(int argc, const char ** argv) {
4600      markRemovedImage(config, removeKernelPath, bootPrefix);      markRemovedImage(config, removeKernelPath, bootPrefix);
4601      markRemovedImage(config, removeMBKernel, bootPrefix);      markRemovedImage(config, removeMBKernel, bootPrefix);
4602      setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,      setDefaultImage(config, newKernelPath != NULL, defaultKernel, makeDefault,
4603      bootPrefix, flags);      bootPrefix, flags, defaultIndex);
4604      setFallbackImage(config, newKernelPath != NULL);      setFallbackImage(config, newKernelPath != NULL);
4605      if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,      if (updateImage(config, updateKernelPath, bootPrefix, newKernelArgs,
4606                      removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;                      removeArgs, newMBKernelArgs, removeMBKernelArgs)) return 1;
4607        if (updateKernelPath && newKernelInitrd) {
4608        if (newMBKernel) {
4609        if (addMBInitrd(config, newMBKernel, updateKernelPath,
4610     bootPrefix, newKernelInitrd))
4611        return 1;
4612        } else {
4613        if (updateInitrd(config, updateKernelPath, bootPrefix,
4614     newKernelInitrd))
4615     return 1;
4616        }
4617        }
4618      if (addNewKernel(config, template, bootPrefix, newKernelPath,      if (addNewKernel(config, template, bootPrefix, newKernelPath,
4619                       newKernelTitle, newKernelArgs, newKernelInitrd,                       newKernelTitle, newKernelArgs, newKernelInitrd,
4620                       newMBKernel, newMBKernelArgs)) return 1;                       (const char **)extraInitrds, extraInitrdCount,
4621                         newMBKernel, newMBKernelArgs, newDevTreePath)) return 1;
4622            
4623    
4624      if (numEntries(config) == 0) {      if (numEntries(config) == 0) {
# Line 2597  int main(int argc, const char ** argv) { Line 4628  int main(int argc, const char ** argv) {
4628      }      }
4629    
4630      if (!outputFile)      if (!outputFile)
4631   outputFile = grubConfig;   outputFile = (char *)grubConfig;
4632    
4633      return writeConfig(config, outputFile, bootPrefix);      return writeConfig(config, outputFile, bootPrefix);
4634  }  }

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